Check if element contains #shadow-root - javascript

Is it possible to see if a Shadow DOM element exists? I'm not too concerned with manipulating it, or even really targeting it per-say. I understand the reasoning of the encapsulation. But I'd like to be able to style other elements in the regular DOM, based on whether or not the Shadow DOM element is present.
Sort of like:
if ( $('#element-id #shadow-root').length ) {
// true
}
Or if not for the shadow-root, at least a specific element within, like the id of a div. So if that div exists, then clearly that Shadow DOM element is on the page.
I know it wouldn't be that simple... From some research I've done, there are things like >>> and /deep/ but their support seems to be low/none/deprecated. Buy maybe there's another way, however inelegant it may be?

If you want to check whether or not a specific element is hosting an open Shadow DOM element, you can do the following:
var el = document.querySelector('#some-element');
if (!!el.shadowRoot) {
// Then it is hosting an OPEN Shadow DOM element
}
You can also get the Shadow DOM element, and then operate on it like a normal node:
var shadowEl = el.shadowRoot;
// And for example:
console.log(shadowEl.innerHTML);
Here is an example that works in the latest version of Chrome:
const div = document.querySelector('div');
const p = document.querySelector('p');
const shadowRoot = p.attachShadow({mode: 'open'})
shadowRoot.textContent = 'A Shadow DOM Paragraph. I overrode the content specified!';
console.log('Paragraph has Shadow DOM:', !!p.shadowRoot); // true
console.log('Div has Shadow DOM:', !!div.shadowRoot); // false
<div>A Normal Div</div>
<p>A Normal Paragraph</p>

You can access the shadowRoot of an element with the property shadowRoot, so you could traverse all the nodes and check if the property is null or not.
You can select all nodes in a document with document.getElementsByTagName('*').
So all in all, we would have something like this:
var allNodes = document.getElementsByTagName('*');
for (var i = 0; i < allNodes.length; i++) {
if(allNodes[i].shadowRoot) {
// Do some CSS styling
}
}
With the additions of ES6, we could do something simpler like this:
document.getElementsByTagName('*')
.filter(element => element.shadowRoot)
.forEach(element => {
// Do some CSS styling
});

The other answers by KevBot and Marko Kajzer only work for ShadowRoot created with mode: 'open'. Here's a way to detect if an element has a ShadowRoot, even if the root is closed. Make sure this runs before other code (before any calls to attachShadow) or it will fail to catch any elements that already have a ShadowRoot by the time this code is set up:
const shadowHosts = new WeakSet()
const original = Element.prototype.attachShadow
Element.prototype.attachShadow = function attachShadow(...args) {
const result = original.apply(this, args)
shadowHosts.add(this)
return result
}
export function hasShadow(el) {
return shadowHosts.has(el)
}
then use hasShadow on any element
if (hasShadow(someElement)) {...}

Related

i want to insert element inside of a div and i want this element to be the first child [duplicate]

