Переключать пункты меню когда блок попадает в область видимости

Архитектурное решение

Механизм взаимодействия

[Viewport]
│
├── Наблюдатель (Observer)
│    │
│    ├── Контентный блок 1 (id="news") 
│    │    │
│    │    └── При пересечении viewport → активация пункта меню "Новости"
│    │
│    ├── Контентный блок 2 (id="cases")
│    │    │
│    │    └── При пересечении viewport → активация пункта меню "Добрые истории"
│    │
│    └── Контентный блок N...
│
└── Навигационное меню
     ├── Пункт меню (data-target-id="news")
     └── Пункт меню (data-target-id="cases")

Техническая реализация

1. Разметка элементов (HTML)

<!-- Навигационное меню -->
<ul class="navigation-aside" data-navigation-container>
  <li data-navigation-item data-target-id="news">
    <a class="nav-link active" href="#news">Новости</a>
  </li>
  <li data-navigation-item data-target-id="cases">
    <a class="nav-link" href="#cases">Добрые истории</a>
  </li>
</ul>

<!-- Контентные блоки -->
<section id="news" data-intersection-target>
  <!-- Контент новостей -->
</section>

<section id="cases" data-intersection-target>
  <!-- Контент историй -->
</section>

2. Инициализация Observer (JavaScript)

class ScrollNavigation {
  constructor() {
    this.observerConfig = {
      rootMargin: '0px',
      threshold: 0.5
    };

    this.navItems = document.querySelectorAll('[data-navigation-item]');
    this.contentBlocks = document.querySelectorAll('[data-intersection-target]');
  }

  init() {
    this.observer = new IntersectionObserver(
      this.handleIntersection.bind(this), 
      this.observerConfig
    );

    this.contentBlocks.forEach(block => {
      this.observer.observe(block);
    });
  }

  handleIntersection(entries) {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        this.updateNavigation(entry.target.id);
      }
    });
  }

  updateNavigation(targetId) {
    this.navItems.forEach(item => {
      const isActive = item.dataset.targetId === targetId;
      item.classList.toggle('active', isActive);

      const link = item.querySelector('.nav-link');
      link.setAttribute('aria-current', isActive ? 'true' : 'false');
    });
  }
}

// Инициализация системы
document.addEventListener('DOMContentLoaded', () => {
  const navSystem = new ScrollNavigation();
  navSystem.init();
});

Ключевые особенности реализации

1. Оптимизация производительности

  • Прерывистая проверка: Observer API использует событийную модель вместо постоянного опроса
  • Процент видимости: Порог срабатывания 50% (threshold: 0.5)
  • Отложенная инициализация: Запуск после полной загрузки DOM

2. Доступность (A11Y)

// Обновление ARIA-атрибутов
link.setAttribute('aria-current', isActive ? 'true' : 'false');

Рекомендации по использованию

  1. Идентификаторы элементов
<!-- Рекомендуемый формат -->
data-target-id="news-section"
id="news-section"
  1. Стилизация состояний
.nav-link[aria-current="true"] {
  border-left: 3px solid #2196F3;
  background: #e3f2fd;
}
  1. Оптимизация для мобильных
// Адаптивный threshold
const mobileThreshold = window.matchMedia('(max-width: 768px)').matches ? 0.25 : 0.5;

Преимущества подхода

  1. Производительность:\ Снижение нагрузки на главный поток за счет использования нативного API

  2. Точность:\ Триггеринг событий только при реальной видимости контента

  3. Расширяемость:\ Легкая интеграция с системами аналитики:

    entry.isIntersecting && ga.send('section_view', {id: entry.target.id});
  4. Кроссбраузерность:\ Поддержка во всех современных браузерах (Polyfill для IE11)


Отладка и тестирование

// Включение debug-режима
constructor(debug = false) {
  this.debugMode = debug;
}

handleIntersection(entries) {
  entries.forEach(entry => {
    if (this.debugMode) {
      console.log(`Элемент ${entry.target.id}: 
        Видимость ${entry.intersectionRatio.toFixed(2)}`);
    }
  });
}

Данная реализация обеспечивает отзывчивую навигацию с минимальными затратами ресурсов, соответствуя современным стандартам веб-разработки.


Чтобы визуализировать взаимодействие, вы можете:

  1. Использовать DevTools Chrome → вкладка Performance для анализа событий Intersection

  2. Добавить временную debug-отметку в код:

    console.log(`Активный блок: ${targetId}`, 
    `Позиция: ${entry.boundingClientRect.y}px`);