Trigger an event when an element is visible in a scrollable area [JavaScript]

EDIT in 2022

It might also be interesting to look at Intersection Observer that is now available in most browsers, and there is also a polyfill.

const observer = new IntersectionObserver((entries, observer) => {
  entries.forEach((entry) => {
    let isVisible=(entry.intersectionRatio===1);
  });
}, {threshold:1});
observer.observe(document.querySelector('#element');

END EDIT

I have this specific need that is to trigger an event when an element becomes visible/invisible into a scrollable area. Here is the code I came to:

/**
 * Create the handler
 * @param {NodeElement} holder The scrollable area to monitor (where the 'scroll' event will be apply)
 */
var VisibleEventListener = function(holder) {
  var _this=this;
  _this.started = false;
  _this.holder = holder;
  // detect if an element is visible
  _this.isScrolledIntoView=function(el) {
    var bndElem = el.getBoundingClientRect();
    var bndHolder = _this.holder.getBoundingClientRect();
    return bndElem.top <= bndHolder.top ? !(bndHolder.top - bndElem.top > bndElem.height) : !(bndElem.bottom - bndHolder.bottom > bndElem.height);
  }
  // permits to deal with the scroll
  _this.scrollHandler=function(e) {
    for (var i=0, len=_this.events.length; i<len; i++) {
      _this.events[i].check();
    }
  }
  _this.events=[];
}
/**
 * Add the visible/invisible event for an element into a scrollable area
 * @param {NodeElement}   element  The element to test
 * @param {String}   listener 'visible' or 'invisible'
 * @param {Function} callback The callback function to be called when the event is triggered
 */
VisibleEventListener.prototype.add = function(element, listener, callback) {
  var _this=this;
  var ElementToMonitor=function(element, listener, callback) {
    this._super=_this;
    this.element=element;
    this.isVisible=false;
    this.callback=callback;
    this.listener=listener;
  }
  ElementToMonitor.prototype.check = function() {
    var visible=this._super.isScrolledIntoView(this.element);
    if (visible && !this.isVisible) { // becomes visible
      this.isVisible=true;
      if (this.listener==='visible') this.callback.call(this.element)
    } else if (!visible && this.isVisible) { // becomes invisible
      this.isVisible=false;
      if (this.listener==='invisible') this.callback.call(this.element)
    }
  };
  var etm=new ElementToMonitor(element,listener,callback);
  _this.events.push(etm);
  // if we have started to monitor
  if (_this.started===true) etm.check();
}
VisibleEventListener.prototype.start = function() {
  this.holder.addEventListener('scroll', this.scrollHandler);
  this.started = true;
  // trigger the check to verify if the elements are already visible
  this.scrollHandler();
}
VisibleEventListener.prototype.stop = function() {
  this.holder.removeEventListener('scroll', this.scrollHandler);
  this.started = false;
}

And to use it:

// initiate our area with VisibleEventListener
var vel = new VisibleEventListener(document.querySelector('#s4-workspace'));
// add the elements to monitor
vel.add(document.querySelector('.slideshow'), 'invisible', function() {
  console.log(this,' is now invisible')
})
vel.add(document.querySelector('.slideshow'), 'visible', function() {
  console.log(this,' is now visible')
})
// start the monitoring
vel.start();

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

*