How to check if element is last child without JQuery - javascript

I have a pure Javascript script with an onclick event that checks the value of the next sibling before deciding what to do. This will cause an error if the element clicked is the last element in its container (because of accessing nextSibling). I need to first check that the element clicked is not the last element in the container, but can't seem to find out how.
Note: I don't think this is a duplicate. There are quite a few questions about checking if an element is the last child, but all accepted answers—all answers in general—use JQuery.

You can use the .nextSibling property on the element and see if it comes back as empty (undefined, etc).

You can use the node.lastChild Property
The Node.lastChild read-only property returns the last child of the node. > If its parent is an element, then the child is generally an element node, > a text node, or a comment node. It returns null if there are no child elements..
var tr = document.getElementById("row1");
var corner_td = tr.lastChild;

The error you get should be some kind of can't set property on undefined.
You have just to check whether the next element exists:
if (typeof element.nextSibling === "undefined")
return;

For some reason none of the answers worked for me, I always ended up seeing a #text node instead of undefined or null, so I ended up comparing my element with the last child of the parent of the element:
element === element.parentNode.children[element.parentNode.children.length-1]
or if you want it as a function
function isLastChild(el) {
return (el === el.parentNode.children[el.parentNode.children.length-1])
}
//Useage
if(isLastChild(el)) {
//Element is the last child of its parent.
}
Might be longer than other answers but surly won't fail you.

Accessing an element's nextSibling element you'll get null if the element has no next sibling, so you can just check before going on with your code, like this:
if (myElement.nextSibling) {
// the element has a next sibling
// go on...
} else {
// the element is the last child
}

Use the .lastChild property of the node.
Example:
Here, we are removing last 4 child nodes in the list.
function clearAll() {
var sidemenu = document.getElementById('side_menu');
console.log("sidemenu.childNodes.length = " + sidemenu.childNodes.length);
while (sidemenu.childNodes.length > 2) {
console.log(sidemenu.childNodes);
sidemenu.removeChild(sidemenu.lastChild);
console.log("removed");
console.log("sidemenu.childNodes.length = " + sidemenu.childNodes.length);
}
console.log("What we have left now:");
console.log(sidemenu.childNodes);
}
clearAll();
<ul id="side_menu">
<li>List Item 1</li>
<li>List Item 2</li>
<li>List Item 3</li>
<li>List Item 4</li>
<li>List Item 5</li>
</ul>
Learn more: Node.lastChild - Web API Interfaces | MDN

This is one way to check:
document.querySelector(":last-child");
Here's one more:
var isLastChild = (element === element.parentNode.lastChild);

If you're trying to make this compatible with older browsers, just use childNodes:
// Last element in the body
document.getElementsByTagName('body')[0].childNodes[document.getElementsByTagName('body')[0].childNodes.length-1]
so for your particular problem, use the event object in your onclick:
element.onclick = function(event) {
var parent = event.target.parentNode;
if(event.target === parent.childNodes[parent.childNodes.length-1])
// Code here
}

How about var isLastChild = element === element.parentNode.lastChild?

Ask if there is a next element or not to know if it is the last or not:
if (typeof element.nextElementSibling == null) {
//is last
return;
} else {
//is not last
}

you must use nextElementSibling, no nextSibling or typeof ...
function isLastElement(element) {
return element.nextElementSibling === null;
}

Related

Compare Elements via jQuery

