Prevent page scrolling when mouse is over one particular div - javascript

My question is similiar to this How to prevent page scrolling when scrolling a DIV element? but I'm wondering if there is an approach with css and/or react that does not require jQuery.
I want to disable page scrolling on a mouseWheel event when the cursor is over one particular div.
The div is a graph which zooms on a mouseWheel event, and is rendered by a React component.
I've tried e.preventDefault however chrome tells me
Unable to preventDefault inside passive event listener due to target being treated as passive
Can anyone help? Thanks in advance.
EDIT: Found a simple solution for anyone looking.
changeScroll(){
let style = document.body.style.overflow
document.body.style.overflow = (style === 'hidden') ? 'auto':'hidden'
}
<div
onMouseEnter={this.changeScroll}
onMouseLeave={this.changeScroll} />
<ReactComponent/>
</div>

Thanks! I was looking for a current answer for managing it.
My ReactJS solution was to add and remove the event when onMouseEnter/Leave is detected. Additionally, with the use of passive, taken from this answer link.
Principal component:
<Wrapper
onWheel={this.handleScroll}
onMouseEnter={this.disableScroll}
onMouseLeave={this.enableScroll}
> ...</Wrapper>
handleScroll():
public handleScroll = (event) => {
if (event.deltaY > 0) {
this.decreaseValue()
} else {
this.increaseValue()
}
}
enableScroll():
public enableScroll = () => {
document.removeEventListener('wheel', this.preventDefault, false)
}
disableScroll():
public disableScroll = () => {
document.addEventListener('wheel', this.preventDefault, {
passive: false,
})
}
preventdefault():
public preventDefault(e: any) {
e = e || window.event
if (e.preventDefault) {
e.preventDefault()
}
e.returnValue = false
}

Related

WKWebView - prevent automatic scrolling triggered by user text selection

When a user performs a tap and hold gesture to select a word and then drags their finger towards either the top or bottom edges of the screen, the page automatically scrolls in order to accommodate the selection.
here is a short clip demonstrating it
I would like to prevent this behavior inside a WKWebView.
Here is what I have tried so far:
in a bridge.js file which is accessible to the webview:
var shouldAllowScrolling = true;
document.addEventListener('selectionchange', e => {
shouldAllowScrolling = getSelectedText().length === 0;
window.webkit.messageHandlers.selectionChangeHandler.postMessage(
{
shouldAllowScrolling: shouldAllowScrolling
});
console.log('allow scrolling = ', shouldAllowScrolling);
});
and then in a WKScriptMessageHandler implementation:
public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage)
{
switch message.name
{
case "selectionChangeHandler":
let params = paramsDictionary(fromMessageBody: message.body)
let shouldEnableScrolling = params["shouldAllowScrolling"] as? Bool ?? true
cell?.webView.scrollView.isScrollEnabled = shouldEnableScrolling
cell?.webView.scrollView.isUserInteractionEnabled = shouldEnableScrolling // not together with the line above
default:
fatalError("\(#function): received undefined message handler name: \(message.name)")
}
}
Similarly, I have tried calling the preventDefault() function directly in the javascript file for a bunch of events, namely scroll and touchmove, like so:
document.addEventListener('touchmove', e => {
if (!shouldAllowScrolling) {
e.preventDefault()
}
}, {passive: false});
both methods successfully prevent scrolling when some text is selected but do not override the behavior described at the very top of my question.
I can accept solutions in either Swift and JavaScript or a mix of both.
I ended up solving this problem by saving the last scroll position and scrolling to it when appropriate, like so:
var shouldAllowScrolling = true;
var lastSavedScrollLeft = 0;
var lastSavedScrollTop = 0;
function saveScrollPosition() {
lastSavedScrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
lastSavedScrollTop = window.pageYOffset || document.documentElement.scrollTop;
}
document.addEventListener('touchstart', e => {
saveScrollPosition();
});
document.addEventListener('touchend', () => {
// enable scrolling when the user lifts their finger, to allow scrolling while text selection is still present
shouldAllowScrolling = true;
});
document.addEventListener('scroll', e => {
if (!shouldAllowScrolling) {
window.scrollTo(lastSavedScrollLeft, lastSavedScrollTop);
}
});
document.addEventListener('selectionchange', e => {
shouldAllowScrolling = getSelectedText().length === 0;
});
If someone can offer a more elegant solution that prevents the scrolling entirely ill be happy to accept it.
EDIT:
this solution may cause light shaking/jittering.
that can be solved by disabling the scroll natively while shouldAllowScrolling is set to false, like so:
webView.scrollView.isScrollEnabled = false

How to stop scrolling the previous page?

I am implementing a website and want to stop users go to the previous page when they scroll with two fingers on their touchpad. I tried to add scroll event and stop propagation but it doesn't work.
document.addEventListener('scroll', (e) => {e.stopPropagation()});
document.addEventListener('touchstart', (e) => {e.stopPropagation()});
document.addEventListener('touchend', (e) => {e.stopPropagation()});
Is there any way to solve this issue?
I have tried to disable touch-action in css but it still doesn't work:
touch-action: none;
I use below code to solve the issue:
export const disableScroll = () => {
windowOnweel = window.onwheel;
window.onwheel = e => {
e = e || window.event;
if (e.preventDefault) e.preventDefault();
e.returnValue = false;
};
};
but you need to remember to restore the onwheel method when you don't need that.