I have an element E and I'm appending some elements to it. All of a sudden, I find out that the next element to append should be the first child of E. What's the trick, how to do it? Method unshift doesn't work because E is an object, not array.
Long way would be to iterate through E's children and to move'em key++, but I'm sure that there is a prettier way.
var eElement; // some E DOM instance
var newFirstElement; //element which should be first in E
eElement.insertBefore(newFirstElement, eElement.firstChild);
2018 version - prepend
parent.prepend(newChild) // [newChild, child1, child2]
This is modern JS! It is more readable than previous options. It is currently available in Chrome, FF, and Opera.
The equivalent for adding to the end is append, replacing the old appendChild
parent.append(newChild) // [child1, child2, newChild]
Advanced usage
You can pass multiple values (or use spread operator ...).
Any string value will be added as a text element.
Examples:
parent.prepend(newChild, "foo") // [newChild, "foo", child1, child2]
const list = ["bar", newChild]
parent.append(...list, "fizz") // [child1, child2, "bar", newChild, "fizz"]
Related DOM methods
Read More - child.before and child.after
Read More - child.replaceWith
Mozilla Documentation
Can I Use
2017 version
You can use
targetElement.insertAdjacentElement('afterbegin', newFirstElement)
From MDN :
The insertAdjacentElement() method inserts a given element node at a given position relative to the element it is invoked upon.
position
A DOMString representing the position relative to the element; must be one of the following strings:
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.
element
The element to be inserted into the tree.
In the family of insertAdjacent there is the sibling methods:
element.insertAdjacentHTML('afterbegin','htmlText')`
That can inject html string directly, like innerHTML but without override everything, so you can use it as a mini-template Engin and jump the oppressive process of document.createElement and even build a whole component with string manipulation process
element.insertAdjacentText for inject sanitize string into element . no more encode/decode
You can implement it directly i all your window html elements.
Like this :
HTMLElement.prototype.appendFirst = function(childNode) {
if (this.firstChild) {
this.insertBefore(childNode, this.firstChild);
}
else {
this.appendChild(childNode);
}
};
Accepted answer refactored into a function:
function prependChild(parentEle, newFirstChildEle) {
parentEle.insertBefore(newFirstChildEle, parentEle.firstChild)
}
Unless I have misunderstood:
$("e").prepend("<yourelem>Text</yourelem>");
Or
$("<yourelem>Text</yourelem>").prependTo("e");
Although it sounds like from your description that there is some condition attached, so
if (SomeCondition){
$("e").prepend("<yourelem>Text</yourelem>");
}
else{
$("e").append("<yourelem>Text</yourelem>");
}
I think you're looking for the .prepend function in jQuery. Example code:
$("#E").prepend("<p>Code goes here, yo!</p>");
I created this prototype to prepend elements to parent element.
Node.prototype.prependChild = function (child: Node) {
this.insertBefore(child, this.firstChild);
return this;
};
var newItem = document.createElement("LI"); // Create a <li> node
var textnode = document.createTextNode("Water"); // Create a text node
newItem.appendChild(textnode); // Append the text to <li>
var list = document.getElementById("myList"); // Get the <ul> element to insert a new node
list.insertBefore(newItem, list.childNodes[0]); // Insert <li> before the first child of <ul>
https://www.w3schools.com/jsref/met_node_insertbefore.asp

Compare node and query selector [duplicate]

Is there any way to test if a selector would match a given DOM Element? Preferably, without the use of an external library like Sizzle. This is for a library and I would like to minimize the amount of third party plugins required for the "core" library. If it ends up requiring Sizzle I'll just add that as a plugin to the library for those who want the feature it would enable.
For example, I would be able to do something like:
var element = <input name="el" />
matches("input[name=el]", element) == true
EDIT: After thinking about it more, I came up with a solution, this technically works, but it doesn't seem optimal in terms of efficiency:
function matchesSelector(selector, element) {
var nodeList = document.querySelectorAll(selector);
for ( var e in nodeList ) {
return nodeList[e] === element;
}
return false;
}
Basically the function queries the entire document with the given selector, and then it iterates over the nodeList. If the given element is in the nodeList, then it returns true, and if it isn't it will return false.
If anyone can come up with a more efficient answer I would gladly mark their response as the answer.
EDIT: Flavius Stef pointed me towards a browser specific solution for Firefox 3.6+, mozMatchesSelector. I also found the equivalent for Chrome (version compatibility unknown, and it may or may not work on Safari or other webkit browsers): webkitMatchesSelector, which is basically the same as the Firefox implementation. I have not found any native implementation for the IE browsers yet.
For the above example, the usage would be:
element.(moz|webkit)MatchesSelector("input[name=el]")
It seems the W3C has also addressed this in the Selectors API Level 2 (still a draft at this moment) specification. matchesSelector will be a method on DOM Elements once approved.
W3C Usage: element.matchesSelector(selector)
Since that specification is still a draft and there is a lag time before popular browsers implement the methods once it becomes the standard, it may be a while until this actually usable. Good news is, if you use any of the popular frameworks, chances are they probably implement this functionality for you without having to worry about cross browser compatibility. Although that doesn't help those of us who can't include third party libraries.
Frameworks or libraries that implement this functionality:
http://www.prototypejs.org/api/element/match
http://developer.yahoo.com/yui/docs/YAHOO.util.Selector.html
http://docs.jquery.com/Traversing/is
http://extjs.com/deploy/dev/docs/output/Ext.DomQuery.html#Ext.DomQuery-methods
http://base2.googlecode.com/svn/doc/base2.html#/doc/!base2.DOM.Element.matchesSelector
http://wiki.github.com/jeresig/sizzle/
For the benefit of those visiting this page after lo these many years, this functionality is now implemented in all modern browsers as element.matches without vendor prefix (except for ms for MS browsers other than Edge 15, and webkit for Android/KitKat). See http://caniuse.com/matchesselector.
For best performance, use the browser implementations ((moz|webkit|o|ms)matchesSelector) where possible. When you can't do that, here is a manual implementation.
An important case to consider is testing selectors for elements not attached to the document.
Here's an approach that handles this situation. If it turns out the the element in question is not attached to the document, crawl up the tree to find the highest ancestor (the last non-null parentNode) and drop that into a DocumentFragment. Then from that DocumentFragment call querySelectorAll and see if the your element is in the resulting NodeList.
Here is the code.
The document
Here's a document structure we'll be working with. We'll grab the .element and test whether it matches the selectors li and .container *.
<!DOCTYPE html>
<html>
<body>
<article class="container">
<section>
<h1>Header 1</h1>
<ul>
<li>one</li>
<li>two</li>
<li>three</li>
</ul>
</section>
<section>
<h1>Header 2</h1>
<ul>
<li>one</li>
<li>two</li>
<li class="element">three</li>
</ul>
</section>
<footer>Footer</footer>
</article>
</body>
</html>
Searching with document.querySelectorAll
Here is a matchesSelector function that uses document.querySelectorAll.
// uses document.querySelectorAll
function matchesSelector(selector, element) {
var all = document.querySelectorAll(selector);
for (var i = 0; i < all.length; i++) {
if (all[i] === element) {
return true;
}
}
return false;
}
This works as long as that element is in the document.
// this works because the element is in the document
console.log("Part 1");
var element = document.querySelector(".element");
console.log(matchesSelector("li", element)); // true
console.log(matchesSelector(".container *", element)); // true
However, it fails if the element is removed from the document.
// but they don't work if we remove the article from the document
console.log("Part 2");
var article = document.querySelector("article");
article.parentNode.removeChild(article);
console.log(matchesSelector("li", element)); // false
console.log(matchesSelector(".container *", element)); // false
Searching within a DocumentFragment
The fix requires searching whatever subtree that element happens to be in. Here's an updated function named matchesSelector2.
// uses a DocumentFragment if element is not attached to the document
function matchesSelector2(selector, element) {
if (document.contains(element)) {
return matchesSelector(selector, element);
}
var node = element;
var root = document.createDocumentFragment();
while (node.parentNode) {
node = node.parentNode;
}
root.appendChild(node);
var all = root.querySelectorAll(selector);
for (var i = 0; i < all.length; i++) {
if (all[i] === element) {
root.removeChild(node);
return true;
}
}
root.removeChild(node);
return false;
}
Now we see that matchesSelector2 works even though the element is in a subtree that is detached from the
document.
// but they will work if we use matchesSelector2
console.log("Part 3");
console.log(matchesSelector2("li", element)); // true
console.log(matchesSelector2(".container *", element)); // true
You can see this working at jsfiddle.
Putting it all together
Here's the final implementation I came up with:
function is(element, selector) {
var node = element;
var result = false;
var root, frag;
// crawl up the tree
while (node.parentNode) {
node = node.parentNode;
}
// root must be either a Document or a DocumentFragment
if (node instanceof Document || node instanceof DocumentFragment) {
root = node;
} else {
root = frag = document.createDocumentFragment();
frag.appendChild(node);
}
// see if selector matches
var matches = root.querySelectorAll(selector);
for (var i = 0; i < matches.length; i++) {
if (this === matches.item(i)) {
result = true;
break;
}
}
// detach from DocumentFragment and return result
while (frag && frag.firstChild) {
frag.removeChild(frag.firstChild);
}
return result;
}
An important note is that jQuery's is implementation is much faster. The first optimization I would look into is avoiding crawling up the tree if we don't have to. To do this you could look at the right-most part of the selector and test whether this matches the element. However, beware that if the selector is actually multiple selectors separated by commas, then you'll have to test each one. At this point you're building a CSS selector parser, so you might as well use a library.
In the absence of xMatchesSelector, I'm thinking to try adding a style with the requested selector to a styleSheet object, along with some arbitrary rule and value that is not likely to be already in use. Then check the computed/currentStyle of the element to see if it has inherited the added CSS rule. Something like this for IE:
function ieMatchesSelector(selector, element) {
var styleSheet = document.styleSheets[document.styleSheets.length-1];
//arbitrary value, probably should first check
//on the off chance that it is already in use
var expected = 91929;
styleSheet.addRule(selector, 'z-index: '+expected+' !important;', -1);
var result = element.currentStyle.zIndex == expected;
styleSheet.removeRule(styleSheet.rules.length-1);
return result;
}
There's probably a handbag full of gotcha's with this method. Probably best to find some obscure proprietary CSS rule that is less likely to have a visual effect than z-index, but since it is removed almost immediately after it is set, a brief flicker should be the only side effect if that. Also a more obscure rule will be less likely to be overridden by a more specific selector, style attribute rules, or other !important rules (if IE even supports that). Anyway, worth a try at least.
The W3C selectors API (http://www.w3.org/TR/selectors-api/) specifies document.querySelectorAll(). This is not supported on all browsers, so you'd have to google the ones that do support it: http://www.google.com/search?q=browsers+implementing+selector+api
I'm dealing with this issue now. I have to support IE8 with native Javascript, which presents a curious challenge: IE8 supports both querySelector and querySelectorAll, but not matchesSelector. If your situation is similar, here's an option for you to consider:
When you're handed the DOM node and a selector, make a shallow copy of the node as well as its parent. This will preserve all of their attributes but won't make copies of their respective children.
Attach the cloned node to the cloned parent. Use querySelector on the cloned parent -- the only thing it needs to search is the only child node it has so this process is constant time. It will either return the child node or it won't.
That'd look something like this:
function matchesSelector(node, selector)
{
var dummyNode = node.cloneNode(false);
var dummyParent = node.parent.cloneNode(false);
dummyParent.appendChild(dummyNode);
return dummyNode === dummyParent.querySelector(selector);
}
It may be worth creating a complete chain of shallow-copied parents all the way up to the root node and querying the (mostly empty) dummy root if you'd like to be able to test your node's relationship to its ancestors.
Off the top of my head I'm not sure what portion of selectors this would work for, but I think
it'd do nicely for any that didn't worry about the tested node's children. YMMV.
-- EDIT --
I decided to write the function to shallow copy everything from the node being tested to root. Using this, a lot more selectors are employable. (Nothing related to siblings, though.)
function clonedToRoot(node)
{
dummyNode = node.cloneNode(false);
if(node.parentNode === document)
{
return {'root' : dummyNode, 'leaf' : dummyNode};
}
parent = clonedToRoot(node.parentNode).root;
parent.appendChild(dummyNode);
return {'root' : parent, 'leaf' : dummyNode};
}
function matchesSelector(node, selector)
{
testTree = clonedToRoot(node)
return testTree.leaf === testTree.root.querySelector(selector)
}
I'd welcome an expert to explain what kinds of selectors there are that this wouldn't cover!
Modern browsers can do it with the document.querySelectorAll function.
http://www.w3.org/TR/selectors-api/
Just use an id for your element? HTML-IDs have to be unique…

How to know if an element, created dynamically, exist?

I have this simple function:
var x = document.createTextNode("ERROR");
document.body.appendChild(x);
So then I need to create an IF to verify if this message exist [If this message has been created]. This is the problem, I don't know how to do that.
GetElementByID seems to don't work with element created by dynamically.
Any help? Thanks.
You can use document.contains to check if a element is in the DOM
Just a quick example of how it works
document.contains($('<div>')[0]); // FALSE
And
document.contains($('<div>').appendTo('body')[0]); // TRUE
jQuery only used for a shorthand to element creation
This also works for text nodes and you can use contains on any node.
document.body.contains(Node); // Example
The browser support is somewhat very good
Documentation: https://developer.mozilla.org/en-US/docs/Web/API/Node.contains
Question specifics:
var x = document.createTextNode("ERROR");
document.body.appendChild(x);
document.contains(x); // Should be TRUE
You are creating a text node, not an element. You need to create an element and give it an id to be able to use getElementById.
I don't know of any reasonable way to search for a text node, although you could always check the text nodes of the element you attached it to and see if it's there.
var message = "ERROR";
var t = document.createTextNode(message);
var node = document.getElementById('content').appendChild(t);
if (document.getElementById('content').innerHTML !== message) {
console.log('element not added');
} else {
console.log('element added');
}
Here is a fiddle:
http://jsfiddle.net/btipling/rBg4w/
I believe this would work:
var x = document.createTextNode("ERROR");
var element = document.body.appendChild(x); //returns text node
if(!element){
//element was not added
}
although if I were you I might create a span element with an id or a class called error. This way you can apply any css styles to it.
Try this:
var x = document.createTextNode("ERROR");
document.body.appendChild(x);
if (document.body.innerText.indexOf('ERROR')>=0){
alert('"ERROR" found');
}
indexOf doesn't work in all browsers.
As #slowpython said I'd rather create a DOM element with ID or NAME.

Remove all child elements of a DOM node in JavaScript

How would I go about removing all of the child elements of a DOM node in JavaScript?
Say I have the following (ugly) HTML:
<p id="foo">
<span>hello</span>
<div>world</div>
</p>
And I grab the node I want like so:
var myNode = document.getElementById("foo");
How could I remove the children of foo so that just <p id="foo"></p> is left?
Could I just do:
myNode.childNodes = new Array();
or should I be using some combination of removeElement?
I'd like the answer to be straight up DOM; though extra points if you also provide an answer in jQuery along with the DOM-only answer.
Option 1 A: Clearing innerHTML.
This approach is simple, but might not be suitable for high-performance applications because it invokes the browser's HTML parser (though browsers may optimize for the case where the value is an empty string).
doFoo.onclick = () => {
const myNode = document.getElementById("foo");
myNode.innerHTML = '';
}
<div id='foo' style="height: 100px; width: 100px; border: 1px solid black;">
<span>Hello</span>
</div>
<button id='doFoo'>Remove via innerHTML</button>
Option 1 B: Clearing textContent
As above, but use .textContent. According to MDN this will be faster than innerHTML as browsers won't invoke their HTML parsers and will instead immediately replace all children of the element with a single #text node.
doFoo.onclick = () => {
const myNode = document.getElementById("foo");
myNode.textContent = '';
}
<div id='foo' style="height: 100px; width: 100px; border: 1px solid black;">
<span>Hello</span>
</div>
<button id='doFoo'>Remove via textContent</button>
Option 2 A: Looping to remove every lastChild:
An earlier edit to this answer used firstChild, but this is updated to use lastChild as in computer-science, in general, it's significantly faster to remove the last element of a collection than it is to remove the first element (depending on how the collection is implemented).
The loop continues to check for firstChild just in case it's faster to check for firstChild than lastChild (e.g. if the element list is implemented as a directed linked-list by the UA).
doFoo.onclick = () => {
const myNode = document.getElementById("foo");
while (myNode.firstChild) {
myNode.removeChild(myNode.lastChild);
}
}
<div id='foo' style="height: 100px; width: 100px; border: 1px solid black;">
<span>Hello</span>
</div>
<button id='doFoo'>Remove via lastChild-loop</button>
Option 2 B: Looping to remove every lastElementChild:
This approach preserves all non-Element (namely #text nodes and <!-- comments --> ) children of the parent (but not their descendants) - and this may be desirable in your application (e.g. some templating systems that use inline HTML comments to store template instructions).
This approach wasn't used until recent years as Internet Explorer only added support for lastElementChild in IE9.
doFoo.onclick = () => {
const myNode = document.getElementById("foo");
while (myNode.lastElementChild) {
myNode.removeChild(myNode.lastElementChild);
}
}
<div id='foo' style="height: 100px; width: 100px; border: 1px solid black;">
<!-- This comment won't be removed -->
<span>Hello <!-- This comment WILL be removed --></span>
<!-- But this one won't. -->
</div>
<button id='doFoo'>Remove via lastElementChild-loop</button>
Bonus: Element.clearChildren monkey-patch:
We can add a new method-property to the Element prototype in JavaScript to simplify invoking it to just el.clearChildren() (where el is any HTML element object).
(Strictly speaking this is a monkey-patch, not a polyfill, as this is not a standard DOM feature or missing feature. Note that monkey-patching is rightfully discouraged in many situations.)
if( typeof Element.prototype.clearChildren === 'undefined' ) {
Object.defineProperty(Element.prototype, 'clearChildren', {
configurable: true,
enumerable: false,
value: function() {
while(this.firstChild) this.removeChild(this.lastChild);
}
});
}
<div id='foo' style="height: 100px; width: 100px; border: 1px solid black;">
<span>Hello <!-- This comment WILL be removed --></span>
</div>
<button onclick="this.previousElementSibling.clearChildren()">Remove via monkey-patch</button>
In 2022+, use the replaceChildren() API!
Replacing all children can now be done with the (cross-browser supported) replaceChildren API:
container.replaceChildren(...arrayOfNewChildren);
This will do both:
remove all existing children, and
append all of the given new children, in one operation.
You can also use this same API to just remove existing children, without replacing them:
container.replaceChildren();
This is fully supported in Chrome/Edge 86+, Firefox 78+, and Safari 14+. It is fully specified behavior. This is likely to be faster than any other proposed method here, since the removal of old children and addition of new children is done without requiring innerHTML, and in one step instead of multiple.
Use modern Javascript, with remove!
const parent = document.getElementById("foo")
while (parent.firstChild) {
parent.firstChild.remove()
}
This is a newer way to write node removal in ES5. It is vanilla JS and reads much nicer than relying on parent.
All modern browsers are supported.
Browser Support - 97% Jun '21
The currently accepted answer is wrong about innerHTML being slower (at least in IE and Chrome), as m93a correctly mentioned.
Chrome and FF are dramatically faster using this method (which will destroy attached jquery data):
var cNode = node.cloneNode(false);
node.parentNode.replaceChild(cNode, node);
in a distant second for FF and Chrome, and fastest in IE:
node.innerHTML = '';
InnerHTML won't destroy your event handlers or break jquery references, it's also recommended as a solution here:
https://developer.mozilla.org/en-US/docs/Web/API/Element.innerHTML.
The fastest DOM manipulation method (still slower than the previous two) is the Range removal, but ranges aren't supported until IE9.
var range = document.createRange();
range.selectNodeContents(node);
range.deleteContents();
The other methods mentioned seem to be comparable, but a lot slower than innerHTML, except for the outlier, jquery (1.1.1 and 3.1.1), which is considerably slower than anything else:
$(node).empty();
Evidence here:
http://jsperf.com/innerhtml-vs-removechild/167 http://jsperf.com/innerhtml-vs-removechild/300
https://jsperf.com/remove-all-child-elements-of-a-dom-node-in-javascript
(New url for jsperf reboot because editing the old url isn't working)
Jsperf's "per-test-loop" often gets understood as "per-iteration", and only the first iteration has nodes to remove so the results are meaningless, at time of posting there were tests in this thread set up incorrectly.
If you use jQuery:
$('#foo').empty();
If you don't:
var foo = document.getElementById('foo');
while (foo.firstChild) foo.removeChild(foo.firstChild);
var myNode = document.getElementById("foo");
var fc = myNode.firstChild;
while( fc ) {
myNode.removeChild( fc );
fc = myNode.firstChild;
}
If there's any chance that you have jQuery affected descendants, then you must use some method that will clean up jQuery data.
$('#foo').empty();
The jQuery .empty() method will ensure that any data that jQuery associated with elements being removed will be cleaned up.
If you simply use DOM methods of removing the children, that data will remain.
The fastest...
var removeChilds = function (node) {
var last;
while (last = node.lastChild) node.removeChild(last);
};
Thanks to Andrey Lushnikov for his link to jsperf.com (cool site!).
EDIT: to be clear, there is no performance difference in Chrome between firstChild and lastChild. The top answer shows a good solution for performance.
Use elm.replaceChildren().
It’s experimental without wide support, but when executed with no params will do what you’re asking for, and it’s more efficient than looping through each child and removing it. As mentioned already, replacing innerHTML with an empty string will require HTML parsing on the browser’s part.
MDN Documentation
Update It's widely supported now
If you only want to have the node without its children you could also make a copy of it like this:
var dupNode = document.getElementById("foo").cloneNode(false);
Depends on what you're trying to achieve.
Ecma6 makes it easy and compact
myNode.querySelectorAll('*').forEach( n => n.remove() );
This answers the question, and removes “all child nodes”.
If there are text nodes belonging to myNode they can’t be selected with CSS selectors, in this case we’ve to apply (also):
myNode.textContent = '';
Actually the last one could be the fastest and more effective/efficient solution.
.textContent is more efficient than .innerText and .innerHTML, see: MDN
Here's another approach:
function removeAllChildren(theParent){
// Create the Range object
var rangeObj = new Range();
// Select all of theParent's children
rangeObj.selectNodeContents(theParent);
// Delete everything that is selected
rangeObj.deleteContents();
}
element.textContent = '';
It's like innerText, except standard. It's a bit slower than removeChild(), but it's easier to use and won't make much of a performance difference if you don't have too much stuff to delete.
Here is what I usually do :
HTMLElement.prototype.empty = function() {
while (this.firstChild) {
this.removeChild(this.firstChild);
}
}
And voila, later on you can empty any dom element with :
anyDom.empty()
In response to DanMan, Maarten and Matt. Cloning a node, to set the text is indeed a viable way in my results.
// #param {node} node
// #return {node} empty node
function removeAllChildrenFromNode (node) {
var shell;
// do not copy the contents
shell = node.cloneNode(false);
if (node.parentNode) {
node.parentNode.replaceChild(shell, node);
}
return shell;
}
// use as such
var myNode = document.getElementById('foo');
myNode = removeAllChildrenFromNode( myNode );
Also this works for nodes not in the dom which return null when trying to access the parentNode. In addition, if you need to be safe a node is empty before adding content this is really helpful. Consider the use case underneath.
// #param {node} node
// #param {string|html} content
// #return {node} node with content only
function refreshContent (node, content) {
var shell;
// do not copy the contents
shell = node.cloneNode(false);
// use innerHTML or you preffered method
// depending on what you need
shell.innerHTML( content );
if (node.parentNode) {
node.parentNode.replaceChild(shell, node);
}
return shell;
}
// use as such
var myNode = document.getElementById('foo');
myNode = refreshContent( myNode );
I find this method very useful when replacing a string inside an element, if you are not sure what the node will contain, instead of worrying how to clean up the mess, start out fresh.
Using a range loop feels the most natural to me:
for (var child of node.childNodes) {
child.remove();
}
According to my measurements in Chrome and Firefox, it is about 1.3x slower. In normal circumstances, this will perhaps not matter.
There are couple of options to achieve that:
The fastest ():
while (node.lastChild) {
node.removeChild(node.lastChild);
}
Alternatives (slower):
while (node.firstChild) {
node.removeChild(node.firstChild);
}
while (node.hasChildNodes()) {
node.removeChild(node.lastChild);
}
Benchmark with the suggested options
var empty_element = function (element) {
var node = element;
while (element.hasChildNodes()) { // selected elem has children
if (node.hasChildNodes()) { // current node has children
node = node.lastChild; // set current node to child
}
else { // last child found
console.log(node.nodeName);
node = node.parentNode; // set node to parent
node.removeChild(node.lastChild); // remove last node
}
}
}
This will remove all nodes within the element.
A one-liner to iteratively remove all the children of a node from the DOM
Array.from(node.children).forEach(c => c.remove())
Or
[...node.children].forEach(c => c.remove())
innerText is the winner! http://jsperf.com/innerhtml-vs-removechild/133. At all previous tests inner dom of parent node were deleted at first iteration and then innerHTML or removeChild where applied to empty div.
Simplest way of removing the child nodes of a node via Javascript
var myNode = document.getElementById("foo");
while(myNode.hasChildNodes())
{
myNode.removeChild(myNode.lastChild);
}
let el = document.querySelector('#el');
if (el.hasChildNodes()) {
el.childNodes.forEach(child => el.removeChild(child));
}
i saw people doing:
while (el.firstNode) {
el.removeChild(el.firstNode);
}
then someone said using el.lastNode is faster
however I would think that this is the fastest:
var children = el.childNodes;
for (var i=children.length - 1; i>-1; i--) {
el.removeNode(children[i]);
}
what do you think?
ps:
this topic was a life saver for me. my firefox addon got rejected cuz i used innerHTML. Its been a habit for a long time. then i foudn this. and i actually noticed a speed difference. on load the innerhtml took awhile to update, however going by addElement its instant!
Why aren't we following the simplest method here "remove" looped inside while.
const foo = document.querySelector(".foo");
while (foo.firstChild) {
foo.firstChild.remove();
}
Selecting the parent div
Using "remove" Method inside a While loop for eliminating First child element , until there is none left.
Generally, JavaScript uses arrays to reference lists of DOM nodes. So, this will work nicely if you have an interest in doing it through the HTMLElements array. Also, worth noting, because I am using an array reference instead of JavaScript proto's this should work in any browser, including IE.
while(nodeArray.length !== 0) {
nodeArray[0].parentNode.removeChild(nodeArray[0]);
}
Just saw someone mention this question in another and thought I would add a method I didn't see yet:
function clear(el) {
el.parentNode.replaceChild(el.cloneNode(false), el);
}
var myNode = document.getElementById("foo");
clear(myNode);
The clear function is taking the element and using the parent node to replace itself with a copy without it's children. Not much performance gain if the element is sparse but when the element has a bunch of nodes the performance gains are realized.
Functional only approach:
const domChildren = (el) => Array.from(el.childNodes)
const domRemove = (el) => el.parentNode.removeChild(el)
const domEmpty = (el) => domChildren(el).map(domRemove)
"childNodes" in domChildren will give a nodeList of the immediate children elements, which is empty when none are found. In order to map over the nodeList, domChildren converts it to array. domEmpty maps a function domRemove over all elements which removes it.
Example usage:
domEmpty(document.body)
removes all children from the body element.
You can remove all child elements from a container like below:
function emptyDom(selector){
const elem = document.querySelector(selector);
if(elem) elem.innerHTML = "";
}
Now you can call the function and pass the selector like below:
If element has id = foo
emptyDom('#foo');
If element has class = foo
emptyDom('.foo');
if element has tag = <div>
emptyDom('div')
element.innerHTML = "" (or .textContent) is by far the fastest solution
Most of the answers here are based on flawed tests
For example:
https://jsperf.com/innerhtml-vs-removechild/15
This test does not add new children to the element between each iteration. The first iteration will remove the element's contents, and every other iteration will then do nothing.
In this case, while (box.lastChild) box.removeChild(box.lastChild) was faster because box.lastChild was null 99% of the time
Here is a proper test: https://jsperf.com/innerhtml-conspiracy
Finally, do not use node.parentNode.replaceChild(node.cloneNode(false), node). This will replace the node with a copy of itself without its children. However, this does not preserve event listeners and breaks any other references to the node.
Best Removal Method for ES6+ Browser (major browsers released after year 2016):
Perhaps there are lots of way to do it, such as Element.replaceChildren().
I would like to show you an effective solution with only one redraw & reflow supporting all ES6+ browsers.
function removeChildren(cssSelector, parentNode){
var elements = parentNode.querySelectorAll(cssSelector);
let fragment = document.createDocumentFragment();
fragment.textContent=' ';
fragment.firstChild.replaceWith(...elements);
}
Usage: removeChildren('.foo',document.body);: remove all elements with className foo in <body>
If you want to empty entire parent DOM then it's very simple...
Just use .empty()
function removeAll() {
$('#parent').empty();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button onclick="removeAll()">Remove all the element of parent</button>
<div id="parent">
<h3>Title</h3>
<p>Child 1</p>
<p>Child 2</p>
<p>Child 3</p>
</div>

How to set DOM element as first child?

I have an element E and I'm appending some elements to it. All of a sudden, I find out that the next element to append should be the first child of E. What's the trick, how to do it? Method unshift doesn't work because E is an object, not array.
Long way would be to iterate through E's children and to move'em key++, but I'm sure that there is a prettier way.
var eElement; // some E DOM instance
var newFirstElement; //element which should be first in E
eElement.insertBefore(newFirstElement, eElement.firstChild);
2018 version - prepend
parent.prepend(newChild) // [newChild, child1, child2]
This is modern JS! It is more readable than previous options. It is currently available in Chrome, FF, and Opera.
The equivalent for adding to the end is append, replacing the old appendChild
parent.append(newChild) // [child1, child2, newChild]
Advanced usage
You can pass multiple values (or use spread operator ...).
Any string value will be added as a text element.
Examples:
parent.prepend(newChild, "foo") // [newChild, "foo", child1, child2]
const list = ["bar", newChild]
parent.append(...list, "fizz") // [child1, child2, "bar", newChild, "fizz"]
Related DOM methods
Read More - child.before and child.after
Read More - child.replaceWith
Mozilla Documentation
Can I Use
2017 version
You can use
targetElement.insertAdjacentElement('afterbegin', newFirstElement)
From MDN :
The insertAdjacentElement() method inserts a given element node at a given position relative to the element it is invoked upon.
position
A DOMString representing the position relative to the element; must be one of the following strings:
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.
element
The element to be inserted into the tree.
In the family of insertAdjacent there is the sibling methods:
element.insertAdjacentHTML('afterbegin','htmlText')`
That can inject html string directly, like innerHTML but without override everything, so you can use it as a mini-template Engin and jump the oppressive process of document.createElement and even build a whole component with string manipulation process
element.insertAdjacentText for inject sanitize string into element . no more encode/decode
You can implement it directly i all your window html elements.
Like this :
HTMLElement.prototype.appendFirst = function(childNode) {
if (this.firstChild) {
this.insertBefore(childNode, this.firstChild);
}
else {
this.appendChild(childNode);
}
};
Accepted answer refactored into a function:
function prependChild(parentEle, newFirstChildEle) {
parentEle.insertBefore(newFirstChildEle, parentEle.firstChild)
}
Unless I have misunderstood:
$("e").prepend("<yourelem>Text</yourelem>");
Or
$("<yourelem>Text</yourelem>").prependTo("e");
Although it sounds like from your description that there is some condition attached, so
if (SomeCondition){
$("e").prepend("<yourelem>Text</yourelem>");
}
else{
$("e").append("<yourelem>Text</yourelem>");
}
I think you're looking for the .prepend function in jQuery. Example code:
$("#E").prepend("<p>Code goes here, yo!</p>");
I created this prototype to prepend elements to parent element.
Node.prototype.prependChild = function (child: Node) {
this.insertBefore(child, this.firstChild);
return this;
};
var newItem = document.createElement("LI"); // Create a <li> node
var textnode = document.createTextNode("Water"); // Create a text node
newItem.appendChild(textnode); // Append the text to <li>
var list = document.getElementById("myList"); // Get the <ul> element to insert a new node
list.insertBefore(newItem, list.childNodes[0]); // Insert <li> before the first child of <ul>
https://www.w3schools.com/jsref/met_node_insertbefore.asp

Categories

Resources