Function containing a for loop stops running after click event - javascript

I am trying to make a shopping list app that requires the following -
add items to list based on user input, and include a delete button.
toggle items being crossed off with a line through style.
remove items from list when delete button is clicked in corresponding li.
enter code here
The issue I am running into, is that I cannot cross off old list items after new list items are added.
I created a function that is a for loop to loop through the list and toggle the .done class on and off. it works to allow me to toggle line through on and off but as soon as a new item is added to the list through click or key press event, the function stops working.
I figured by placing the toggleDone(); inside of the functions inside of the click and keypress functions as well as globally, it would work on both newly added list items and the original list items.
what actually happens is if i click to add a new item to the list, it no longer functions on the original list items, and then if i keypress to add an item to the list, the function works on the old list items as well as the keypress items, but not the click items.
const input = document.getElementById("userInput");
const btn = document.getElementById("enter");
const ul = document.querySelector("ul");
const list = document.getElementsByTagName("li");
function toggleDone(){
for (let i=0; i < list.length; i++){
list[i].addEventListener("click", function(){
list[i].classList.toggle("done");
});
}
}

If I understand your question, you could simplify things by using event delegation technique. Attach a click event handler to the ul element instead of each individual li - this works because events propagate upward (aka bubbling). You can inspect the event.target passed to the callback function to see exactly what was clicked.
Here is an example demonstrating how to toggle a style on li elements using event delegation:
document.querySelector("ul").addEventListener("click", function(event) {
event.target.classList.toggle("done");
});
.done {
text-decoration: line-through;
}
<ul>
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
</ul>

You might not be calling the function in the right place
You should add if else statement when calling for loop
For the for loop don't say "let" (depends on version) say "var"
This link might help (example)
https://www.w3schools.com/howto/howto_js_todolist.asp

Related

Unhiding relevant data in an Each Loop on a button click of dynamic elements

I have a table that's populated from Node with each row representing a document. There's too much repetitive data, so I want some of it to be hidden and then unhidden on a button click.
In the For Each loop in my pug template I've added an icon so that when you click it, the hidden contents should be shown.
tbody
each output in project.outputs
tr
td= output.title
svg.report__icon.dropbtn
table.dropdown-content
- for (let i=0; i<5; i++)
output.repetitive-data[i]
I then wrote an onclick event in js, like this:
document.querySelectorAll('.dropbtn').forEach(item => {
item.addEventListener('click', (e) => {
document.querySelector('.dropdown-content').classList.toggle('show')
})
})
However while this does allow a click on each icon, no matter which icon/row is clicked it only ever unhides the data in the first row of the table, which does make sense, because 'querySelector' is going to toggle the first element it finds that has that class.
How would I toggle the correct CSS show/hide property for the correct row/element? Do I need a loop within a loop?
querySelector also works when called on regular DOM elements, in that case it will limit its search within that element only
document.querySelectorAll('.dropbtn').forEach(item => {
item.addEventListener('click', (e) => {
e.currentTarget.parentNode.querySelector('.dropdown-content').classList.toggle('show')
})
})

How to select a specific <li> with jquery get the attr value of value and display it on the website

