Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

286
Vistas
Looking for a better (more efficient) way to test for scroll position

This is a Javascript project, using the jQuery library.

I have a sticky bar I would like to show whenever the browser window is below a certain page position, and otherwise hide it. Its ID is #sticky-atc-bar.

The page position is determined by the top of an element with class .product-section.

Based on research into how to do this, I originally came up with the following JS.

$( document ).ready(function() {
    $('#sticky-atc-bar').hide();

    var section = $(".product-section");
    var offsetSection = section.offset().top; //check for top property
    $(function() {
        $(window).scroll(function() {
        console.log('Scroll Event');

            if ($(window).scrollTop() >= offsetSection) { // reached product section
                console.log('True:');
                $('#sticky-atc-bar').show();
            } else { // not reached product section
                console.log('False');
                $('#sticky-atc-bar').hide();
            }
        });
    }); 
});

This works. Yet, I am aware it generates a lot of scroll events, the entire time the user is engaged with the page. Knowing that's very inefficient, I went looking for alternative approaches. Along the way I read this article called "Learning from Twitter", which made total sense.

I also came across this solution to a similar requirement. My version of that code is:

var target = $(".product-section").offset().top,
    timeout = null;

$('#sticky-atc-bar').hide();

$(window).scroll(function () {
    if (!timeout) {
        timeout = setTimeout(function () {
            console.log('scroll');            
            clearTimeout(timeout);
            timeout = null;
            if ($(window).scrollTop() >= target) {
                 $('#sticky-atc-bar').show();            
            } else {
                 $('#sticky-atc-bar').hide();
            }
        }, 250);
    }
});

I set it up on this jsFiddle, and it works.

However ... It still generates a significant number of events. They have simply been reduced to one event every 250 ms, so 4 every second.

What I would like to know is if there's a better way to go about this? Put another way, aside from changing the timeout on this second piece of code, can I reduce this operation to even fewer events? And is that even necessary at this point, or is one event every 250ms not a significant impact?

about 4 years ago · Juan Pablo Isaza
1 Respuestas
Responde la pregunta

0

Here you go: https://jsfiddle.net/son0azfj/

var productEl = document.querySelector('.product-section')
var stickyEl = document.querySelector('#sticky-atc-bar')

// check if we have elements
if (productEl && stickyEl) {
  // create an observer object, binding toggleDisplayFactory to it
  var observer = new IntersectionObserver(toggleDisplayFactory(stickyEl))
  
  // observe the product element
  observer.observe(productEl)
}

// [1] create a named function which accepts an element...
function toggleDisplayFactory(element) {
  // and returns a function - the handler that IntersectionObserver expects
  return function handler(entries) {
      // determine if the observed element is in the viewport or not
      var isInViewPort = Boolean(entries.find(entry => entry.intersectionRatio > 0))
      /**
       * if in the viewport:
       *   we want the element passed in at [1] to use it's own styles, i.e. remove the inline style property
       * else:
       *   we want the element to inherit its default display property (e.g. block, inline, inline-block, etc.) and thus be visible
       */
      var display = isInViewPort ? null : 'inherit'
    
      // set the display property of the element passed in at [1]
      element.style.display = display
  }
}
  1. use InterSectionObserver to watch for the position of an element with respect to the viewport. Use that element's position in the viewport to "do something else", i.e. show / hide another element
  2. avoid setTimeout for manipulating the DOM - use requestAnimationFrame instead (not required here)
  3. avoid querying DOM elements repeatedly - get them once, and reference them where they need to be manipulated
  4. prefer setting defaults in CSS, and then in JS override a specific property inline, removing the property when you no longer need it

Reasoning:

IntersectionObserver allows you to observe an arbitrary element with respect to its position in the viewport, instead of a scroll event which fires rapidly as one scrolls. The events an observed element trigger are only triggered when its position relative to the viewport changes

setTimeout - If you were to use a timeout, you would need to think of some number which is appropriate to use when using setTimeout. How do you do this? Well, you can only guess... you'd have to use a magic number. requestAnimationFrame instead knows when the DOM is ready for re-rendering and calculating layout - no magic numbers required


Links:

  • InterSectionObserver
  • requestAnimationFrame

EDIT: Feel free to replace document.querySelector with $(selector) - the rest of the code will work as-is.

about 4 years ago · Juan Pablo Isaza Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda