Handling click events for many elements by bubbling - javascript

I have a list of controls contained in a parent div called overlay-controls.
There is many list controls that each have their own overlay-controls.
I am using a for loop to add the event listener to each button that contains the class delete.
Before the user can delete the item, they must confirm. I am trying to attach this to every delete button found in overlay-controls.
I got it to work using a for loop but I know there is a better way using bubbling and capturing. I am having trouble targeting only the delete class inside overlay-controls by bubbling up to parent div.
See the live demo here by clicking on each delete button: http://jsfiddle.net/8qqfeoa2/1/
Here is my code using the for loop:
(function() {
function getConfirmation(e){
var retVal = confirm("Are you sure you want to delete this request?");
if( retVal == true ){
return true;
}else{
e.preventDefault();
return false;
}
}
var del = document.querySelectorAll('.delete');
for(var i = 0, len = del.length; i < len; i++){
del[i].addEventListener('click', function(e) {
getConfirmation(e);
}, false);
}
}());

You dont event need the For / .each loop
Jquery takes care of it internally
$('.delete').on('click', function(e){
getConfirmation(e);
});
Provided you are using jQuery and in getConfirmation method you may also get that specific (clicked) element by using e.target which returns the target on which click happened.

Only Javascript solution
As you requested one
var deletebuttons = document.getElementsByClassName('delete');
for(var button in deletebuttons) {
button.onclick = getConfirmation;
}

Related

Enable and disable onclick funtion

I am dynamically creating a table where i am adding onclick function to each column.
for (var x = 0; x < r.length; x++) {
//Setting the columns
if (i === 1) {
var headerCell = document.createElement("TH");
headerCell.innerHTML = r[x];
headerCell.id = x;
headerCell.onclick = function () {
sortTable(this.id, name);
}
row.appendChild(headerCell);
}
}
In a specific situation I want to disable the onclick function. Here is the code and it works.
$('#errorTable TH').prop("onclick", null).off("click");
and in another situation i want to reattach the onclick function. And that doesn't work. I want to enable the original function....
Any ideas ?
The way you created your table and adding/removing events are not easily maintainable. I also have some suggestions:
Review your code and define code click handler separately.
If you use jQuery in your project use it every where, if not, do not use it anywhere.
In your code i is undefined.
Add Remove Event Listener with jQuery
First define your handler function:
var myClickHandler = function(){
// this is your click handler
alert('Yes!!!');
}
Select your element and assign to a variable. <div id="clickable">Click Me!</div> must be in the DOM at the time of below script executed.
var element = $('#clickable');
// assign event listener
element.on('click',myClickHandler);
// remove event listener:
element.off('click',myClickHandler);
note that you must have to inform jQuery which handler should be removed.
See a sample https://codepen.io/softberry/pen/BEpove
An alternative is to build a click handler that checks a "kill switch".
var tableClickable = true;
headerCell.onclick = function () {
if (tableClickable) {
sortTable(this.id, name);
}
}
//In a specific situation I want to disable the onclick function.
something.addEventListener('someEvent', function () {
tableClickable = false;
});
//and in another situation i want to reattach the onclick function.
something.addEventListener('someOtherEvent', function () {
tableClickable = true;
});

What is this piece of Javascript for Isotope filter doing?

