My span won't delete or not getting targetted - javascript

I don't understand why my span isn't getting deleted when all others is behaving like they supposed. Here's my function.
const item = e.target;
if (item.classList[0] === 'trash-btn') {
// this works
const task = item.parentElement.parentElement;
task.classList.add('vanish');
task.remove();
}
if (item.classList[0] === 'text-duedate') {
// this is not working
item.remove();
}
if (item.classList[0] === 'check-btn') {
// this works
const task = item.parentElement.parentElement;
completed.appendChild(task);
item.remove();
completed.addEventListener('click', statusCheck);
}
The div that I am targeting:
<div class="card-date">
<span class="text-duedate">Due: </span>
<span class="alert">9/12/22</span>
</div>
Any help is appreciated.

Maybe the text-duedate class is not the first class in the classList, I suggest to use contains
if (item.classList.contains('text-duedate')) {
// this is not working
item.remove();
}
NOTE
The target element is the element who clicked not who fired the event, so maybe a child element of it, I suggest you use currentTarget.
const item = e.currentTarget;

I just ran your code and it seems to work fine
<div class="card-date">
<span class="text-duedate">Due:</span>
<span class="alert">9/12/22</span>
</div>
and JS
window.addEventListener('click', (e) => {
const item = e.target;
if (item.classList.contains('text-duedate')) {
item.remove();
}
})

thank you for the help. This was the only way I could get it to work.
if (item.classList[0] === 'trash-btn') {
const task = item.parentElement.parentElement;
task.classList.add('vanish');
// task.addEventListener('transition-end', function(){
// task.remove();
// });
task.remove();
}
let elems = document.querySelector('.text-duedate');
elems.remove();
if (item.classList.contains('check-btn')) {
const task = item.parentElement.parentElement;
completed.appendChild(task);
item.remove();
completed.addEventListener('click', statusCheck);
}

Related

I am having problems trying to toggle an icon in javascript

I am trying to toggle my edit button to the class of hidden and my trash button to the class of show. but I am not able to target the elements I always am getting the error of NULL would anyone be able to help I am stuck.
I have tried many ways but I cant get the edit button to toggle to display none and my trash button to toggle to display flex thanks guys for any help
HTML
```
TO-DO LIST
Today's To Do
<div class="todo-list"></div>
<button id="clear-btn" class="clear-btn" type="button"
name="button">
Clear all completed
</button>
</div>
</body>
</html>
```
const clear = document.querySelector(".clear-btn");
const list = document.querySelector(".todo-list");
const input = document.getElementById("add-input");
const form = document.getElementById("todoform");
let todos = [];
const LINE_THROUGH = "lineThrough";
form.addEventListener("submit", (e) => {
e.preventDefault();
saveTodo();
});
function saveTodo() {
const todoValue = input.value;
todos.push({
value: todoValue,
completed: false,
});
input.value = "";
renderTodos();
}
function renderTodos() {
list.innerHTML = "";
todos.forEach((todo, index) => {
list.innerHTML += `
<div class="todo" id=${index}>
<i class="fa ${
todo.checked ? "solid fa-check" : "regular fa-square"
}" data-action="check"
></i>
<p class=${todo.checked ? LINE_THROUGH : ""} data-
action="check">${todo.value}</p>
<span class= "edit">
<i class='fas fa-ellipsis-v edit-task ' data-action="edit">
</i>
</span>
<i class="fa-solid fa-trash-can trash-btnn hidden " data-
action="delete">
</i>
</div>
`;
});
}
list.addEventListener("click", (event) => {
const { target } = event;
const parentElement = target.parentNode;
if (parentElement.className !== "todo") return;
const todo = parentElement;
const todoId = Number(todo.id);
const { action } = target.dataset;
action === "check" && checkTodo(todoId);
// action === 'edit' && checkTodo(todoId);
// action === 'delete' && checkTodo(todoId);
});
function checkTodo(todoId) {
todos = todos.map((todo, index) => ({
...todo,
checked: index === todoId ? !todo.checked : todo.checked,
}));
renderTodos();
}
const threeVBtn = document.querySelector(".edit-task");
const trashBtn = document.querySelector(".trash-btnn");
threeVBtn.addEventListener("click", (e) => {
trashBtn.classList.toggle("show");
threeVBtn.classList.toggle("hidden");
});
.hidden {
display: none;
}
.trash-btnn {
cursor: pointer;
color: rgb(204, 16, 47);
}
.show {
display: flex;
}
It is because on your first render there is no todo initially and the element specified by querySelector for threeVBtn returns undefined. One simple way is to check if the element is added to document before attaching event listener, like this:
if (threeVBtn) {
threeVBtn.addEventListener("click", (e) => {
trashBtn.classList.toggle("show");
threeVBtn.classList.toggle("hidden");
});
//perform any action for once threeVBtn is added to Document
}
The problem is when you run any of the DOM selection methods (querySelector, getElementByClassName, etc), it only grabs what's currently in the DOM (ie. currently rendered html). When you add or remove items from the DOM, you must re-select elements to get the current items. It's kind of a pain, but there is a solution.
There is a concept called "event delegation" which is a technique that solves this problem. Delegation works by picking a parent element of the dynamic list - it can be any parent element so long as the element stays in the DOM while your list is dynamically changing. Many people just use the <body> since it's always guaranteed to be there. Whenever something inside the parent element is clicked, the event does this cool thing called "bubbling" - which is beyond the scope of this answer. You can read all about DOM events, bubbling, and delegation on MDN.
You can attach your click handlers to the <body> element and check the event.target for the different types of things you want, best shown by example:
document.body.addEventListener('click', (ev) => {
const el = event.target;
if (el.classList.contains("edit-task")) {
// edit button was clicked
ev.preventDefault();
return;
}
if (el.classList.contains("trash-btnn")) {
// trash button was clicked
ev.preventDefault();
return;
}
});

