Can somebody explain in simple terms, what is the difference between classical DOM parentNode and newly introduced in Firefox 9 parentElement
parentElement is new to Firefox 9 and to DOM4, but it has been present in all other major browsers for ages.
In most cases, it is the same as parentNode. The only difference comes when a node's parentNode is not an element. If so, parentElement is null.
As an example:
document.body.parentNode; // the <html> element
document.body.parentElement; // the <html> element
document.documentElement.parentNode; // the document node
document.documentElement.parentElement; // null
(document.documentElement.parentNode === document); // true
(document.documentElement.parentElement === document); // false
Since the <html> element (document.documentElement) doesn't have a parent that is an element, parentElement is null. (There are other, more unlikely, cases where parentElement could be null, but you'll probably never come across them.)
In Internet Explorer, parentElement is undefined for SVG elements, whereas parentNode is defined.
Use .parentElement and you can't go wrong as long as you aren't using document fragments.
If you use document fragments, then you need .parentNode:
let div = document.createDocumentFragment().appendChild(document.createElement('div'));
div.parentElement // null
div.parentNode // document fragment
Also:
let div = document.getElementById('t').content.firstChild
console.log(div.parentElement) // null
console.log(div.parentNode) // document fragment
<template id="t"><div></div></template>
Apparently the <html>'s .parentNode links to the Document. This should be considered a decision phail as documents aren't nodes since nodes are defined to be containable by documents and documents can't be contained by documents.
Just like with nextSibling and nextElementSibling, just remember that, properties with "element" in their name always returns Element or null. Properties without can return any other kind of node.
console.log(document.body.parentElement, "is body's parent element");//<html>
console.log(document.body.parentNode, "is body's parent node"); //<html>
var html = document.body.parentElement;
console.log(html.parentElement, "is html's parent element"); //null
console.log(html.parentNode, "is html's parent node"); //document
there is one more difference, but only in internet explorer. It occurs when you mix HTML and SVG. if the parent is the 'other' of those two, then .parentNode gives the parent, while .parentElement gives undefined.
Related
Is there a convenient way to check if an HTMLElement is an offsetParent?
I have a situation where I need to determine an element's offsetParent before it is inserted in the DOM. I can access the element's immediate parent, before insertion.
There doesn't seem to be any properties on HTMLElements that indicate whether or not it is an offsetParent.
Is there a good way to do this?
There is to my knowledge unfortunately nothing in the DOM API that does expose this information on the Element itself.
According to specs, an ancestor can be an offsetParent if
The element is a containing block of absolutely-positioned descendants
This means that not only positioned elements will qualify, but any element with a transform, or a filter property, a will-change with a value containing any of the aforementioned ones will also do.
However this behavior was not always specified this way, so it may return false positives in some browsers.
Also, it may be that in the future other CSS properties will affect what makes a containing block, or even in the present since I only got these from the tip of my head...
With that in mind, the surest is to append a test element inside your element and to check its offsetParent.
However, this will create forced reflows, so use it sporadically.
document.querySelectorAll('.container > div')
.forEach(elem => {
elem.textContent = isOffsetParent(elem) ? 'offsetParent' : 'not offsetParent';
});
function isOffsetParent(elem) {
const test = document.createElement('span');
elem.appendChild(test);
const result = test.offsetParent === elem;
elem.removeChild(test);
return result;
}
<div class="container">
<div class="is-offset-parent" style="position:relative"></div>
<div class="can-be-offset-parent" style="transform:translate(0)"></div>
<div class="can-be-offset-parent" style="filter:blur(1px)"></div>
<div class="is-not"></div>
</div>
But if you really wish some unsafe way which may need to be updated, then you could check all the properties I mentioned before using getComputedStyle(elem).
Assume I have an element in a variable:
var element = document.getElementsByTagName("div")[0]
// here can be any kind of getting element, e. g. React ref, Chrome's devtools $0, etc.
At some point of time my markup is changing (like in SPA), and element from variable has been removed from DOM, but it still available in the element with all properties, such as parentElement, etc.
The question is: how to check, if my DOM element from element is present in DOM?
I tried to check the element.getBoundingClientRect(), and yes, there are some differences: element that removed from DOM has all the zeroes in his bounding rect. But there is one thing: element with display: none also has all the zeroes in its bounding rect, despite of it is still presents in the DOM (physically, lets say). This is not acceptable in my case, because I need to differ hidden element from removed element.
You can use contains for this purpose
function contains() {
const result = document.body.contains(element);
console.log(result);
}
const element = document.getElementById('app');
contains();
element.classList.add('hide');
contains();
element.parentNode.removeChild(element);
contains();
.hide {
display: none;
}
<div id="app">App</div>
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
I am used to using jQuery for manipulating the DOM, for example:
var mything = $("#mything");
mything.on("click", function() {
mything.addClass("red");
mything.html("I have sinned.");
});
But now I want to do the same things with Vanilla JavaScript. Is this possible? How can I do it?
Notice: This question is intended to be a comprehensive resource on Vanilla JavaScript DOM manipulation.
When manipulating the Document Object Model with Vanilla JS, you will be directly be accessing the Document and Nodes. A document contains Elements, particularly HTMLElements and SVGElements which are both Nodes. An Element may contain Text too.
Finding Elements
You can get the first element which matches a CSS selector with mynode.querySelector(), and all elements that match the selector with myNode.querySelectorAll(). Most of the time myNode will be Document, so you can get anything in the document which matches the selector – however, you can look through only a node's descendants when myNode is a an element.
document.querySelectorAll('p:hover'); // Returns a NodeList of hovered paragraphs
This is similar to jQuery('p:hover').
There are also more specialized methods like:
myNode.getElementById()
myNode.getElementsByTagName()
myNode.getElementsByClassName()
myNode.getElementsByName()
Which have self-explanatory names. Notice that .getElementBy... returns a single element while .getElementsBy... (plural elements) returns a NodeList, which is essentially an array of nodes, but it doesn't have the standard array methods.
See also: What's the best way to loop through a set of elements in JavaScript?
Each element may also have a:
parentNode
previousSibling
previousElementSibling (excludes text nodes)
nextSibling
nextElementSibling (excludes text nodes)
firstChild
firstElementChild (excludes text nodes)
lastChild
lastElementChild (excludes text nodes)
childElementCount (same as children.length)
And NodeLists of:
childNodes
children (excludes text nodes)
In this way, we can traverse the DOM.
For example, to get the last child of the first paragraph element in the parent of #clickme here:
document.getElementById('clickme').addEventListener('click', function() {
console.log(this.parentNode.getElementsByTagName('p')[0].lastChild);
});
<div>
<blockquote>This is a really great quote.</blockquote>
<p>This is a <em>really</em> interesting paragraph. <span>this will be selected</span></p>
<p>In fact, here's another!</p>
<button id="clickme">Click me!</button>
</div>
...you find its parentNode, use getElementsByTagName on that to only get paragraph descendants, take the first one of those, and get its lastChild.
To get the text contained in it, you could get its text node (its first child) then use text.wholeText.
Creating & Deleting
You can create an element with document.createElement('aTagName') or clone another one with newElement = myElement.cloneNode(). Pass cloneNode true as it's first argument to also duplicate its descendants. Don't clone elements with an ID because it will cause 2 elements with the same ID to appear in the same document.
You can then append the new element (or an existing one) to a parent element using parent.appendChild(newElement) or append it after another element with parent.insertBefore(newElement, referenceElement). An insertAfter method doesn't exist, but it can be created:
HTMLElement.prototype.insertAfter = function(newEl, refEl) {
if (refEl.nextSibling) refEl.parentNode.insertBefore(newEl, refEl.nextSibling);
else refEl.parentNode.appendChild(newEl);
};
A node can be removed with parent.removeChild() or replaced with parent.replaceChild(newChild) or just removed inline with mynode.remove().
function poof() {
this.remove();
}
var elements = document.getElementsByClassName('poof');
for (var i = 0; i < elements.length; i++) {
elements[i].addEventListener('click', poof);
}
<span class="poof">hi,</span>
<span class="poof">click</span>
<span class="poof">to</span>
<span class="poof">delete</span>
<span class="poof">me;</span>
<span class="poof">it</span>
<span class="poof">was</span>
<span class="poof">fun</span>
<span class="poof">being</span>
<span class="poof">a</span>
<span class="poof">span</span>
Classes and styles
By "and styles," I mean just classes. Styles are for CSS. You can apply CSS styles only to elements who have had a class added with JavaScript.1
Elements in HTML have a classList property which is a DOMTokenList representing a space-separated property, in this case class. You can .add(), .remove(), and .toggle() classes in the classList or check if it .contains() a class.
document.getElementById('clickme').addEventListener('click', function() {
document.getElementById('colors').classList.toggle('green');
});
.green { color: green }
<div id="colors">hello!</div>
<button id="clickme">Click me!</button>
Attributes
Elements with certain attributes can be selected with querySelector and querySelectorAll. Most attributes are properties of the element you're working with already. For example:
myDiv.hidden = true; // Hides element from view and from screenreaders
But if they're not, any attribute can be accessed with getAttributeNode, setAttributeNode, and removeAttributeNode. AttributeNodes have ownerElements and values.
"data-*" attributes can be accessed with myelement.dataset. For example, mydiv.dataset.pie = 'yummy' would add data-pie="yummy" to the div.
Events
Events are slightly more complicated. Binding one (like jQuery('selector').on) is pretty easy:
myElement.addEventListener('event-name', afunction);
(Other objects also have this method – for example, window)
Events can also be removed:
myelement.removeEventListener('event-name', afunction);
See: removeEventListener
An event list can be found here.
The function passed to the addEventListener will be passed an argument of the event occurring and have a this of the element the event listener is bound to.
However, events aren't this simple: something as trivial as clicking on a button may fire many event listeners on different elements and for different events.
– Browser Input Events: Can We Do Better Than The Click? by Smashing Magazine
See also: What is event bubbling and capturing?
1 If you really need to modify a style with JS, use myElement.style.styleProperty = 'value' to change the inline style attribute.
I have a function for my rpg game that simulates grabbing an element in a create js container object by removing it from the container (thereby removing it from the stage) when the player gets near it.
function grabIt(NPC_id, index) {
console.log(ContainerOfAnimals.children[index].id);
var childToRemove = document.getElementById(ContainerOfAnimals.children[index]);
console.log(childToRemove);
ContainerOfAnimals.removeChild(childToRemove);
}
The first console.log gives correct id of child: 21
But when I want to grab the child container object using getElementById, the child is null.
Why is this?
EaselJS elements have an id property but there aren't DOM elements. They're plain JavaScript objects (instances of a subclass of DisplayObject). And they're not added to the DOM tree.
Therefore, you can't get them by using document.getElementById.
To remove your element, simply do
ContainerOfAnimals.removeChild(ContainerOfAnimals.children[index]);
or (faster)
ContainerOfAnimals.removeChildAt(index);
Why does the author of Test-Driven JavaScript Development (Christian Johansen) use the while statement instead of the if statement in the code below?
function getEventTarget(event) {
var target = event.target || event.srcElement;
while (target && target.nodeType != 1) {
target = target.parentNode;
}
return target;
}
Because the author wanted to keep walking up the tree until the correct node type was found; it might not be the immediate parent.
However, in this case it makes no sense, as parentNode will always return an element in real-world usage (or a Document).
Because he is walking up..
If you see pretty well, in the loop he is assigning the target again with its parent and the parent is not nodetype 1
target = target.parentNode;
I don't know what is he trying or what is the purpose or the goal but it's quite simple..
Imagine the DOM
<div>
<div>
<div>
<div>
Imagine he starts from here.. he will always get the max parent with not nodetype 1 the hightes parent so the first div..
</div>
</div>
</div>
</div>
SO basically. He is getting the Higher parent ... That's why is he looping.. if uses the If.. he will get just the First parent