pass mouse wheel event from parent to child - javascript

I have big parent div and narrow child div in the center of it.
When I scroll mouse wheel over child, it scrolls as it should,
but I want to be able to scroll that child element even if mouse is not on it, but somewhere on parent, and mouse wheel is scrolled.
I'm passing event from parent to child like this:
parent.addEventListener('mousewheel', parentMouseWheel);
function parentMouseWheel(e) {
if (e.target.id === 'parentId') { //to prevent double events when mouse is over child
if (e.wheelDelta > 0) child.scrollTop = child.scrollTop - 120;
else child.scrollTop = child.scrollTop + 120;
}
}
this works, but the problem is, there is no smooth scrolling like there is when I scroll mouse wheel over child (probably because of forcing/passing like this).
So, is there any other way to pass mouse wheel event to child, that will also trigger smooth scrolling?
I'm styling my extension options page, if that means anything...

Related

Hover the element below the mouse cursor when scrolling

One can determine the element below the mouse cursor (i.e. the top-most hovered element) with the following techniques:
Listen for the mousemove event. The target is
event.target or
document.elementFromPoint(event.clientX, event.clientY).
This does not work when scrolling while not moving the mouse. Then, the mouse technically doesn’t move; thus, no mouse event will fire.
Unfortunately, both techniques from above are no longer applicable when listening for the scroll event. event.target will be whichever element is scrolled (or document). Also, the mouse cursor position is not exposed on the event object.
As described in this answer to “Determine which element the mouse pointer is on top of in Javascript”, one possible solution is querying the hovered element via the CSS :hover pseudo-class.
document.addEventListener('scroll', () => {
const hoverTarget = document.querySelector('.element:hover');
if (hoverTarget) {
hover(hoverTarget);
}
});
However, this is not usable because it is very inefficient and inaccurate. The scroll event is one of the rapidly firing events and needs to be slowed down when performing anything mildly costly (e.g. querying the DOM).
Also, the hovered element lags behind when scrolling. You can observe this on any kind of website with a lot of links: Hover over one of them and scroll to another link without moving the mouse. It updates only after a few milliseconds.
Is there any way, this can be implemented nicely and efficient? Basically, I want the inverse of mouseenter: Instead of knowing when the mouse enters and element, I want to know when an element intersects with the mouse (e.g. when the mouse is not moved but the element [i.e. when scrolling]).
One approach of tackling this is storing the mouse cursor location with the mousemove event and in the scroll event use document.elementFromPoint(x, y) to figure out the element that should be hovered.
Keep in mind that this is still pretty inefficient due to the scroll event being fired with such a high frequency. The event handler should be debounced to limit execution of the function to once per delay. David Walsh explains how to do this in JavaScript Debounce Function.
let hoveredElement;
let mouseX = 0, mouseY = 0;
document.addEventListener('DOMContentLoaded', () => {
document.addEventListener('mousemove', event => {
mouseX = event.clientX;
mouseY = event.clientY;
hover(event.target);
});
document.addEventListener('scroll', () => {
const hoverTarget = document.elementFromPoint(mouseX, mouseY);
if (hoverTarget) {
hover(hoverTarget);
}
});
});
function hover(targetElement) {
// If the target and stored element are the same, return early
// because setting it again is unnecessary.
if (hoveredElement === targetElement) {
return;
}
// On first run, `hoveredElement` is undefined.
if (hoveredElement) {
hoveredElement.classList.remove('hover');
}
hoveredElement = targetElement;
hoveredElement.classList.add('hover');
}
.element {
height: 200px;
border: 2px solid tomato;
}
.element.hover {
background-color: lavender;
}
<div class="container">
<div class="element element-1">1</div>
<div class="element element-2">2</div>
<div class="element element-3">3</div>
<div class="element element-4">4</div>
<div class="element element-5">5</div>
</div>
Currently, the solution will hover the top-most element under the mouse both when moving the mouse and when scrolling. It might be more suitable for your needs to attach the mousemove listener to a set of specific elements and then always hover event.currentTarget (i.e. the element the event listener was attached to). As for the scroll part, you can use hoverTarget.closest to find the suitable element up in the DOM tree.

