How to implement event order in parent/child elements using jquery - event-handling

I have a parent & child elements in my web page where both have separate event handlers defined for them. I have tested in firefox, where the event handler for parent element gets executed first. I want it to execute the other way - child element's event handler getting executed first.
I have read about making use of bind, stopPropagation, preventDefault etc. make that happen but I am a bit confused as a to how to get this working? Can somebody shed some light on this topic?
Here is how I am implementing the event handling....
$('#Sidebar ul li .DeleteList').live('click', function(e) {
alert("I was deleted");
});
$('#Sidebar ul').delegate('li', 'click', function(e) {
alert("I was selected");
});

Why not be consistent and just use .live() for both? :D
$('#Sidebar ul li .DeleteList').live('click', function(e) {
alert("I was deleted");
});
$('#Sidebar ul li').live('click', function(e) {
alert("I was selected");
});
This way 'deleted' gets triggered first.
fiddle: http://jsfiddle.net/c9sBD/

Related

Does there exist a function to tell if the current tileLayer is **already** loaded?

I want to programmatically flyTo to a new location, then once arriving at the new location and the tiles in the new location are loaded, I would like to do the next step.
Using the load event does not work if the new location is very close to the origin, since they are already loaded, so no new load event would be fired. Nevertheless, the code still needs to handle whether the visible tiles are loaded, since it is possible that the new location is quite far from the origin.
How do I tell if the current tile layer is already loaded? Something like map.whenReady, which is fired immediately if the map is already ready, unlike tileLayer.on("load"), which won't be fired if the tile layer is already loaded.
Sample code that doesn't work:
map.on("moveend zoomend", function(){
baseLayer.on("load", function(){
// do something
});
});
map.flyTo(latlng);
If the tile layer is already loaded, the "load" event won't fire, and do something won't be executed.
What I would like to have:
map.on("moveend zoomend", function(){
baseLayer.whenLoaded(function(){
// do something
});
});
map.flyTo(latlng);
How to achieve this in Leaflet?
As already answered on github, it's easy to implement whenReady in your own code using Leaflet extension methods, like this:
L.GridLayer.include({
whenReady: function (callback, context) {
if (!this._loading) {
callback.call(context || this, {target: this});
} else {
this.on('load', callback, context);
}
return this;
},
});
This is not the most elegant solution, but I think you could solve your problem using TileLayer isLoading() method that returns true if any tile in the grid layer has not finished loading.
map.on("moveend zoomend", function(){
baseLayer.on("load", function(){
doSomething("load: YES");
});
if (!baseLayer.isLoading()) doSomething("load: NO");
});
function doSomething(info) {
alert("do something - " + info);
}

Unbinding event handler that was bound with .on()

