I have made a TODO app and added a counter to keep a count of the items in the list. If the counter hits zero, I've set it to re-show a message 'You currently have no tasks. Use the input field above to start adding.'
if(count === 0){
noTasksText.classList.remove('d-none');
}
In the console I print out the div and it doesn't have d-none
in the class list any more which is what I want, however, in the actual DOM it does.
Here is a full example - https://codepen.io/tomdurkin/pen/LYdpXKJ?editors=1111
I really can't seem to work this out. I can't seem to interact with that div when the counter becomes zero, however I can get console logs etc to show when expected.
Any help would be appreciated!
const mainInput = document.querySelector('#main-input');
const todoContainer = document.querySelector('#todo-container');
const errorText = document.querySelector('#js-error');
const noTasksText = document.querySelector('.js-no-tasks')
let tasks = [];
let count = 0;
// focus input on load
window.onload = () => {
mainInput.focus();
const storedTasks = JSON.parse(localStorage.getItem('tasks'));
if (storedTasks != null && storedTasks.length > 0) {
// set count to number of pre-existing items
count = storedTasks.length
// hide the 'no tasks' text
noTasksText.classList.add('d-none');
// overwrite tasks array with stored tasks
tasks = storedTasks;
tasks.forEach(task => {
// Build the markup
const markup = `
<div >
<div >
<div >
<h5 data-title="${task}">${task}</h5>
</div>
<div >
<button >Remove Item</button>
</div>
</div>
</div>`;
// Append it to the container
todoContainer.innerHTML = markup;
});
} else {
if (noTasksText.classList.contains('d-none')) {
noTasksText.classList.remove('d-none');
}
}
};
// event listener for 'enter on input'
mainInput.addEventListener("keydown", e => {
// if error is showing, hide it!
if (!errorText.classList.contains('d-none')) {
errorText.classList.add('d-none');
}
if (e.key === "Enter") {
// Get the value of the input
let inputValue = mainInput.value;
if (inputValue) {
// Build the markup
const markup = `
<div >
<div >
<div >
<h5 data-title="${inputValue}">${inputValue}</h5>
</div>
<div >
<button >Remove Item</button>
</div>
</div>
</div>`;
// hide 'no tasks' text
noTasksText.classList.add('d-none');
// Append it to the container
todoContainer.innerHTML = markup;
// Push value to 'tasks' array
tasks.push(inputValue);
// Put in localStorage
textTasks = JSON.stringify(tasks);
localStorage.setItem("tasks", textTasks);
// Reset the value of the input field
mainInput.value = '';
// add 1 to the count
count
} else {
// Some very basic validation
errorText.classList.remove('d-none');
}
}
});
// remove task
todoContainer.addEventListener('click', (e) => {
// Find the button in the row that needs removing (bubbling)
const buttonIsDelete = e.target.classList.contains('js-remove-task');
if (buttonIsDelete) {
// Remove the HTML from the screen
e.target.closest('.js-single-task').remove();
// Grab the name of the single task
let taskName = e.target.closest('.js-single-task').querySelector('.js-single-task-name h5').getAttribute('data-title');
// filter out the selected word
tasks = tasks.filter(item => item != taskName);
textTasks = JSON.stringify(tasks);
localStorage.setItem("tasks", textTasks);
// update counter
count--
// check if counter is zero and re-show 'no tasks' text if true
if (count === 0) {
noTasksText.classList.remove('d-none');
console.log(noTasksText);
}
}
});
body {
background: #e1e1e1;
}
<div >
<div >
<div >
<div >
<h2>To dos</h2>
<p>
Use this app to keep a list of things you need to do
</p>
<input id="main-input" type="text" placeholder="Type your todo and hit enter..." />
<small id="js-error" >
Please type a value and press enter
</small>
<hr />
<h4 >Your 'To dos'</h4>
<div id="todo-container">
<!-- todos append in here -->
<div >
<small >
<i>
You currently have no tasks. Use the input field above to start adding
</i>
</small>
</div>
</div>
</div>
<!-- /card -->
</div>
</div>
</div>
CodePudding user response:
You can do the following, it will work (here innerHTML is changing the DOM, so I added an extra function to recalculate elements after DOM is changed due to innerHTML):
var mainInput = document.querySelector("#main-input");
var todoContainer = document.querySelector("#todo-container");
var errorText = document.querySelector("#js-error");
var noTasksText = document.querySelector(".js-no-tasks");
let tasks = [];
let count = 0;
function getAllElements() {
mainInput = document.querySelector("#main-input");
todoContainer = document.querySelector("#todo-container");
errorText = document.querySelector("#js-error");
noTasksText = document.querySelector(".js-no-tasks");
}
// focus input on load
window.onload = () => {
mainInput.focus();
var storedTasks = JSON.parse(localStorage.getItem("tasks"));
if (storedTasks != null && storedTasks.length > 0) {
// set count to number of pre-existing items
count = storedTasks.length;
// hide the 'no tasks' text
noTasksText.classList.add("d-none");
// overwrite tasks array with stored tasks
tasks = storedTasks;
tasks.forEach((task) => {
// Build the markup
const markup = `
<div >
<div >
<div >
<h5 data-title="${task}">${task}</h5>
</div>
<div >
<button >Remove Item</button>
</div>
</div>
</div>`;
// Append it to the container
todoContainer.innerHTML = markup;
getAllElements();
});
} else {
if (noTasksText.classList.contains("d-none")) {
noTasksText.classList.remove("d-none");
}
}
};
// event listener for 'enter on input'
mainInput.addEventListener("keydown", (e) => {
// if error is showing, hide it!
if (!errorText.classList.contains("d-none")) {
errorText.classList.add("d-none");
}
if (e.key === "Enter") {
// Get the value of the input
let inputValue = mainInput.value;
if (inputValue) {
// Build the markup
const markup = `
<div >
<div >
<div >
<h5 data-title="${inputValue}">${inputValue}</h5>
</div>
<div >
<button >Remove Item</button>
</div>
</div>
</div>`;
// hide 'no tasks' text
noTasksText.classList.add("d-none");
// Append it to the container
todoContainer.innerHTML = markup;
getAllElements();
// Push value to 'tasks' array
tasks.push(inputValue);
// Put in localStorage
textTasks = JSON.stringify(tasks);
localStorage.setItem("tasks", textTasks);
// Reset the value of the input field
mainInput.value = "";
// add 1 to the count
count ;
} else {
// Some very basic validation
errorText.classList.remove("d-none");
}
}
});
// remove task
todoContainer.addEventListener("click", (e) => {
// Find the button in the row that needs removing (bubbling)
const buttonIsDelete = e.target.classList.contains("js-remove-task");
if (buttonIsDelete) {
// Remove the HTML from the screen
e.target.closest(".js-single-task").remove();
// Grab the name of the single task
let taskName = e.target
.closest(".js-single-task")
.querySelector(".js-single-task-name h5")
.getAttribute("data-title");
// filter out the selected word
tasks = tasks.filter((item) => item != taskName);
textTasks = JSON.stringify(tasks);
localStorage.setItem("tasks", textTasks);
// update counter
count--;
// check if counter is zero and re-show 'no tasks' text if true
if (count === 0) {
noTasksText.classList.remove("d-none");
console.log(noTasksText);
}
}
});
body {
background: #e1e1e1;
}
<div >
<div >
<div >
<div >
<h2>To dos</h2>
<p>
Use this app to keep a list of things you need to do
</p>
<input id="main-input" type="text" placeholder="Type your todo and hit enter..." />
<small id="js-error" >
Please type a value and press enter
</small>
<hr />
<h4 >Your 'To dos'</h4>
<div id="todo-container">
<!-- todos append in here -->
<div >
<small >
<i>
You currently have no tasks. Use the input field above to start adding
</i>
</small>
</div>
</div>
</div>
<!-- /card -->
</div>
</div>
</div>
CodePudding user response:
Upon setting innerHTML
by using = innerHTML
the node noTasksText
is lost, because browser processes the whole new set innerHTML
and creates new objects. You can either retrieve noTasksText
again after that, or append nodes using todoContainer.appendChild
. I forked your pen and solved it with the latter solution.