I have a javascript code where I store an element in an array upon clicking on a button, and change the button from "+" to "-". Upon clicking the button again, I want to remove that element from the array.
I have this code. It does the first part well, but it also removes the element without clicking on the button the second time.
let favorites = []
let buttonList = document.querySelectorAll("button")
let click = 0
buttonList.forEach((button, index) => {
button.addEventListener('click', function saveFavourites() {
click++
favorites.push(products[index])
button.textContent = "-"
console.log(favorites)
if (click === 1) {
this.removeEventListener('click', saveFavourites)
}
})
button.addEventListener('click', function removeFavourites() {
click ++
let i = favorites.indexOf(products[index])
favorites.splice(i, 1)
button.textContent = "+"
console.log(favorites)
if (click === 2) {
this.removeEventListener('click', removeFavourites)
}
})
})
You're adding two separate event listeners to the same button element. Use a single event listener and toggle a boolean flag variable to keep track of whether the element should be added or removed.
let favorites = []
let buttonList = document.querySelectorAll("button")
buttonList.forEach((button, index) => {
let isAdded = false; // flag variable to track whether the element is added or removed
button.addEventListener('click', function toggleFavourites() {
if (isAdded) {
let i = favorites.indexOf(products[index])
favorites.splice(i, 1)
button.textContent = "+"
isAdded = false;
} else {
favorites.push(products[index])
button.textContent = "-"
isAdded = true;
}
console.log(favorites)
})
})
When the button is clicked, the code checks the value of isAdded and either adds the element to the favorites array or removes it.
Here is the Demo:- Codesandbox
Related
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", "");
I am working on follow button and with the help of JavaScript I've come up with the following code.
But the problem is I have to double click the follow button to functioning this is due to using click event 2 time. I am open to better methods of solving this too.
var value = null;
const onClick = (event) => {
// event.target.id
value = event.target.id;
console.log(value);
document.getElementById(`${value}`).addEventListener('click',function(){
// console.log(value.id);
if(this.classList.contains('follow')){
this.classList.remove('follow');
this.innerHTML ="Following";
this.style.backgroundColor = 'green' ;
}else{
this.classList.add('follow');
this.style.backgroundColor = 'rgb(27,18,83)' ;
this.innerHTML="Follow";
}
})
}
window.addEventListener('click', onClick);
There is a double click event. You can check the if it satisfies your requirement.
MDN link - https://developer.mozilla.org/en-US/docs/Web/API/Element/dblclick_event
For multiple buttons with the same function, I would give all of them the same class (e.g. "btn"). Then in JS simply get all of the elements with this class, loop over the HTMLCollection which you would get and assign each element an eventlistener. When you want to change something on the button you have to use event.target in the function:
let buttons = document.getElementsByClassName("btn");
for (btn of buttons) {
btn.addEventListener("click", (event) => {
if(event.target.classList.contains('follow')){
event.target.classList.remove('follow');
event.target.innerHTML ="Following";
event.target.style.backgroundColor = 'green' ;
}else{
event.target.classList.add('follow');
event.target.style.backgroundColor = 'rgb(27,18,83)' ;
event.target.innerHTML="Follow";
}
});
}
<button class="btn">1</button>
<button class="btn">2</button>
I am trying to make a calendar web application and want to save a users' state using localstorage. I have tried using two methods (innerHTML as well as a javascript DOM-to-JSON; https://github.com/azaslavsky/domJSON), but each removes the button's onclick event.
I believe the problem is that it is stringifying only the innerhtml, which for some reason doesnt have my onlick event listed (although the onlick event fires). What should I do so that a user can reload the page and still use the buttons.
Here is an example that shows the button stop working (delete cache and website cookies/data to make the button work again):
https://codepen.io/samuel-solomon/pen/XWdzKjd?editors=1011
window.pageState;
$(document).ready(function(){
let div = document.getElementById("div")
window.pageState = JSON.parse(localStorage.getItem('pageState'));
if (window.pageState === null) {
let btn = document.createElement('button')
btn.innerText = "Hello";
btn.style = "height: 100px";
btn.onclick = function (btn) {change_text(btn)}.bind(null, btn);
div.appendChild(btn);
window.pageState = {state: div.innerHTML};
}
else {div.innerHTML = window.pageState.state}
});
function change_text(btn) {
let div = document.getElementById("div");
if (btn.innerText === "Hello") {btn.innerText = "Goodbye";}
else if (btn.innerText === "Goodbye") {btn.innerText = "Hello";}
localStorage.setItem('pageState', JSON.stringify(window.pageState));
}
Here is my wbesite where the problem exists (Ex: double click 'Ae 101A' and double click it in the calender. it should disappear. Now add it again and reload the page. Now it doesnt disappear on double click):
https://turtle-pond.com/
It seems you are using jQuery. For hooking events to dynamically created elements, I suggest the selector filtering approach: instead of binding the click event on the button element itself, listen for the click event on the parent container (or even the document) and filter by the button's selector. Here is how:
window.pageState;
$(document).ready(function(){
let div = document.getElementById("div")
$(document).on('click', "#my-button", function (event) {
const btn = event.target; // or btn = document.getElementById("my-button");
change_text(btn);
});
window.pageState = JSON.parse(localStorage.getItem('pageState'));
if (window.pageState === null) {
let btn = document.createElement('button')
btn.innerText = "Hello";
btn.style = "height: 100px";
btn.id = "my-button";
div.appendChild(btn);
window.pageState = {state: div.innerHTML};
}
else {div.innerHTML = window.pageState.state}
});
function change_text(btn) {
let div = document.getElementById("div");
if (btn.innerText === "Hello") {btn.innerText = "Goodbye";}
else if (btn.innerText === "Goodbye") {btn.innerText = "Hello";}
localStorage.setItem('pageState', JSON.stringify(window.pageState));
}
Read more about this on jQuery documentation and here.
I have a button with a click event listener
let button = document.createElement('button');
let show = false;
button.addEventListener('click', (e) => {
e.stopPropagation();
show = !show;
console.log("show from inside:", show)
});
console.log("show from outside:", show) // => is always equal to false
But this line above still shows the same value for variable show as initialized.
How can i modify show variable from inside like this line show = !show and get that modification from outside at this line console.log("show from outside:", show) ?
You have created the element but did not append it to body/parent node. You need to add the following line to your code.
document.body.appendChild(button);
If you need to get the modified show value, you can write a function to return the value of show and use it whereever you want.
Final Code:
let button = document.createElement('button');
document.body.appendChild(button);
let show = false;
function getShow() { return show; } // => returns the latest value of show
button.addEventListener('click', (e) => {
e.stopPropagation();
show = !show;
console.log("show from inside:", getShow());
});
console.log("show from outside:", getShow()); // => Keep in mind that this line will run before the event handler does and only for once.
Hope this helps!
I am currently working on a Todo list for practice and was wondering if there was a way to restore a list. I was thinking of creating a copy of the list and then re-adding the contents to the DOM once the user hits a restore 'Restore List' button but I just can't figure it out!
Here is my current code, the button appears when I hit 'Reset List' button but it is not functional yet.
document.addEventListener('DOMContentLoaded', () => {
//Targets the unordered list element
const list = document.getElementById("myUL");
//Targets the children of the unordered list
const li = list.children;
const resetDiv = document.getElementById('resetPanel');
const resetButton = document.createElement('button');
const restoreButton = document.createElement('button');
restoreButton.textContent = 'Restore Items';
resetButton.textContent = 'Reset List';
resetDiv.appendChild(resetButton);
//Targets the form element.
const form = document.getElementById("registrar");
//Function declaration (Begins process of creating list item)
function createListItem(text){
//Creates the list item
const li = document.createElement('li');
//Function delcaration (creates an element and returns element)
function createElement(elementName, property, value) {
const element = document.createElement(elementName);
element[property] = value;
return element;
}
//Function declaration (Adds the created element to the list)
function appendToLi(elementName, property, value){
const element = createElement(elementName, property, value);
li.appendChild(element);
return element;
}
//Appends all children to the list item.
appendToLi('span', 'textContent', text);
appendToLi('label', 'textContent', 'Completed')
.appendChild(createElement('input', 'type', 'checkbox'));
appendToLi('button', 'textContent', 'remove');
appendToLi('button', 'textContent', 'edit');
/*Returns the list item and it's children to what has called the
createListItem function*/
return li;
}
//Sets an event listener to the reset button.
resetButton.addEventListener('click', (e) => {
const ul = document.getElementById("myUL");
const listCopy = document.getElementById("myUL");
//Moves through the unordered list and removes each list item.
while (ul.firstChild) {
ul.removeChild(ul.firstChild);
}
resetDiv.appendChild(restoreButton);
});
//This button should restore the removed list items. ***********************
restoreButton.addEventListener('click', (e) => {
resetDiv.removeChild(restoreButton);
});
//Event listener (listens for click on submit button/enter press)
form.addEventListener('submit', (e) => {
e.preventDefault();
//Targets the input element.
const input = document.querySelector('input');
//If the user has not entered any text in the input field, alerts.
if(input.value === '') {
alert('Please enter an item!');
//Otherise begins the process of creating the list item.
} else {
//Holds the user text input.
const text = input.value;
/*Calls the createListItem function which will begin the process
through various other functions.*/
const listItem = createListItem(text);
list.appendChild(listItem);
input.value = '';
}
});
//Listens for clicks on the list item's children buttons.
list.addEventListener('click', (e) => {
const button = e.target;
const li = e.target.parentNode;
const ul = li.parentNode;
//Click on remove button
if(button.textContent === 'remove'){
ul.removeChild(li);
//Click on edit button
} else if (button.textContent === 'edit'){
const span = li.firstElementChild;
const input = document.createElement('input');
input.type = 'text';
input.value = span.textContent;
//Inserts a text field in place of the previous span item (User's input)
li.insertBefore(input, span);
li.removeChild(span);
button.textContent = 'save';
//Click on save button
} else if (button.textContent === 'save'){
const span = document.createElement('span');
const input = li.firstElementChild;
span.textContent = input.value;
//Inserts the new text over the input field.
li.insertBefore(span, input);
li.removeChild(input);
button.textContent = 'edit';
}
});
});
Looking at what you wanna do u obviously have to save deleted items into a cache but copying d whole thing wont be efficient (it would work maybe with a bit more code) try placing d deleted item into an array list with d position iD on d initial list and putting d item back u could just put it back at the initial position and if d id is a numbering system that helps with position you could just add 1 to all d items after d previously deleted item