I'm trying to unbind some event handlers that were bound with .on(), but nothing seems to work:
HTML:
<div class='parent'>
<a id='test-1' class='test'>Test 1</a>
<a id='test-2' class='test'>Test 2</a>
<a id='test-3' class='test'>Test 3</a>
</div>
<hr>
<a class='unbind'>Unbind Test 1</a>
JS:
$('.parent').on('click', 'a.test', function(e){
alert('click');
});
$('a.unbind').click(function(e){
$('a#test-1').unbind('click');
$('a#test-1').off('click');
});
Fiddle:
http://jsfiddle.net/g9ucd/5/
CLARIFICATION: Per the example, I want to know if it's possible to unbind specific elements that had previously been bound with .on(), rather than just reversing the all the bindings with .off().
The jQuery API on method documentation states that the event is actually placed on the elemented it is called from, the filter selector you apply is used to then determine which elements the event is valid on. So the short answer is NO you can't unbind the even from an element that is designated by the filter selector.
However, have you considered working around this by constraining your selector further?
You could remove the test class from the a element and then by that scenario it would no longer meet the filter selector's criteria and thus be untied to the event.
Just a thought.
Here's my fiddle and below is the snipped that i changed.
$('a.unbind').click(function(e){
$('a#test-1').removeClass('test');
});
How about this:
var clickie = function(e){
alert('click');
}
$('.parent').on('click', 'a.test', clickie);
$('a.unbind').click(function(e){
$('.parent').off('click', 'a.test', clickie);
});
Thing is, you never set a handler on a#test-1 - you set it on .parent. You can't remove what you didn't set. If you need to remove a handler from a#test-1, you must not use the live functionality: $('.parent a.test').on('click', clickie) will bind your function onto the elements themselves, so you can off them individually.
You can try this
$('a.test').on('click', function(e){
alert('click');
});
$('a.unbind').click(function(e){
$('a#test-1').unbind('click');
$('a#test-1').off('click');
});
You need to add more logic to it:
$('.parent').on('click', 'a.test', function(e){
// retrieve switch value:
var disable_calls = $(this).data('disable-calls') || false;
if (!disable_calls){
// your logic here...
alert('click!');
};
});
$('a.unbind').click(function(e){
// disable call by turning the switch:
$('a#test-1').data('disable-calls', true);
});
Event handlers are attached to the outer element (.parent) and jQuery gives you a shortcut for actually checking event.target (you do that by supplying selector within .on() call). To alter that logic, you need to add your own special handling, or eg. make sure that selector is no longer matched:
$('.parent').on('click', 'a.test.calls-enabled', function(e){
alert('click!');
});
$('a.unbind').click(function(e){
// disable call by turning the switch:
$('a#test-1').removeClass('calls-enabled');
});
A variation on #ermagana's and #Tadeck's answers is simply to use the :not selector on the initial delegated binding. That way, you can "unbind" certain elements by explicitly disabling them with an extra class.
(In this case, I'll use the .disable class, which would give extra benefit of disabling them visually if you were using Bootstrap etc..)
$('.parent').on('click', 'a.test:not(.disabled)', function(e){
alert('click');
});
$('a.unbind').click(function(e){
$('a#test-1').addClass('disabled');
});

Does anyone know if there's an equivalent of TinyMCE 3.5.8 Editor.onEvent(...) is in TinyMCE 4.0b1

The plugin I'm trying to port(https://github.com/NYTimes/ice) to TinyMCE 4 needs to have access to a keypress event BEFORE it is handled by the MCE editor and works with onEvent(...) in 3.5.8 but needs to be migrated to something more like on('event') in tinymce 4, however there isn't an obvious alternative.
In tiny MCE 3.5.8 I have
ed.onEvent.add(function(ed, e) {
return changeEditor.handleEvent(e);
});
But I need something more like
ed.on('event', function(e) {
return changeEditor.handleEvent(e);
});
However the ed.on('event',...) doesn't seem to exist in tinymce 4.
It needs to be able to catch the delete key before any other event handler for the keydown, keyup, and keypress.
Ok, After 2 workdays trying to get this to work I figured out what the problem was with this particular issue.
For starters there is no equivalent to onEvent(...) in tinymce 4. However the plugin doesn't need access to every event anyway.
If you are going to port any tinymce plugin that uses the onEvent() method then you will need to identify the events the plugin is trying to handle and explicitly set the event handler for each of the events that need to be handled:
ed.on('mousedown', function(e) {
return changeEditor.handleEvent(e);
});
ed.on('keyup', function(e) {
return changeEditor.handleEvent(e);
});
ed.on('keydown', function(e) {
return changeEditor.handleEvent(e);
});
ed.on('keypress', function(e) {
return changeEditor.handleEvent(e);
});
In my case I needed to not only delegate the mousedown,mouseup, keyup,keydown, and keypress events to the plugin I also had to prevent them from being fired prematurely by the editor/textarea:
ed.on('keydown', function(e) {
// prevent the delete key and backspace keys from firing twice
if(e.keyCode == 46 || e.keyCode==8)
e.preventDefault();
});
So keep this in mind if you run into a similar issue.
Oh and I added a fork of this ICE plugin on my github: https://github.com/catsgotmytongue/ice/

can't remove specific event handlers when attached to document with .on()

Here's a simple fiddle to demo my situation...
http://jsfiddle.net/UnsungHero97/EM6mR/17/
What I'm doing is adding an event handler for current & future elements, using .on(). I want to be able to remove these event handlers for specific elements when something happens; in the case of the fiddle, when the radio button is selected, the event handler for the blue elements should be removed and clicking those elements should not do anything anymore.
It doesn't seem to be working :(
How do I remove the event handler attached to document that I created with .on() for those specific blue elements?
The signature for your .on() and .off() has to match.
These two do not match so the .off() call won't find matching event handlers to remove:
$(document).on('click', '.btn', function() {
update();
});
$(document).off('click', '.blue');
Note, the selector passed to .on() and .off() is different.
When using the dynamic form of .on() (where you pass a selector as an argument to .on()), you can't remove just part of the items. That's because there's only one event handler installed on the root element and jQuery can only remove the entire thing or not at all. So, you can't just .off() some of the dynamic items.
Your options are to remove all the event handlers with:
$(document).off('click', '.btn');
and, then install a new event handler that excludes the items you don't want such as:
$(document).off('click', '.btn:not(.blue)');
Or, teach the event handler itself how to ignore .blue items:
$(document).on('click', '.btn', function() {
if (!$(this).hasClass('blue')) {
update();
}
});
Be careful of how you attach your events; this works fine for me:
$('.btn').on('click', function() {
update();
});
$('#disable').on('change', function() {
$('.btn').off('click');
});
Only way seems to be:
$('#disable').on('change', function() {
$(document)
.off('click', '.btn')
.on('click', '.btn:not(.blue)', update);
});

jquery selection with .not()

I have some troubles with jQuery.
I have a set of Divs with .square classes. Only one of them is supposed to have an .active class. This .active class may be activated/de-activated onClick.
Here is my code :
jQuery().ready(function() {
$(".square").not(".active").click(function() {
//initialize
$('.square').removeClass('active');
//activation
$(this).addClass('active');
// some action here...
});
$('.square.active').click(function() {
$(this).removeClass('active');
});
});
My problem is that the first function si called, even if I click on an active .square, as if the selector was not working. In fact, this seems to be due to the addClass('active') line...
Would you have an idea how to fix this ?
Thanks
Just to give something different from the other answers. Lonesomeday is correct in saying the function is bound to whatever they are at the start. This doesn't change.
The following code uses the live method of jQuery to keep on top of things. Live will always handle whatever the selector is referencing so it continually updates if you change your class. You can also dynamically add new divs with the square class and they will automatically have the handler too.
$(".square:not(.active)").live('click', function() {
$('.square').removeClass('active');
$(this).addClass('active');
});
$('.square.active').live('click', function() {
$(this).removeClass('active');
});
Example working: http://jsfiddle.net/jonathon/mxY3Y/
Note: I'm not saying this is how I would do it (depends exactly on your requirement) but it is just another way to look at things.
This is because the function is bound to elements that don't have the active class when you create them. You should bind to all .square elements and take differing actions depending on whether the element has the class active:
$(document).ready(function(){
$('.square').click(function(){
var clicked = $(this);
if (clicked.hasClass('active')) {
clicked.removeClass('active');
} else {
$('.square').removeClass('active');
clicked.addClass('active');
}
});
});