Firefox: trigger autoscroll programmatically - javascript

I want to listen for a middle-button press (mousedown event) and conditionally either take an action (not relevant what specifically) or revert to the default, which would be to enter "autoscroll mode" with the autoscroll icon displayed until the user releases the middle button.
My code listens for the mousedown event and checks whether the pressed button is the middle button. Then determines whether to take the default action, and if not, prevents the default action by calling event.preventDefault. The problem is that there is a delay between the actual mouse press and executing the action, so when the user presses the button and holds it, the action is not taken immediately. Instead, mouse movement is observed for a short amount of time, and based on this movement, either a custom action is taken after the button is released, or the default action for mousedown is taken immediately. In the case the default action is taken, I want Firefox to behave as if the user pressed the middle button, eg. to start autoscroll. This is not happening, since the browser chrome does not react to synthetic events (events produced programmatically).
How do I go about triggering autoscroll in Firefox programmatically? Is there a way to change the behavior (eg. to make autoscroll listen for synthetic events) using userChrome.css?
var elem = document.getElementById('watchedElement')
var active = false
elem.onmousedown = function(event) {
if (event.button !== MIDDLE_BUTTON || active) { return }
event.preventDefault()
startParams = getStartParams()
active = true
}
elem.onmousemove = function(event) {
if (!active) { return }
if (shouldTakeDefaultAction()) {
elem.dispatchEvent(new MouseEvent('mousedown',
{'button': MIDDLE_BUTTON, 'which': MIDDLE_BUTTON}))
active = false
}
}
elem.onmouseup = function(event) {
if (event.button !== MIDDLE_BUTTON || !active) { return }
event.preventDefault()
takeCustomAction()
active = false
}
EDIT: More information
I have made a Greasemonkey plugin for YouTube, which does this:
when I click the middle button (without dragging), toggles fullscreen mode
when I drag left / right with the middle button pressed, it seeks in the video backward / forward
when I drag up / down, I want scrolling
So the default action has to be prevented, and when the mouse has been dragged over a certain distance, we can decide whether to seek or scroll. The angle of the trajectory is computed and evaluated once a certain amount of pixels has been "traveled" by the drag. If the drag is vertical, then I want to activate scrolling, otherwise wait for the button to be released and then seek. The video is sought only after the button is released, eg. no "live preview" while dragging.

As far as I know, there's no way to trigger Firefox's autoscroll mode from within JavaScript. However, it apparently is possible to interrupt it while it's active, e.g. by calling window.scrollTo().
Also, dragging the mouse cursor horizontally while autoscroll mode is active does nothing if the page doesn't in fact scroll horizontally. Most web pages don't.
Based on those observations, here's (a sketch of) an alternative solution:
Let the middle click propagate and trigger its default action, enabling autoscroll mode.
Do your horizontal drag detection as you normally do.
If you detect a horizontal drag, call window.scrollTo(window.scrollX, window.scrollY), which will turn off Firefox's autoscroll mode, and then proceed with the video seek.
The main disadvantages I see with this method are a) that the autoscroll indicator will be briefly visible while you're detecting the drag direction, and b) that if the user drags the mouse slightly diagonally, they may still end up scrolling the page a few pixels up or down. (This probably depends on how careful and coordinated your users are. The autoscroll feature itself seems to have a small built-in "dead zone", such that you need to move the mouse cursor up or down by at least a dozen or so pixels before the page actually starts scrolling vertically.)
The former seems like an unavoidable but minor interface quirk; your users might not even really notice it. To mitigate the latter issue, you might want to save the values of window.scrollX and window.scrollY in step 1, and use the saved values in step 3, thereby snapping the page back to the scroll position it had before the start of the drag. This might still result in an annoying "jerk" as the page scrolls a bit and then snaps back, but it may be less annoying than having the page move permanently down.

Related

Disable scrolling but capture scrolling data with JavaScript?

