Nested buttons: how to achieve separate functionality - javascript

I have a button appended to a parent button:
var parent_button = document.createElement("button");
var child_button = document.createElement("button");
parent_button.appendChild(child_button);
I want to create functionality for the child_button that's independent from that of the parent_button:
parent_button.onclick = function () {
//do stuff
};
child_button.onclick = function () {
//do some other stuff
};
But given this code, whenever I click on child_button, I am necessarily triggering parent_button.onclick(). How do I separate the two?
The overlapping buttons look like this:

Use stopPropagation() on the event object of the child button. It will prevent the event from propagating from the child to the parent.
You can see an example here:
var pb = document.getElementById("pb");
var cb = document.getElementById("cb");
pb.addEventListener("click", function(e) {
console.log("Parent");
});
cb.addEventListener("click", function(e) {
console.log("Child");
e.stopPropagation();
})
<button id="pb">Parent Button <button id="cb">Child Button</button></button>
Note: I think that the default behavior is that when you click on a child element, the event of both the child and the parent should trigger, but in this example, this doesn't happen; maybe it's something particular to buttons being parents.

Related

Binding listener to a dynamically created element

I am using bootstrap's list group to create a row of tabs. When someone clicks on an element in a table, it dynamically creates a new tab and appends it to that list group.
var newtext = "#"+ticket+" - "+parele.find("td:nth-child(3) strong").html();
var closebtn = $("<button>").addClass("close ml-2 mr-n2 newlyaddedclose").html("×");
var newdiv = $("<div>").addClass("d-flex justify-content-between").append(newtext).append(closebtn);
var newa = $("<a>").addClass("list-group-item list-group-item-action").attr("data-toggle","list").attr("href","#ticket"+ticket).attr("id","ticket"+ticket+"-tab").append(newdiv);
$("#ticketpanel").append(newa);
The problem I am having is the newly created close button. I need to bind a function that identifies when that is clicked to handle closing that tab, but it doesn't seem to be working. In my example here, I added the "newlyaddedclose" class to help identify the new element temporarily and I added the following code below to bind a function that is defined at the top of my script tag:
$(".newlyaddedclose").on("click",".close",closebtn).removeClass("newlyaddedclose");
This still doesn't work. When I inspect the close button element, console shows this error: Framework Event Listeners API Errors:
event listener's handler isn't a function or empt
Am I making this harder than it needs to be, or what am I doing wrong? I can simple put at the end of this element creation this:
$(".close").click(function() { ... });
But doing this starts to double up and triple up etc, those events on already created tabs.
EDIT:
Here is my entire block of script to clear up any confusion.
$(function() {
function closebtn() {
alert("Close button clicked...");
}
$(".ticket-line").click(function() {
var parele = $(this);
var ticket = parele.data("tnum");
// Check to see if ticket is already open in tabs
if($("#ticket"+ticket).length == 0) {
// Create tab on ticket panel
var newtext = "#"+ticket+" - "+parele.find("td:nth-child(3) strong").html();
var closebtn = $("<button>").addClass("close ml-2 mr-n2 newlyaddedclose").html("×");
var newdiv = $("<div>").addClass("d-flex justify-content-between").append(newtext).append(closebtn);
var newa = $("<a>").addClass("list-group-item list-group-item-action").attr("data-toggle","list").attr("href","#ticket"+ticket).attr("id","ticket"+ticket+"-tab").append(newdiv);
$("#ticketpanel").append(newa);
$(".newlyaddedclose").on("click",".close",closebtn).removeClass("newlyaddedclose");
// Create DIV with content
var newdata = $("<div>").addClass("tab-pane").attr("id","ticket"+ticket);
$("#ticket-tabs").append(newdata);
$("#ticket"+ticket+"-tab").tab("show");
} else {
// Ticket is already open, switch to it instead
$("#ticket"+ticket+"-tab").tab("show");
}
});
})
The error is clearly stating you are binding a non function to the event listener. So the error is saying that closeBtn is not a function. Your code, you defined closeBtn as the button you are trying to attach the event too. So change closeBtn in the click event listener to the name of the function you are actually trying to call. If it is the same function name, rename something.
Your problem:
var closeBtn = 1;
if (1===1) {
var closeBtn = 2;
console.log(closeBtn);
}
console.log(closeBtn);
It is unclear why you are selecting the element you just added. You can just attach the event when you create the button, no need to look up the element.
var closebtn = $("<button>")...
closeBtn.on("click", function (){
console.log('clicked', closeBtn);
});
Or use event delegation so any element you add will trigger the function.
$("#ticketpanel").on("click", ".close", function () {
const closeBtn = $(this);
console.log('clicked', closeBtn);
});

