To do list with Javascript : can't delete item - javascript

I made the remove item button with the js code while doing the to-do list project.
removing items which are earlier made in Code editor works well.
*so in browser preview, I typed and added "new To-do" item to list.*
but removing "new To-do" item doesn't work. even chrome Devtool shows no error. new item's tag and className is all correct.
p.s : don't need to worry about 'X' or X icon. I skipped for uploading.
[HTML]
<ul id="listUl">
<li>Meet George <span class="remove"></span></li>
<li>Pay Bills <span class="remove"></span></li>
<li>new To-do <span class="remove"></span></li>
</ul>
[JS] - removing part.
var remove = document.getElementsByClassName('remove');
for (var i = 0; i < remove.length; i++) {
remove[i].onclick = function() {
var target = document.getElementById('listUl');
target.removeChild(this.parentElement);
}
}

That's because every new item you add does not have any listener on them (remove[i].onclick = function(){...}). So when you click on them, nothing happens. You need to add the onClick listener to every new items, like you are doing for the initial ones.

Add the onClick listener to ul tag. Check
if(e.target && e.target.nodeName == "LI") {
//Your code here
}

I rewrote your code:
There is only one (delegated) event assigned now to the <ul> parent element, it will trigger only, when the click happens on an element with class="remove". In that case it is the <span> element which I also made a bit more visible by adding an "X" inside. Right, when the action occurs I have to go up two levels until I reach the parent of the actual element I want to remove: the <li> element.
var i=3,UL=document.querySelector('#listUl');
UL.addEventListener('click',function(ev){
// ev.target - this element was clicked on
if (ev.target.classList.contains('remove'))
// ev.target.parentNode - this must be the <li> element
// ev.target.parentNode.parentNode - and this is the <ul> element
ev.target.parentNode.parentNode.removeChild(ev.target.parentNode);
})
document.querySelector('#addTask').addEventListener('click',
function(){UL.innerHTML+='<li>and task no. '+(++i)+'... <span class="remove">X</span></li>'})
<button id="addTask">add another task</button>
<ul id="listUl">
<li>Meet George <span class="remove">X</span></li>
<li>Pay Bills <span class="remove">X</span></li>
<li>new To-do <span class="remove">X</span></li>
</ul>
The "add" button demonstrates how newly created<li>s also respond to clicks on the "remove"-span.

It looks like that the JS code only adds delete function to these todos that already exists when the page loads. Newly added todos will not have onclick callback function attached if you only execute your js once when the pages loads.
The workaround here is to attach the callback function every time you create a TODO element.

Your newly (dynamically) added DOM elements don't have event listeners attached to them, because on page load, you only iterated (looped) only currently available elements, so You may try the following approach (Read About Event Delegation):
document.getElementById('listUl').addEventListener('click', function(e) {
if (e.target.nodeName == 'LI') {
e.target.parentNode.removeChild(e.target);
}
});
<ul id="listUl">
<li>Meet George <span class="remove">X</span></li>
<li>Pay Bills <span class="remove">X</span></li>
<li>new To-do <span class="remove">X</span></li>
</ul>

