artemsites
7/6/2021 - 1:18 PM

Фиксация блоков при прокрутке | Block fixing when scrolling

Фиксация блоков при прокрутке | Block fixing when scrolling

/**
 * @version 3.6 - 31.10.2023
 * @author  Artem Kuznecov
 * @email   info@artemsites.ru
 * @website https://artemsites.ru
 * @source  https://snippets.cacher.io/snippet/79e169f9ca705c345531
 * @source  https://gist.github.com/artemijeka/130fd58f6859999faafcea60de686262
 * @param {String|Node}  obj.target   | Блок который фиксируем
 * 
 * Класс --fixed просто добавляется без style изменений (для гибкости)
 * @param {Boolean} obj.fixAtTop      | Зафиксировать везде начиная с верха сайта
 * 
 * @param {Boolean} obj.fixAfter      | Зафиксировать после ухода этого блока за верх экрана
 * @param {Array}  obj.addClass       | Добавить класс к какому-то элементу - addClass: ['.header', '_header-fixed']
 * @param {String}  obj.fixUnderBlock |
 * @param {String}  obj.fixOverBlock  |
 * @param {Number}  obj.marginBottom  |
 */

/**
  new Fixer({
    target: '.header__self',
    fixAtTop: true,
    addClass: ['.header', '_header-fixed']
  });
*/
function Fixer(obj) {
  var self = this;

  if (typeof obj.target === 'string') {
    this.target = document.querySelector(obj.target);
  } else {
    this.target = obj.target
  }

  this.fixAfter = document.querySelector(obj.fixAfter);
  this.windowOfTop = null;
  this.targetOfTop = null;
  this.targetOffsetTopFixed = null;
  // this.height = null;
  this.fixed = false;
  this.pxOfTop = null;
  this.targetAbove = document.querySelector(obj.fixUnderBlock);
  this.cssTop = window.getComputedStyle(self.target).getPropertyValue("top");
  this.cssTopNumber = parseFloat(this.cssTop);
  this.marginBottomPx = obj.marginBottom || 0;

  if (obj.addClass) {
    this.elAddClass = document.querySelector(obj.addClass[0])
  }

  this.loopLoadScrollResizeWindow = (function () {
    ["load", "scroll", "resize"].forEach(function (event) {
      window.addEventListener(event, function () {
        self.windowOfTop = window.scrollY;
        // this.height = self.target.height;
        self.targetOfTop = self.target.offsetTop;

        if (obj.fixAfter) {
          if (self.fixAfter.getBoundingClientRect().top <= 0) {
            self.target.classList.add("--fixed");
          } else {
            self.target.classList.remove("--fixed");
          }
        }

        if (obj.fixAtTop) {
          if (!self.fixed && self.windowOfTop > self.targetOfTop) {
            self.targetOffsetTopFixed = self.targetOfTop;
            self.target.classList.add('--fixed');
            if (self.elAddClass) self.elAddClass.classList.add(obj.addClass[1]);
            self.fixed = true;
          } else if (
            self.fixed &&
            self.windowOfTop < self.targetOffsetTopFixed
          ) {
            self.target.classList.remove('--fixed');
            if (self.elAddClass) self.elAddClass.classList.remove(obj.addClass[1]);
            self.fixed = false;
          }
        }

        if (obj.fixUnderBlock) {
          if (
            self.windowOfTop < self.targetAbove.getBoundingClientRect().height
          ) {
            self.target.classList.add("--fixed");
            self.target.style.top =
              "calc(" + self.cssTop + " - " + self.windowOfTop + "px)";
          } else {
            // self.target.classList.remove('--fixed');
            self.target.style.top =
              "calc(" + self.targetAbove.getBoundingClientRect().height + "px)";
          }
        }

        if (obj.fixOverBlock) {
          var blockAboveOfViewport = document
            .querySelector(obj.fixOverBlock)
            .getBoundingClientRect();
          var targetHeight = self.target.getBoundingClientRect().height;
          var calcTopOfViewport =
            blockAboveOfViewport.top - targetHeight - self.marginBottomPx;

          if (
            blockAboveOfViewport.top <
            targetHeight + self.cssTopNumber + self.marginBottomPx
          ) {
            self.target.style.top = calcTopOfViewport + "px";
          } else {
            self.target.style.top = self.cssTop;
          }
        }
      });
    });
  })();
}
.--fixed {
	position: fixed;
}

Фиксация блоков при прокрутке | Block fixing when scrolling

fixer.js

Ссылка на видео представление 1
Ссылка на видео представление 2

При фиксации наверху target просто задаётся класс --fixed и его надо стилизовать под себя.

Usage:

Фиксация блока при прокрутке по верху окна | Fixing a block when scrolling along the top of the window:

var headerNav = new Fixer({
  target: node,
  fixAtTop: true,
});

Фиксация блока по низу предыдущего блока, при прокрутке | Fixing a block when scrolling along the bottom of the previous block:

var headerMobileMenu = new Fixer({
  target: '.header__mobile-menu',
  fixUnderBlock: '.header__nav',
});

Фиксация блока по верху нижелещащего блока, при прокрутке | Fixing a block to the top of the underlying block when scrolling:

var sidebarMenu = new Fixer({
  target: '.sidebar-menu',
  fixOverBlock: '.form-bottom',
  blockMarginBottomPx: 40,
});

#module_fixer #frontend_module_fixer #module_fixer_scrollig #frontend_module_fixer_scrollig #module_fixer_scroll #frontend_module_fixer_scroll #module_scroll_fixer #frontend_module_scroll_fixer #module_scrolling_fixer #frontend_module_scrolling_fixer