I'm trying to prevent default scrolling behavior while still determining the number of pixels a user has attempted to scroll.
My objective is (at some vertical position on my page) to fix a navigation element to the top of the screen and hijack the scroll/swipe event to pull down a mobile menu when the user scrolls back up (so moving said element up and down by n pixels depending on how many pixels the user tries to scroll).
I am aware of the UX/accessibility concerns insofar as blocking native behavior, but the suits want what the suits want.
So far I have:
$('body').on({
'mousewheel' : function(e) {
e.preventDefault();
e.stopPropagation();
}
});
but am stumped as to how to access the number of pixels scrolled (since element/scroll offsets are no longer a guide).
Edit: Please note that this question is specifically asking for information regarding mouse/scroll actions while scrolling is blocked. Don't think this has been appropriately marked as duplicate.
This is browser-depended because of the mousewheel event you are using. This is because it is non-standard. Don't use it!
In Chrome (43.0) you get different properties with different values:
e.originalEvent.wheelDelta: -120
e.originalEvent.wheelDeltaY: -120
e.originalEvent.deltaY: 100
In IE (11.0), you can get only one property:
e.originalEvent.wheelDelta: -120
In Firefox (38.0.5), the capturing of the mousewheel event doesn't work at all.
Solution:
Use the wheel event (MDN reference). This event has the e.originalEvent.deltaY property in all browsers.
Before cancelling event propagation take the deltaY out of the original event like this
$('body').on({
'wheel' : function(e) {
console.log(e.originalEvent.deltaY);
e.preventDefault();
e.stopPropagation();
}
});

How to identify REAL mouse movement when entering fullscreen mode

I have the problem, that I need to know if the user actually moved his mouse for real when entering fullscreen, or if it just is a programatically side effect of entering the fullscreen.
Because, when entering fullscreen, the mouse Y coordinates change automatically because the mouse moves upwards on the absolute screen position (since the top navigation of the browser disappears). And since every browser brings a notification in fullscreen mode, this very notification triggers a mousemove event.
So, this makes it very painful to find out, whether the user acually move the mouse, or not.
Is there a solution to identify REAL mouse movement?
$(document).on('mousemove', function(event){
/* gets also triggered when just entering fullscreen,
but without actual movement of the physical mouse..
how can this be identified/ignored?
*/
});
JS Fiddle
What I've tried so far
I tried already relativating the mouse position by using something like window.screen.top - but this seems not to be implemented yet by any browser so far.
I don't think there's anything formally implemented as yet to detect full screen. There's a fullscreenchange as part of the Fullscreen API but it's still experimental and requires vendor-specific prefixes.
So, basically you'll have to get around that limitation with some tricks, like intersecting the resize event and skipping whatever logic you are running on mousemove. Here's an example...
var resizing = false;
$(document).on('mousemove', function(event){
if(resizing == false){
$('p').text(event.pageX + ':' + event.pageY);
console.log("moving");
}
});
$(window).resize(function(){
resizing = true;
setTimeout(function(){
resizing = false;
}, 4000);
});
This example simply defines a flag that determines whether the window is resizing or not, if resizing the onmousemove logic is skipped. Particularly I hate to use setTimeout with an arbitrary time to switch off the resizing flag, but if your requirements are not so strict it can get the job done beautifully
Why don't you incorporate a delay (for example 0.5 seconds) where you ignore all mouse inputs. After the delay, any mouse movements are likely to be from the user...
I solved it now by saving the mouse coordinates, and check if they change - while I force one mousemove event after fullscreen in order to update the coordinates once.
$(document).on('mousemove', function(event){
if(event.pageX == $(this).data('mouseX') && event.pageY == $(this).data('mouseY'))
return;
$(this)
.data('mouseX', event.pageX)
.data('mouseY', event.pageY)
;
});
$(document).mousemove();

Any options for position during webkit-overflow-scrolling: touch event

I have a div with webkit-overflow-scrolling set to touch. On iOS this then gives me an updated position during the touchmove event, but once the user lets go this event ends and a final call to touchend is made before events all stop, but the div continues to momentum scroll.
This is the behaviour I want, but I also want to update the page during this momentum scrolling.
I trigger a call to requestAnimationFrame when the touchend event happens, and I can loop this while the momentum scroll occurs. But when I get DOM information, it's frozen until after the mometnum scroll ends.
I've tried using both the scroll position of the scrolling element and elementFromPoint, but both just have the position the scrolled div was in at the time touchend was triggered, and don't update until the momentum scroll ends.
Does anyone know of any way to get real time DOM information on iOS (6+, not worried about 5)
Here's some code I'm using:
var glideStart;
var bird_scanner = document.getElementById('bird-scanner');
bird_scanner.addEventListener('touchend',function()
{
glideStart = null;
requestAnimationFrame(glide);
});
function glide(timestamp)
{
// if we need to reset the timestamp
if( glideStart === null )
{
glideStart = timestamp;
}
// determine if we've moved
var bird_scanner = document.getElementById('bird-scanner');
console.log( document.elementFromPoint(337,568) );
// calculate progress (keep running for a very long time so we see what happens when momentum ends)
var progress = timestamp - glideStart;
if( progress < 10000 )
{
requestAnimationFrame(App.Controller.bird.glide);
}
}
Update
After a lot of attempts at this, I think it really is impossible without using some library to try and mimic the momentum scroll instead of using the built in option (something I find never really gives as smooth results). Apple are clearly very worried about things interfering with their momentum scroll animation and preventing it rendering properly.
I ended up removing the momentum scroll and just detecting swipes and moving through a bunch of elements at once when that's triggered.
I did notice some particularly strange behaviour. When I had webkit-overflow-scrolling: touch set on an element that was scrolling the page up/down and added a setTimeout(some_func,0) to the touchend event, the function wasn't triggered until the momentum scroll ended. When I tried the same thing on a scroll going left/right it triggered the function straight away. No clue why this happens, decided it must just be some strange webkit quirk.

