I have a nested list, as shown below. I'm struggling to find a specific CSS selector that I can use in querySelectorAll() JavaScript function, which will only affect all <li> tags that do NOT contain <ul> tag. So in this case it should be just the lines
<li>foot</li>
<li>leg</li>
<li>tiger</li>
<li>elephant</li>
<li>food</li>
I tried querySelectorAll("ol > li") and some others ways with the :not() selector, but to no success.
querySelectorAll("ol > li ul") is the opposite of what I want, because when I use
console.log(document.getElementById("translation").querySelectorAll("ol > li ul").length) it returns 3.
I need code of the following type console.log(document.getElementById("translation").querySelectorAll(blank).length) which will return 5. I don't know if that's possible, and I can't find it anywhere online.
Another way of looking at it is to let this code of CSS only color items which do not contain other nested lists (so that only points 2-6 will have colored background):
#translation ol > li ul{
background-color: cyan;
}
The entire list:
<div id="translation">
<ol>
<li>
<ul>
<li>parting</li>
<li>parting</li>
<li>parting</li>
<li>separation</li>
<li>separation</li>
<li>separation</li>
<li>farewell</li>
<li>(lateral) branch</li>
<li>fork</li>
<li>offshoot</li>
</ul>
</li>
<li>foot</li>
<li>leg</li>
<li>tiger</li>
<li>elephant</li>
<li>food</li>
<li>
<ul>
<li>branch</li>
<li>parting</li>
<li>disaffiliation</li>
<li>disaffiliation</li>
<li>separation</li>
<li>(lateral) branch</li>
<li>farewell</li>
<li>branch</li>
<li>division</li>
</ul>
</li>
<li>
<ul>
<li>dissociation</li>
<li>disaffiliation</li>
<li>dissociation</li>
</ul>
</li>
</ol>
</div>
You need to check each li is childNodes has ul. For example like this:
const test = [...document.querySelectorAll("ol > li")].filter(
li => ![...li.childNodes].find(child => child.localName === "ul")
);
if you are able to use jQuery then make the :not selector to catch elements that have ul as childs.
something like this:
$("translation").find("ol > li:not(:has(>ul))")
Notice this solution will work for jQuery selectors but not for the Vanilla JS (Vanilla JS doesn't support :has as a seclctor).
With Vanilla JS you might want to do something like this:
const elems = document.getElementById("translation").querySelectorAll('ol > li');
let items = Array.from(elems); // convert to Array type
items.filter(item => { // filter the array
item.querySelectorAll('ul').length == 0
})
Related
How can I add a class to a span element for only these spans which are part of li tags with class='error'
per example, this is the HTML which is generated:
<li class="error">
<!--if list has class error, then add also class error to span which is part of that list tag-->
<span class="error"></span>
</li>
This is the code I have so far:
if(status == 'error') {
data.context.addClass('error'); // adds class error to li tag
}
This is easy using JQuery, just select all li items that have the class .error and then use find() to find all the spam elements inside it, finally add the class .error to those:
$(document).ready(function()
{
$("li.error").find("span").addClass("error");
});
span.error {
background-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<li class="">
<span>Parent "li" do not have class error</span>
</li>
<li class="error">
<span>Parent "li" have class error</span>
</li>
This should achieve the expected result. Iterate through all li elements with the error class and find any spans, then add the class error to it.
$('li.error').each(function() {
$(this).find('span').addClass('error');
})
For a pure vanilla JavaScript solution, you can use an attribute selector to find all your li elements that have a class called error. Once you have that list of li elements, just loop thru (using forEach or a for-loop) and append an a span element as a child of the li. The span element should also be given a class attribute called "error" via setAttribute.
function appendErrorSpan(li) {
var errorEl = document.createElement('span');
errorEl.setAttribute('class', 'error');
errorEl.appendChild(document.createTextNode('Error span added!'));
li.appendChild(errorEl);
};
var errorListItems = document.querySelectorAll('li[class="error"]');
errorListItems.forEach(appendErrorSpan);
<li class="">
<span></span>
</li>
<li class="error">
</li>
Instead of using loops you can do it like this-
let a=document.querySelectorAll("li.error > span");
$(a).addClass('error');
Easiest way-
$("li.error > span").addClass('error');
This is a build off of my previous question: Select a child of a child
I now have a <ul> within another <ul>. The behavior is an expandable menu. I'm doing this by adding and removing classes. For some reason...on the sub list - it completely removes the <li> elements from the DOM rather than just toggling it's classes. Why would it do that!?
You can see an example below:
$(function() {
// main expansion element
$(".expander").click(function() {
var subShown = $("ul > li", this).hasClass("show");
if (!subShown) {
$(".indented", this).slideDown('100').addClass("show");
$(".caret", this).addClass("reversedCaret");
} else {
$(".indented", this).slideUp('100').removeClass("show");
$(".caret", this).removeClass("reversedCaret");
}
});
// sub expansion element
$(".sub-expander, .caret").click(function() {
var subSelectText = $(".sub-expander").text();
if (subSelectText != "More") {
$(".indented--sub", this).slideUp('100').removeClass("show");
$(".caret", this).removeClass("reversedCaret");
$(".more-or-less").text("More");
} else {
$(".indented--sub", this).slideDown('100').addClass("show");
$(".caret", this).removeClass("reversedCaret");
$(".more-or-less").text("Show Less");
}
});
// stop propagation on the link element within .expander class
$(".indented").click(function(event) {
event.stopPropagation();
});
});
.expander:hover {
cursor: pointer;
}
.sub-expander--indented {
padding: 0 0 0 23px;
}
.sub-caret {
margin-right: 75px;
}
.indented,
.indented--sub {
display: none;
}
.show {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<div class="expander">
<span class="caret downCaret right visibleCaret">+</span>
<ul>
<li class="category">Item 1
</li>
<li class="indented">Item 2</li>
<li class="indented">Item 3
<ul class="sub-expander indented more" style="padding-top:
0px;">
<li class="indented--sub">Chapter 5</li>
<li class="indented--sub">Chapter 6</li>
<li class="indented--sub">Chapter 7</li>
<span class="sub-caret moreCaret visibleLessCaret right">+</span>
<li class="more-or-less less sub-expander--
indented">More</li>
</ul>
</li>
</ul>
</div>
I'm giving it a separate classname to differentiate from the main section so that they don't show on initial open, so I'm not sure why it is behaving the way it is. It seems like there is a better way to go about this but I don't know what that would be.
UPDATE: was pointed out I did not have valid HTML. Fixed it following this thread. Still broken.
UPDATE #2: It seems like the problem is .text() - so it completely erases everything? I thought it just replaced the text node, and not all of it's children. I tried .html() but it does the same thing. What method do I use to just replace text then?
UPDATE #3 - one answer suggests I needed a more specific selector. I gave the list item a class of .more-or-less but doing that, it doesn't expand at all.
You'd probably want to use a more strict selector.
In your example case you use .sub-expander to select the node of which you want to replace the text. This matches with the ul.sub-expander however.
Since you want it to replace the text of the li.sub-expander the simplest thing you could do would be to use a more specific selector:
$("li.sub-expander").text("Show Less"); or (better) give the node which contains the text you want to replace another classname, id or other identifier to prevent targeting a different element.
have a code structure like this :
<ul class="ulNotif">
<span id="notiftype1"></span>
<span id="notiftype2"></span>
<span id="notiftype3"></span>
</ul>
I sent li with jquery to each span. Every span will have li if the data match with the condition.
I tried to count with $(".ulNotif span li").length but still 0 value.
How to count span which have li inside it with javascript? Thanks
#junkfoodjunkie is correct. You should use <li> tags as direct descendants of <ul>. Nonetheless, your code should look like this:
$(".ulNotif span li").length
Your <ul> has a .class, not an #ID
EDIT: This works totally find for me: returns 3
$('.ulNotif span').each((index, child) => {
$(child).append('<li>')
})
console.log($('.ulNotif span li').length)
I am using socket.io and twitter streaming API.
Now I have to keep the recent three tweets in a list
<ul>
<li></li>
<li></li>
<li></li>
</ul>
My Question is how to do this in Jquery? Whenever there is a new tweet I want to insert it in to first li and move the first li tweet in to second li and so on and remove the 4th tweet.
Use .prepend to insert new element at the beginning of the target element.
Use :gt() to remove the element if length is greater than expected
length.
var count = 0;
$('button').on('click', function() {
var li = '<li>Data New' + count + '</li>';
++count;
$('ul').prepend(li);
if ($('ul li').length > 3) {
$('ul li:gt(2)').remove();
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<ul>
<li>Data</li>
<li>Data</li>
<li>Data</li>
</ul>
<button>Add</button>
Use .prepend() method in jQuery. This adds HTML element as the first node of the target.
$("ul").prepend("<li>new item</li>");
And as #Kartikeya suggested, remove the last one like:
$("ul > li:last").remove();
I have a series of unordered lists wrapped in another unordered list. The task is to separate the individual li tags with commas and omitting the each list's last li tag.
I can do it on an individual list level i.e. When testing with just one list I was able to remove the last comma from the last element. But when I'm trying to apply the JavaScript in affects the last li element of the wrapping ul...
Here is my code:
<script type="text/javascript">
$(document).ready(function () {
$('.tagsList li span').each(function () {
$(this).append(',');
});
var lTag = $('.tagsList li:last span').text().replace(',','');
$('.tagsList li:last span').text(lTag);
});
</script>
<ul class="postsList">
<li>
<ul class="tagsList">
<li><span>tag1</span></li>
<li><span>tag2</span></li>
</ul>
</li>
<li>
<ul class="tagsList">
<li><span>tag1</span></li>
<li><span>tag2</span></li>
</ul>
</li>
</ul>
You should use a slightly modified selector
$('.tagsList li:not(:last-child) span').each(function () {
$(this).append(',');
});
This directly target all the span elements inside a li except the last li of each list.
demo http://jsfiddle.net/gaby/wwTjH/2/