Expand my Community achievements bar.

SOLVED

Is it possible to conditionally include different clientlibs depending on the browser in AEM?

Avatar

Level 1
Is it possible to conditionally include different clientlibs based on the user agent of the browser?
IE
<sly data-sly-use.clientLib="/libs/granite/sightly/templates/clientlib.html"
     data-sly-call="${clientlib.js @ categories='a'}"/>

Modern Browsers
<sly data-sly-use.clientLib="/libs/granite/sightly/templates/clientlib.html"
     data-sly-call="${clientlib.js @ categories='b'}"/>

AEM version: 6.3

If not, what are the other alternatives to achieve the same?

1 Accepted Solution

Avatar

Correct answer by
Level 10

Technically speaking, yes it's possible. You would have to:

  1. Create a Sling model which parses the user agent and returns the result (booleans isChrome, isEdge, etc)
  2. Use the model in your HTL file using data-sly-use
  3. Use data-sly-test to check the browser and load the right clientlib

That being said though, I agree with @BrianKasingli that this type of logic is best done in the browser by JS for caching and maintainability reasons.

View solution in original post

7 Replies

Avatar

Community Advisor

bbasu,

The quickest answer is no, you wouldn't want to achieve this with server-side render. 

Instead, if you only want JavaScript to run on the edge Browser, you can include a condition to check for the current browser's user agent. Like this:

 

 

 

 

 

const isEdge = window.navigator.userAgent.indexOf("Edge") > -1;
if (isEdge) {
  // do logic
}

 

 

 

 

 

Or in combination, to dynamically load javascript in the DOM (just sure you have a special client library for edge

 

 

 

 

 

function loadjscssfile(filename){
  var fileref=document.createElement('script')
  fileref.setAttribute("type","text/javascript")
  fileref.setAttribute("src", filename)
 if (typeof fileref!="undefined")
  document.getElementsByTagName("head")[0].appendChild(fileref)
}

const isEdge = window.navigator.userAgent.indexOf("Edge") > -1;
if (isEdge) {
  loadjscssfile("https://1uopu.csb.app/test.js") //dynamically load and add this .js file
  // loadjscssfile("/etc.clientlibs/my-site/clientlibs/clientlib-edge.js") //example dynamicallly loading an AEM clientlib
}

 

 

 

 

 

Also, when you are rendering a page in AEM, the page should be cached from the webserver (dispatcher module) to deliver the best performance. If you implement a different client library to load for each browser, you will lose the opportunity to cache websites. So using Sightly to render specific client libraries per browser is not a good idea.
I hope this helps.

Avatar

Correct answer by
Level 10

Technically speaking, yes it's possible. You would have to:

  1. Create a Sling model which parses the user agent and returns the result (booleans isChrome, isEdge, etc)
  2. Use the model in your HTL file using data-sly-use
  3. Use data-sly-test to check the browser and load the right clientlib

That being said though, I agree with @BrianKasingli that this type of logic is best done in the browser by JS for caching and maintainability reasons.

Avatar

Level 1

please find my response as a comment to my question

Avatar

Employee

It's possible. However, it depends on how different browsers show userAgent values when you do

window.navigator.

In case of Google Chrome, I found out that user agent includes: Mozilla, Chrome, Safari, whereas it should include just Chrome. It also has a property named "vendor" with value "Google Inc.".

sunjot16_0-1586453256729.png

 

In case of Mozilla Firefox, userAgent includes Mozilla & Firefox, but not Chrome, which should be the correct case.

sunjot16_1-1586453361749.png

It all depends on how different browsers & their different versions display userAgent property values.

 

I would recommend you to start with different browsers to see what values they display for userAgent property, and then based on that, write a js code, validating which browser you are expecting in userAgent. Ex: in case of Google Chrome, userAgent contains 3 different values, however, vendor states that it's Google Inc. However, in case of Firefox, vendor property is empty. You may have to include multiple properties to verify which browser your script is loading on.

 

Hope it helps !!

Avatar

Level 1

Hi Guys,

Thanks for your answers! I am aware of the user agent scrapping technique to identify different browsers/devices and the sling model approach. But as @theop76211228 correctly pointed out that using sling models to under the user agent via request is discouraged as sling models will only get invoked during the first request and after that any subsequent calls with be served via dispatcher cache.

However, what I wanted to know is to whether it's possible to dynamically include an AEM clienlib?
I saw what @BrianKasingli posted. But it is impossible to know the clienlib path beforehand given the fact clientlibs in prod generate random md5 hash for versioning.

My solution:

Since, as per my knowledge, it's not possible to know the clientlib path (due to above reasons), I used a sling rewriter (Clientlib transformer) to rewrite the 2 data-sly-clientlibs (one for modern browser and another for IE) into divs with unique ids and script path. In this way, the browser will not load both the scripts as it does not contain scripts tags! And then use the technique @Briankasingli mentioned to load the desired script based on user-agent.

This is kind of hacky but it works! Hence I wanted to learn from the community whether are there better ways to do this. Any suggestions?

Avatar

Level 10

Hahahah XD I had to laugh when I read this, because you're right: its a pretty hacky-hack! But if its stupid and it works, it ain't stupid, right? 

If your clientlibs are getting versioned in PROD it's because you have this installed: https://adobe-consulting-services.github.io/acs-aem-commons/features/versioned-clientlibs/index.html. It's not clear from your comment if this is intentional or if maybe your predecessor(s) put the versioning in place and you're dealing with the consequences. Essentially what you have at the moment is one rewriter (the ACS plugin) to add the hash and one rewriter (yours) to filter the clientlib. As far as performance goes, its barely noticeable so honestly why not? Just add some Javadoc!

However, this does beg the following questions:

  1. Why do you want to filter one clientlib or another at render-time anyways?
  2. Do you really need the MD5 hashing? Be warned: the day you decide to move your on-premise instance into the cloud you can expect some weird behavior from this ACS plugin. As they so eloquently state on their website: "This feature is not AEM as a Cloud Service compatible. Even if it appears to work on AEM as a Cloud Service, please do not use it, as it will cause problems!!!"

You seem like a smart cookie though, add me on LinkedIn: https://www.linkedin.com/in/theo-pendle-1630a52a/

 

 

Avatar

Level 1

Haha sure 
Yes using versioned clientlibs was a decision we took because of its benefits.
Although, we have to revisit this once we decide to move to the cloud as you pointed out.