How do I bind this event handler in a jQuery plugin? - javascript

I am learning JavaScript and jQuery, so I apologize in advance if this is a stupid question.
(function ($) {
$.fn.addDiv = function () {
this.append('<div></div>');
return this;
};
}(jQuery));
$('div').click(function () {
$('body').addDiv();
});
In the code above, I have written a simple jQuery plugin which appends a <div> element to the current value of this.
Now, after I click on an element and add some <div> elements, I realize that the click handler in not associated with the newly appended elements. This seems perfectly acceptable since naturally when the handler function was bound, it was to only the ones that existed and hence were present in the $('div') object.
Now, I know that binding the handler to all <div> elements within the plugin is as simple as follows.
(function ($) {
$.fn.addDiv = function () {
this.append('<div></div>');
$('div').click(function () {
$('body').addDiv();
}); // added this statement
return this;
};
}(jQuery));
But I'm sure that there is a better way to handle such event binding. This clearly duplicates code. So what is the best practice with regards to such event binding to elements that the plugin itself adds?
This is meant to be an educational example, please do not consider it to be any kind of use case and give me alternative solutions.
http://jsbin.com/jokururiyaca/1/edit
Edit: I know that some people recommend the use of .selector but it is deprecated in jQuery 2. Also, using it doesn't change the fact that the event handling code is still duplicated within the plugin.

This is one way you could solve your problem of re-binding the click handler to all elements.
You can create the DOM element using $('<div></div>'), add the click handler to only that element, using $('<div></div>').click(...), and then append it to the body with $('<div></div>').click(...).appendTo("body").
Example:
(function ($) {
$.fn.addDiv = function () {
$('<div></div>').click(function () {
$('body').addDiv();
}).appendTo("body");
return this;
};
}(jQuery));
Usually Event Delegation would be used to handle events on future elements.
Example:
(function ($) {
$.fn.addDiv = function () {
this.append('<div></div>');
return this;
};
}(jQuery));
$(document).on("click", "div", function(){
$('body').addDiv();
});

Related

Firing a function with a newly added class

