I'm trying to make a grocery list app that functions as a to-do list. I've got most of the basics down for it so far. I've got a button that takes the input field value, adds it to an array, and displays it to the page. However, I would also like for this to trigger by pressing the enter key. I've given it a shot and so far I've had no luck. I'm not sure why, but instead of just triggering from the enter key, it is creating a new item on the page with every keypress. I would also like to know how to make it so that if the input field is empty, then nothing happens upon click or enter keypress.
let addButton = document.getElementById('add-button');
addButton.addEventListener('click', add);
let addInput = document.getElementById('add-input');
addInput.addEventListener('keydown', add);
//let removeButton = document.getElementById('remove-button');
//removeButton.addEventListener('click', remove);
let groceryList = []
function add() {
groceryInput = addInput.value;
groceryList.push(groceryInput);
addInput.value = '';
displayGroceries();
}
function remove(event) {
let position = event.currentTarget.id;
groceryList.splice(position, 1);
displayGroceries();
}
function displayGroceries() {
let groceryUl = document.getElementById('grocery-ul');
groceryUl.innerHTML = '';
for (var i = 0; i < groceryList.length; i++) {
let groceryLi = document.createElement('li');
groceryLi.innerHTML = groceryList[i];
groceryUl.appendChild(groceryLi);
let removeButton = document.createElement('button');
removeButton.innerText = "Remove";
removeButton.addEventListener('click', remove);
removeButton.id = i;
groceryLi.appendChild(removeButton);
if (add.keyCode === 13) {
add();
}
}
}
<div class="container">
<h1>Grocery List</h1>
<input id="add-input" placeholder="Add Groceries" autocomplete="off">
<button id="add-button">Add</button>
<!--<button id="remove-button">Remove</button>-->
<div>
<ul id="grocery-ul">
</ul>
</div>
You use 'keydown' event which fire when any key is pressed not only enter key. To call add function only for enter press you must customize it. Like -
if(key.which == 13) {
add();
}
And to prevent post empty input value you must check it.
if(groceryInput) {
groceryList.push(groceryInput);
addInput.value = '';
displayGroceries();
}
working Fiddle - https://jsfiddle.net/0xtzb5mg/1/
'keydown' is an event which will work if any key is pressed. You have to specify the keyCode of the key if you need an action to be taken when a particular key is pressed.
In your requirement, the enter key is used and the keyCode for enter key is 13.
document.addEventListener("keydown", function(event) {
if(event.keyCode === 13){
add();
}
});
You can check the keycode for all the keys in this link http://gcctech.org/csc/javascript/javascript_keycodes.htm
Related
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 want to add an event listener to the checkbox that is created within an <li>
When the event is triggered I want to check if it is checked or unchecked
From my understanding the event for a checkbox is "onChange" and the property is "checked" but my solution is not working.
Can someone please explain to me why this solution does not work?
New to JavaScript so please explain in the most simple way.
JavaScript only please
Thank you in advance
HTML
<body>
<div class="wrapper">
<ul id="taskList">
</ul>
<div id="add-task-area">
<input type="text" id="addTaskInput" value="">
<button id="addTaskButton">Add Task</button>
</div>
</div>
</body>
JAVASCRIPT
let taskList = document.querySelector("#taskList");
const addTaskInput = document.querySelector("#addTaskInput");
const addTaskButton = document.querySelector("#addTaskButton");
const addTask = () => {
if (addTaskInput.value != " ") {
let taskItem = document.createElement("li");
let taskText = document.createElement("span");
taskText.textContent = addTaskInput.value;
let checkBox = document.createElement("input");
checkBox.setAttribute("type", "checkBox");
checkBox.setAttribute("class", "checkbox");
let removeItem = document.createElement("button");
removeItem.setAttribute("class", "remove");
removeItem.textContent = "Delete";
taskList.appendChild(taskItem);
taskItem.appendChild(taskText);
taskItem.appendChild(checkBox);
taskItem.appendChild(removeItem);
addTaskInput.value = " ";
};
}
addTaskButton.addEventListener("click", addTask);
addTaskInput.addEventListener("keydown", (event) => {
if (event.keyCode == 13) {
addTask();
}});
let checkbox = document.querySelectorAll(".checkbox")
checkbox.addEventListener("onChange", test);
const test = () => {
if (checkbox.checked) {
alert("checked");
} else {
alert ("unchecked")
}
}
I am working on the same problem and managed to get it figured out!
You can bind event handlers to dynamically created elements the same way you assign an id to them, such as by doing 'li.onclick = checkCount;', where checkCount is the name of the function you want to assign to the handler.
After a lot of time and effort, I created a function that checks the state of checkboxes, pushes the number that are checked to an array, and returns the length of the array(aka the dynamically clicked checkboxes) within a span tag.
When you add the event listener to the checkbox, it is important to state that the other function is false, or else the binding will not work. I have attached my code below, I hope it is helpful to you.
const list = document.getElementById("todo-list");
//creates new li and checkboxes
function newTodo(evt) {
//stops page from reloading every time
evt.preventDefault();
const input = document.getElementById("todo-text").value;
const li = document.createElement("li");
li.appendChild(document.createTextNode("- " + input));
list.appendChild(li);
document.getElementById("todo-text").value = "";
var checkbox = document.createElement('input');
checkbox.type= "checkbox";
checkbox.value = 1;
checkbox.class = "todo";
li.appendChild(checkbox);
li.onclick = checkCount;
}
// binds event listener to call first function
const form = document.getElementById("btn").addEventListener('click', newTodo);
//specifies this will call only 2nd function since 1st function has finished
check = document.getElementsByClassName('todo');
for (var i = 0; i < check.length; i++) {
check[i].addEventListener('click', newTodo, false);
}
const checkCount = function () {
var selected = [];
var span = document.getElementById('item-count');
var checkboxes = document.querySelectorAll('input[type=checkbox]:checked');
for (var i = 0; i < checkboxes.length; i++) {
selected.push(checkboxes[i]);
}
span.innerHTML = selected.length;
};
It doesn't work because the checkbox doesn't exist by the time you are adding the event listener: checkbox.addEventListener("onChange", test).
You can try adding the event listener right after you create the checkbox:
...
var checkbox = document.createElement('input');
checkbox.addEventListener("onChange", test) // <-- add this line
checkbox.type= "checkbox";
...
How do I make make an Enter keypress in an <input> element shift focus to the next <input> element on the page?
I have a for loop that creates <li> elements with <input> elements inside. I need to make so that when the user hits enter on their keyboard, the website will focus on the next input field so that the user can enter the next player name without having to toggle between using their mouse and their keyboard.
I thought using the nextSibling property was the solution but it wont work because the <input> elements technically dont have any siblings because each of them is inside/are children of diferent <li> elements.
Here is my JavaScript:
for ( var i = 1 ; i <= numberOfPlayers ; i++ ){
var inputElement = document.createElement('input');
var liElement = document.createElement('li');
inputElement.setAttribute( 'type' , 'text' );
inputElement.setAttribute ( 'id' , 'name-input-' + i );
inputElement.setAttribute ( 'class' , 'name-input');
inputElement.setAttribute ( 'placeholder' , 'Enter a name for player ' + i );
liElement.appendChild(inputElement);
nameInputArray[i] = inputElement;
document.getElementById('name-list').appendChild(liElement);
inputElement.addEventListener( 'keypress' , function(event){
if ( event.which === 13 ) {
alert(this);
document.getElementById( 'name-input-' + (i+1)).focus();
}
} );
}
I tried using the "i" in the for loop and string concatenation to select the ID of the next element but the "i" variable isn't working either because by the time that code runs that "i" is equal to the highest number that it can be after the whole for loop has ran.
Problem:
The problem with your actual code is that i is always equal to numberOfPlayers+1 in the event handler callback function, so you were trying to focus on a null element, you can read more about JavaScript closures to see why i was always equal to numberOfPlayers+1.
Solution:
First you need to use the onkeypress event on your input, then test if the pressed key is the Enter, if it's pressed get the id of the current input, extract i value from it and focus on the next input element using the next id.
This is how should be your code:
inputElement.addEventListener('keypress', function(event) {
if (event.which === 13) {
var i = parseInt(this.id.charAt(this.id.length-1));
console.log(i);
if(i<=numberOfPlayers){
document.getElementById('name-input-' + (i + 1)).focus();
}
}
});
This is a working snippet:
var numberOfPlayers = 5;
var nameInputArray = [];
for (var i = 1; i <= numberOfPlayers; i++) {
var inputElement = document.createElement('input');
var liElement = document.createElement('li');
inputElement.setAttribute('type', 'text');
inputElement.setAttribute('id', 'name-input-' + i);
inputElement.setAttribute('class', 'name-input');
inputElement.setAttribute('placeholder', 'Enter a name for player ' + i);
liElement.appendChild(inputElement);
nameInputArray[i] = inputElement;
document.getElementById('name-list').appendChild(liElement);
inputElement.addEventListener('keypress', function(event) {
if (event.which === 13) {
var i = parseInt(this.id.charAt(this.id.length - 1));
console.log(i);
if(i<numberOfPlayers){
document.getElementById('name-input-' + (i + 1)).focus();
}
}
});
}
<ul id="name-list"></ul>
If you want to stick with vanilla JS, use this:
for (var i = 1; i <= numberOfPlayers; i++) {
var inputElement = document.createElement("input");
var liElement = document.createElement("li");
inputElement.type = "text";
inputElement.id = "name-input-" + i;
inputElement.className = "name-input";
inputElement.placeholder = "Enter a name for player " + i;
liElement.appendChild(inputElement);
nameInputArray[i] = inputElement;
document.getElementById("name-list").appendChild(liElement);
(function(i) {
inputElement.addEventListener("keypress", function(event) {
if (event.which === 13) {
alert(this);
document.getElementById("name-input-" + (i + 1)).focus();
}
});
})(i);
}
This is my solution.
Do not forget that the created <li> element needs to be appended to something like <body>. I have actually added a <ul> element and appended it to the <body> and then appended the <li> elements to the <ul> element.
If you use nextSibling, you do not need to keep elements in nameInputArray. I have not removed it to show how it should be initialized before you can use it in your loop. Also, nextSibling works in my solution since I have put all the <li>s under one <ul> which I think is the correct thing to do anyway.
Other than that, I just corrected a few things here and there. Let me know if you have more questions about this code.
function eventFunc(event) {
if (event.which === 13) {
var nextInput = event.target.parentElement.nextSibling.childNodes[0];
if (nextInput !== null)
nextInput.focus();
}
}
var numberOfPlayers = 4;
var nameInputArray = [];
var ulElement = document.createElement('ul');
document.body.append(ulElement);
for (var i = 0; i < numberOfPlayers; i++) {
var liElement = document.createElement('li');
ulElement.append(liElement);
var inputElement = document.createElement('input');
inputElement.setAttribute('type', 'text');
inputElement.setAttribute('id', 'name-input-' + i);
inputElement.setAttribute('class', 'name-input');
inputElement.setAttribute('placeholder', 'Enter a name for player ' + i);
inputElement.setAttribute('onkeypress', "eventFunc(event)");
liElement.appendChild(inputElement);
nameInputArray[i] = inputElement;
}
UPDATE: Getting each input box from parent <li> elements:
After comment from the OP, I see that they want a structure like this:
<ul>
<li>input box1</li>
<li>input box2</li>
<li>input box3</li>
</ul>
In this structure, each input box is the first child node of its parent <li> element. Therefore, we can still use nextSibling (as the OP intended to use) in this way:
nextInput = event.target.parentElement.nextSibling.childNodes[0];
This line first finds the parent <li> element, applies nextSibling to get the next li element and then gets the input box inside that element.
$('input').on('keyup', function(event){
if(event.keyCode == 13){ // 13 is the keycode for enter button
$(this).next('input').focus();
}
});
https://jsfiddle.net/jat1merL/
are you looking for this?
by the way, use keyup instead of keypress. if a key is hold it fires mass of keypress events in a speed you can't handle ;)
I am using addEventListener to bind an event to a node. The addEventListener adds addItem function to node. But when I press enter the function is not running.
Here is the JavaScript:
document.getElementById('add-item').addEventListener('keypress', function (e) {
if (e.keyCode == 13) {
addItem();
}
}, false);
function addItem() {
var list = document.querySelector('ul.todo-list');
var newItem = document.getElementById('new-item-text').value;
var newListItem = document.createElement('li');
newListItem.className = 'todo-item';
newListItem.innerHTML = newItem + '<span class="remove"></span>';
list.insertBefore(newListItem, document.querySelector('.todo-new'));
//1. Empty the Input field once the item
document.getElementsByTagName('input')[0].value = '';
}
Here is the HTML:
<li class='todo-new'>
<input id='new-item-text' type='text'/>
<a id='add-item' href='#'>+</a>
</li>
On other hand the function runs with click
document.getElementById('add-item').addEventListener('click', addItem, false);
I want to do this with JavaScript only not using jQuery library.
Edited:
I want to attach the event to input field.
I'm willing to place a bet that the add-item element isn't in focus when you press enter. Instead, try changing the trigger to be the input field.
document.getElementById('new-item-text').addEventListener('keypress', function (e) {
if (e.keyCode == 13) {
addItem();
}
}, false);
I'm a newbie with JavaScript, so please bear with me. I am doing a To-Do List app which requires the user to enter a text in the text box and the item would be added when a button is clicked or when you it the Enter Key. I was able to do it with the Button Click and when I added the functionality for the Enter key, the button stopped working. Could you tell me what I'm doing wrong? Below is the java script code I have. Thank You.
var itemTextBox, newButton;
function addNewItem(list, itemText)
{
var listItem;
listItem = document.createElement("li");
listItem.innerText = itemText;
list.appendChild(listItem);
}
itemTextBox = document.getElementById("inputText");
itemTextBox.focus();
newButton = document.getElementById("addButton");
newButton.onclick = userInput();
itemTextBox.onkeyup = function (event) {
if (event.which === 13)
{
userInput();
itemTextBox.value = '';
}
};
function userInput()
{
var itemText;
itemText = itemTextBox.value;
if (itemText || itemText !== "")
{
addNewItem(document.getElementById("listOfItems"), itemText);
}
}