欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

javascript实现弹出层

程序员文章站 2022-05-06 09:40:33
...

弹出层和弹窗相似但是并不相同;弹窗和弹出层都是由用户触发的显示提示信息的弹出面板;但是弹窗只是显示一些信息,没有太多的复杂的交互事件;而弹层类似一个整个页面,可以实现页面的所有功能;
现在前端弹层使用的很频繁,如支付宝支付弹层等…所以掌握弹层是一个很重要的技能。如果只是简单的隐藏和切换,当然就不必说,我要说的html+animate+es6实现弹层;
javascript实现弹出层
javascript实现弹出层
javascript实现弹出层
javascript实现弹出层;
html弹层结构:

<main class="main">
        <header class="head flex-center gray-theme">This is Header</header>
        <section class="body flex-center">
            <ul class="btn-list flex-center btn-group">
                <li class="btn-box"><span class="btn inline-flex-center" data-position="top">上弹层</span></li>
                <li class="btn-box"><span class="btn inline-flex-center" data-position="right">右弹层</span></li>
                <li class="btn-box"><span class="btn inline-flex-center" data-position="bottom">下弹层</span></li>
                <li class="btn-box"><span class="btn inline-flex-center" data-position="left">左弹层</span></li>
            </ul>
        </section>
        <footer class="foot flex-center gray-theme">This is Footer</footer>
        <!-- popup -->
        <section class="popup flex-center hide hidden">
            <section class="popup-bg"></section>
            <section class="popup-wrapper">
                <header class="title head flex-center"></header>
                <section class="container body flex-center"></section>
                <footer class="btn-wrapper foot flex-center">
                    <ul class="btn-list flex-center">
                        <li class="btn-box"><span class="btn cancel inline-flex-center">取消</span></li>
                        <li class="btn-box"><span class="btn confirm inline-flex-center">确认</span></li>
                    </ul>
                </footer>
            </section>
        </section>
    </main>

css代码:

@mixin slide-top{
    0%{
        -webkit-transform: translate3d(0, -400%, 0);
        -moz-transform: translate3d(0, -400%, 0);
        -ms-transform: translate3d(0, -400%, 0);
        -o-transform: translate3d(0, -400%, 0);
        transform: translate3d(0, -400%, 0);
    }
    100%{
        -webkit-transform: translate3d(0, 0, 0);
        -moz-transform: translate3d(0, 0, 0);
        -ms-transform: translate3d(0, 0, 0);
        -o-transform: translate3d(0, 0, 0);
        transform: translate3d(0, 0, 0);
    }
}

@keyframes slideTop{
    @include slide-top;
}

@-webkit-keyframes slideTop{
    @include slide-top;
}

@mixin slide-top-hide{
    0%{
        -webkit-transform: translate3d(0, 0, 0);
        -moz-transform: translate3d(0, 0, 0);
        -ms-transform: translate3d(0, 0, 0);
        -o-transform: translate3d(0, 0, 0);
        transform: translate3d(0, 0, 0);
    }
    100%{
        -webkit-transform: translate3d(0, -400%, 0);
        -moz-transform: translate3d(0, -400%, 0);
        -ms-transform: translate3d(0, -400%, 0);
        -o-transform: translate3d(0, -400%, 0);
        transform: translate3d(0, -400%, 0);
    }
}

@keyframes slideTopHide{
    @include slide-top-hide;
}

@-webkit-keyframes slideTopHide{
    @include slide-top-hide;
}

@mixin slide-right{
    0%{
        -webkit-transform: translate3d(400%, 0, 0);
        -moz-transform: translate3d(400%, 0, 0);
        -ms-transform: translate3d(400%, 0, 0);
        -o-transform: translate3d(400%, 0, 0);
        transform: translate3d(400%, 0, 0);
    }
    100%{
        -webkit-transform: translate3d(0, 0, 0);
        -moz-transform: translate3d(0, 0, 0);
        -ms-transform: translate3d(0, 0, 0);
        -o-transform: translate3d(0, 0, 0);
        transform: translate3d(0, 0, 0);
    }
}

@keyframes slideRight{
    @include slide-right;
}

@-webkit-keyframes slideRight{
    @include slide-right;
}

@mixin slide-right-hide{
    0%{
        -webkit-transform: translate3d(0, 0, 0);
        -moz-transform: translate3d(0, 0, 0);
        -ms-transform: translate3d(0, 0, 0);
        -o-transform: translate3d(0, 0, 0);
        transform: translate3d(0, 0, 0);
    }
    100%{
        -webkit-transform: translate3d(400%, 0, 0);
        -moz-transform: translate3d(400%, 0, 0);
        -ms-transform: translate3d(400%, 0, 0);
        -o-transform: translate3d(400%, 0, 0);
        transform: translate3d(400%, 0, 0);
    }
}

