Custom Header / Шапка

ver.1.0.0
от 30.07.2025 15:38

Особенности

  1. Открыть/закрыть меню каталога
    Открытие специального меню каталога. Меню может быть как одноуровневым, так и двух и трех уровневым. А также закрыть меню по ESC
  2. Закрепление шапки
    Возможность закреплать как всю шапку целиков, так и отдельные ее разделы/линии.
JS
от 30.07.2025 15:38
Параметры
PARAMTYPEDEFAULTDESCRIPTION
fixedboolean|stringfalse

Закрепление шапки.

При указании true - закрепляться будет вся шапка.

При указании .header__main - закрепляться будет только нужная вам строка шапки.

Как юзать
Not params
      new CustomHeader();
    
With params
      new CustomHeader({
    fixed: false
});
    
Код
      /**
 *
 * Custom Header 1.0.0
 * Кастомная шапка
 *
 * Copyright 2025 Mihail Pridannikov
 *
 * Released on: July 30, 2025
 *
 */

window.CustomHeader = function(settingsCustom) {
    this.deepMergeObjects = function (obj1, obj2) {
        const result = {};
        for (const key in obj2) {
            if (obj2.hasOwnProperty(key)) {
                if (typeof obj2[key] === "object" && obj1.hasOwnProperty(key) && typeof obj1[key] === "object") {
                    result[key] = this.deepMergeObjects(obj1[key], obj2[key]);
                } else {
                    result[key] = obj2[key];
                }
            }
        }
        for (const key in obj1) {
            if (obj1.hasOwnProperty(key) && !result.hasOwnProperty(key)) {
                if (typeof obj1[key] === "object") {
                    result[key] = this.deepMergeObjects(obj1[key], {});
                } else {
                    result[key] = obj1[key];
                }
            }
        }
        return result;
    }

    const HEADER = document.querySelector('.header');
    const HEADER_CONTENT = document.querySelector('.header__content');
    const HEADER_TOP = document.querySelector('.header__top');
    const HEADER_MAIN = document.querySelector('.header__main');
    const HEADER_BOTTOM = document.querySelector('.header__bottom');
    let last_scroll = 0;

    const pinSection = {
        height: 0,
        top: 0,
    }

    const DEFAULT_SETTINGS = {
        fixed: false
    }

    let settings = this.deepMergeObjects(DEFAULT_SETTINGS, settingsCustom);

    this.init = () => {
        const CATALOG = document.querySelector('.header-catalog');
        if (CATALOG) {
            CATALOG.querySelector('.header-catalog__button-link').addEventListener('click', (e) => this.handlerClickOnCatalogLink(e, CATALOG));
        }

        this.setHeightHeader(this.calcHeightHeader());
        this.pinHeaderAll();
        this.getDataAboutPinSection();
        this.pinningHeaderSection();

        window.addEventListener("scroll", this.handlerWindowScroll);
        document.addEventListener("click", (e) => this.handlerClickOnDocument(e, CATALOG));
        document.body.addEventListener('keydown', e => this.handlerClickOnEsc(e, CATALOG), {passive: true});
    }

    this.handlerClickOnCatalogLink = (e, catalog) => {
        e.preventDefault();
        this.openCatalog(catalog);
    }
    this.openCatalog = (catalog) => {
        catalog.classList.toggle('is-opened');
    }
    this.closeCatalog = (catalog) => {
        catalog.classList.remove('is-opened');
    }

    this.calcHeightHeader = (height) => {
        let headerHeight = 0;
        HEADER.querySelectorAll('.header__line').forEach(line => {
            headerHeight += line.clientHeight;
        });
        return headerHeight;
    }
    this.setHeightHeader = (value) => {
        HEADER.style.height = value + 'px';
    }
    this.pinHeaderAll = () => {
        if (typeof settings.fixed === "boolean" && settings.fixed) {
            HEADER_CONTENT.classList.add('is-fixed');
        }
    }
    this.getDataAboutPinSection = () => {
        if (typeof settings.fixed === "string") {
            pinSection.height = HEADER.querySelector(settings.fixed).clientHeight;
            pinSection.top = HEADER.querySelector(settings.fixed).offsetTop;
        }
    }
    this.pinningHeaderSection = () => {
        if (typeof settings.fixed === "string") {
            if (HEADER_TOP && !HEADER_BOTTOM) {
                if(window.scrollY > last_scroll) {
                    if (document.documentElement.scrollTop >= HEADER.querySelector(settings.fixed).offsetTop) {
                        this.pinHeaderSection();
                    }
                } else {
                    if (document.documentElement.scrollTop <= pinSection.top) {
                        this.unpinHeaderSection();
                        HEADER_CONTENT.removeAttribute('style');
                    }
                }
            }
            if (!HEADER_TOP && HEADER_BOTTOM) {
                if(window.scrollY > last_scroll) {
                    if (document.documentElement.scrollTop >= HEADER.querySelector(settings.fixed).offsetTop) {
                        this.pinHeaderSection();
                        HEADER_CONTENT.style.paddingTop = HEADER.querySelector(settings.fixed).clientHeight + 'px';
                    }
                } else {
                    if (document.documentElement.scrollTop <= pinSection.top) {
                        this.unpinHeaderSection();
                        HEADER_CONTENT.removeAttribute('style');
                    }
                }
            }
            if (!HEADER_TOP && !HEADER_BOTTOM) {
                if(window.scrollY > last_scroll) {
                    if (document.documentElement.scrollTop >= HEADER.querySelector(settings.fixed).offsetTop) {
                        this.pinHeaderSection();
                    }
                } else {
                    if (document.documentElement.scrollTop <= pinSection.top) {
                        this.unpinHeaderSection();
                        HEADER_CONTENT.removeAttribute('style');
                    }
                }
            }
            if (HEADER_TOP && HEADER_BOTTOM) {
                if(window.scrollY > last_scroll) {
                    if (document.documentElement.scrollTop >= HEADER.querySelector(settings.fixed).offsetTop) {
                        this.pinHeaderSection();
                        HEADER_CONTENT.style.paddingTop = HEADER.querySelector(settings.fixed).clientHeight + 'px';
                    }
                } else {
                    if (document.documentElement.scrollTop <= pinSection.top) {
                        this.unpinHeaderSection();
                        HEADER_CONTENT.removeAttribute('style');
                    }
                }
            }
        }
    }
    this.pinHeaderSection = () => {
        HEADER.querySelector(settings.fixed).classList.add('is-fixed');
    }
    this.unpinHeaderSection = () => {
        HEADER.querySelector(settings.fixed).classList.remove('is-fixed');
    }

    this.handlerWindowScroll = (e) => {
        this.pinningHeaderSection();
        last_scroll = window.scrollY;
    }
    this.handlerClickOnDocument = (e, catalog) => {
        const target = e.target;
        const its_element_1 = target === catalog || catalog.contains(target);
        if (!its_element_1) {
            this.closeCatalog(catalog);
        }
    }
    this.handlerClickOnEsc = function(e, catalog) {
        e.keyCode === 27 && this.closeCatalog(catalog);
    }

    HEADER ? this.init() : null;
}
    

Changelogs

v1.0.0
latest
от 30.07.2025
  • Открыть/закрыть меню каталога
  • Закрепление шапки