Performance with infinite scroll or a lot of dom elements? - javascript

I have a question on a big # of dom elmenets and performance.
Let's say I have 6000 dom elements on a page and the number of the elements can be increased as a user interact with the page (user scrolls to create a new dom element) like twitter.
To improve the performance of the page, I can think of only two things.
set display to none to invisible items to avoid reflow
remove invisible items from the dom then re-add them as needed.
Are they any other ways of improving a page with a lot of dom elements?

We had to deal with a similar problem on FoldingText. As the document grew larger, more line elements and associated span elements were created. The browser engine just seemed to choke, and so a better solution needed to be found.
Here's what we did, may or may not be useful for your purposes:
Visualize the entire page as a long document, and the browser viewport as the lens for a specific part of the long document. You really only have to show the part within the lens.
So the first part is to calculate the visible view port. (This depends on how your elements are placed, absolute / fixed / default)
var top = document.scrollTop;
var width = window.innerWidth;
var height = window.innerHeight;
Some more resources to find a more cross-browser based viewport:
How to get the browser viewport dimensions?
Cross-browser method for detecting the scrollTop of the browser window
Second, you need a data structure to know which elements are visible in that area
We already had a balanced binary search tree in place for text editing, so we extended it to manage line heights too, so this part for us was relatively easy. I don't think you'll need a complex data structure for managing your element heights; a simple array or object might do fine. Just make sure you can query heights and dimensions easily on it. Now, how would you get the height data for all your elements. A very simple (but computationally expensive for large amounts of elements!)
var boundingRect = element.getBoundingClientRect()
I'm talking in terms of pure javascript, but if you're using jQuery $.offset, $.position, and methods listed here would be quite helpful.
Again, using a data structure is important only as a cache, but if you want, you could do it on the fly (though as I've stated these operations are expensive). Also, beware of changing css styles and calling these methods. These functions force redraw, so you'll see a performance issue.
Lastly, just replace the elements offscreen with a single, say <div> element with calculated height
Now, you have heights for all the elements stored in your Data structure, query all the elements that lie before the visible viewport.
Create a <div> with css height set (in pixels) to the sum of the element heights
Mark it with a class name so that you know its a filler div
Remove all the elements from the dom that this div covers
insert this newly created div instead
Repeat for elements that lie after the visible viewport.
Look for scroll and resize events. On each scroll, you will need to go back to your data structure, remove the filler divs, create elements that were previously removed from screen, and accordingly add new filler divs.
:) It's a long, complex method, but for large documents it increased our performance by a large margin.
tl;dr
I'm not sure I explained it properly, but the gist of this method is:
Know the vertical dimensions of your elements
Know the scrolled view port
Represent all off-screen elements with a single div (height equal to the sum of all element heights it covers for)
You will need two divs in total at any given time, one for elements above the visible viewport, one for elements below.
Keep track of the view port by listening for scroll and resize events. Recreate the divs and visible elements accordingly

No experience myself with this, but there are some great tips here: http://engineering.linkedin.com/linkedin-ipad-5-techniques-smooth-infinite-scrolling-html5
I had a look at Facebook and they don't seem to do anything in particular on Firefox. As you scroll down, the DOM elements at the top of the page don't change. Firefox's memory usage climbs to about 500 meg before Facebook doesn't allow you to scroll further.
Twitter appears to be the same as Facebook.
Google Maps is a different story - map tiles out of view are removed from the DOM (although not immediately).

It's 2019. The question is really old, but I think it is still relevant and interesting and maybe something changed as of today, as we all now also tend to use React JS.
I noticed that Facebook's timeline seems to use clusters of content which is hidden with display: none !important as soon as the cluster goes out of view, so all the previously rendered elements of the DOM are kept in the DOM, it's just that those out of view are hidden with display: none !important.
Also, the overall height of the hidden cluster is set to the parent div of the hidden cluster.
Here are some screenshots I've made:
As of 2019, what do you think about this approach? Also, for those who use React, how could it be implemented in React? It would be great to receive your opinions and thoughts regarding this tricky topic.
Thank you for the attention!

Related

Pass Pointer Events through empty area of an iframe

