Getting the height of an element before added to the DOM
Asked Answered
Z

3

47

Is there any way to get an element's height prior to appending it to the DOM? I know that clientHeight doesn't work as I've tried and it always returns 0. Are there other methods that may return the height or does the element have to be a part of the DOM in order for the height to be calculated?

This is a sample of what I'm going for:

function test(a) {
    var a=document.createElement(a)
    a.style.top=(window.innerHeight/2-a.clientHeight/2)+'px' //fixed position in CSS
    document.body.appendChild(a)
    }

*Note: This is only a simplified version of the function I'm working on in order to project what I'm trying to achieve without all of the unneeded mess.

Zoon answered 10/5, 2011 at 0:49 Comment(1)
Different elements have different properties. We need to know specific types if you want a good answer.Diecious
D
49

Elements don't have a height, in any real sense, until they've been added to the DOM, as their styles cannot be evaluated until then.

You can get around this easily enough using visibility: hidden so that the element can be added to the DOM (and its height determined) without causing visible flickering.

function test(a) {
  var a = document.createElement(a);
  a.style.visibility = 'hidden';
  document.body.appendChild(a);
  a.appendChild(document.createTextNode('Hello'));
  a.style.fontStyle = 'italic';
  a.style.top=(window.innerHeight/2 - a.clientHeight/2) + 'px';
  a.style.visibility = '';
  return a;
}

test('p').style.background = '#0f0';
p { position: absolute; top: 0; left: 0; }

(This is working on the assumption that you're using top because the element is absolutely positioned or fixed. If it weren't, you'd need to make it so temporarily.) Hidden elements still take up space in the DOM (so their sizes must be calculated), but cannot actually be seen by the user.

Dorothadorothea answered 10/5, 2011 at 0:53 Comment(4)
Flickering isn't the problem because it's practically instant. Edit: This does work! I thought that changing properties of elements as variables after appending them would have no affect, but evidently it does, and that's as good as a solution as I was looking for.Zoon
@Jimmy — I'm not sure what you mean. a can still be manipulated normally after adding it to the DOM. Any changes which might affect the height should be placed between the appendChild and a.top lines, but other than that, everything should work as expected.Dorothadorothea
I realized that I made an error in my function the first time trying it (the actual function has numerous elements within eachother). It worked when I added it after being placed at the very end, which is pretty much the solution I'm looking for even if in a bit of a different order.Zoon
using getBoundingClientRect after mounting will give the height and width or not irrespective of the position of the element?Shroudlaid
V
7

If the height of the DOM node is/can be dynamic then you probably cannot measure it before it exists in the DOM, unless you somehow take into consideration all possible scenarios (font-size, padding/margin/border, actual size, etc.)

A viable option is to inject the node into the DOM, visually hidden, and measure things, and only then reveal it.

Here is a working demo of how to measure an element by briefly adding it to the DOM and inspecting its height, then removing it. This method might be costly since the node is cloned, so the added styles won't affect it afterwards and all DOM events will be removed from it.

If the inspected node has a lot of sub-nodes, this might not be efficient, but stil can be quite handy.

function getNodeHeight(node) {
    var height, clone = node.cloneNode(true)
    // hide the meassured (cloned) element
    clone.style.cssText = "position:fixed; top:-9999px; opacity:0;"
    // add the clone to the DOM 
    document.body.appendChild(clone)
    // meassure it
    height = clone.clientHeight
    // cleaup 
    clone.parentNode.removeChild(clone)
    return height
}


var newDiv = document.createElement("div");
newDiv.innerHTML = `Lorem ipsum dolor sit amet, consectetur adipiscing elit, 
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 

<h1>deserunt mollit anim id est laborum.<h1>`


// print the height of "newDiv"
console.log( getNodeHeight(newDiv) )
Valona answered 28/11, 2019 at 16:29 Comment(3)
would it work smoothly for a large scale. For example, if I have 10000 heavily rendered elements to count, would it be able count and render in just a few seconds?Glyco
If you have 10,000 elements on your page than you have major major performance problem... you should consider lazy-loading them on demand and not rendering what's off the viewportValona
thanks, then do you have any suggestion for lazy-loading them? I know we can do in the way Facebook do, but I'm looking for anything kind of similar to "ListView.builder" in flutter/dart.Glyco
C
0

Before adding the element to the DOM I don't think it is possible, unless you know all the variables involved beforehand (unlikely).

However it is possible to get it's height before it gets displayed on the screen.

The screens have a refresh rate (maximum number of images shown per second) so it is not possible to render the page at each change made by the DOM.

You can ask to the DOM to notify you right before the repaint by calling requestAnimationFrame. Here you can get the element's properties and change them.

You can even cancel this frame by calling cancelAnimationFrame.

https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame

https://developer.mozilla.org/en-US/docs/Web/API/Window/cancelAnimationFrame

Circumspection answered 14/12, 2023 at 23:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.