Simple mind-blowing thing; CSS selectors vs jQuery selectors - javascript

CSS selector :not() is not working and jQuery :not does.
Consider this structure:
<div class="main">
<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>
<div>
<div class="doc-view">
<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>
</div>
</div>
And this simple CSS:
li:not(.doc-view li) {
color:green;
}
And a little of jQuery here:
$('li:not(.doc-view li)').css('color','red');
As you can see, selectors are the same. But if you play with jsFiddle, you will see, that are not the same. jQuery selector targets elements, CSS does not.
http://jsfiddle.net/LL429/3/
EDIT:
The question is:
How to target all <li>s on the page, except those which are in .doc-view container?

This is because they mean different things. Let's start with the CSS.
li:not(.doc-view li)
This means select all list items, but not those that have descendants that have the class doc-view with a list item descendant. Your code has none of those, plus only simple selectors are allow to be used with :not(), so the selector is invalid anyway.
Now for the jQuery.
$('li:not(.doc-view li)')
This says select all list items, but do not include in that collection any elements with the class doc-view with a list item descendant. This works because it first select all list items, and then removes the matching group of elements that fit the :not(.doc-view li) selector.

CSS selectors != jQuery selectors.
jQuery uses Sizzle CSS selector engine.
Try:
ul > li {
color:green;
}
DEMO

Since .doc-view is on the div, you need to use the negation on the div and then access the li.
So the pure CSS solution is:
ul > li {
color: red;
}
div:not(.doc-view) > ul > li {
color:green;
}
See working jsFiddle demo

Related

Why isn't jQuery removeClass working with a click function?

