Breaking javascript functions - javascript

I have a click event
$('#ship_Move').click(function (event) {
event.stopPropagation();
$('.shipActionsList').remove();
moveShip(shpID);
});
that calls a function moveShip
function moveShip(shp_id) {
$('.fieldDivs').click(function () {
$("#hello").append("Move "+ shp_id + "to " + $(this).attr('id')+"<br/>");
});
}
And the debugging is giving the right results - but with one problem. If I first click the #ship_Move element 4 times, and then select a destination (.fieldDivs) I get this:
Move 162 to FD455
Move 162 to FD455
Move 162 to FD455
Move 162 to FD455
Because the moveShip function has been called 4 times, and each of them are waiting for a .fieldDivs element to be clicked, and when it is clicked, all 4 of them append their results (162 is the ship ID I want to move, FD455 is its destination).
What I need is something among these lines:
function moveShip(shp_id) {
$('.fieldDivs').click(function () {
$("#hello").append("Move "+ shp_id + "to " + $(this).attr('id')+"<br/>");
// IF A NEW #SHIP_MOVE IS CLICKED, END THIS FUNCTION AS IT WILL BE CALLED AGAIN
});
}
I know I can return false to break functions, but for a click event I need a new function, so return false would just end the click event inside that event. What I mean is - if you exchange the above commented line //IF A NEW SHIP with the following code:
$('.ship_Move').click(function () {
// return false would break this function
});
This code will only end this click event, but not the moveShip function.
I hope I made myself clear enough...
Thanks!

You could unbind existing click event handler beforehand:
$('.fieldDivs').off('click').click(function() {
// ...
});
If there are other click event handlers that should be kept, I suggest to use namespaced events:
$('.fieldDivs').off('click.move').on('click.move', funciton() {
// ...
});
Though, after thinking about it, instead of binding event handlers over and over again, you could just bind them once and share necessary data via variables:
$(function() {
var shpID = ...; // however this is set
$('#ship_Move').click(function (event) {
event.stopPropagation();
$('.shipActionsList').remove();
// set shpID here somehow?
});
$('.fieldDivs').click(function () {
$("#hello").append("Move "+ shpID + "to " + $(this).attr('id')+"<br/>");
});
});
It doesn't look like you have to bind a new event handler only because shpID changes. Of course this is a simplified example since I don't know your code, but maybe it helps.

Related

Incorrect selector is called on click event

There is the following JS:
for (var catId in this.data.categories) {
(function (catId) {
$("li#tsCat-" + catId).click(function (event) {
$(this).addClass("tax-active");
event.stopPropagation();
})
}(catId))
for (var floor in this.data.floors) {
(function (floor, catId) {
var selector = "#tsCatFloor_" + catId + "_" + floor;
$(selector).on("click", function (event) {
$(this).addClass("active-filt");
event.stopPropagation();
});
}(floor, catId));
}
}
This code forms two level list.
On clicking links on 1st level, it executes correct click handler $("li#tsCat-" + catId).
But on clicking 2nd level it again executes 1st handler $("li#tsCat-" + catId).
It needs to execute 2nd handler $(selector).
Where is the problem?
Sorry, I didn't provided the full view, but what is good I found a solution what could be useful for everyone:
If You're adding .click() event be sure, that You put the data (against which .click is called) already to DOM. Otherwise .click will never be called.

jquery .off(): how to remove a certain click handler only?

