When implementing a custom scrollbars there’s a need to detect changes in height of the element to which you are applying the scrollbars to, as the height of the scrollbars need to be adjusted based on the content’s height. We also need to take into account that the content could change and height needs to be recalculated i.e paging on queries could yield 10 results, whilst on the last page it could yield just 5 results, and these changes need to be accounted for by the scrollbar. And the same problem arises when getting content asynchronously, because whilst the content is empty on initial render it gets rendered only after successful fetch of the required data and we should calculate the scrollbar height once the content is available which is hard to determine as the operation is asynchornous.
Different libraries and plugins deal with the problem separately.
-
jQuery Scrollbar runs a timeout every 300ms to account for any changes in the DOM. https://github.com/gromo/jquery.scrollbar/blob/master/jquery.scrollbar.js#L665
-
ftscroller [Financial Times] listens for DOM mutations on the element that hosts the content. https://github.com/ftlabs/ftscroller/blob/master/lib/ftscroller.js#L2033
-
IScroll 5 does not deal with it as there's no straightforward answer for detecting DOM mutations, whilst trying to support a wide range of devices.
Currently in our application we use the method that has been used by ftscroller which is to detect DOM mutations using Mutation Observer and for unsupported browsers like IE9, IE10 Android 4.1.2 we fall back to DOM Mutation events.
var MutationObserver = window.MutationObserver || window.WebkitMutationObserver;
if (MutationObserver) {
obs = new MutationObserver(function (mutations, observer) {
if (mutations[0].addedNodes.length || mutations[0].removedNodes.length) {
domChanged(); //recalculate scrollbar height if necessary
}
});
obs.observe(observeElem, { childList: true, subtree: true });
} else {
/*
This is expensive in terms of performance. However the only alternative
we would have is to use setInterval.
*/
observeElem.addEventListener('DOMSubtreeModified', domChanged, true);
}
Mutation observers are performant and don't cause any visible performance lag but falling back on the mutation events on devices that don't support mutaion observer is an expensive operation which has been well documented all over the internet and somewhat visible on IE9 on our current application. https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Mutation_events
Using set interval is another viable operation but firing set intervals for all the Gadgets could eat up the CPU cycles and over time bog the application performance.
One approach we could take to tackle the issue is to detect Touch devices and fallback to native overflow scrolling for these devices which could potentially leave us with only IE9 and IE10 suffering from the performance problem. But this would mean that we won't have any visible scrollbars on the gadget themselves and the users will have to rely on their on intuition/sixth sense to understand that content is scrollable.
Any thoughts and suggestion is welcome.