My web app has keyboard shortcuts that involve holding the space bar. The problem is that the mouse cursor disappears when the space bar is held. I think what's happening is that the browser is trying to scroll down (even though in my case there's never anything to scroll down to). If the user moves the mouse cursor while holding the space bar, the cursor flickers into view, only to disappear again when the mouse has stopped moving. Once the user releases the space bar, the mouse cursor stay hidden, until the mouse is moved again, after which the cursor stays visible. This happens in Chrome, Safari, Opera (webkit/blink).
Among many things, I've tried the canonical solution of preventDefault() on the event, which doesn't work, regardless of where I listen. Clearly, this is possible, because I've used apps before that employ the space bar to do something other than scroll down.
var html = document.documentElement;
var body = document.getElementsByTagName('body')[0];
document.addEventListener("keydown", function(e) {
console.log("document keydown");
e.preventDefault();
e.stopPropagation();
});
window.addEventListener("keydown", function(e) {
console.log("window keydown");
e.preventDefault();
e.stopPropagation();
});
html.addEventListener("keydown", function(e) {
console.log("html keydown");
e.preventDefault();
e.stopPropagation();
});
body.addEventListener("keydown", function(e) {
console.log("body keydown");
e.preventDefault();
e.stopPropagation();
});
document.addEventListener("keypress", function(e) {
console.log("document keypress");
e.preventDefault();
e.stopPropagation();
});
window.addEventListener("keypress", function(e) {
console.log("window keypress");
e.preventDefault();
e.stopPropagation();
});
html.addEventListener("keypress", function(e) {
console.log("html keypress");
e.preventDefault();
e.stopPropagation();
});
body.addEventListener("keypress", function(e) {
console.log("body keypress");
e.preventDefault();
e.stopPropagation();
});
Note: My app is always exactly 100% of the viewport. There is never any reason to scroll, which is why I am comfortable with the idea of overriding the convention.
Any help very appreciated.
TL/DR: apply overflow: hidden style on document.body.
I just found this unanswered (!!) question here after same problem, but in 5 min found my solution.
Several years of no answer so I'll explain my own situation too since it must be a rare context.
My scenario: Developing full-page graphic app using PIXI, along with overlaid HTML DOM elements. Using Safari on MacBook Pro.
Interest: press spacebar to interact with PIXI content
Approach: use a state tracking variable using keydown and keyup listeners. Press spacebar, in keydown update the cursor style when the event target is the document.body. Likewise revert cursor style in keyup.
Problem: the browser wants to scroll. Even when using event.preventDefault.
I never use spacebar to scroll a page, so I was entirely blind to the existing browser behaviour.
Discovery: Experimenting on other pages, including here on the SO question, I found the cursor ceases to be hidden on spacebar press when at bottom of the page. So I know the browser is considering the body length...
Solution: apply overflow: hidden style on document.body.
Now I know for various applications this may not suffice, but it DOES SOLVE THIS PROBLEM for me. The browser knows there is zero overflow processing on the body, so spacebar is inert for scrolling.
After applying this, overflow elsewhere still works: other divs of the web app can still scroll, with assigned dimensions.
Related
I need to show my pop-up when the mouse leaves the <body>, this identifies an exit intention.
So when my clients are typing their emails, the popup just appears at the exact moment their pointer is over the suggestion and it should not have happened. But it happens because this part is not in the DOM, so it triggers the mouse leave
however, this event is triggered when the mouse is over a native input suggestion on browsers (I tested on Firefox and Chrome).
So, any ideas how can I skip this fake trigger?
document.body.onmouseleave = function(e) {
console.log("mouse leave was trigged")
}
Take a look what is happen:
I've encountered the same issue, solved it by looking at what exactly triggered the mouseleave event. So if it was not an input field from your form, you could proceed and show your popup
$('body').on('mouseleave', function (e) {
if ('INPUT' !== e.target.nodeName) {
// do your stuff
}
});
I have a problem with Jquery hover and click on mobile.. Let me explain!
I have square div and, when the mouse is hover it, a new div appear and follow the mouse. You can even click the square div and if so, a new page is opened. The problem now is that, on mobile, I need two click for the new page to be opened, since the first click is read as "hover".
I tried the
$("#mydiv").on('click touchend', function(e)
Actually it works, but with this, if I want to scroll the page on mobile, and I start the swipe on the square div, the new page is opened, which it shouldn't since I didn't click on the square div, just "passed by".
Try using one of those events
https://github.com/benmajor/jQuery-Touch-Events#4-the-events
$('#mydiv').bind('tap', function(e) {
console.log('User tapped #myDiv');
});
As per documentation:
"The event's target is the same element that received the touchstart event corresponding to the touch point, even if the touch point has moved outside that element."
You can see the documentation of touchend also:
https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
If you start your scroll with the square div then square div touchend event will be fired after the release of that finger even after you move your finger to the other elements.
To solve this problem, you can use these events:
https://github.com/benmajor/jQuery-Touch-Events#4-the-events
If you want to stick with this touchend event then there is a workaround:
Declare a global variable i.e.
var isScroll = false, timer;
Apply touchmove eventhandler on document which will fired for touch devices only, this handler detect whether the document is getting scrolled if yes set the isScroll flag to true that will false after 500ms:
$(document).on("touchmove", function(e) {
isScroll = true;
if(timer) clearTimeout(timer);
timer = setTimeout(function() {
isScroll = false;
}, 800);
})
and insert if condition in your eventHandler:
$("#mydiv").on('click touchend', function(e) {
if(!isScroll) {
//insert your code here;
}
}
I am practicing writing a file drop & upload plugin.
In the 400px * 400px div, where catches the 'drop' event, I've already added this code in the handler:
DropUpload.prototype.drop = function (event) {
...
event.stopPropagation();
event.preventDefault();
...
}
But when I drop a picture file in the div, the browser just open the picture on this page, flushing out the previous script page.
I learned by googling that
$(document).on('dragenter', function (e)
{ e.stopPropagation(); e.preventDefault(); });
$(document).on('dragover', function (e)
{ e.stopPropagation(); e.preventDefault(); });
$(document).on('drop', function (e)
{ e.stopPropagation(); e.preventDefault(); });
can prevent opening the picture. But I think I've already prevent the drop event bubbling up in the div element by adding stopPropogation(), why should I do it again on the document element?
Is it because the drop event enters document first and then reaches div?
Please help me understand this.
I am answering my own question since I made a mistake. I hope I can save you some time by sharing my mistake.
In order let drop zone handle the drop event, you have to do
event.stopPropagation();
event.preventDefault();
for all 3
this.$element.on('dragenter', this.dragenter);
this.$element.on('dragover', this.dragover);
this.$element.on('drop', this.drop);
events. If you miss any one function for any one of the 3 events, the new drop will cause the browser to load the file that you just drop.
Or you can disable the default event behavior on document element, but doing it on drop zone is more logical and looks less like a hack.
This MDN document explain some details.
Sincerely,
Nick
I need to disable the default iPAD scrolling (via capturing touchmove on the body) but still allow a list on my page to scroll.
I tried:
$('body').on('touchmove', function(e) { e.preventDefault(); });
$('itemList').on('touchmove', function(e) { alert('hi'); e.stopPropagation(); });
But it seems that itemList's touchmove is not being called at all. on the iPAD nothing gets scrolled.
see http://jsfiddle.net/e8dcJ
Any ideas how to solve this ?
Thanks!
maybe don't apply the event to the body, which covers everything. Instead, apply the event to a the various elements you want to prevent scrolling. Alternately, wrap everything in a DIV except the list and then set the position to fixed and add the event.
I use preventDefault() on touchstart on the document to prevent scrolling on a page. Unfortunately this prevents too much. The user can no longer give focus to an input (and open the soft keyboard). Using jQuery focus(), I can give the focus to the input on a touchstart. This opens the soft keyboard on iOS (most of the time), but never on Android.
Background:
I have a webpage that I want to make as much like a mobile app as possible. I'm only really concerned with Android and iOS, but any form factor. I start by making the content in the page exactly the same size as the screen size. This is nice until the user puts their finger on the page. I need to prevent user scrolling. The following code accomplishes this, but in slightly different ways on the two operating systems.
$(document).on('touchstart', function(e) {
e.preventDefault();
});
On iOS, this prevents the elastic scrolling: where a user can reveal a texture behind the webpage by attempting to scroll off the page. The feature is nice for a regular webpage, but in my case it detracts from the UX.
On Android, prevents the revelation of a handy hack that I use to hide the address bar. Android browsers start with the address bar visible, but as the user scrolls down the page, it disappears. To programmatically force the browser hide the address bar, simply add this line of code to your function called at load.
$('html, body').scrollTop(1);
This is a hacky (but also the only) way to tell the android browser that we have scrolled, and the address bar is no longer necessary.
Without the preventDefault on the document, the Android browser will allow scrolling and the address bar can be revealed.
So, both OS's have a good reason to have this preventDefault() called on every touchstart on the document, but it prevents too much. Tapping on an input field does nothing. Using a call to jQuery focus() can help, but only opens the soft keyboard on iOS, not Android.
$('input').on('touchstart', function() {
$(this).focus();
});
How can I prevent the page from scrolling, but use the browser native functionality for giving focus to input fields?
Note:
This code
$(document).on('touchstart', function(e) {
if (e.target.nodeName !== 'INPUT') {
e.preventDefault();
}
});
is flawed because the user can still scroll the page as long as the initial touch originates from within the input field.
I actually solved this problem on another project, forgot about it, and remembered it most of the way through typing this up.
They key is to just do it on touchmove.
$(document).on('touchmove', function(e) {
e.preventDefault();
});
However, preventDefault on touchstart does all kinds of nice things like preventing the image save menu on a gesture enabled slideshow. My projects also include this.
$(document).on('touchstart', function(e) {
if (e.target.nodeName !== 'INPUT') {
e.preventDefault();
}
});
If anyone has some suggestions on additional content or how to reformat this so that it can reach a greater audience that would be great. I haven't ever seen the content I have here all in one place, so I felt that it needed to be on SO.
Combine the two!
// prevent scrolling from outside of input field
$(document).on('touchstart', function(e) {
if (e.target.nodeName !== 'INPUT') {
e.preventDefault();
}
});
// prevent scrolling from within input field
$(document).on('touchmove', function(e) {
if (e.target.nodeName == 'INPUT') {
e.preventDefault();
}
});
This probably isn't perfect either, and I am especially worried that the first function will prevent following links, but I'll leave it to others to do extensive tests.
The simple answer to your question is don't use "preventDefault" instead use pointer-events css property to disable the scrolling on the element that scrolls.
CSS for your inputs:
input {
pointer-events: auto !important;
}
touchstart event listener:
document.body.addEventListener('touchstart', function(e) {
if (e.target.nodeName === 'INPUT') {
this.style.pointerEvents = 'none';
}
});
You will need to reset the pointer-events when you blur the input.
document.body.pointerEvents = 'auto';
+1 Good Question