Loomia logo.png
Menu
HomePricingBlogWork
Book a 15-min intro callhello@loomia.com
class MenuToggle {
    constructor(config = {}) {
        this.config = {
            toggleSelector: '[data-menu-toggle]',
            overlaySelector: '[data-menu-overlay]',
            openText: 'Close',
            closedText: 'Menu',
            animationDuration: 100,
            staggerDelay: 80,
            ...config
        };
        
        this.isOpen = false;
        this.initialized = false;
        
        this.init();
    }
    
    init() {
        if (this.initialized) return;
        
        this.elements = this.getElements();
        if (!this.elements.toggle || !this.elements.overlay) {
            return;
        }
        
        this.injectStyles();
        this.forceInitialStateImmediate();
        this.createArrow();
        this.bindEvents();
        this.setAriaAttributes();
        this.setupMenuItems();
        
        this.initialized = true;
    }
    
    forceInitialStateImmediate() {
        this.elements.overlay.style.transform = 'translateY(-100%)';
        this.elements.overlay.style.transition = 'none';
        this.elements.overlay.classList.add('menu-closed');
        this.isOpen = false;
        
        setTimeout(() => {
            this.elements.overlay.style.transition = 'transform 0.6s cubic-bezier(0.4, 0.0, 0.2, 1)';
        }, 50);
    }
    
    getElements() {
        return {
            toggle: document.querySelector(this.config.toggleSelector),
            overlay: document.querySelector(this.config.overlaySelector)
        };
    }
    
    setupMenuItems() {
        this.menuItems = this.elements.overlay.querySelectorAll('*:not(style):not(script)');
        this.menuItems = Array.from(this.menuItems).filter(item => {
            return item.textContent.trim().length > 0 && 
                   !item.querySelector('*') && 
                   item.offsetParent !== null;
        });
        
        this.menuItems.forEach((item, index) => {
            item.style.opacity = '0';
            item.style.transform = 'translateY(30px)';
            item.style.transition = `opacity 0.5s ease ${index * this.config.staggerDelay}ms, transform 0.5s ease ${index * this.config.staggerDelay}ms`;
        });
    }
    
    injectStyles() {
        if (document.getElementById('menu-toggle-styles')) return;
        
        const style = document.createElement('style');
        style.id = 'menu-toggle-styles';
        style.textContent = `
            ${this.config.overlaySelector} {
                position: fixed !important;
                top: 0 !important;
                left: 0 !important;
                width: 100vw !important;
                height: 100vh !important;
                z-index: 9999 !important;
            }
            
            body.menu-open .brx-header {
                z-index: 1 !important;
            }
            
            .menu-arrow {
                display: inline-block;
                margin-right: 8px;
                transition: transform 0.4s cubic-bezier(0.4, 0.0, 0.2, 1);
                width: 8px;
                height: 8px;
                border-right: 2px solid currentColor;
                border-bottom: 2px solid currentColor;
                transform: rotate(45deg);
                transform-origin: center;
                vertical-align: middle;
                position: relative;
                top: -1px;
            }
            
            .menu-arrow.rotated {
                transform: rotate(-135deg);
            }
            
            body.menu-open {
                overflow: hidden !important;
            }
        `;
        document.head.appendChild(style);
    }
    
    createArrow() {
        this.arrow = document.createElement('span');
        this.arrow.className = 'menu-arrow';
        this.arrow.setAttribute('aria-hidden', 'true');
        
        this.textNode = document.createTextNode(this.config.closedText);
        
        this.elements.toggle.innerHTML = '';
        this.elements.toggle.appendChild(this.arrow);
        this.elements.toggle.appendChild(this.textNode);
    }
    
    setAriaAttributes() {
        this.elements.toggle.setAttribute('aria-expanded', 'false');
        this.elements.toggle.setAttribute('aria-controls', this.elements.overlay.id || 'menu-overlay');
        this.elements.overlay.setAttribute('aria-hidden', 'true');
    }
    
    bindEvents() {
        this.boundClickHandler = this.handleClick.bind(this);
        this.boundKeyHandler = this.handleKeydown.bind(this);
        
        this.elements.toggle.addEventListener('click', this.boundClickHandler);
        this.elements.toggle.addEventListener('keydown', this.boundKeyHandler);
    }
    
