Select all "li" that do NOT contain a "ul" within - javascript

I'm building a nested drop down navigation for products and I want to automatically hide any categories that are empty.
Unfortunately server side language does not allow efficient way of doing this, so I thought I could output number of products each category has directly, then use jQuery to remove any empty nodes.
I want to target only the li's within nav#top_nav:
<nav id="top_nav">
<nav class="nav1">
<ul>
<li data-num-products="0">
AAA
<ul>
<li data-num-products="3">BBB</li>
<li data-num-products="0">CCC</li>
</ul>
</li>
<li data-num-products="7">DDD</li>
</ul>
<nav>
</nav>
Given 1 level of nesting ul's, I want to remove any li's that...
have no nested ul's within them and
have data-num-products == 0.
So in the example above AAA is retained because it has ul children, but CCC is removed because it has no ul children and no products.
UPDATE:
It might require 2 passes of removal because if a li contains a ul whose li elements are all removed, then we'll want to remove the ul too.

$( "#top_nav li").filter( function(){
return !this.getElementsByTagName("ul").length && !+this.getAttribute("data-num-products");
}).remove();
http://jsfiddle.net/X2D7y/
This will only remove if there are no UL descendants AND have attribute value of 0

Like this:
$('#top_nav li[data-num-products="0"]:not(:has(ul))').remove();
The selector breakdown is...
'#top_nav' // start at element with "top_nav" id
' ' // and select descendant...
'li' // li elements...
'[data-num-products="0"]' // ...where attribute "data-num-products" is "0"
':not(' // ...but exclude li elements that...
':has(ul)' // ...have descendant ul elements
')'
Regarding your updated question, just change :not(:has(ul)) to :not(:has(li)).
$('#top_nav li[data-num-products="0"]:not(:has(li))').remove();
http://jsfiddle.net/4YFDd/

$("#top_nav li[data-num-products='0']").filter(function() {
return $(this).children("ul").length === 0;
}).remove();

In Javascript:
var lis = document.querySelectorAll( '#top_nav li[data-num-products="0"]' );
for( var li = 0; li < lis.length; li++ ) {
!lis[li].getElementsByTagName( 'ul' ).length && lis[li].parentNode.removeChild( lis[li] );
};
Demo: http://jsfiddle.net/ThinkingStiff/2zS9B/

Related

do something when an instance becomes true in an each() function