Is there any way to detect middle click in React JS?

I am trying to find a way to detect middle click event in React JS but so far haven't succeeded in doing so.
In Chrome React's Synthetic Click event does show the button clicked ->
mouseClickEvent.button === 0 // Left
mouseClickEvent.button === 1 // Middle but it does not execute the code at all
mouseClickEvent.button === 2 // Right (There is also onContextMenu with event.preventDefault() )
Please share your views.
If you are using a stateless component:
JS
const mouseDownHandler = ( event ) => {
if( event.button === 1 ) {
// do something on middle mouse button click
}
}
JSX
<div onMouseDown={mouseDownHandler}>Click me</div>
Hope this helps.
You can add a mouseDown event and then detect the middle button click like:
handleMouseDown = (event) => {
if(event.button === 1) {
// do something on middle mouse button click
}
}
You code might look like:
class App extends React.Component {
constructor() {
super();
this.onMouseDown = this.onMouseDown.bind(this);
}
componentDidMount() {
document.addEventListener('mousedown', this.onMouseDown);
}
componentWillUnmount() {
document.removeEventListener('mousedown', this.onMouseDown);
}
onMouseDown(event) {
if (event.button === 1) {
// do something on middle mouse button click
}
}
render() {
// ...
}
}
You can find more information on MouseEvent.button here:
https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
Be careful. Using mousedown won't always get you the behavior you want. A "click" is both a mousedown and a mouseup where the x and y values haven't changed. Ideally, your solution would store the x and y values on a mousedown and when mouseup occurs, you would measure to make sure they're in the same spot.
Even better than mousedown would be pointerdown. This configures compatibility with "touch" and "pen" events as well as "mouse" events. I highly recommend this method if pointer events are compatible with your app's compatible browsers.
The modern way of doing it is through the onAuxClick event:
import Card from 'react-bootstrap/Card';
import React, { Component } from 'react';
export class MyComponent extends Component {
onAuxClick(event) {
if (event.button === 1) {
// Middle mouse button has been clicked! Do what you will with it...
}
}
render() {
return (
<Card onAuxClick={this.onAuxClick.bind(this)}>
</Card>
);
}
You can use React Synthetic event as described below
<div tabIndex={1} onMouseDown={event => { console.log(event)}}>
Click me
</div>
You can keep onClick. In React, you have access to nativeEvent property from where you can read which button was pressed:
const clickHandler = (evt) => {
if (e.nativeEvent.button === 1) {
...
}
}
return (
<a onClick={clickHandler}>test</a>
)

evt.preventDefault is not working in IE and Edge on mouse move event, even tried evt.returnValue = false; but didn't work to stop propagation

I have a re sizable div. While trying to resize it the whole page is getting selected with blue color even though I didn't intend to in iE and Edge. I have tried many solutions shown on web but nothing worked. Below is my code. I am unable to prevent default action by event on mouse move. I am listening on ownerDocument for mouse move event.
Below code is working as expected in chrome and mozilla
I have seen in console by inspecting in evt variable, before stop propagation prevent default is true, after stop propagation prevent default is false. Same as google chromes behavior but still dont get why is whole page getting selected
React Code:
<div className="resizer"
tabIndex={-1}
onMouseDown={this.MouseDown}
/>
private MouseDown(evt: any) {
this.viewState.resizing = true;
const {ownerDocument} = ReactDOM.findDOMNode(this);
ownerDocument.addEventListener('mousemove', this.MouseMove);
ownerDocument.addEventListener('mouseup', this.MouseUp);
this.setState(this.viewState);
}
private MouseMove(evt) {
this.viewState.width = width;
this.viewState.height = height;
if (evt.preventDefault) {
evt.returnValue = false;
evt.preventDefault();
}
else {
evt.cancelBubble = true;
}
this.setState(this.viewState);
}
Instead of making evt.preventDefault(); in mouse move make it in mousedown/Click event itself
private MouseDown(evt: any) {
this.viewState.resizing = true;
const {ownerDocument} = ReactDOM.findDOMNode(this);
evt.preventDefault();
evt.stopPropagation();
ownerDocument.addEventListener('mousemove', this.MouseMove);
ownerDocument.addEventListener('mouseup', this.MouseUp);
this.setState(this.viewState);
}
If the issue is that the page gets selected with blue color, there is another approach to prevent selection.
Try this :
<body onselectstart="return false">
Try this pattern:
if (event.preventDefault){
event.preventDefault();
}
else {
event.returnValue = false;
}

Disable scrolling in an iPhone web application?

Is there any way to completely disable web page scrolling in an iPhone web app? I've tried numerous things posted on google, but none seem to work.
Here's my current header setup:
<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=no;"/>
<meta name="apple-mobile-web-app-capable" content="yes"/>
document.body.addEventListener('touchmove', function(e){ e.preventDefault(); });
doesn't seem to work.
Change to the touchstart event instead of touchmove. Under One Finger Events it says that no events are sent during a pan, so touchmove may be too late.
I added the listener to document, not body.
Example:
document.ontouchstart = function(e){
e.preventDefault();
}
document.addEventListener('touchstart', function (e) {
e.preventDefault();
});
Do not use the ontouchmove property to register the event handler as you are running at risk of overwriting an existing event handler(s). Use addEventListener instead (see the note about IE on the MDN page).
Beware that preventing default for the touchstart event on the window or document will disable scrolling of the descending areas.
To prevent the scrolling of the document but leave all the other events intact prevent default for the first touchmove event following touchstart:
var firstMove;
window.addEventListener('touchstart', function (e) {
firstMove = true;
});
window.addEventListener('touchmove', function (e) {
if (firstMove) {
e.preventDefault();
firstMove = false;
}
});
The reason this works is that mobile Safari is using the first move to determine if body of the document is being scrolled. I have realised this while devising a more sophisticated solution.
In case this would ever stop working, the more sophisticated solution is to inspect the touchTarget element and its parents and make a map of directions that can be scrolled to. Then use the first touchmove event to detect the scroll direction and see if it is going to scroll the document or the target element (or either of the target element parents):
var touchTarget,
touchScreenX,
touchScreenY,
conditionParentUntilTrue,
disableScroll,
scrollMap;
conditionParentUntilTrue = function (element, condition) {
var outcome;
if (element === document.body) {
return false;
}
outcome = condition(element);
if (outcome) {
return true;
} else {
return conditionParentUntilTrue(element.parentNode, condition);
}
};
window.addEventListener('touchstart', function (e) {
touchTarget = e.targetTouches[0].target;
// a boolean map indicating if the element (or either of element parents, excluding the document.body) can be scrolled to the X direction.
scrollMap = {}
scrollMap.left = conditionParentUntilTrue(touchTarget, function (element) {
return element.scrollLeft > 0;
});
scrollMap.top = conditionParentUntilTrue(touchTarget, function (element) {
return element.scrollTop > 0;
});
scrollMap.right = conditionParentUntilTrue(touchTarget, function (element) {
return element.scrollWidth > element.clientWidth &&
element.scrollWidth - element.clientWidth > element.scrollLeft;
});
scrollMap.bottom =conditionParentUntilTrue(touchTarget, function (element) {
return element.scrollHeight > element.clientHeight &&
element.scrollHeight - element.clientHeight > element.scrollTop;
});
touchScreenX = e.targetTouches[0].screenX;
touchScreenY = e.targetTouches[0].screenY;
disableScroll = false;
});
window.addEventListener('touchmove', function (e) {
var moveScreenX,
moveScreenY;
if (disableScroll) {
e.preventDefault();
return;
}
moveScreenX = e.targetTouches[0].screenX;
moveScreenY = e.targetTouches[0].screenY;
if (
moveScreenX > touchScreenX && scrollMap.left ||
moveScreenY < touchScreenY && scrollMap.bottom ||
moveScreenX < touchScreenX && scrollMap.right ||
moveScreenY > touchScreenY && scrollMap.top
) {
// You are scrolling either the element or its parent.
// This will not affect document.body scroll.
} else {
// This will affect document.body scroll.
e.preventDefault();
disableScroll = true;
}
});
The reason this works is that mobile Safari is using the first touch move to determine if the document body is being scrolled or the element (or either of the target element parents) and sticks to this decision.
If you are using jquery 1.7+, this works well:
$("donotscrollme").on("touchmove", false);
This should work. No more gray areas at the top or bottom:)
<script type="text/javascript">
function blockMove() {
event.preventDefault() ;
}
</script>
<body ontouchmove="blockMove()">
But this also disables any scrollable areas. If you want to keep your scrollable areas and still remove the rubber band effect at the top and bottom, see here: https://github.com/joelambert/ScrollFix.
Disable:
document.ontouchstart = function(e){ e.preventDefault(); }
Enable:
document.ontouchstart = function(e){ return true; }
'self.webView.scrollView.bounces = NO;'
Just add this one line in the 'viewDidLoad' of the mainViewController.m file of your application. you can open it in the Xcode and add it .
This should make the page without any rubberband bounces still enabling the scroll in the app view.
The page has to be launched from the Home screen for the meta tag to work.
document.ontouchmove = function(e){
e.preventDefault();
}
is actually the best choice i found out it allows you to still be able to tap on input fields as well as drag things using jQuery UI draggable but it stops the page from scrolling.
I tried above answers and particularly Gajus's but none works. Finally I found the answer below to solve the problem such that only the main body doesn't scroll but other scrolling sections inside my web app all work fine.
Simply set position fixed for your body:
body {
height: 100%;
overflow: hidden;
width: 100%;
position: fixed;
}

Categories

Resources