Reputation: 3004
I am wanting to trigger an event with vanilla javascript when an ID reaches the top of the parent div. Have seen examples of doing such with jQuery, but I don't want to load that much for a simple effect. Plus most examples deal with the top of the viewport, whereas I have a main div that is about 90% of the viewport height, with an info div above.
I am working with a long webpage, with several sections, that scrolls within the main div. When the title of each section <h2 id="title1">bla bla bla</h2>
reaches the top of the main div, I want to trigger a simple show/hide of the title <div id="info1">bla bla bla</div>
shown in the info div. The show / hide is simple enough:
function chngInfo(x) {
const targets = document.querySelectorAll('[id^="info"]');
for (let i = targets.length; i--;) {
targets[i].style.display = 'none';
}
document.getElementById('info' + x).style.display = 'block';
}
It is triggering the function that has got me stymied. One further complication is that the height of the main div varies depending on device, so can't easily do a formula based on viewport height.
It would be major bonus if a single function could look for any titleX as it passes the top while scrolling down or passes the bottom while scrolling up.
Upvotes: 1
Views: 2475
Reputation: 944
Not very clear about the needs. I think you could try
getBoundingClientRect
to decide show which contentHope it helps, thanks.
function entry() {
// 1. detect the main bound
const mainRect = document.querySelector('#main').getBoundingClientRect();
console.log('main', mainRect.y);
// 2. get all the title
const titles = document.querySelectorAll('h2');
// hideAll
titles.forEach(t => {
t.nextElementSibling.style.display = 'none';
});
const first = Array.from(titles).find((t, i) => {
const rect = t.getBoundingClientRect();
rect.y > 0 && console.log('show', i);
return rect.y > 0;
});
window.t = first;
first.nextElementSibling.style.display = 'block';
}
var timer = null;
window.addEventListener('scroll', function() {
if(timer !== null) {
clearTimeout(timer);
}
timer = setTimeout(entry, 150);
}, false);
// init show;
entry();
<html>
<style>
#main {
background: green;
height: 1800px;
}
#info1 {
display: none;
}
h2 {
height: 150px;
}
div {
height: 500px;
background: red;
}
</style>
<body>
<div id="main">
<h2 id="title1">bla bla bla</h2>
<div id="info1">bla bla bla</div>
<h2 id="title1">bla bla bla</h2>
<div id="info1">bla bla bla</div>
<h2 id="title1">bla bla bla</h2>
<div id="info1">bla bla bla</div>
<h2 id="title1">bla bla bla</h2>
<div id="info1">bla bla bla</div>
<h2 id="title1">bla bla bla</h2>
<div id="info1">bla bla bla</div>
<h2 id="title1">bla bla bla</h2>
<div id="info1">bla bla bla</div>
<h2 id="title1">bla bla bla</h2>
<div id="info1">bla bla bla</div>
<h2 id="title1">bla bla bla</h2>
<div id="info1">bla bla bla</div>
<h2 id="title1">bla bla bla</h2>
<div id="info1">bla bla bla</div>
<h2 id="title1">bla bla bla</h2>
<div id="info1">bla bla bla</div>
<h2 id="title1">bla bla bla</h2>
<div id="info1">bla bla bla</div>
<h2 id="title1">bla bla bla</h2>
<div id="info1">bla bla bla</div>
</div>
</body>
</html>
Upvotes: 1
Reputation: 1003
I use a scroll event listener in combination with a bit of throttling to avoid some of the performance pitfalls of listening to scroll events.
If you run the snippet below, you'll notice that it provides you with a hook where you can call some logic that only gets called once when the element reaches the top of the viewport. If the user scroll back up, it will reset.
I also left some comments in the code in case it helps.
// from
// @peduarte
// https://gist.github.com/peduarte/969217eac456538789e8fac8f45143b4
function throttle(func, wait = 100) {
let timer = null;
return function(...args) {
if (timer === null) {
timer = setTimeout(() => {
func.apply(this, args);
timer = null;
}, wait);
}
};
}
// throttle scroll by this much (ms)
const SCROLL_THROTTLE = 200;
// get our elements
const wrapper = document.querySelector('.wrapper');
const target = document.querySelector('.this-one');
// reserve this so we can overwrite it later
let reachedTop;
// function that fires when we scroll
const checkOffset = () => {
// get out offsets
const wrapperOffset = wrapper.scrollTop + wrapper.offsetTop;
const targetOffset = target.offsetTop;
// try to do some work
if (wrapperOffset >= targetOffset && !reachedTop) {
reachedTop = true;
// element reached top do more work...
console.log(reachedTop);
} else if (wrapperOffset <= targetOffset && reachedTop) {
reachedTop = false;
// user scrolled up and element longer at top...
console.log(reachedTop);
}
}
// throttle our function
const throttledScroll = throttle(checkOffset, SCROLL_THROTTLE);
// call the throttled function when we scroll
wrapper.addEventListener('scroll', throttledScroll);
.this-one {
color: blue;
}
.wrapper {
border: 1px solid red;
height: 50vh;
overflow: auto;
margin-top: 2em;
padding: 2em;
display: inline-block;
}
<div class="wrapper">
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p class="this-one">Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
<p>Example</p>
</div>
Upvotes: 2
Reputation: 1419
I think There Two way to achieve that :
scroll
event with offsetTop , scrollY
properties to get the point you need to reach ... but it have some performance issues :var myTarget = document.querySelector('.target')
window.addEventListener('scroll', function() {
if(myTarget.offsetTop - window.scrollY <= 0){
myTarget.style.color = "red"
}
})
<h1>hello</h1>
<h1>hello</h1>
<h1>hello</h1>
<h1>hello</h1>
<h1>hello</h1>
<h1>hello</h1>
<h1>hello</h1>
<h1>hello</h1>
<h1>hello</h1>
<h1>hello</h1>
<h1 class='target'>Target elemnt example</h1>
<h1>hello</h1>
<h1>hello</h1>
<h1>hello</h1>
<h1>hello</h1>
<h1>hello</h1>
<h1>hello</h1>
<h1>hello</h1>
<h1>hello</h1>
<h1>hello</h1>
<h1>hello</h1>
intersection Observer
this link have good explanation and examples : https://webdesign.tutsplus.com/tutorials/how-to-intersection-observer--cms-30250
Upvotes: 2