Whilst this question references Drift, no prior knowledge of this plugin is required as this is essentially just a CSS issue. In short, it is a chat widget that utilises an auto-inserted iframe to provide it's service (and it is an absolute pain to apply a custom position to).
Background
I have recently installed a chat plugin on my client's site (powered by Drift). After much excruciating pain trying to apply a custom position to the iframe, I finally managed to get it to position correctly (or as best as it would allow).
The reason it is so painful is simply because Drift repositions and resizes it's containing iframe based on numerous, very weird, factors, so these need to be tracked.
The Problem
As mentioned, the Drift JS automatically resizes it's containing iframe according to it's contents. However, sometimes the contents and the calculated height do not match and we are left with an iframe much larger than it needs to be, but due to it's functionality, it must have a higher z-index than anything else on the page, thus the iframe creates a large 'dead' patch where no pointer-events can pass through.
Consider the following:
Can anyone suggest a way in which I can keep the pointer events of the elements within the Drift iframe (red rectangle), but also allow pointer events to pass through in the areas of the iframe that there is no content (yellow area)?
The Problem (Updated)
Please note the red rectangle in the image represents the iframe, not a containing div. i.e. Ignore my annotation
What I have Tried
I have played around with various values for pointer-events but have had no luck. I thought that something along the lines of pointer-events: visiblefill; would work but unfortunately this is only compatible with SVGs...
My last resort solution is to override the calculated height of the Drift iframe but this is extremely dirty and I cannot be sure that my calculated height will be correct, thus the widget may end up looking terrible.

Get HTML element size

I spend couple of hours to try to get width of my div container. I read many questions and answers but none of them seem to work in my case - I always got 0. Finally I found that I can get it through scrollWidth property which wasn't even mentioned in this question and similar.
Now I have what I wanted by I still have no idea how this works.
Why all the other methods fail? Why this is named scrollWidth? I don't want to scroll anything - it's so confusing. Can I get the size of an element before I append it to a document - even scrollWidth don't work in that case. Is there any model I should follow to always get what I see on the screen? I can't see any patterns right now, I write someting, see something different on the screen, and get something even more different in the output. Every time I want to do someting it seem to be 10 different methods I can use but usually only one or two work because it depends on which methods I used earlier. When i work with 2d graphics I used to have x,y,width and height but in html I allways got those smarty CSS which seem to know better what I want to do. Sorry if this sound a little officious but I'm a little annoyed when i need to spend so much time on a trivial task like this. I use to draw graphic on canvas and it was much simpler but now I want to make a simple website so it's probably not a good idea to build it this way.
Here is my example
I understand that i get 0 size becouse those properties refer to element independently of its children and my container has 0,0 size, right? I read that if I set display: inline-block; style it will adapt size to match its children automatically - why this in not working?
The container element in fact has no width. This is why you are getting widths of zero. You can see this if you use dev tools element inspector.
The reason for it being that the container element is absolutely positioned and has no set width. Your child elements are also position: absolute and therefore they will not force the container element to 'grow' to their size.
To get the visible width of the menu, instead you could total up the widths of all the child elements (the first-level menu items). This is probably the best approach to use for the way you currently have things set up. Otherwise, I would suggest completely changing your approach with the html elements you are using, to the CSS properties you are using to position elements where it would not be required to use position: absolute.
I would advise you to open up your dev tools element inspector and start looking at how things react when you change the position from 'absolute' to 'relative'.
Understanding how to position elements and how widths/heights are affected using CSS will save you a lot of headaches - like the one you are having now :)
document.getElementById("mydiv").offsetWidth
This will return the width, including padding.

Javascript detect elements in front of elements

How can one know if an element is in front of another element, if the overlaying element is transparent? The purpose for this is if you're artificially clicking a page element by its ID, and you're ensuring there's no overlay on top of the element that would make confirm the click as synthetic (as a normal user would have to click on the overlay).
Case 1: The overlay is a child of the clickable element To Detect it: Ensure there's no children of the clickable element that look unusual.
Case 2: The overlay has an absolute position and a higher z-index to overlay the clickable element
To Detect it: No clue! Unless you iterate through the bounding rectangles and z-index of every element in the DOM or go through the entire DOM looking for particular style attributes. That is expensive.
So, given the number of ways an element can be made to overlay another element, how can a user script detect elements overlaying elements? I suspect a very verbose method of going through the entire DOM could be avoided.
It turns out there's two ways to do this, and by this I mean two ways to find out if any given element is the top most element regardless of opacity.
Adapted from this blog post post and using their code (with some modifications) it's possible to see the top most element at any given mouse position. I modified their code to look for the computed style zIndex of any elements at a position. The downside to this approach is it's computationally costly in that you're looking at the positions of every element in the DOM more or less.
This stack question about checking whether an element is really visible on screen received an answer that linked to a browser method document.elementFromPoint which when given some screen coordinates returns the top most element, regardless of opacity but in accordance with its display style. It appears as though that function is supported in at least Firefox and Chrome.
In the end, there's two solutions to choose from, with the latter likely being the best solution in terms of reliability and cost.

