Binding a click event in my JavaScript module - javascript

Since I started structuring my JavaScript as a module pattern, some of my click events no longer work. Since other parts of my JavaScript add HTML to the DOM, I need to use $('body').on('click') for a button.
This is what my module currently looks like:
var s,
MyApp = {
settings: {
fooButton: $(".foo-button"),
barButton: $(".bar-button")
},
init: function() {
s = this.settings;
this.bindEvents();
},
bindEvents: function() {
// this works
s.fooButton.on("click", function() {
MyApp.clickButton("foo");
});
// this does NOT work
$('body').on('click', s.barButton, function (event) {
MyApp.clickButton("bar");
});
},
clickButton: function(button) {
console.log("You clicked " + button)
}
};
The first click event is working, the second isn't. How can I bind and event for an element that was created by JavaScript code?

The second argument for your handler when the event is delegated is expected to be a string.
In your case it is a jQuery Object.
That is the root cause your click event is not working.
Change
barButton: $(".bar-button")
to
barButton: ".bar-button"

If you're creating the element in JS, you have to bind the event AFTER the element is created.
So put the binding event in a function, then call that function after your JS code has created the element. :)

When using .on() for event delegation, the second parameter has to be a string. Passing anything else won't work.
http://api.jquery.com/on/#on-events-selector-data

Related

How to wrap multiple dynamic eventListeners into one?

