What's the difference between onScroll and onScrollCapture in React?
Not sure if this is a DOM or pure JS topic since I can't find any resources about onScrollCapture.
const node = (
<div onScrollCapture={handleScrollCapture} />
);
What's the difference between onScroll and onScrollCapture in React?
Not sure if this is a DOM or pure JS topic since I can't find any resources about onScrollCapture.
const node = (
<div onScrollCapture={handleScrollCapture} />
);
onScrollCapture in React is the equivalent of doing:
addEventListener('scroll', listener, { capture: true })
or
addEventListener('scroll', listener, true)
in vanilla JS.
Both say: If the event fires on a child element, run my handler first (parent) before running their handlers. For example, when you click the button in the demo below, the browser will first run the event handler for the parent div before it runs the handler for the button itself:
const parent = document.querySelector('.parent');
const button = parent.querySelector('button');
parent.addEventListener('click', () => console.log('parent'), true);
button.addEventListener('click', (e) => {
console.log('button');
});
.parent {
width: 100px;
height: 100px;
border: solid 1px black;
display: flex;
align-items: center;
justify-content: center;
}
button {
background: white;
}
<div class="parent">
<button>
Click me
</button>
</div>
Traditionally, when registering an event listener, you would just do addEventListener('event', listener) without the third argument (or onEvent={listener} in React). If the event target is a child nested inside the current element (like the button in the example above), this means the event will:
Which is the reverse order of what you'd get with onEventCapture and its vanilla-JS equivalent.
More generally, all events go through three phases:
For parent elements, events only fire in either the bubbling or capturing phase, not both, and when they fire is determined by:
addEventListener.Importantly, the scroll event does not bubble. So in the example below, only the child div's messages are logged to the console when you scroll it:
const parent = document.querySelector('.parent');
const child = parent.querySelector(':scope > *');
parent.addEventListener('scroll', () => console.log('parent scrolled'));
child.addEventListener('scroll', () => console.log('child scrolled'));
body {
display: flex;
justify-content: center;
align-items: center;
}
div {
border: solid 1px black;
}
.parent {
height: 250px;
overflow-y: scroll;
max-width: 200px;
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
}
.parent>* {
height: 200px;
overflow-y: auto;
}
<div class="parent">
<div tabindex="0" role="region">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin mattis augue quis turpis vestibulum volutpat a a sem. Maecenas ornare ac felis sed volutpat. Curabitur dapibus ante nec ante congue, et consectetur nunc tempor. Sed gravida posuere sollicitudin.
Etiam feugiat a nulla vel consequat. Vestibulum vel massa faucibus, rutrum felis quis, mollis orci. Vivamus vestibulum ligula non orci cursus tincidunt. Proin hendrerit tincidunt finibus. Donec vitae aliquet sem. Nunc vel purus non nunc consectetur
luctus in vel mauris. Donec varius quam risus, non lacinia orci rutrum in. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam nunc turpis, cursus sodales vulputate eu, eleifend fermentum est. Aliquam sollicitudin congue justo vitae ullamcorper.
Proin lectus mi, faucibus vitae pharetra id, fringilla sit amet arcu. Interdum et malesuada fames ac ante ipsum primis in faucibus. Ut ut ipsum nulla. Proin in aliquet purus, quis consectetur risus. Aliquam erat volutpat. Sed auctor tristique interdum.
Morbi ac purus eget leo interdum gravida at ac nunc. Donec commodo vitae quam pulvinar bibendum. Duis nunc enim, scelerisque vel maximus quis, semper eu ipsum. Nam eu mollis ante.
</div>
</div>
This means that if you want to add an event listener to a parent element and detect scrolls on a child element, then you need to use scroll capturing, like this:
const parent = document.querySelector('.parent');
const child = parent.querySelector(':scope > *');
parent.addEventListener('scroll', () => console.log('parent scrolled'), true);
child.addEventListener('scroll', () => console.log('child scrolled'));
body {
display: flex;
justify-content: center;
align-items: center;
}
div {
border: solid 1px black;
}
.parent {
height: 250px;
overflow-y: scroll;
max-width: 200px;
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
}
.parent>* {
height: 200px;
overflow-y: auto;
}
<div class="parent">
<div tabindex="0" role="region">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin mattis augue quis turpis vestibulum volutpat a a sem. Maecenas ornare ac felis sed volutpat. Curabitur dapibus ante nec ante congue, et consectetur nunc tempor. Sed gravida posuere sollicitudin.
Etiam feugiat a nulla vel consequat. Vestibulum vel massa faucibus, rutrum felis quis, mollis orci. Vivamus vestibulum ligula non orci cursus tincidunt. Proin hendrerit tincidunt finibus. Donec vitae aliquet sem. Nunc vel purus non nunc consectetur
luctus in vel mauris. Donec varius quam risus, non lacinia orci rutrum in. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam nunc turpis, cursus sodales vulputate eu, eleifend fermentum est. Aliquam sollicitudin congue justo vitae ullamcorper.
Proin lectus mi, faucibus vitae pharetra id, fringilla sit amet arcu. Interdum et malesuada fames ac ante ipsum primis in faucibus. Ut ut ipsum nulla. Proin in aliquet purus, quis consectetur risus. Aliquam erat volutpat. Sed auctor tristique interdum.
Morbi ac purus eget leo interdum gravida at ac nunc. Donec commodo vitae quam pulvinar bibendum. Duis nunc enim, scelerisque vel maximus quis, semper eu ipsum. Nam eu mollis ante.
</div>
</div>