why i can't toggle classes? - javascript

I work on a small to-do list that you can add new task then when you click on the li element it should add 2 classes and remove 1
<ul id="myUL" class="todoList">
<li class='todoList card border-primary'>my li</li>
</ul>
this is my js code for toggling classes
$( "#myUL" ).children().on("click",function() {
$( this ).toggleClass( "checked" );
$( this ).toggleClass( "border-primary" );
$( this ).toggleClass( "border-success" );
});
I included it in the bottom bootstrap.min ,and jquery-3.3.1.min js files because it dependencies for bootstrap.
But my code works only on the first element. How can I fix it ?

This works only on the first element because you run the JS code to attach eventlisteners only once. Thus, only the first element has the 'click' eventlistener attached to it.
You can add a listener to the parent and then handle it from there.
Or, alternative is remove all the eventlisteners of all li's and then re-add them using the code you have right now. This is not a good practice but will get the work done.
Here is a example from w3schools
// Add a "checked" symbol when clicking on a list item
var list = document.querySelector('ul');
list.addEventListener('click', function(ev) {
if (ev.target.tagName === 'LI') {
ev.target.classList.toggle('checked');
}
}, false);
Source: https://www.w3schools.com/howto/howto_js_todolist.asp

Have you tried something like this?
$("#myUL li").click(function() {
$(this).toggleClass("checked");
$(this).toggleClass("border-primary");
$(this).toggleClass("border-success");
});

Related

How to sync slideToggle() elements so they expand/collapse all together regardless of current state?

I'm trying to show/hide sections using triggers based on classes. So it's easy to throw the trigger class on any element and it will slideToggle the next show/hide element class in the DOM.
The first part works fine. Now I'm adding a trigger to expand/collapse ALL elements. Trouble is, I can't figure out how to sync their states.
For example: I click on 3 of 8 elements so they slide open and become visible, the other 5 are still closed... Now when I click my expand/collapse all trigger, using slideToggle just switches their states so I end up with 3 closed and 5 open.
How do I get their states to sync regardless of what their current state is?
I've been trying to figure out the conditionals to check if the trigger has the opened or close class on it, then toggle the next element, but I've only made a mess so far.
Here's my code:
jQuery( document ).ready( function( $ ) {
// The element to hide/reveal
$( '.bodhi-hide-reveal' ).hide();
// The trigger to hide/reveal
$( '.bodhi-reveal-trigger' ).click( function( e ) {
e.preventDefault();
// Target only the next element to hide/reveal and toggle it
$( this ).next( '.bodhi-hide-reveal' ).slideToggle();
// Toggle the trigger class
$( '.bodhi-reveal-trigger' ).toggleClass( 'opened closed' );
});
// Expand/collapse all button
$( '.expand-collapse-all' ).click( function( e ) {
e.preventDefault();
// Find all hide/reveal elements and toggle them all together
$( 'body' ).find( '.bodhi-hide-reveal' ).slideToggle();
});
});
Just a class named "opened" is enough to detect if the next div is open or close. So I write an IF/ELSE block to decide whether to slideUp or SlideDown. Also you have to decide what would you do when some of divs are expanded? Do you want to collapse all or expand all? I prefer to collapse all first. So I search to find an opened one (using array length) and if I find it, I collapse all divs, elsewhere I expand all:
Also there are some inefficient selectors like $( 'body' ).find(). I also replace those selectors with efficient ones:
jQuery( document ).ready( function( $ ) {
// The element to hide/reveal
$('.bodhi-hide-reveal').hide();
$('.bodhi-reveal-trigger').removeClass("opened");
// The trigger to hide/reveal
$('.bodhi-reveal-trigger').click( function( e ) {
e.preventDefault();
// Target only the next element to hide/reveal and toggle it
$(this).next('.bodhi-hide-reveal').slideToggle();
// Toggle the trigger class
$(this).toggleClass('opened');
});
// Expand/collapse all button
$('.expand-collapse-all').click( function( e ) {
e.preventDefault();
//Check if there is at least one open div:
if ($('.bodhi-reveal-trigger.opened').length){
$('.bodhi-reveal-trigger').removeClass("opened")
$('.bodhi-hide-reveal').stop().slideUp();
}
else {
$('.bodhi-reveal-trigger').addClass("opened")
$('.bodhi-hide-reveal').stop().slideDown();
}
});
});

jquery and Jplist jquery plugin table data with many pages