@keyframes slideRightHide{
    @include slide-right-hide;
}

@-webkit-keyframes slideRightHide{
    @include slide-right-hide;
}

@mixin slide-bottom{
    0%{
        -webkit-transform: translate3d(0, 400%, 0);
        -moz-transform: translate3d(0, 400%, 0);
        -ms-transform: translate3d(0, 400%, 0);
        -o-transform: translate3d(0, 400%, 0);
        transform: translate3d(0, 400%, 0);
    }
    100%{
        -webkit-transform: translate3d(0, 0, 0);
        -moz-transform: translate3d(0, 0, 0);
        -ms-transform: translate3d(0, 0, 0);
        -o-transform: translate3d(0, 0, 0);
        transform: translate3d(0, 0, 0);
    }
}

@keyframes slideBottom{
    @include slide-bottom;
}

@-webkit-keyframes slideBottom{
    @include slide-bottom;
}

@mixin slide-bottom-hide{
    0%{
        -webkit-transform: translate3d(0, 0, 0);
        -moz-transform: translate3d(0, 0, 0);
        -ms-transform: translate3d(0, 0, 0);
        -o-transform: translate3d(0, 0, 0);
        transform: translate3d(0, 0, 0);
    }
    100%{
        -webkit-transform: translate3d(0, 400%, 0);
        -moz-transform: translate3d(0, 400%, 0);
        -ms-transform: translate3d(0, 400%, 0);
        -o-transform: translate3d(0, 400%, 0);
        transform: translate3d(0, 400%, 0);
    }
}

@keyframes slideBottomHide{
    @include slide-bottom-hide;
}

@-webkit-keyframes slideBottomHide{
    @include slide-bottom-hide;
}

@mixin slide-left{
    0%{
        -webkit-transform: translate3d(-400%, 0, 0);
        -moz-transform: translate3d(-400%, 0, 0);
        -ms-transform: translate3d(-400%, 0, 0);
        -o-transform: translate3d(-400%, 0, 0);
        transform: translate3d(-400%, 0, 0);
    }
    100%{
        -webkit-transform: translate3d(0, 0, 0);
        -moz-transform: translate3d(0, 0, 0);
        -ms-transform: translate3d(0, 0, 0);
        -o-transform: translate3d(0, 0, 0);
        transform: translate3d(0, 0, 0);
    }
}

@keyframes slideLeft{
    @include slide-left;
}

@-webkit-keyframes slideLeft{
    @include slide-left;
}

@mixin slide-left-hide{
    0%{
        -webkit-transform: translate3d(0, 0, 0);
        -moz-transform: translate3d(0, 0, 0);
        -ms-transform: translate3d(0, 0, 0);
        -o-transform: translate3d(0, 0, 0);
        transform: translate3d(0, 0, 0);
    }
    100%{
        -webkit-transform: translate3d(-400%, 0, 0);
        -moz-transform: translate3d(-400%, 0, 0);
        -ms-transform: translate3d(-400%, 0, 0);
        -o-transform: translate3d(-400%, 0, 0);
        transform: translate3d(-400%, 0, 0);
    }
}

@keyframes slideLeftHide{
    @include slide-left-hide;
}

@-webkit-keyframes slideLeftHide{
    @include slide-left-hide;
}

@mixin popup-hide{
    0%{
        z-index: 99999;
        opacity: 1;
    }
    100%{
        z-index: -1;
        opacity: 0;
    }
}

@keyframes popupHide{
    @include popup-hide;
}

@-webkit-keyframes popupHide{
    @include popup-hide;
}

