I\'m sure the answer is no, but is it possible to determine the width of an element before it is appended to the DOM?
Once it\'s appended, I know I
Modified the code a bit. Here is a pure JS solution:
function measure(el, fn) {
var pV = el.style.visibility,
pP = el.style.position;
el.style.visibility = 'hidden';
el.style.position = 'absolute';
document.body.appendChild(el);
var result = fn(el);
el.parentNode.removeChild(el);
el.style.visibility = pV;
el.style.position = pP;
return result;
}
var div = document.createElement('div');
div.innerHTML = "<p>Hello</p><br/>";
alert(div.offsetHeight); // 0
alert(measure(div, function(el){return el.offsetHeight})); // 68
It is not possible, at least not accurately, because styling affects these properties, and where it's put determines how it is styled and what rules affect it.
For example placing a <p></p>
in the page would by default be the width of the body if appended as a child to it, but if you appeneded it inside for example a <div style="width: 100px;"></div>
, then you see how that quickly changes things.
What you can do with MooTools is use the Element.Measure class - meaning, you inject the element to the DOM, but keep it hidden. Now, you can measure the element without actually showing it.
http://mootools.net/docs/more/Element/Element.Measure
The trick is to show the element (display:block) but also hide it (visibility:hidden) and to set it’s position to absolute so that it doesn’t affect the page flow.
The MooTools Element.Measure class does this, as Oscar mentioned.
/**
* Get bounding client rect for an element (not exists at current DOM tree)
* @param {!HTMLElement} el
* @return {!Promise<!ClientRect>}
*/
function getElementRect(el) {
return new Promise(resolve => {
const element = el.cloneNode(true);
element.style.visibility = "hidden";
element.style.position = "absolute";
document.body.appendChild(element);
resolve(element.getBoundingClientRect());
element.remove();
});
}
const div = /** @type {!HTMLElement} */ (document.createElement("div"));
div.innerHTML = "<p>Hello</p><br/>";
// Execute
(async () => {
const rect = await getElementRect(div);
console.log(rect.width);
})();
DEMO
The Mootools Element.Measure functionality that Oscar mentioned is awesome. For those that use jQuery, here's a quick plugin that accomplishes the same thing:
$.fn.measure = (fn)->
el = $(this).clone(false)
el.css
visibility: 'hidden'
position: 'absolute'
el.appendTo('body')
result = fn.apply(el)
el.remove()
return result
You can call it like this, making sure to return the value (thanks Sam Fen for pointing that out!):
width = $('.my-class-name').measure( function(){ return this.width() } )