I am having some trouble understanding what is happening in a piece of vanilla JS for the Isotope filter. The original code is here: https://codepen.io/desandro/pen/VWLJEb
var buttonGroups = document.querySelectorAll('.button-group');
for (var i = 0; i < buttonGroups.length; i++) {
var buttonGroup = buttonGroups[i];
var onButtonGroupClick = getOnButtonGroupClick(buttonGroup);
buttonGroup.addEventListener('click', onButtonGroupClick);
}
function getOnButtonGroupClick(buttonGroup) {
return function(event) {
// check for only button clicks
var isButton = event.target.classList.contains('button');
if (!isButton) {
return;
}
var checkedButton = buttonGroup.querySelector('.is-checked');
checkedButton.classList.remove('is-checked')
event.target.classList.add('is-checked');
}
}
What is happening between the getOnButtonGroupClick function and it being assigned to a variable in the for loop preceding it?
getButtonGroupClick returns a closure that saves the value of buttonGroup. When you click on a button in the button group, it uses that closure variable to search for the checked button in the group, uncheck it, and then check the button you clicked on.
This complexity isn't really needed. When an event listener is called, event.currentTarget is set to the element that the listener was attached to, so you could just use that.
var buttonGroups = document.querySelectorAll('.button-group');
for (var i = 0; i < buttonGroups.length; i++) {
var buttonGroup = buttonGroups[i];
buttonGroup.addEventListener('click', onButtonGroupClick);
}
function OnButtonGroupClick(event) {
// check for only button clicks
var isButton = event.target.classList.contains('button');
if (!isButton) {
return;
}
var checkedButton = event.currentTarget.querySelector('.is-checked');
checkedButton.classList.remove('is-checked')
event.target.classList.add('is-checked');
}
The for loop is used to iterate over all of the elements with the class of button-group and and a click event listener to them. getOnButtonGroupClick returns a function to be used as the function to be used as the event listener for the element i.e. the function that is run when the element is clicked on.
var buttonGroups = document.querySelectorAll('.button-group');
//get all elements within the document with a class of button-group
//buttonGroups is a NodeList
for (var i = 0; i < buttonGroups.length; i++) {
//loop through all of the elements with a class of button-group matched by the above query selector
var buttonGroup = buttonGroups[i];
//get the element in the NodeList with the index i
var onButtonGroupClick = getOnButtonGroupClick(buttonGroup);
//get the function to be run when the element is clicked on
buttonGroup.addEventListener('click', onButtonGroupClick);
//add a click event listener to the element
}
function getOnButtonGroupClick(buttonGroup) {
return function(event) {
// check for only button clicks
var isButton = event.target.classList.contains('button');
//check if the element has a class of button
if (!isButton) {
//if the element does not have a class of button, do nothing
return;
}
var checkedButton = buttonGroup.querySelector('.is-checked');
checkedButton.classList.remove('is-checked')
event.target.classList.add('is-checked');
}
}
If I understood your question correctly, It means that a click event is being added to every button in the buttonGroups there is. Although, if you ask me, it would be way better and cleaner to just use a forEach, like so:
const buttonGroups = document.querySelectorAll('.button-group');
buttonGroups.forEach(button => button.addEventListener("click", OnButtonGroupClick)
function OnButtonGroupClick(event) {
// check for only button clicks
let isButton = event.target.classList.contains('button');
if (!isButton) {
return;
}
let checkedButton = event.currentTarget.querySelector('.is-checked');
checkedButton.classList.remove('is-checked')
event.target.classList.add('is-checked');
}
So, you add a click event to ALL the buttons in the buttonGroups that will run the function onButtonGroupClick.
EDIT: And there's no really need to assign the function like that... at all. Just call it on the click event and that's it.

Converting jQuery Code to detect clicks on links with a specific class to Native JavaScript

ok so I can achieve what I am looking to do using jQuery very easily using the following code:
<script type="text/javascript">
// <![CDATA[
$('.pnTrig').on('click', function(e){
e.preventDefault();
var id = $(this).attr('href').split("_").pop(); // get last character of string
console.log(id); // check correct character is returned
P7_TP3ctrl('p7TP3_1',id); // controls to show accordian panels
});
// ]]>
</script>
What I would like is for someone to show me how to convert this jQuery code to native JavaScript please.
Here is a documented vanilla Javascript version.
function clickHandler(event) {
// execute preventDefault() if don't want the link to be followed (default browser behavior)
event.preventDefault();
// get the event target (what `this` would refer to in jQuery)
var target = event.target;
// same as before
var id = target.href.split('_').pop();
// same as before
P7_TP3ctrl('P7_TP3', id);
}
// get all elements with `pnTrig` class
var triggers = document.querySelectorAll('.pnTrig');
// apply the event handler to all matching elements
for (var i = 0; i < triggers.length; i++) {
// attach the event handler (don't define the event handling function here)
triggers[i].addEventListener('click', clickHandler, false);
}
function P7_TP3ctrl(label, id) {
console.log("Clicked id: ", id);
}
Link One
Link Two

Check the form element triggered at onclick event

I have a js function inside a web page that trap the position of the click on it. I had to implement it in a way that is recognized if the page contain a form element, and, if so, do a check as to whether that the click that I'm recording is made on one at random form element or not.
I got up to check the form and to learn its elements, but I do not know how to check if the click is made ​​on a single element:
$('*').on('mousedown', function (e) {
// make sure the event isn't bubbling
if (e.target != this) {
return;
}
// do something with the click somewhere
// for example, get the coordinates:
x = e.pageX;
y = e.pageY;
pa = $(location).attr('href');
//Window dimension
var width = $(window).width();
//var width = $getWidth();
var height = $(window).height();
//Check if page contain a form
var elementF = document.querySelectorAll('form');
var fname = '';
var elements = '';
for (var i=0; i<elementF.length; i++){
fname = elementF[i].id;
elements = document.forms[fname].elements;
for (x=0; x<elements.length; x++){
//Now i need to check if element is clicked or not
}
}
What you're doing here is binding a mousedown event to every element on the page. This will be resource intensive and should not be necessary. Also, since it's bound everywhere, the if (e.target !== this){ return; } does prevent it from firing a million times whenever you mousedown on something, but also prevents it from bubbling. Just let it bubble and use e.target and e.currentTarget to tell stuff apart. There shouldn't be any reason to do $('*').on("mousedown")
Why can't you just do this?
$("form").on("click", function(e){
// e.currentTarget will be the form element capturing the click
console.log( $(e.currentTarget).html() );
// e.target will be whatever element you clicked on
// so you can do whatever you want with it
console.log( $(e.target).text() );
// All your handler code here
});
Some possibly helpful stuff
http://api.jquery.com/event.currenttarget/
Is there a difference between $(e.currentTarget) and $(this)?
http://learn.jquery.com/events/event-delegation/
Try
$('*').on('mousedown', function (e) {
var elementClicked = e.target.nodeName;
console.log("element: " + elementClicked)
})

AJAX addEventListener - passing parameters to another function

I'll keep this short - I've got a list of buttons, that I create using a loop, and when one of them gets clicked I want to be able to pass its id attribute to another file in order to dynamically generate a new page.
Here's the code:
for (var i in data.contacts) {
var temp = document.createElement('div');
temp.className = "contacts";
var dude = document.createElement('input');
dude.type = "button";
dude.value = data.contacts[i];
dude.id = data.contacts[i];
dude.className = "dude_button" + data.contacts[i];
dude.addEventListener('click', function(event) { gotoProfile(dude.id); }, false);
temp.appendChild(dude);
temp.appendChild(document.createElement('br'));
theDiv.appendChild(temp);
}
// and now in another file, there's gotoProfile():
function gotoProfile(x) {
var username = document.getElementById(x).value;
if (xmlHttp) {
try {
.... etc.
Now see this works, sort of, but the problem is that when I click any button, it only passes the last dude.id value from the list data.contacts. Obviously I want every button's addEventListener to pass its own data.contacts[i] value, instead of just the last one.
Help appreciated, thanks guys.
Because JavaScript has no block scope, dude will refer to the last assigned element (because the loop finished) when the event handler is called. You have to capture the reference to the current dude by e.g. using an immediate function:
dude.addEventListener('click', (function(d) {
return function(event) {
gotoProfile(d.id);
}
}(dude)), false);
This is a common error when creating functions in a loop.
But you can make it even easier. The event object has a property target that points to the element the event was raised on. So you can just do:
dude.addEventListener('click', function(event) {
gotoProfile(event.target.id);
}, false);
And with that said, you don't need to add a handler for every button. As you are doing the same for every button, you could attach the same event handler above to the parent of the buttons (or a common ancestor) and it would still work. You just have to filter out the clicks that don't happen on a button:
parent.addEventListener('click', function(event) {
if(event.target.nodeName == 'INPUT' && event.target.type == "button") {
gotoProfile(event.target.id);
}
}, false);

Categories