Given a containing jQuery object $c that has multiple elements in it that contain elements with classnames tmpSelected and selected, I would like to select only one element from $c for each classname, preferably tmpSelected. The markup is a complex version of this:
<ul id="a">
<li class="selected">Foo</li>
<li>Bar</li>
<li>Baz</li>
<li>Biz</li>
</ul>
<ul id="b">
<li>Woo</li>
<li>War</li>
<li class="tmpSelected">Waz</li>
<li>Wiz</li>
</ul>
<ul id="c">
<li class="selected">Xuu</li>
<li class="tmpSelected">Xur</li>
<li>Xuz</li>
<li>Xyz</li>
</ul>
In this case what I want to end up with is $("#a > .selected, #b > .tmpSelected, #c > .tmpSelected") – I want to avoid the .selected element if it has a sibling of .tmpSelected, and I don't want to select more than one child element for each member of $c where $c = $("#a, #b, #c").
So this is what I came up with:
var $c = $("#a, #b, #c");
var $selected = $c.map(function (idx, el) {
var $el = $(el);
var $tmpSel = $el.children(".tmpSelected");
return $tmpSel.length ? $tmpSel : $el.children(".selected");
});
Is there a reasonable way to do this without explicit looping? (P.S. - It's fine to return an empty selector when no .tmpSelected or .selected child exists.)
Here is a selector but it is pretty messy. I believe it gives the correct solution:
$("ul > li.tmpSelected, ul:not(:has(li.tmpSelected)) > li.selected");
First off you look for and .tmpSelected elements. Then you look for any ul that only have .selected elements. The :has selector looks for the child and I use the :not selector to find ul elements. Then I simply grab the children selected elements.
jsFiddle
I would suggest this which is pretty similar in concept to what you already had except this guarantees that it only ever returns a single item for each parent.
var selected = $("#a, #b, #c").map(function() {
var item = $(this).find(".tmpSelected");
if (!item.length) {
item = $(this).find(".selected");
}
return(item.get(0));
});
Related
Basically I have some HTML code that is a tree, I was traverse the Nodelist for it and and assign certain classes to nodes if they have children, here's a snippet:
<li id='test' class="parentNode">
<button class="customer-btn button"><a href='#'>Customer 6</a></button>
<ul>
<li>
<a href='#'>Customer A</a>
</li>
</ul>
</li>
<li class="parentNode">
<button class="customer-btn button"><a href='#'>Customer 7</a></button>
<ul>
<li>
<a href='#'> Customer A</a>
</li>
</ul>
</li>
This is my Javascript:
parent_list = document.getElementsByTagName("LI");
var i;
$(document).ready(function() {
for (i=0; i < parent_list.length; i++){
children = $(i).find('LI');
document.getElementById('check').innerHTML = children;
}
});
The for loop I have return [object Object], what's the best what to do this?
You don't need jQuery.
window.addEventListener('DOMContentLoaded', run );
function run() {
var allLIElements = document.getElementsByTagName('LI');
for( var i = 0; i < allLIElements.length; i++ ) {
var li = allLIElements[i];
if( li.firstElementChild != null ) {
li.classList.add('hasChildren');
}
}
}
Note that this will soon be unnecessary as CSS has the proposed :has() pseudo-class which you can use to select elements that meet some criteria.
https://developer.mozilla.org/en-US/docs/Web/CSS/:has
The :has() CSS pseudo-class represents an element if any of the selectors, relative to the:scope of the given element, passed as parameters, matches at least one element. The :has() pseudo-class takes a selector list as an argument.
Consider this style rule instead, it will match any li element that contains another element. No JavaScript required.
li:has(> *) { /* As of early 2017 no browser supports this selector yet! */
}
Since you're using jQuery you don't need a for loop
$('li').each(function(index, element){
if($(element).children().length > 0){
$(element).addClass('myClass')
}
})
You can use a for loop for anything, but your code isn't correct...assigning children to innerHTML isn't a compatible assignment, and doing it in a loop will just result in the element 'check' being assigned multiple times, not with an addition.
If you are using jQuery then use:
$('#check').append(children);
I am trying to store some list item into a variable and then loop through the variable and display only the list items that have a certain data attribute. It is worth mentioning that just using a simple show/hide on the li's on the page will not work for what I'm doing. https://jsfiddle.net/0jbnLv0k/
HTML:
<ul>
<li data-color="blue"></li>
<li data-color="red"></li>
<li data-color="green"></li>
<li data-color="blue"></li>
<li data-color="red"></li>
<li data-color="green"></li>
</ul>
<button class="blue">blue</button>
<button class="red">red</button>
<button class="yellow">yellow</button>
Jquery:
var items = $('ul li');
items.remove();
var result = $.grep(items, function(e){ return e.data == 'blue'; });
$('ul').html('<li>' + result + '</li>');
Problem is that
typeof e.data === 'undefined'
Solution is this:
var items = $('ul li');
items.remove();
var result = $.grep(items, function(e){
return $(e).attr('data-color') == 'blue';
});
$('ul').append( result );
But this is very bad end expensive ( time consuming ) way to display DOM elements. Much better would be add class with
display: none;
property.
Instead all this code you can use only one line:
$("li:not([data-color='blue'])").addClass('hide')
$("button").click(function() {
var getClass = $(this).attr("class");
$("ul").find("li").not("li[data-color="+getClass+"]").remove();
});
you could use not selector removing all list elements except the selected class.
https://jsfiddle.net/0jbnLv0k/5/
I have to arrays that I have made. One grabs some anchor tags and the other a set of ul's. The anchor tags all have a name attribute and the ul's have id's that match the anchor tags name attribute. In addition to this, all the ul's have the class ".students" and ".hidden". (all the .hidden does is set the display:none)
<div>
<ul>
<li><h4><a name="5th-grade">5th Grade</a></h4></li>
<li><h4><a name="6th-grade">6th Grade</a></h4></li>
<li><h4><a name="7th-grade">7th Grade</a></h4></li>
<li><h4><a name="8th-grade">8th Grade</a></h4></li>
</ul>
</div>
<div>
<ul id="5th-grade" class="students hidden ">
<li>Billy Bob</li>
</ul>
<ul id="6th-grade" class="students hidden">
<li>Bob Sackamano</li>
</ul>
<ul id="7th-grade" class="students hidden">
<li>Matt Blunt</li>
</ul>
</div>
What I am trying to do is have it so when I click on one of the anchor tags, it will match it's corresponding name attribute to the ul with the same id, make it appear by removing the ".hidden" class, and then hide any other ul's that do not match by adding the ".hidden" class.
Here is what I have come up with so far using a little jquery and where I stopped:
var aGradelist = $('#grade-list ul li a');
var aStudents = $('.students');
aGradelist.click(function(){
var i = '#' + $(this).attr('name');
$('.students'+i).removeClass('hidden');
for(var j=0;j<aStudents.length;j++)
{
console.log(aStudents.attr('id')[j]);
}
});
It's no problem getting the correct ul's to appear, but I couldn't add the ".hidden" class to the other ul's. I consoled it found that at least one of my problems is in the for loop, I am not going through each ul's in the aStudents array, but through the letters of the id of the first item in the aStudents array.
Am I even approaching this the right way? Have you got some ideas of how to do this?
Provided your html is valid (no duplicated IDs) this should do the trick.
var aGradelist = $('#grade-list ul li a');
var aStudents = $('.students');
aGradelist.click(function() {
aStudents.addClass('hidden');
$('#' + $(this).attr('name')).removeClass('hidden');
});
jQuery iterates over the DOM elements out of the box when you call methods on it.
Also, if you want to know what was wrong with your initial loop, you should use [j] before retrieving the id attribute which is a string. So to retrieve the id property properly:
for(var j=0;j<aStudents.length;j++) {
console.log(aStudents[j].id);
}
$()[j] is a shorthand for $().get(j) (jQuery.fn.get docs).
Try this:
var aGradelist = $('#grade-list ul li a');
var aStudents = $('.students');
aGradelist.click(function(){
var i = '#' + $(this).attr('name');
aStudents.addClass('hidden');
$('ul' + i).removeClass('hidden');
}
What this does is add the hidden class to all the uls and then remove it only from that ul which has the id of the anchor that was clicked
I'm using jQuery UI's sortable for my UL list. Each time the user sorts the list, I want each li element to update it's "position" attribute to it's position in the list.
<ul>
<li position="1">a</li>
<li position="2">b</li>
<li position="3">c</li>
</ul>
So when a user swaps c with a, the position will also update. I tried to use .each but it seems that javascript doesn't follow the order of how the LI elements are displayed but the order of the element's creation.
As mentioned in another answer, using update is all you need:
$(function() {
var $sortable = $('ul').sortable({
update: function(event, ui) {
var counter = 1;
$('li', $sortable).each(function() {
$(this).attr('position', counter);
counter++;
});
}
});
});
Example link
Have you tried :eq selector or index method? Provided you know which li element you're trying to find the position of you could find the position like so:
<ul>
<li id="c">c</li>
<li id="b">b</li>
<li id="a">a</li>
</ul>
var position = $('li#b').index();
You'll want to take advantage of the Sortable "update" event:
$( "ul" ).sortable({
update: function(event, ui) {
var order = $(this).sortable('serialize');
console.info(order);
}
});
You can then use the "serialize" method to pull the updated order of items. One requirement for this to work is that the IDs of each list item contain an underscore, so you'd want to update your HTML to:
<ul>
<li id="position_1">a</li>
<li id="position_2">b</li>
<li id="position_3">c</li>
</ul>
for example, how to detect the index of the li elem in it's parent ul?
<ul>
<li>this</li>
<li>is</li>
<li>ul</li>
</ul>
<ul>
<li>this</li>
<li>is</li>
<li>another</li>
<li>ul</li>
</ul>
if you have something like above, you could do jQuery as:
$(function(){
$('li').click(function(){ alert('the index is '+$(this).index()) })
})
this will alert the index of the li element based on its parent ul
more on index() here.
In non-IE browsers, whitespace between elements are considered text nodes (which is not the case in IE), which affects the index of subsequent elements. To give the same results in all browsers, the function below by default filters out whitespace nodes, and has an optional parameter to include them if that's what you want:
function indexOfNode(node, includeWhitespace) {
var index = 0, n = node;
while ( (n = n.previousSibling) ) {
if (includeWhitespace || n.nodeType != 3 || !/^\s*$/.test(n.data)) {
++index;
}
}
return index;
}