I have an assignment that goes as follows:
Create an event listener so when you click any list item the value of its "value" attribute will be displayed next to this line.
I have in my HTML and ordered list with 8 list items. value=1,2,3.. etc
Where im at now
$("li").click(function () {
var value = $("li").attr("value");
$("#spanFieldId").text(value);
});`
spandFieldId is where the list item value should print. At the moment it always print 1 whether i click the first list item, the second or any other.
So basicly, how do i get it to select the specific li (which i click on) with jquery take out the attr value of value and display it on the website? List item in html look like this
<li value="3">blablablabla</li>
<li value="4">blablablabla</li>
<li value="5">blablablabla</li>
Any suggestions out there?
Try this:
$("li").click(function () {
var value = $(this).attr("value");
$("#spanFieldId").text(value);
});
Replace
var value = $("li").attr("value");
with
var value = $(this).attr("value");
The string selector "li" selects all <li>s and the .attr() will only return the first value found in that selection. Using this instead takes advantage of the context that jQuery passes to listeners, which contains a reference to the DOM element the event is acting on.
Instead of
var value = $("li").attr("value");
use
var value = $(this).attr("value");
That will select the clicked list instead of the first one.

Moving <li> to another list need to update click listeners

See an example here: https://jsfiddle.net/06bf3w2c/1/
I have two <ul>s. Clicking an item in the second one will move that item to the first one.
In the first list each <li> contains a <span> with the class delete-btn. when this span gets clicked it should remove the item from the list.
This is working correctly except for when you move an item from the second list into the first list and then try to delete it. Once the delete-btn span of a moved item gets clicked it seems to still be registering the click listener for the second list even though it is no longer in the second list.
My click listener is applied like this:
$(".second li").click(function(){
$(".first").append($(this).append(' <span class="delete-btn">[ X ]</span>'));
});
after the li is moved and then the delete-btn span is clicked it calls this click listener even though the <li> no longer is a decedent to the second class.
How can I update the click listener when I move the <li> from the second list to the first so that the delete button listener will work after the item has been moved.
That's because you're attaching the click listener to the li's themselves. When the li is moved, it still has the click event listener and just duplicates itself. You should instead attach the event listeners to the lists and have it listen for clicks on child lis like so:
$(".second").on('click', 'li', function(){
$(".first").append($(this).append(' <span class="delete-btn">[ X ]</span>'));
});
$(".first").on('click', '.delete-btn', function(){
$(this).parent().remove();
});
ul{
float: left;
}
.second li{
background-color: #BABABA;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="first">
<li>item 1 <span class="delete-btn">[ X ]</span></li>
<li>item 2 <span class="delete-btn">[ X ]</span></li>
</ul>
<ul class="second">
<li>item 3</li>
<li>item 4</li>
</ul>

how to address list-items in the DOM

I've this markup
<ul id="body">
<li class="item">1</li>
<li class="item">2</li>
<li class="item">3</li>
<li class="item">4</li>
</ul>
When an item is clicked, is there a way in jQuery to identify in the DOM, the previous and the next li so that I can use jQuery functions on those elements??
Say, if the person clicks on item 2, i want to hide item 1 and item 3.. similarly, if the user clicks on item 3, hide item 2 and item 4 (previous and next item in the list).
Get a pointer to the previous and next elements.
Select all siblings that are not the previous and next elements.
Hide the previous and next siblings.
Show the other elements.
This allows the code to work more than once :)
$('#body > li').click(function() {
var prev = $(this).prev(),
next = $(this).next(),
siblings = $(this).siblings().not(prev).not(next);
prev.add(next).hide();
siblings.show();
});
jsFiddle.
If you don't care if the other elements are hidden forever once clicked, simply remove the all references to the siblings variable and its relevant code.
$('#body .item').click(function() {
$(this).prev().add($(this).next()).hide();
});
Edit:
$('#body .item').click(function() {
$(this).siblings().show().end().prev().add($(this).next()).hide();
});
From the comment, This will show all before hiding the prev and next li elements.

Javascript syntax for objects in jquery

I am trying to enhance my page with a jquery right mouse menu, but am having trouble building the correct structures to populate it easily.
Currently my page contains (among other things) a list of items for the user to review. (an html table) Based on the users role, and the current state and context of the row, the user may take one of various actions on each row of data. (approve, reject, refer it to someone else, ect.) My ASP.Net page handles this by setting the visibility of an imagebutton within the row to true, if the option is available. I can control the Cssclass of each button, and am setting the class of for example the "approve" button to “approvebtn”.
Now I want to enhance my site with a right menu.
I am extending my site with Cory S.N. LaViska’s jQuery Context Menu Plugin -
http://abeautifulsite.net/notebook/80
This plugin allows the default right mouse behavior for any elelement to be overridden with a user controlled context menu. The menu is inserted into your page as an unordered list and becomes visible when it is needed.
<ul id="rightMenu" class="contextMenu">
<li class="details">Details </li>
<li class="addnote">AddNote </li>
<li class="listnote">ShowNotes </li>
<li class="approve">Approve </li>
<li class="reject">Reject </li>
<li class="release">Release </li>
<li class="takeover">Takeover </li>
</ul>
Your app gets a callback when something on the right menu is clicked, and you can interrogate the action (the bogus href element) to see which item it was.
I really like this menu because it is simple to use and is completely CSS styled.
However, I need to do something that this plugin does not nativly seem to support. I need to change which items are available on the menu from row to row. Basically if an Imagebutton (for say approve) is avaiable in the row, then its corrisponding menu item should exist as well.
I was able to gain access to the menu just before it is displayed by altering the plugin slightly, to call my function right before the menu is displayed.
This works, but the logic I had to write seems so brute force, that there must be a better way….
In my callback:
function jimsbuggeredfunction(menu,el)
"el" is the element that was right clicked on (usually a table cell), and "menu" is the menu that this right click is bound to. (so I should be using that name and not hardcoding to #rightMenu')
So, the “if” line finds out if the table row containing the element that was “right clicked” contains a specific button (by its class name) if it does the menu item is enabled, otherwise it is disabled. This process continues for every menu item that I want to be flexable row-to-row.
function jimsbuggeredfunction(menu,el) {
if($(el).parents("tr:eq(0)").find('.approvebtn').length > 0)
$('#rightMenu').enableContextMenuItems('#approve');
else
$('#rightMenu').disableContextMenuItems('#approve');
if($(el).parents("tr:eq(0)").find('.rejectbtn').length > 0)
$('#rightMenu').enableContextMenuItems('#reject');
else
$('#rightMenu').disableContextMenuItems('#reject');
if($(el).parents("tr:eq(0)").find('.releasebtn').length > 0)
$('#rightMenu').enableContextMenuItems('#release');
else
$('#rightMenu').disableContextMenuItems('#release');
if($(el).parents("tr:eq(0)").find('.takeoverbtn').length > 0)
$('#rightMenu').enableContextMenuItems('#takeover');
else
$('#rightMenu').disableContextMenuItems('#takeover');
if($(el).parents("tr:eq(0)").find('.revertbtn').length > 0)
$('#rightMenu').enableContextMenuItems('#revert');
else
$('#rightMenu').disableContextMenuItems('#revert');
if($(el).parents("tr:eq(0)").find('.removebtn').length > 0)
$('#rightMenu').enableContextMenuItems('#remove');
else
$('#rightMenu').disableContextMenuItems('#remove');
if($(el).parents("tr:eq(0)").find('.addnotebtn').length > 0)
$('#rightMenu').enableContextMenuItems('#addnote');
else
$('#rightMenu').disableContextMenuItems('#addnote');
if($(el).parents("tr:eq(0)").find('.listnotebtn').length > 0)
$('#rightMenu').enableContextMenuItems('#listnote');
else
$('#rightMenu').disableContextMenuItems('#listnote');
};
There must be a better way to set this up, so that it also just ignores menu items that I want to display all of the time) but it is escaping me at the moment. Is there a better way to accomplish this?
Thanks,
Jim
I would find some way to create a mapping between the two IDs and some more systematic way of finding the relevant button. For example, if the button always belongs inside a certain cell that has a class, let's say "buttonclass", then something like this should work:
var mapping = {
takeoverbtn: '#takeover',
listnotebtn: '#listnote'
// ...
};
function jimsbuggeredfunction(menu,el) {
var buttontype = $(el).parents("tr:eq(0)").find('.buttonclass').children().attr("class");
$('#rightMenu').disableContextMenuItems(mapping[buttontype]);
}
My jQuery is a little rusty, there's probably a cleaner way of retrieving the buttontype, but that general idea ought to work.

Categories

Resources