I'm learning basic javascript events and would like to know how I can undo a click event.
I have a simple event that changes text using the changeText method.
That's only half of what I wan't to do. As user experience is important , I'd like for this event to be "undone" when the user clicks anywhere on the page.
So essentially after the user has clicked the button and changed text, they should be able to click anywhere on the page causing the text to go back to its default "I will change".
const changeText = () => {
const p = document.querySelector('p');
p.textContent = "I changed because of an event listener.";
}
// Listen for click event
const button = document.querySelector('button');
button.addEventListener('click', changeText);
<button>Click me</button>
<p>I will change.</p>
You could add another function revertText which undoes the change, and call that when the document is clicked. If you do this, however, it will be triggered when you click the button as well. So, in order to only tigger the button event when the button is clicked use e.stopPropagation() to stop the document click event from also executing.
See working example below:
const changeText = e => { // Pass event (e) argument into function
e.stopPropagation(); // Stop the document click event from running
const p = document.querySelector('p');
p.textContent = "I changed because of an event listener.";
}
const revertText = () => {
const p = document.querySelector('p');
p.textContent = "I will change";
}
// Listen for click event
const button = document.querySelector('button');
button.addEventListener('click', changeText);
document.addEventListener('click', revertText) // Add second event listener on the document
<button>Click me</button>
<p>I will change.</p>
So you just want another click event but on another object.
In your HTML you have to make some sort of container and then:
const changeTextBack = () => {
const p = document.querySelector('p');
p.textContent = "I will change.";
}
// Listen for click event
const buttonContainer = document.querySelector('button-container');
buttonContainer.addEventListener('click', changeTextBack);
you can attach an eventListener to the document:
document.addEventListener('click', changeTextBack)
changeTextback (event) {
// use event target to make sure he is not clicking on button
// use same methods as in changeText method to target element and alter text
}
The document represents the entire page. You could also attach it to the window.
https://developer.mozilla.org/en-US/docs/Web/API/Document
https://developer.mozilla.org/nl/docs/Web/API/Window
was this helpful?
You can bind an event on the document and store the default text somewhere globally. See the solution below:
let obj = {}; // object to store the def text of "p"
const changeText = (event) => { // pass the event object here
event.stopPropagation(); // stop the event to propagate back to parent
const p = document.querySelector('p');
if (event.target.tagName === "BUTTON") { // if clicked item is button
if (!obj['p']) {
obj['p'] = p.textContent; // store the def text in obj
}
p.textContent = "I changed because of an event listener."; // change the text
} else { // otherwise
p.textContent = obj.p; // change back the def text
}
}
// Listen for click event
const button = document.querySelector('button');
button.addEventListener('click', changeText);
document.addEventListener('click', changeText);
<button>Click me</button>
<p>I will change.</p>
Related
document.getElementById("btn2").onclick = false;
I did this to stop getting on click event after the first one and when I want to set it back to normal
document.getElementById("btn2").onclick = True;
it does not take click events
You could always disable the button, like this:
document.getElementById("btn2").disabled = true;
This sets the disabled attribute to true, therefore stopping the onClick function from being called when the user clicks the button.
Declare a variable boolean and change using logical not operator (!: see in https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_NOT), example:
let toggle = true;
let button = document.querySelector('button');
let result = document.querySelector('span');
button.onclick = () => {
toggle = !toggle;
result.textContent = `Your switch to ${toggle}`;
}
<button>Click me</button>
<span></span>
You may not set onclick event as True instead try this way.
const setEvent = (enable) => {
if(enable){
document.getElementById("btn2").addEventListener('click', onClickEvent);
}
else{
document.getElementById("btn2").removeEventListener('click');
}
}
function onClickEvent(){
//Your actual event when clicking the button
}
//Now enable or disable the event as follows
setEvent(true); //Will attach the event
setEvent(false); //Will remove the event
Make sure you call setEvent(true) once only, because it can attach multiple events.
my main project is too complicated to show here so I created a small script demonstrating the problem I am working on. In simple terms, I need to create a button that once clicked, generates a button that also has an event listener that returns that button's id to the console.
See code below:
button_number = 0
create_buttons = document.getElementById('create_buttons')
div = document.getElementById('div')
create_buttons.addEventListener('click', e=>{
button_number += 1
new_button = document.createElement('button')
new_button.setAttribute('id', 'button'+button_number)
new_button.innerHTML = 'What number am I?'
new_button.addEventListener('click', show_button_number)
div.appendChild(new_button)
})
function show_button_number () {
let number = button_number
button = document.getElementById('button' + number)
console.log(button.id)
}
<div id="div">
<button id="create_buttons">Create a button!</button>
</div>
As written, all generated buttons return the button id of the most recently generated button versus their own id. Is there anyway I can change the anonymous function to return the button id of the button that was clicked? In order to integrate this into my main project, I need to create the event listener for the dynamically generated buttons using an anonymous function.
You could make a higher-order function, one that takes the current button number as an argument and returns a function using it:
const makeListener = num => () => {
const button = document.getElementById('button' + num)
console.log(button.id)
};
new_button.addEventListener('click', makeListener(button_number))
Or, you may not need the ID at all, just pass the element itself:
create_buttons.addEventListener('click', e=>{
const btn = div.appendChild(document.createElement('button'));
btn.textContent = 'What number am I?'
btn.addEventListener('click', () => {
console.log(new_button);
});
});
The major way to do that is to use event delegation mechanim
const divParent = document.getElementById('div')
var button_number = 0
divParent.addEventListener('click', e =>
{
if (!e.target.matches('button')) return // ignore clicks from other things
if (e.target.id === 'create_buttons')
{
let new_button = document.createElement('button')
new_button.id = 'button' + ++button_number
new_button.textContent = 'What number am I?'
divParent.appendChild(new_button)
}
else
{
console.clear()
console.log( e.target.id )
}
})
<div id="div">
<button id="create_buttons">Create a button!</button>
</div>
When a user types something in an <input type="text"> and removes the focus, both blur and change are fired.
However, when I do the same thing in JavaScript using the blur() method, only the blur event is fired and not the change event. Is there a reason for this inconsistency?
See this example code (jsfiddle):
<input type="text">
<button>Test</button>
const input = document.querySelector("input")
input.addEventListener("blur", () => console.log("blur"))
input.addEventListener("change", () => console.log("change"))
const button = document.querySelector("button")
button.addEventListener("click", () => {
setTimeout(() => {
input.focus()
input.value = "something"
input.blur()
}, 1000)
})
When clicking the button, after a second, it should focus the input, change the value and blur the input. However, only the blur event is fired. Doing the same by hand will fire both events.
I like to trigger some validation logic on the change event and it works perfectly fine in real-live but when I try to recreate the workflow in a unittest it fails and I'm not sure why and how to solve it.
So the alternative question is: How can I trigger the change event from JavaScript?
This issue
The change event is specifically emitted when a user interacts with an element. This is built in a was intentional. #see HTMLElement: change event.
The solution
Use synthetic events to mimic user interaction in changing the value: input.dispatchEvent(new Event("change"));
Below is a working example with the logic in its own function updateValueWithChangeEvent .
const input = document.querySelector("input")
input.addEventListener("blur", () => console.log("blur"))
input.addEventListener("change", () => console.log("change"))
// Updates the value of an input and triggers the change Event.
const updateValueWithChangeEvent = (input, value) => {
if (input.value === value) return
input.value = value
input.dispatchEvent(new Event("change"));
}
// Updated example using function above
const button = document.querySelector("button")
button.addEventListener("click", () => {
setTimeout(() => {
// This will update value and trigger change event
updateValueWithChangeEvent(input, "something")
// This will trigger the blur event
input.focus()
input.blur()
}, 1000)
})
<input type="text">
<button>Test</button>
You can trigger an event like this:
const input = document.querySelector("input");
const event = new Event("change");
// blur the input
input.blur();
// dispatch change event manually
input.dispatchEvent(event);
https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events
I have this function that should I think trigger an alert whenever one of the buttons in my page gets clicked, however nothing happens and if I open the console on the webpage, no errors show. why is it?
document.addEventListener('DOMContentLoaded' , () => {
document.querySelectorAll('.new-button').forEach (button => {
button.onclick = () => {
const buttonName = button.innerHTML;
alert(`you have selected the button! ${buttonName}`);
}
});
});
I am using the ES6 version of JavaScript if that's any help.
If you dynamically add elements, you have to attach the eventListener to some ancestor element that was statically added. documentworks, but you can add more specific elements for performance. This concept is called delegate listener.
document.addEventListener('click',function(e){
if(e.target && e.target.matches('.new-button')){
const buttonName = e.target.innerHTML;
alert(`you have selected the button! ${buttonName}`);
const newButton = document.createElement('button');
newButton.classList.add('new-button');
newButton.innerHTML = 'click me!!';
document.getElementById('container').append(newButton);
}
});
<div id="container">
<button class="new-button">click me!</button>
</div>
I have an input field with a JS focusout event. Under my input field, I have an autocomplete popup with suggestions. But when I click on a suggestion, it’s playing the focusout before the event listener on the click of the autocomplete! Any clues on how I can I fix this conflict?
Picture of the input and its autocompletion:
The click event:
resultsFrom.addEventListener('click', (event) => {
let e;
e = event.target.parentNode;
inputFrom.value = e.getAttribute('data-display');
});
The focusout event:
inputFrom.addEventListener('focusout', () => {
const list = document.querySelector('#results-from');
let first = list.firstChild;
inputFrom.value = first.getAttribute('data-display');
resultsFrom.innerHTML = '';
});
The focusout event has a property on the event object of relatedTarget - this is the element that's going to gain the focus, in this case, it will be the element you're clicking on.
You need to check if that element is within your results, and not clear them out if that's the case. Something like this:
inputFrom.addEventListener('focusout', (e) => {
const list = document.querySelector('#results-from');
if (!list.contains(e.relatedTarget)) {
//the target is not in the list, continue as before
//otherwise allow the click to function by not clearing out resultsFrom
let first = list.firstChild;
inputFrom.value = first.getAttribute('data-display');
resultsFrom.innerHTML = '';
}
});