We are able to run this code
str.parentNode.nextSibling.tagName
and get the name accordingly. What we need is the nextSibling after this nextSibling so we tried this
str.parentNode.childNodes[3].tagName
is not working? Any solution to this?
childNodes[3] will return the fourth child of parentNode, not its second next sibling.
To get that node, you can apply nextSibling twice:
var tagName = str.parentNode.nextSibling.nextSibling.tagName;
Note that an element's nextSibling might be a text node, which doesn't have a tagName property, or may not exist at all, in which case nextSibling will be undefined and trying to access its tagName property will throw an error.
So you likely want do to something like:
var nextSibling = getNextElementSibling(str.parentNode);
var tagName = nextSibling? nextSibling.tagName : ''; // or maybe undefined
if (tagName) {
// do stuff
} else {
// no element sibling was found
}
and the getNextElementSibling function is something like:
function getNextElementSibling(node) {
while (node && (node = node.nextSibling)) {
if (node.nodeType == 1) {
return node;
}
}
// return undefined
}
Related
I am surprised there isn't some cookbook code ready that enumerates or searches along the preceding-axis in a DOM with javascript the way that XPath can do it. And of course, the following-axis as well.
Example problem is: I have a text node, and I want to find the preceding text node.
Obviously the precedingSibling is insufficient, because there could be none, or it could be an element.
I have implemented that several times for myself, but am too lazy to try to find my code. Instead I prefer doing it over again. But also like to compare notes, and surprised that nobody seems to ask about it, or show off theirs.
So here is a function signature with some beginning scribble:
Node.prototype.findPrevious function(foundPr = x => x, giveUpPr = () => false) {
node = this;
while(true) {
let previousSibling = node.previousSibling;
if(!previousSibling) // no previousSibling means we need to go up
while(true) {
const parent = node.parentNode;
previousSibling = parent.previousSibling;
if(parent.previousSibling)
break;
node = parent;
if(!node || giveUpPr(node))
return null;
}
// now we have the nearest previous sibling (or we gave up already)
node = previousSibling;
// but we aren't done, we have to go down now
// now finally we have the last previous item
while(true) {
let lastChild = node.lastChild;
if(!lastChild)
break;
node = lastChild;
}
// now our node is the deepest lastChild that doesn't have any more children
// now only can we check our found predicate:
if(foundPr(node))
return node;
// and if not we go to the beginning
}
};
I guess I just wrote the code instead of explaining what it would do. And the cool thing is that the same logic works the other way around for findNext:
Node.prototype.findNext = function(foundPr = x => x, giveUpPr = () => false) {
let node = this;
while(true) {
let nextSibling = node.nextSibling;
if(!nextSibling) // no nextSibling means we need to go up
while(true) {
const parent = node.parentNode;
nextSibling = parent.nextSibling;
if(nextSibling)
break;
node = parent;
if(!node || giveUpPr(node))
return null;
}
// now we have the nearest next sibling (or we gave up already)
node = nextSibling;
// but we aren't done, we have to go down now
// now finally we have the first next item
while(true) {
let firstChild = node.firstChild;
if(!firstChild)
break;
node = firstChild;
}
// now our node is the deepest firstChild that doesn't have any more children
// now only can we check our found predicate:
if(foundPr(node))
return node;
// and if not we go to the beginning trying the nextSibling
}
};
But isn't there some built-in solution already?
Working on a JS function: How would I check to see if a parent element exists and if it doesn't print out 'not found'. Also how do I check for a parent element with a certain className?
var findParentByClassName = function(element, targetClass) {
if (element) {
var currentParent = element.parentElement;
while (currentParent.className !== targetClass && currentParent.className !== null) {
currentParent = currentParent.parentElement;
} // closes loop
return currentParent;
} // closes if statement
};
I was thinking to write this:
if(element.parentElement !== targetClass) {
console.log('Parent not found');
}
This can be an example of closest() and get() Jquery functions. Of course everything in the DOM has a parent, so we will check for something special, a form with class="myclass":
var element = $(yourElement).closest('form.myclass');
if(element.get(0)){
//you got your element
}
else{
console.log('not found');
}
The method get([index]) is needed due, the selector won't return empty.
If you want to check if immediate parent has certain class, you can do it several ways:
Pure JavaScript (best performance):
if (element.parentElement.classList.contains('nameOfClass')) {}
jQuery (best jQuery performance):
if ($(element).parent().hasClass('nameOfClass')) {}
jQuery (usable for every selector, not just class, worse performance):
if ($(element).parent('.nameOfClass').length) {}
I want to know, how to find out recursively all parent nodes of an element.
Suppose i have following snippet
<font>Hello</font>
In this I would like to find out whether font tag's parent node is an anchor tag or not.
Now this can be achieved by simply checking .parentNode property. But what if there are following cases like,
<font><b>Hello<b></font>
or
<font><b><u>Hello</u><b></font>
So, basically, how to know if we have reached the top most parent node ?
You can traverse from an element up to the root looking for the desired tag:
function findUpTag(el, tag) {
while (el.parentNode) {
el = el.parentNode;
if (el.tagName === tag)
return el;
}
return null;
}
You call this method with your start element:
var el = document.getElementById("..."); // start element
var a = findUpTag(el, "A"); // search <a ...>
if (a) console.log(a.id);
The following recursive function will return an ascending ordered array, with all the parents nodes for the provided DOM element, until BODY node is reached.
function parents(element, _array) {
if(_array === undefined) _array = []; // initial call
else _array.push(element); // add current element
// do recursion until BODY is reached
if(element.tagName !== 'BODY' ) return parents(element.parentNode, _array);
else return _array;
}
Usage :
var parentsArray = parents( document.getElementById("myDiv") );
You can use jQuery closest() method to get the closest ahref:
$("#your-element").closest("a").css("color", "red");
Or you can have a look at the parentsUntil method:
$("#your-element").parentsUntil("#yourWrapper", "a").first().css("color", "red");
Try it out here: http://www.lunarailways.com/demos/parents.html
I been working on similar thing. Trying to close a div if user clicks outside the div. It needs to loop through all its parent nodes.
have a look at this:
http://jsfiddle.net/aamir/y7mEY/
Here's a shorter one:
function parentByTag(el, tag) {
if(!el || el.tagName == tag) {
return el
} else {
return parentByTag(el.parentElement, tag)
}
}
Returns undefined if not found.
I need to get the next sibling of an element but it should not have the class "foo". In jQuery this would be:
myEl.next("div.goodClass")
or
myEl.next().not(".foo")
Can this be done with nextSibling?
You can to check the className property of nextSibling using a regular expression:
if (/(?:^|\s)foo(?:\s|$)/.test(el.nextSibling.className)) {
// next sibling has foo class
}
If you want to find the next element that doesn't have that class, you can do something like this:
var el = document.getElementById("myEl");
while (el = el.nextSibling) {
if (!(/(?:^|\s)foo(?:\s|$)/.test(el.className)))
break;
}
// el is either null, or an element that doesn't have the foo class
Try this:
if (elem.nextSibling && !/(?:^|\s+)foo(?:\s|$)/.test(elem.nextSibling.className)) {
// there is a next sibling that does not have the class foo
}
Try this code,
x=myEl.nextSibling;
while (x.nodeType!=1)
{
if(x.className=="foo"){
x=x.nextSibling;
}
}
return x;
I'm trying to write javascript to find page elements relative to a given element by using parentNode, firstChild, nextSibling, childNodes[], and so on. Firefox messes this up by inserting text nodes between each html element. I've read that I can defeat this by removing all whitespace between elements but I've tried that and it doesn't doesn't work. Is there a way to write code that works on all modern browsers?
For example:
<div id="parent"><p id="child">Hello world</p></div>
In IE parent.firstChild is child but in Firefix it's a phantom Text element.
I have a workaround. You can insert the two methods below:
Element.prototype.fChild = function(){
var firstChild = this.firstChild;
while(firstChild != null && firstChild.nodeType === 3){
firstChild = firstChild.nextSibling;
}
return firstChild;
}
Element.prototype.nSibling = function(){
var nextSibling = this.nextSibling;
while(nextSibling != null && nextSibling.nodeType === 3){
nextSibling = nextSibling.nextSibling;
}
return nextSibling;
}
and you can now use:
document.getElementById("parent").fChild();
document.getElementById("parent").nSibling();
instead of:
document.getElementById("parent").firstChild;
document.getElementById("parent").nextSibling;
You have to check that the nodeType == 1.
if (el.nodeType === 1) {
return el;
}
I wrote a small DOM traversing class for ya (mostly copied from MooTools).
Download here: http://gist.github.com/41440
DOM = function () {
function get(id) {
if (id && typeof id === 'string') {
id = document.getElementById(id);
}
return id || null;
}
function walk(element, tag, walk, start, all) {
var el = get(element)[start || walk], elements = all ? [] : null;
while (el) {
if (el.nodeType === 1 && (!tag || el.tagName.toLowerCase() === tag)) {
if (!all) {
return el;
}
elements.push(el);
}
el = el[walk];
}
return elements;
}
return {
// Get the element by its id
get: get,
walk: walk,
// Returns the previousSibling of the Element (excluding text nodes).
getPrevious: function (el, tag) {
return walk(el, tag, 'previousSibling');
},
// Like getPrevious, but returns a collection of all the matched previousSiblings.
getAllPrevious: function (el, tag) {
return walk(el, tag, 'previousSibling', null, true);
},
// As getPrevious, but tries to find the nextSibling (excluding text nodes).
getNext: function (el, tag) {
return walk(el, tag, 'nextSibling');
},
// Like getNext, but returns a collection of all the matched nextSiblings.
getAllNext: function (el, tag) {
return walk(el, tag, 'nextSibling', null, true);
},
// Works as getPrevious, but tries to find the firstChild (excluding text nodes).
getFirst: function (el, tag) {
return walk(el, tag, 'nextSibling', 'firstChild');
},
// Works as getPrevious, but tries to find the lastChild.
getLast: function (el, tag) {
return walk(el, tag, 'previousSibling', 'lastChild');
},
// Works as getPrevious, but tries to find the parentNode.
getParent: function (el, tag) {
return walk(el, tag, 'parentNode');
},
// Like getParent, but returns a collection of all the matched parentNodes up the tree.
getParents: function (el, tag) {
return walk(el, tag, 'parentNode', null, true);
},
// Returns all the Element's children (excluding text nodes).
getChildren: function (el, tag) {
return walk(el, tag, 'nextSibling', 'firstChild', true);
},
// Removes the Element from the DOM.
dispose: function (el) {
el = get(el);
return (el.parentNode) ? el.parentNode.removeChild(el) : el;
}
};
}();
// Now you can do:
DOM.getFirst("parent") // first child
// or
DOM.getFirst("parent", "p") // first p tag child
// or
var el = DOM.get("parent") // get element by id
DOM.getFirst(el) // first child
you could use tagName to check the name of the tag. If undefined then this is your 'phantom' text node.
e.g.
function getFirstTag(node) {
return ((node.firstChild.tagName) ? node.firstChild : node.firstChild.nextSibling);
}
Check the DOM level 2 core reference to see the various possible types of nodes, so you can filter out the undesired ones with a simple snippet of JavaScript. One solution is to monkey patch the object (see the answer of Vernicht) or if you do not like monkey patching, then you can add these methods to your own library, or an even better solution might be to use a fancy library like jQuery or prototype.