jquery: Trying to understand the next() function - javascript

I was using the jquery next() method on the following html:
<div id="NavBar">
<div class="dropDownList">
<ul>
<li><span>Home</span></li>
<li><span>Products</span>
<ul>
<li><span>test</span>
<li><span>test</span>
</ul>
</li>
<li><span>Company</span>
</li>
<li><span>Contact</span></li>
</ul>
</div>
<div class="dropDownList">
<ul>
<li><span>Home</span></li>
<li><span>Products</span>
</li>
<li><span>Company</span>
</li>
<li><span>Contact</span></li>
</ul>
</div>
</div>
I added the following click handler to anchors in the list and wrote the current and next() element to the console like so:
$('.dropDownList > ul > li > a').click(function() {
console.dir($(this));
var checkElement = $(this).next();
console.dir(checkElement);
}
When I click the "Products" list item of the first ul I see this in the console:
So I can see the first element is the anchor and the next is the ul- this makes sense to me. Now my confusion comes in when I click the "Products" list item in the second ul. I get back two objects like before, but this time they are both anchors. I thought that the first anchor element would be "Products" and the second would be "Company" because that is the next element in the list- or maybe even the <li> element containing the next anchor. Instead when I drill down into the objects in the console they appear to be the same element. The text and textContent field is the same for both:
Why is this?

In your second example, the next element in the list is not a sibling to the a anchor; it's a sibling to its parent li.
That Products anchor has no .next() element, and you should see in your console that $(this).next() is an empty jQuery object with zero .length. http://jsfiddle.net/mblase75/6LFPG/

So the problem is that in your a anchor has no next() it will return null. So in that case you will have to go up to the parent and get the next sibling. Here is a really quick rough example on how it can be done.
var CheckElement;
if($(this).next().length != 0){
checkElement = $(this).next();
}
else{
checkElement = $(this).parent().next();
}

the JQuery next() method brings you to the next sibling of the selected element.
Siblings are those that elements that have the same parents.
From your given html
<li><span>Products</span> //child of 'li'
<ul> <----------------------------------//child of 'li'
<li><span>test</span>
<li><span>test</span>
</ul>
</li>
What you selected was the first child.
So when you called next in the first example it selected the next child.
In your second example there is no second child to traverse therefore it defaults to an empty jquery element.

Related

How can I add a slideDown() animation to newly created elements in my Meteor/Blaze site?

I have two unordered lists that contains a set of li elements. The li elements in each unordered list are represented by an array in my backend with each li element corresponding to an item in the respective array.
When I hit the checkbox on one li element, my backend splices the item from one array and puts it into another. This effectively destroys the li element and recreates it in the other list.
I am trying to call a slide up and slide down animation for when the item disappears and reappears from on list to another. However I am only able to get the slide up animation to work when it leaves one list.
When it appears in the other list, the slide down animation does not work, the element simply pops in.
Is there any way that I can append the slideDown() animation to all newly created li elements? Would it be done through an event listener?
Here is a basic representation of what my code currently looks like:
HTML:
<div>
<ul class="top-list">
<li class="js-checkbox"><input type="checkbox">I am list item number</li>
<li class="js-checkbox"><input type="checkbox">I am list item number</li>
<li class="js-checkbox"><input type="checkbox">I am list item number</li>
</ul>
<ul class="bot-list">
<li class="js-checkbox"><input type="checkbox">I am list item number</li>
<li class="js-checkbox"><input type="checkbox">I am list item number</li>
</ul>
</div>
JavaScript
const target = event.target;
let children1 = target.parentElement;
let input = $(children1).find('.filled-in');
var list = $(children1).parents('li'); //<li> element
var parentList = list.parents('ul'); //<ul> element
var parentDiv = list.parents('div'); //parent div
var findTop = parentDiv.children("ul.top-list"); //find sibling
var findBot = parentDiv.children("ul.bot-list"); //find sibling
if (input[0].checked === false) {
$(list[0]).slideUp(300, function() {
instance.data.changed(instance.data.instruction._id.toString(), 0, true);
$(list[0]).appendTo($(findTop[0]));
$(list[0]).slideDown(300);
});
} else {
$(list[0]).slideUp(300, function() {
instance.data.changed(instance.data.instruction._id.toString(), 0, true);
$(list[0]).appendTo($(findBot[0]));
$(list[0]).slideDown(300);
});
}
Explanation of JavaScript:
If an item is being checked, it gets sent to 'top-list' and send to 'bot-list' if unchecked.
The action of disappearing from the first list gives me the correct slideUp() animation, but since there is JavaScript in my backend doing the actual splice and push of the corresponding item in the array, the slideDown() animation does not register when it appears in the other list.
I hope this is a good enough explanation. Here is also a JSFiddle that shows the animation in action, however it is only for one list. I built it for demonstration purposes.
I don't know if this helps but I was wondering if DOMNodeInserted was a potential solution.
You should have one template for the <ul> rendering one template for each <li>:
<template name="Items>
<ul>
{{#each items}}
{{> Item}}
{{/each}}
</ul>
</template>
<template name="Item">
<li class="item">{{text}}</li>
</template>
Be carefull with the template names Items and Item (there is an S at the end of the first).
This way, everytime a <li> changes, the Item template is rendered.
Make the slide up animation on Template.Item.events:
Template.Item.events({
'click .item'() {
// Slide up
// Edit your database
}
});
Make the slide down animation on Template.Item.onRendered:
Template.Item.onRendered(function (){
// Slide down
});

Javascript - How to highlight the last clicked list item

I have a sidebar list of items in HTML on a bootstrap page and i would like the last clicked item to be highlighted with a "active" class. I was wondering how i can get the whole list (ul) like an array so i could highlight a certain item (li) or highlight the last clicked item.
The list is structured like this:
<div class="container-fluid">
<div class="row">
<div class="col-sm-3 col-md-2 sidebar sidebar-style">
<ul class="nav nav-sidebar sidebar-scrollable">
<li class="active">Home</li>
<li>Site 001</li>
<li>Site 002</li>
<li>Site 003</li>
<li>Site 004</li>
<li>Site 005</li>
What i need is some JS code which can set the last clicked li item to class="active" but still have the option to set any of the li items to the active one, for example if i wanted to have a random button and it selected item 4 then 2 etc. I guess the main thing i need is a way to have all list items like an array.
I strongly suggest you use jQuery which is kind of simulating DOM manipulation like an array or more precisely an object.
My suggestion would be the following:
$(".nav-sidebar").on('click', 'li', function(e) {
$(this).parent().find('li.active').removeClass('active');
$(this).addClass('active');
});
This code is removing the active class from all li items, and adding active class to currently clicked li item.
Cheers,
Edit PS:
You could also bind your event directly like this:
$(".nav-sidebar li").click(...);
But the purpose of using .on() is that it will bind the event dynamically, so that if you decide to add a new li to your $(".nav-sidebar") element, it will also trigger the event on that element.
Edit:
To answer #zeddex your question: How to select manually li 4?
You simply use the following:
$("li:eq(3)").trigger('click');
That'll manually trigger a click event for li 4. If you want to select a specific li you can use a method of the like: :eq(x) in which x is the position of the li item you want to reach starting from 0 which corresponds to your first li. Have a look at jQuery DOC on that: jQuery DOC on :eq()

Add return false to parent link only with jQuery

I have a list inside a toggled div...
<li>
Link
<ul stlye="display:none;">
<li>Child Link</li>
</ul>
</li>
Ive written a piece of jQuery to toggle the display of the child UL only when a child link is clicked it no longer works (It doesnt go through to google), can anybody see where im going wrong?
// Dropdown
$('.archives ul li a').click(function(){
$(this).parent().find('ul').slideToggle();
return false;
});
STYLE is spelled wrong.
stlye=
From your post's title it appears you want something like this...
$('.archives ul li a').click(function(){
var $children = $(this).parent().find('ul');
$children.slideToggle();
return $children.length > 0 ? false : true;
});
Return will be false only when child ULs are found.
Assuming what you've shown is inside a ul which is in turn inside an element with class archives, then the selector .archives ul li a matches both the parent and child anchors, because you've used a descendant selector, and so your handler gets called for the child, and the return false; prevents it from doing its default action (following the link).
If your goal is to have the handler triggered only for the earlier link and not for the child link, then you may need to be more specific. You haven't shown enough of your markup for us to help you be more specific, though. If I assume your markup looks something like this:
<div class="archives">
<ul>
<li>
Link
<ul style="display:none;">
<li>Child Link</li>
</ul>
</li>
</ul>
</div>
...then the selector to match only the "Link" anchor and not the "Child Link" anchor would be .archives > ul > li > a (e.g., using direct child selectors).
Also note that you had stlye rather than style, but I assume that's just a typo in the question. (Why don't people use copy and paste?! ;-) )
The ChildLink is also matched by your selector, and in the click handler you're preventing the default action (which would be "navigate to Google").
So you should adapt your selector to only get the toggle Link, or you use this:
$('.archives ul li a').click(function(e){
if ($(this).siblings('ul').slideToggle().length) // if we found a list to toggle
e.preventDefault(); // or return false
});
Since your return false statement is cancelling the default link action, you need to be more specific so that you don't target the links that want to allow to continue to function.
Try this:
$('.archives > li > a').click(function () {
$(this).parent().find('ul').slideToggle();
return false;
});
jsFiddle example
By using the > child selector and changing the target to only the immediate child links of the outermost list, the sublinks won't be selected and will continue to work. In your code your $('.archives ul li a') will apply to any child links, not just the top level.

jQuery selector and cloning in a smart fashion

I am having trouble finding the way to solve this issue. I have this ul-menu output by Wordpress:
<ul class="menu">
<li>
Page 1
</li>
<li>
Page 2
</li>
</ul>
But I want the end result to be like this - cloning and appending the anchor and put a clone below:
<ul class="menu">
<li>
Page 1
Page 1
</li>
<li>
Page 2
Page 2
</li>
</ul>
I have used jQuery - but I am not having any luck at all for 2 hours of trial and error. This is as close as I can get. But it is wrong.
/*jQuery*/
$('.menu li a:first-child').eq(0).clone().insertAfter('.menu li a:first-child');
Fiddle here: http://jsfiddle.net/67jXz/1/
You're not supposed to .eq(0); that will limit it to the first a element that's matched, so that will be cloned and inserted after every subsequent a, resulting in copies of "Page 1".
Instead, you need to perform the cloning and inserting for each individual element by iterating with .each(), like so:
$('.menu li a:first-child').each(function() {
$(this).clone().insertAfter(this);
});
Note that the .insertAfter(this) part refers to inserting the cloned element after the original element that was matched by the .menu li a:first-child selector; the same this in $(this) that references the matched element.
Updated fiddle
Try this code:
$(function(){
$('.menu li a:first-child').each(function(k,v){
$(v).clone().insertAfter(v);
});
});
jsfiddle

jQuery Retrieve ID

I tried tackling my first project, an accordion menu. I have set to each item that needs to show/hide a class of .menu and an id.
While trying to retrieve the id's of each element i've used this statement:
var $currentId = $('ul.menu').attr('id');
Only problem is that it seems this only retrieves the id of the first element. Can anyone tell how can I retrieve all the It's to store them in a variable. I am planing to use if statements in order to check for each particular id when it's clicked.Thank You!
EDIT:It seems I was misunderstood what I have to do is this I'll start from the beginning:
Here is my HTML :
<ul id="container">
<li class="select">Downloads</li>
<li >
<ul class = "menu" id="first" >
<li>
iTunes
</li>
<li>
iTunes
</li>
</ul>
</li>
<li class="select">
Products List
</li>
<li>
<ul class = "menu" id="second" >
<li>
iTunes
</li>
<li>
iTunes
</li>
</ul>
</li>
What I have to do is when I click on the li with class of select I have to make the ul with the class of menu appear. How I wanted to do this to retrieve all the id's of the ul.menu and store them in a variable and when I click on any of the li.select the underlying ul should show.
Use each loop to get the ids of each element. Inside the loop use just just use this.id to get the id of the element where `this represents the dom element.
$('ul.menu').each(function(){
alert(this.id);
});
If you want the ids of all elements into an array you can use jQuery map method.
var Ids = $.map($('ul.menu'), function(){
return this.id;
});
map() translates all items in an array or object to new array of items. Ids will contain the ids of all the elements.
Then you can use $.inArray to search for a specific id within it. It will return its index or -1 if not found in the array.
if($.inArray("someId", Ids) != -1){
//Id found in the array
}
You may need to iterate over all the items and check the current status.
You can do this using the each method.
You can even dynamically add a listener for the click event for each element that matches your selector (in this case 'ul.menu'):
$('ul.menu').each(function(){
$(this).click(function()
{
alert('click');
});
});
EDIT: You can do this to hide/show the secondary items when the li.select items are clicked:
$('li.select').click(function(){
$(this).next().toggle();
});
This will give you an array that contains the ids:
var listOfIds = [];
$('ul.menu').each(function(){ listOfIds.push(this.id); });
The better thing to do would be to use
$('ul.menu li').click(function(event){
$(this).attr('id');
});
That will setup an event listener for each menu item and then allow you to do some thing when that menu item is clicked. The line with $(this).attr('id') can be replaced with any code you wish. Maybe even $(this).find('ul').show();

Categories