.popup{
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: -1;
    opacity: 0;
    .popup-bg{
        position: absolute;
        top: 0;
        left: 0;
        background: rgba(0,0,0, 0.5);
        width: 100%;
        height: 100%;
        z-index: 1;
    }
    .popup-wrapper{
        position: relative;
        width: 70%;
        height: 70%;
        margin: auto;
        background: #fff;
        border-radius: 5%;
        z-index: 100;
    }
    &.hidden{
        display: none;
    }
    &.show{
        z-index: 99999;
        opacity: 1;
        &.top{
            .popup-wrapper{
                -webkit-animation: slideTop 0.6s linear forwards;
                -moz-animation: slideTop 0.6s linear forwards;
                -ms-animation: slideTop 0.6s linear forwards;
                -o-animation: slideTop 0.6s linear forwards;
                animation: slideTop 0.6s linear forwards;
            }
        }
        &.right{
            .popup-wrapper{
                -webkit-animation: slideRight 0.6s linear forwards;
                -moz-animation: slideRight 0.6s linear forwards;
                -ms-animation: slideRight 0.6s linear forwards;
                -o-animation: slideRight 0.6s linear forwards;
                animation: slideRight 0.6s linear forwards;
            }
        }
        &.bottom{
            .popup-wrapper{
                -webkit-animation: slideBottom 0.6s linear forwards;
                -moz-animation: slideBottom 0.6s linear forwards;
                -ms-animation: slideBottom 0.6s linear forwards;
                -o-animation: slideBottom 0.6s linear forwards;
                animation: slideBottom 0.6s linear forwards;
            }
        }
        &.left{
            .popup-wrapper{
                -webkit-animation: slideLeft 0.6s linear forwards;
                -moz-animation: slideLeft 0.6s linear forwards;
                -ms-animation: slideLeft 0.6s linear forwards;
                -o-animation: slideLeft 0.6s linear forwards;
                animation: slideLeft 0.6s linear forwards;
            }
        }
    }
    &.hide{
        -webkit-animation: popupHide 0.7s linear forwards;
        -moz-animation: popupHide 0.7s linear forwards;
        -ms-animation: popupHide 0.7s linear forwards;
        -o-animation: popupHide 0.7s linear forwards;
        animation: popupHide 0.7s linear forwards;
        &.top{
            .popup-wrapper{
                -webkit-animation: slideTopHide 0.6s linear forwards;
                -moz-animation: slideTopHide 0.6s linear forwards;
                -ms-animation: slideTopHide 0.6s linear forwards;
                -o-animation: slideTopHide 0.6s linear forwards;
                animation: slideTopHide 0.6s linear forwards;
            }
        }
        &.right{
            .popup-wrapper{
                -webkit-animation: slideRightHide 0.6s linear forwards;
                -moz-animation: slideRightHide 0.6s linear forwards;
                -ms-animation: slideRightHide 0.6s linear forwards;
                -o-animation: slideRightHide 0.6s linear forwards;
                animation: slideRightHide 0.6s linear forwards;
            }
        }
        &.bottom{
            .popup-wrapper{
                -webkit-animation: slideBottomHide 0.6s linear forwards;
                -moz-animation: slideBottomHide 0.6s linear forwards;
                -ms-animation: slideBottomHide 0.6s linear forwards;
                -o-animation: slideBottomHide 0.6s linear forwards;
                animation: slideBottomHide 0.6s linear forwards;
            }
        }
        &.left{
            .popup-wrapper{
                -webkit-animation: slideLeftHide 0.6s linear forwards;
                -moz-animation: slideLeftHide 0.6s linear forwards;
                -ms-animation: slideLeftHide 0.6s linear forwards;
                -o-animation: slideLeftHide 0.6s linear forwards;
                animation: slideLeftHide 0.6s linear forwards;
            }
        }
    }

}

html,
body,
.main{
    width: 100%;
    height: 100%;
    overflow: hidden;
}

.head,
.body,
.foot{
    width: 100%;
}

.head,
.foot{
    height: 15%;
}

.body{
    height: 70%;
}

.gray-theme{
    background: #333;
    color: #fff;
}

.btn-list{
    width: 40%;
    min-width: 270px;
    height: 38px;
    .btn-box{
        width: 22%;
        height: 100%;
        padding-right: 4%;
        &:last-child{
            padding-right: 0;
        }
    }
}

.btn{
    line-height: 1em;
    width: 100%;
    height: 100%;
    border-radius: 10%;
    font-size: 16px;
    background: green;
    color: #ff0;
    text-align: center;
    vertical-align: middle;
    cursor: pointer;
}

js代码:

class PopupComponent {
        constructor() {
            this.btnGroupEl = document.getElementsByClassName("btn-group")[0];
            this.popupEl = document.getElementsByClassName("popup")[0];
            this.popupBGEl = this.popupEl.querySelector(".popup-bg");
            this.popupTitleEl = this.popupEl.querySelector(".popup-wrapper .title");
            this.popupBodyEl = this.popupEl.querySelector(".popup-wrapper .body");
            this.cancelBtnEl = this.popupEl.querySelector(".popup-wrapper .btn.cancel");
            this.confirmBtnEl = this.popupEl.querySelector(".popup-wrapper .btn.confirm");
            this.popupElClasslist = this.popupEl.classList;
            this.LEGVALS = ["top", "right", "bottom", "left"];
            this.detaultPosition = "";
            this.position = "";
            this.SHOWCLASSNAME = "show";
            this.HIDECLASSNAME = "hide";
            this.HIDDENCLASSNAME = "hidden";
        }

        /**
         * 给弹窗的标题和主体添加内容
         * @param Object        contentObj      传递的对象
         */
        setContentForPopup(contentObj = {'title':'title', 'body': 'body'}) {
            try{
                if (!TB.isObject(contentObj)) {
                    throw new Error("The param of setContentForPopup function error!");
                }
                let value;
                for (let prop in contentObj) {
                    if (!contentObj.hasOwnProperty(prop)) {
                        continue;
                    }

                    if (prop === 'title') {
                        value = contentObj[prop];
                        this.popupTitleEl.innerText = value;
                    }

                    if (prop === 'body') {
                        value = contentObj[prop];
                        this.popupBodyEl.innerText = value;
                    }
                }
            } catch (e) {
                console.log("Popup element is not exist!");
                console.error(e);
            }
        }