Prevent 'wheel' event scrolling if element has basic scroll

I've built custom scrolling on my page through wheel event on window. However it shoots even when I want to scroll the element with basic scrollbar. How to disable wheel event on this element, but be able to scroll?
You can check in the event handler if the element is scrollable
window.addEventListener('wheel', function(event) {
if ( event.target.scrollHeight < event.target.clientHeight ) {
// scollheight less than clientheight, means it doesn't have scrollbars ...
// do stuff
}
})

Capture mouse leaving body element crossing top border

I was wondering if there is a way to capture some kind of mouse event or another event when the mouse leaves body crossing top border?
Well I have this campaign at my site when the user leaves body element and cookie is not set I show them this popup for one time only. But this event triggers for all sides of body.
tr = trigger event on leave
I woule like:
tr
+----------+
| |
no-tr| |no-tr
| |
+----------+
no-tr
But i have
tr
+----------+
| |
tr| |tr
| |
+----------+
tr
I have something like this:
$("body").one("mouseleave", initPopup)
My question is if there is a way to trigger this event only when the user leaves body by top border, but not for other borders?
You could use e.offsetY and check if it is less than 0. The offset is calculated from the upper left corner of the element with Y increasing downwards. If e.offsetY is less than 0 it means the event was triggered when the mouse moved above the element with the eventhandler attached.
If the page is scrolled you need to take the scrollTop value into account as the offsetY will have the offset position from the elements top corner even if that is scrolled off the top of the screen.
Since the event will be fired even if the mouse moved out on the other side you can't use .one() but instead you could add a condition to check if the event has been triggered from a "mouse leave" on the top.
DEMO
triggered = false;
$("body").on("mouseleave", function (e) {
if (e.offsetY - $(window).scrollTop() < 0 && !triggered) {
triggered = true;
alert("leave");
}
});
You can try this with jquery:
$('#element').mouseout(function(e) {
if (e.offsetY < 0) {
// your event
}
});
EDIT: edited answer to no-tr on bottom edge. Didn't notice that in question!

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

How can I make page scrolling trigger mouseover events?

When the mouse starts hovering over an element because of scrolling (either by wheel, or by keyboard scrolling), it does not trigger a mouseover event on the elements it is hovering (Chrome 6 on OSX). What would be an elegant way to trigger the mouseover event for the correct elements when scrolling?
Honestly, this is gonna be a pain. You'll have to
determine the size and position of every element that should get a mouseover handler.
add a scroll listener to the window.
In the handler, get the mouse cursor position and pageOffset.
Find out which element(s) the cursor is in.
manually call the actual mouseover handler
(Find out which elements the cursor has left, if you want some mouseout behaviour too)
You may need to re-calculate the elements' positions and sizes if they are dynamic. (move 1. beneath 3.)
While this should work fine with block-level elements, I have absolutely no idea on a solution for inline elements.
This is much more simple in the modern day web using document.elementsFromPoint:
Add a scroll listener to the window.
In the handler, call document.elementsFromPoint.
Manually call the actual pointerover handler for those elements. Keep track of these elements.
(optionally) Manually call the actual pointermove handler for those elements.
Check the list of elements from the previous time around. Manually call the actual pointerleave handler for elements no longer being hovered.
Here's some psuedo-code:
let prevHoveredEls = [];
document.addEventListener("scroll", (e) => {
let hoveredEls = document.elementsFromPoint(e.pageX, e.pageY);
hoveredEls = hoveredEls.filter(
(el) => el.classList.contains("elements-cared-about")
);
const notHoveredEls = prevHoveredEls.filter(
(el) => !prevHoveredEls.includes(el)
);
hoveredEls.forEach((el) => {
const bcr = el.getBoundingClientRect();
el.handlePointerEnter({
layerX: e.pageX - bcr.left,
layerY: e.pageY - bcr.top,
});
});
notHoveredEls.forEach((el) => {
const bcr = el.getBoundingClientRect();
el.handlePointerLeave({
layerX: e.pageX - bcr.left,
layerY: e.pageY - bcr.top,
});
});
prevHoveredEls = hoveredEls;
});
Try some hack like myDiv.style.opacity = 1+Math.random(); on scroll ;)

Categories

Resources