I just started to learn js and need a little help: I have the following function:
//SET CHAT BEHAVIOR
function chatSettings() {
console.log('ChatSettings called')
function BtnAndScrollBar(texteditor) {
console.log('BTNAndScrollBar called');
const sendBtn = $('.cl.active').find('.sendBtn');
const attachBtn = $('.cl.active').find('.attachBtn');
console.log(sendBtn)
}
function sendAndDeleteMessage(send) {
console.log(send);
}
var sendBtn = $('.cl.active').find('.sendBtn');
sendBtn.mousedown(function () {
sendAndDeleteMessage(this);
});
var textEditor1 = $('.cl.active').find('.chatTextarea');
textEditor1.on('focus change mousedown mouseout keyup mouseup', function (){
console.log(this);
BtnAndScrollBar(this)
});
}
$('document').ready(function () {
console.log('hello');
$('.tabs').tabs();
chatSettings();
});
I prepared a js.fiddle - As you can see from console.log when clicking into the textarea, the eventListener always listens to #cl1, even if .cl.active switches along with the according TAB.
The events in the textarea are just relevant, if .cl is active. My target is to wrap all three eventListener into one and apply the event to the textarea in the active stream, but all I tried went wrong... Can anyone help? #Dontrepeatyourself #DRY
$(".chatTextarea").on(
'focus change mousedown mouseout keyup mouseup',
function (this) {
//this.id can contain the unique id
greatFunction(this);
});
This will bind event individually with unique id found with this keyword and also wraps all event listener into one function but this is better when you want to process each event with same functionality
please let me know if this helps.
Peace
$(".cl textarea").on('focus change mousedown mouseout keyup mouseup', function () {
greatFunction(this)
});
Tada!
P.S. Is there a reason greatFunction is defined inside window.onload?
Try using $(document).ready function to load code when the page loads.
Also use $('textarea #cl1').on to get the textarea with the #cl1 or whichever id you want to use and then call the function after using the .on.
Hope this helps!
Let me know if it works!
$(document).ready(function () {
function greatFunction(elem) {
//do stuff
}
$('textarea').on('focus change mousedown mouseout keyup mouseup', function () {
greatFunction(this)
});
}
First off, I changed the onload to bind with jQuery, so all your logic is doing jQuery bindings, rather than swapping back and forth between jQuery and vanilla javascript. Also, doing an actual binding removes an inline binding.
Next, the binding has been condensed into a single delegate event listener. Since you eluded in your comments that it wasn't working for the active element after the active was moved or added, this reflected that you were dealing with dynamic elements. Delegate event listeners are one way to handle such things.
Delegate event listeners bind on a parent element of the elements that will change, or be created. It then waits for an event to happen on one of it's children. When it gets an event it is listening for, it then checks to see if the element that it originated from matches the child selector (second argument) for the listener. If it does match, it will then process the event for the child element.
Lastly, I added some buttons to swap around the active class, so you could see in the snippet that the event handler will start working for any element that you make active, regardless of it starting out that way.
$(window).on('load', function () {
function greatFunction (elem) {
console.log(elem.value);
}
$(document.body).on(
'focus change mousedown mouseout keyup mouseup',
'.cl.active .chatTextarea',
function () {
greatFunction(this);
}
);
$('.makeActive').on('click', function () {
$('.active').removeClass('active');
$(this).closest('div').addClass('active');
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="cl1" class="cl active"><textarea class="chatTextarea">aa</textarea><button class="makeActive">Make Active</button></div>
<div id="cl2" class="cl"><textarea class="chatTextarea">bb</textarea><button class="makeActive">Make Active</button></div>
<div id="cl3" class="cl"><textarea class="chatTextarea">cc</textarea><button class="makeActive">Make Active</button></div>

How to reattach event bindings after DOM is completely changed [duplicate]

This question already has answers here:
Event binding on dynamically created elements?
(23 answers)
Closed 4 years ago.
I am working on a C# Razor site and I am POSTing from a boostrap modal which then returns a new view and model. To reload the entire page with the response, I am using the following line within this code block.
$("html").html(response);
function addDevice(e) {
e.preventDefault();
var ID = $("#txtNewDeviceID").val();
var Name = $("#txtNewDeviceName").val();
$.post('#Url.Action("AddDevice", "Devices")', { 'DeviceID': ID, 'DeviceName': Name }, function (response) {
$('#newDeviceModal').modal('hide');
$("html").html(response);
AttachBindings();
});
}
Here is the code behind AttachBindings():
function AttachBindings() {
$(document).on('click', 'table tr', {}, tableClick);
$(document).on('keyup', '#search', {}, search);
$(document).on('click', '#btnAdd', {}, function (e) {
addDevice(e);
});
$(document).on('click', '#btnRemove', {}, function (e) {
removeDevice(e);
});
$(document).on('click', '#btnUpdate', {}, function (e) {
updateDevice(e);
});
}
Unfortunately AttachBindings() is never hit and I can't seem to find a way to reattach these events. The only event that seems to work is keyup which is attached to #search. Any ideas on how to fix this?
Try using live function and call it on document.ready
As per the details provided in the documentation for LIVE:
Description: Attach an event handler for all elements which match the
current selector, now and in the future.
Hence, even if an element is added later in the DOM, the event will be triggered by that element
Whereas, in the case of ON, it will be added to the present elements only:
Description: Attach an event handler function for one or more events
to the selected elements.
So, calling it once, after document is ready and bind the events using bind or live will do the magic for you.
I doubled checked this morning and the events are being hit the way I have it. The real issue is the bootstrap modal that I have on the page does not work if the DOM has been updated. It looks like I will need to reconnect the data-toggle and data-dismiss events. I'm guessing bootstrap does this in the background when the page first loads.
Also the .Live method is deprecated according to the JQuery documentation.
As of jQuery 1.7, the .live() method is deprecated. Use .on() to attach event >handlers. Users of older versions of jQuery should use .delegate() in preference >to .live().
Also AttachBindings does not need to be called more than once. My code updated looks like this now:
function addDevice(e) {
e.preventDefault();
var ID = $("#txtNewDeviceID").val();
var Name = $("#txtNewDeviceName").val();
$.post('#Url.Action("AddDevice", "Devices")', { 'DeviceID': ID, 'DeviceName': Name }, function (response) {
$('#newDeviceModal').modal('hide');
$("html").html(response);
});
}

Trouble removing one of several event listeners in a Backbone.js view

I'm trying to remove an event listener from a particular DOM item and having trouble. The following shows the parts of the code I'm playing with:
Search.Views.MainSearch = Backbone.View.extend({
// initialization function
events: {
'click #search-submit' : 'searchSubmit',
'click #some-button': 'disableSearch',
'click #some-other-button': 'someFunction'
}|,
disableSearch: function(){
// this statement works, but it removes all listeners
$(this.el).off('click');
// this statement doesn't work
// $('#search-submit').off('click', this.searchSubmit);
// This also doesn't work
// $(this.el).off('click', this.searchSubmit);
}
Clicking the #search-submit button works as expected. Clicking the #some-button item works insofar as it calls the disableSearch method. However, I can't remove just the click event from the #search-submit button. I can remove all listeners, but this doesn't suit my purpose because it removes the listener from #some-other-button as well.
Is there some way to remove just the event listeners I care about and leave the rest intact? Is there a better way to attach the event listeners in the first place? Thanks...
You can remove an event listener on a specific element by undelegating it:
this.$el.off('click', '#search-submit');
and a demo http://jsfiddle.net/nikoshr/fS278/
You cannot remove a specific function when wired by the hash of events (Backbone transforms the functions by forcing the this context). However, handling the search-submit events attachments yourself would let you unbind a function :
var V = Backbone.View.extend({
events: {
'click #some-button': 'disableSearch',
'click #some-other-button': 'someFunction'
},
initialize: function () {
// let's make sure searchSubmit has the view as context
_.bindAll(this, 'searchSubmit');
// and then direct the event to the bound function
this.$el.on('click', '#search-submit', this.searchSubmit);
},
disableSearch: function(){
// this.searchSubmit is the same function as in initialize
// it can be detached
this.$el.off('click', this.searchSubmit);
return false;
},
searchSubmit: function() {
console.log('searchSubmit');
return false;
}
});
And a demo http://jsfiddle.net/nikoshr/fS278/1/

Using .on to bind to dynamically generated content

I am using Infinite Scroll to display some content and I'm having trouble binding some mouseenter/mouseleave events to the newly generated items.
I know that I need to bind .on to a container already existing on the page, but I'm having trouble figuring out the syntax to alter the current jQuery that toggles.
This is the current js:
$(document).ready(function() {
$('.grid-box .actions').hide();
$('.grid-box').on({
mouseenter: function () {
$(this).find('.actions').show();
},
mouseleave: function () {
$(this).find('.actions').hide();
}
});
});
The main container is #grid-container and each individual item is .grid-box. How can I alter the above so that the actions show/hide upon entering/leaving .grid-box?
I think I need something along the lines of this:
$('#grid-container').on('mouseenter mouseleave', '.grid-box', function(e) {
// some action
});
Exactly, this is known as event delegation and it waits for the event to bubble up then matches the event based on the selector. This is much more efficient because there is only one handler registered rather than N times the number of elements. Also, you only have to bind once rather than every time the dynamic content is changed.
$('#grid-container').on('mouseenter', '.grid-box', function(e) {
// some action
}).on('mouseleave', '.grid-box', function(e) {
// some action
});
The selector as the second argument will still work:
$('#grid-container').on({ ...}, '.grid-box');
http://jsfiddle.net/QkFTz/1/
An alternate method would just be to bind them separately, which I personally think is clearer:
$("#grid-container").on('mouseenter', '.grid-box', function () {})
.on('mouseleave', '.grid-box', 'function () {});

Changing the onclick event delegate of an HTML button?

How do you change the JavaScript that will execute when a form button is clicked?
I've tried changing its onClicked and its onclicked child attributes like so:
$('mybutton').onClick = 'doSomething';
and
$('mybutton').attributes["onclick"] = 'doSomething()';
Neither seem to work. My other options are:
To have two buttons and hide one and show the other.
To have it directed to a function that evals a string and change the string to the function I want to execute.
Neither seem very elegant.
I'm using Prototype as a js library so it that has any useful tools I can use them.
If the original onclick event was set through HTML attributes, you can use the following to overwrite it:
$("#myButtonId").setAttribute("onclick", "myFunction();");
For Prototype, I believe that it would be something like this:
$("mybutton").observe('click', function() {
// do something here
});
EDIT: Or, as it says in the documentation, you could simply specify the function you want to call on click:
$('mybutton').observe('click', respondToClick);
function respondToClick(event) {
// do something here
}
But this is all, again, Prototype-specific.
Using the Prototype framework you can do:
Event.observe("mybutton", "click", clickHandler);
or:
Event.observe("mybutton", "click", function() {
alert("Button clicked!");
});
or:
$("mybutton").observe("click", clickHandler);
or:
$("mybutton").observe("click", function() {
alert("Button clicked!");
});
See the Event class documentation
The general way to set an onclick handler in javascript is to set onclick to a function, by passing it the name of a function directly, not in a string. So if myButton is set to a DOM Element, you would write:
myButton.onclick = doSomething;
So when you click the 'mybutton' button, the doSomething function will be called as doSomething(). For anonymous functions, you can write:
myButton.onclick = function() {
alert("myButton was clicked!");
};
In JQuery it's
$("#myButtonId").click(myFunction);
function myFunction(){
alert("Clicked");
}
Or if you want to put the function inline:
$("#myButtonId").click(function(){
alert("Clicked");
});
If you are using JQuery firstly make sure you use the relevant selector prefix (IE: If your using the Id of the element put a # in front of it). Secondly it's the click method to assign a callback to the click event.
Last I used Prototype, it was something like this:
Event.observe('mybutton', 'click', doSomething);
By the way, your examples might've even worked if you didn't quote the function names.
EDIT: Yes, Element.observe(element, eventName, handler) and someElement.observe(eventName, handler) also work. And don't quote the handler name - you want to pass the function not a string!
I found a solution for your issue with prototype under firefox:
$("#myButtonId").writeAttribute('onclick', ''); // first remove the attribute
$("#myButtonId").observe('click', function () { ... }); // then add the event

Categories