With javascript / query, is there a way to just "nudge" or "flick" the scroll so it's interruptible by the user (better description within)

I want some js to automatically scroll just a slight bit down the page, however I also want this scroll to be interruptible by the user.
When using jquery to auto scroll, when you animate the scroll with .animate and then the user starts scrolling while the animation scroll is still going they interact with each other and create a strange jumping effect.
Is there a way to make so when the user scroll during a javascript scroll it just stop the javascript scroll?
It can't be done since you can't know if the end-user scrolled or you scrolled the page via javascript.
A scroll event is sent whenever the element's scroll position changes, regardless of the cause. A mouse click or drag on the scroll bar, dragging inside the element, pressing the arrow keys, or using the mouse's scroll wheel could cause this event.
Docs
What I tried to do but failed because the above:
// callback for the scroll event
$(document.body).scroll(function(){
// Stop the scrolling!
$('html, body').stop();
});
A not working demo...
The other users answer is actually incorrect, it is possible and has been answered before:
How can I differentiate a manual scroll (via mousewheel/scrollbar) from a Javascript/jQuery scroll?
Check the answer
"$('body,html').bind('scroll mousedown wheel DOMMouseScroll mousewheel keyup', function(e){
if ( e.which > 0 || e.type == "mousedown" || e.type == "mousewheel"){
$("html,body").stop();
}
})"
I've updated the previous users JS Fiddle to work with the proposed solution and it works perfectly.
http://jsfiddle.net/Lwvba/7/

How can I react when a user touches an HTML element on an iPhone?

I'm displaying some HTML content in my iPhone app using a UIWebView. I have an image link, and I want it to change when the user touches it - at the moment the user puts a finger on the screen, rather than waiting until they lift their finger back off.
What CSS or JavaScript concept could accomplish this? I've looked at the hover and active states in CSS, but they don't seem to be what I'm after: hover relates to touch-up rather than touch-down, while active seems to have no effect at all.
You could try this.
I think it should be what you are looking for!
http://articles.sitepoint.com/article/iphone-development-12-tips/2
8: Touch Events
Of course, you use your iPhone with a
finger instead of a mouse; rather than
clicking, you tap. What’s more, you
can use several fingers to touch and
tap. On the iPhone, mouse events are
replaced by touch events. They are:
touchstart
touchend
touchmove
touchcancel (when the system cancels the touch)
When you subscribe to any of those
events, your event listener will
receive an event object. The event
object has some important properties,
such as:
touches — a collection of touch objects, one for each finger that
touches the screen. The touch objects
have, for example, pageX and pageY
properties containing the coordinates
of the touch within the page.
targetTouches — works like touches, but only registers touches on
a target element as opposed to the
whole page.
The next example is a simple
implementation of drag and drop. Let’s
put a box on a blank page and drag it
around. All you need to do is
subscribe to the touchmove event and
update the position of the box as the
finger moves around, like so:
window.addEventListener('load', function() {
var b = document.getElementById('box'),
xbox = b.offsetWidth / 2, // half the box width
ybox = b.offsetHeight / 2, // half the box height
bstyle = b.style; // cached access to the style object
b.addEventListener('touchmove', function(event) {
event.preventDefault(); // the default behaviour is scrolling
bstyle.left = event.targetTouches[0].pageX - xbox + 'px';
bstyle.top = event.targetTouches[0].pageY - ybox + 'px';
}, false);
}, false);
The touchmove event listener first cancels the default behavior of the finger move—otherwise Safari will scroll the page. The collection event.targetTouches contains a list of data for each finger currently on the target div element.
We only care about one finger, so we use event.targetTouches[0]. Then pageX gives us the X coordinate of the finger. From this value we subtract half the width of the div so that the finger stays in the center of the box.
Hope it helps!
Try the Javascript "onMouseDown", hopefully the mobile Safari will fire the event.
Link

Categories

Resources