I've an element with two handler bound to it:
<button class="pippo pluto">
push me
</button>
$('.pippo').on('click', function () {
alert("pippo");
});
$('.pluto').on('click', function () {
alert("pluto");
});
I'm trying to .off() only one of them, but the syntax eludes me :-( I'm trying with something among the line of..
<button class="dai">
remove
</button>
$('.dai').on('click', function () {
$('.pippo').off('click');
alert("ok, removed");
});
but this removes both the handler. So I'm trying with...
$('.pippo').off('click .pippo');
but then nothing gets removed.
So I removed the middle space:
$('.pippo').off('click .pippo');
but back to square 1: both handler gets removed.
The right syntax would then be... ?
https://jsfiddle.net/6hm00xxv/
The .off(); method allows you to target multiple selectors as well as a specific event.
$('.pippo').off() would remove all events for the .pippo selector.
$('.pippo').off('click') would remove all click events for the .pippo selector.
$('.pippo').off('click', handler) would remove all click events with that handler for the .pippo selector.
In your case the handler used to add the event listener was an anonymous function so the handlercannot be used in the off() method to turn off that event.
That leaves you with three options, either use a variable, use a namespace or both.
Its quite simple to figure out which one to use.
if( "The same handler is needed more than once" ){
// you should assign it to a variable,
} else {
// use an anonymous function.
}
if ( "I intent to turn off the event" && ( "The handler is an anonymous function" || "I want to turn off multiple listeners for this selector at once" ) ){
// use a namespace
}
In your case
your handler is only used once so your handler should be an anonymous function.
you wish to turn off the event and your handler is anonymous so use a namespace.
So it would look like this
$('.pippo').on('click.group1', function () {
alert("pippo");
});
$('.dai').on('click', function () {
$('.pippo').off('click.group1');
alert("ok, removed");
});
It would work just as well to assign you handler to a variable if you prefer.
This allows you to specify which selector, eventType and handler to remove.
var pippo_click = function (e) {
alert("pippo");
});
$('.dai').on('click', function () {
$('.pippo').off('click', pippo_click);
alert("ok, removed");
});
But as a rule you shouldn't create variables if they're not needed.
One easier alternative with jQuery is to define a namespace for your click events:
$('.pippo').on('click.first', ...);
$('.pluto').on('click.second', ...);
// Remove only the pippo listener
$('.pippo').off('click.first');
Note that your classes pippo and pluto refer to the same element so using one or the other will not change anything.
https://jsfiddle.net/6hm00xxv/2/
Ok, solved. I just had to bind the handler to document:
function showMsg(text) {
alert("showMsg called with text: " + text);
};
$(document).on('click', '.pippo', function () {
showMsg("pippo");
});
$(document).on('click', '.pluto', function () {
showMsg("pluto");
});
$('.dai').on('click', function () {
$(document).off('click', '.pippo');
alert("ok, removed");
});
https://jsfiddle.net/6hm00xxv/1/
Because you are calling .off for click event. It is removing all possible click events on that selected element. The trick is to define a handler and remove that particular handler only.
function showPluto() {
showMsg("pluto");
};
function showPippo() {
showMsg("pippo");
};
function showMsg(text) {
alert("showMsg called with text: " + text);
};
$('.pippo').on('click', showPippo);
$('.pluto').on('click', showPluto);
$('.dai').on('click', function() {
$('.pippo').off('click', showPippo);
alert("ok, removed");
});

Passing eventobejct in jquery

I want to pass eventObject with trigger function, means when i manually trigger any event say:
$(".test").bind({ click : Testing });
$('.test').trigger("click");
function Testing(e)
{
}
When the function testing is called by mouseclick , the parameter e contains the eventobject, so i want this same thing when we trigger it manually.Can we pass eventobject when we trigger any event manually, Is this possible?
As gdoron points out (+1), jQuery will supply the event object for you. But you can create it explicitly if you like, to fill it in with information that jQuery can't fill in for you. You can create an Event object and pass it into trigger.
Here's an example of both using the default event object and creating your own: Live copy | source
jQuery(function($) {
$(".test").click(function(e) {
display("Received click on target");
display("typeof e = " + typeof e);
if (e) {
display("e.type = " + e.type);
if (e.type === "click") {
display("Coords: (" + e.pageX + "," + e.pageY + ")");
}
}
});
//Create a new jQuery.Event object without the "new" operator.
var e = $.Event("click");
// Fill in more info
e.pageX = 42;
e.pageY = 27;
// Trigger an artificial click event
display("Issuing a click via <code>$('.test').trigger('click')</code>");
$('.test').trigger("click");
// Trigger an artificial click event
display("Issuing a click creating our own event object with more info on it.");
$('.test').trigger(e);
function display(msg) {
$("<p>").html(msg).appendTo(document.body);
}
});
You don't need to do anything, the Event object is there already when you trigger an event as well....
The event object is always passed as the first parameter to an event handler
...
...
trigger docs
Live DEMO