    handleKeydown(e) {
        if (e.key === 'Enter' || e.key === ' ') {
            e.preventDefault();
            this.toggle();
        }
        if (e.key === 'Escape' && this.isOpen) {
            this.close();
        }
    }
    
    handleClick(e) {
        e.preventDefault();
        this.toggle();
    }
    
    toggle() {
        this.isOpen ? this.close() : this.open();
    }
    
    open() {
        if (this.isOpen) return;
        
        document.body.classList.add('menu-open');
        this.elements.overlay.classList.remove('menu-closed');
        this.elements.overlay.style.transform = 'translateY(0)';
        this.textNode.textContent = this.config.openText;
        this.arrow.classList.add('rotated');
        
        setTimeout(() => {
            this.menuItems.forEach((item, index) => {
                setTimeout(() => {
                    item.style.opacity = '1';
                    item.style.transform = 'translateY(0)';
                }, index * this.config.staggerDelay);
            });
        }, 300);
        
        this.elements.toggle.setAttribute('aria-expanded', 'true');
        this.elements.overlay.setAttribute('aria-hidden', 'false');
        
        this.isOpen = true;
    }
    
    close() {
        if (!this.isOpen) return;
        
        document.body.classList.remove('menu-open');
        
        this.menuItems.forEach((item) => {
            item.style.opacity = '0';
            item.style.transform = 'translateY(30px)';
        });
        
        setTimeout(() => {
            this.elements.overlay.style.transform = 'translateY(-100%)';
            this.elements.overlay.classList.add('menu-closed');
        }, 200);
        
        this.textNode.textContent = this.config.closedText;
        this.arrow.classList.remove('rotated');
        
        this.elements.toggle.setAttribute('aria-expanded', 'false');
        this.elements.overlay.setAttribute('aria-hidden', 'true');
        
        this.isOpen = false;
    }
    
    destroy() {
        if (!this.initialized) return;
        
        this.elements.toggle.removeEventListener('click', this.boundClickHandler);
        this.elements.toggle.removeEventListener('keydown', this.boundKeyHandler);
        
        document.body.classList.remove('menu-open');
        
        const style = document.getElementById('menu-toggle-styles');
        if (style) style.remove();
        
        this.initialized = false;
    }
}

document.addEventListener('DOMContentLoaded', () => {
    new MenuToggle();
});
Tantangan Iklan Meta Anda

Mengapa Iklan Meta Anda Belum Memberikan Hasil Maksimal

Banyak bisnis kesulitan mencapai ROI positif dari kampanye iklan Meta mereka. Apakah Anda menghadapi tantangan dalam menargetkan audiens yang tepat, mengoptimalkan anggaran, atau menganalisis kinerja kampanye secara efektif?

Dapatkan Audit Iklan GratisKonsultasi Sekarang
Anggaran Terbuang & Potensi HilangScroll to explore

"Kerja sama dengan tim ini benar-benar mengubah performa iklan Meta kami. ROI meningkat signifikan dan target audiens tercapai dengan presisi yang luar biasa. Sangat direkomendasikan!"

Woman
Budi SantosoManajer Pemasaran, PT Maju Jaya

FAQs

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero uctus non, massa.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.

Dipercaya oleh Brand Terkemuka

Kami bangga telah bermitra dengan berbagai perusahaan untuk mengoptimalkan kampanye iklan Meta mereka, mencapai hasil yang signifikan dan pertumbuhan bisnis yang berkelanjutan.

Saatnya Mengubah Klik Menjadi Keuntungan Nyata

Wujudkan Potensi Penuh Iklan Meta Anda Sekarang

Jangan biarkan investasi iklan Meta Anda sia-sia. Tim ahli kami siap membantu Anda merancang, mengelola, dan mengoptimalkan kampanye untuk hasil yang terukur dan ROI yang maksimal. Ambil langkah pertama menuju kesuksesan digital Anda hari ini.

Mulai Konsultasi GratisLihat Studi Kasus Kami
Tagline

An eye-catching heading to grab attention

This is where your story begins — a short and engaging paragraph that draws readers in and makes them want to explore more.

ButtonButton