I'm fairly new to JavaScript, so looking for the simplest solution possible (also, not looking for a jQuery solution).
I'm trying to add clicked list items to a new list, only if the clicked list item isn't already in the new list. I have managed to add the clicked list item to the new list, but I fail to create a functioning if statement that checks the new list for the current item.
window.onload = function () {
var ul = document.getElementById('bulk');
ul.addEventListener('click', function (e) {
var target = e.target;
while (target && target.parentNode !== ul) {
target = target.parentNode;
if(!target) { return; }
}
if (target.tagName === 'LI'){
var node = document.createElement("LI");
var textnode = document.createTextNode(target.id);
var textarea = document.getElementById("test1");
if (!textarea.querySelector(target.id)) {
node.appendChild(textnode);
document.getElementById("test1").appendChild(node);
}
}
});
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="test.js"></script>
</head>
<body>
<ul id="bulk">
<li id="banana"><b>banana</b></li>
<li id="apple"><b>apple</b></li>
</ul>
<ul id="test1"></ul><br>
</body>
</html>
Since you're not supposed to have multiple elements with the same ID in a single document, I changed things so the unique ID of each element is in a dataset attribute data-id.
Beyond that, it's just a question of using querySelector() to figure out whether such a node already is in the destination list.
var ul = document.getElementById('bulk');
var destination = document.getElementById("test1");
ul.addEventListener('click', function(e) {
var target = e.target;
while (target && target.parentNode !== ul) {
target = target.parentNode;
if (!target) {
return;
}
}
if (target.tagName === 'LI') {
var id = target.dataset.id;
var node = document.createElement("LI");
node.dataset.id = id;
var textnode = document.createTextNode("element with id " + id);
if (destination.querySelector("[data-id=" + id + "]")) {
return;
}
node.appendChild(textnode);
destination.appendChild(node);
}
});
<ul id="bulk">
<li data-id="banana"><b>banana</b></li>
<li data-id="apple"><b>apple</b></li>
</ul>
<hr />
<ul id="test1">
</ul>
Related
I'm working on a simple todo list app in JavaScript.
I need to add new items from the input field on a list, and have the ability to remove items from the list.
I wrote all the necessary functions for storing values from input field and adding it to a list. However I am not able to make removing elements work.
HTML, CSS and JS are provided. removeItems() function doesn't work.
const enterBtn = document.getElementById('enter');
const input = document.querySelector('input');
const ulList = document.querySelector('ul');
const delBtn = document.querySelector('button');
// Get input from the input field and create a new li itemn in ul list
function inputLength() {
return input.value.length;
}
function addDelButtonsToExistingItems() {
const liItems = document.querySelectorAll('li');
for (let i = 0; i < liItems.length; i++) {
const delBtn = document.createElement('button');
delBtn.appendChild(document.createTextNode('delete'));
liItems[i].appendChild(delBtn);
}
}
// Add delete buttons to already existing items in a document
addDelButtonsToExistingItems();
// to add new items from the input field
function addNewItem() {
if (inputLength() > 0) {
const newLiItem = document.createElement('li');
newLiItem.appendChild(document.createTextNode(input.value));
const delBtn = document.createElement('button');
delBtn.appendChild(document.createTextNode('delete'));
newLiItem.appendChild(delBtn);
ulList.appendChild(newLiItem);
input.value = '';
}
}
/**
* To remove items from the list on delete btn pressed
*/
function removeItems(e) {
if (e.target.tagName === 'BUTTON') {
e.target.classList.toggle('remove');
}
}
/**
* To toggle done class on list items clicked
*/
function toggleDone(e) {
if (e.target.tagName === 'LI') {
e.target.classList.toggle('done');
}
}
// If enter key is pressed
function addOnEnterKeyPressed(e) {
if (e.keyCode === 13) {
addNewItem();
}
}
// To add new items on enter btn clicked
enterBtn.addEventListener('click', addNewItem);
// To add new items on enter key pressed
input.addEventListener('keypress', addOnEnterKeyPressed);
// To toggle the done class
ulList.addEventListener('click', toggleDone);
// To remove the items on del btn click
// ???????
.done{
text-decoration: line-through;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Shipping List</title>
</head>
<body>
<h1>Shopping List</h1>
<h3>Get it done today</h3>
<input type="text" name="input" id="input">
<button id="enter">Enter</button>
<ul>
<li>NoteBook</li>
<li>Pens</li>
<li>Eraser</li>
<li>CMPSC 412 book</li>
</ul>
<script src="script.js"></script>
</body>
</html>
Updated the code a bit to get rid of some of the const and simplified the way the delete button click event target is found.
Mostly, I've added a class delete to the delete buttons, so we can target those specifically without relying on the tag.
I commented the code I changed.
const enterBtn = document.getElementById('enter');
const input = document.querySelector('input');
const ulList = document.querySelector('ul');
// Get input from the input field and create a new li itemn in ul list
function inputLength() {
return input.value.length;
}
function addDelButtonsToExistingItems() {
const liItems = document.querySelectorAll('li');
for (let i = 0; i < liItems.length; i++) {
const delBtn = document.createElement('button');
// Let's add a class so we can target it instead of using a tag name.
delBtn.classList.add('delete');
delBtn.appendChild(document.createTextNode('delete'));
liItems[i].appendChild(delBtn);
}
}
// Add delete buttons to already existing items in a document
addDelButtonsToExistingItems();
// to add new items from the input field
function addNewItem() {
if (inputLength() > 0) {
const newLiItem = document.createElement('li');
newLiItem.appendChild(document.createTextNode(input.value));
const delBtn = document.createElement('button');
// Adding a class again.
delBtn.classList.add('delete');
delBtn.appendChild(document.createTextNode('delete'));
newLiItem.appendChild(delBtn);
ulList.appendChild(newLiItem);
input.value = '';
}
}
/**
* To remove items from the list on delete btn pressed. We will do this looking at the document.
*/
function removeItem(e) {
// Since we are watching the document for a click, we can see if the target of the event has the class "delete" and we can remove its parent li.
if (e.target.classList.contains('delete')) {
e.target.parentNode.remove();
}
}
/**
* To toggle done class on list items clicked
*/
function toggleDone(e) {
if (e.target.tagName === 'LI') {
e.target.classList.toggle('done');
}
}
// If enter key is pressed
function addOnEnterKeyPressed(e) {
if (e.keyCode === 13) {
addNewItem();
}
}
// To add new items on enter btn clicked
enterBtn.addEventListener('click', addNewItem);
// To add new items on enter key pressed
input.addEventListener('keypress', addOnEnterKeyPressed);
// To toggle the done class
ulList.addEventListener('click', toggleDone);
// To remove the items on del btn click. We are targeting the document, since the delete elements can update.
document.addEventListener('click', removeItem);
.done {
text-decoration: line-through;
}
<h1>Shopping List</h1>
<h3>Get it done today</h3>
<input type="text" name="input" id="input">
<button id="enter">Enter</button>
<ul>
<li>NoteBook</li>
<li>Pens</li>
<li>Eraser</li>
<li>CMPSC 412 book</li>
</ul>
I am facing a bit of trouble here. I am trying to create a to-do list with local storage but the only things I got to work are adding list item it to the local storage and deleting all items from the local storage but I can't delete a single SELECTED item out from the list. Can someone help me figure:
1) Removing a selected single item from the list.
2) Putting Checkbox before the List Text.
3) On clicking checkbox, toggle class list "strike" and remembering it on load/page refresh.
Here is my code:
<body>
<div>
<h1>To-do's list</h1>
<div>
<input type="text" id="textBox">
<button id="enterBtn" type="button">Enter</button>
<div>
<uL id="ul">
<li class="li"><input type="checkbox" class="checkBox" name=""> Buy food for Siboo <button class="deleteBtn">Delete</button></li>
<li class="li"><input type="checkbox" class="checkBox" name=""> Get a new controller <button class="deleteBtn">Delete</button></li>
</uL><br>
<button id="deleteAllBtn"><i class="fa fa-trash"></i> Delete All Items</button>
</div>
<script type="text/javascript" src="script.js"></script>
</div>
</body>
Here is CSS:
.strike {
text-decoration: line-through;
}
Here is my JS:
var textBox = document.getElementById("textBox");
var enterBtn = document.getElementById("enterBtn");
var ul = document.querySelector("ul");
var li = document.getElementsByClassName("li");
var checkBox = document.getElementsByClassName("checkBox");
var deleteBtn = document.getElementsByClassName("deleteBtn");
var deleteAllBtn = document.getElementById("deleteAllBtn");
var itemsArray = localStorage.getItem('items') ? JSON.parse(localStorage.getItem('items')) : [];
localStorage.setItem('items', JSON.stringify(itemsArray));
var data = JSON.parse(localStorage.getItem('items'));
// Functions *********************
// Adding New Items to List - adding list element with checkbox and delete button *********************
function addNewItemToList(text)
{
itemsArray.push(textBox.value);
localStorage.setItem('items', JSON.stringify(itemsArray));
liMaker(textBox.value);
}
function liMaker(text) {
var newLi = document.createElement("li");
newLi.textContent = text;
newLi.className = "li";
ul.appendChild(newLi);
var createCheckBox = document.createElement("input");
createCheckBox.type = "checkbox";
createCheckBox.className = "checkBox";
newLi.appendChild(createCheckBox);
var createDeleteButton = document.createElement("button");
var nameButtonDelete = document.createTextNode("Delete");
createDeleteButton.appendChild(nameButtonDelete);
createDeleteButton.className = "deleteBtn";
newLi.appendChild(createDeleteButton);
}
data.forEach(item => {
liMaker(item);
});
// CheckBox ELEMENT - click on checkBox to strike the list item off list*********************
function checkBoxFunction() {
for (var i = 0; i < checkBox.length; i++) {
checkBox[i].onclick = function () {
this.parentNode.classList.toggle("strike");
}}
}
// // DELETE BUTTON - click the delete button to delete the list item *********************
function deleteBtnFunction() {
for (var i = 0; i < deleteBtn.length; i++) {
deleteBtn[i].onclick = function () {
this.parentNode.parentNode.removeChild(this.parentNode);
}}
}
// DELETE ALL BUTTON - click the Delete ALl Items button to remove all items from the list *********************
function deleteAllBtnFunction()
{
localStorage.clear();
while (ul.firstChild) {
ul.removeChild(ul.firstChild);
}
itemsArray = [];
}
// TEXTBOX - press enter key to add an item to list *********************
function textBoxFunction()
{
if (event.keyCode === 13 && textBox.value.length > 0)
{
addNewItemToList();
textBox.value = "";
}
else if (event.keyCode === 13)
{
alert("Please enter an item to-do!");
}
}
// ENTER BUTTON - click the enter button to add item to list *********************
function enterBtnFunction()
{
if (textBox.value.length > 0)
{
addNewItemToList();
textBox.value = "";
}
else
{
alert("Please enter an item to-do!");
}
}
listItemFunction();
deleteBtnFunction();
// Event Listeners *********************
textBox.addEventListener("keypress", textBoxFunction);
enterBtn.addEventListener("click", enterBtnFunction);
deleteAllBtn.addEventListener("click", deleteAllBtnFunction);
// End of Event Listeners *********************
localStorage.removeItem(/*key*/);
You can use this function to remove a specific item and save the items to localstorage again:
function removeItem(text) {
var items = JSON.parse(localStorage.getItem("items"));
items = items.filter(function(e) {return e !== text; });
localStorage.setItem("items", JSON.stringify(items));
}
removeItem("itemname");
I am not a professional but recently I created a todo app like this. I figured out after adding a new item, action listeners do not count them. So I inserted the event listeners inside the new element creation function after adding the element to the DOM.
Here is my suggestion
First, you have to link jQuery-3.2.1.slim.min.js to your project
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
Put this code to the end of your liMaker function.
$('input[type="checkbox"]').click(function() {
if ($(this).is(':checked')) {
//Call your function
console.log(this.id)
}
});
When creating the list element give each of them a unique id (add it to your checkbox). Maybe you can give them the array index.
createCheckBox.id ={unique id}
After doing that when you click the checkbox to invoke the above function. In that function this.id is the unique value you gave to the checkbox.
Then you can use it to remove that index from your array.
Second Method (Pure JS)
Give a common class name to all checkboxes and a unique id also.
const checkBoxes = document.querySelectorAll(".{common_class_name}")
for(const cb of checkBoxes){
button.addEventListener('click',function(){
let id = this.id
/*this id is the value you gave to the checkbox.
Call your deleting methods here*/
})
}
Place this method at the end of your element creation method. This must run every time you add an element.
I copied your code and my method is working fine. I am a beginner there may be better ways to do that. but my method works.
Good Luck
I am trying to add an event listener on my JavaScript file to link it to my HTML however, it does not recognise it. Am I doing something wrong because they are in separate text documents. However, it works with my other sections so I think it might be a small mistake that I have not spotted out yet.
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<head>
<title>Outliner</title>
<link href="style.css" rel="stylesheet" title="Style">
<script type= "text/javascript" src='setting.js'></script>
<div>
<input type="button" onclick="reset();" value="Reset form">
<button id="pr" onclick="reset()"> Clear </button>
<div id="ul" class="editor" contenteditable="true" draggable="true" ></div>
</div>
</head>
</body>
const TAB_KEY = 9;
const ENTER_KEY = 13;
let editor = document.querySelector('.editor');
editor.appendChild(document.createElement('li')); // Add initial (for visibility)
window.editor.addEventListener('focus', (e) => {
//let ul = e.target;
//let li = document.createElement('li');
//ul.appendChild(li);
});
window.editor.addEventListener('keydown', (e) => {
let code = e.keyCode || e.which;
if (code == TAB_KEY) {
e.preventDefault();
let parent = e.target;
let ul = document.createElement('ul');
let li = document.createElement('li');
ul.appendChild(li);
parent.appendChild(ul);
moveCursorToEnd(li);
} else if (code == ENTER_KEY) {
e.preventDefault(); // Stop treating it like an actual line feed
let parent = e.target;
let li = document.createElement('li');
parent.appendChild(li);
moveCursorToEnd(li);
}
});
function moveCursorToEnd(el) {
el.focus();
document.execCommand('selectAll', false, null);
document.getSelection().collapseToEnd();
}
I commented out all of your main code and put an alert in the event listener to find out what was going on. It turns out it was sending the error cannot use property addEventListener of undefined as it did not like window.editor.
The solution was to replace window.editor with just editor.
Try typing in your contenteditable div in the snippet below to see how it works when you use editor instead. This is probably because you declared the editor variable with let instead of var, which affects the scope of the variable in a different way so it does not become a property of the window object.
const TAB_KEY = 9;
const ENTER_KEY = 13;
let editor = document.querySelector('.editor');
editor.appendChild(document.createElement('li')); // Add initial (for visibility)
/*editor.addEventListener('focus', (e) => {
//let ul = e.target;
//let li = document.createElement('li');
//ul.appendChild(li);
});*/
editor.addEventListener('keydown', (e) => {
/*let code = e.keyCode || e.which;
if (code == TAB_KEY) {
e.preventDefault();
let parent = e.target;
let ul = document.createElement('ul');
let li = document.createElement('li');
ul.appendChild(li);
parent.appendChild(ul);
moveCursorToEnd(li);
} else if (code == ENTER_KEY) {
e.preventDefault(); // Stop treating it like an actual line feed
let parent = e.target;
let li = document.createElement('li');
parent.appendChild(li);
moveCursorToEnd(li);
}*/
alert("Works");
});
<div>
<input type="button" onclick="reset();" value="Reset form">
<button id="pr" onclick="reset()"> Clear </button>
<div id="ul" class="editor" contenteditable="true" draggable="true" ></div>
</div>
I am creating a to do list project in vanilla Javascript and have been trying to figure out how to edit an li item in the list on a double click mouse event. I want to be able to have the original text in the textbox after double clicking and update with the new text after clicking away from the textbox or pressing enter.
This is something I tried to write. Please let me know what I am doing wrong or if there is a better approach to the problem.
editInput function() {
var todosUl = document.querySelector('ul');
todosUl.addEventListener('dblclick', function(event) {
if (event.target.childNodes[0]) {
var originalInput = event.target.childNodes[0];
var editingInput = document.createElement("input");
editingInput.type = 'text';
parent = editingInput.parentNode;
parent.replaceChild(originalInput, editingInput);
}
Please no jQuery!
you can use contenteditabe property of html, and add the property on click to the desired element:
Object.prototype.insertAfter = function (newNode) {
this.parentNode.insertBefore(newNode, this.nextSibling);
};
document.addEventListener('keypress', function(e) {
if(e.currentTarget.activeElement.className === 'content'){
if ((e.keyCode || e.which) == 13) {
var li = document.createElement('li');
var ce = document.createAttribute('contenteditable');
ce.value = "true";
var cl = document.createAttribute('class');
cl.value='content';
li.setAttributeNode(ce);
li.setAttributeNode(cl);
e.currentTarget.activeElement.insertAfter(li);
li.focus();
return false;
}else{
return true;
}
}
});
li {
border: 1px solid #ccc;
}
<ul>
<li contenteditable="true" class="content">
</li>
So I have a simple script that adds "li" elements to the "ul" and assigns them a class. Now I want to change the class of "li" item on click event.
Here is the HTML:
<form class="form">
<input id="newInput" type="text" placeholder="Dodaj pozycję">
<button id="createNew" type="button">Dodaj</button>
</form>
<h2>Moja lista:</h2>
<div class="listBg">
<ul id="list">
</ul>
</div>
<button id="deleteAll" type="button">Wyczyść</button>
And JS:
function addItem() {
var myList = document.getElementById("list"); // get the main list ("ul")
var newListItem = document.createElement("li"); //create a new "li" element
var itemText = document.getElementById("newInput").value; //read the input value from #newInput
var listText = document.createTextNode(itemText); //create text node with calue from input
newListItem.appendChild(listText); //add text node to new "li" element
if (itemText === "") { // if input calue is empty
alert("Pole nie może być puste"); // show this alert
} else { // if it's not empty
var x = document.createElement("span"); // create a new "span" element
x.innerText = "X"; // add inner text to "span" element
x.className = "closer"; // add class to "span" element
myList.appendChild(newListItem); // add created "li" element to "ul"
newListItem.className = "item"; // add class to new "li" element
newListItem.appendChild(x); // add a "span" to new "li" element
var itemText = document.getElementById("newInput"); // read current input value
itemText.value = ""; // set current input calue to null
}
};
I was thinking something like this should do the trick, but it's not working:
function itemDone() {
var listItems = document.querySelectorAll("li");
var i;
for (i = 0; i < listItems.length; i++) {
listItem[i].className = "itemDone";
};
};
var item = document.getElementsByClassName("item");
item.addEventListener("click", itemDone);
I'm fairly new to javascript so I would appreciate some explanation with the answer.
Use event delegation for the dynamically created elements. With this, you only need one event listener on the ul#list and it will work for all elements you dynamically attach to it:
document.getElementById("list").addEventListener("click",function(e) {
if (e.target && e.target.matches("li.item")) {
e.target.className = "foo"; // new class name here
}
});
Here's a simplified example so you can see what happens with the code:
function addItem(i) {
var li = document.createElement('li');
li.appendChild(document.createTextNode(i));
li.className = 'item';
document.getElementById('list').appendChild(li);
}
var counter = 2;
document.getElementById('btn').addEventListener('click', function() {
addItem(counter++);
});
document.getElementById("list").addEventListener("click", function(e) {
if (e.target && e.target.matches("li.item")) {
e.target.className = "foo"; // new class name here
alert("clicked " + e.target.innerText);
}
});
<ul id="list">
<li class="item">1</li>
</ul>
<button id="btn">
add item
</button>
You'll have to set the eventListener on each single item, as document.getElementsByClassName() returns a collection of items and you can't simply add an event listener to all of them with one call of addEventListener().
So, just like the loop you used in itemDone(), you'll have to iterate over all items and add the listener to them:
var items = document.getElementsByClassName("item");
for (var i = 0; i < items.length; i++) {
items[i].addEventListener("click", itemDone);
}
As pointed out in the comments, you can also do so directly when creating the elements, so in your addItem() function, add:
newListItem.addEventListener("click", itemDone);
try this :
var item = document.getElementsByClassName("item");
for (var i = 0; i < item.length; i++) {
item[i].addEventListener("click", itemDone);
}
function addItem(i) {
var li = document.createElement('li');
li.appendChild(document.createTextNode(i));
li.className = 'item';
document.getElementById('list').appendChild(li);
}
var counter = 2;
document.getElementById('btn').addEventListener('click', function() {
addItem(counter++);
});
document.getElementById("list").addEventListener("click", function(e) {
if (e.target && e.target.matches("li.item")) {
e.target.className = "foo"; // new class name here
alert("clicked " + e.target.innerText);
}
});
<ul id="list">
<li class="item">1</li>
<li class="item">1</li>
<li class="item">1</li>
<li class="item">1</li>
<li class="item">1</li>
</ul>
<button id="btn">
add item
</button>
First, try using getElementByTagName instead of querySelectorAll, because querySelectorAll is slower. And second, item receives an array, so item.addEventListener will give you an error. You have to do the addEventListener over item[counter], in a loop.
document.getElementById("list").addEventListener("click", function (e) {
const parent = e.target.parentElement;
const siblings = Array.from(parent.getElementsByTagName("LI"));
siblings.forEach(n => (n.className = (e.target === n) ? "foo" : ""));
});