How to use jQuery's on(..) to dynamically bind to multiple events

I am trying to create leverage jQuery's .on() (ex-live()) to bind multiple events. It is working for elements which exist on document.ready, but if I dynamically add a second link after page load, my event handler isn't triggered.
This makes sense since the outer-most method iterates over the elements, and doesn't listen for newly added DOM nodes, etc. The .on(..) is what listens for new DOM nodes, but requires an event name params, which I don't have until I have the DOM node.
Seems like a chick and the egg sort of situation.
Thoughts?
Test 1
Test 2
$(function() {
$('.js-test').each(function() {
var $this = $(this);
var e, events = $this.data('test-events');
for(e in events) {
$this.on(events[e], function() {
console.log("hello world!")
});
}
});
});
Update, The following does seem work either; $(this) doesn't appear to be in the right scope.
Test 1
Test 2
$(function() {
$('.js-test').on($(this).data('test-events'), function() {
// call third party analytics with data pulled of 'this'
});
});
Update 1:
I think my best bet will be to create special .on methods for all the methods I want to support like so:
$(document).on('click', '.js-test[data-test-events~="click"]' function(event) {
record(this, event);
});
$(document).on('mouseover', '.js-test[data-test-events~="mouseover"]', function(event) {
record(this, event);
});
... etc ...
$('a.js-test').on('click mouseover', function(event) {
// you can get event name like following
var eventName = event.type; // return mouseover/ click
console.log(eventName);
// you code
console.log('Hello, World!');
});
Sample example
If you want something like live event then:
$('body').on('click mouseover', 'a.js-test', function(event) {
// you can get event name like following
var eventName = event.type; // return mouseover/ click
console.log(eventName);
// you code
console.log('Hello, World!');
});
According to your last edit try this:
$('.js-test').on($('.js-test').data('test-events'), function() {
console.log("hello world!")
});
Sample example for edit
and for live event delegation
$('body').on($('.js-test').data('test-events'), '.js-test', function() {
console.log("hello world!")
});
Afraid you can't do this because you need to provide jQuery with either DOM elements or event names.
You can bind events to new DOM elements manually or bind all possible events that can be in data-test-events (if you have 3-5 of them, with all DOM events it will become a silly and slow solution) and check if your element has one of them:
$('body').on("mouseover click mouseout mouseenter mouseleave", '.js-test', function(e) {
if (!$.inArray(e.type, $(this).data('test-events').split(' '))) {
return;
}
console.log("hello world!");
});​
If you want to trigger an event whenever a matching element is added to the DOM, you might want to have a look at livequery - http://docs.jquery.com/Plugins/livequery.
This code will allow you to register multiple event handlers as a function array. It's tested and working. See this jsfiddle demo and test cases.
JavaScript:
$(document).ready(function() {
eventFnArray = [];
eventFnArray["click"] = function(e) {
if(e.type != "click") return ;
alert("click event fired - do xyz here");
// do xyz
};
eventFnArray["mouseover"] = function(e) {
if(e.type != "mouseover") return ;
alert("mouseover fired - do abc here");
// do abc
};
eventFnArray["mouseout"] = function(e) {
if(e.type != "mouseout") return ;
alert("mouseout fired - do JKL here");
// do JKL
};
$('.js-test').each( (function(fn) {
return function(i) {
if(i != 0) return;
var _that = this;
var events = [];
events = $(_that).attr("data-events").split(" ");
// alert($(_that).attr("data-events") + " : " + events.join(" "));
$(this).parent().on(
events.join(" "),
'.js-test',
function() {
console.info("hello - this is the " + event.type + " event");
// alert("data-events = " + $(this).attr("data-events") + " : event.type = " + event.type);
// delegate to the correct event handler based on the event type
if($(this).attr("data-events").indexOf(event.type) != -1)
fn[ event.type ](event);
}
);
}
})(eventFnArray)); // pass function array into closure
});
HTML:
<div id="container">
Test 1
Test 2
</div>
Testing adding more elements:
Here are 3 test cases:
// adds a link with the click event attached.
$('#container').append("<a href='#' class='js-test' data-events='click'>TEst333</a>");
// adds a link with the mouseover event
$('#container').append("<a href='#' class='js-test' data-events='mouseover'>TEst444</a>");
// adds a link with mouseout
$('#container').append("<a href='#' class='js-test' data-events='mouseout'>TEs555</a>");
// adds a link with both mouseover and mouseout attached
$('#container').append("<a href='#' class='js-test' data-events='mouseout mouseover'>TEstLast</a>");
// mouseout and click
$('#container').append("<a href='#' class='js-test' data-events='mouseout click'>TEstLastForREAL</a>");
Word of caution:
I noticed that one of your links has both the click and mouseover attached. While this code will handle multiple events per link, as demonstrated by the last test case, the click event will not fire if a mouseover event is present.
This is not a fault in the above code but in the way events are processed, as demonstrated here:
// mouseover and mouseout fire, but not the click event
$('#container').on('mouseover mouseout click', '.js-test',function() { alert("afdas " + event.type); });

