byCoder
byCoder

Reputation: 9184

Check when element appears in the viewport -> addClass

I have a lot of objects in the dom tree, on which i'm adding new class, when they appeat in the viewport. But my code is very slow - it causes page to slow down...

I have such dom:

...
<span class="animation"></span>
...

and such jquery:

$.each($('.animation'), function() {
  $(this).data('offset-top', Math.round($(this).offset().top));
});

var wH = $(window).height();

$(window).on('scroll resize load touchmove', function () {
  var windowScroll = $(this).scrollTop();

  $.each($('.animation'), function() {
    if (windowScroll > (($(this).data('offset-top') + 200) - wH)){
      $(this).addClass('isShownClass');
    }
  });

});

maybe i can somehow speed up my scroll checking and class applying?

Upvotes: 1

Views: 1907

Answers (3)

Alejandro Garcia Anglada
Alejandro Garcia Anglada

Reputation: 2403

IntersectionObserver has a limited support in browsers, but it's improving.

I'm basically lazy loading the polyfill only if the browser user is loading my website in doesn't support IntersectionObserver API with the code bellow.

loadPolyfills()
   .then(() => /* Render React application now that your Polyfills are 
ready */)

/**
 * Do feature detection, to figure out which polyfills needs to be imported.
 **/

function loadPolyfills() {
  const polyfills = []

  if (!supportsIntersectionObserver()) {
    polyfills.push(import('intersection-observer'))
  }

  return Promise.all(polyfills)
}

function supportsIntersectionObserver() {
  return (
    'IntersectionObserver' in global &&
    'IntersectionObserverEntry' in global &&
    'intersectionRatio' in IntersectionObserverEntry.prototype
  )
}

Upvotes: 0

user6213434
user6213434

Reputation:

The Intersection Observer API method works on chrome only, but the performance faster by 100%. The code below loads in 3/1000 second

$(document).ready(function () {
  'use strict';

  var startTime, endTime, sum;
  startTime = Date.now();

  var anim = $('.animation');

  anim.each(function (index, elem) {
    var animoffset = $(elem).offset().top;
    $(window).on('scroll resize touchmove', function() {
      var winScTop = $(this).scrollTop();
      var windowHeight = $(window).height();
      var winBottom = winScTop + windowHeight;
      if ( winBottom >= animoffset ) {
        $(elem).addClass('showed');
      }
    });
  });

  endTime = Date.now();
  sum = endTime - startTime;
  console.log('loaded in: '+sum);
});
html {
  height: 100%;
}

body {
  margin: 0;
  height: 9000px;
}

.animation {
  display: block;
  width: 400px;
  height: 400px;
  background-color: blue;
  margin-top: 1000px;
}

.animation:not(:first-of-type) {
  margin-top: 10px;
}

.animation.showed {
  background-color: yellow;
  transition: all 3s ease
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<span class="animation"></span>
<span class="animation"></span>
<span class="animation"></span>
<span class="animation"></span>

Upvotes: 0

Jon Koops
Jon Koops

Reputation: 9271

You can use the Intersection Observer API to detect when an element appears in the viewport. Here is an example that adds a class to an element that is scrolled into the viewport and animates the background color from red to blue:

var targetElement = document.querySelector('.block');
var observer = new IntersectionObserver(onChange);

observer.observe(targetElement);

function onChange(entries) {
  entries.forEach(function (entry) {
    entry.target.classList.add('in-viewport');
    observer.unobserve(entry.target);
  });
}
body {
  margin: 0;
  height: 9000px;
}

.block {
  width: 100%;
  height: 200px;
  margin-top: 2000px;
  background-color: red;
  transition: background 1s linear;
}
.block.in-viewport {
  background-color: blue;
}
<div class="block">
</div>

Upvotes: 4

Related Questions