How to select a child ul and not the parent ul? - javascript

I have a 'tree' of information that's displayed in many nested lists (ul li ul li ....). Two problems:
When I click a <li>, the parent ul is toggled as well as the child.
If I click on an <li> in a nested list that does not have a ul in it (say, Blue under Cranberry below) the parent ul is toggle, even though this shouldn't be matching 'li.has(ul)'.
I've tried various JQuery selectors, next(ul), children().first(), etc with the same results. No luck - though I'm sure I'm just missing something simple here.
On JSFiddle.
$('li.has(ul)').click(function() {
$(this).children('ul').toggle();
});
And the html:
<ul class="unstyled" id="full_ontology">
<li>Apple</li>
<li>Banana</li>
<li>Cranberry
<ul>
<li>Blue</li>
<li>Red
<ul>
<li>Round</li>
<li>Square</li>
<li>Circular</li>
</ul>
</li>
<li>Purple</li>
</ul>
</li>
<li>Date</li>
<li>Elderberry
<ul>
<li>Yellow</li>
<li>Pink</li>
<li>Black</li>
</ul>
</li>
<li>Fig</li>
</ul>

The problem is event propagation, prevent it
$('li:has(ul)').click(function (e) {
$(this).children('ul').addClass('thisOne').toggle();
});
$('li').click(function (e) {
e.stopPropagation();
});
Demo: Fiddle

It's all about propagation.
Here is the working JavaScript:
$('li').click(function(e) {
if ($(this).has('ul')) {
$(this).children('ul').addClass('thisOne').toggle();
}
e.stopPropagation();
});
JSFiddle: http://jsfiddle.net/zkFGU/3/
Basically, first you want this event on all of the list items, not just the ones that have the ul. The reason is because if you don't, you can't stop propagation. For example, "Blue" under "Cranberry" has no list, so it wouldn't call the event. However, because "Blue" is actually in Cranberry, which is a list item and does trigger the event, clicking Blue counts as clicking Cranberry, so Cranberry retracts. By giving Blue a change to chance to stop propagation, it prevents Cranberry from erroneously collapsing.
The other part of stopping propagation is simply to stop parents. We want only the element we directly clicked on (which is triggered first) and no other element to (attempt) to toggle.

Related

Click Event doesn't working for dynamically generated id of an span element

