Paper.js: why does tool.onMouseDown not trigger? - javascript

I'm creating a web application that uses Paper.js to manipulate vectors that are imported through SVG. Up until recently, everything worked. Then, all of a sudden, the onMouseDown event stopped getting triggered, even though onKeyDown and onMouseMove trigger just fine.
I create an instance of a Tool object like this:
paper.setup("canvas");
var tool = new paper.Tool();
tool.activate();
Then I bind events later in the code like this:
tool.onMouseDown = function (event) {
console.log("Mouse down triggered!");
}
tool.onMouseMove = function (event) {
console.log("Mouse move triggered!");
};
For some reason the latter does trigger, but the former does not. Other events like onKeyDown and onKeyUp trigger as well, but onMouseDown and onMouseDrag don't do anything. Does anyone have an idea what's causing this? I'm also using jQuery and jQueryUI, maybe that causes some kind of interference?

I had the same effect. It was caused by an overlaying html element eating up the click events. Add
style="pointer-events:none;"
to the elements html definition and then both events should trigger you paper.js object.
The full line reads:
<div id="colorizerOptions" style="pointer-events:none;"></div>

Related

jQuery trigger(clickEvent) behaves differently to clicking the item

I have a HTML5 canvas element, which the user can interact with, it has an onclick handler that just catches the event and stops it, as follows:
this.canvas.onclick = function(e){
$("#mycanvas")[0].focus();
e.stopPropagation();
e.bubbles = false;
};
I have an onclick handler on the document, as follows:
document.onclick = function(e){
//do something I only want done when the canvas is NOT clicked
}
When I click on the canvas, as expected the document.onclick handler is not fired. However, I also need to be able to trigger the click programatically, for this I am using jQuery:
$("#mycanvas").trigger($.Event(savedClickEvent));
when this code runs, the document.onclick handler is triggered. From looking at the jQuery.trigger code, it seems to be wanting not only stopPropagation(), but also preventDefault().
Why is there a discrepancy between the requirement of preventDefault() on click, and on trigger? Is this a bug in jQuery that I need to workaround, or should I not be calling stopPropagation() without preventDefault() - my understanding of these two functions is that they do fairly different things.
Bonus question: Why does $("#mycanvas")[0].click return function(){[native code]}? I suspect this is what is causing the problem.
Edit:
It seems jQuery doesn't support inline event handlers. Using addEventListener fixed the problem for click but not for mouseup - I ended up getting around this issue by not using jQuery at all, instead I used document.getElementById("mycanvas").dispatchEvent(savedEvent). I left this open in case I get a meaningful response to the bug report (https://github.com/jquery/jquery/issues/3660#issuecomment-299667529)

Method for triggering certain click event using .trigger() but suppressing all others

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.

List jQuery elements listening for a trigger.

How do I find out what elements on a page are listening for a jQuery event?
I'm trying to write a script to move the tiles on the site: http://threesjs.com/. I tried to send a keypress event for the arrow keys, but it didn't work:
$('*').trigger(jQuery.Event('keypress',{which:$.ui.keyCode.UP}));
and:
var e=jQuery.Event('keypress');
e.which=39;
$('*').trigger(e);
The tiles move when the arrow keys on the keyboard are pressed, and I should be sending this keypress to all elements, but the board doesn't react when executing this in the console. I think the best way to trigger board movement is to find out which element is listening for the key down event.
A better way than manually triggering jQuery events is just to pull out the handlers into callable functions. Something like this:
function handleArrow(direction){
// code here
}
// handlers for the keypress
$(document).on('keypress',function(){
var direction = /*find out which key*/;
handleArrow(direction);
});
// Manual trigger
handleArrow('left');
(insert standard globals and namespacing warning here) :P

Using mousedown event on mobile without jQuery mobile?

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.

Properly bind Javascript events

I am looking for the most proper and efficient way to bind Javascript events; particularly the onload event (I would like the event to occur after both the page AND all elements such as images are loaded). I know there are simple ways to do this in jQuery but I would like the more efficient raw Javascript method.
There are two different ways to do it. Only one will work; which one depends on the browser. Here's a utility method that uses both:
function bindEvent(element, type, handler) {
if(element.addEventListener) {
element.addEventListener(type, handler, false);
} else {
element.attachEvent('on'+type, handler);
}
}
In your case:
bindEvent(window, 'load', function() {
// all elements such as images are loaded here
});
I know you did only ask about how to bind events. But Ooooo boy the fun doesn't end there. There's a lot more to getting this right cross-browser than just the initial binding.
#d.'s answer will suffice just fine for the specific case of the load event of window you're looking for. But it may give novice readers of your code a false sense of "getting it right". Just because you bound the event doesn't mean you took care to normalize it. You may be better of just fixing window.onload:
window.onload = (function(onload) {
return function(event) {
onload && onload(event);
// now do your thing here
}
}(window.onload))
But for the general case of event binding #d.'s answer is so far from satisfying as to be frustrating. About the only thing it does right is use feature-detection as opposed to browser detection.
Not to go on too much of a rant here but JavaScript event binding is probably the #1 reason to go with a JavaScript library. I don't care which one it is, other people have fixed this problem over and over and over again. Here're the issues in a home-brewed implementation once inside your handler function:
How do I get a hold of the event object itself?
How do I prevent the default action of the event (eg clicking on a link but not navigating)
Why does this point to the window object all the time?
(Re mouse events) What are the x/y coords of the event?
Which mouse button triggered the event?
Was it a Ctrl-click or just a regular click?
What element was the event actually triggered on?
What element is the event going to? (ie relatedTarget, say for blur)
How do I cancel the bubbling of the event up through its parent DOM?
(Re event bubbling) what element is actually receiving the event now? (ie currentTarget)
Why can't I get the freaking char code from this keydown event?
Why is my page leaking memory when I add all these event handlers?? Argh!
Why can't I Unbind this anonymous function I bound earlier?
And the list goes on...
The only good reason to roll your own in these days is for learning. And that's fine. Still, read PPK's Intro to browser events. Look at the jQuery source. Look at the Prototype source. You won't regret it.
Something like that
window.onload = (function(onload) {
return function(event) {
onload && onload(event);
$(".loading-overlay .spinner").fadeOut(300),
$(".loading-overlay").fadeOut(300);
$("body").css({
overflow: "auto",
height: "auto",
position: "relative"
})
}
}(window.onload));
window.onload = function() {
// ...
};

Categories