How to do early binding for event handler in JavaScript? (example with jQuery)

JavaScript's late binding is great. But how do I early bind when I want to?
I am using jQuery to add links with event handlers in a loop to a div. The variable 'aTag ' changes in the loop. When I click the links later, all links alert the same message, which is the last value of 'aTag'. How do I bind a different alert message to all links?
All links should alert with the value that 'aTag' had when the event handler was added, not when it was clicked.
for (aTag in tagList) {
if (tagList.hasOwnProperty(aTag)) {
nextTag = $('');
nextTag.text(aTag);
nextTag.click(function() { alert(aTag); });
$('#mydiv').append(nextTag);
$('#mydiv').append(' ');
}
}
You can pass data to the bind method:
nextTag.bind('click', {aTag: aTag}, function(event) {
alert(event.data.aTag);
});
This will make a copy of aTag, so each event handler will have different values for it. Your use case is precisely the reason this parameter to bind exists.
Full code:
for (aTag in tagList) {
if (tagList.hasOwnProperty(aTag)) {
nextTag = $('');
nextTag.text(aTag);
nextTag.bind('click', {aTag: aTag}, function(event) {
alert(event.data.aTag);
});
$('#mydiv').append(nextTag);
$('#mydiv').append(' ');
}
}
You can also make a wrapper function that takes the text to alert as a parameter, and returns the event handler
function makeAlertHandler(txt) {
return function() { alert(txt); }
}
and replace
nextTag.click(function() { alert(aTag); });
with
nextTag.click(makeAlertHandler(aTag));
You need to keep a copy of this variable, like this:
for (aTag in tagList) {
if (tagList.hasOwnProperty(aTag)) {
nextTag = $('');
nextTag.text(aTag);
var laTag = aTag;
nextTag.click(function() { alert(laTag); });
$('#mydiv').append(nextTag);
$('#mydiv').append(' ');
}
}
The aTag variable is changing each time you loop, at the end of the loop it's left as the last item in the loop. However, each of the functions you created point at this same variable. Instead, you want a variable per, so make a local copy like I have above.
You can also shorten this down a lot with chaining, but I feel it clouds the point in this case, since the issue is scoping and references.

Categories

Resources