Выполнение функции один раз для каждого элемента

118
20 ноября 2019, 02:10

У меня есть несколько блоков с анимированными счетчиками (блоки с animated-block-1, animated-block-2, animated-block-3 и т. д.).

Задача в том, чтобы запускать счетчик countNumbersSlider один раз для каждого элемента при прокрутке и при наличии этого элемента в зоне видимости. Проблема появляется при скролле вверх-вниз. Счетчик перезапускается на каждый скролл вверх, не успев завершиться.

Я пробовал делать счетчики скроллов и флаги, но похоже, запутался.

Подскажите как правильно реализовать выполнение countNumbersSlider один раз для каждого элемента?

 $(window).on('wheel DOMMouseScroll', function (e) {
        var wd = event.wheelDelta || -event.originalEvent.detail;
        $('[id^="animated-block-"]').each(function () {
            if (document.querySelector('#' + this.id).getClientRects()[0].top < 300 && wd < 0) {
                countNumbersSlider('#' + this.id);
            }
        });
    });

UPDATE

Добавил фидл как в целом всё выглядит. https://jsfiddle.net/vy652L3a/

Answer 1

Я сделялЪ :), для маленького экрана плоховато работает функция, определяющая что элемент на экране, но суть вопроса не в этом...

$(window).on('wheel', () => { 
    $('[id^="animated-block-"]').each( function () { 
        let el = $('#' + this.id); 
        isScrolledIntoView(el) && countNumbersSlider(el); 
    }); 
}); 
 
function countNumbersSlider(el) { 
  if (el.attr('intervalId')) 
    return; 
     
  el.html(10) 
  el.attr('intervalId', setInterval(count, 1000)); 
   
  function count() { 
      let v = +el.html()-1; 
      el.html(v); 
      !v && clearInterval(el.attr('intervalId')); 
      !v && el.attr('intervalId', null) 
  } 
} 
 
function isScrolledIntoView(elem) { 
    var docViewTop = $(window).scrollTop(); 
    var docViewBottom = docViewTop + $(window).height(); 
    var elemTop = elem.offset().top; 
    var elemBottom = elemTop + elem.height(); 
    return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop)); 
}
div { 
  border: 1px solid black; 
  font-size: 100px; 
  width: 100px; 
  background-color: wheat; 
  text-align: center; 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> 
<hr><br><br> 
 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<div id="animated-block-1"></div> 
 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<div id="animated-block-2"></div> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<div id="animated-block-3"></div> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br> 
<hr><br><br>

Answer 2

Придумал решение https://jsfiddle.net/2pktvcua/

Создаем объекты, которые содержат селектор и флаг. Получаем массив объектов вида:

var projectCounter = [
     selector: 'animated-block-1', 
     isCalled: false
];
  1. Проходимся по массиву.
  2. Находим элемент по селектору.
  3. Запускаем countNumbersSlider.
  4. Ставим для isCalled значение true.

    $(window).on('wheel DOMMouseScroll', function (e) {
        projectCounter.forEach(function (entry) {
            if (elementInView($('#' + entry.selector), false) && !entry.isCalled) {
                countNumbersSlider('#' + entry.selector);
                entry.isCalled = true;
            }
        })
    });
    
READ ALSO
Ссылка в мобильном приложении vue.js на сторонний сайт

Ссылка в мобильном приложении vue.js на сторонний сайт

Делаю приложение на vuejs монтирую его кордовой в apk \ ipa как в приложении разместить кнопку с ссылкой на сторонний сайт, что бы при нажатии она...

131
Зачем тут нужен this [дубликат]

Зачем тут нужен this [дубликат]

На данный вопрос уже ответили:

119
JavaScript ООП, передать Jquery объект через bind

JavaScript ООП, передать Jquery объект через bind

В методе obr необходимо получить объект jquery, на котором сработало событиеКаким образом это можно сделать?

109
LEFT JOIN Дублирует поля [MySQL]

LEFT JOIN Дублирует поля [MySQL]

Столкнулся с проблемой дублирования полей при использовании LEFT JOIN в MySQL запросе

142