Event listener for A tag to close overlay menu not working

I have an overlay menu that I need to shut when clicking on the links. I have some event listeners but it doesn't work on the links. The menu event used on the burger icon works, the menuItems is for the links that doesn't work. I need it to also work with Pjax link
I have tried target the a tags like menuItems = document.querySelectorAll('.__overlay_nav_content_list_item a'); but it does not work.
(function($) {
"use strict";
var app = function() {
var body = undefined;
var menu = undefined;
var menuItems = undefined;
var init = function init() {
body = document.querySelector('body');
menu = document.querySelector('.burger_menu_icon');
menuItems = document.querySelectorAll('.__overlay_nav_content_list_item');
applyListeners();
};
var applyListeners = function applyListeners() {
menu.addEventListener('click', function() {
return toggleClass(body, '__overlay_nav-active');
});
menuItems.addEventListener('click', function() {
return toggleClass(body, '__overlay_nav-active');
});
};
var toggleClass = function toggleClass(element, stringClass) {
if (element.classList.contains(stringClass))
element.classList.remove(stringClass);
else element.classList.add(stringClass);
};
init();
}();
})(jQuery);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Have you checked event bubbling? If menuItems are descendants of menu, clicking the menu items will trigger menuItems.addEventListener('click', function() { and hence toggle the class, then the event will bubble up to menu and trigger menu.addEventListener('click', function() {, removing the class you just added. So the end result is that it looks like nothing changed.
If that is the issue, only use the click event on menu, or stop the bubbling up of the event of the menuItems by using event.stopPropagation().
Although i would prefer only using the click event of the menu, first try:
Keep in midn that querySelectorAll() returns a nodeList, so it's an arraylike object containing all the links, not a single link.
Array.from( menuItems ).forEach(function( menuItem ) {
menuItem.addEventListener('click', function( event ) { // add event here so you have access to it!
event.stopPropagation(); // call stopPropagation() first
return toggleClass(body, '__overlay_nav-active'); // Once you return, the function stops.
});
});
so we know that this is the problem or not. Do not forget to add event as the parameter for the event handler function.

How to add one click event handler for two different buttons without using jQuery?

When a user opens a modal, there are two ways of closing it, one by pressing the 'x' in the top right corner of the box, or by pressing the cancel button. The two buttons are both in the same class name modal-hide but also have id's of modal-close and modal-cancel.
var cancel = document.getElementById('modal-cancel');
var close = document.getElementById('modal-close');
cancel.onclick = function () {
//close window
}
close.onlick = function () {
//close window
}
What is the best way to implement an event handler so that I don't have to write two different onclick functions that do the same thing?
I do not want to use jQuery for this at all!
First of all, don't use event properties! Use addEventListener (MDN) as a modern standard instead. Then you should define a separate handler function (let's say onClick) to bind it with your elements:
var cancelElt = document.getElementById('modal-cancel');
var closeElt = document.getElementById('modal-close');
var onClick = function () {
alert('Hello!');
};
cancelElt.addEventListener('click', onClick);
closeElt.addEventListener('click', onClick);
Simply use named instead of anonymous functions. I use addEventListener below instead of onclick.
var cancel = document.getElementById('modal-cancel');
var close = document.getElementById('modal-close');
var clickFunction = function() {
// close modal
}
cancel.addEventListener('click', clickFunction, false);
close.addEventListener('click', clickFunction, false);
A solution that basically is one line.
Array.prototype.forEach.call(document.querySelectorAll("#modal-cancel, #modal-close"), function(element) {
element.addEventListener("click", function() {
//close window
}, false);
});

How to get id of clicked button in winjs

I have several buttons in my WinJS page.
<button id="btn1">
Button 1
</button>
<button id="btn2"">
button 2
</button>...
and javascript to add click event to clicked button:
(function () {
WinJS.UI.processAll().done(function () {
var showButton = document.querySelector("xxx");
showButton.addEventListener("click", function () {
});
});
})();
How do i determine what button is clicked and set value of "xxx" to id of that button (btn1, btn2 etc...)
If I understood you correctly, you want to identify the button (sender) when you have multiple buttons that are attached to a single event handler.
MSDN:
In JavaScript, Windows Runtime event arguments are represented as a
single event object. In the following example of an event handler
method, the ev parameter is an object that contains both the sender
(the target property) and the other event arguments. The event
arguments are the ones that are documented for each event.
So you need to define an argument for the event handler and use its target property.
Let's say you have the following HTML:
<div id="label1"/>
<div>
<button id="button1">Button1</button><br />
<button id="button2">Button2</button><br />
<button id="button3">Button3</button><br />
</div>
and attached a single event handler to all of the buttons:
var button1 = document.getElementById("button1");
button1.addEventListener("click", buttonClickHandler);
var button2 = document.getElementById("button2");
button2.addEventListener("click", buttonClickHandler);
var button3 = document.getElementById("button3");
button3.addEventListener("click", buttonClickHandler);
you can access to sender in this way:
function buttonClickHandler(eventInfo) {
var clickedButton = eventInfo.target;
var label1 = document.getElementById("label1");
label1.innerHTML = clickedButton.id.toString();
}
Here's a WinJS solution to get the buttons :
var buttons = WinJS.Utilities.query('button');
Then you can bind the event to the buttons click :
buttons.forEach(function (btn) {
btn.addEventListener("click", function () {
console.log('button ' + this.id + ' has been clicked.');
})
});
I am new to WinJS, so there is probably a prettier solution to replace the forEach.
Something like this should work. querySelector only returns the first match, so you need to use querySelectorAll (see docs).
var buttons = document.querySelectorAll("button");
for (var i = 0; i < buttons.length; i++) {
buttons[i].addEventListener("click", function () {
var id = this.id;
// do stuff with "id"
});
}
You might also consider looking into jQuery as that can make things like this a little bit cleaner.

Restore page content and retain event listeners

How would you "save" a page somewhere (on the client), then restore it, with all its event listeners intact?
I have a sample at http://jsfiddle.net/KellyCline/Fhd55/ that demonstrates how I create a "page", save it and go to a "next" page on a button click, and then restore the first page on another button click, but now the first page's click listeners are gone.
I understand that this is due to the serialization that html() performs, so, obviously, that's what I am doing wrong, but what I'd like is a clue to doing it right.
This is the code:
var history = [];
$(document).ready(function () {
var page1 = $(document.createElement('div'))
.attr({
'id': 'Page 1'
}).append("Page ONE text");
var nextButton = $(document.createElement('button'));
nextButton.append('NEXT');
nextButton.on('click', function () {
CreateNextPage();
});
page1.append(nextButton);
$("#content").append(page1);
});
function CreateNextPage() {
history.push( $("#content").html( ) );
$("#content").html( 'Click Me!');
var page1 = $(document.createElement('div'))
.attr({
'id': 'Page 2'
}).append("Page TWO text");
var nextButton = $(document.createElement('button'));
nextButton.append('NEXT');
nextButton.on('click', function () {
CreateNextPage();
});
var prevButton = $(document.createElement('button'));
prevButton.append('PREVIOUS');
prevButton.on('click', function () {
GoBack();
});
page1.append(nextButton);
page1.append(prevButton);
$("#content").append(page1);
}
function GoBack() {
$("#content").html( history[history.length - 1]);
history.pop( );
}
and this is the html:
Click Me!
I think this is best solved via event delegation. Basically, you encapsulate content that you know you are going to refresh within a wrapper element and bind your listeners to that. The jQuery .on() method allows you to pass a selector string as a filter and will only trigger the handler if the originating element matches. Give your buttons a class or something to get a handle on them and then bind them up above. This article has some examples.
$( '#content' ).on( 'click', 'button.next', createNextPage )
for instance.

Categories

Resources