I'm attempting to write a Vimperator plugin to allow use of hints mode to simulate mouse over on drop down menus. I have the hints mode working and can correctly choose elements that have mouseover events attached. The problem is my function to simulate the mouse over is not working. This is what I currently have:
function SimulateMouseOver(elem)
{
var evt = elem.ownerDocument.createEvent('MouseEvents');
evt.initMouseEvent('mouseover',true,true,
elem.ownerDocument.defaultView,0,0,0,0,0,
false,false,false,false,0,null);
var canceled = !elem.dispatchEvent(evt);
if(canceled)
alert('Event Cancelled');
}
The above code works for some pages but not for others. For example it doesn't work on AccuWeather. Any ideas how to simulate a mouse over that will work for most pages?
here's some code to start with to create the event, simpler and works for more browsers (if you don't need to specify exact mouse coordinates)
if( document.createEvent ) {
var evObj = document.createEvent('MouseEvents');
evObj.initEvent( 'mouseover', true, false );
elem.dispatchEvent(evObj);
} else if( document.createEventObject ) {
elem.fireEvent('onmouseover');
}
hope that helps
In case anyone bumps into this looking for a framework agnostic way to fire any HTML and Mouse event (and set some options, if needed), have a look here: How to simulate a mouse click using JavaScript?
You may only trigger mouseover event on fields/elements that have a mouseover event bound to them. You can't just hijack the mouse.
Related
In the code below, the right-click is not getting trapped. left-click works fine. This code was given in the dojo documentation. Can someone tell me why isRight is not working? Essentially, when I right-click the element, I just get the browser's right-click menu, no console message is generated.
https://dojotoolkit.org/reference-guide/1.10/dojo/mouse.html
on(myNode,'click',function(e) {
if (mouse.isLeft(e)){
console.log("left click", e);
} else if (mouse.isRight(e)){
console.log("right click",e);
}
});
The browser right click context menu consumes the click event. It will work if you use 'mousedown' instead of 'click'. There are also many questions about right click detection in javascript that you can look into for alternate methods. For example: Failing to identify right click event in Mozilla Firefox.
The dojo/mouse module is mostly a utility wrapper over the usual event handling, so the information in these questions still applies.
You cannot detect mouse.isRight when using event click. Instead you could use mousedown as in the following example:
https://jsfiddle.net/xgekrp5e/
require(["dojo/mouse", "dojo/on"], function(mouse, on) {
on(document, "mousedown", function(evt) {
if (mouse.isLeft(event)) {
// handle mouse left click
alert('MOUSE LEFT');
} else if (mouse.isRight(event)) {
// handle mouse right click
alert('MOUSE RIGHT');
}
});
});
Recording onmousedown and onmouseup to use in onmousemove does not work because onmouseup only fires once if the buttons are released outside the window: http://jsfiddle.net/f1nqproy/5/
event.button only returns meaningful results in Internet Explorer.
event.buttons only exists in Firefox.
So what to do with other browsers?
EDIT:
MouseEvent.buttons has been standardized now, so this problem has been solved:
https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
If you want to do something browser agnostic the easiest way is to use jQuery
jQuery is a fast, small, and feature-rich JavaScript library. It makes
things like HTML document traversal and manipulation, event handling,
animation, and Ajax much simpler with an easy-to-use API that works
across a multitude of browsers. With a combination of versatility and
extensibility, jQuery has changed the way that millions of people
write JavaScript.
You can easly bind to mouseup,mousedown event and forget about browser compatibility
$( window).mouseup(function(e){
console.log("mouseup:", e.button);
});
$( window).mousedown(function(e){
console.log("mousedown:", e.button);
});
-----UPDATE------
IE (as usual) do another work so you will not get the mouseup event if the pointer is outside the window.
An hack is to use the mouseleave event:
//hack for IE
$( window).mouseleave (function(e){
console.log("mouseleave");
});
JsFiddle
May be, the problem is that events onmouseup are triggered simultaneously by browser. Every time it checks, whether event triggered at the moment when mouse cursor is on the element, to which event was bound.
So, in your case first event triggers (don't know why), while the second doesn't work - it is correct behavior - instead mouseleave event triggered.
Try to catch mouseleave:
var buttonDown = [false, false, false];
$( window).on('mouseup mouseleave', function(e){
buttonDown[e.button] = true;
console.log("Buttons up:", buttonDown);
buttonDown = [false, false, false];
});
$( window).on('mousedown', function(e){
buttonDown[e.button] = true;
console.log("Buttons down:", buttonDown);
buttonDown = [false, false, false];
});
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.
I am trying to simulate the swipe event from the iPhone with Raphaeljs.
To do so, I am using the drag and drop event.
To simulate the event in have a method in the move event that calculate the distance between my object and the mouse position. If the mouse goes after that distance I want to stop the drag and drop event. This is where I'm stuck.
Here is the code:
var start = function (event) {
},
move = function (event) {
inrange = self.inRange (circle.attr("cx"), circle.attr("cy"), event.pageX, event.pageY);
if(inrange == false){
//Stop draging!
}
},
up = function () {
circle.animate({ r: 40, "stroke-width": 1 }, 200);
};
circle.drag(move, start, up);
In the move method I need the stop the drag event or simulate a mouseup. How can I do so?
From the documentation:
To unbind events use the same method names with “un” prefix, i.e. element.unclick(f);
and
To unbind drag use the undrag method.
So I would think circle.undrag(); should work.
#Gregory You can use a JS closure to detect if the circle needs to stop dragging. And #apphacker there is a known issue in Raphael that prevents undrag from working. It is scheduled to be fixed in 2.0, but that doesn't have a release date as of yet (and the bug isn't fixed in the beta code, despite the ticket saying it is.
I recommend manually implementing the mousedown, mouseup and mousemove events using jQuery, as per #floyd's recommendation, and add a JS closure check in your move function to see if the circle needs to stop being dragged yet or not.'
It also occurs to me that your original post was last edited in Nov '10, so you might have moved on since then ;)
If you can include jQuery you can use trigger on "mouseup." If you can't include jQuery maybe just take a look at the source and lift that one function?
UPDATE
After some brief googling I came across this implementation. Only tested in in Chrome though:
function fireEvent(element,event){
if (document.createEventObject){
// dispatch for IE
var evt = document.createEventObject();
return element.fireEvent('on'+event,evt)
} else{
// dispatch for firefox + others
var evt = document.createEvent("HTMLEvents");
evt.initEvent(event, true, true ); // event type,bubbling,cancelable
return !element.dispatchEvent(evt);
}
}