I wanted to make a simple temporary "Happy 2017" page on my site, and I've found on Code.pen a JS code for fireworks that looked pretty interesting:
https://codepen.io/chuongdang/pen/yzpDG
The code works wonderfully with a mouse, as the firework explodes exactly where you clicked, but it doesn't work at all with touch commands, making it not interactive at all on touch screens.
I can see that the code calls mousedown, mousemove and mouseup events, but not touchstart and touchend:
if( timerTick >= timerTotal ) {
if( !mousedown ) {
fireworks.push( new Firework( cw / 2, ch, random( 0, cw ), random( 0, ch / 2 ) ) );
timerTick = 0;
}
} else {
timerTick++;
}
if( limiterTick >= limiterTotal ) {
if( mousedown ) {
fireworks.push( new Firework( cw / 2, ch, mx, my ) );
limiterTick = 0;
}
} else {
limiterTick++;
}
I guess that's the problem, but being a complete JS noob I lack the knowledge to make it recognize touch coordinates. Is there a simple way to do so?
This is a suggestion, it may work or not.
Try and associate it to a different event.
At the link you sent (https://codepen.io/chuongdang/pen/yzpDG), there are two event listeners to handle both onmousedown and onmouseup events.
canvas.addEventListener( 'mousedown', function( e ) {
e.preventDefault();
mousedown = true;
});
canvas.addEventListener( 'mouseup', function( e ) {
e.preventDefault();
mousedown = false;
});
Try and replace them by the following code:
canvas.addEventListener( 'click', function( e ) {
e.preventDefault();
mousedown = true;
setTimeout(function() {
mousedown = false;
}, 20);
});
mousedown is just the variable it uses to check if a click is being made. As long as it is true, it will set the target to the fireworks.
So, what the code above does is to associate the whole thing with a different event click (which is sometimes interchangable with a screen tap). When a click is made, it sets mousedown as true, count 20 milisecounds, and them sets mousedown as false again.
I hope this helped! =)
I made this example here: http://jsbin.com/pokahec/edit?html,output
// creates a global "addWheelListener" method
// example: addWheelListener( elem, function( e ) { console.log( e.deltaY ); e.preventDefault(); } );
(function(window,document) {
var prefix = "", _addEventListener, onwheel, support;
// detect event model
if ( window.addEventListener ) {
_addEventListener = "addEventListener";
} else {
_addEventListener = "attachEvent";
prefix = "on";
}
// detect available wheel event
support = "onwheel" in document.createElement("div") ? "wheel" : // Modern browsers support "wheel"
document.onmousewheel !== undefined ? "mousewheel" : // Webkit and IE support at least "mousewheel"
"DOMMouseScroll"; // let's assume that remaining browsers are older Firefox
window.addWheelListener = function( elem, callback, useCapture ) {
_addWheelListener( elem, support, callback, useCapture );
// handle MozMousePixelScroll in older Firefox
if( support == "DOMMouseScroll" ) {
_addWheelListener( elem, "MozMousePixelScroll", callback, useCapture );
}
};
function _addWheelListener( elem, eventName, callback, useCapture ) {
elem[ _addEventListener ]( prefix + eventName, support == "wheel" ? callback : function( originalEvent ) {
!originalEvent && ( originalEvent = window.event );
// create a normalized event object
var event = {
// keep a ref to the original event object
originalEvent: originalEvent,
target: originalEvent.target || originalEvent.srcElement,
type: "wheel",
deltaMode: originalEvent.type == "MozMousePixelScroll" ? 0 : 1,
deltaX: 0,
deltaZ: 0,
preventDefault: function() {
originalEvent.preventDefault ?
originalEvent.preventDefault() :
originalEvent.returnValue = false;
}
};
// calculate deltaY (and deltaX) according to the event
if ( support == "mousewheel" ) {
event.deltaY = - 1/40 * originalEvent.wheelDelta;
// Webkit also support wheelDeltaX
originalEvent.wheelDeltaX && ( event.deltaX = - 1/40 * originalEvent.wheelDeltaX );
} else {
event.deltaY = originalEvent.detail;
}
// it's time to fire the callback
return callback( event );
}, useCapture || false );
}
})(window,document);
You can test in Firefox that scroll event is fired, except when over vimeo iframe ( and I guess any iFrame )
Is there any solution to fire event on iframe ?
PS - I want to use this in a custom scrollbar
This is basically by design. Your code should be completely unaware of what the user does inside an IFRAME (especially one from a different origin like YouTube - this is a part of the web's security architecture, as mandated by the Same Origin Policy.)
Now, even in the cross-origin case browsers can choose to let scrolling affect the frame's ancestor if the frame itself doesn't scroll. This scrolling should happen without any events firing on the top document - see Chrome's behaviour if you scroll to the bottom of this IFRAME and keep scrolling:
http://jsfiddle.net/8cj0dofx/1/
HTML:
<iframe src="data:text/html,<body style='background:grey;height:550px'>Hello" seamless></iframe>
<div style="height:100px">Hello</div>
JS:
document.addEventListener('DOMMouseScroll', function(e){
document.getElementsByTagName('div')[0].firstChild.data += ' ' + e.type
});
document.addEventListener('mousewheel', function(e){
document.getElementsByTagName('div')[0].firstChild.data += ' ' + e.type
});
What you'll see is that when you have scrolled to the end of the IFRAME, the main document will scroll but no events will fire until the mouse is above the parent document.
It looks like it's a bug in Firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=1084121
So there may not be a straightforward way to handle this. But since the action has an effect even if it's not being dispatched, there's a workaround that can be used. It may not work in every situation, but it should cover many cases.
Instead of detecting wheel event, you detect scroll, and use a switch detecting if the mouse is clicked or not. If the window scrolls and the mouse isn't clicked, then it's most likely from the mousewheel. Other cases will be if you trigger it from a script, in which case this can be handled easily also.
One case you won't handle is when the window cannot scroll anymore, then you won't get the event.
It would look like this:
var mouseDown = false;
function handle_wheel() {
if (!mouseDown) {
document.getElementById("debug-textarea").value = document.getElementById("debug-textarea").value + ' wheel';
} else {
document.getElementById("debug-textarea").value = document.getElementById("debug-textarea").value + ' scroll';
}
}
window.onscroll = handle_wheel;
window.onmousedown = function () {
mouseDown = true;
}
window.onmouseup = function () {
mouseDown = false;
}
http://jsfiddle.net/wu9y6yua/4/
I was facing the same problem but with a zoom on scroll feature.
Since it's not possible to capture the mousewheel event without hacks, I think the best option is to place a transparent div over the iframe and add the &autoplay=1 parameter
in the vimeo/youtube url on click.
Good day all.
I'm having some problems with hoverintent.js a jquery plugin that handle the mouseOver events in a different way than normal.
Due to some complications, I can't modifiy anything but the js of this plugin, but I need to make it compliant with touch events and not only with mouseOver and mouseLeave.
after some debugs, I have managed to recognize this part of the code to be the one to modify:
var handleHover = function(e) {
// next three lines copied from jQuery.hover, ignore children onMouseOver/onMouseOut
var p = (e.type == "mouseover" ? e.fromElement : e.toElement) || e.relatedTarget;
while ( p && p != this ) { try { p = p.parentNode; } catch(e) { p = this; } }
if ( p == this ) { return false; }
// copy objects to be passed into t (required for event object to be passed in IE)
var ev = jQuery.extend({},e);
var ob = this;
// cancel hoverIntent timer if it exists
if (ob.hoverIntent_t) { ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); }
// else e.type == "onmouseover"
if (e.type == "mouseover") {
// set "previous" X and Y position based on initial entry point
pX = ev.pageX; pY = ev.pageY;
// update "current" X and Y position based on mousemove
$(ob).bind("mousemove",track);
// start polling interval (self-calling timeout) to compare mouse coordinates over time
if (ob.hoverIntent_s != 1) { ob.hoverIntent_t = setTimeout( function(){compare(ev,ob);} , cfg.interval );}
// else e.type == "onmouseout"
} else {
// unbind expensive mousemove event
$(ob).unbind("mousemove",track);
// if hoverIntent state is true, then call the mouseOut function after the specified delay
if (ob.hoverIntent_s == 1) { ob.hoverIntent_t = setTimeout( function(){delay(ev,ob);} , cfg.timeout );}
}
}
};
// bind the function to the two event listeners
return this.mouseover(handleHover).mouseout(handleHover);
what I've done so far is to make the function working different with mobiles:
var handleHover = function(e) {
isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
if(isMobile){
console.log("Ismobile");
}else{
... Same code as before here ...
}
// bind the function to the two event listeners
return this.mouseover(handleHover).mouseout(handleHover);
and now i'm struck. I would like it to "change" its behavior to handle the touch, and not the mouse over event, so on mobiles I will need to touch the element, instead to hovering on it. May someone give me an help? Am I on the right way? Is it the right way to think of it?
unluckily I have only the possibility to change this file and some few more.
Recently i bumped into several problems with changing hoverIntent.js, and ended up in writing my own plugin: hoverDelay.js (much simpler, and less code). see if you can use it, and modify it to your own needs (and maybe contribute the mobile code to it :-)
I have a canvas which listens for mouse down event... but I wanted to make it more detailed by listening on the buttons and if they were double clicks or not.
I have this:
canvas.addEventListener("mousedown", what_button, false);
Then a function named what_button:
function what_button(e){
//check which button on the mouse
//was it a double click ?
}
Is this possible in JavaScript?
I don't think that the 'mousedown' event is able to anticipate whether or not a second mouse click will occur. You'll have to bind to both 'click' and 'dblclick' and then override the behavior if a double-click occurred...
Inside the handler, the e.button property tells you which button was clicked:
0 => left
1 => middle
2 => right
This works for me:
var dblclick;
var timeout;
$( document ).on( 'click', function ( e ) {
if ( !timeout ) {
timeout = setTimeout( function () {
timeout = 0;
handler( dblclick ? 'dblclick' : 'click' );
dblclick = false;
}, 200 );
}
});
$( document ).on( 'dblclick', function ( e ) {
dblclick = true;
});
// this function receives either 'click' or 'dblclick'
// and performs the corresponding action
function handler ( type ) {
alert( type );
}
Live demo: http://jsfiddle.net/f73tY/1/
I use a delay value of 200. I have found that (at least on my machine) a value of 100 does not detect a double-click.
Use click/dblclick:
var button = document.getElementById('myButton');
button.ondblclick = function() {
doubleClick = true;
};
You can even add an normalClick = false then use that information in your code.
Is there a way to detect if a mouse button is currently down in JavaScript?
I know about the "mousedown" event, but that's not what I need. Some time AFTER the mouse button is pressed, I want to be able to detect if it is still pressed down.
Is this possible?
Regarding Pax' solution: it doesn't work if user clicks more than one button intentionally or accidentally. Don't ask me how I know :-(.
The correct code should be like that:
var mouseDown = 0;
document.body.onmousedown = function() {
++mouseDown;
}
document.body.onmouseup = function() {
--mouseDown;
}
With the test like this:
if(mouseDown){
// crikey! isn't she a beauty?
}
If you want to know what button is pressed, be prepared to make mouseDown an array of counters and count them separately for separate buttons:
// let's pretend that a mouse doesn't have more than 9 buttons
var mouseDown = [0, 0, 0, 0, 0, 0, 0, 0, 0],
mouseDownCount = 0;
document.body.onmousedown = function(evt) {
++mouseDown[evt.button];
++mouseDownCount;
}
document.body.onmouseup = function(evt) {
--mouseDown[evt.button];
--mouseDownCount;
}
Now you can check what buttons were pressed exactly:
if(mouseDownCount){
// alright, let's lift the little bugger up!
for(var i = 0; i < mouseDown.length; ++i){
if(mouseDown[i]){
// we found it right there!
}
}
}
Now be warned that the code above would work only for standard-compliant browsers that pass you a button number starting from 0 and up. IE uses a bit mask of currently pressed buttons:
0 for "nothing is pressed"
1 for left
2 for right
4 for middle
and any combination of above, e.g., 5 for left + middle
So adjust your code accordingly! I leave it as an exercise.
And remember: IE uses a global event object called … "event".
Incidentally IE has a feature useful in your case: when other browsers send "button" only for mouse button events (onclick, onmousedown, and onmouseup), IE sends it with onmousemove too. So you can start listening for onmousemove when you need to know the button state, and check for evt.button as soon as you got it — now you know what mouse buttons were pressed:
// for IE only!
document.body.onmousemove = function(){
if(event.button){
// aha! we caught a feisty little sheila!
}
};
Of course you get nothing if she plays dead and not moving.
Relevant links:
MouseEvent's button (DOM 2)
MSDN's button
Update #1: I don't know why I carried over the document.body-style of code. It will be better to attach event handlers directly to the document.
This is an old question, and the answers here seem to mostly advocate for using mousedown and mouseup to keep track of whether a button is pressed. But as others have pointed out, mouseup will only fire when performed within the browser, which can lead to losing track of the button state.
However, MouseEvent (now) indicates which buttons are currently pushed:
For all modern browsers (including Safari v11.1+ [v11.3+ on iOS]), use MouseEvent.buttons
For Safari < 11.1 (11.3 on iOS), use MouseEvent.which (buttons will be undefined for Safari) Note: which uses different numbers from buttons for Right and Middle clicks.
When registered on document, mousemove will fire immediately as soon as the cursor reenters the browser, so if the user releases outside then the state will be updated as soon as they mouse back inside.
A simple implementation might look like:
var primaryMouseButtonDown = false;
function setPrimaryButtonState(e) {
var flags = e.buttons !== undefined ? e.buttons : e.which;
primaryMouseButtonDown = (flags & 1) === 1;
}
document.addEventListener("mousedown", setPrimaryButtonState);
document.addEventListener("mousemove", setPrimaryButtonState);
document.addEventListener("mouseup", setPrimaryButtonState);
That code tracks the state of the primary mouse button (typically the left), ignoring the state of other mouse buttons.
If more complicated scenarios are required (different buttons/multiple buttons/control keys), check out the MouseEvent docs.
I think the best approach to this is to keep your own record of the mouse button state, as follows:
var mouseDown = 0;
document.body.onmousedown = function() {
mouseDown = 1;
}
document.body.onmouseup = function() {
mouseDown = 0;
}
and then, later in your code:
if (mouseDown == 1) {
// the mouse is down, do what you have to do.
}
the solution isn't good.
one could "mousedown" on the document, then "mouseup" outside the browser, and on this case the browser would still be thinking the mouse is down.
the only good solution is using IE.event object.
I know this is an old post, but I thought the tracking of mouse button using mouse up/down felt a bit clunky, so I found an alternative that may appeal to some.
<style>
div.myDiv:active {
cursor: default;
}
</style>
<script>
function handleMove( div ) {
var style = getComputedStyle( div );
if (style.getPropertyValue('cursor') == 'default')
{
// You're down and moving here!
}
}
</script>
<div class='myDiv' onmousemove='handleMove(this);'>Click and drag me!</div>
The :active selector handles the mouse click much better than mouse up/down, you just need a way of reading that state in the onmousemove event. For that I needed to cheat and relied on the fact that the default cursor is "auto" and I just change it to "default", which is what auto selects by default.
You can use anything in the object that is returned by getComputedStyle that you can use as a flag without upsetting the look of your page e.g. border-color.
I would have liked to set my own user defined style in the :active section, but I couldn't get that to work. It would be better if it's possible.
If you're working within a complex page with existing mouse event handlers, I'd recommend handling the event on capture (instead of bubble). To do this, just set the 3rd parameter of addEventListener to true.
Additionally, you may want to check for event.which to ensure you're handling actual user interaction and not mouse events, e.g. elem.dispatchEvent(new Event('mousedown')).
var isMouseDown = false;
document.addEventListener('mousedown', function(event) {
if ( event.which ) isMouseDown = true;
}, true);
document.addEventListener('mouseup', function(event) {
if ( event.which ) isMouseDown = false;
}, true);
Add the handler to document (or window) instead of document.body is important b/c it ensures that mouseup events outside of the window are still recorded.
The following snippet will attempt to execute the "doStuff" function 2 seconds after the mouseDown event occurs in document.body. If the user lifts up the button, the mouseUp event will occur and cancel the delayed execution.
I'd advise using some method for cross-browser event attachment - setting the mousedown and mouseup properties explicitly was done to simplify the example.
function doStuff() {
// does something when mouse is down in body for longer than 2 seconds
}
var mousedownTimeout;
document.body.onmousedown = function() {
mousedownTimeout = window.setTimeout(doStuff, 2000);
}
document.body.onmouseup = function() {
window.clearTimeout(mousedownTimeout);
}
In case someone else runs into this, you can use .matches with the :active selector:
function mouseDown() {
return document.body.matches(":active");
}
Using the MouseEvent api, to check the pressed button, if any:
// Mouse buttons
document.addEventListener('mousedown', e => console.log(e.buttons))
// Keyboard keys
document.addEventListener('keydown', e => console.log(e.key))
Return:
A number representing one or more buttons. For more than one button
pressed simultaneously, the values are combined (e.g., 3 is primary +
secondary).
0 : No button or un-initialized
1 : Primary button (usually the left button)
2 : Secondary button (usually the right button)
4 : Auxilary button (usually the mouse wheel button or middle button)
8 : 4th button (typically the "Browser Back" button)
16 : 5th button (typically the "Browser Forward" button)
You can combine #Pax and my answers to also get the duration that the mouse has been down for:
var mousedownTimeout,
mousedown = 0;
document.body.onmousedown = function() {
mousedown = 0;
window.clearInterval(mousedownTimeout);
mousedownTimeout = window.setInterval(function() { mousedown += 200 }, 200);
}
document.body.onmouseup = function() {
mousedown = 0;
window.clearInterval(mousedownTimeout);
}
Then later:
if (mousedown >= 2000) {
// do something if the mousebutton has been down for at least 2 seconds
}
You need to handle the MouseDown and MouseUp and set some flag or something to track it "later down the road"... :(
Short and sweet
I'm not sure why none of the previous answers worked for me, but I came up with this solution during a eureka moment. It not only works, but it is also most elegant:
Add to body tag:
onmouseup="down=0;" onmousedown="down=1;"
Then test and execute myfunction() if down equals 1:
onmousemove="if (down==1) myfunction();"
Using jQuery, the following solution handles even the "drag off the page then release case".
$(document).mousedown(function(e) {
mouseDown = true;
}).mouseup(function(e) {
mouseDown = false;
}).mouseleave(function(e) {
mouseDown = false;
});
I don't know how it handles multiple mouse buttons.
If there were a way to start the click outside the window, then bring the mouse into the window, then this would probably not work properly there either.
As said #Jack, when mouseup happens outside of browser window, we are not aware of it...
This code (almost) worked for me:
window.addEventListener('mouseup', mouseUpHandler, false);
window.addEventListener('mousedown', mouseDownHandler, false);
Unfortunately, I won't get the mouseup event in one of those cases:
user simultaneously presses a keyboard key and a mouse button, releases mouse button outside of browser window then releases key.
user presses two mouse buttons simultaneously, releases one mouse button then the other one, both outside of browser window.
var mousedown = 0;
$(function(){
document.onmousedown = function(e){
mousedown = mousedown | getWindowStyleButton(e);
e = e || window.event;
console.log("Button: " + e.button + " Which: " + e.which + " MouseDown: " + mousedown);
}
document.onmouseup = function(e){
mousedown = mousedown ^ getWindowStyleButton(e);
e = e || window.event;
console.log("Button: " + e.button + " Which: " + e.which + " MouseDown: " + mousedown);
}
document.oncontextmenu = function(e){
// to suppress oncontextmenu because it blocks
// a mouseup when two buttons are pressed and
// the right-mouse button is released before
// the other button.
return false;
}
});
function getWindowStyleButton(e){
var button = 0;
if (e) {
if (e.button === 0) button = 1;
else if (e.button === 1) button = 4;
else if (e.button === 2) button = 2;
}else if (window.event){
button = window.event.button;
}
return button;
}
this cross-browser version works fine for me.
Below jQuery example, when mouse is over $('.element'), color is changing depending on which mouse button is pressed.
var clicableArea = {
init: function () {
var self = this;
('.element').mouseover(function (e) {
self.handlemouseClick(e, $(this));
}).mousedown(function (e) {
self.handlemouseClick(e, $(this));
});
},
handlemouseClick: function (e, element) {
if (e.buttons === 1) {//left button
element.css('background', '#f00');
}
if (e.buttons === 2) { //right buttom
element.css('background', 'none');
}
}
};
$(document).ready(function () {
clicableArea.init();
});
Well, you can't check if it's down after the event, but you can check if it's Up... If it's up.. it means that no longer is down :P lol
So the user presses the button down (onMouseDown event) ... and after that, you check if is up (onMouseUp). While it's not up, you can do what you need.