I have an event Handler that should return me the value of outerHTML of the element clicked.
//Callback function
var done = arguments[arguments.length - 1];
//Take all the events
var array_events = []
var retour = (e) => {
array_events.push(e.target.outerHTML)
}
var quit = (key) => {
console.log(array_events);
(key.keyCode == 27 )? done(JSON.parse(JSON.stringify(array_events))) : undefined
}
// Listen to the clicks
getHtml = document.addEventListener("click", retour, true)
// Listen to the key "esc" which means user has gathered all needed events
getKey = document.addEventListener("keydown", quit, true)
Then, I happened in a case where it didn't return just one value per click, but two! I'm demonstrating below the html of that element and a simple alert function to easily demonstrate visually the problem:
window.addEventListener("click", (e) => {alert(e.target.outerHTML)})
<div class="cycle-selector-buttons-old">
<input type="radio" id="1" value="1">
<label for="1" class="text-sm hover:bg-purple-100 focus:border-purple-500 focus:ring focus:ring-purple-200 md:text-lg">1 month</label>
<input type="radio" id="12" value="12" checked="">
<label for="12" class="text-sm hover:bg-purple-100 focus:border-purple-500 focus:ring focus:ring-purple-200 md:text-lg">12 months</label>
<input type="radio" id="24" value="24">
<span class="savings-label">Save up to 33%</span>
<label for="24" class="text-sm hover:bg-purple-100 focus:border-purple-500 focus:ring focus:ring-purple-200 md:text-lg">24 months</label>
</div>
[TIP-try clicking on the text of the options that appear from html code, or click randomly so you get the phenomenon I have described]
Seeing this, my preferred output would be the first one, I'm not sure if this applies in every situation.
All this code is being executed by python, JavaScript isn't my first language, so I couldn't find any similar example or solution of this problem on my research.
The final question is, How can I return only 1 value from the eventHandler 100% of the time (preferably the first, main or outer element)
When you click on a <label for="id1">
, it triggers a click on the <input id="id1">
, because of the for
attribute. So now you have two clicks, and your handler is executed twice.
You can prevent the second click from happening by calling Event.preventDefault()
in your handler. But this means that the radio button will not be activated when you click on the label, so it is as if there was no for
attribute at all.
Another solution would be to check in the handler, if the click happened on a label
tag with a for
attribute, and if so skipping the alert in favor of the subsequent event, which will trigger the alert on the input:
window.addEventListener("click", (e) => {
if(e.target.tagName === 'LABEL' && e.target.attributes.for?.value){
return
}
alert(e.target.outerHTML)
})
<div class="cycle-selector-buttons-old">
<input type="radio" id="1" value="1">
<label for="1" class="text-sm hover:bg-purple-100 focus:border-purple-500 focus:ring focus:ring-purple-200 md:text-lg">1 month</label>
<input type="radio" id="12" value="12" checked="">
<label for="12" class="text-sm hover:bg-purple-100 focus:border-purple-500 focus:ring focus:ring-purple-200 md:text-lg">12 months</label>
<input type="radio" id="24" value="24">
<span class="savings-label">Save up to 33%</span>
<label for="24" class="text-sm hover:bg-purple-100 focus:border-purple-500 focus:ring focus:ring-purple-200 md:text-lg">24 months</label>
</div>
Events in Firefox have a property indicating the source node of a click (Event.explicitOriginalTarget), which allows you to filter clicks that came from another node:
window.addEventListener("click", (e) => {
if(e.target !== e.explicitOriginalTarget){
return
}
alert(e.target.outerHTML)
})
but this will not work in any other browser.