Is there a handy way to "touch" a DOM element? I'd like to remove the element and insert it again at the same position. Something like this:
element.parentNode.removeChild(element).appendChild(element);
except that appendChild inserts the element as the last sibling.
Use insertBefore instead of appendChild.
var other = element.nextSibling;
if ( other ) {
other.parentNode.removeChild(element);
other.parentNode.insertBefore(element,other);
} else {
other = element.parentNode;
other.removeChild(element);
other.appendChild(element);
}
This creates a dummy text node to be used as a marker and replaces it with the node. Later when the node is to be re-inserted, replace it with the dummy node so the position is preserved.
Node.replaceChild
var dummy = document.createTextNode('');
var parent = element.parentNode;
parent.replaceChild(dummy, element); // replace with empty text node
parent.replaceChild(element, dummy); // swap out empty text node for original
Yes but it would be better to use the DOM cloneNode(true) as it would retain all of the child nodes and properties:
// Copy the node.
var theOldChild = document.getElementById("theParent").childNodes[blah]
var theNewChild = theOldChild.cloneNode(true);
// Find the next Sibling
var nextSib = theOldChild.nextSibling();
// Remove the old Node
theOldChild.parentNode.removeChild(theOldChild)
// Append where it was.
nextSib.parentNode.inserertBefore(theNewChild, nextSib);
That is the way that I would do it as you can hold onto the variable "theNewChild" 100% as it was and insert it back into the document at any time.
Related
I just notice I couldn't do
var d1 = document.createElement('div');
var d2 = document.createElement('div');
var p = document.createElement('p');
d1.appendChild(p); // d1 has p now
d2.appendChild(p); // d2 has p now
// but where is p in d1 ?
Some would say it's logic, but well, when I first noticed that I thought how uncool it was.
Why isn't that possible ?
The DOM is a tree structure.
When you append an element, you change its parent.
A node, in the browser, is much more than just the text inside your P (that string could be shared, in fact). It also has a position, dimensions, a visibility, receives events that could have been fired in child elements, propagate events to its parent, and so on. Everything here depends on the position in the tree. Just like would many CSS selectors. It doesn't make a lot of sense to imagine it's the same element at two places, it's better to think about it as two nodes, with maybe some identical content.
If you want to have the same content at two places, you have to clone it.
jQuery's appendTo() method inserts "every element in the set of matched elements to the end of the target". Try this:
p.appendTo(".div-class1, .div-class2")
for AppendChild same element multiple times , we can use this way :
//main function
function appendChildMultiple(parent) {
//check function argument is an element
if (parent.nodeType !== undefined) {
const pTag = document.createElement("p");
pTag.innerText = "This is the appended element";
//finally append child to parent
parent.appendChild(pTag);
}
}
and :
// target the wrapper and create test elements
const wrapper = document.querySelector(".wrapper");
const d1 = document.createElement("div");
const d2 = document.createElement("div");
//append test elements to wrapper
wrapper.appendChild(d1);
wrapper.appendChild(d2);
//use appendChildMultiple function
appendChildMultiple(d1);
appendChildMultiple(d2);
//we appended "pTag" multiple times
if we use Functions , we can AppendChild same element multiple times whitout cloneNode
https://codepen.io/kiumad/pen/eYMNKYa
When I run this script, the page is continuously loading and eventually freezes. Is this because everytime I create an element, the main DOMContentLoaded listener is being called?
If so, how can I stop this recursive behaviour and just add one node to every pre existing node?
//Waits for page to load
document.addEventListener('DOMContentLoaded', function() {
//Get all elements
var items = document.getElementsByTagName("*");
//Loop through entire DOM
for (var i = 0; i < items.length; i++) {
//If it is not a text node
if (!(items[i].nodeType == 3)){
//Create a div
var newDiv = document.createElement("div");
//Add div to current object
items[i].appendChild(newDiv);
}
}
});
It's because items is referencing a "live list". This means that any updates to the DOM are going to be reflected in your list if they match the original selector.
Because you're appending a div, and your selector selects all elements, it gets added to the list, pushing any subsequent members up an index, and so the iteration continues.
To avoid this, make a non-live copy of the collection before iterating.
var items = Array.from(document.getElementsByTagName("*"));
And FYI, the if (!(items[i].nodeType == 3)){ can be removed because getElementsByTagName will never return text nodes.
If you're supporting very old versions of IE, you may want to check that the .nodeType === 1, since some of those old versions included comment nodes when using "*".
Lastly, you can use modern features to clean this up a bit.
document.addEventListener('DOMContentLoaded', () => {
for (const el of [...document.getElementsByTagName("*")]) {
var newDiv = el.appendChild(document.createElement("div"));
// Work with newDiv
}
});
You get a continuously loading page because you have this:
var items = document.getElementsByTagName("*");
Which returns a "live" node list - meaning a list that can/will change as the matched elements change. Since your selector is for everything and then you create new elements, the set of matched elements changes (it gets bigger). Which, in turn causes the length of the node list to change, which keeps your loop running.
You should not try to match all the elements or you should use another method for getting the elements that doesn't return a live node list, like querySelectoAll()
I am making a game in JavaScript, and need an event log. If i attack, it tells me if i hit or i miss. Here is my code:
function eventlogshow (text){
var para = document.createElement("p");
var node = document.createTextNode(text);
para.appendChild(node);
var element = document.getElementById("eventlog");
element.appendChild(para);
}
It lists the most recent event on the bottom, with the oldest on top. How do i reverse that?
I would like it to show the most recent event on the top.
Prepend the child element instead. Since there is no prependChild() function, you need to "insert it before the first child":
function eventlogshow (text){
var para = document.createElement("p");
var node = document.createTextNode(text);
para.appendChild(node);
var element = document.getElementById("eventlog");
element.insertBefore(para, element.firstChild);
}
A similar question has been asked here: How to set DOM element as first child?.
Read more about Node.firstChild and Node.insertBefore()
appendChild adds a node as a last child. You want to insert before the first node:
element.insertBefore(para, element.childNodes[0]);
I want to get the textContent of the current node but not of any of the descendant nodes. Any ideas as to how I might do that?
textContent does the later. nodeValue returns an empty string.
Not sure if there is an easier way, but you can always iterate over all node's children and get value of text nodes.
var text = "";
var child = element.firstChild;
while(child) {
if (child.nodeType === 3) { // nodeType === Node.TEXT_NODE
text += child.nodeValue;
}
child = child.nextSibling;
}
console.log(text);
http://jsfiddle.net/zqbyE/
I had the same issue and solved it by getting the textContent of the first child, which actually is the text. The code therefore becomes :
node.firstChild.textContent
I just created a span within the current node that contains that node's specific textContent and am getting the txt for that. I dunno... seems easier than going over all the node's children. Thanks for the suggestion though!
function countChars(elm) {
if (elm.nodeType == 3) { // TEXT_NODE
return elm.nodeValue.length;
}
var count = 0;
for (var i = 0, child; child = elm.childNodes[i]; i++) {
count += countChars(child);
}
return count;
}
I tried passing this function a string like countChars("hello"); but that didn't work. What are examples of elements that I can pass?
It expects a reference to a DOM node. That could be a text node (nodeType == 3) or an element node (nodeType == 1) by the look of the function. For example:
countChars(document.getElementById("someId"));
The following HTML would cause the above call to return 5:
<span id="someId">Hello</span>
If the argument is a text node the function returns the number of characters that make up that node. If the argument is an element node the function recursively counts the number of characters that make up the descendant text nodes of the element.
The following HTML would cause the above call to return 10 (the child node is included):
<div id="someId">Outer<span>inner</span></div>
You can see the full list of node types on MDN.
It expects DOM nodes, like something you'd get from document.getElementById() or the list of child nodes on another DOM node.
What it does is find all the text nodes in the list of child nodes of an element and count up how many characters they contain. It does it recursively, so it will find all the text within a container, no matter how far separated in the element graph.
Also it should be noted that this code expects a node to be either a text node or else an element. There are other types of nodes, however, most notably comment nodes.
Looks like you should pass a dom element.
An element (node, actually, hence why it checks if nodeType is a text node), e.g.:
countChars(document.getElementById("myid"));
You can get nodeType over a DOM fragment, so... you must enter a DOM element as input
https://developer.mozilla.org/en/nodeType
The example from that link shows it
var node = document.documentElement.firstChild;
if(node.nodeType != Node.COMMENT_NODE)
alert("You should comment your code well!");