I've tried to simplify it, simple enough to make my question clearer.
The alert 'I am a boy' didn't popup with even after the addClass has been executed.
Here is my code:
$(".first").click(function () {
var a = $(this).html();
if (a=='On') {
$(this).removeClass('first').unbind().addClass('second');
$(this).html('Off');
}
});
$(".second").click(function () {
alert('I am a boy');
});
<button class="first">On</button>
This behavior is because you are apply a class to an element after the DOM has loaded, in other words dynamically. Because of this, your event listener attached to the control for '.second' isn't aware of the newly added class and doesn't fire when you click on that control.
To fix this, you simply need to apply your event listener to a parent DOM object, typically $(document) or $('body'), this will ensure it is aware of any children with dynamically added classes.
As George Bailey said, you can refer here for a in depth explanation.
In regards to your specific code, the fix is to simply adjust it as so:
$(".first").click(function () {
var a = $(this).html();
if (a=='On') {
$(this).removeClass('first').unbind().addClass('second');
$(this).html('Off');
}
});
/* Changed this:
$(".second").click(function () {
alert('I am a boy');
});
*/
// To this:
$(document).on('click', '.second', function () {
console.log('I am a boy');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="first">On</button>
The function you pass to $.post doesn’t run until later (a callback). So the class is added after you try to select it. Do it inside the callback, the same way you added the class (and you don’t need to select that class, just use $this)

javascript events not working with dynamic content added with json

I'm stuck with a situation where my DOM elements are generated dynamically based on $.getJSON and Javascript functions for this elements are not working. I'll post some general idea on my code because I'm looking just an direction of what should I do in this situation.
site.js contains general features like
$(document).ready(function() {
$('.element').on('click', function() {
$(this).toggleClass('active');
});
$(".slider").slider({
// some slider UI code...
});
});
After that:
$.getJSON('json/questions.json', function (data) {
// generating some DOM elements...
});
I have also tried to wrap all site.js content into function refresh_scripts() and call it after $.getJSON() but nothing seems to be working.
Firstly you need to use a delegated event handler to catch events on dynamically appended elements. Then you can call the .slider() method again within the success handler function to instantiate the plugin on the newly appended content. Try this:
$(document).ready(function(){
$('#parentElement').on('click', '.element', function() {
$(this).toggleClass('active');
});
var sliderOptions = { /* slider options here */ };
$(".slider").slider(sliderOptions);
$.getJSON('json/questions.json', function(data) {
// generating some DOM elements...
$('#parentElement .slider').slider(sliderOptions);
});
});
Instead of calling on directly on the element, call it on a parent that isn't dynamically added and then use the optional selector parameter to narrow it to the element.
$('.parent').on('click', '.element', () => {
// do something
});
The difference between this and:
$('.element').on('click', () => {});
is with $('.element').on(), you're only applying the listener to the elements that are currently in that set. If it's added after, it won't be there.
Applying it to $(.parent), that parent is always there, and will then filter it to all of it's children, regardless when they're added.
the easiest way is to add this after you add/generate your DOM
$('script[src="site.js"]').remove();
$('head').append('<script src="site.js"></script>');
of course your js function that generates DOM needs to be on another file than your site.js

Javascript, Jquery append functions to an event

I have a function that is associated with an event, say onfocus() and in some cases, I want to be able to execute the default function as well as one or more additional functions.
So I don't want to replace the original function, but I want to append another so that both functions will fire.
<div id="mydiv" onfocus="alert('hello');">
if(something == somethingelse) $('#mydiv').onFocus += "alert('world');"
So in this example, sometimes just Hello will Fire and sometimes Hello and then World will both fire.
I'm just using onfocus() and alert() as an example, these would actually be functions that I have defined.
How do I go about doing this ?
Use jQuery to add a focus event handler
<script>
$('#mydiv').on('focus', function(){
//do soemthing
})
</script>
If you work with jQuery don't use inline event bindings, use the following instead:
$("#mydiv").on("focus", function() {
alert("hello");
});
// add one more action for the same event
$("#mydiv").on("focus", function() {
alert("world");
});
You should do
$('#myDiv').on('focus', function(){alert('world')});
$('#mydiv').focus( function(){
})//This is for the elements which load while the page is loading
or
$('#mydiv').on('focus', function(){
}) //This is for the elements which will load dynamically after the page load completed.
If you don't want to use jQuery try this, its an pure javascript equivalent:
document.getElementById("mydiv").addEventListener("focus", function() { alert('world'); });
and if you want it to be compatible with IE8 and older you should try
var el = document.getElementById("mydiv");
if(el.addEventListener)
el.addEventListener("focus", function() { alert('world'); });
else
el.attachEvent("focus", function() { alert('world'); });
if you're using jQuery, you want to use on() to bind event handlers to elements as opposed to specifying them inline
$('#mydiv').on('focus', function () {
alert('hello');
});
$('#mydiv').on('focus', function () {
if (something === somethingelse) {
alert('world');
}
});
or combining into one handler function seems reasonable in this case
$('#mydiv').on('focus', function () {
alert('hello');
if (something === somethingelse) {
alert('world');
}
});
When specifying them inline as you have done, only one event handler can be bound to the event so if you want to bind multiple event handlers, you either need to bend the one event handler limitation to handle this or use another approach, such as DOM Level 2 events or an abstraction on top of it (such as jQuery's on() function).
Event handlers need to be bound when the element to which you are binding the handlers exists in the DOM. To do this, you can use jQuery's ready() function
// bind an event handler to the "ready" event on the document
$(document).ready(function () {
// ..... here
});
or shorthand
$(function () {
// ..... here
});

Javascript error when I try to add click event to span

Does anyone know what I'm doing wrong here?
var spanclickhandler = $('.officeapprovalspan').click(function () {
// do some stuff
});
Basically, I have this spanclickhandler. Anything with the class officeapprovalspan on loading the page gets this assigned to its click event. No problems there.
In another place I have the code:
$(this).replaceWith('<span class="officeapprovalspan">wero<span>');
$(document).on('click', '.officeapprovalspan', spanclickhandler);
So I'm replacing some HTML with a new span of this class. I use the on to add the click event to the spans of class officeapprovalspan. I gather I have to do this because the new span will not have the click handler attached to it.
So that's OK, but when I click the new span I get this error:
Anyone know what I'm doing wrong and how to fix?
The click method doesn't return the event handler, it returns the jQuery object. Define the event handler first, and use it in the click method:
var spanclickhandler = function () {
...
do some stuff
...
};
$('.officeapprovalspan').click(spanclickhandler);
This chunk of code doesn't do what you think it does (I'm pretty sure that handler function isn't returned):
var spanclickhandler = $('.officeapprovalspan').click(function () {
...
do some stuff
...
});
I suggest you code it like this, which should work:
var spanclickhandler = function () {
...
do some stuff
...
}
$('.officeapprovalspan').click(spanclickhandler);
What you want to do is:
var spanclickhandler = function () {
...
do some stuff
...
};
$(this).replaceWith('<span class="officeapprovalspan">wero<span>');
$('.officeapprovalspan').on('click', spanclickhandler);
OR BETTER
$(document).on('click', '.officeapprovalspan' function () {
...
do some stuff
...
});
// Because 'on' will attach event on DOM element even if they are not created yet, if you define a selector descendant inside. You can replace `document` by the nearest top parent of all your `.officeapprovalspan` and it will improve performance. `document` is not really optimal here
$(this).replaceWith('<span class="officeapprovalspan">wero<span>');
spanclickhandler returns a jQuery object, $('.officeapprovalspan') in this case. You need to either create a separate function or use event namescapes.
Can you just do this:
$(this).replaceWith('<span class="officeapprovalspan">wero<span>').click(spanclickhandler);

Enable/Disable events of DOM elements with JS / jQuery

I stuck here with a little problem I have put pretty much time in which is pretty bad compared to its functionality.
I have tags in my DOM, and I have been binding several events to them with jQuery..
var a = $('<a>').click(data, function() { ... })
Sometimes I would like to disable some of these elements, which means I add a CSS-Class 'disabled' to it and I'd like to remove all events, so no events are triggered at all anymore. I have created a class here called "Button" to solve that
var button = new Button(a)
button.disable()
I can remove all events from a jQuery object with $.unbind. But I would also like to have the opposite feature
button.enable()
which binds all events with all handlers back to the element
OR
maybe there is a feature in jQuery that actually nows how to do that?!
My Button Class looks something similar to this:
Button = function(obj) {
this.element = obj
this.events = null
this.enable = function() {
this.element.removeClass('disabled')
obj.data('events', this.events)
return this
}
this.disable = function() {
this.element.addClass('disabled')
this.events = obj.data('events')
return this
}
}
Any ideas? Especially this rebind functionality must be available after disable -> enable
var a = $('<a>').click(data, function() { ... })
I found these sources that did not work for me:
http://jquery-howto.blogspot.com/2008/12/how-to-disableenable-element-with.html
http://forum.jquery.com/topic/jquery-temporarily-disabling-events
-> I am not setting the events within the button class
Appreciate your help.
$("a").click(function(event) {
event.preventDefault();
event.stopPropagation();
return false;
});
Returning false is very important.
Or you could write your own enable and disable functions that do something like:
function enable(element, event, eventHandler) {
if(element.data()[event].eventHandler && !eventHandler) { //this is pseudo code to check for null and undefined, you should also perform type checking
element.bind(event, element.data()[event]);
}
else (!element.data()[event] && eventHandler) {
element.bind(event, element.data()[event]);
element.data({event: eventHandler}); //We save the event handler for future enable() calls
}
}
function disable(element, event) {
element.unbind().die();
}
This isn't perfect code, but I'm sure you get the basic idea. Restore the old event handler from the element DOM data when calling enable. The downside is that you will have to use enable() to add any event listener that may need to be disable() d. Otherwise the event handler won't get saved in the DOM data and can't be restored with enable() again. Currently, there's no foolproof way to get a list of all event listeners on an element; this would make the job much easier.
I would go on this with different approach:
<a id="link1">Test function</a>
<a id="link2">Disable/enable function</a>
<script type="text/javascript">
$(function() {
// this needs to be placed before function you want to control with disabled flag
$("#link1").click(function(event) {
console.log("Fired event 1");
if ($(this).hasClass('disabled')) {
event.stopImmediatePropagation();
}
});
$("#link1").click(function() {
console.log("Fired event 2");
});
$("#link2").click(function() {
$("#link1").toggleClass("disabled");
});
});
</script>
This may not be what you require, since it may effect also other functions binded into this event later. The alternative may be to modify the functions itself to be more like:
$("#link1").click(function(event) {
console.log("Fired event 1");
if ($(this).hasClass('disabled')) {
return;
}
// do something here
});
if that is an option.
Instead of adding event handler to each element separately, you should use event delegation. It would make much more manageable structure.
http://www.sitepoint.com/javascript-event-delegation-is-easier-than-you-think/
http://cherny.com/webdev/70/javascript-event-delegation-and-event-hanlders
http://brandonaaron.net/blog/2010/03/4/event-delegation-with-jquery
This why you can just check for class(es) on clicked element , and act accordingly. And you will be able even to re-eanble them , jsut by changing the classes of a tag.
P.S. read the links carefully, so that you can explain it to others later. Event delegation is a very important technique.
You could use an <input type="button"> and then use $("#buttonID").addAttr('disabled', 'disabled'); and $("#buttonID").removeAttr('disabled');. Disabling and enabling will be handled by the browser. You can still restyle it to look like an anchor, if you need that, by removing backgrounds and borders for the button. Be aware though, that some margins and padding might still bugger u in some browsers.

Categories

Resources