I'm in a situation where I want to check if two elements (one is clicked and another one a reference) are the same, what I'm trying to do is:
$("#process li").click(function() {
currentElement = $(this);
referenceElement = $("#process li:first-child");
if (currentElement === referenceElement) {
$(".mark").removeClass("mark");
$(this).addClass("mark");
}
});
So what I want is to check if the clicked <li> is the first child of the ul#process and if so first remove a .mark class from another element and then add it to the clicked one. I don't get any working result - ideas anyone?
UPDATE:
Thanks you very much! This is my solution:
$("#processlist li").click(function() {
currentElement = $(this);
if (currentElement.is('li:first-child')) {
$(this).addClass("mark");
}
});
Now if I click on a , if it is the first child of this list, the class .mark is added - sweet!
Comparing objects in JS is very troublesome. The simplest way is to just pick a few key properties and compare those, eg:
if (currentElement.prop('id') === referenceElement.prop('id') {
// rest of your code...
}
However, given your use case you could use is:
if (currentElement.is('#process li:first-child')) {
// rest of your code...
}
Example fiddle
You need to extract the DOM element from the jQuery object. You can use the get method of jQuery for this.
e.g. if( currentElement.get( 0 ) === referenceElement.get( 0 ) )

Get the previous visible element jquery

I have a <ul> like
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li style="display:none;">4</li>
<li class="curSelected">5</li>
<li>6</li>
</ul>
Now from <li> 5 I want the reference of the previous visible element i.e <li> 3
How can I get it?
I tried $('li.curSelected').prev(":visible:last");
But this is not working.
Please help.
Try this:
$('li.curSelected').prevAll(":visible:first");
prev() returns only the immediate element preceding the selected one. prevAll() returns all previous.
You can use .prevUntil() to search up to (but not including) the first visible item, then use .prev():
$('.curSelected').prevUntil(':visible').prev()
Update
As pointed out in the comments, this wouldn't work if the immediately previous item is already visible. Unfortunately, there's no optimal jQuery for that, so here's an alternative:
$prev = $('.curSelected')
do {
$prev = $prev.prev();
} while ($prev.length && $prev.is(':hidden'));
Since jQuery traverses the dom, using prev, from the current to the first, but returns only one element(the previous), you should use .prevAll.
$("li.curSelected").prevAll(":visible:first");
See a working fiddle of this example.
Here's my example. Works perfect, any browser.
var elem = $( some_thing ).next();
while( elem && elem[0] != undefined && !elem.is( ':visible' ) )
elem = elem.next();
if( elem && elem[0] != undefined )
elem.doSomething();

Select element by element's content

I have list:
<ul class='mates'>
<li class='m' id='1'>Jakub</li>
<li class='f' id='2'>Vinnie</li>
<li class='m' id='3'>David</li>
</ul>
How can I select 'li' tags "ONE BY ONE" to be checked if their content (between 'li' tags) is equal to 'xyz'.
element = document.getElementsByClassName('.mates').firstChield.innerHTML;
do {
if(){
//do something
}
}while (element = element.nextSibling);
but I'm not getting even far enough to select firstChild. This error is showing in console: "Cannot read property 'innerHTML' of undefined". This needs to be done in plain JavaScript. Any ideas?
<ul class='mates'>
<li class='m' id='1'>Jakub</li>
<li class='f' id='2'>Vinnie</li>
<li class='m' id='3'>David</li>
</ul>
<script>
var mates = document.getElementsByClassName('mates')[0];
for (var i=0; i< mates.childNodes.length; i++){
if(mates.children[i].innerHTML == 'Vinnie') alert("Got you! ID "+mates.children[i].id)
}
</script>
Drop the dot in the parameter. Like this:
element = document.getElementsByClassName('mates').firstChild.innerHTML;
The dot is not a part of the name of the class.
EDIT also notice that the question originally had a typo in firstChild.
Your element variable is not an element (its value is probably undefined). It should work if you use it like this:
var element = document.getElementsByClassName('mates')[0].firstChild;
do {
if(element.innerHTML == 'foo'){
//do something
}
} while (element = element.nextSibling);
The code above fixes:
The class name as pointed out by #Renan
The typo in .firstChild
Also note that getElementsByClassName returns a list of elements, so you have to grab the first one in the list (index 0) to reach your <ul>.
Finally, keep in mind that you'll be looping over all children of the <ul>, including empty text nodes (see a demonstration at http://jsfiddle.net/58ZZF/). This can be avoided if you use firstElementChild and nextElementSibling, but I'm not sure if there are cross browsers issues with those properties (MDN only says it's Firefox 3.5+).
Few mistakes
Class name to getElementsByClassName should not have .
Spelling mistake in firstChild
getElementsByClassName returns an array, not a dom reference
When using nextSibling it could return text nodes also, you need to check the nodeType to make sure the element is a element node(nodeTye = 1), also you can check the tagName == 'LI'
Try
var element = document.getElementsByClassName('mates')[0].firstChild;
do {
if(element.nodeType == 1){
console.log(element.textContent)
}
}while (element = element.nextSibling);
Demo: Fiddle

how do i traverse ancestors with jQuery?

How do I traverse ancestors with jQuery?
The current code is getting stuck in a recursive loop:
HTML:
<html>
<body>
<div>
<ul>
<li></li>
</ul>
</div>
</body>
</html>
JS:
function traverse($node){
while ( $node.get(0) !== $("html").get(0) ) {
console.log($node);
traverse($node.parent());
}
}
//traverse($("ul li"));
To observe the problem, un-comment the last line, but be warned that it may freeze your browser.
The same on JSFiddle: http://jsfiddle.net/WpvJN/1/
You can get the collection with .parents() and iterate with .each():
$('ul li').parents().each(function () {
console.log(this);
});
I expect you need to put a limit on the traverse in case the parent you seek is not an ancestor of where you started. Otherwise, you either end up in an endless loop or run out of parents.
For example, a generic function to go from an element up to a parent with a particular tag name, you might do (not jQuery but I'm sure you can convert it if necessary):
function upTo(tagName, el) {
tagName = tagName.toLowerCase();
// Make sure el is defined on each loop
while (el && el.tagName && el.tagName.toLowerCase() != tagName) {
el = el.parentNode;
}
return el;
}
An alternative is to check for el.parentNode and stop when there are no more.
There is always an HTML node in an HTML document, so as long as you don't start from the document node, you'll always get there.

Determine if element has children with X tag

I am looping through some elements and need to determine if an element has a child(grandchild?) with the li tag, like in the information element below. The li elements will vary in id so I am not referencing them that way. I am currently looping through the li elements and if I check for children it always returns true because there are "a" tag children, I just want to check for 'lil' tag children.
<ul id="navMenu">
<li id="home">Home</li>
<li id="information">Information
<ul>
<li>Credits</li>
<li>Lorem Ipsum</li>
</ul>
</li>
<li id="contact">Contact</li>
</ul>
Here is what I have now...
$('#test').load('../common.html #navMenu', function() {
$.each($("#test #navMenu li"), function(i,v) {
var theElement = $(v);
if ($(theElement).children('li')){
alert('This Element has children');
}
});
});
Thank you once again,
Todd
You could try -
$('#test').load('../common.html #navMenu', function() {
$.each($("#test #navMenu li"), function(i,v) {
var theElement = $(v);
if ($(theElement).find('li').length > 0){
alert('This Element has children');
}
});
});
find will go deeper into the current element than children which only searches one level down.
$(theElement).children('li') returns a jQuery object which always passes an if clause, even when it's empty.
Moreover, you want .find, since .children only returns direct children and not grandchildren.
So:
if ($(theElement).find('li').length > 0) {
or:
if ($(theElement).find('li').length) {
// 0 won't pass an if clause, and all other numbers will, so you can eliminate `> 0`
Given:
> var theElement = $(v);
> if ($(theElement).children('li')) {
> alert('This Element has children');
> }
doesn't $(v) return an jQuery object? So $(theElement) is redundant.
Anyhow, if v is a reference to one of the elements passed to .each, then you can replace all of the above with:
if (v.getElementsByTagName('li').length) {
/* v has li descendants */
]
you could also add the extra li to your query: "#test #navMenu li li"

Categories

Resources