Using mousedown event on mobile without jQuery mobile?
Asked Answered
B

4

35

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?

Benign answered 21/6, 2012 at 18:21 Comment(3)
Hey budy! You've got your answer bellow, and thats all fine. But look, just for note: Using image on buttons is not the best user experience, try using only css. You can find some nice buttons style at getbootstrap.com/components and many others frameworks and even creating your own style. Try it! you'll like itDepict
@Guilherme: Just so you know, there are valid reasons for using images on buttons. In my case, I'm simulating a physical rocker switch using SVG, something that would be impossible to do using just CSS. But I'm also not manipulating the button style directly. All I'm doing is adding and removing a class name.Versicle
Nice :D image is not bad, just not always the best option. In this case, even though its possible to css it, I would use image too.Depict
T
81

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.

Tyika answered 21/6, 2012 at 19:20 Comment(11)
Though this seems to satisfy the specific use case of the OP, this answer does not answer the question as it was posed, and it is inaccurate. Vmousedown and vmouseup don't just "attempt to mimic" touchstart and touchend. They also abstract away all the differences between touch event and mouse events, so that you can use vmousedown and vmouseup just like you would use mouseup and mousedown. If you use touchstart and touchend, in general, a huge lot of things may need to be changed and you also need to duplicate code for the two kinds of events.Vladivostok
So, the question is still unanswered: how to take advantage of vmouseup and vmousedown without using the whole jquerymobile framework.Vladivostok
@Vladivostok Feature detect, then bind to the appropriate event. Using a script like Modernizr, this adds a couple lines of custom code. Also, could you elaborate on a huge lot of things may need to be changed?Tyika
It's not just that!!! There are a lot of differences between the way mouseup/down and touchstart/end work and the data you get from the events! That's what vmouseup and vmousedown are for: they take care of that for you. See api.jquerymobile.com/vmousedownVladivostok
e.g. "(...) The virtual mouse events also normalize how coordinate information is extracted from the event, so in touch based environments, coordinates are available from the pageX, pageY, screenX, screenY, clientX, and clientY properties, directly on the event object."Vladivostok
@Vladivostok So the coordinates of the event are normalized. A line of code inside your event handler would fix normalizing that data.Tyika
Well, if it's that easy, then maybe if you post an implementation, that would be the answer to the question.Vladivostok
Thank you very much. In the meantime, I've found out that it is possible to download a custom build of jquerymobile at jquerymobile.com/download-builder and include only the modules you need, in this case Virtual Mouse, which by the way seems to be a fair amount of code (but it includes vmousemove too). Still have to try it, thoughVladivostok
@Vladivostok That's a nice tool the jQuery Mobile guys made. Looks like downloading just the vmouse events from jQuery Mobile is about the same file size as using Modernizr.Tyika
Yeah but apart from the filesize I guess they have little in common. Jquery mobile's vmouse module seems to take care of a lot of things in order to keep the virtual mouse event as much a complete and transparent drop-in replacement for mouse events as possible, which I don't think modernizer does (apart from detecting feature support); also modernizer most probably does a lot of things that are not related that obviously jquerymobile's vmouse doesn't. Btw I just tried it and it works.Vladivostok
but touch events are not supported on IE, Edge, Safari? so can't use 'touchstart'Unstopped
I
7

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/

Intumesce answered 7/7, 2014 at 23:31 Comment(6)
I don't know why this answer got downvoted, is indeed and I agree that's not what the starter was asking for, but specifically taking in consideration that the request itself is about image swapping (and also to be fair, from the kind of request he seems to do not be aware of few concepts) I totally support this answer. Also I want to add about JS being extremely abused for tasks which are entirely entitled to CSS. This behaviour should be completely eradicated and I encourage people to use the CSS for rendering (which is also faster) and leave JS to do what's is made for: be a controller.Georgiannegeorgic
I downvoted the answer because it has nothing to do with answering the question, which is about a very specific problem with the vmouse event, and this answer suggests to not even use events. That may be a solution for the specific example provided in the question, but it doesn't address the question and would not apply to any use case where an event is needed. The correct place for such a suggestion would be a COMMENT to the question. If this was a comment, I would have even upvoted it. How much knowledge the OP shows is irrelevant; answers are for everybody, not just for the OP.Vladivostok
@Vladivostok The OP says in the first sentence "for a little bit of polish, I wanted...to swap out images (in this case, to make a button look like it's being pressed)." They are asking how to do that but have already started down a certain path using jQuery. That path is not the ideal way to 'make a button look like it's being pressed'. The OP may still go with their jQuery solution but CSS is the 'right' way to do this. This answer doesn't belong as a comment, even if it doesn't end up the accepted answer.Intumesce
The question is titled "Using mousedown event on mobile without jQuery mobile?". The swap-images use case can be considered a simplistic example of an application. If the body of the question seems inconsistent with its title, a comment is the place for asking the OP to clarify. If the question evidences a wrong motivation for wanting to do what the question asks, a comment is the place to suggest that IMHO. You are free to post this as an answer, but I consider it a bad answer to the question and that's why I downvoted it (and by the way I explained just because I was asked).Vladivostok
So any bad answer is one that isn't the accept one? Do you really down vote every answer you see that isn't the exact solution the OP is already trying to use?Intumesce
It has nothing to do with whether or not not it's the accepted one (actually I don't consider the accepted one a very good answer either, but it does address the question, and I only upvoted it after it was edited to include some more detail, because I apprectiate the help it provides). And no, I don't downvote every answer that isn't the exact solution, I only do if i think it's a bad answer or one that would belong as a comment and not an answer. I especially dislike answers of the kind "what you're asking for is not what you should be doing". Of course it's just my opinion and my vote.Vladivostok
V
5

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)

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.

Vladivostok answered 2/7, 2014 at 20:52 Comment(3)
vmouse events are custom events not methods, hence you need to bind .on() unbind .off() them.Petaloid
Awesome, works great and doesn't force me to use jQuery UI nor the full jQuery Mobile library.Valence
Perfect, exactly what I was after, I have no need for the Jquery mobile CSS or other stuff and that loading message it displays was aggravating. many thanks =)Convincing
L
2

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.

Lunik answered 24/1, 2021 at 21:1 Comment(1)

© 2022 - 2024 — McMap. All rights reserved.