I am working on To Task Project.
Here i added items dynamically and also generated items and dynamically.
But when i want to delete items by clicking on a span element.
This click doesn't work.
I search each and every solution regarding this issue but none of them meets my requirement as i want.
$("[id^=remove_]").click(function() {
alert("working");
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul id="task-list">
<li class="task" id="item_1"><span class="delete-todo" id="remove_1">X</span><span class="main-task">3232</span></li><li class="task" id="item_2"><span class="delete-todo" id="remove_2">X</span><span class="main-task">djkhjsh</span></li>
<li class="task" id="item_3"><span class="delete-todo" id="remove_3">X</span><span class="main-task">snsajdsakm</span></li><li class="task" id="item_4"><span class="delete-todo" id="remove_4">X</span><span class="main-task">sahgsabsa\</span></li>
<li class="task" id="item_5"><span class="delete-todo" id="remove_5">X</span><span class="main-task">sjghjdsn</span></li>
</ul>
I tried this.
But It is not working.
Can someone explain me why its not working.
NB: I tried several solution found on SO.
Select the remove button by class, not ID. And change the code to use .on() method that is bound to the document so all elements can be selected as following:
$(document).on('click', '.delete-todo', (function() {
alert("working");
});
Delegate events to an ancestor tag that all clickable tags have in common. By doing so, allows any tags nested within the ancestor tag to be clickable (including tags that are added dynamically later on). In order to pinpoint the clicked tag, $(this) always points to the tag the user currently clicked (in this case this = event.target = "currently clicked tag"). By using this pattern, #id is pointless and .class a more efficient and versatile selector.
Anotomy of jQuery event delegation
//$('ancestor-selector').on('event', 'target-selector', callback);
<ul class="list">
<li class="task"><b class="delete">X</b>Item</li>
<li class="task"><b class="delete">X</b>Item</li>
<li class="task"><b class="delete">X</b>Item</li>
</ul>
$('.list').on('click', '.delete', deleteTask);
Demo
Details commented in demo
/*
Delegate the click event (ie .on('click',...)) to the
ancestor tag (ie .list) of all clickable tags (ie .delete)
NOTE: callback function (ie deleteTask) does NOT have "()" because
it is called when the event is triggered. deleteTask() would be
triggered immediately (which is not desired)
*/
$('.list').on('click', '.delete', deleteTask);
/*
Although this callback function doesn't use the event object,
it is passed by default and it's just a personal habit of mine to
always include it when defining a callback/event handler.
$(this) | in this case points to the tag the user clicked
(aka event.target)
.closest('.task') | will traverse up the DOM to find the first tag
matching .task
.remove() | self explanitory
*/
function deleteTask(event) {
$(this).closest('.task').remove();
}
/*
This code is to dynamically add items to list for demonstration
purposes and is not required as part of the solution to OP question
*/
$('.add').on('click', function(event) {
$('.list').append(`<li class='task'><b class='delete'>X</b> Dynamically added list item</li>`);
});
.delete {
display: inline-block;
padding: 1px 3px;
cursor: pointer
}
<ul class="list">
<li class="task"><b class="delete">X</b> 1010</li>
<li class="task"><b class="delete">X</b> 2929</li>
<li class="task"><b class="delete">X</b> 3838</li>
<li class="task"><b class="delete">X</b> 4747</li>
<li class="task"><b class="delete">X</b> 5656</li>
</ul>
<!--
This button is to dynamically add items to list for demonstration
purposes and is not required as part of the solution to OP question
-->
<button class='add'>ADD</button>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
I tried running the code snippet which you attached.
when i just clicked on the span element 'X' click event is getting triggered
when i clicked on entire li element click event is not getting triggered because you have defined your click event only for span element
Some Alternatives to trigger click event using ID:
$("#id").click(function(){
alert('working');
})

How do I make the eventListener to listen only for that particular element?

$ul.on("mouseenter", 'li', function() {
$("li button").slideDown();
});
So I have a li with a button adjacent to it like this:
<ul>
<li>Buy Robes<button>Delete</button></li>
<li>Go shopping<button>Delete</button></li>
<li>Buy noodles<button>Delete</button></li>
</ul>
But when I hover over any particular li, buttons for all lis is shown. But I want for that particular li I hover on. Like using the this keyword.
$ul.on("mouseenter", 'li', function() {
$(this).$("li button").slideDown();
});
Obviously the above code is wrong, but I want that to work something like this.
As pointed out in comments, your HTML is invalid. buttons are adjecent to the lis, but the selector you use focuses on finding the buttons inside the lis.
Correct your HTML; put the buttons inside the lis:
<ul>
<li>Buy Robes <button>Delete</button></li>
<li>Go shopping <button>Delete</button></li>
<li>Buy noodles <button>Delete</button></li>
</ul>
Now your selector $("li button") targets the correct elements. But still, there is one more issue. You're showing all the buttons, irrespective of which li was hovered. To correct this, scope your selector with this:
$ul.on("mouseenter", 'li', function() {
$(this).find("> button").slideDown();
});
This way, only the button, whose li was hovered, will be shown.

Clicking List-Items with JQuery outputs to many results

I do have a list item which looks like this:
HTML
<ul>
<li id="nav-menu-item-7" class="menu-element">
TEST 2
<ul>
<li id="nav-menu-item-8" class="menu-element">TEST 3</a></li>
<li id="nav-menu-item-11" class="menu-element">
PAGE 4
<ul>
<li id="nav-menu-item-77" class="menu-element">LINK</li>
</ul>
</li>
</ul>
</li>
</ul>
JQUERY
$(document).ready(function(){
$(document).on('click', '.menu-element', function(e){
var id = $(this).attr('id').replace(/\D/g,'');
alert(id);
});
});
I simply want to output the numeric ID of the container in an popup which i achieve with JQuery. The Problem that i am having now it that i get an Popup for the element i clicked and for the parent objects. This means in this case 77,11,7. I only want the first element to be ouput, means the 77 without having to change the whole structure of the menu. Any Ideas? Thank you.
Just stop the event propagation to the parent elements:
$(document).on('click', '.menu-element', function(e){
e.stopPropagation();
var id = $(this).attr('id').replace(/\D/g,'');
alert(id);
});
You should read about how events traverse the DOM, first from the document root to the clicked elements, then down to the document object. You can stop the propagation in both phases.

Add return false to parent link only with jQuery

I have a list inside a toggled div...
<li>
Link
<ul stlye="display:none;">
<li>Child Link</li>
</ul>
</li>
Ive written a piece of jQuery to toggle the display of the child UL only when a child link is clicked it no longer works (It doesnt go through to google), can anybody see where im going wrong?
// Dropdown
$('.archives ul li a').click(function(){
$(this).parent().find('ul').slideToggle();
return false;
});
STYLE is spelled wrong.
stlye=
From your post's title it appears you want something like this...
$('.archives ul li a').click(function(){
var $children = $(this).parent().find('ul');
$children.slideToggle();
return $children.length > 0 ? false : true;
});
Return will be false only when child ULs are found.
Assuming what you've shown is inside a ul which is in turn inside an element with class archives, then the selector .archives ul li a matches both the parent and child anchors, because you've used a descendant selector, and so your handler gets called for the child, and the return false; prevents it from doing its default action (following the link).
If your goal is to have the handler triggered only for the earlier link and not for the child link, then you may need to be more specific. You haven't shown enough of your markup for us to help you be more specific, though. If I assume your markup looks something like this:
<div class="archives">
<ul>
<li>
Link
<ul style="display:none;">
<li>Child Link</li>
</ul>
</li>
</ul>
</div>
...then the selector to match only the "Link" anchor and not the "Child Link" anchor would be .archives > ul > li > a (e.g., using direct child selectors).
Also note that you had stlye rather than style, but I assume that's just a typo in the question. (Why don't people use copy and paste?! ;-) )
The ChildLink is also matched by your selector, and in the click handler you're preventing the default action (which would be "navigate to Google").
So you should adapt your selector to only get the toggle Link, or you use this:
$('.archives ul li a').click(function(e){
if ($(this).siblings('ul').slideToggle().length) // if we found a list to toggle
e.preventDefault(); // or return false
});
Since your return false statement is cancelling the default link action, you need to be more specific so that you don't target the links that want to allow to continue to function.
Try this:
$('.archives > li > a').click(function () {
$(this).parent().find('ul').slideToggle();
return false;
});
jsFiddle example
By using the > child selector and changing the target to only the immediate child links of the outermost list, the sublinks won't be selected and will continue to work. In your code your $('.archives ul li a') will apply to any child links, not just the top level.

Swap a class of an <li> on click

I am trying to swap the selected class from tab to tab to display which one is current
Sample Code
$("li.selected").idTabs(function(id,list,set){
$("a",set).removeClass("selected")
.filter("[#href='"+id+"']",set).addClass("selected");
for(i in list)
$(list[i]).hide();
$(id).fadeIn();
return false;
});
so on click I am trying to remove and load the selected class with no luck, tried this
<ul class="idTabs">
<li class="selected">Request more information</li>
<li>Request a test drive</li>
<li>Make an offer</li>
<li>Get a quote</li>
</ul>
$('.idTabs li').click(function(){
$('.idTabs li').removeClass('selected');
$(this).addClass('selected');
return false;
});
Aaron, your second example seems like it should work, but only works on the first two list items for some reason. I added classes to the li's to make the selector more specific and it works fine now.
<ul class="idTabs">
<li class="navTab selected">Request more information</li>
<li class="navTab">Request a test drive</li>
<li class="navTab">Make an offer</li>
<li class="navTab">Get a quote</li>
</ul>
$('.navTab').click(function(){
$('.navTab').removeClass('selected');
$(this).addClass('selected');
return false;
});
Regarding your comment below:
It works every time when you click on an li... does not work when clicking on the anchor text because the click handler is not attached to that. You should add "display: block;" to your anchor within your li to expand the click area to the entire li (you will need to remove the padding from your li and in turn pad your 'a' so that the entire li is clickable). then... move the click handler to the anchor and have it change the parent's (li) class. I'm thinking it should go something like this (I'm not able to test it out right now):
$('.navTab a').click(function(){
$('.navTab').removeClass('selected');
$(this).parent().addClass('selected');
return false;
});
Give all your tabs a class like 'tab', then try something along the lines of this:
$('li.tab').click(function(){
$('.tab').removeClass('slected');
$(this).addClass('selected');
return false;
});
You haven't really said what the problem is, but I'm guessing your selectors aren't quite right. You seem to be passing in the "set" class as a second argument, rather than as part of the selector string.
Try:
$("a," + set).removeClass("selected")
and
.filter("[#href='"+id+"']" + set)

Categories