Animate page reflow?

I am switching the contents of divs (fading old contents out, then fading new contents in) and because they are slightly different contents, the moment they change there is a jarring reorganization of everything below them.
My question is, is there a way to make this movement smooth?
I suspect that pretty much the only feasible way to do this is to use javascript to determine ahead of time what the heights (in my case I only deal with blocks where the vertical alignment shifts) of the starting and ending elements are, and assign these values directly. Once I do this I am sure CSS3 transition will apply a pleasant animation.
Is there perhaps a way to get this without specifying explicit dimensions? I seem to recall at some point having experienced items getting moved around the page in an animated fashion. This gives me hope that it could be done using just CSS.
I'd normally create a temporary (invisible) element holding new content so as to calculate its height. After that, the original element can be animated from its current height to the newly calculated height.
It is important that the temporary element created is an identical sibling of the original element so that all the necessary styles cascade and get inherited correctly (for instance, calculating new content height is useless if it doesn't have correct font-size applied)
While animating between different heights set explicitly (i.e. with JS as described above) can be accomplished with CSS3 (transition: height .5s ease;), it will not work for different heights set implicitly (i.e. modifying element content with height:auto)

Why isn't z-index working as I'd expect it to here?

A requirement for a current project of mine involves "highlighting" an HTML element in the context of a page. That is, I need to provide some sort of visual effect that decreases the brightness of the surrounding page while leaving the element at full brightness.
To achieve this, I'm trying the following approach:
Determining the highest z-index value of any element on the page (using JavaScript).
Creating an element to function as a "backdrop" on top of the page. This is just a <div> with a translucent gray background image, sized to 100% of the width and height of the <body> element, with position: fixed. I set its z-index to 1 greater than the highest z-index I've found on the page, with the intent that it will overlay every other element on the page.
Change the z-index of the "highlighted" element to 1 greater than the backdrop. The intent is to allow it to sit on top of the backdrop, which in turn sits on top of the rest of the page.
I got it working on a quick test page:
http://troy.onespot.com/static/stack_overflow/z_index_test.html
but when I tried to set it up on a few actual Web pages, it didn't work in all cases. For example:
http://troy.onespot.com/static/stack_overflow/z_index.html
Here, I've inserted two "dummy" elements on a copy of a Jacksonville.com article page, both with a class of test (if you're looking at the page source, they're at lines 169 & 859).
I also added some JavaScript (using jQuery) at the very end of the page that functions as I've described above.
The first <div class="test"> does function as I'd expect it to. However, the second one does not - it seems to still be stuck beneath the "backdrop" element, despite having a higher z-index.
I think this may have something to do with stacking contexts, but after reading through the relevant W3C docs (http://www.w3.org/TR/CSS21/visuren.html#z-index & http://www.w3.org/TR/CSS21/zindex.html), I still can't fathom why this is happening. I'd appreciate anyone more familiar with z-index and stacking order than I to take a look at my examples and let me know if anything looks suspicious.
Please note that I've only tested these examples in Firefox v3.6.
Thanks very much for any help!
The problem is that the second test div is inside a bunch of other HTML elements, one of which must be creating a new stacking context (it may be the #wl-wrapper-tier-1 div). Basically, a new stacking context is created whenever an element is positioned and has a z-index other than auto, see this MDC article for more info on stacking contexts.
Ultimately this means you can't achieve your desired effect reliably with this method. I think you're probably better off composing 4 divs to surround the target element.
If the element that you're highlighting is inside a different element (stacking context) with a z-index lower than the backdrop, it will not appear higher than the backdrop, since the element's z-index only controls stacking order within that parent.
The only good solution is to clone the highlighted element and add the clone to the <body> tag.
Beware of inherited CSS styles, which would be lost.

Categories

Resources