if you have jquery added to your page, you can do it like
$('#listUl').on('click', 'span.remove', function() {
$(this).parent().remove();
});
$('.add').click(function() {
$("#listUl").append("<li>Whatever Content <span class='remove'>x</span</li>");
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul id="listUl">
<li>Meet George <span class="remove">x</span></li>
<li>Pay Bills <span class="remove">x</span></li>
<li>new To-do <span class="remove">x</span></li>
</ul>
<butoon class="add">Add</butoon>

Related

Replace fields in columns on click with JS

I am trying to find a way to do something with JavaScript. I have a list of exercises, they are 5, and I want when I click on one of them, this exactly one to be transferred to another list(right one), this will be the exercises the user choose from the left list, and if he changes his mind, to be able to return this exercise to the left list. Also, when some exercises are chosen, to be visible only on the right site.
<ul id="left" th:each="exercise : ${exercises}">
<button type="submit">
<li th:text="${exercise.name}">
</button>
</li>
</ul>
<ul id="right" th:each="exercise : ${exercises}">
<button type="submit">
<li th:text="${exercise.name}">
</button>
</li>
</ul>
Something like that.
(I build the whole project with Java and Spring)
I assume the 2 lists come from the Back End and are rendered on the UI.
You need to send a request to the server to tell that an item that clicked on to the server "API endpoint" to move the item to the 2nd list
If you are using ajax you need to send a get request to get the updated lists
If you are using Spring MVC that will require reloading the View to update the UI
If the lists are built on the Front end and then will be submitted to the server
You need to remove the element from 1st list and then add it to the 2nd list
<ul id="left">
<li id="i" onclick="remove(this)">Item that clicked on</li>
</ul>
<ul id="right">
<li id="li1">li 1</li>
<li id="li2">li 2</li>
</ul>
<script>
function remove(el) {
const textContent = el.textContent;
// copy classes or id or anything
el.remove();
addElementToRightList(textContent);
}
function addElementToRightList(textContent) {
const parent = document.querySelector("#right");
const element = document.createElement("li");
// do stuff with element eg: set text by getting it from item that clicked on using textContent
element.textContent = textContent;
parent.appendChild(element);
}
</script>

Trouble changing list text colour

I'm trying to change the colour of text in my a list on a button press.
My trouble is when I'm trying to get the colour to reset on other list items before applying the 'selected' colour. See code:
function changeNavColour(clicked_id) {
document.getElementById("navAbout").style.color = "black";
document.getElementById("navShows").style.color = "black";
document.getElementById("navPrices").style.color = "black";
clicked_id.style.color = "#c60707";
}
<html>
<nav class='navigation' id='navigation'>
<ul class="navbar" id='nav'>
<li class="navbar-item" id='navAbout'>ABOUT</li>
<li class='navbar-item'>-</li>
<li class="navbar-item" id='navShows'>SHOWS</li>
<li class='navbar-item'>-</li>
<li class="navbar-item" id='navPrices'>PRICES</li>
</ul>
</nav>
</html>
So at the moment, the text turns red on click, however the other colours (if they're already red) do not get set to black and stay red.
Any ideas?
Thanks
As Alex already pointed out, your text is between the <a></a> tags. Easiest way to fix it is to add firstChild (like document.getElementById("navAbout").firstChild.style.color ="black" ). A better way to do it is using classes, because using style property usually not a good practice (read more about adding and removing classes here).
you can use something like this, or change the selector however you need using querySelectorAll:
function changeNavColour(clicked_id) {
x=document.querySelectorAll("a")
x.forEach(function(item){
item.style.color = "black";
})
clicked_id.style.color = "#c60707";
}
Hope this help.
Your event handler is currently dealing with a HTMLAnchorElement reference directly, not an id attribute value. See Javascript snippet comments.
function changeNavColour(anchorElement) {
// Look for every anchor tag within the navigation list
document.getElementById('navigation').querySelectorAll('a').forEach(a => {
// Change their color depending on supplied anchor reference (the event target reference)
a.style.color = a === anchorElement ? "#c60707" : 'black'
});
}
<!-- Keep your markup as is -->
<nav class='navigation' id='navigation'>
<ul class="navbar" id='nav'>
<li class="navbar-item" id='navAbout'>ABOUT</li>
<li class='navbar-item'>-</li>
<li class="navbar-item" id='navShows'>SHOWS</li>
<li class='navbar-item'>-</li>
<li class="navbar-item" id='navPrices'>PRICES</li>
</ul>
</nav>
EDIT
As is, individual anchors ids are not hardcoded anymore, this means the Javascript function will still work when you add or remove items from navigation list.
But we can go a step further and implement event delegation strategy instead.
There are several advantages, it makes possible to add navigation item(s) later programmaticaly and automaticaly benefit from the ul#navigation registered click event handler behavior. It also limits the number of event listeners attached to the DOM (that's generally not an issue but it is optimization and may be considered as best practice IMO), :
document.getElementById('navigation').addEventListener('click', function(evt) {
const anchor = evt.target instanceof HTMLAnchorElement && evt.target;
if (anchor) {
// Look for every anchor tag within the navigation list
// Event handler is attached to the ul#navigation
// so "this" keyword is a reference to the uordered list HTML element
this.querySelectorAll('a').forEach(a => {
// Change their color depending on supplied anchor reference (the event target reference)
a.style.color = a === anchor ? "#c60707" : 'black'
});
}
});
<!-- Keep your markup as is -->
<nav class='navigation' id='navigation'>
<ul class="navbar" id='nav'>
<li class="navbar-item" id='navAbout'>ABOUT</li>
<li class='navbar-item'>-</li>
<li class="navbar-item" id='navShows'>SHOWS</li>
<li class='navbar-item'>-</li>
<li class="navbar-item" id='navPrices'>PRICES</li>
</ul>
</nav>

jQuery first element of type click

I am working through an issue where I have a menu that is mutli levels deep. I am trying to get it so that if a first level element is clicked (add class .ubermenu-active) it will look for any other first level element with that class and remove it.
$('.ubermenu-item-level-0 span').on('click',function() {
var target = $('.ubermenu-item-level-0');
$('.ubermenu li.ubermenu-item-level-0').removeClass('ubermenu-active');
target.parents('.ubermenu li.ubermenu-item-level-0').toggleClass('ubermenu-active');
});
HTML (mock):
<ul class="ubermenu">
<li class="ubermenu-item-level-0">
<a class="ubermenu-target">
<span class="ubermenu-target-title">Level 1</span>
</a>
<ul class="ubermenu-submenu">
<li class="ubermenu-item-level-1">
<a class="ubermenu-target">
<span class="ubermenu-target-title">Level 2</span>
</a>
</li>
<li class="ubermenu-item-level-1">
<a class="ubermenu-target">
<span class="ubermenu-target-title">Level 2</span>
</a>
<ul class="ubermenu-submenu">
<li class="ubermenu-item-level-2">
<a class="ubermenu-target">
<span class="ubermenu-target-title">Level 3</span>
</a>
</li>
</ul>
</li>
</ul>
<li class="ubermenu-item-level-0">
<a class="ubermenu-target">
<span class="ubermenu-target-title">Level 1</span>
</a>
</li>
</li>
</ul>
Right now if any of the sub elements are clicked the parent closes
Currently your target var is selecting all elements with class .ubermenu-item-level-0, so when you toggle the class you're toggling all parent elements. Your target var should be something relative to the element clicked, like var target = $(this).closest('.ubermenu-item-level-0');
So, your sample HTML isn't what I expected to see . . . specifically, your second <li class="ubermenu-item-level-0" . . . element is currently showing as a child under the first one, while your description makes it sound like they should be siblings.
For the sake of the solution I'm going to assume that those two lis are supposed to be siblings of each other and that somehow the code got mixed up. :)
So, here's how I would handle it . . .
var sFirstLevelMenuClass = ".ubermenu-item-level-0";
var sActiveMenuClass = "ubermenu-active";
var $firstLevelMenuOptions = $(".ubermenu").find(sFirstLevelMenuClass);
$firstLevelMenuOptions.children("a").children("span").on("click", function() {
$firstLevelMenuOptions.removeClass(sActiveMenuClass);
$(this).closest(sFirstLevelMenuClass).addClass(sActiveMenuClass);
});
Basically, I've simplified your logic and fixed one small issue that you had in your jQuery code.
Detailed Explanation
The issue was that when you used $('.ubermenu-item-level-0 span') as your selector for your change event. That translates to "any span element that is a descendant of a ubermenu-item-level-0 element". So, in addition to the spans that are directly under the ubermenu-item-level-0 list items, it was also picking up the ones under the ubermenu-item-level-1 and ubermenu-item-level-2 elements, since they are also descendants.
So, I changed your selector to $firstLevelMenuOptions.children("a").children("span") which translates to "all spans, that are direct children of an a, that is the direct child of an ubermenu-item-level-0 element" (Note: $firstLevelMenuOptions is set to equal $(".ubermenu").find(".ubermenu-active"); through the logic earlier in the code). This stops the selector from picking up the lower level spans.
Outside of that and trimming down some of your selectors to be more efficient, the only other thing that I changed was the flow of how the ubermenu-active class is manipulated. In my solution, there are two steps:
Remove ubermenu-active from ALL ubermenu-item-level-0 elements
Add ubermenu-active to the closest ubermenu-item-level-0 element to the span that was clicked (i.e., $(this))
That basically resets the list and then reselects the appropriate menu item to me active.
You can stop the event propagation:
event.stopPropagation()
Without seeing the full HTML, I was going to suggest trying this:
$('.ubermenu-item-level-0 span').on('click',function(e) {
e.stopPropagation();
var target = $('.ubermenu-item-level-0');
$('.ubermenu li.ubermenu-item-level-0').removeClass('ubermenu-active');
target.parents('.ubermenu li.ubermenu-item-level-0').toggleClass('ubermenu-active');
});

Layering Knockout Click binding

Very simple scenario here, I'm hoping there is a simple solution.... Jquery would handle this but for efficiency I want to use the knockout click binding.
<ul>
<li data-bind="click: ShowMyUser">
<span>My Email Address Or Username</span>
<i data-bind="click: DeleteMyUser">Delete Icon</i>
</li>
<ul>
In this example I have a list of Usernames or Email Addresses. What I would like to do, is on the click event of the ROW (e.g. li), show the user details. On the click event of the DELETE icon I would like to show a pop up.
I have both of those methods written and working. My problem is that on clicking the delete icon it ALSO fires the li event.
In order to get round this I have implemented the following:
<ul>
<li>
<span data-bind="click: ShowMyUser">My Email Address Or Username</span>
<i data-bind="click: DeleteMyUser">Delete Icon</i>
</li>
<ul>
However this means that the user must click on the text, I'm not a big fan of this, I think it's un-intuitive.
Any thoughts guys n gals?
KnockoutJS already supports stop bubbling, no need to write it from scratch: http://knockoutjs.com/documentation/click-binding.html (go to Note 4)
Just add clickBubble: false to the child click.
<ul>
<li data-bind="click: ShowMyUser">
<span>My Email Address Or Username</span>
<i data-bind="click: DeleteMyUser, clickBubble: false">Delete Icon</i>
</li>
<ul>
Thank you to Roy J for posting a very helpful link, from that page I found a solution. Firstly I created the following bindingHandler:
ko.bindingHandlers.stopBubble = {
init: function(element) {
ko.utils.registerEventHandler(element, "click", function(event) {
event.cancelBubble = true;
if (event.stopPropagation) {
event.stopPropagation();
}
});
}
};
I then added this to my delete icon so it would not propagate through and call the li click event:
<ul>
<li>
<span data-bind="click: ShowMyUser">My Email Address Or Username</span>
<i data-bind="click: DeleteMyUser, stopBubble: true">Delete Icon</i>
</li>
<ul>
Use binding clickBubble or return false from DeleteMyUser click handler
if you want to have dynamic logic to the bubble but seperated from the click handler you can do
https://jsfiddle.net/h1mrfLe6/

Jquery Dynatree plugin using clicks in id

Using Firebug I have found that the Dynatree plugin changes the following code:
<li id="id3.1" class="expanded">Menu 1
<ul>
<li id="id3.1.1">Sub-menu 1</li>
</ul>
</li>
To this:
<li class="">
<span class="dynatree-node dynatree-exp-c dynatree-ico-c">
<span class="dynatree-connector"></span>
<span class="dynatree-icon"></span>
<a class="dynatree-title" href="#">Sub-menu 1</a>
</span>
</li>
So when I try to make a click event on the id="id3.1.1" nothing happens because this id doesn't exist anymore.
I made a search here and found the onActivate option that will make my click happen on the menu:
$("#treeMenu").dynatree({
onActivate: function(node){
var menuTitle = node.data.title;
alert(menuTitle);
}
});
My question: Is this the only way to do the click event using Dynatree?
Well I think that is the best option, because it uses the API of the plugin, but of course you could still attach an event to the <a> like this:
$('a.dynatree-title').live('click', function(e){
//here e.target is the link you have clicked
});

Categories