Manera más eficiente de manejar $ (ventana) .scroll funciones en jquery?

En el siguiente código, estoy verificando si la ventana se está desplazando más allá de cierto punto y, si lo está, cambie un elemento para usar la posición fija para que no se desplace desde la parte superior de la página. El único problema es que parece estar ALMACENAMENTE en la memoria del lado del cliente (y realmente atasca la velocidad de desplazamiento) porque en cada píxel de desplazamiento estoy actualizando los atributos de estilo una y otra vez en el elemento.

¿Sería una gran diferencia comprobar si el attr ya está allí antes de intentar actualizarlo? ¿Hay una práctica completamente diferente y más eficiente para obtener el mismo resultado?

$(window).scroll(function () { var headerBottom = 165; var fcHeight = $("#pnlMainNavContainer").height(); var ScrollTop = $(window).scrollTop(); if (ScrollTop > headerBottom) { $("#HeaderContentBuffer").attr("style", "margin-top:" + (fcHeight) + "px;"); $("#AddFieldsContainer").attr("style", "position:fixed;width:320px;top:70px;left:41px;"); } else { $("#HeaderContentBuffer").attr("style", "margin-top: 0px;"); $("#AddFieldsContainer").removeAttr("style"); } }); 

Mientras escribo esto, veo que StackOverflow.com usa el mismo tipo de funcionalidad con sus menús amarillos de “Preguntas similares” y “Ayuda” en el lado derecho de esta página. Me pregunto cómo lo hacen.

Una técnica que puede usar es establecer un temporizador en el evento de desplazamiento y solo realizar el trabajo principal cuando la posición de desplazamiento no ha cambiado durante un corto período de tiempo. Utilizo esa técnica en eventos de cambio de tamaño que tienen el mismo problema. Puede experimentar con qué valor de tiempo de espera parece funcionar correctamente. Un tiempo de actualización más corto con pausas más cortas en el desplazamiento y, por lo tanto, puede ejecutarse más a menudo durante el desplazamiento, un tiempo más prolongado requiere que el usuario pause todos los movimientos durante un tiempo significativo. Deberá experimentar qué valor de tiempo de espera funciona mejor para sus propósitos y sería mejor probarlo en una computadora relativamente lenta, ya que allí es donde el problema del desfase de desplazamiento sería más pronunciado.

Aquí está la idea general de cómo esto podría implementarse:

 var scrollTimer = null; $(window).scroll(function () { if (scrollTimer) { clearTimeout(scrollTimer); // clear any previous pending timer } scrollTimer = setTimeout(handleScroll, 500); // set new timer }); function handleScroll() { scrollTimer = null; var headerBottom = 165; var fcHeight = $("#pnlMainNavContainer").height(); var ScrollTop = $(window).scrollTop(); if (ScrollTop > headerBottom) { $("#HeaderContentBuffer").attr("style", "margin-top:" + (fcHeight) + "px;"); $("#AddFieldsContainer").attr("style", "position:fixed;width:320px;top:70px;left:41px;"); } else { $("#HeaderContentBuffer").attr("style", "margin-top: 0px;"); $("#AddFieldsContainer").removeAttr("style"); } } 

También puede acelerar su función de desplazamiento almacenando en el caché algunos de los selectores cuando se inicia el desplazamiento, para que no tengan que volver a calcularse cada vez. Este es un lugar donde la sobrecarga adicional de crear un objeto jQuery cada vez podría no ser de ayuda.


Aquí hay un método de complemento jQuery que maneja el temporizador de desplazamiento por usted:

 (function($) { var uniqueCntr = 0; $.fn.scrolled = function (waitTime, fn) { if (typeof waitTime === "function") { fn = waitTime; waitTime = 500; } var tag = "scrollTimer" + uniqueCntr++; this.scroll(function () { var self = $(this); var timer = self.data(tag); if (timer) { clearTimeout(timer); } timer = setTimeout(function () { self.removeData(tag); fn.call(self[0]); }, waitTime); self.data(tag, timer); }); } })(jQuery); 

Demostración de trabajo: http://jsfiddle.net/jfriend00/KHeZY/

Su código se implementaría así:

 $(window).scrolled(function() { var headerBottom = 165; var fcHeight = $("#pnlMainNavContainer").height(); var ScrollTop = $(window).scrollTop(); if (ScrollTop > headerBottom) { $("#HeaderContentBuffer").attr("style", "margin-top:" + (fcHeight) + "px;"); $("#AddFieldsContainer").attr("style", "position:fixed;width:320px;top:70px;left:41px;"); } else { $("#HeaderContentBuffer").attr("style", "margin-top: 0px;"); $("#AddFieldsContainer").removeAttr("style"); } }); 

He encontrado que este método es mucho más efficeint para $(window).scroll()

 var userScrolled = false; $(window).scroll(function() { userScrolled = true; }); setInterval(function() { if (userScrolled) { //Do stuff userScrolled = false; } }, 50); 

Echa un vistazo a la publicación de John Resig sobre este tema.

Una solución aún más eficaz sería establecer un intervalo más largo que detecte si está cerca de la parte inferior o superior de la página. De esa forma, ni siquiera tendría que usar $(window).scroll()

Haciendo tu función un poco más eficiente.

Simplemente verifique si el atributo de estilo está presente / ausente antes de eliminar / agregar los estilos.

 $(window).scroll(function () { var headerBottom = 165; var fcHeight = $("#pnlMainNavContainer").height(); var ScrollTop = $(window).scrollTop(); if (ScrollTop > headerBottom) { if (!$("#AddFieldsContainer").attr("style")) { $("#HeaderContentBuffer").attr("style", "margin-top:" + (fcHeight) + "px;"); $("#AddFieldsContainer").attr("style", "position:fixed;width:320px;top:70px;left:41px;"); } } else { if ($("#AddFieldsContainer").attr("style")) { $("#HeaderContentBuffer").attr("style", "margin-top: 0px;"); $("#AddFieldsContainer").removeAttr("style"); } } }); 

Establezca algo de lógica aquí. De hecho, necesita configurar los atts una vez en adelante, y una vez encendidos. Asi que:

 var checker = true; $(window).scroll(function () { ....... if (ScrollTop > headerBottom && checker == true) { $("#HeaderContentBuffer").attr("style", "margin-top:" + (fcHeight) + "px;"); $("#AddFieldsContainer").attr("style", "position:fixed;width:320px;top:70px;left:41px;"); checker == false; } else if (ScrollTop < headerBottom && checker == false) { $("#HeaderContentBuffer").attr("style", "margin-top: 0px;"); $("#AddFieldsContainer").removeAttr("style"); checker == true; } });