I am trying to find all top li from html .I have this mark up
<ul class="chapters">
<li>
tc_1
</li>
<li>
tc_2<ul>
<li>tc_1_1</li>
<li>tc_1_2</li>
<li>tc_1_3</li>
</ul>
</li>
<li>
tc_3
</li>
</ul>
Expected Answer: tc_1 ,tc_2,tc_3
I tried like that actually I inspect and write my code on console .
I tried like that $('li') and I get null
Then I put a class attribute "chapters" on ul then try like this
$(".chapters") again and I got null.
how to achieve this ?
You could iterate through the childNodes of the topmost li elements and filter their first textNode:
var textArray = $(".chapters > li").map(function() {
var nodes = this.childNodes, len = nodes.length;
for ( var i = 0; i < len; i++ ) {
if ( nodes[i].nodeType === 3 && $.trim(nodes[i].nodeValue).length ) {
return $.trim(nodes[i].nodeValue);
}
}
}).get();
console.log(textArray.join());
Here is a demo.
$('.chapters > li').each(function(){
alert($(this) .clone()
.children()
.remove()
.end()
.text());
});
JSFIDDLE
for more explanation you can read http://viralpatel.net/blogs/jquery-get-text-element-without-child-element/
This locates the ul, takes its immediate children (each of the lis), then removes any subchildren, .end(), to return to the lis, and .text() to get the contents.
$("ul").clone().children().children().remove().end().text()
This returns
"tc_1
tc_2
tc_3"
because of the line breaks and whitespace in between.
$(".chapters li").text()
should work.
Related
I ran into a problem of targeting different nested levels.
I read that it is possible to use .children feature + for loop, but I failed to do that.
Let's say I want to have a function where you pass the nesting level and it will change some property of <li> element on that level only.
I wrote this function to add classes to all last <li>
function changeElement(){
var lastLi = document.querySelectorAll('li:last-child');
for(i = 0; i < lastLi.length; i++){
lastLi[i].classList.add('last');
lastLi[i].style.background = 'green';
}
}
But now I need to target <li> elements on specific nested level
function changeElementOnLevel(level) {
var lastLi = document.querySelectorAll('li:last-child');
for (i = 0; i < lastLi.length; i++) {
//if level is 1 than we should change ul.root > li:last-child
//if level is 3 than we should change ALL <li:last-child> inside ul.root
> li > ul >
}
}
changeElementOnLevel(3)
<ul class='root'>
<li>Cat
<ul>
<li>Eats1</li>
<li>Sleeps</li>
<li>Snuggles</li>
<li>Plays</li>
<li>Meows</li>
</ul>
</li>
<li>Dog
<ul>
<li>Eats2</li>
<li>Sleeps</li>
<li>Snuggles</li>
<li>Plays</li>
<li>Barks</li>
</ul>
</li>
<li>Fish
<ul>
<li>Eats3</li>
<li>Sleeps</li>
<li>Swims</li>
<li>Plays</li>
<li>Swims</li>
</ul>
</li>
I consider that you have some nested ul tags, so in your example, the ul.root is level 1 and the inner ul is level 2 and if instead of 'Eats1' you have a ul tag, it will be level 3 and ...
Use this function:
function changeElementOnLevel(level){
let query = 'ul.root>li';
for (let i = 1; i < level; i++) {
query += '>ul>li'
}
query += ':last-child'
var lastLi = document.querySelectorAll(query);
for(i = 0; i < lastLi.length; i++){
// your code
}
}
It's depends what exactly you want to do. Are you going to change inner HTML on click? What do you mean by saying third level? You have 3 levels, and 3 lists. More information needed in order to help you.
var myElement = document.getElementsByClassName('root');
var arrayOfPets = myElement[0].children; // we are getting first level, all animals
var secondLevelArr = [];
for(i = 0; i < arrayOfPets.length; i++){
arrayOfPets[i].classList.add('last');
arrayOfPets[i].style.background = 'green';
var secondLevel = arrayOfPets[i].children[0].children;
// Push results to array
secondLevelArr.push(secondLevel);
} // will add class and background to all of them
// To add styles only to last element, you do not need to loop through them
arrayOfPets[arrayOfPets.length - 1].style.background = 'red';
for(i = 0; i < secondLevelArr.length; i++){
secondLevelArr[i][0].style.color = "white";
for(j = 0; j < secondLevelArr[i].length; j++){
secondLevelArr[i][j].style.textDecoration = 'line-through';
}
secondLevelArr[i][secondLevelArr[i].length - 1].style.textDecoration = 'none';
}
<ul class='root'>
<li>Cat
<ul>
<li>Eats1</li>
<li>Sleeps</li>
<li>Snuggles</li>
<li>Plays</li>
<li>Meows</li>
</ul>
</li>
<li>Dog
<ul>
<li>Eats2</li>
<li>Sleeps</li>
<li>Snuggles</li>
<li>Plays</li>
<li>Barks</li>
</ul>
</li>
<li>Fish
<ul>
<li>Eats3</li>
<li>Sleeps</li>
<li>Swims</li>
<li>Plays</li>
<li>Swims</li>
</ul>
</li>
</ul>
Here's what I came up with on the fly:
First, I'd make a small tweak to your HTML (I'm not sure what you have is actually valid...it might display how you like, but structurally it's going to cause problems). It is going to be difficult to set the value of an "li", if it has the value AND a nested list. If you reset the innerText or the innerHtml, you're going to completely overwrite the rest of the HTML in that tag, meaning you'll lose the nested list. You could work around this, but why bother, just close those tags predictably.
(Note I don't think any of the other answers address this issue).
So I'd first do this, notice how I close the "li" for Cat, Dog, and Fish:
<ul class='root'>
<li>Cat</li>
<ul>
<li>Eats1</li>
<li>Sleeps</li>
<li>Snuggles</li>
<li>Plays</li>
<li>Meows</li>
</ul>
<li>Dog</li>
<ul>
<li>Eats2</li>
<li>Sleeps</li>
<li>Snuggles</li>
<li>Plays</li>
<li>Barks</li>
</ul>
<li>Fish</li>
<ul>
<li>Eats3</li>
<li>Sleeps</li>
<li>Swims</li>
<li>Plays</li>
<li>Swims</li>
</ul>
</ul>
Now you can select elements and set values very straightforwardly (and the HTML is sound); the selectors here basically say "give me the li and ul children that are ONLY direct descendants of whatever element I'm currently working with" (otherwise you will get all of them no matter how deeply nested, which you don't want).
This code gets you the desired result by working recursively on nested "li" and "ul" collections, note that it also works on the top level "li" collection:
const top_layer = document.querySelectorAll ( '.root' );
const the_new_val = 'THE NEW VAL';
function setProps ( elems, level ) {
Array.from ( elems ).forEach ( x => {
const li = x.querySelectorAll ( ':scope > li' );
const ul = x.querySelectorAll ( ':scope > ul' );
if ( Array.from ( li ).length >= level ) {
li [ level ].innerText = the_new_val;
setProps ( li [ level ].children, level );
}
if ( Array.from ( ul ).length ) {
setProps ( ul, level );
}
});
}
setProps ( top_layer, 2 );
Yes, you could work with "children", but since we are directly interested in setting "li" values, which always appear in "ul" tags, the explicit selectors make it more obvious what's going on and would ignore any other children that may be around, feel free to make that change if you like.
The displayed result:
It is not very clear what you are trying to achieve :) But you can try :nth-child() - CSS pseudo-class selector that allows you to select elements based on their index (source order) inside their container.
This is just an example:
function find(n) {
// returns NodeList
var liNodeList = document.querySelectorAll('li:nth-child(' + n + ')');
console.log(li);
// if you need to do something with those elements, you can iterate
for (var i = 0; i < liNodeList.length; ++i) {
var item = liNodeList[i];
// do what you need with particular item
}
}
Also, the right method is querySelectorAll(...). What you are using querySelectAll does not exist.
Try like below,
I had used mix of query selectors and traversal to achieve this,
function changeElementOnLevel(level) {
var rootElement = document.querySelector(".root");
let targetLi;
if (level === 1) {
targetLi = rootElement.children[rootElement.children.length - 1];
let ul = targetLi.querySelector("ul"); // Since a ul is also there in level 1, I am expecting we need to change only li value not the ul
targetLi.textContent = "changed Value";
targetLi.append(ul);
} else if (level === 3) {
targetLi = rootElement.querySelector(
"li:last-child ul li:last-child"
);
targetLi.textContent = "changed Value";
}
}
changeElementOnLevel(3);
<ul class="root">
<li>
Cat
<ul>
<li>Eats1</li>
<li>Sleeps</li>
<li>Snuggles</li>
<li>Plays</li>
<li>Meows</li>
</ul>
</li>
<li>
Dog
<ul>
<li>Eats2</li>
<li>Sleeps</li>
<li>Snuggles</li>
<li>Plays</li>
<li>Barks</li>
</ul>
</li>
<li>
Fish
<ul>
<li>Eats3</li>
<li>Sleeps</li>
<li>Swims</li>
<li>Plays</li>
<li>Swims</li>
</ul>
</li>
</ul>
This code resolved my issue:
var parent = document.querySelector('.root');
function setFirstItemClass(element, level){
level = +level;
if(level == 1){
console.dir(element);
if(element.children.length > 0){
element.children[0].style.backgroundColor = 'red';
}
} else {
if(element.children.length > 0){
level--;
for(child of element.children){
setFirstItemClass(child, level);
}
}
}
}
setFirstItemClass(parent, 3);
I have the following code in my HTML:
<li class="strange">1</li>
<li class="strange">2</li>
<li class="strange">3</li>
<li class="strange">4</li>
I want to choose the exact <li> which contains number '3' as a Text.
Is there any method to choose the exact element?
Can we choose by using some OO-JS methods?
try using jQuery selector :contains
Description: Select all elements that contain the specified text.
$('li:contains("3")')
DEMO
Match exact text
As per #Raoulito mention in his comment here updated answer which match exact text using jQuery filter().
Description: Reduce the set of matched elements to those that match
the selector or pass the function's test.
$(document).ready(function(){
$("li").filter(function() {
return $(this).text() === "3";
}).css("color", "red");
});
DEMO
Try with jQuery contains
$('li:contains("3")');
Demo
Using JavaScript you can do like:
var list = document.getElementsByClassName("strange");
for (i = 0; i < list.length; i++) {
if(list[i].innerHTML ==3)
{
list[i].style.color = "blue";
list[i].style.backgroundColor = "red";
}
}
Check Fiddle.
If you want to use just javascript you can do something like that:
HTML
<ul id="ul">
<li class="strange">1</li>
<li class="strange">2</li>
<li class="strange">3</li>
<li class="strange">4</li>
</ul>
Javascript
var nums = document.getElementById("ul"); // get the ul element
var listItem = nums.getElementsByClassName("strange"); //fetch all elements inside the ul that have a class of strange
var element;
// loop through the list elements
for (var i=0; i < listItem.length; i++) {
// check whether list's inner html is equal to 3
if (parseInt( listItem[i].innerHTML) == 3) {
// set the value of element equal to the list element
element = listItem[i];
break;
}
}
console.log(element); // logs out the element that has the required value, you can use element.innerHTML to check
This only select the element which has exactly '3' as text:
$('li.strange').each(function() {
if ( $(this).text() == '3' ) {
$(this).css('color','red');
}
});
http://jsfiddle.net/dremztou/1/
$('li.strange:contains("3")').addClass('selected'); would select all the elements containing 3, including 13, 23, 33, etc.
I know there are dozens of similar topics but I am so dumb I can't learn anything from it.
Other words: My code is just mean and doesn't work with any fixes published online. ;)
My HTML:
<ul id="main_menu">
<li class="menu-item">Link 1</li>
<li class="menu-item">Link 2</li>
<li class="menu-item">Link 3</li>
<li class="menu-item">Link 4</li>
</ul>
and how the LI should look after JS does its magic:
...
<span data-title="Link 1">Link 1</span>
...
JS/JQ mission:
add class="roll-link" to every A
add SPAN right after A tag
add data-title="xxx" attribute to SPAN with A value (text exactly the same as the A)
close SPAN tag before A closing tag
My JS try:
var menuLis = document.querySelectorAll("ul.main_menu li"); //It's an Array right?
for(var i=0; i<menuLis.length; i++) {
this.nextChild.setAttribute('class', 'rollink');
var span = document.createElement('span');
this.nextChild.nextSibling.insertBefore(span); //Auto-closing </span> may be an issue...
span.setAttribute('data-title', hrefvalue[i]); //but how to get value?
}
It may be total crap but I have completely no experience in JS/JQ, only had few hours of basic training online...
Thanks for reading and even bigger thanks for trying to help.
Greets!
it should be as simple using jQuery(because you tagged it with jQuery)
jQuery(function ($) {
$('#main_menu li a').addClass('roll-link').wrapInner(function () {
return $('<span />', {
'data-title': $.trim($(this).html())
});
})
})
Demo: Fiddle
See
selectors
addClass()
wrapInner()
dom ready
To make your code work, first the main_menu is an id, not a class so you need to use id selector, then try
var as = document.querySelectorAll("#main_menu li a");
for (var i = 0; i < as.length; i++) {
as[i].className = 'rollink';
var span = document.createElement('span');
span.setAttribute('data-title', as[i].innerHTML);
span.appendChild(as[i].firstChild);
as[i].appendChild(span)
}
Demo: Fiddle
$('#main_menu > li > a').each(function () {
var text = $(this).addClass('roll-link').contents().first();
text.wrap($('<span>').attr('data-title', text.text()));
});
DEMO: http://jsfiddle.net/Bw723/
Here is your solution with pure Javascript.
//get the li elements
var menuLis = document.getElementById("main_menu").getElementsByTagName('li');
for(var i=0; i<menuLis.length; i++) {
menuLis[i].firstChild.setAttribute('class', 'rollink');
var span = document.createElement('span');
span.setAttribute('data-title', menuLis[i].innerHTML);
//If you want to get href of a then use this one.
//span.setAttribute('data-title', menuLis[i].href);
//appending the span into a
menuLis[i].firstChild.appendChild(span);
}
DEMO
I have a block of code like this
<span class='Wrapper'>
<span class="title"></span>
<span class="body">
<ul class="items">
<li></li>
<li></li>
</ul>
<span>
</span>
Once I access the span wrapper element using document.getElementsByTagName('span');
how do I access the inner span elements with title class and the ul elements of the span element with class body.I need to do this using plain javascript
First get an array holding all the span elements:
var yourSpans = document.getElementsByTagName('span');
Then then loop over each element in the array checking if the element has the specific class:
for(var i in yourSpans){
if (yourSpans[i].className == "title" || yourSpans[i].className == "body") {
// your code here
}
}
var spans = document.getElementsByTagName('span');
would return an array of spans. You would access the spans using spans[0], spans[1], etc.
Adding to reagan's answer, you would then need to do something like
for( var i = 0, j= spans.length; i < j; i+=1 ) {
var classes = span[i].getAttribute("class");
if( classes ) {
if( classes.indexOf("your_class_name") != -1) {
//span[i] is one of thelements you need containing 'your_class_name'.
}
}
}
I would really recommend using jQuery, it would make your life a lot easier!
$('.title').dostuff...
But if you want a JS only solution, here you go...
function editClass(matchClass,content) {
var elems = document.getElementsByTagName('*'),i;
for (i in elems) {
if((" "+elems[i].className+" ").indexOf(" "+matchClass+" ") > -1) {
elems[i].innerHTML = content;
}
}
}
Here is a fiddle (Pure-JS, no jQuery) as an example.
I'd like to use Javascript (not jquery) to access all items in a <ul> list and remove the active class from everything except my chosen menu item.
Here is the list:
<ul id='flash-menu'>
<li id="menu1" class='something active'>item 1</li>
<li id="menu2" class='somethingelse'>item 2</li>
<li id="menu3" class='somethingelse'>item 3</li>
</ul>
This is my javascript:
function updateMenu(view_name) {
var list_items = document.getElementById('flash-menu').childNodes;
for (var i=0 ; i<list_items.length ; i++){
list_items[i].className = list_items[i].className.replace('/\bactive\b/','');
}
document.getElementById(view_name).className += " active";
}
The last line of the Javascript (adding the active class) works, but I don't think I'm accessing the list items right to remove the classes from the other items. Any suggestions? - thanks!
First off, your regex is wrong:
list_items[i].className.replace(/\bactive\b/, '');
Note: No quotes on regex'es in JavaScript. A slighty altered, working version is available on JsFiddle.
Furthermore, I get a few instances of HTMLTextElements in list_items. They're breaking the loop (Fx3.6/Win7) when trying to access the non-existing className attribute. You can avoid this by either using:
var list_items = document.getElementById('flash-menu').getElementsByTagName('li');
// Selecting _all_ descendant <li> elements
or by checking for the existence of .className before read/write within the loop body (example). The latter is probably the cleanest choice since it still only affects direct children (you may have several levels of <ul>s in each <li>).
I.e.,
function updateMenu(view_name) {
var list_items = document.getElementById('flash-menu').childNodes;
for (var i=0, j=list_items.length; i<j; i++){
var elm = list_items[i];
if (elm.className) {
elm.className = elm.className.replace(/\bactive\b/, '');
}
}
document.getElementById(view_name).className += ' active';
}
You can use javascript function getElementsByTagName:
var listitems = document.getElementsByTagName("li");
this would return an array of all the lists and can be iterated for each list element and processed as required.
You can try:
In the case that you can have more than ul, first you have to get all references to them and then process each ul:
var uls = document.getElementsByTagName("ul");
for (uli=0;uli<uls.length;uli++) {
ul = uls[uli];
if (ul.nodeName == "UL" && ul.className == "classname") {
processUL(ul);
}
}
An illustration of proccessUL can be:
function processUL(ul) {
if (!ul.childNodes || ul.childNodes.length == 0) return;
// Iterate LIs
for (var itemi=0;itemi<ul.childNodes.length;itemi++) {
var item = ul.childNodes[itemi];
if (item.nodeName == "LI") {
// Iterate things in this LI
in the case that you need it put your code here
.....
}
}
}
Of course you can also use: item.className = "classname"; if you dont need to iterate between childs of LI
document.getElementById('flash-menu').childNodes will also include white space nodes.
function updateMenu(view_name) {
var list_items = document.getElementById('flash-menu').getElementsByTagName('li'), i;
for (i=0 ; i<list_items.length ; i++){
if (list_items[i].className.indexOf('active') > -1) {
list_items[i].className = list_items[i].className.replace(/\bactive\b/,'');
}
}
document.getElementById(view_name).className += " active";
}
i agree with jensgram,and you'd better code like this:
list_items[i].className.replace(/\bactive\b/g, '');
add the regex string a 'g'
g is for Global ,using ‘/g’ can replace all the same Which Match the regex ,but if you don't use '/g',you just replace the first string .
like this :
var test= "testeetest" ;
alert(test.replace(/e/,"")) ;//result
: tsteetest but using 'g' var
test= "testeetest" ;
alert(test.replace(/e/g,"")) ;//result
: tsttst
Have a look at this here: https://developer.mozilla.org/en-US/docs/Web/API/element.classList
It helped me a lot with finding class elements!
This is my solution, maybe not the best, but for my works fine.
window.addEventListener('load', iniciaEventos, false);
function iniciaEventos(e)
{
var menu = document.querySelectorAll('nav li');
for(var i = 0; i < menu.length; i++ )
{
menu[i].addEventListener('mousedown', clickMenu);
}
}
function clickMenu()
{
var menu = document.querySelectorAll('nav li');
for(var i = 0; i < menu.length; i++)
menu[i].classList.remove('active');
this.classList.add('active');
}