How to run only one function if we have multiple class

So. to begin with,
I am writing my eventlisteners in this way.
document.addEventListener('click',(e)=>{
const element = e.target;
if(element.classList.contains('classOne'){
fire_function_one();
}
if(element.classList.contains('classTwo'){
fire_function_two();
}
});
I have a div like follows
<div class='classOne classTwo'>Something</div>
So what I want to achieve is,
When our div has classOne, I want to fire 'fire_function_one()', However when our div has both classOne and ClassTwo, I want to fire 'fire_function_two()' but I dont want to run 'fire_function_one()'.
What I have tried,
event.stopPropogation; //Not working
event.preventDefault; //Not working
if(element.classList.contains('classTwo' && !element.classList.contains('classOne'){
fire_function_two();
//Doesnt acheive what I want
}
Change the Order of your condition and use else if statement.
document.addEventListener('click',(e)=>{
const element = e.target;
if(element.classList.contains('classTwo'){
fire_function_two();
}
else if(element.classList.contains('classOne'){
fire_function_one();
}
});
If you are sure that the element can have classOne or both classTwo and classOne, you can just change the order and use else if statement:
document.addEventListener('click',(e)=>{
const element = e.target;
if(element.classList.contains('classTwo'){
fire_function_two();
} else if(element.classList.contains('classOne'){
fire_function_one();
}
});
You need to write click on element as below.
var eleOne = document.getElementsByClassName('classOne')
if(eleOne.length > 0) {
var currentEleOne = eleOne[0];
currentEleOne.onclick = function () {
// Click code for classOne
}
}
var eleTwo = document.getElementsByClassName('classTwo')
if(eleTwo.length > 0) {
var currentEleTwo = eleTwo[0];
currentEleTwo.onclick = function () {
// Click code for classTwo
}
}
Here you have two cases,
When both classes are present, fire only class two
If only class one is present, fire class one
So, First check with if whether both classes are present or not. If true then fire class two. Otherwise inside else if, check if class one is present and if this condition is met, fire class one.
document.addEventListener('click', function(e) {
const element = e.target;
if (element.classList.contains('classTwo')) {
console.log("Fire class two");
} else if (element.classList.contains('classOne')) {
console.log("Fire class one");
}
});
<div class='classOne classTwo'>Something 1 2</div>
<div class='classOne'>Something 1</div>
You could try a simple ternary like this:
document.addEventListener('click', (e) => {
const element = e.target;
element.classList.contains('classTwo') ? fire_function_two() : fire_function_one();
});
If the classList contains 'classTwo' then run fire_function_two() else fire_function_one()

How to stop .className when click anywhere on the screen

I have a function that makes an element from a list of elements change its .className when clicked, so lets say when I click the element becomes one color and the others another color. This function is the following:
const memberB = document.querySelectorAll('#memberBoxAlex,
#memberBoxLiv, #memberBoxFlo');
for (let i = 0; i < memberB.length; i++)
memberB[i].onclick = function(){
memberBoxAlex.className = "faded";
memberBoxLiv.className = "faded";
memberBoxFlo.className = "faded";
if(memberB[i].className=="open"){
memberB[i].className="";
}
else{
memberB[i].className="open";
}
This works perfectly, but what I want to happen next, is when I click outside its box to stop the all the effects so to make all memberB "normal" let's say, so to have .className="". I've tried to give to their container this function:
let exitEffect = document.getElementById(team)
exitEffect.onclick = function(){
memberBoxAlex.className = "";
memberBoxLiv.className = "";
memberBoxFlo.className = "";}
How can I do so when I click outside the box of the member all className for memberB will "stop" or become .className="".
use a single class for this for a more generic selector and I use this snippet to use a single event listener for this.
window.addEvent = (event_type, target, callback) => {
document.addEventListener(event_type, function (event) {
// If the event doesn't have a target
// Or the target doesn't look like a DOM element (no matches method
// Bail from the listener
if (event.target && typeof (event.target.matches) === 'function') {
if (!event.target.matches(target)) {
// If the element triggering the event is contained in the selector
// Copy the event and trigger it on the right target (keep original in case)
if (event.target.closest(target)) {
const new_event = new CustomEvent(event.type, event);
new_event.data = { originalTarget: event.target };
event.target.closest(target).dispatchEvent(new_event);
}
} else {
callback(event);
}
}
});
};
and then
window.addEvent('click', '.openable-member', (event) => {
document.querySelectorAll('.openable-member').each((element) => {
if (element !== event.target) {
element.classList.add('faded');
element.classList.remove('open'); // guessing you'll need this too
}
});
event.target.classList.toggle('open');
});
The Document method querySelectorAll() returns a static (not live) NodeList representing a list of the document's elements that match the specified group of selectors.
So you can map through memberB because it's not an array.
What you can do is:
const memberB = document.querySelectorAll('#memberA,#memberAA, #memberAAA ');
memberB.onclick = function(){
memberB.className = "faded";
if(memberB.className == "open"){
memberB.className = "";
}
else{
memberB.className = "open";
}
}
You can try this:
memberB[i].className = memberB[i].className.replace("open", "");

Event listener targeting all elements on filtering method

I am having a little trouble building a mock shopping site. When a button is clicked it appends that item to a checkout menu. The button on the checkout menu is supposed to remove the selected element but instead it deletes the whole menu. I have ran a forEach and I have tried to append the remove button with the createElement method but it doesn't work. I think the problem is that the buttons aren't actually creating separate elements for itself and I don't know how to fix it. Can someone point me in the right direction?
function addToCart(btns, cartItem) {
btns.forEach(btn => {
btn.addEventListener('click', (e) => {
if (shoppingList.indexOf(cartItem) !== -1) {
return
}
else {
cartMenuItems.innerHTML += `
<div class=${'menu-items-container'}>
<img width=150 height=125 src=${cartItem.image}>
<div class=${'menu-titles-container'}>
<h2> ${cartItem.title}</h2>
<h3> ${cartItem.price}</h3>
<button class=${'menu-btn'}> Remove</button>
</div>
</div>
`
shoppingList.push(cartItem)
cartItemCount.innerText = shoppingList.length;
}
const removeBtn = document.querySelectorAll('.menu-btn');
removeItem(shoppingList, removeBtn)
const getTotalOfItems = shoppingList.reduce((acc, current) => {
return acc + current.price
}, 0);
menuTotal.innerText = getTotalOfItems
return shoppingList
});
});
}
function removeItem(shoppingItems, btns) {
btns.forEach(btn => btn.addEventListener('click', (e) => {
let newArr = shoppingItems.filter(item => item.id !== item.id)
console.log(newArr)
shoppingList = newArr
cartMenuItems.innerHTML = newArr
cartItemCount.innerText = newArr.length
return newArr
}))
}
The line below in your removeItem function will always create a newArr element which is empty because you're comparing two values that are the same (item.id). And always return an empty list
let newArr = shoppingItems.filter(item => item.id !== item.id)

Foreach element create one element after it on click (error warning)

So for a form I'd like to create an error message after each element which is invalid, onclick of the submit button.
I have almost got it right, except it adds the span twice after the element (because two elements are invalid). I need to to add it once, to both invalid elements.
So my JS/jquery (yes, I know, I mixed it :p):
function checkrequired() {
var nbform = document.getElementById("userpanel");
var elements = document.querySelectorAll('[required]');
elements.forEach(element => {
if (element.value === "") {
nbform.classList.add('submitted-form-invalid');
$('.show-error').after("<span class='col-sm-12' style='color:red;'> Name is required.</span>");
} else {
return true;
}
});
How it looks:
I also know it adds it twice, instead of once, because it adds it foreach element. But I don't know how to surpass this. Anyone?
EDIT:
I came up with the next Javascript, this idea is all that I need. It doesn't need to be really complicated:
function checkrequired() {
var el = document.createElement("div");
el.setAttribute('class', 'error-message');
el.innerHTML = "Dit veld is verplicht";
var x = document.querySelectorAll('.show-error');
for (var i = 0; i < x.length; i++) {
x[i].appendChild(el);
}
}
The only problem now is that it only appends the child to the sconds div with class "error-message". Not both.
try this
// select your inputs with an id
var elements = document.querySelectorAll('#voornaam');
$("button").click(function(){
elements.forEach(element => {
if (element.value === ""&& element.nextSibling.id !="error") {
$("<span class='col-sm-12 error' id='error' style='color:red;'> Name is required.</span>").insertAfter(element);
return
}
else return true
})
});
});
$('.show-error') returns an array of elements. For all these elements you are adding the span after it checks invalid. I think plain js gives you more control here.
So here's an example snippet in plain vanilla js.
Maybe this jsFiddle is helpfull too. And remember: checking in the browser will not render a server side check unnecessary.
document.addEventListener("click", checkRequired);
const setError = (elem, remove) => {
const span = document.createElement("span");
span.classList.add("foutmelding");
span.appendChild(document.createTextNode(` vul svp ${elem.getAttribute("placeholder")} in`));
elem.insertAdjacentElement("afterEnd", span);
};
function checkRequired(evt) {
if (evt.target.id === "check") {
document.querySelectorAll(".foutmelding")
.forEach(el => el.parentNode.removeChild(el));
const validMsg = document.querySelector("#formValidation");
validMsg.dataset.invalid = "";
document.querySelectorAll('[required]')
.forEach(element => {
if (element.value.trim() === "") {
setError(element);
validMsg.dataset.invalid = "onvolledig ingevuld";
}
});
validMsg.dataset.invalid = validMsg.dataset.invalid || "all checks out well";
}
}
input {
margin: 0.3rem 0;
}
.foutmelding,
.formulierMelding {
color: red;
}
.formulierMelding:before {
content: attr(data-invalid);
}
<div id="theForm">
<input type="text" id="voornaam" placeholder="voornaam" required><br>
<input type="text" id="email" placeholder="e-mailadres" required>
</div>
<button id="check">Check</button>
<span id="formValidation" data-invalid class="formulierMelding"></span>

Categories

Resources