$( '#list' ).on( "click", ".list-item", function( event ) {
event.preventDefault();
console.log( "toto" + $(this).text());
var $this = this;
$(this).addClass('selected');
$('.list-item').not($this).removeClass('selected');
});
Hello, I have a problem with the line $('.list-item').not($this).removeClass('selected'); which doesn't work for div present in another pages when navigated. thank you for your help.
I don't know that particular plugin, but I looked into it and it seems it caches the 'other' pages somewhere while they are not displayed. At the moment your script is executed the elements are not existing in the DOM.
You will have to run a code similar to yours everytime the plugin loads a page:
// event "loadNewPage" is not an actual event; you will have to figure out which callbacks/hooks/events your plugin offers
$( '#list' ).on( "loadNewPage", function( event ) {
$('.list-item').removeClass('selected');
});
This only works if your changes are cached as well, otherwise you will have to save the selected element in your javascript and reselect it everytime the plugin displays a page.
In the JPList plugin, when you navigate only the content of the div elements are replaced and not the complete div. So, you'll have to reset the selected class upon navigation or any such event.
While initializing the plugin with default options use 'redrawCallback'
i.e.,
redrawCallback: function() {
$('.list .selected').removeClass('.selected');
}
The above code will reset the selected class upon the div.
and also update your code to be
$( '#list' ).on( "click", ".list-item", function( event ) {
$('.list .selected').removeClass('selected');
event.preventDefault();
console.log( "toto" + $(this).text());
var $this = this;
$(this).addClass('selected');
});
Try this approach, as this would first remove the existing selected class from the elements and add selected class to clicked element

Jquery only click once

