Famo.us attaching click and touchstart events - javascript

I'm wondering how to bind both events "click" and "touchstart" within a single line:
Engine.on("click"), function(){ aCode() }
Engine.on("touchstart", function(){ aCode() }
I expected something like this (as it is implemented in some other frameworks):
Engine.on("click touchstart", function(e){ aCode() });
My current workaround is chaining them:
Engine.on("click", function(){Engine.emit("touchstart")});
Engine.on("touchstart", function() { aCode() }
Is there a better practice for this ? The problem is that click is not caught on iOS nor the touchstart with a mouse on desktop… obviously I would like to handle the event the same way whatever the device.

[EDIT] For handling the click and touchstart->touchend in the same way, just use the FastClick override shim.
just add:
FastClick = require('famous/inputs/FastClick');
then this will also work on tablet:
anElement.on("click", function(){alert("Click caught")})
Another alternative is using this helper:
function onEvent(source, events, callback)
{
for (var event in events)
{
source.on(events[event], function(e) {
callback(e);
});
}
}
Then:
onEvent(Famous.Engine, ["click","touchstart"],function(){});

The on() method in Famo.us only accepts one event type. It doesn't do any jQuery style string processing to determine separate events. In theory you could have an event named get to the chopper
What I do when creating my custom views however is create a bindEvents() function which groups all the event listeners together. The only code in these handlers are other functions. If I wanted to react to two different events in the same way, I'd just use the same function for both of them.
// An example Class demonstrating events - some off-topic parts omitted
function myLovelyHorseButton() {
// Recomended Reading: https://famo.us/guides/dev/events.html
this._eventInput = new EventHandler();
this._eventOutput = new EventHandler();
this._eventInput.pipe(this.sync);
this.sync.pipe(this._eventInput);
EventHandler.setInputHandler(this, this._eventInput);
EventHandler.setOutputHandler(this, this._eventOutput);
/** Set options & variables
/* ...
*/
// create event listeners for this specific instance
_bindEvents.call(this);
};
// bind all the events for the button
function _bindEvents() {
//Call event handlers with this set to owner.
this._eventInput.bindThis(this);
// listen for events
this._eventInput.on('click', _handleClick);
this._eventInput.on('touchstart', _handleClick); //Same as 'click'
};
// Nay to the console on click/touch
function _handleClick(event) {
console.log('Nayyyyy!!!');
};
// create an instance of myLovelyHorseButton
var button = new myLovelyHorseButton;
// We would then add the button to the Render Tree
mainContext.add(button);
There is a big reason why you don't want to do the chaining pattern you're currently using. And that's because by emitting a touchstart event on a click, you're inferring that there's going to be some code there to act upon it. One day you might get drunk and decide "Nobody on touch devices is using this!" and delete the ontouchstart handler. And in an instant, your code doesn't work for anybody, touch or mouse.
TL;DR There is nothing wrong with using multiple on() calls.
I hope this helps.

Related

Change the Attr and do new function not working in jQuery [duplicate]

I have a site that uses AJAX to navigate. I have two pages that I use a click and drag feature using:
$(".myDragArea").mousedown(function(){
do stuff...
mouseDrag = true; // mouseDrag is global.
});
$("body").mousemove(function(){
if (mouseDrag) {
do stuff...
}
});
$("body").mouseup(function(){
if (mouseDrag) {
do stuff...
mouseDrag = false;
}
});
I just type that out, so excuse any incidental syntax errors. Two parts of the site use almost identical code, with the only difference being what is inside the $("body").mouseup() function. However, if I access the first part, then navigate to the second part, the code that runs on mouseup doesn't change. I have stepped through the code with Firebug, and no errors or thrown when $("body").mouseup() is run when the second part loads.
So, why doesn't the event handler change when I run $("body").mouseup() the second time?
Using $("body").mouseup( ... ) will add an event handler for the body that is triggered at mouseup.
If you want to add another event handler that would conflict with current event handler(s) then you must first remove the current conflicting event handler(s).
You have 4 options to do this with .unbind(). I'll list them from the least precise to the most precise options:
Nuclear option - Remove all event handlers from the body
$("body").unbind();
This is pretty crude. Let's try to improve.
The elephant gun - Remove all mouseup event handlers from the body
$("body").unbind('mouseup');
This is a little better, but we can still be more precise.
The surgeon's scalpel - Remove one specific event handler from the body
$("body").unbind('mouseup', myMouseUpV1);
Of course for this version you must set a variable to your event handler. In your case this would look something like:
myMouseUpV1 = function(){
if (mouseDrag) {
do stuff...
mouseDrag = false;
}
}
$("body").mouseup(myMouseUpV1);
$("body").unbind('mouseup', myMouseUpV1);
$("body").mouseup(myMouseUpV2); // where you've defined V2 somewhere
Scalpel with anesthesia (ok, the analogy's wearing thin) - You can create namespaces for the event handlers you bind and unbind. You can use this technique to bind and unbind either anonymous functions or references to functions. For namespaces, you have to use the .bind() method directly instead of one of the shortcuts ( like .mouseover() ).
To create a namespace:
$("body").bind('mouseup.mySpace', function() { ... });
or
$("body").bind('mouseup.mySpace', myHandler);
Then to unbind either of the previous examples, you would use:
$("body").unbind('mouseup.mySpace');
You can unbind multiple namespaced handlers at once by chaining them:
$("body").unbind('mouseup.mySpace1.mySpace2.yourSpace');
Finally, you can unbind all event handlers in a namespace irrespective of the event type!
$("body").unbind('.mySpace')
You cannot do this with a simple reference to a handler. $("body").unbind(myHandler) will not work, since with a simple reference to a handler you must specify the event type ( $("body").unbind('mouseup', myHandler) )!
PS: You can also unbind an event from within itself using .unbind(event). This could be useful if you want to trigger an event handler only a limited number of times.
var timesClicked = 0;
$('input').bind('click', function(event) {
alert('Moar Cheezburgerz!');
timesClicked++;
if (timesClicked >= 2) {
$('input').unbind(event);
$('input').val("NO MOAR!");
}
});​
Calling $("body").mouseup(function) will add an event handler.
You need to remove the existing handler by writing $("body").unbind('mouseup');.
jQUery doesn't "replace" event handlers when you wire up handlers.
If you're using Ajax to navigate, and not refreshing the overall DOM (i.e. not creating an entirely new body element on each request), then executing a new line like:
$("body").mouseup(function(){
is just going to add an additional handler. Your first handler will still exist.
You'll need to specifically remove any handlers by calling
$("body").unbind("mouseUp");

Should I / how do I clear a mousemove JQuery event listener?

When I use
$(".page").mousemove(function(event){});
As soon as the mouseup event comes, I no longer need this listener. Since I'll be applying the listener repetitively for different actions, it seems to me that these listeners might stick around and build up (needlessly wasting CPU) as the user activates the function many times. I'm not really sure how this works internally, that's just my guess.
Should I / how do I clear a mousemove JQuery event listener?
Here is the code:
$('.page').off('mousemove');
But please note that the following approach turns off all functions firing on mousemove. If you want to turn off a prticular function then you should do the following:
// Define function that fires on mousemove
function anyFunctionName () {
// Some code
}
// Set listener
$('.page').on('mousemove', anyFunctionName);
// Turn off only function called funcionName
$('.page').off('mousemove', anyFunctionName);
Another way for turning off a particular function would is defining a name for the event:
// Define function that fires on mousemove
function anyFunctionName () {
// Some code
}
// Set listener
$('.page').on('mousemove.anyEventName', anyFunctionName);
// Turn off only function fired on event called anyEventName
$('.page').off('mousemove.anyEventName');
I was able to get a working example using the more general .on and .off JQuery functions instead of their explicit handlers. Here is the code I used:
$('.page').on('mousedown', function() {
$(this).on('mousemove', function(event) {
// Do something meaningful
});
});
$('.page').on('mouseup', function() {
$(this).off('mousemove');
});
Here is a JFiddle Demo of it.

javascript/jquery How to cancel .on when a class is present

I have several places throughout my code where I use .on to attach events (usually to delegate the events). We're changing around how we're doing a few things, and we're now wanting to add a .disabled class to the elements that we want to be disabled. I'd like to block all the events on disabled items without having to refactor each location, I'm wondering if it's possible.
Example code: I've added this to the top of my script
$('body').on('click', '.disabled', function(event){
console.log("blocked");
event.stopImmediatePropagation();
// event.preventDefault();
// event.stopPropogation();
return false;
});
And an example of my normal events:
$('.ActionsContainer').on('click', '.Link', functions.ClickAction);
Problem is that even with the return false and all the others it still runs both the "blocked" and functions.ClickAction
Is there anyway around refactoring every one? I mean I can change that line below to:
$('.ActionsContainer').on('click', '.Link:not(.disabled)', functions.ClickAction);
but that's really annoying, and feels brittle.
It's not too hard. You'll need to take advantage of jQuery's special events and basically override calls to any of the original event handlers setup in the existing code. jQuery's special events hooks let you override a number of features of the event system. jQuery essentially sets up it's own handler on an element the first time a listener is attached, and then adds the callback for the listener to its queue. As other listeners get attached to the element later, their callbacks get added to this queue as well.
Using the 'events.special.click' hook, we can add a function that gets called prior to any callbacks on that element's event queue which lets us intercept the call and check for, as you mentioned, that the element has a 'disabled' class and if so, stop the original callback from executing; or if it doesn't have the class, allow the original callback to execute normally.
I've put together a jsFiddle to show how it works. See if that solves your issue. The code for the override using special events is embedded below the link:
http://jsfiddle.net/datchley/bthcv/
// ADDED TO OVERRIDE CLICKS ON 'DISABLED' ELEMENTS
(function($) {
$.event.special.click = {
add: function(handle) {
// Save original handler
var orig_handlefn = handle.handler,
$el = $(this);
// Reassign our new handler to intercept here
handle.handler = function(ev) {
if ($el.hasClass('disabled')) {
// Don't allow clicks on disabled elements
$('.output').html('<b>Warning</b> You clicked a disabled element!');
ev.preventDefault();
}
else {
return orig_handlefn.apply(this, arguments);
}
};
}
};
})(jQuery);
Assuming every .Link has that container and you're handling all events at that container, this is the most straightforward way:
$('.disabled').click( function(e){ e.stopPropagation(); } );
stopProp prevents that event from ever bubbling up to the action containers.

Can jQuery click events be unbound or reset?

I've discovered a resource leak on a webpage I'm working on.
This webpage has two textfields, that upon click show a modal dialog, perform a data request to the backend, and then present that information in a table that the user can select an entry from for use in the textbox they originally clicked.
I'm binding the click events to the textboxes like so:
var $field = $('#one-of-the-text-fields');
$field.click(function () {
App.DialogClass.show();
App.DialogClass.PopulateTable();
App.DialogClass.GotoPageButtonAction(actionArgs); // Offender!
});
...Which calls...
App.DialogClass = (function($) {
var pub {},
$gotoPage = $('#pageNumberNavigationField'),
$gotoPageButton = $('#pageNumberNavigationButton');
// ...SNIP unimportant other details...
pub.GotoPageButtonAction = function (args) {
$gotoPageButton.click(function () {
var pageNumber = $gotoPage.val();
pub.PopulateTable(args); // Breakpoint inserted here...
});
};
return pub;
})(jQuery);
I noticed the leak because when I ran through using Chrome's JavaScript debugger, I'm always having one extra breakpoint hit every time I click a different button (e.g. the first time I click field A, the breakpoint is hit twice. When I hit field B after that, the break point is hit three times. If I click A after that, the breakpoint is hit four times. Extrapolate as necessary.)
Nowhere in my code am I doing anything about an existing click event for a given field. I suspect my leak stems from the fact that the events are not getting cleaned up. That being said, I am also not terribly familiar with JavaScript/jQuery. What are some techniques for removing click events from a control?
Sure. Just unbind them:
$field.unbind('click');
However, bear in mind that this will remove all event handlers for click, not just yours. For safety, you should use namespaces when binding handlers:
$field.bind('click.mynamespace', function(){
// do something
});
Then,
$field.unbind('click.mynamespace');
So, then, only your handler will be removed.
If you have used .bind() to bind them .unbind() removes the events
If you have used .on() to bind them .off() removes the events
JQuery offers the unbind function to unbind event listeners.
Note that you may also do it in vanilla JS using removeEventListener.
But instead of unbinding, you probably should not bind each time in GotoPageButtonAction : once is enough.

Override all JavaScript events bound to an element with a single new event

Assuming that there are a large number of elements throughout the site that have an unknown number and type of events bound to them.
If I need to override all of these events with one single bound event, and only that event will fire, what are some recommendations?
I would be binding the event to a click event handler, and I am using jQuery.
Thanks in advance.
You’re looking for jQuery#unbind.
To remove all event handlers on an element or a set of elements, just do:
$('.some-selector').unbind();
To unbind only click handlers, use unbind('click'):
$('.some-selector').unbind('click');
To unbind all click handlers and immediately bind your own handler after that, you can do something like this:
$('.some-selector').unbind('click').click(function(event) {
// Your code goes here
});
Note that this will only work for events bound using jQuery (using .bind or any jQuery method that uses .bind internally). If you want to remove all possible onclick events from a given set of elements, you could use:
$('.some-selector')
.unbind('click') // takes care of jQuery-bound click events
.attr('onclick', '') // clears `onclick` attributes in the HTML
.each(function() { // reset `onclick` event handlers
this.onclick = null;
});
I would like to provide a thought without removing all events all together (just override them).
If your new one single bound event (we call it "click" here) is specific to the element it binds to, then I believe you can ignore any other events simply by stopPropagation() function. Like this
$("specific-selector").on("click", ".specific-class", function (e) {
e.stopPropagation()
// e.stopImmediatePropagation()
/* your code continues ... */
});
It will stop events bubbles up, so your other events won't fire. use stopImmediatePropagation() to prevent other events attached onto the same elements as "click" does.
For example, if "mouseleave" event is also bind to $("specific-selector .specific-class") element, it won't fire, too.
At last, all other events won't fire on this element but your new "click" element.
The unsolved question is, what if other events also use stopPropagation()? ... Then I think the one with best specification wins, so try to avoid complex, too many events is final suggestion.
You can see "Direct and delegated events" on jQuery site for more information.
Looks like this is pretty simple actually:
$('#foo').unbind('click');
$('#foo').bind('click', myNewFunction);
Thanks for your responses though.
Try to use live instead of bind. Then you can easily remove live binding with die from selector which is fast operation and set another live equally fast.
$('selection here').live('..', .....); // multiple invocations
$('selection here').die();
$('selection here').live('click',.....);
DOM is not touched at all. Event condition is evaluated on event occurrence.
But generally if you just want to swap handler functions why not to do it this way:
var ahandler = function(evt) { /* first implementation */ }
$('.selector').bind('click', function(evt) { ahandler(evt); });
//and then if you want to change handlers
ahandler = function(evt) { /* new implementation */ };
This gives absolutely no cost of any changes, rebinding etc.

Categories

Resources