fbpx

Tutorial

Elementor How To BEM Based Reveal Blocks

Uses Plugins

Elementor Free

Description

Referring to my previous video https://youtu.be/KajHhSl27Ro Some users asked about the method I use for “Reveal Blocks”. This video shows how I have done this. I now exclusively use the BEM naming conventions to keep code more consistent.

Steps

Code (click to copy)

JavaScript for Reveal Blocks (javascript)

;((w,d)=>{   
    let revealContainers;
    const blockClass = "reveal-block";
    const triggerOpenElementClass = "reveal-block__trigger--open";
    const triggerCloseElementClass = "reveal-block__trigger--close";
    const triggerToggleElementClass = "reveal-block__trigger--toggle";
    const triggerHoverEventClass = "reveal-block__trigger--hover";
    const triggerMouseventClass = "reveal-block__trigger--click"; /* not really needed as Click is the default */
    const openModifierClass = "reveal-block--open";
    const contentElementClass = "reveal-block__content";

    const init = ()=>{
        revealContainers = d.querySelectorAll( '.' + blockClass);
        revealContainers.forEach( blockEl => {
            let trigger, event;

            /* Toggle mode */
            trigger = blockEl.querySelector('.' + triggerToggleElementClass);
            if(trigger){
                event = trigger.classList.contains(triggerHoverEventClass)? 'hover' : 'click';                
                switch(event){
                    case 'hover':
                        trigger.addEventListener('mouseenter', e => { e.preventDefault(); show(blockEl)});
                        trigger.addEventListener('mouseleave', e => { e.preventDefault(); hide(blockEl)})
                        break;
                    default:
                        trigger.addEventListener('click', e => { e.preventDefault(); toggleReveal(blockEl)})
                        break;
                }          
            }


            /* open mode */
            trigger = blockEl.querySelector('.' + triggerOpenElementClass);
            if(trigger){
                event = trigger.classList.contains(triggerHoverEventClass)? 'hover' : 'click';
                if(event === 'click'){
                    trigger.addEventListener('click', e => { e.preventDefault(); show(blockEl)});
                }else{
                    trigger.addEventListener('mouseenter', e => { e.preventDefault(); show(blockEl)});
                }                      
            }


            /* close mode */
            trigger = blockEl.querySelector('.' + triggerCloseElementClass);
            if(trigger){
                event = trigger.classList.contains(triggerHoverEventClass)? 'hover' : 'click';
                if(event === 'click'){
                    trigger.addEventListener('click', e => { e.preventDefault(); hide(blockEl)});
                }else{
                    trigger.addEventListener('mouseenter', e => { e.preventDefault(); hide(blockEl)});
                }  
            }       
        })
    }

    const show = el =>{
        const blockEl = el.closest('.' + blockClass);
        const contentEl = blockEl.querySelector('.' + contentElementClass);
        blockEl.classList.add(openModifierClass);
        blockEl.style.setProperty('--open-height', contentEl.scrollHeight + 'px');
    }
    const hide = el => {
        const blockEl = el.closest('.' + blockClass);
        blockEl.classList.remove(openModifierClass);
    }

    const toggleReveal = (element)=> {
        const blockEl = element.closest('.' + blockClass);
        if(blockEl.classList.contains(openModifierClass)){
            hide(element);
        }else{
            show(element)
        }
    }

    w.addEventListener('DOMContentLoaded', ()=>{
        init();
    })

})(window, document)

SCSS (scss)

[class*="reveal-block__trigger"]{
    cursor: pointer;
}
/* BLOCK */
.reveal-block {
    --closed-height: 100px;
    --open-height: 200px; /* initial, updated per element by JS */
    --open-duration: 1s;
    --close-duration: 0.5s;

    /* Element */
    &__content{
        max-height: var(--closed-height);
        overflow: hidden;
        transition: max-height var(--close-duration); 

    /* Modifier */
        &--fade{
            mask-image: linear-gradient(to top, transparent 0, black var(--closed-height));
            -webkit-mask-image: linear-gradient(to top, transparent 0, black 70px);
        }       
    }

    /* Modifier */
    &--open{
        /* Element */
        .reveal-block__content{
            max-height: var(--open-height);
            transition: max-height var(--open-duration);

            /* Modifier */
            &--fade{
                    mask-size: 100% calc(100% + var(--closed-height));
                    -webkit-mask-size: 100% calc(100% + var(--closed-height));
            }
        }
        
    }
}

SCSS Transpiled to CSS (css)

Use this if you do not have an SCSSS preprocessor

[class*="reveal-block__trigger"]{
    cursor: pointer;
}
/* BLOCK */
.reveal-block {
    --closed-height: 100px;
    --open-height: 200px; /* initial, updated per element by JS */
    --open-duration: 1s;
    --close-duration: 0.5s;

    /* Element */
    &__content{
        max-height: var(--closed-height);
        overflow: hidden;
        transition: max-height var(--close-duration); 

    /* Modifier */
        &--fade{
            mask-image: linear-gradient(to top, transparent 0, black var(--closed-height));
            -webkit-mask-image: linear-gradient(to top, transparent 0, black 70px);
        }       
    }

    /* Modifier */
    &--open{
        /* Element */
        .reveal-block__content{
            max-height: var(--open-height);
            transition: max-height var(--open-duration);

            /* Modifier */
            &--fade{
                    mask-size: 100% calc(100% + var(--closed-height));
                    -webkit-mask-size: 100% calc(100% + var(--closed-height));
            }
        }
        
    }
}