I'm trying to disable a li click event after it has clicked the first time. Essentially to stop the array data being doubled. The click is working fine for each time. My current method doesn't appear to be working. I also need to disable the other li's from being clicked once the first one has :)
Thanks
JS code is:
$('#eventType ul li').click(function(e) {
e.preventDefault();
var value = $(this).attr('value');
answers.push(value);
// Below isn't working
$(this).click(function() {
return false;
});
console.log(answers);
});
you need to use one:
$('#eventType ul li').one('click',function(){
//your code here
});
this event will be fired only once
UPDATE
you can do that using $.off()
$('#eventType ul li').one('click',function(){
//your code here
$('#eventType ul li').off('click');
});
jQuery is just JavaScript so you can easily add behaviors that you want
// basic jQuery plugin boilerplate
$.fn.once = function once(eventType, f) {
// this = the selected elements
return this.each(idx, elem) {
// create reference to jQuery-wrapped elem
var $elem = $(elem);
// add event listener for eventType
$elem.on(eventType, function(event) {
// call the event handler
return f(event);
// remove the event handler
$elem.off(eventType, f);
});
});
};
Usage would look like this
$('#eventType ul li').once('click', function(event) {
console.log("you will only see this once");
});
However, this is obviously a common need so it exists in jQuery already. It's called $.one. As APIs grow, you may not know about the existence of such procedures. This answer exists to show you that you can use your brain to program the things that you want or that might be missing from a particular library. This lessens your dependence on the creator's of the lib to introduce the functionality you need.
EDIT
In a comment, you ask if the event handler can be disabled for all other LI elements after the first LI is clicked. The trouble here is that jQuery uses implicit iteration, which means that when you call $('li').on('click', ...), jQuery will bind an onclick event handler for each LI.
A better solution to this problem would be to use jQuery's event delegation
// only fire event handler for the first LI clicked
$('ul').one('click', 'li', function(event) {
console.log($(this).text());
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li>one</li>
<li>two</li>
<li>three</li>
</ul>
This will delegate the event listener to the children LI, but once one of the LI is clicked, the event handler will be removed (because we delegated using the $.one procedure).
Try clicking one LI, you will see a message in the console. When you click the second LI, nothing will happen because the event handler was removed.
var used = false;
$('#eventType ul li').click(function(e) {
if (used == false) {
used = true;
e.preventDefault();
var value = $(this).attr('value');
answers.push(value);
console.log(answers);
}
});
the way you did it was just adding another on click handler, not removing or overriding the old ond.
You can use CSS classes; add the class 'disabled' to elements you don't need, and avoid adding elements that have the classe 'disabled'.
https://plnkr.co/edit/6aloNPETHGxfiP5oYZ9f?p=preview
$('ul li').click(function(e) {
if(!$(this).hasClass('disabled')) {
var value = $(this).text();
answers.push(value);
$('li').addClass('disabled');
}
console.log(answers);
});

click event being removed on previous elements

answer:
/* I bound the events via the parent instead of binding it
on each span tag individually. this allowed me to manipulate
the span tags uniquely. ( thank you Jason P ) */
$( '.selected-option-wrapper' ).on( 'click', 'span', function() { });
disclaimer:
I am losing my mind.
details:
I am trying to list in html a bunch a options that a user selected via a ul/li dropdown. I want the user to click on a li a and have part of the html within the a tag, placed in a separate div.
For example:
html
// html within the li tag that I want cloned over
<a id="met" class="item-1" href="#">
<div class="left check-wrapper">
<span class="gicon-ok"></span>
</div>
<div class="hide item-display"> // exact element to be moved over
<span class="gicon-remove-sign left remove-option-item"></span>
<div class="left">Metallic Thread</div>
</a>
javascript
$( '.options' ).find( 'a' ).on( 'click', function( e ) {
ind_option_html = $( this ).find( '.item-display' ).clone();
/* attach a click event to the span tag */
ind_option_html.find( 'span' ).on( 'click', function() {
console.log( this );
});
/* this is in a $.each loop that appends each new ind_option_html */
$( '.selected-option-wrapper' ).show().append( ind_option_html );
});
problem
whenever I click just one li a the function fires fine, the this for the span tag is logged out. but what is amazing is that when a user clicks another li a the click event is placed on ONLY the most recent span tag.
Where is the onclick event going with the first span tag?
I'm not sure why you're having the problem you are, but you can probably avoid it using event delegation. Instead of binding the event handler each time, do it like this when the DOM is ready:
$('.selected-option-wrapper').on('click', 'span', function() { ... });
See on(), and the section about direct and delegated events.
Where is the initial var ind_option_html (or is it an accidental global)? Looks like all of the instances of the click handler use the same reference, which is always the last one to be processed.
So your fix would be:
ind_option_html = $( this ).find( '.item-display' ).clone();
to:
var ind_option_html = $( this ).find( '.item-display' ).clone();

document.click to remove class on page not behaving properly

I'm trying to create a global style of dropdowns which toggle between open and closed when their menu icon is clicked, but also close whenever anywhere else on the page is clicked. The way in which I'm opening or closing this dropdowns is by adding or removing a class called "open" to the parent of the dropdown.
The idea of that (to be more clear) is that the normal class of the dropdown has display: none; set on it, but if it's a descendant of something with the class "open", then it has display: block;
So, without further ado, here's what I have so far:
"openable" is a class of 'parent' elements which can be clicked on to add the "open" class.
<script>
$(document).ready(function(){
$('.openable').click(function(){
if($(this).hasClass("open")){
$(this).removeClass("open");
}
else{
$(this).addClass("open");
}
});
});
On it's own that actually works fine - it acts as a decent enough toggle for the dropdowns. Of course, clicking anywhere else won't close the dropdowns.
My apparently non-functioning close script is:
<script>
$(document).ready(function(){
$(document).click(function(event) {
var clicked = $(event.target);
if(clicked.hasClass(".open")){
}
else{
if($(".open").length > 0){
$(".open").each(function(){
$(this).removeClass("open");
});
}
}
});
});
</script>
With that script on the page, dropdowns cease to work altogether, and console isn't throwing up any errors for me to work off of.
Any better way of doing this?
Thanks
edit: html markup is something like
<li class="navItem dropdown openable">
<span><img src="img/settings.png"></span>
<ul class="subNav hubDrop">
<li>Nav item 1</li>
<li>Nav item 2</li>
<li>Nav item 3</li>
<li>Nav item 4</li>
</ul>
</li>
for each one. the li tag there is within another ul (remember, this is for dropdown menu essentially)
jsFiddle Demo - Since you haven't provided any HTML I mocked up some elements...
Update: You don't specify if more than one element can be 'open' at once; in your current solution they can be, so I kept that behavior. However, to limit it to one being open you can add $('.open').not(this).removeClass('open'); inside the .openable click handler.
Part One: Why not just use .toggleClass
$(document).ready( function() {
$('.openable').click( function() {
$(this).toggleClass("open");
});
});
Part Two: No need for a second ready handler; in the first, add this:
$(document).click( function(e) {
var clicked = $(e.target).closest('.openable');
if ( clicked.length == 0 ) {
$(".open").removeClass('open');
}
});
jsBin demo
just played around a bit (don't know your markup.)
<div>
<h2 class="openable">ICON 1</h2>
<div class="cont"></div>
</div>
$('.openable').next('.cont').hide();
$('.openable').click(function(e) {
e.stopPropagation();
$('.opened').removeClass('opened');
var d = $(this).next('.cont');
var visib = (d.is(':visible')) ?
/*yes*/ d.slideUp() :
/*no */ ($('.cont').slideUp()) (d.slideDown().prev('.openable').addClass('opened')) ;
});
$(document).click(function(){
$('.cont:visible').slideUp().prev('.openable').removeClass('opened');
});
I don't believe you can just do $(document).click(), it's not wrong but you never click the document itself, you click children of.
I have a very similar menu system and I capture an event this way:
$('.navTab').mouseover(function (event) { navEvent($(this), event.type); });
Then remove all "open" and reapply "open" to the selected item.
I believe you don't want to capture all document click events. jQuery Event.target
How about making everything but the openable classed elements execute your click method?
var openable = $(".openable");
$("div, h2").not(openable).click(function(){
$('.cont').slideUp().prev('.openable').removeClass('opened');
});

Categories