I have a problem with my "CheckCheck" function. The following part of the code should generate a to-do task. The input tag dynamically created in JS provides an option to set the priority to the given task. There is an option to set the task to "normal" or "priotity". However, the code sets the fisk task to "on" and after continues with the imposed "priority" and "normal" but inversely. How to prevent this from happening?
The code:
let tasklist = [];
function Apply() {
const Tasktask = document.querySelector(".task-form");
const Taskdate = document.querySelector(".date");
const Taskpriority = document.querySelector(".check-box");
function Prevent() {
if (Tasktask.value.length === 0 || Taskdate.value === "") {
alert("Fields cannot be empty!");
} else {
Pushed();
render();
clear();
}
}
Prevent();
function Pushed() {
let newTasks = new Tasks(Tasktask.value, Taskdate.value, Taskpriority.value);
tasklist.push(newTasks);
updateLocalStorage();
}
function render() {
CheckCheck();
insertTd();
}
function CheckCheck() {
if (Taskpriority.checked === true) {
Taskpriority.value = "priority"
} else {
Taskpriority.value = "normal"
}
}
function clear() {
Tasktask.value = "";
Taskdate.value = "";
Taskpriority.checked = false;
}
function insertTd() {
checkLocalStorage();
const parent2 = document.querySelector(".table-body");
parent2.innerHTML = "";
tasklist.forEach((item) => {
const table = `<tr>
<td>${item.task}</td>
<td>${item.date}</td>
<td>${item.priority}</td>
<td><a class="delete">delete</a></td>
</tr>`;
parent2.insertAdjacentHTML("afterbegin", table);
});
}
function deleteItem() {
const Table = document.querySelector("table").addEventListener("click", (e) => {
const currentTarget = e.target.parentNode.parentNode.childNodes[1];
if (e.target.innerHTML == "delete") {
if (confirm(`Are you sure you want to delete ${currentTarget.innerText}?`))
deleteTask(findTask(tasklist, currentTarget.innerText));
}
if (e.target.classList.contains("status-button")) {
findTask(tasklist, currentTarget.innerText);
}
updateLocalStorage();
insertTd();
});
}
deleteItem();
function deleteTask(currentTask) {
tasklist.splice(currentTask, currentTask + 1);
}
function findTask(taskArray, task) {
if (taskArray.length === 0 || taskArray === null) {
return;
}
for (let item of taskArray)
if (item.task === task) {
return taskArray.indexOf(item);
}
}
}
The other thing which is not working as intended is the confirm prompt. The more tasks I add, the more confirm prompts I get. I.e. for 1 task it is only one confirm window, for 3 tasks - 3 windows etc. Why is that?
I also attach below a JSFiddle link how better understanding.
Link
Thanks in advance for answers.
You don't get the state of a checkbox by reading its value but its checked property. Try document.querySelector('.check-box').checked
You keep reusing the same buttons and add an event listener to them each time. Either clone them every time, or add the listener once right after creating them.
Simple illustration of the problems here
document.querySelector('#readstates').addEventListener('click', e => {
e.preventDefault();
const disp = `Checked\n 1: ${chk1.checked}, 2: ${chk2.checked} \n`
+ `Value\n 1: ${chk1.value}, 2: ${chk2.value}`;
alert(disp);
});
const spawnBut = document.createElement('button');
spawnBut.id = 'spawned';
spawnBut.textContent = 'Spawned';
document.querySelector('#spawnDirty').addEventListener('click', e => {
const previous = document.querySelector('form #spawned');
if (previous) previous.remove();
document.querySelector('#spawnHere').append(spawnBut);
spawnBut.addEventListener('click', e => {
e.preventDefault();
alert('click!');
});
});
document.querySelector('#spawnClone').addEventListener('click', e => {
const previous = document.querySelector('form #spawned');
if (previous) previous.remove();
const nSpawnBut = spawnBut.cloneNode(true);
document.querySelector('#spawnHere').append(nSpawnBut);
nSpawnBut.addEventListener('click', e => {
e.preventDefault();
alert('click!');
});
});
<form>
<p class="inputs">
<label for="chk1">chk1:</label> <input type="checkbox" id="chk1" />
<label for="chk2">chk2:</label> <input type="checkbox" id="chk2" value="mycheckedvalue" />
<button id="readstates">Read chks</button>
</p>
<p class="button-spawners">
Try spamming those then click below:
<button type="button" id="spawnDirty"
title="Each time you click this one, the button below is respawned and a new handler is attached">
Spawn button
</button>
<button type="button" id="spawnClone"
title="This one will spawn a clone each time, so the click handler is attached only once">
Spawn button clone
</button>
</p>
<p id="spawnHere">
New button will spawn here
</p>
</form>
Related
I am making a to do list app and when the user presses the delete button, the list item should be removed from the list on the page but it should also be removed from the tasks list variable, which is a list of objects, containing a task and done property.
The delete button can be used access its parent element using btn.parentElement but when I try to use this to get to another child element in that parent element (div) it does not work as it is returned as [object HTMLLIElement].
The child element I want to access contains the name of the task so I could use that to delete the list item from the tasks list, however if I did this I would have to loop through it and delete the 1st time the task appears. But this may delete the wrong task if there are multiple tasks with the same task name.
How do I fix this?
var tasks = [];
var oneTask = false;
// html elements
const form = document.querySelector("form");
const formInput = document.querySelector(".task-input");
const formSubmit = document.querySelector(".submit");
const tasksHTML = document.querySelector(".tasks");
var deleteHTML = document.querySelectorAll(".delete");
console.log(deleteHTML);
// objects
function Task(task) {
this.task = task;
this.done = false;
}
// event listeners
form.addEventListener("submit", () => {
console.log("form submitted");
tasks[tasks.length] = new Task(formInput.value);
console.log(tasks);
formInput.value = "";
addItem();
if (oneTask === false) {
oneTask = true;
tasksHTML.classList.add("background");
}
deleteHTML = document.querySelectorAll(".delete");
console.log(deleteHTML);
deleteBtn();
});
// functions
function addItem() {
let task = tasks[tasks.length - 1];
tasksHTML.insertAdjacentHTML(
"beforeend",
`
<li class="task">
<div class="left">
<input type="checkbox" class="box" />
<p class="content">${task.task}</p>
</div>
<button class="btn delete">
<i class="fa-solid fa-trash-can"></i>
</button>
</li>
`,
);
}
function deleteBtn() {
deleteHTML.forEach(btn => {
btn.addEventListener("click", () => {
console.log("delete");
btn.parentElement.style.display = "none";
let p = document.querySelector(`${btn.parentElement} .content`);
console.log(p);
// let index = tasks.indexOf()
console.log(tasks.length, "length");
if (tasks.length < 1) {
console.log("remove background");
tasksHTML.classList.remove("background");
}
});
});
}
Change
let p = document.querySelector(`${btn.parentElement} .content`);
to
let p = btn.parentElement.querySelector('.content");
You already have a reference to the element, you don't need to put it into a selector.
Problem Summary: jQuery todo list app offers 3 functions; add items, edit items, and remove items. I can add items continuously, but cannot edit or remove items continuously. A single item can be edited or removed at a time. A page refresh is required to remove or edit another item. The goal is to have all 3 functions working without having to refresh the page. There are no error messages displayed in the console.
What I have tried: I have attempted to remove event listeners with .off() at the completion of a function and then reinitialize the event listeners after the fact. This does not seem to help or make things worse.
Live Demonstration: codepen.io
jQuery:
function checkTodos() {
// todos in the key of my localStorage
let dataInLocalStorage = localStorage.getItem("todos");
let todos;
// check if it is null or not
if (dataInLocalStorage == null) {
todos = [];
$('#notices').html(`<div class='card'>No list items exist yet. Use the input box on the left to add items.</div>`);
} else {
todos = JSON.parse(dataInLocalStorage);
let html = "";
todos.forEach((todo, index) => {
html += `<div id='item-${index}' class='card' data-index='${index}'>${todo}</div>`;
});
$(".incomplete").empty().append(html);
$('#notices').empty();
}
}
$(document).ready(function() {
checkTodos();
// adding items in todos
$("input").keydown((e) => {
if (e.code === 'Enter' && $("input").val() !== "") {
todo = $("input").val();
let todosData = localStorage.getItem("todos");
if (todosData == null) {
todos = [];
} else {
todos = JSON.parse(todosData);
}
todos.push(todo);
localStorage.setItem("todos", JSON.stringify(todos));
$("input").val("");
}
checkTodos();
});
// list item removal
$('.incomplete > div').click((e) => {
let id = $(e.target).attr('id');
let selector = '#' + id;
let todosData = localStorage.getItem('todos');
let index = $(selector).attr('data-index');
todos = JSON.parse(todosData);
if (e.shiftKey) {
if (confirm("Remove the list item?")) {
todos.splice(index, 1);
localStorage.setItem('todos', JSON.stringify(todos));
checkTodos();
}
}
});
// list item editing
$('.incomplete > div').click((e) => {
let id = $(e.target).attr('id');
let selector = '#' + id;
let k = $(selector).attr('data-index');
let todosData = localStorage.getItem('todos');
todos = JSON.parse(todosData);
if (e.altKey) {
$(selector).attr('contenteditable','true');
$(selector).keydown(function(evt) {
if (evt.code === 'Enter') {
$(selector).removeAttr('contenteditable');
todos[k] = $(selector).html();
localStorage.setItem('todos', JSON.stringify(todos));
checkTodos();
}
});
}
});
});
Currently your event handlers are only registered for items that exist when loading the page (after the first call to checkTodos()). You can use event delegation to also handle events on dynamically added items by replacing
$('.incomplete > div').click((e) => { ... })
$('input').keydown((e) => { ... })
with
$(document).on('click', '.incomplete > div', (e) => { ... })
$(document).on('keydown', 'input', (e) => { ... })
etc.
I have two buttons that execute the same function, and that function needs to evaluate go backward or go forward as I show in the following image
and in the following code I want to evaluate what function next() or prev() to execute depending on which button was touched, this is built in react
const onFinish = (values) => {
if (values.users) {
const Operator = values.users.map((item) => ({
ruleAttributeName: item.ruleAttributeName,
isoAttributeId: item.isoAttributeId,
ruleOperationId: item.ruleOperationId,
mandatoryValue: item.mandatoryValue,
}));
setAttributes(Operator);
}
if (values.users) {
const attributesSave = values.users.map((item) => ({
ruleAttributeName: item.ruleAttributeName,
isoAttributeEntity: {
isoAttributeId: item.isoAttributeId,
},
ruleOperationEntity: {
ruleOperationId: item.ruleOperationId,
},
mandatoryValue: item.mandatoryValue,
}));
console.log('mandatory';
setAttributesSave(attributesSave);
setMandatoryValue();
next();
prev();
};
and in this form I pass by parameter the function
<Form
name='dynamic_form_nest_item'
onFinish={onFinish}
autoComplete='off'
>
You can identify them by assigning unique Id to both button like this
<button onClick={onFinish} Id={'1'}>Next</button>
<button onClick={onFinish} Id={'2'}>Next</button>
And in you listener check id which button clicked.
const onFinish = (event) => {
Let id = event.target.id;
if(id=== "1") {
// Do for one
} else {
// For second
}
}
I don't know the parameters that you got on that custom buttons and how is it triggering onClick event. But here is the solution for HTML buttons.
You can set a value to the button like this.
<button onClick={onFinish} value="next">Next</button>
const onFinish = (ev) => {
ev.preventDefault() // this is to prevent the refresh
const { value } = ev.target // equals with const value = ev.target.value
if(value === "next") {
next()
} else {
prev()
}
}
This question already has an answer here:
Why onchange function not called when checkbox changed using checked property
(1 answer)
Closed 2 years ago.
I tried to check a checkbox with js and detect the change, but the event onchange not detect the change, while that is visually changing. (sorry for by bad english, i'm french)
Here is an example:
document.querySelector("#checkbox").onchange = (e) => {
let checked = e.target.checked;
if (checked) {
console.log("checked !");
} else {
console.log("unchecked...");
}
}
document.querySelector("#buttonOn").onclick = (e) => {
document.querySelector("#checkbox").checked = true;
}
document.querySelector("#buttonOff").onclick = (e) => {
document.querySelector("#checkbox").checked = false;
}
<button id="buttonOn">On</button>
<button id="buttonOff">Off</button>
<input type="checkbox" name="" id="checkbox">
You need to trigger event while you click buttons
document.querySelector("#checkbox").onchange = (e) => {
let checked = e.target.checked;
if (checked) {
console.log("checked !");
} else {
console.log("unchecked...");
}
}
// Create new change event
var event = new Event('change');
document.querySelector("#buttonOn").onclick = (e) => {
document.querySelector("#checkbox").checked = true;
// Trigger onchange
document.querySelector("#checkbox").dispatchEvent(event);
}
document.querySelector("#buttonOff").onclick = (e) => {
document.querySelector("#checkbox").checked = false;
// Trigger onchange
document.querySelector("#checkbox").dispatchEvent(event);
}
<button id="buttonOn">On</button>
<button id="buttonOff">Off</button>
<input type="checkbox" name="" id="checkbox">
I'm trying to move individual li elements from one ul to another when a checkbox is selected.
Full code can be found here:http://jsfiddle.net/8f27L0q3/1/
My function that moves the li item can be found below.
ul.addEventListener('change', (e) => {
const checkbox = e.target;
const checked = checkbox.checked;
const listItem = e.target.parentNode.parentNode;
const completedItems =
document.querySelector('.completedItems');
const label = document.querySelector('.completedLabel');
if (checked) {
completedItems.appendChild(listItem);
label.style.display = 'none';
}
});
Once the li is moved to the other ul, the child span containing a label and checkbox disappear. This functionality works when the first child li moves but doesn't work when a li after the first child is moved. Also the first li's span disappears and therefore cannot be moved to the other ul
Looks like you are asking for the .completedLabel selector globally when you just need to search for it inside the item that was clicked.
May reducing the scope of the query selector to the element you are storing in listItem may work. Here is an example:
const label = listItem.querySelector('.completedLabel');
That way it works reusing your sample code:
//move li item to completed list when checkbox selected
ul.addEventListener('change', (e) => {
const checkbox = e.target;
const checked = checkbox.checked;
const listItem = e.target.parentNode.parentNode;
const completedItems = document.querySelector('.completedItems');
const label = listItem.querySelector('.completedLabel');
if (checked) {
completedItems.appendChild(listItem);
label.style.display = 'none';
}
});
However the implementation can be tweaked a little bit more.
would you mind reconsidering your strategy in solving this case? It is recommended to work with data such as arrays and objects instead of DOM nodes.
Please consider this example
const form = document.forms.form;
const todoList = document.querySelector('#todoList');
const completedList = document.querySelector('#completedList');
const tasks = [];
form.addEventListener('submit', handleSubmit, true);
todoList.addEventListener('change', handleInputChange, true);
function handleSubmit(event) {
event.preventDefault();
const task = this.task;
if (task.value === '') {
return;
}
const item = createTask(task.value);
tasks.push(item);
task.value = '';
syncTodoList();
}
function handleInputChange(event) {
const target = event.target;
if (target.nodeName === 'INPUT') {
const id = event.target.id;
const task = tasks.find(task => task.id === parseInt(id, 10));
task.status = 'completed';
syncTodoList();
syncCompletedList();
}
}
function createTask(task) {
return {
id: Date.now(),
text: task,
status: 'todo'
};
}
function syncTodoList() {
const todos = tasks
.filter(task => task.status === 'todo')
.map(task => `<li>${task.text} <input type="checkbox" id="${task.id}"></li>`)
.join('');
todoList.innerHTML = todos;
}
function syncCompletedList() {
const completeds = tasks
.filter(task => task.status === 'completed')
.map(task => `<li>${task.text}</li>`)
.join('');
completedList.innerHTML = completeds;
}
<form name="form">
<input id="task">
<button>Send</button>
</form>
<p>Todo</p>
<ul id="todoList"></ul>
<p>Completed</p>
<ul id="completedList"></ul>