How can I check if one DOM element is a child of another DOM element? Are there any built in methods for this? For example, something like:
if (element1.hasDescendant(element2))
or
if (element2.hasParent(element1))
If not then any ideas how to do this? It also needs to be cross browser. I should also mention that the child could be nested many levels below the parent.
You should use Node.contains, since it's now standard and available in all browsers.
https://developer.mozilla.org/en-US/docs/Web/API/Node.contains
Update: There's now a native way to achieve this. Node.contains(). Mentioned in comment and below answers as well.
Old answer:
Using the parentNode property should work. It's also pretty safe from a cross-browser standpoint. If the relationship is known to be one level deep, you could check it simply:
if (element2.parentNode == element1) { ... }
If the the child can be nested arbitrarily deep inside the parent, you could use a function similar to the following to test for the relationship:
function isDescendant(parent, child) {
var node = child.parentNode;
while (node != null) {
if (node == parent) {
return true;
}
node = node.parentNode;
}
return false;
}
I just had to share 'mine'.
Although conceptually the same as Asaph's answer (benefiting from the same cross-browser compatibility, even IE6), it is a lot smaller and comes in handy when size is at a premium and/or when it is not needed so often.
function childOf(/*child node*/c, /*parent node*/p){ //returns boolean
while((c=c.parentNode)&&c!==p);
return !!c;
}
..or as one-liner (just 64 chars!):
function childOf(c,p){while((c=c.parentNode)&&c!==p);return !!c}
and jsfiddle here.
Usage:
childOf(child, parent) returns boolean true|false.
Explanation:
while evaluates as long as the while-condition evaluates to true.
The && (AND) operator returns this boolean true/false after evaluating the left-hand side and the right-hand side, but only if the left-hand side was true (left-hand && right-hand).
The left-hand side (of &&) is: (c=c.parentNode).
This will first assign the parentNode of c to c and then the AND operator will evaluate the resulting c as a boolean.
Since parentNode returns null if there is no parent left and null is converted to false, the while-loop will correctly stop when there are no more parents.
The right-hand side (of &&) is: c!==p.
The !== comparison operator is 'not exactly equal to'. So if the child's parent isn't the parent (you specified) it evaluates to true, but if the child's parent is the parent then it evaluates to false.
So if c!==p evaluates to false, then the && operator returns false as the while-condition and the while-loop stops. (Note there is no need for a while-body and the closing ; semicolon is required.)
So when the while-loop ends, c is either a node (not null) when it found a parent OR it is null (when the loop ran through to the end without finding a match).
Thus we simply return that fact (converted as boolean value, instead of the node) with: return !!c;: the ! (NOT operator) inverts a boolean value (true becomes false and vice-versa).
!c converts c (node or null) to a boolean before it can invert that value. So adding a second ! (!!c) converts this false back to true (which is why a double !! is often used to 'convert anything to boolean').
Extra:
The function's body/payload is so small that, depending on case (like when it is not used often and appears just once in the code), one could even omit the function (wrapping) and just use the while-loop:
var a=document.getElementById('child'),
b=document.getElementById('parent'),
c;
c=a; while((c=c.parentNode)&&c!==b); //c=!!c;
if(!!c){ //`if(c)` if `c=!!c;` was used after while-loop above
//do stuff
}
instead of:
var a=document.getElementById('child'),
b=document.getElementById('parent'),
c;
function childOf(c,p){while((c=c.parentNode)&&c!==p);return !!c}
c=childOf(a, b);
if(c){
//do stuff
}
Another solution that wasn't mentioned:
Example Here
var parent = document.querySelector('.parent');
if (parent.querySelector('.child') !== null) {
// .. it's a child
}
It doesn't matter whether the element is a direct child, it will work at any depth.
Alternatively, using the .contains() method:
Example Here
var parent = document.querySelector('.parent'),
child = document.querySelector('.child');
if (parent.contains(child)) {
// .. it's a child
}
You can use the contains method
var result = parent.contains(child);
or you can try to use compareDocumentPosition()
var result = nodeA.compareDocumentPosition(nodeB);
The last one is more powerful: it return a bitmask as result.
Take a look at Node#compareDocumentPosition.
function isDescendant(ancestor,descendant){
return ancestor.compareDocumentPosition(descendant) &
Node.DOCUMENT_POSITION_CONTAINS;
}
function isAncestor(descendant,ancestor){
return descendant.compareDocumentPosition(ancestor) &
Node.DOCUMENT_POSITION_CONTAINED_BY;
}
Other relationships include DOCUMENT_POSITION_DISCONNECTED, DOCUMENT_POSITION_PRECEDING, and DOCUMENT_POSITION_FOLLOWING.
Not supported in IE<=8.
I came across a wonderful piece of code to check whether or not an element is a child of another element. I have to use this because IE doesn't support the .contains element method. Hope this will help others as well.
Below is the function:
function isChildOf(childObject, containerObject) {
var returnValue = false;
var currentObject;
if (typeof containerObject === 'string') {
containerObject = document.getElementById(containerObject);
}
if (typeof childObject === 'string') {
childObject = document.getElementById(childObject);
}
currentObject = childObject.parentNode;
while (currentObject !== undefined) {
if (currentObject === document.body) {
break;
}
if (currentObject.id == containerObject.id) {
returnValue = true;
break;
}
// Move up the hierarchy
currentObject = currentObject.parentNode;
}
return returnValue;
}
Consider using closest('.selector')
It returns null if neither element nor any of its ancestors matches the selector. Alternatively returns the element which was found
try this one:
x = document.getElementById("td35");
if (x.childElementCount > 0) {
x = document.getElementById("LastRow");
x.style.display = "block";
}
else {
x = document.getElementById("LastRow");
x.style.display = "none";
}
TL;DR: a library
I advise using something like dom-helpers, written by the react team as a regular JS lib.
In their contains implementation you will see a Node#contains based implementation with a Node#compareDocumentPosition fallback.
Support for very old browsers e.g. IE <9 would not be given, which I find acceptable.
This answer incorporates the above ones, however I would advise against looping yourself.
Related
i try to detect null this way
if(!$(this))
{
alert('here is null');
}
OR
if($(this)===null)
{
alert('here is null');
}
but still no luck.
here is partial code
$elements.each(function(){
//alert($(this).html());
var $item = $('<li />').append($(this));
if(!$(this))
{
alert('here is null');
}
//alert($item.text());
$list.append($item);
});
anyone can see full code from here https://jsfiddle.net/tridip/41s1pq3a/12/
edit
i was iterate in td's content. td has some link and text. i was trying to wrap each text and link inside li. so iterate this below way. code is working but some time it is also showing null which i need to detect.
i am looking for way not consider any null or empty.
here is the code
var $elements = $('.webgrid-footer td').contents()
.filter(function() {
return this.nodeType === 3 || this.nodeType === 1; // 1 means elements, 3 means text node
});
var $list = $('<ul />');
$elements.each(function(){
//alert($(this).html());
var $item = $('<li />').append($(this));
if(this===null)
{
alert('here is null');
}
//alert($item.text());
$list.append($item);
});
//alert($list.html());
$('#dv').append($list);
see this line var $item = $('<li />').append($(this)); it is getting some time empty or null which i do not want tp consider. if anyone knows it how to handle this situation then share the idea. thanks
$(null) is an empty jQuery object, not null. And all objects are truthy.
If you want to test null, use this === null. You don't need jQuery for this.
However, I don't see why do you expect this to be null sometimes. Instead, it seems you want to ignore whitespace text nodes.
var $elements = $('.webgrid-footer td').contents().filter(function() {
return (this.nodeType === 3 && $.trim(this.nodeValue) !== '')
|| this.nodeType === 1;
});
$(this) will never be either null or falsey, because jQuery always returns an object reference, which is not null or falsey.
In strict mode, it's possible for this (not $(this)) to be null. In loose mode, it isn't; attempts to make this be null will cause this to be a reference to the global object.
So it may be that you want to test this, not $(this). But only in strict mode. In loose mode, bizarrely, you'd want if (this == window) to be your "null" test.
Having said that, $elements is clearly meant to be a jQuery object, and I'm not immediately thinking of a way to to create a jQuery objct with nulls in through the public API. (It's easy if you muck about with the internals...)
I've been working through some text book to learn web development and i've become confused on an example. The example creates a meter element and fills it with some attributes. There is then some javascript to check for browser support for the tag. The part where i'm confused is after the first expression returns either true or false for the support, shouldn't there be a check for if true or false was returned on the following if statement? also as an aside, when the create element builds the element does is give it default values, or grab values from an existing meter in the html.
The check for support is as follows.
var noMeterSupport = function(){
return(document.createElement('meter').max === undefined);
}
the next part that builds the meter if the support isn't found is below. This is where i become confused as it seems to take either value and continue without checking if it was true or false.
if (noMeterSupport()) {
var fakeMeter, fill, label, labelText, max, meter, value;
value = meter.attr("value");
meter = $("#pledge_goal");
max = meter.attr("max");
labelText = "$" + meter.val();
fakeMeter = $("<div></div>");
fakeMeter.addClass("meter");
label = $("<span>" + labelText + "</span>");
label.addClass("label");
fill = $("<div></div>");
fill.addClass("fill");
fill.css("width",(value / max * 100) + "%");
fill.append("<div style='clear:both;'><br></div>");
fakeMeter.append(fill);
fakeMeter.append(label);
meter.replaceWith(fakeMeter);
}
The body of the if is only executed if noMeterSupport() returns true. The condition in an if statement requires something "truthy", i.e. something that can be interpreted as true or false. Since the function returns a boolean value, that is sufficient. (See first Google hit for truthiness javascript, which is a good explanation.)
EDIT: Forgot about your second question. When a new element is created with document.createElement, it does indeed get default values. In your example, the default value of max for a <meter> is 1.
if (noMeterSupport()) { checks the return value. It means exactly the same as this:
var supported = noMeterSupport();
if(supported) {
I hope that I understand your question correctly and will try to answer it.
So you would expect something like this:
if (noMeterSupport() == true)
Actually, this is equivalent to this:
if (noMeterSupport())
And if you want to check false:
if (noMeterSupport() == false)
This is equivalent to:
if (!noMeterSupport())
This statement will make the function either return true or false:
return(document.createElement('meter').max === undefined)
basically it would be synonymous with writing:
if(document.createElement('meter').max === undefined) {
return true;
} else {
return false;
}
That makes the value of noMeterSupport() either true or false.
var noMeterSupport = function(){
return(document.createElement('meter').max === undefined);
}
noMeterSupport returns the result of the comparison document.createElement('meter').max === undefined.
The comparison will be either true or false, ok?
So, now, when you do
if (noMeterSupport()) { /*then do something*/}
is like saying
if (/*the result of noMeterSupport() is true*/) {/*then do something*/}
So, this if statement will only run if noMeterSupport returns true
var noMeterSupport = function(){
return(document.createElement('meter').max === undefined);
}
This section of code is not actually doing the check, it is defining a function called noMeterSupport.
The code is not actually run until the function is called. It is called by adding () to the function name.
noMeterSupport()
Your if() statement is where it is being called as it the brackets.
You expect a boolean condition inside the if statement:
if(<boolean_condition>)
{
...
}
The noMeterSupport() is actually returning true or false, so the
if(noMeterSupport())
is converted to if(true) or if(false)
depending on the result of the document.createElement('meter').max === undefined evaluation.
You are receiving a boolean condition and the if statement works fine.
As a beginner, there's two points to quickly learn in programming :
The comparison operators == and === not only do the comparison, but returns in fact the result of this comparison (you can place it in var to test)
var bool = 1 === 2;
console.log(bool); // will print false
The test if(boolean === true) is equivalent to if(boolean), and the test if(boolean === false) is equivalent to if(!boolean)
I'm writing a script that adds labels to things on a page using an element from an array based on part of the link... so my array looks like this:
var componentList[9] = "Sunnyseed"
var componentList[10] = "Echoberry"
var componentList[11] = "Riverstone"
var componentList[13] = "Auraglass"
var componentList[14] = "Skypollen"
You'll notice there is no '12'... I want the label to be 'Unknown' when the array item doesn't exist. Now, I can't exactly test my solution since I can't cause the target page to throw me a 12... so I was hoping somebody would tell me whether this will do what I want or not...
var component = ""
if(typeof componentList[critterIDval] == 'undefined'){
component="Unknown"
}
else{
component=componentList[critterIDval]
}
This is obviously not the full script, but it should be the important stuff... I just want to know if that will make it say 'Unknown' when the critterIDval is 12 - since it could take years to come across the situation for testing.
You're pretty much there. You're using a single-equals sign in your comparison, so that will mess it up, and I'm not sure you can create a JS array like that, but aside from that, you're good.
Here is the test I ran for it:
var componentList = [];
componentList[9] = "Sunnyseed";
componentList[10] = "Echoberry";
componentList[11] = "Riverstone";
componentList[13] = "Auraglass";
componentList[14] = "Skypollen";
for (var critterIDval = 9; critterIDval < 15; critterIDval++) {
if (typeof componentList[critterIDval] == 'undefined') { // double equals here
component = "Unknown";
} else {
component = componentList[critterIDval];
}
console.log(component);
}
It looks fine.
Though if you are sure that the value will never be an empty string(like componentList[14] = '';) then you can try
var component = componentList[critterIDval] || 'Unknown'
I want the label to be 'Unknown' when the array item doesn't exist.
The typeof operator does not tell you if a property exists or not as it returns undefined when the property doesn't exist but also when it does exist and has been assigned a the value undefined or simply created but hasn't been assigned a value.
There are two primary ways to test for the existence of a property: the in operator, which also looks on the [[Prototype]] chain and the hasOwnProperty method of all Objects. So
if (componentList.hasOwnProperty(critterIDval)) {
component = "Unknown"
} else {
component = componentList[critterIDval]
}
which you could also write as:
component = componentList.hasOwnProperty(critterIDval)? componentList[critterIDval] : 'unknown';
PS. there are other methods, such as looking at Object.keys(componentList) and componentList.propertyIsEnumerable(critterIDval), but the above are the most common.
Edit
If your requirement is not just to test for property existence but to also test for a "truthy" value, then:
if (componentList[critterIDval])
may be sufficient and will return false where the value is '' (empty string), 0, false, NaN, undefined or null.
Maybe just testing for a non–empty string or number will do:
if (/.+/.test(componentList[critterIDval]))
but that returns true for NaN, null and so on. So you need to specify what you are actually testing for, otherwise you may get undesired results for some values.
I have a specific id ("mysubid"), now I want to check if this element (this id) is in a child path of an other id ("mymainid").
Is there an easy way to do this or will I go upwards, element by element, to see if the element is in a child path.
By child path I am talking about something like this:
A > B > C > D
So D is in the Child Path of A,B and C
You all are making this very complicated. Use the descendant selector:
if ($('#mymainid #mysubid').length) {
// #mysubid is inside #mymainid
}
var isInPath = $("#mysubid").closest("#mymainid").length > 0;
if( $("#mymainid").find("#mysubid").length > 0 )
if($('#mysubid','#mymainid').length)
{
}
This will check to see if #mysubid is within #mymainid
jQuery( selector, [ context ] )
selector: A string containing a selector expression
context: A DOM Element, Document, or jQuery to use as context
This is a just an overlaod for $('#mymainid').find('#mysubid').lentgh btw, verified from: http://github.com/jquery/jquery/blob/master/src/core.js#L162
On another note, using a method such as $('#a #b') resorts to using the Sizzle Selector witch is slower than doing $('#a',$('#b')), witch uses purely javascript's getElementById
Note: as jQuery returns an empty object by default if the selection is not found you should always use length.
If you want to see the entire chain as an array use elm.parentNode and work backwards. So, to answer your question (and the depth or distance between the elements) in POJ, you can use:
var doc = document,
child = doc.getElementById("mysubid"),
parent = doc.getElementById("mymainid"),
getParents = function (elm) {
var a = [], p = elm.parentNode;
while (p) {
a.push(p);
p = p.parentNode;
}
return a;
};
getParents(child).indexOf(parent);
I tried on various browsers and the DOM function below is between 3 to 10 times faster than the selector methods(jQuery or document.querySelectorAll)
function is(parent){
return {
aParentOf:function(child){
var cp = child.parentNode;
if(cp){
return cp.id === parent.id ?
true : is(parent).aParentOf(cp);
}
}
}
}
The call below will return true if A is a parent of D
is(document.getElementById('A')).aParentOf(document.getElementById('D'))
For just few calls I would use the $('#A #D').length
For very frequent calls I would use the DOM one.
Using the 'is' method actually returns a boolean.
if($('#mymainid').is(':has(#mysubid)')) // true
Going the other direction...
if($('#mysubid').parents('#mymainid').length) // 1
trying to determine a decent, cross browser method for obtaining attributes with javascript? assume javascript library use (jQuery/Mootools/etc.) is not an option.
I've tried the following, but I frequently get "attributes" is null or not an object error when IE tries to use the "else" method. Can anyone assist?
<script type="text/javascript">
//...
getAttr: function(ele, attr) {
if (typeof ele.attributes[attr] == 'undefined'){
return ele.getAttribute(attr);
} else {
return ele.attributes[attr].nodeValue;
}
},
//...
</script>
<div>
Link
</div>
using the above html, in each browser, how do I getAttr(ele, 'href')? (assume selecting the ele node isn't an issue)
For the vast majority of cases you can simply use the built in getAttribute function.
e.g.
ele.getAttribute(attr)
According to QuirksMode this should work on all major browsers (IE >= 6 included), with a minor exception:
In IE5-7, accessing the style attribute gives an object, and accessing the onclick attribute gives an anonymous function wrapped around the actual content.
With regard to your question's update, you could try this.
It may be overkill, but if getAttribute() and the dot notation don't return a result, it iterates through the attributes object to try to find a match.
Example: http://jsfiddle.net/4ZwNs/
var funcs = {
getAttr: function(ele, attr) {
var result = (ele.getAttribute && ele.getAttribute(attr)) || null;
if( !result ) {
var attrs = ele.attributes;
var length = attrs.length;
for(var i = 0; i < length; i++)
if(attrs[i].nodeName === attr)
result = attrs[i].nodeValue;
}
return result;
}
};
var result = funcs.getAttr(el, 'hash');
It's up to you to do some cross-browser testing, though. :o)
Using ele.attributes, you need to access them by index, as in:
ele.attributes[0].nodeName; // "id" (for example)
ele.attributes[0].nodeValue; // "my_id" (for example)
Trying to pass attributes an attribute name appears to return a value whose typeof is object, so your else code is running even though ele.attributes[attr] doesn't give you the value you want.
You are trying to access properties of ele before you've established if those properties exist. Try this kind of evidence chain:
if (ele.attributes && ele.attributes[attr] && typeof ele.attributes[attr] == 'undefined')
etc.