DOM Element Width before Appended to DOM - javascript

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 can use offsetWidth and offsetHeight.
Thanks

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.

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() } )

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

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

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.

/**
* 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

A slightly different version of #kashesanders: Add the element to a div and put that div inside the DOM.
function getSizeOfNonDomElement(domElement)
{
// setup
let div = document.createElement("div");
div.style.position = "absolute";
div.style.visibility = "hidden";
div.style.display = "block";
div.appendChild (domElement);
document.body.appendChild (div);
// run
let rect = domElement.getBoundingClientRect ();
// cleanup
document.body.removeChild (div);
div.removeChild (domElement);
return rect;
}
If you want to make it more secure, add a construct to check whether it already has a parent and/or is inside the DOM.

Related

Why does a <div> have 0 width? [duplicate]

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 can use offsetWidth and offsetHeight.
Thanks
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.
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() } )
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
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
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.
/**
* 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
A slightly different version of #kashesanders: Add the element to a div and put that div inside the DOM.
function getSizeOfNonDomElement(domElement)
{
// setup
let div = document.createElement("div");
div.style.position = "absolute";
div.style.visibility = "hidden";
div.style.display = "block";
div.appendChild (domElement);
document.body.appendChild (div);
// run
let rect = domElement.getBoundingClientRect ();
// cleanup
document.body.removeChild (div);
div.removeChild (domElement);
return rect;
}
If you want to make it more secure, add a construct to check whether it already has a parent and/or is inside the DOM.

How do we convert jQuery prepend() to VanillaJS [duplicate]

How can I implement prepend and append with regular JavaScript without using jQuery?
Here's a snippet to get you going:
theParent = document.getElementById("theParent");
theKid = document.createElement("div");
theKid.innerHTML = 'Are we there yet?';
// append theKid to the end of theParent
theParent.appendChild(theKid);
// prepend theKid to the beginning of theParent
theParent.insertBefore(theKid, theParent.firstChild);
theParent.firstChild will give us a reference to the first element within theParent and put theKid before it.
Perhaps you're asking about the DOM methods appendChild and insertBefore.
parentNode.insertBefore(newChild, refChild)
Inserts the node newChild as a child of parentNode before the
existing child node refChild. (Returns newChild.)
If refChild is null, newChild is added at the end of the list of
children. Equivalently, and more readably, use
parentNode.appendChild(newChild).
You didn't give us much to go on here, but I think you're just asking how to add content to the beginning or end of an element?
If so here's how you can do it pretty easily:
//get the target div you want to append/prepend to
var someDiv = document.getElementById("targetDiv");
//append text
someDiv.innerHTML += "Add this text to the end";
//prepend text
someDiv.innerHTML = "Add this text to the beginning" + someDiv.innerHTML;
Pretty easy.
If you want to insert a raw HTML string no matter how complex, you can use:
insertAdjacentHTML, with appropriate first argument:
'beforebegin'
Before the element itself.
'afterbegin'
Just inside the element, before its first child.
'beforeend'
Just inside the element, after its last child.
'afterend'
After the element itself.
Hint: you can always call Element.outerHTML to get the HTML string representing the element to be inserted.
An example of usage:
document.getElementById("foo").insertAdjacentHTML("beforeBegin",
"<div><h1>I</h1><h2>was</h2><h3>inserted</h3></div>");
DEMO
Caution: insertAdjacentHTML does not preserve listeners that where attached with .addEventLisntener.
I added this on my project and it seems to work:
HTMLElement.prototype.prependHtml = function (element) {
const div = document.createElement('div');
div.innerHTML = element;
this.insertBefore(div, this.firstChild);
};
HTMLElement.prototype.appendHtml = function (element) {
const div = document.createElement('div');
div.innerHTML = element;
while (div.children.length > 0) {
this.appendChild(div.children[0]);
}
};
Example:
document.body.prependHtml(`Hello World`);
document.body.appendHtml(`Hello World`);
Here's an example of using prepend to add a paragraph to the document.
var element = document.createElement("p");
var text = document.createTextNode("Example text");
element.appendChild(text);
document.body.prepend(element);
result:
<p>Example text</p>
In order to simplify your life you can extend the HTMLElement object. It might not work for older browsers, but definitely makes your life easier:
HTMLElement = typeof(HTMLElement) != 'undefined' ? HTMLElement : Element;
HTMLElement.prototype.prepend = function(element) {
if (this.firstChild) {
return this.insertBefore(element, this.firstChild);
} else {
return this.appendChild(element);
}
};
So next time you can do this:
document.getElementById('container').prepend(document.getElementById('block'));
// or
var element = document.getElementById('anotherElement');
document.body.prepend(div);
In 2017 I know for Edge 15 and IE 12, the prepend method isn't included as a property for Div elements, but if anyone needs a quick reference to polyfill a function I made this:
HTMLDivElement.prototype.prepend = (node, ele)=>{
try { node.insertBefore(ele ,node.children[0]);}
catch (e){ throw new Error(e.toString()) } }
Simple arrow function that's compatible with most modern browsers.
var insertedElement = parentElement.insertBefore(newElement, referenceElement);
If referenceElement is null, or undefined, newElement is inserted at the end of the list of child nodes.
insertedElement The node being inserted, that is newElement
parentElement The parent of the newly inserted node.
newElement The node to insert.
referenceElement The node before which newElement is inserted.
Examples can be found here: Node.insertBefore
You can also use unshift() to prepend to a list
document.write() is not a good practice, some browsers like Chrome give you a warning if you use it, and it may be a bad solution if you are providing it to a customer, they don't want to use your code and see warnings in the debug console!
Also jQuery may also be a bad thing if you are giving your code to a customer who already uses jQuery for other functionality on their site, there will be a conflict if there is already a different version of jQuery running.
If you want to insert content into an iframe, and do that with pure JS, and with no JQuery, and without document.write(), I have a solution.
You can use the following steps
1.Select your iframe:
var iframe = document.getElementById("adblock_iframe");
2.Create an element that you want to insert into the frame, let's say an image:
var img = document.createElement('img');
img.src = "https://server-name.com/upload/adblock" + id + ".jpg";
img.style.paddingLeft = "450px";
//scale down the image is we have a high resolution screen on the client side
if (retina_test_media == true && high_res_test == true) {
img.style.width = "200px";
img.style.height = "50px";
} else {
img.style.width = "400px";
img.style.height = "100px";
}
img.id = "image";
3.Insert the image element into the iframe:
iframe.contentWindow.document.body.appendChild(img);
This is not best way to do it but if anyone wants to insert an element before everything, here is a way.
var newElement = document.createElement("div");
var element = document.getElementById("targetelement");
element.innerHTML = '<div style="display:none !important;"></div>' + element.innerHTML;
var referanceElement = element.children[0];
element.insertBefore(newElement,referanceElement);
element.removeChild(referanceElement);

How do I add a div to a page using javascript?

So... I want to add the following right before the /body of a document, I can't seem to find a way to make it work:
document.body.innerHTML+="<div style=\"position:absolute; right:-10px; bottom:10px;\">response</div>\"");
Especially with the <body> element, you shouldn't be using innerHTML to append elements to an element. An easier way is with DOM methods like createElement, insertBefore or appendChild.
https://developer.mozilla.org/en-US/docs/DOM/document.createElement
https://developer.mozilla.org/en-US/docs/DOM/Node.insertBefore
https://developer.mozilla.org/en-US/docs/DOM/Node.appendChild
Try this:
var div = document.createElement("div");
div.style.position = "absolute";
div.style.right = "-10px";
div.style.bottom = "10px";
div.innerHTML = "response";
var lastChild = document.body.lastChild;
document.body.insertBefore(div, lastChild.nextSibling);
Although I guess it would make sense to just append it to the body:
document.body.appendChild(div);
(instead of the last two lines in my first example)
It also depends on when you're calling this code. Of course it will work if executed in the middle of the <body>, but you probably want to wait until the body (DOM) is ready so that the element is actually appended at the real end of the body. By using something like:
window.onload = function () {
// Your code from above
};
This will make sure the original <body> contents are ready.
Don't add stuff like that! Instead, do this:
var newDiv = document.createElement('div')
newDiv.style.position = 'absolute'
newDiv.id = 'myDiv'
newDiv.innerHTML = 'hello'
//etc.
document.body.appendChild(newDiv)
Change code to
document.body.innerHTML="<div style=\"position:absolute; right:-10px; bottom:10px;\">response</div>\"";
Remove ) at the end
What about:
var div = document.createElement("div");
// it's better use a CSS here instead
div.style.position = "absolute";
div.style.right = "-10px";
div.style.bottom = "10px";
div.innerHTML = "response";
document.body.appendChild(div);
?

How to remove default document style from an element using Javascript

I am trying to create a div element which shows some text as popup. But the problem is when i create the element using javascript, the div already contains the default style which is defined in document style sheet or external css. Like for example:
popup = document.createElement("div");
popup.setAttribute("id","myElement");
popup.style.left = "100px";
popup.style.top = "100px";
popup.style.width = "100px";
popup.style.height = "100px";
document.body.appendChild(popup);
and now when it shows up, it already has colors, borders etc because in css there is this div style which is being applied on this element. I want it to not inherit anything which is defined for the document itself, how can i do that, or may be overwrite the original style??
A way to achieve this is to override all of the available properties set with the css in this document. But with JavaScript is a lot of work.
Better way is to add an id or class to the newly created element. The style should have all of the properties that are applicable for the type of element you are creating. If you miss a property it will be set from the present css.
I would give these new divs a specific class name and link a custom stylesheet that resets all properties for that class to the desired defaults (ensuring that the linked styles have the necessary precedence in the cascade). Moving this reset into your JavaScript sounds like a bad idea.
So I made something like this. It may not be ideal solution but it atleast does what i wanted:
function displayMenu(){
var popup;
//see if there is already default style defined in the document
var styleSheets = document.styleSheets;
var size = styleSheets.length;
var cssRules = new Array();
var rules = "";
var css, len, st, sp;
for (i=0;i<size;i++){
cssRules[i] = styleSheets[i].rules || styleSheets[i].cssRules;
for (j=0;j<cssRules[i].length;j++){
if(cssRules[i][j].cssText.search(/div/i) != -1){
css = cssRules[i][j].cssText;
css = css.substr(((css.search("{"))+1),((css.search("}"))-(((css.search("{"))+1))));
if((css.search("{") == -1) && (css.search("}") == -1)) {
//no of css-properties in this specific rule
len = css.split(";").length - 1;
for (k=0;k<len;k++){
st = css.search(";") + 1;
rules += css.substr(0,(css.substr(0,st).search(":")+1)) + "0\n";
css = css.substr(st);
}
} else {} //ignore this rule
}
}
}
var reset = '.myStyle { '+ rules +' }\n';
//now create a css Class which overwrite default document properties for this <div> element
var myStyle = document.createElement('style');
myStyle.type = 'text/css';
//TODO: should be replaced with style from arguments
myStyle.innerHTML = reset;
document.getElementsByTagName('head')[0].appendChild(myStyle);
//start creating the popup menu:
var popup;
popup = document.createElement("div");
popup.setAttribute("id","guide_popup");
popup.setAttribute("class","myStyle");
//now define own style rules: (All basic properties need to be defined as there is none by defualt now)
popup.style.top = top;
popup.style.left = left;
popup.style.width = width;
popup.style.height = height;
popup.style.zIndex = index;
//TODO: should be replaced with str in aruguments
var popup_text = document.createTextNode("This is my sample text");
popup.appendChild(popup_text);
//finally process the DOM
document.body.appendChild(popup);
}

How can I implement prepend and append with regular JavaScript?

How can I implement prepend and append with regular JavaScript without using jQuery?
Here's a snippet to get you going:
theParent = document.getElementById("theParent");
theKid = document.createElement("div");
theKid.innerHTML = 'Are we there yet?';
// append theKid to the end of theParent
theParent.appendChild(theKid);
// prepend theKid to the beginning of theParent
theParent.insertBefore(theKid, theParent.firstChild);
theParent.firstChild will give us a reference to the first element within theParent and put theKid before it.
Perhaps you're asking about the DOM methods appendChild and insertBefore.
parentNode.insertBefore(newChild, refChild)
Inserts the node newChild as a child of parentNode before the
existing child node refChild. (Returns newChild.)
If refChild is null, newChild is added at the end of the list of
children. Equivalently, and more readably, use
parentNode.appendChild(newChild).
You didn't give us much to go on here, but I think you're just asking how to add content to the beginning or end of an element?
If so here's how you can do it pretty easily:
//get the target div you want to append/prepend to
var someDiv = document.getElementById("targetDiv");
//append text
someDiv.innerHTML += "Add this text to the end";
//prepend text
someDiv.innerHTML = "Add this text to the beginning" + someDiv.innerHTML;
Pretty easy.
If you want to insert a raw HTML string no matter how complex, you can use:
insertAdjacentHTML, with appropriate first argument:
'beforebegin'
Before the element itself.
'afterbegin'
Just inside the element, before its first child.
'beforeend'
Just inside the element, after its last child.
'afterend'
After the element itself.
Hint: you can always call Element.outerHTML to get the HTML string representing the element to be inserted.
An example of usage:
document.getElementById("foo").insertAdjacentHTML("beforeBegin",
"<div><h1>I</h1><h2>was</h2><h3>inserted</h3></div>");
DEMO
Caution: insertAdjacentHTML does not preserve listeners that where attached with .addEventLisntener.
I added this on my project and it seems to work:
HTMLElement.prototype.prependHtml = function (element) {
const div = document.createElement('div');
div.innerHTML = element;
this.insertBefore(div, this.firstChild);
};
HTMLElement.prototype.appendHtml = function (element) {
const div = document.createElement('div');
div.innerHTML = element;
while (div.children.length > 0) {
this.appendChild(div.children[0]);
}
};
Example:
document.body.prependHtml(`Hello World`);
document.body.appendHtml(`Hello World`);
Here's an example of using prepend to add a paragraph to the document.
var element = document.createElement("p");
var text = document.createTextNode("Example text");
element.appendChild(text);
document.body.prepend(element);
result:
<p>Example text</p>
In order to simplify your life you can extend the HTMLElement object. It might not work for older browsers, but definitely makes your life easier:
HTMLElement = typeof(HTMLElement) != 'undefined' ? HTMLElement : Element;
HTMLElement.prototype.prepend = function(element) {
if (this.firstChild) {
return this.insertBefore(element, this.firstChild);
} else {
return this.appendChild(element);
}
};
So next time you can do this:
document.getElementById('container').prepend(document.getElementById('block'));
// or
var element = document.getElementById('anotherElement');
document.body.prepend(div);
In 2017 I know for Edge 15 and IE 12, the prepend method isn't included as a property for Div elements, but if anyone needs a quick reference to polyfill a function I made this:
HTMLDivElement.prototype.prepend = (node, ele)=>{
try { node.insertBefore(ele ,node.children[0]);}
catch (e){ throw new Error(e.toString()) } }
Simple arrow function that's compatible with most modern browsers.
var insertedElement = parentElement.insertBefore(newElement, referenceElement);
If referenceElement is null, or undefined, newElement is inserted at the end of the list of child nodes.
insertedElement The node being inserted, that is newElement
parentElement The parent of the newly inserted node.
newElement The node to insert.
referenceElement The node before which newElement is inserted.
Examples can be found here: Node.insertBefore
You can also use unshift() to prepend to a list
document.write() is not a good practice, some browsers like Chrome give you a warning if you use it, and it may be a bad solution if you are providing it to a customer, they don't want to use your code and see warnings in the debug console!
Also jQuery may also be a bad thing if you are giving your code to a customer who already uses jQuery for other functionality on their site, there will be a conflict if there is already a different version of jQuery running.
If you want to insert content into an iframe, and do that with pure JS, and with no JQuery, and without document.write(), I have a solution.
You can use the following steps
1.Select your iframe:
var iframe = document.getElementById("adblock_iframe");
2.Create an element that you want to insert into the frame, let's say an image:
var img = document.createElement('img');
img.src = "https://server-name.com/upload/adblock" + id + ".jpg";
img.style.paddingLeft = "450px";
//scale down the image is we have a high resolution screen on the client side
if (retina_test_media == true && high_res_test == true) {
img.style.width = "200px";
img.style.height = "50px";
} else {
img.style.width = "400px";
img.style.height = "100px";
}
img.id = "image";
3.Insert the image element into the iframe:
iframe.contentWindow.document.body.appendChild(img);
This is not best way to do it but if anyone wants to insert an element before everything, here is a way.
var newElement = document.createElement("div");
var element = document.getElementById("targetelement");
element.innerHTML = '<div style="display:none !important;"></div>' + element.innerHTML;
var referanceElement = element.children[0];
element.insertBefore(newElement,referanceElement);
element.removeChild(referanceElement);

Categories

Resources