Is there any technique or set of techniques that can be used to implement what, in effect, would be an onVisible 'event' in JavaScript?
I would like my JavaScript to detect when an element in a web page, such as a paragraph of text, or an image, becomes visible in a browser window as a user scrolls down a page. I would also like a corresponding 'event', onNotVisible, to fire when an element that was once visible in the browser window can no longer be seen.
If it cannot be easily implemented in JavaScript, are there any browser specific events that can provide the same functionality?
I had to try this myself, and this is what I came up with:
<!DOCTYPE html> <html> <head> <script> var EventListener = function(element, callback) { this._el = element; this._cb = callback; this._at = false; this._hasBeenVisible = false; this._hasBeenInvisible = true; var _me = this; window.onscroll = function() { for (q in EventListener.queue.onvisible) { EventListener.queue.onvisible[q].call(); } for (q in EventListener.queue.oninvisible) { EventListener.queue.oninvisible[q].call(); } }; return { onvisible: function() { EventListener.queue.onvisible.push(function() { if (!_me._at && _me._hasBeenInvisible && (window.pageYOffset + window.innerHeight) > _me._el.offsetTop && window.pageYOffset < (_me._el.offsetTop + _me._el.scrollHeight)) { _me._cb.call(); _me._at = true; _me._hasBeenVisible = true; } }); EventListener.queue.oninvisible.push(function() { if (_me._hasBeenVisible && ((window.pageYOffset + window.innerHeight) < _me._el.offsetTop || window.pageYOffset > (_me._el.offsetTop + _me._el.scrollHeight))) { _me._hasBeenInvisible = true; _me._hasBeenVisible = false; _me._at = false; } }); }, oninvisible: function() { EventListener.queue.oninvisible.push(function() { if (!_me._at && _me._hasBeenVisible && ((window.pageYOffset + window.innerHeight) < _me._el.offsetTop || window.pageYOffset > (_me._el.offsetTop + _me._el.scrollHeight))) { _me._cb.call(); _me._at = true; _me._hasBeenInvisible = true; } }); EventListener.queue.onvisible.push(function() { if (_me._hasBeenInvisible && (window.pageYOffset + window.innerHeight) > _me._el.offsetTop && window.pageYOffset < (_me._el.offsetTop + _me._el.scrollHeight)) { _me._hasBeenVisible = true; _me._hasBeenInvisible = false; _me._at = false; } }); } }; } EventListener.queue = { onvisible: [], oninvisible: [] }; function addListener(element, event, fn) { if (typeof element == 'string') element = document.getElementById(element); var listener = new EventListener(element, fn); if (listener['on' + event.toLowerCase()]) return listener['on' + event.toLowerCase()].call(); } window.onload = function() { addListener('event-element', 'visible', function() { alert("Element One Visible!"); }); addListener('event-element', 'invisible', function() { alert("Element One Invisible!"); }); addListener('event2-element', 'visible', function() { alert("Element Two Visible!"); }); addListener('event2-element', 'invisible', function() { alert("Element Two Invisible"); }); } </script> </head> <body> <h1>Hey!</h1> <div style="height: 1500px;"> Please scroll down some pixels. </div> <p id="event-element"> This element should cast an event 'onvisible' and 'oninvisible'. </p> <div style="height: 1000px;"> </div> <p id="event2-element"> Another one! </p> </body> </html>
Tested in:
(The code is also available on PasteBin)
The above code is not optimized in any way, I just started writing and ended when it worked. Preferrably you would probably want to use only one queue, refactor some code and make it more generic. Provided as is.
You'd have to set up an event listener for the document's or window's onscroll event. Then you'd measure the currently visible area by adding the clientHeight to scrollTop, and then you check if your element of choice is within that area. Something like this
myElem=document.getElementById('some_id'); scrollOffset=document.getElementsByTagName('html')[0].scrollTop; visibleHeight=document.getElementsByTagName('html')[0].clientHeight; if(myElem.offsetTop>=scrollOffset && myElem.offsetTop<=scrollOffset+visibleHeight){ // element is in visible area }
If that's the case you set some flag somewhere in your code.
You're likely to run into browser incompatibilities there though. So better use some library.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With