I am trying to remove the last <li> element from a <ul> element only if it exceeds a particular length. For this, I am doing something like this:
var selector = "#ulelement"
if($(selector).children().length > threshold) {
$(selector + " >:last").remove();
}
I don't like the fact that I have to use the selector twice. Is there a shorter way to do this? Something like a "remove-if-length-greater-than-threshold" idea. I was thinking that maybe there is a way to do this using the live() function but I have no idea how.
It is common to cache the results of your selector. Here, you can search for the <li>s directly:
var lis = $("#ulelement li");
if(lis.length > threshold) {
lis.eq(lis.length - 1).remove();
}
In this case you can also achieve this with a single selector:
$("#ulelement li:gt(4):last").remove();
That is: Among all <li> with index greater than 4 (or your threshold), select the last and remove it.
var ul = document.getElementById('myUL');
if (ul.childNodes.length > threshold)
ul.lastChild.parentNode.removeChild(ul.lastChild);
Hope that helped.
selector = '#ulelement';
while($(selector).children().length > threshHold)
{
$(selector + " li:last").remove();
}
Try using a while loop, as your code only runs once, the while will loop untill its less than thresh hold!
Related
I'm currently toying around with Zendesk, and trying to make changes to a text element on the page. Sadly, Zendesk's elements are dynamic enough that the name of the main element will change on each page load.
Thankfully the structure of the element trees stay pretty static, but it's only the parent element's name that's changing:
#ember6030 > div.comment > div > p
#ember3483 > div.comment > div > p
Currently, here's where I'm at so far:
var item = document.querySelectorAll("[name^=ember] > div.comment > div > p");
var itemtext = item.innerHTML;
console.log(itemtext);
I'm sure I'm missing something, but wouldn't my selector var be correct?
Something like finding an element that begins with "ember" but then follows the rest of the parent-child tree just fine.
EDIT: Things are being a bit more stubborn than I thought, but I've got some extra details if this helps: For the div.comment > div > p elements, a handful of those load up. For right now, I'd like to try targeting just one, but if I can get the text contents of all these elements in console messages, that'd be awesome.
For those CSS paths you would use:
var item = document.querySelector("[id^=ember] > div.comment > div > p");
var itemtext = item.textContent;
console.log(itemtext);
since # is the CSS selector for the id attribute, not the name.
See also David Klinge's answer about NodeLists.
So the final code is:
var items = document.querySelectorAll("[id^=ember] > div.comment > div > p");
items.forEach (item => {
var itemtext = item.textContent;
console.log(itemtext);
} );
Finally, for what you seem to be trying to do, you probably want textContent instead of innerHTML. This avoids false hits on attributes, comments, etc.
document.querySelectorAll returns a NodeList that must be iterated over. You should be able to use a forEach to iterate over the elements querySelectorAll selects.
var items = document.querySelectorAll("[id^=ember] > div.comment > div > p");
items.forEach (item => {
var itemtext = item.textContent;
console.log(itemtext);
} );
Is there a possibility in jQuery to select by multiple possible attribute values without having to use a comma separated list of selectors.
So in stead of:
#list1 > option[value="1"], #list1 > option[value="2"], etc
Something like:
#list1 > option[value="1"|value="2"], etc
Not that I know of. The cleanest way I can think of doing this is to first select using the common elements across all items, then just .find() or .filter() the OR values out.
Something like
$('#list1 > option[value]')
.filter('[value="1"],[value="2"]')
;
You can make a custom jQuery function like this:
$.fn.filterAttrVals = function (attr, vals) {
var filter = '[' + attr + '="' + vals.split(',').join('"],[' + attr + '="') + '"]';
return this.filter(filter);
};
For your example you could use it in the following way:
$('#list1 > option').filterAttrVals('value','1,2');
You can do this
var valuesNeeded = [1,2,etc];
$('#list1 > option').filter(function( index ) {
return valuesNeeded.indexOf($(this).attr('value')) != -1;
});
I have a lot of <ul> list and try to get from every first li of this list the text.
the markup is simple like:
<ul>
<li>abc</li>
<li>def</li>
<li>ghi</li>
</ul>
and so on.
my jQuery attempt is:
var elems = $('ul'); // returns a nodeList
var arr = jQuery.makeArray(elems);
arr.reverse(); // use an Array method on list of dom elements
for( var i=0; i < elems.length; i++) {
console.log($(this).find('li:lt(1)').text());
}
But I have a mistake in the for loop with $(this). I don't know how to get the first text of ul number 1 or 3 if i don't use $(this).
So how can point it correctly in the for loop?
.each will give you this.
$('ul').each(function() {
console.log($(this).find('li').eq(0).text());
})
Alternative sytax using :first instead of :eq(0)
$('ul').each(function() {
console.log($(this).find('li:first').text());
});
or, to forgo the find() function.
$('ul').each(function() {
console.log( $('li:first', this).text() );
});
you can also use:
$("li:nth-child(1)").each(function()
{
console.log($(this).text());
});
notes:
with :nth-child(n), all children are counted, regardless of what they are.
with :nth-child(n), n is 1-based (the first index is 1 instead of 0)
is there any reason this chain does not work? It does not add the class:
document.getElementsByTagName('nav')[0].firstChild.className = "current"
It should return the first child of the nav element which is an <a> which does not happen.
Thanks for your help!
That's because you have text nodes between nav and a. You can filter them by nodeType:
var childNodes = document.getElementsByTagName('nav')[0].childNodes;
for (var i = 0; i < childNodes.length; i++) {
if (childNodes[i].nodeType !== 3) { // nodeType 3 is a text node
childNodes[i].className = "current"; // <a>
break;
}
}
It may seem strange but, for example, if you have the following markup:
<nav>
<a>afsa</a>
</nav>
Here's a DEMO.
Why does this happen? Because some browsers may interpret the space between <nav> and <a> as an extra text node. Thus, firstChild will no longer work since it'll return the text node instead.
If you had the following markup, it'd work:
<nav><a>afsa</a></nav>
You can simply document.querySelectorAll to select the list.
use "firstElementChild" to get first child node and add class.
const firstChild = document.querySelectorAll('nav').firstElementChild;
firstChild.classList.add('current');
The statement:
document.getElementsByTagName('nav')[0].firstChild.className = "current"
is somewhat fragile as any change in the assumed document structure breaks your code. So more robust do do something like:
var links,
navs = document.getElementsByTagName('nav');
if (navs) links = nav[0].getElementsByTagName('a');
if (links) links[0].className = links[0].className + ' ' + 'current';
You should also have robust addClassName and removeClassName functions.
Jquery can make this very easy:
$("#nav:first-child").addClass("current");
This is driving me nuts... But I surely miss something.
So the HTML looks like:
<ul>
<li><span>Product spec name</span><span>232112412</span></li>
<li><span>Product spec name</span><span>cm</span></li>
<li><span>Product spec name</span><span>80 cm</span></li>
<li><span>Product spec name</span><span>75 cm</span></li>
<li><span>Product spec name</span><span>cm</span></li>
</ul>
So what I want to achieve is to hide those list elements where the second span contains less than or equal to 2 characters.
I thought about putting them into a variable, loop through them, and if the length of the current item is less than or equal to 2 then jQuery should hide its parent.
Heres the code I wrote:
$(document).ready(function () {
var pspec = $('ul li span:nth-child(2)');
for(i=0;i<pspec.length;i++) {
if($(pspec[i]).text().length <= 2) {
$(this).parent().hide();
}
}
});
But this code won't do the trick... I still consider myself a jQuery beginner so please would You be so kind to help me out on this one?
Thanks in advance!
Best Wishes,
Matt
Demo: http://jsfiddle.net/PFaav/
$(document).ready(function () {
$('ul li').filter(function () {
return $(this).find('span').eq(1).text().length <= 2;
}).hide();
});
Your code will work if you replace
$(this).parent().hide();
by this
$(pspec[i]).parent().hide();
Try below,
$(document).ready(function(){
$.each ($('ul li'), function (idx, el) {
var $span = $(this).find('span').eq(1); //2nd span
if ($span.text().length <= 2) {
$span.parent().hide();
}
});
});
use the filter function
$('ul li span:nth-child(2)').filter(function() {
return $(this).text().length < 3; // <-- get 2nd span elements whose text length < 3
}).parent().hide(); // <-- hide parent elements of the returned elements
http://jsfiddle.net/y9dSU/
You could use jQuery each instead of using for and mixing jquery and javascript,
$(document).ready(function(){
var pspec = $('ul li span:nth-child(2)').each(function(){
if($(this).text().length <= 2) {
$(this).parent().hide();
}
});
});