I am running jQuery's .each function on a button click that runs through three line items. When another object on the page is clicked one of the line items will receive a class of 'selected', it starts on the first line item as default.
I want to execute something when the data-index of the li is greater than 0 and that li has the class name of selected.
const $orderStatusButton = $('form#oar-widget-orders-and-returns-form button');
$orderStatusButton.on('click', function() {
$(".selectric-scroll ul li").each(function() {
var DataIndex = $(this).data('index');
console.log(DataIndex);
if ((DataIndex > 0) && ($(this).hasClass('selected'))) {
$('div#oar_select-error').css('display', 'none');
} else {
$('div#oar_select-error').css('display', 'block');
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li data-index="0" class="selected"></li>
<li data-index="1" class=""></li>
<li data-index="2" class=""></li>
</ul>
Essentially, I want to hide a div when the line item with the data-index of 1 or 2 has the class name of selected and show it when the line item with the data-index of 0 has the class name selected and I'm not sure if I'm going about this correctly as it isn't working.
Since you are looping through all elements you'll always end up with the display value of the last iteration of the each function.
What you should probably be doing is using the selected class on your jquery selector:
$(".selectric-scroll ul li.selected")
then you only have to process one element on the each function.
Why not just use toggleClass("someclass") and add CSS
li.someclass {
display:none;
OR
visibility:hidden;
};

How to append <ul><li> element in between list of parent elements for some specific condition

I have a list of elements where i want to show max 5 elements and add show more button if total elements is more than 5. Show/Hide part is done but I am stuck to customize this list using jquery.
For Example here is a list of brands which having total 13 items.
<ul class="search-filter" id="attributeLevel1Facet">
<li>brand1</li>
<li>brand2</li>
<li>brand3</li>
<li>brand4</li>
<li>brand5</li>
<li>brand6</li>
<li>brand7</li>
<li>brand8</li>
<li>brand9</li>
<li>brand10</li>
<li>brand11</li>
<li>brand12</li>
<li>brand13</li>
</ul>
I want to make this list like this using jquery only if total item is more than 5
<ul class="search-filter" id="attributeLevel1Facet">
<li>brand1</li>
<li>brand2</li>
<li>brand3</li>
<li>brand4</li>
<li>brand5</li>
<li class="search-opt hide">
<ul class="search-opt">
<li>brand6</li>
<li>brand7</li>
<li>brand8</li>
<li>brand9</li>
<li>brand10</li>
<li>brand11</li>
<li>brand12</li>
<li>brand13</li>
</ul>
</li>
</ul>
<c:if test="${fn:length(***) gt 5}">
<a data-role="more-options" class="more-option" title="more-option">More Options</a>
</c:if>
I want to change the first list to second list using jquery if items > 5. If $("#attributeLevel1Facet > li").length > 5 then it should wrap it with another <ul><li> element and add more-option button (second list above).
Please help me.
You can use something like this,
$("#attributeLevel1Facet > li").filter(function() {
return $(this).index() > 4;
}).wrapAll("<li class='search-opt hide'><ul class='search-opt'></ul></li>");
Fiddle
In the above code, filter will return the li elements whose index is greater than 4. Then you can wrap them with any elements.
You can check the length of the li elements and then create a new ul like
var $lis = $('#attributeLevel1Facet li');
if ($lis.length > 5) {
var $ul = $('<ul class="search-opt"/>').append($lis.slice(5));
$ul.wrap('<li class="search-opt hide" />').parent().appendTo('#attributeLevel1Facet')
}
Demo: Fiddle
Check this -> jQuery load first 3 elements, click "load more" to display next 5 elements
From the answer i think you can get what you want
You can try something like this
if($('li').length > 5){
var element='<li class="search-opt hide">'+
'<ul class="search-opt">';
$('li:nth-child(5)').after(element)
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<ul class="search-filter" id="attributeLevel1Facet">
<li>brand1</li>
<li>brand2</li>
<li>brand3</li>
<li>brand4</li>
<li>brand5</li>
<li>brand6</li>
<li>brand7</li>
<li>brand8</li>
<li>brand9</li>
<li>brand10</li>
<li>brand11</li>
<li>brand12</li>
<li>brand13</li>
</ul>

jQuery: How to select a child or element with attribute that contains x?

Let's say I have some unordered lists with items, and one or many that has a class of 'active'
<ul id='list'>
<li class='row active'></li>
<li attrA='something active'></li>
<li attrB='active'></li>
</ul>
<ul id='otherList'>
<li class='row active'></li>
<li attrA='something active'></li>
<li attrB='active'></li>
</ul>
I want to get the item where class is "active" that is a child or element of the unordered list, "list".
I tried...
$("#list li[class~='active']")
$("#list > li[class~='active']")
But to no avail...
Here's a more detailed version of my problem. The code used to print this output is...
var listId = "typeahead-3-6260";
console.log( $("#"+listId+" li") );
console.log( $("#"+listId+" li[class~='active']") );
As you can see, line 2 had some results, but line 3 didn't.
$("#list li").filter(function(){return $(this).attr('name').indexOf("name") >= 0)
Use element.attributes to get the list of attributes and use nodeValue to get the value of attribute to check if it has active
Live Demo
$("#list li").each(function(){
for(i=0; i < this.attributes.length; i++)
if(this.attributes[i].nodeValue.indexOf('active') != -1)
$(this).text('has active attribute');
});
As you have mentioned that you need to check the 'class' attribute as 'active' here is the code that might help you
$(document).on('click','#list>li.active',function(){
alert('I am here');
});
JSFiddle

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"

Displaying nested HTML ul list in columns

I have an autogenerated nested list structure, like so
<ul>
<li>AAA</li>
<li>BBB
<ul>
<li>111
<ul>
<li>XXX</li>
</ul>
</li>
<li>222</li>
</ul>
</li>
<li>CCC</li>
...etc...
</ul>
I want to layout in columns like so:
AAA 111 XXX
BBB 222
CCC
Using JQuery and a few CSS tags, it's then relatively easy to create a navigation style menu. e.g. select BBB from the first column, then this makes its children appear in the second column. Any other second level depth ULs are hidden.
What's leaving me stuck is simply how to style the list in the first place to put the depths into columns. I can add tags to each UL or LI to show the depth. But if I simply use relative positioning and move each column left, then column 1 will leave a vertical gap where each of the entries have been moved across. Absolute positioning works, but doesn't seem too neat. Any better ideas?
Using recursive functions this can be quite straight-forward: http://jsfiddle.net/uvxfm/1/.
If you want interactivity you could save which elements are children of which parent and show the appropriate ones on click.
var $tr = $("#tr");
function text(x) { // returns text without children
return x.clone()
.children()
.remove()
.end()
.text();
}
function add(elem, level) {
elem.children().each(function() { // iterate children
var $this = $(this);
var appendToThis = $tr.find("td").eq(level); // find td to append this level to
var appendedText = text($this) + "<br>"; // text to append
if(appendToThis.length) { // if td exists
appendToThis.append(appendedText);
} else { // if td doesn't exist yet
$tr.append($("<td>").append(appendedText));
}
var children = $this.children();
if(children.length) { // call recursively for children, if any
add(children, level + 1);
}
});
}
add($("ul:eq(0)"), 0); // start the process
References: http://viralpatel.net/blogs/2011/02/jquery-get-text-element-without-child-element.html

Categories

Resources