        /**
         * 删除弹窗的类名
         * @return void
         */
        deletePopupPreviousClassName() {
            if (this.HIDDENCLASSNAME && KB.checkType.isString(this.HIDDENCLASSNAME)) {
                this.popupElClasslist.remove(this.HIDDENCLASSNAME);
            }

            if (this.detaultPosition && KB.checkType.isString(this.detaultPosition)) {
                this.popupElClasslist.remove(this.detaultPosition);
            }

            this.detaultPosition = this.position;
        }

        /**
         * 弹窗隐藏
         * @return void
         */
        popupHide() {
            if (this.popupElClasslist.contains(this.SHOWCLASSNAME)) {
                this.popupElClasslist.remove(this.SHOWCLASSNAME);
                this.popupElClasslist.add(this.HIDECLASSNAME);
            }
        }

        /**
         * 弹窗显示
         * @return void
         */
        popupShow() {
            this.deletePopupPreviousClassName();            
            if (this.popupElClasslist.contains(this.HIDECLASSNAME)) {
                this.popupElClasslist.remove(this.HIDECLASSNAME);
                this.popupElClasslist.add(this.SHOWCLASSNAME);
                this.popupElClasslist.add(this.position);
            }

            this.setContentForPopup({
                'title': this.position + ' title',
                'body': this.position + ' body',
            });
        }

        /**
         * 按钮容器的点击事件
         * @param  Object       e       点击的事件event
         * @return void
         */
        btnGroupClickEvent(e) {
            let btnEl = e.target || e.srcElement;
            if (!TB.isElement(btnEl)) {
                throw new Error("Get btn element error!");
            }
            this.position = btnEl.getAttribute("data-position");
            if (!TB.isString(this.position) || (this.LEGVALS.indexOf(this.position) < 0)) {
                throw new Error("Can not get position value from btn element!");
            }
            this.popupShow();

        }

        /**
         * 给DOM元素添加点击事件
         * @param Object        elem        添加点击事件的dom元素  
         * @param Function      fn          触发事件调用的回调函数
         */
        addClickEventFormElem(elem, fn) {
            TB.addHandler.call(this, elem, 'click', fn, false);
        }

        /**
         * 初始化函数
         * @return void
         */
        init() {
            this.addClickEventFormElem(this.btnGroupEl, this.btnGroupClickEvent);
            this.addClickEventFormElem(this.cancelBtnEl, this.popupHide);
            this.addClickEventFormElem(this.confirmBtnEl, this.popupHide);
        }
    }

    let popupComponent = new PopupComponent();
    popupComponent.init();

TB.addHandler:

/*
* 给DOM元素添加事件
* @param    void
* @return   callback    function    回调函数
* */
function addHandler(elem, type, callback, useCapture) {
    var checkType = checkArgumentType(),
        _document = document,
        _callback = checkType.isFunction(callback)? callback:function(){},
        _self = this;

    if (!checkType.isElement(elem) || !checkType.isString(type)) {
        return;
    }

    if (_document.addEventListener) {
        addHandler = function(elem, type, callback, useCapture) {
            elem.addEventListener(type, function(e) {
                _callback.call(_self, e);
            }, useCapture || false); 
        }
    } else if (_document.attachEvent) {
        addHandler = function(elem, type, callback, useCapture) {
            elem.attachEvent("on" + type, function(e){
                _callback.apply(_self, [e]);
            }); 
        }
    } else {
        addHandler = function(elem, type, callback, useCapture) {
            elem["on" + type] = function(e) {
                _callback.call(_self, e);
            };
        }
    }
    addHandler(elem, type, callback, useCapture);
}

首先说一下弹窗的DOM结构:
在外层一般是绝对定位,并使用flex布局使得内容居中;它的直接子元素一般有两个,黑色背景层和内容容器;如图所示:
javascript实现弹出层
样式的动画相信前端的同学一般都知道怎么做;但是有一点要注意,不要试图使用display来实现动画
我使用的解决的办法是首先
动画的显示隐藏使用的z-index和opacity;但是这样存在开始时,弹窗会缓缓隐藏,那么我们可以用一个类表示display: none;点击显示弹层时,移除该类即可;
对于js没什么好说的,无非是操作类名;但是有一点要注意事件注册和this的指向;
注意我的addHandler函数,该函数只会判断一次浏览器的环境,不需要反复的判断浏览器的环境;
在btnGroupClickEvent函数中this是指向PopupComponent类,而不是事件的event;所以addHandler函数中首先保存this的指向,然后使用call或者apply改变this的指向