a need to speed up links with onClick attr in Phonegap app.
I would like to use Fast buttons plugin, that i found here:
https://developers.google.com/mobile/articles/fast_buttons
But i dont know, how to right use this plugin should i add this after pageinit or where?
Maybe is it quite silly question, but if i tried to find some examples, with no luck.
Could You add somebody add put here some example, how solve this problem?
Many thanks.
The touchstart (or touchend) event works great if you know the user won't be scrolling. That's actually the reason click events take so long to resolve on mobile devices, the device is waiting to see if the user is scrolling or clicking.
This will perform quite fast as there is no delay for dispatching this event:
$('#myButton').on('touchstart', function () {
//run click code now
});
You can also use jQuery Mobile's vclick event which attempts to use the native touch events but it's main problem is that you can dispatch multiple events using vclick so you should set a timeout to only allow one click at a time, for example:
var clickOk = true;
$('#myButton').on('vclick', function () {
if (clickOk === true) {
clickOk = false;
setTimeout(function () {
clickOk = true;
}, 350);
//run click code now
}
return false;
});
This will allow the event handler to run only once per 350ms which will take care of the multiple events being dispatched since the second event will be ignored.
I would set these event handlers up in a delegated event handler that runs when a pseudo-page gets initialized:
$(document).on('pageinit', '.ui-page', function () {
//bind "fast-click" event handlers now, use "$(this).find(...)" to only bind to elements of the current pseudo-page
});
Jiri If it's not too late I had to do the same thing for my app and needed to pass parameters to the function. I did it by placing the parameters in the id of the button (separarted by underscores) and using this function which grabs the id from every clicked button with a classname of "clickbutton" and splits it into the individual parameters.
$('.clickbutton').live('click', function(event) {
event.preventDefault();
var id = $(this).attr('id');
var parts = $(this).attr('id').split("_");
var item = parts[0];
var button = parts[1];
var type = parts[2];
console.log(item+button+type);
getItemCondition(item,type);
return false;
});
Still having issues with unresponsiveness from JQM click event though!
What about fastclick ?
FastClick is a simple, easy-to-use library for eliminating the 300ms delay between a physical tap and the firing of a click event on mobile browsers. The aim is to make your application feel less laggy and more responsive while avoiding any interference with your current logic.
Related
My situation is that I am trying to trigger a single event using the jQuery .trigger() method. However the element I am triggering has multiple click event listeners.
Actually finding what these listeners are and what they trigger from the source code is probably not viable as its included in the sites main JS file and its all minified and pretty much unreadable.
At the moment I know that the element when clicked performs some kind of ajax call and loads more data into the DOM of the page (which is what i want to trigger), however it also displays an overlay (which is what I want to suppress temporarily).
As its just an overlay there are workaround I can make; using a display:none on it straight after click etc. However it would be much more elegant if i could somehow suppress all click events on this element except the desired event.
Any ideas if this is actually possible? And if so how I would go about it?
You need to register your own event at the top of the event chain. And cancel the event chain in your event. Here is a solution with writing a custom jquery extention.
$.fn.bindFirst = function (which, handler) {
var $elm = $(this);
$elm.unbind(which, handler);
$elm.bind(which, handler);
var events = $._data($elm[0]).events;
var registered = events[which];
registered.unshift(registered.pop());
events[which] = registered;
}
$("#elm").bindFirst("click", function(e) {
// edit: seems like preventing event does not work
// But your event triggers first anyway.
e.preventDefault();
e.stopPropagation();
});
Reference:
https://gist.github.com/infostreams/6540654
EDIT:
https://jsfiddle.net/8nb9obc0/2/
I made a jsFiddle and it seems like event preventing does not work in this example. There might be another solution.
I am working on some javascript UI, and using a lot of touch events like 'touchend' for improved response on touch devices. However, there are some logical issues which are bugging me ...
I have seen that many developers mingle 'touchend' and 'click' in the same event. In many cases it will not hurt, but essentially the function would fire twice on touch devices:
button.on('click touchend', function(event) {
// this fires twice on touch devices
});
It has been suggested that one could detect touch capability, and set the event appropriately for example:
var myEvent = ('ontouchstart' in document.documentElement) ? 'touchend' : 'click';
button.on(myEvent, function(event) {
// this fires only once regardless of device
});
The problem with the above, is that it will break on devices that support both touch and mouse. If the user is currently using mouse on a dual-input device, the 'click' will not fire because only 'touchend' is assigned to the button.
Another solution is to detect the device (e.g. "iOS") and assign an event based on that:
Click event called twice on touchend in iPad.
Of course, the solution in the link above is only for iOS (not Android or other devices), and seems more like a "hack" to solve something quite elementary.
Another solution would be to detect mouse-motion, and combine it with touch-capability to figure out if the user is on mouse or touch. Problem of course being that the user might not be moving the mouse from when you want to detect it ...
The most reliable solution I can think of, is to use a simple debounce function to simply make sure the function only triggers once within a short interval (for example 100ms):
button.on('click touchend', $.debounce(100, function(event) {
// this fires only once on all devices
}));
Am I missing something, or does anyone have any better suggestions?
Edit: I found this link after my post, which suggests a similar solution as the above:
How to bind 'touchstart' and 'click' events but not respond to both?
After a day of research, I figured the best solution is to just stick to click and use https://github.com/ftlabs/fastclick to remove the touch delay. I am not 100% sure this is as efficient as touchend, but not far from at least.
I did figure out a way to disable triggering events twice on touch by using stopPropagation and preventDefault, but this is dodgy as it could interfere with other touch gestures depending on the element where it is applied:
button.on('touchend click', function(event) {
event.stopPropagation();
event.preventDefault();
// this fires once on all devices
});
I was in fact looking for a solution to combine touchstart on some UI elements, but I can't see how that can be combined with click other than the solution above.
This question is answered but maybe needs to be updated.
According to a notice from Google, there will be no 300-350ms delay any more if we include the line below in the <head> element.
<meta name="viewport" content="width=device-width">
That's it! And there will be no difference between click and touch event anymore!
Yes disabling double-tap zoom (and hence the click delay) is usually the best option. And we finally have good advice for doing this that will soon work on all browsers.
If, for some reason, you don't want to do that. You can also use UIEvent.sourceCapabilities.firesTouchEvents to explicitly ignore the redundant click. The polyfill for this does something similar to your debouncing code.
Hello you can implement the following way.
function eventHandler(event, selector) {
event.stopPropagation(); // Stop event bubbling.
event.preventDefault(); // Prevent default behaviour
if (event.type === 'touchend') selector.off('click'); // If event type was touch turn off clicks to prevent phantom clicks.
}
// Implement
$('.class').on('touchend click', function(event) {
eventHandler(event, $(this)); // Handle the event.
// Do somethings...
});
Your debounce function will delay handling of every click for 100 ms:
button.on('click touchend', $.debounce(100, function(event) {
// this is delayed a minimum of 100 ms
}));
Instead, I created a cancelDuplicates function that fires right away, but any subsequent calls within 10 ms will be cancelled:
function cancelDuplicates(fn, threshhold, scope) {
if (typeof threshhold !== 'number') threshhold = 10;
var last = 0;
return function () {
var now = +new Date;
if (now >= last + threshhold) {
last = now;
fn.apply(scope || this, arguments);
}
};
}
Usage:
button.on('click touchend', cancelDuplicates(function(event) {
// This fires right away, and calls within 10 ms after are cancelled.
}));
For me using 'onclick' in the html element itself, worked for both touch and click.
<div onclick="cardClicked(this);">Click or Touch Me</div>
I've built a webapp, and for a little bit of polish, I wanted to add mousedown and mouseup handlers to swap out images (in this case, to make a button look like it's being pressed).
my code is something like this:
window.onload = function() {
//preload mouse down image here via Image()
$("#button_img").mousedown(function(){$("#button_img").attr("src","button_on.png");});
$("#button_img").mouseup(function(){$("#button_img").attr("src","button_off.png")});
}
This works swimmingly on the desktop, but on mobile (testing in iOS Safari), the mousedown and mouseup events happen at the same time, so effectively nothing happens.
I tried to use the vmousedown and vmouseup events in jQueryMobile, however this code:
//include jquerymobile.js and jquerymobile.css
window.onload = function() {
//preload mouse down image here via Image()
$("#button_img").vmousedown(function(){$("#button_img").attr("src","button_on.png");});
$("#button_img").vmouseup(function(){$("#button_img").attr("src","button_off.png")});
}
Just gave me the errors that vmousedown and vmouseup don't exist. Also, jQueryMobile overrides the CSS I've already written for the page.
So is there a way to get vmousedown and vmouseup to work, and to do so without jQuery Mobile's CSS?
You're looking for touchstart and touchend. They are the events that vmousedown and vmouseup attempt to mimic.
Here's an example:
window.onload = function() {
//preload mouse down image here via Image()
$("#button_img").bind('touchstart', function(){
$("#button_img").attr("src","button_on.png");
}).bind('touchend', function(){
$("#button_img").attr("src","button_off.png");
});
}
This will work without any framework on any device that supports touch events. You could use something like Modernizr to do this test and if the device does not support touch events, bind to the regular desktop events.
When you use touchstart/touchend/touchmove you get some interesting information, for instance how many touches are occurring at once, so you can detect if the user is scrolling or attempting to zoom.
UPDATE
Since the event object inside an event handler differs for touch events and mouse events, if you want to know the coordinates of the event either way, you can do something like this (the example below assumes Modernizr has been loaded):
//determine which events to use
var startEventType = 'mousedown',
endEventType = 'mouseup';
if (Modernizr.touch === true) {
startEventType = 'touchstart';
endEventType = 'touchend';
}
//bind to determined event(s)
$("#button_img").bind(startEventType, function(event) {
//determine where to look for pageX by the event type
var pageX = (startEventType === 'mousedown')
? event.pageX
: event.originalEvent.touches[0].pageX;
...
})...
UPDATE
I was looking this over and it seems like you don't need to detect the event type before binding the event handler:
//bind to determined event(s)
$("#button_img").bind('mousedown touchstart', function(event) {
//determine where to look for pageX by the event type
var pageX = (event.type.toLowerCase() === 'mousedown')
? event.pageX
: event.originalEvent.touches[0].pageX;
...
})...
If you are worried about receiving both events in quick succession you could use a timeout to throttle the event handler:
//create timer
var timer = null;
//bind to determined event(s)
$("#button_img").bind('mousedown touchstart', function(event) {
//clear timer
clearTimeout(timer);
//set timer
timer = setTimeout(function () {
//determine where to look for pageX by the event type
var pageX = (event.type.toLowerCase() === 'mousedown')
? event.pageX
: event.originalEvent.touches[0].pageX;
...
}, 50);
})...
Note: You can force mousedown and touchstart events in quick succession with developer tools but I'm not sure about the real world use case here.
Have you considered styling your buttons using CSS instead? the :active state will be triggered when a user is clicking/touching the element. Here is an example:
/* Default state */
#button_img {
background-image: url('button_off.png');
}
/* Clicked/touched state */
#button_img:active {
background-image: url('button_on.png');
}
CSS will be much more performant and you will also be able to better separate concerns (display vs logic, etc).
JSBin: http://jsbin.com/beyin/1/
There is a way to get the vmouseup, vmousedown, vmousemove, vclick, etc. functionality of jQueryMobile without getting all the rest (and especially the side effects) of jquerymobile (i.e. enhancement, extra css, and the like)
Go to http://jquerymobile.com/download-builder/ (a tool for downloading a custom build of jquerymobile with only the components you need)
select ONLY "Virtual Mouse (vmouse) Bindings"
download it.
The download will contain only a single .js files (in both minimized and uncompressed version). No css.
Link this script in the head of your html after plain jquery, and use it like this:
<head>
<script src="http://code.jquery.com/ui/1.10.4/jquery-ui.min.js"></script>
<script src="whatever/path/jquery.mobile.custom.min.js"></script>
<script>
$(function(){ // or replace this with window.onload for that matter
// Your code here, e.g.
$("#button_img").on("vmousedown", function() {
/*whatever*/
});
// CAUTION: this won't work (see note below):
// $("#button_img").vmousedown(function(){/*whatever*/}); // WON'T WORK
});
</script>
</head>
NOTE: the methods .vmousedown(), .vmouseup(), etc. won't work. You have to bind the event listener with .on("vmousedown", ...).
Not sure why: I guess this is because the part of jquerymobile that creates shortcut methods with the same name as the events is in some other module. Maybe it is possible to figure out which module it is and include it in the download, but I think it would force you to include other undesired dependencies.
Use touchstart or touchend for touch devices.
Most times you want to catch touchstart as well as mousedown. You need to make sure though that the handler is only triggered once. The simplest way to do this is to catch them both and call e.preventDefault().
$("#button_img").on('touchstart mousedown', function(e) {
//your code...
e.preventDefault(); //prevents further events from being dispatched
}
Source: developer.mozilla.org:
If the browser fires both touch and mouse events because of a single user input, the browser must fire a touchstart before any mouse events. Consequently, if an application does not want mouse events fired on a specific touch target element, the element's touch event handlers should call preventDefault() and no additional mouse events will be dispatched.
Is there any way to know, in a jQuery onmouseup handler, if the event is going to be followed by a click event for the same element?
I have an event handler for a menu hyperlink which unbinds itself when the user either clicks on an element or "drops" (as in drag-n-drop) on an element. I want to avoid prematurely unbinding the handler on mouseup if a click is coming next.
I realize I can track mousedown and mouseup events myself or otherwise hack up a solution (e.g. wait 50 msecs to see if a click comes soon), but I was hoping to avoid rolling my own implementation if there's something built-in for this purpose.
There is nothing built-in because it's really specific to your needs. Thus, there would kilometers of code and documentation to maintain if jQuery would handle any combination of clicks, long clicks, moves, etc.
It's also hard to give you a snippet that satisfies your needs, but a setTimeout is usually the first step to take, with something like that :
obj.mouseup = function (){
obj.click = action; // do action
setTimeout ( function() {
obj.click = functionOrigin // after 500 ms, disable the click interception
}, 500);
};
you can use $(selector).data('events') for that
$('div').mouseup(function(){
if($(this).data('events').click){
console.log('Has a click event handler')
}
});
I develop an app using phonegap and jquerymobile/jquery.
During development I only had a virtual iOS device and now since I'm testing the app on a real device I see, that time between click on an element and handle this event is very long.
E.g if i Click on an icon a loader icon is shown but this icon first come in the moment the next page is final loaded (a very short time the loader is shown).
I develop with Javascript since a long time and always have focus on performant execution but this is very strange.
The App has got about 10 views in one HTML file. And on click on an element only show the next part of these file.
Does anyone know about solutions to solve "Bugs" like these?
Thanks in advance.
The click delay on iPhones is a feature used to distinguish between clicks and scrolls. When you bind to the click event iOS waits approximately 300ms to decide whether you were clicking an object or trying to scroll the page.
You can use jQuery Mobile's vclick event which fires much faster however you will probably run into situations where the vclick event is fired off twice in a row which can result in multiple elements being clicked. Here is some sample code of how to use vclick events and only capture the event triggered first:
$(function () {
//setup a function to check if a vclick event has fired within the last 500ms
function check_vclick () {
//if a vclick event has fired in the last 500ms then return false
if (do_vclick == false) return false;
//otherwise set a flag to disallow vclicks for 500ms
do_vclick = false;
//setup a timeout to allow vclicks in 500ms
setTimeout(function () {
do_vclick = true;
}, 500);
//return true so the event handler knows it's ok to run its code
return true;
}
//setup a flag to allow/disallow vclick events from firing
var do_vclick = true;
//bind an event handler to the vclick event for an element
$('#link_id').bind('vclick', function () {
if (check_vclick()) {
//run the code associated with the element, if it's a link referencing a pseudo-page on the same HTML document, you can do something like this
$.mobile.changePage($(this.href));
}
});
});
Here's a link to the documentation for $.mobile.changePage(): http://jquerymobile.com/demos/1.0rc2/docs/api/methods.html
Here's a link to the documentation for vclick (notice the notes under the virtual mouse event section): http://jquerymobile.com/demos/1.0rc2/docs/api/events.html