Mousewheel handler for child element and his parent - javascript

I made a jQuery plugin that replaces default scrollbar with my own and handles mousewheeling and dragging the bar events.
When I put the content with my scrollbar into another content with my scrollbar, and then if I use mousewheel on the child content, the parent content wheels as well.
It happens because I bound mousewheel event listener to both child and parent contents, and when my mouse over them both, it triggers both event handlers.
The problem is that I need to wheel only the child content without affecting the parent.
Do you have any tips how to resolve that? Dragging scrollbar event works ok.

You need to stop the propagation of the event. This will stop the event from bubbling up the DOM tree and triggering on parent elements.
http://jsbin.com/yelijelowa/1/edit?js,output
$('body').on('mousewheel', function (e) {
alert('Body scroll');
});
$('.child').on('mousewheel', function (e) {
e.stopPropagation();
alert('Child scroll only');
});
If you comment out the e.stopPropagation(); line you'll note that both alerts fire.
Documentation:
MDN
jQuery API

Related

Layered elements with oncontextmenu bindings

Repro: https://jsfiddle.net/ssabc/cL6qxn1r/13/
I have a background element and a foreground element (you can think of it as a dialog popup on top of a canvas). When the user right-clicks on the foreground I would like to prevent the context menu from appearing in the background.
I tried binding a handler for the foreground's context menu and returning false from it to no avail:
document.getElementById('above').oncontextmenu = function(e) {
e.preventDefault();
return false;
}
As you can see in the JSFiddle, the oncontextmenu-event triggers on both elements. Here's a screenshot showing event firing in the background no matter which element is right-clicked
Is there any way to prevent the background event from firing?
You just need to add
e.stopPropagation();
to your child element right click event handler. With the change, it would look like this:
document.getElementById('above').oncontextmenu = function(e) {
e.preventDefault();
e.stopPropagation(); // <=== add this
getResultP().innerHTML += '<li>Dialog oncontextmenu called</li>';
return false;
}
This prevents the event from bubbling up the DOM tree. Read more about it here.

How to invert Javascript element bubble order?

I have created a custom Slider component that works perfectly except for one issue.
If it is present in a modal, then the mouseUp event for it fires on it for all events below it (z-index-wise) before on the element itself.
This is because to have a nice slider you need to attach the mouseMove and mouseUp events to the document instead of the slider itself so that you can still move the thumb and capture mouse up events even when the mouse moves off the slider while sliding the thumb.
As a result, I am now having issues where the mouseUp event causes the click events for elements below it to run before the slider element. This causes issues like buttons below it clicking and the modal closing (since these are both click events).
A potential solution is to just add checks to all the potential event handlers that might be intercepted (something like if (thisModalOpened) { return false; } to all the various other elements), but that solution seems a bit messy.
Is there any better way? A way to invert the bubbling order seems like it could work, because then I could just call stopPropagation in the mouseUp for the Slider component. However, I'm not sure if that can be done, because currently the event listener is registered in the document so that it can be detected outside of the element itself.
Some code snippets:
componentDidMount() {
document.addEventListener("pointermove", this.onThumbMove, false);
document.addEventListener("pointerup", this.onThumbUp, false);
}
onThumbDown = e => {
this.setState({ dragging: true, startX: this.getX(e) });
}
onThumbMove = e => {
if (this.state.dragging) {
this.setState({ position: e.clientX });
}
}
onThumbUp = e => {
if (this.state.dragging) {
let value = this.getValue();
this.setState({ dragging: false }, () =>
this.props.onSubmit(value);
});
}
}
<div onPointerDown={this.onThumbDown} ref={this.sliderRef}>
<div>+</div>
</div>
Edit: Okay, I think I have finally figured out the problem (but I don't know the solution yet): So, modals often (mine does as well) have the tinted black backdrop that you can click on to close the modal easily. I can either have that black backdrop as a direct parent or a parent's sibling of the model's contents.
If I have it as a direct parent, then I have to call stopPropagation / return false on literally every single possible child component, so that clicking them doesn't cause the click to propagate up and click the backdrop modal and cause it to close. So I don't want it as a parent because that's a huge hassle.
On the other hand, if I have it as a parent's sibling, then I cannot call stopPropagation on the document thumbUp event, because the modal backdrop mask is not a direct parent, so it will be registered as a click and close the modal despite the stopPropagation call, because it's not a direct parent.
Basically, both approaches don't work. I don't know how to reconcile this.
As #Sarvesh mentioned in the comments, you can add a third argument to your event listener but instead of passing a boolean, you need to use the capture option to fire events from top to bottom instead of bottom to top as you wanted like this:
document.addEventListener("pointerup", this.onThumbUp, { capture: true });
You can now add a stopPropagation() at the mouseUp event or at any other event depending on what you want.

Forwarding a touchmove event

I have an element that sits on top of a horizontally scrollable element. When I scroll over this element I want to forward that scroll event to the scrollable element below, but when I click on it I want it to do something else.
$(".scroll-indicator").on("touchmove", function(evt){
// forward scroll event to ".scrollable-element"
});
$(".scroll-indicator").on("click", function(evt){
alert("Clicked!");
});
How can I forward the scroll event to my scrollable element?
Use the jquery trigger function to manually fire an event on an specific element.

How do I delegate a keypress up/down event to scroll another element?

I have a page with a scrollable element. My problem is that I want the up/down arrows to scroll this element even if it does not currently have focus.
I'm trying to just trigger a keydown with the same event when any keydown event is detected outside the target element.
$(document).keydown(function(e) {
if ($(e.target).hasClass('box')) {
return;
}
$('.box').trigger('keydown', e);
})
See this JS fiddle: https://jsfiddle.net/4tx6aqrr/2
This is not working. Is what I want to do possible?

Prevent Swipe events when interacting with elements on a page

I am building an iPad application which is essentially a series of slides.
When I've finished reading a slide I am able to swipe to the next slide *(using Zepto's swipe) which changes the window.location to the next slide. (the swipe event is bound to the window.body as it needs to work on the whole page)...
Here is the problem: some slides have interactive elements such as buttons, draggable items etc. The problem is that the swipe event is triggered when using some of these interactive elements.
Does anyone know of a way to prevent swipe from triggering in these instances? Perhaps settings a sensitivity etc?
I'm stumped...
Best wishes and many thanks!!
The way Zepto manages touch events is it binds listeners to the touchstart, touchend, and touchmove events on document.body. It then performs calculations on what event to send and triggers an event on the element that received the touchstart event. This event then bubbles up through the DOM tree evoking the listeners of each element.
This gives us two ways of preventing swipe events:
First, you could do something like:
$('#my-child-element').bind('touchstart touchend touchup', function(event) {
event.stopPropagation();
});
When your child element receives one a touch event, it will prevent it from propagating to parent elements, most importantly the body tag. This prevents the Zepto touch processor from doing anything, blocking swipe, tap, singleTap, longTap, and doubleTap events from occurring while operating in that element.
Because swipe events also bubble, you could also just prevent those specific events from bubbling to your element that listens to page change swipes:
$('#my-child-element').bind('swipeLeft swipeRight', function(event) {
event.stopPropagation();
});
This will allow you to still receive the Zepto generated events inside your child element but not outside. Zepto tap events will also still work for all elements within your child.
Fiddle here: http://jsfiddle.net/bnickel/dUuUd/
Hope "excludedElements" method will help you, like below.
$(".block").swipe({
swipe: function (event, direction, distance, duration, fingerCount, fingerData) {
},
excludedElements: ".link, a",
threshold: 0
});

Categories

Resources