I can't seem to understand why removeClass isn't removing the active class when I click on another li. If it were the same li then I could use siblings to remove the class but sadly that doesn't work here either.
I'd like to understand this simple problem that I'm having.
$('.r-picker li').click(function(){
$('.r-picker li .data.active').removeClass('active');
$('.r-picker li .data').addClass('active');
});
.active{
color:red;
font-size:25px;
font-weight:bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="r-picker">
<ul>
<li>
<div class="data">me1</div>
</li>
<li>
<div class="data">me1</div>
</li>
</ul>
</div>
http://jsfiddle.net/Lb65e/125/
Updated fiddle.
You should use $(this) instead to refer to the clicked li :
$('.data', this).addClass('active');
Else the .data selector in your code will add class to all the elements with this class :
$('.r-picker li .data').addClass('active');
NOTE : Also you need to remove the class active from all the elments with class data when you click using :
$('.r-picker li .data').removeClass('active');
Hope this helps.
$('.r-picker li').click(function() {
$('.r-picker li .data').removeClass('active');
$('.data', this).addClass('active');
});
.active{
color:red;
font-size:25px;
font-weight:bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="r-picker">
<ul>
<li>
<div class="data">me1</div>
</li>
<li>
<div class="data">me1</div>
</li>
</ul>
</div>
Your code is working. Here's what it does:
$('.r-picker li .data.active').removeClass('active');
That finds all elements with classes data and active, and removes active from all of them.
$('.r-picker li .data').addClass('active');
That finds all elements with class data and adds the class active to all of them.
Thus, once that runs, you'll have added the class active back to all the elements from which it was removed.
In your event handler, when adding the class, you need to add it only to the <li> involved:
$(this).find(".data").addClass("active");
The jQuery event mechanism makes sure that this is bound to the DOM element involved with the event, so $(this) gives you a jQuery object for that element. The .find() method performs a DOM selector search starting from that element, so $(this).find(".data") finds elements with class data only in the DOM subtree beneath the clicked element.
You need to highlight the div in the current li.
$('.r-picker li').click(function() {
$('.r-picker li .data.active').removeClass('active');
$(this).find('.data').addClass('active');
});
Which first removes the active class, and then find the div.data in .this li and adds the class back...
Heres your fiddle updated and working: http://jsfiddle.net/Lb65e/126/

previousSibling and nextSibling returns text?

Could someone clarify why the code below returns #text instead of 'li'
?
Shouldn't the next sibling of first li be li ?
Similarly previous sibling of last li be li ?
<body>
<ul>
<!-- comment -->
<li id="A"></li>
<li id="B"></li>
<!-- comment -->
</ul>
<script>
//cache selection of the ul
var ul = document.querySelector('ul');
//What is the nextSibling of the first li?
console.log(ul.querySelector('#A').nextSibling.nodeName); //logs text
//What is the previousSibling of the last li?
console.log(ul.querySelector('#B').previousSibling.nodeName); //logs text
</script>
</body>
The whitespace between the two is also a node. That's why JS libraries exist. To give you options like retrieving element siblings.
If the HTML source looked like this:
<ul>
<li id="A"></li><li id="B"></li>
</ul>
It would work as you expect it, because there's no whitespace between the li elements.
More recently, two more properties have been introduced, called previousElementSibling and nextElementSibling, which ignore that whitespace. It works from IE9 and up, with the other major browsers supporting it for a while now.
beter is use 'nextElementSibling' and 'previousElementSibling'

jquery how to select the next element after the current element

<div id="main">
<ul>
<li><div></div></li>
</ul>
<div>needs to be selected</div>
<div></div>
</div>
How can I select, using jquery, only the first div inside the main div?
Or
How can I select, using jquery, only the first div after <ul>?
I tried doing this but didn't work:
$('#main ul').next('div:first').addClass('my_class');
Try with a child selector:
$('#main > div').addClass('my_class');
If you have more than one direct child div and want the first one, you can do:
$('#main > div').first().addClass('my_class');
The easiest way would be to add a class to the div you are interested in so that you can easily identify it:
<div class="to-be-selected">needs to be selected</div>
then
$('#main > div.to-be-selected').addClass('my_class');
or even
$('#main .to-be-selected').addClass('my_class');
etc.
You can do simply like that.
$("#main").find("ul").next().addClass("my_class");
Hope it will be work for you.
Standard CSS selectors will do, no need for jQuery extravaganza:
First div inside #main, using :first-of-type:
jQuery('#main > div:first-of-type').addClass('my_class').
First div after ul, using the adjacent sibling selector.
jQuery('#main > ul + div').addClass('my_class');

Select LI children from a not none-displayed UL

The title sounds strange but what I want to achieve is simple.
In a tree of uls I want to get all li children from any ul that have not the - inline - style display: none. So I found this post and I mixed with the negation function :not(). The result was:
'ul:not([style*="display: none"]) .k-item'
Where .k-item is a common class for all my li elements. That selector worked in this simple fiddle. The problem is that it doesn't works in my application. I have a screenshot of some console commands that will illustrate my scenario:
As you can see on second command, it returns some li elements that lies under an ul which haves display: none among other attributes in its inline style. Example of those unexpected li with attribute data-uid with values starting with 099d, bbca and 14d2.
I don't know what I'm doing wrong or if exists a better selector for that purpose.
I would suggest using jQuery's :visible rather than looking for something in the style string and string matching in the style string could be problematic.
$("ul:visible .k-item")
First of all get all the li and check whether its parent (ul) is visible.
jsfiddle
$('li', '#layers').each(function(){
if($(this).parent().is(":visible")){
alert($(this).text())
}
});
OR
a neat version
jsfiddle
$(".k-item:visible").each(function(){
alert($(this).text())
});
Try using
$('ul:not([style*="display: none"]) li.k-item').each(function() { alert($(this).html()) });
HTML
<ul style="display: none">
<li class="k-item">1</li>
<li class="k-item">2</li>
<li class="k-item">3</li>
</ul>
<ul>
<li class="k-item">4</li>
<li class="k-item">5</li>
<li class="k-item">6</li>
</ul>
Fiddle: http://jsfiddle.net/3M2ZM/

How would I prevent JQuery from selecting more than 1 level of children dom elements?

How would I only select Item A and Item B pragmatically while excluding the sub item?
<div id="nav">
<ul>
<li>
<p>Item A</p>
<ul>
<li>
<p>Sub Item A</p>
</li>
</ul>
</li>
<li>Item B</li>
</ul>
</div>
Well after a quick test run - this is my contribution to this issue
$("#nav p:first, #nav > ul > li:eq(1)");
You specified that you wanted only those two items and no sub items so this is what jQuery will capture :
[<p>​Item A​</p>​, <li>​Item B​</li>​]
You can easily separate selectors by placing a comma between them.
Now that you have seen my solution I would strongly suggest that you take Xenon06's advice...
Giving your markup classes really helps you to keep track of them. Especially with jQuery. The class attribute while IMO mostly used for styling is a perfectly valid selector to use and abuse in your jQuery code. That is of course if you actually have access to that HTML. If you don't kindly ignore my last paragraph :)
This will select any first level li's that have only text and no children and any children of a li that isnt a ul. Given this is not a good way to do it. You should really put classes on your stuff to start with. But if that's not an option this will get you there.
$($('#nav').children()).children().each(function(){
if($(this).text() !== "" && $(this).children().length === 0 ){
$(this).addClass("IwantThisElement");
}
});
$($($('#nav') .children()) .children()) .children(':not(ul)').each(function(){
if($(this).text() !== ""){
$(this).addClass("IwantThisElement");
}
});
$('.IwantThisElement').text('Assuming Control');
Well, if your structure was more consistent, you could use direct children selectors, ie:
$("#nav ul li > p")
However your Item B is not in a paragraph. Without defining more what you want, you'll need to put classes on the items you want and do
$("#nav .yourclass")

Categories