Анимация: бегущий текст внутри контейнера уводит за собой сам контейнер (решено), главное: не добавляется класс анимации и ничего не происходит

Анимация бегущей строки должна начинаться тогда, когда контейнер .stage попадает в поле зрения пользователя. Отслеживатель работает, потому что при попадании блока в поле зрения в консоли выпадает ошибка:

Uncaught TypeError: Cannot read properties of undefined (reading 'classList')

  
 (function () {
  var visualBlock = document.querySelector ('.stage');

  const observer = new IntersectionObserver(entries => {    
    entries.forEach (entry => {
  var entryString = entry.target.querySelector ('.string');
  if (typeof getCurrentAnimationPreference === 'function' && !getCurrentAnimationPreference()) {
    return;
  }
  if (entry.isIntersecting) { 
    entryString.target.classList.add ('string-animation');
    return;
  }
  entryString.classList.remove ('string-animation');
});
  });

observer.observe (visualBlock);
})();
.stage {
  position: relative;
  box-sizing: border-box;
  height: 130px;
  width: 90%;
  margin: 50px auto 0;
  background: #ffffff;
  overflow: hidden;
  border: 7px double;
  border-radius: 10px;
  border-color: #005490;
}

.running__string {
  display: block;
}

.string {
  font-size: 40px;
  font-weight: 600;
  color: rgb(111, 84, 84);
  text-transform: uppercase;
  padding-top: 35px;
  padding-left: 100%;
  white-space: nowrap;
}

.string-animation {
  -webkit-animation: text 15s linear infinite;
  -moz-animation: text 15s linear infinite;
  -o-animation: text 15s linear infinite;
  animation: text 15s linear infinite;
}

@-webkit-keyframes text {
  0% {
    transform: translate(0, 0);
  }
  100% {
    transform: translate(-350%, 0);
  }
}

@-moz-keyframes text {
  0% {
    transform: translate(0, 0);
  }
  100% {
    transform: translate(-350%, 0);
  }
}

@-o-keyframes text {
  0% {
    transform: translate(0, 0);
  }
  100% {
    transform: translate(-350%, 0);
  }
}

@keyframes text {
  0% {
    transform: translate(0, 0);
  }
  100% {
    transform: translate(-350%, 0);
  }
}
<div class="stage">
            <div class="running__string">
                <div class="string">
                    <p>При заключении договора на бухгалтерское обслуживание консультация по вопросам оптимизации налогообложения - бесплатно!</p>
                </div>
            </div>
        </div>


Ответы (2 шт):

Автор решения: soledar10

Пример

(function() {
  const running__string = document.querySelector('.running__string');

  const observer = new IntersectionObserver(entries => {
    // перебор записей
    entries.forEach(entry => {
      // если элемент появился
      if (typeof getCurrentAnimationPreference === 'function' && !getCurrentAnimationPreference()) {
        return;
      }
      if (entry.isIntersecting) {
        entry.target.classList.add('is-animation');
      }
    });
  });

  // Сообщаем наблюдателю, какие элементы следует отслеживать
  observer.observe(document.querySelector('.running__string'));
})();
section {
  min-height: 100vh;
  background-color: #999;
}

.running__string {
  box-sizing: border-box;
  height: 130px;
  width: 90%;
  margin: 50px auto 0;
  background: #ffffff;
  overflow: hidden;
  border: 7px double;
  border-radius: 10px;
  border-color: #005490;
}

.running__string p {
  font-size: 40px;
  font-weight: 600;
  color: rgb(111, 84, 84);
  text-transform: uppercase;
  padding-top: 35px;
  padding-left: 100%;
  white-space: nowrap;
}

.is-animation .string {
  animation: text 15s linear 1s infinite;
}

@keyframes text {
  0% {
    transform: translate(0, 0);
  }
  100% {
    transform: translate(-350%, 0);
  }
}
<section></section>
<div class="running__string">
  <p class="string">При заключении договора на бухгалтерское обслуживание консультация по вопросам оптимизации налогообложения - бесплатно!</p>
</div>
<section></section>

→ Ссылка
Автор решения: Grebnik

Нужно было убрать свойство target при добавлении анимации, как я понял в данном случае это не уместно. Также добавил блок обертку. Прикладываю результат, вдруг кому пригодится.

(function () {
  const visualBlock = document.querySelector ('.stage');
  const observer = new IntersectionObserver(entries => {
    entries.forEach (entry => {
      const entryString = entry.target.querySelector ('.string');
      if (typeof getCurrentAnimationPreference === 'function' && !getCurrentAnimationPreference()) {
        return;
      }
      if (entry.isIntersecting) {
        entryString.classList.add('string-animation');
        return;
      }
      entryString.classList.remove('string-animation');
    });
  });

observer.observe (visualBlock);

})();
.stage {
  display: block;
  box-sizing: border-box;
  height: 130px;
  width: 90%;
  margin: 50px auto 0;
  background: #ffffff;
  overflow: hidden;
  border: 7px double;
  border-radius: 10px;
  border-color: #005490;
}

.wrap {
  display: block;
}

.string {
  font-size: 40px;
  font-weight: 600;
  color: rgb(111, 84, 84);
  text-transform: uppercase;
  padding-top: 35px;
  padding-left: 100%;
  white-space: nowrap;
}

.string-animation {
  -webkit-animation: text 15s linear infinite;
  -moz-animation: text 15s linear infinite;
  -o-animation: text 15s linear infinite;
  animation: text 15s linear infinite;
}
  
@-webkit-keyframes text {0% {transform: translate(0, 0);} 100%{transform: translate(-350%, 0);}}
@-moz-keyframes text {0% {transform: translate(0, 0);} 100%{transform: translate(-350%, 0);}}
@-o-keyframes text {0% {transform: translate(0, 0);} 100%{transform: translate(-350%, 0);}}
@keyframes text {0% {transform: translate(0, 0);} 100%{transform: translate(-350%, 0);}}
<div class="stage">
                <div class="wrap">
                    <div class="string">
                        При заключении договора на бухгалтерское обслуживание консультация по вопросам оптимизации налогообложения - бесплатно!
                    </div>
                </div>
                <script src="string.js"></script>      
            </div>

→ Ссылка