Position 浮层定位

简介

以往的定位是基于popper.js定位引擎改造而来,所使用的版本是v2.10.2,详见官网Github。目前本框架使用axPopupPosition插件进行浮层定位。

特点

该定位插件具备如下特性:

  • 功能单一,代码量少。
  • 支持已显示的DOM节点定位和不可见DOM节点定位(visibility:hidden)
  • 不支持内存中节点和display:none隐藏元素定位
  • 支持三种监听方式:scroll(页面滚动)、resize(窗口尺寸变化)和mutation(body节点变化)
  • 支持13种位置定位
  • 支持自适应边界并自动修改位置
  • 支持修改button按钮,即可转移浮层
  • 支持手动重置定位

使用方法

完整写法:axPopupPosition(btnDom, boxDom, options),可简写为:axPopupPosition(btnDom, boxDom),参数说明如下:

  • btnDom:必填项,定位按钮,可以填"#id"、".className"、"div"等原生选择器以及Dom对象。
  • boxDom:必填项,定位浮层,可以填"#id"、".className"、"div"等原生选择器以及Dom对象。
  • node:必填项,父节点名称,比如div,span,a等。
  • options:选填项,插件参数,详细如下:
    • placement:位置,字符串格式,默认top,可填:left、left-start、left-end,right、right-start、right-end,top、top-start、top-end,bottom、bottom-start、bottom-end和center,其中center将使用fixed绝对居中定位
    • arrowShow:是否显示三角指示箭头,布尔值,默认true显示,可以false不显示。
    • arrowOffset:箭头偏移值,字符串格式,默认2.8rem(当*-start或*-end时并且btn尺寸大于box一半,三角指示箭头的边距,单位可以是px或rem,注意1rem=10px)
    • gap:浮层与按钮的间距,字符串格式,默认0.8rem(单位可以是px或rem,注意1rem=10px)
    • observer:监听观察对象,可以填"#id"、".className"、"div"等原生选择器以及Dom对象,默认是document.body
    • onInit:监听函数,监听初始化,支持参数placement
    • onUpdated:监听函数,监听更新位置,支持参数placement
需要注意的是,由于定位浮层是使用position:absolute绝对定位,所以不应该将浮层放在某相对定位的容器里,而应该放在body之下。

操作方法

插件实例存在可使用的变量:

  • this.btnDom 按钮节点,是一个对象
  • this.btnData 按钮的数据,是一个对象,包含按钮的宽、高、边距等
  • this.boxDom 浮层节点,是一个对象
  • this.boxData 浮层的数据,是一个对象,包含浮层的宽、高、边距等
  • this.browserData 浏览器可见窗口的数据,是一个对象,包含浮窗口的宽和高
  • this.placement 当前浮层的位置
  • this.arrow 三角指示箭头节点,是一个对象

插件实例存在可使用的操作方法:

  • this.update(callback) 更新位置,支持callback回调,回调函数支持placement参数
  • this.change(btn,callback) 更新定位按钮,支持新按钮节点和callback回调:
    • btn:新按钮节点,可以填"#id"、".className"、"div"等原生选择器以及Dom对象。
    • callback:重定位后的回调函数,支持参数placement即当前的浮层位置。

监听事件

本插件功能单一,但是也支持两个监听事件。具体事件说明如下:

  • onInit 初始化后执行,无参数
  • update/onUpdate 更新位置后执行,支持参数placement

示例:现有浮层定位

如果浮层已经写入了页面,同时不是display:none的隐藏状态,则可直接定位到按钮上。

为了避免占位或者跳跃,可事先对浮层使用visibility:hidden属性隐藏,定位完成了才显示出来。

如果浮层是display:none的隐藏状态,则应该先删除display:none再追加visibility:hidden属性。

关于两种隐藏状态display:none和visibility:hidden的区别请自行查阅资料。

  •                                             <a href="###" class="ax-btn ax-primary" id="btn01">已显示浮层</a>
                                                <a href="###" class="ax-btn ax-primary" id="btn02">不可见浮层</a>
                                                <a href="###" class="ax-btn ax-primary" id="btn03">display:none浮层</a>
                                                <!--box要放在body之下-->
                                                <div class="box" id="box01">已存在并显示在页面的浮层</div>
                                                <div class="box" id="box02">已存在但不显示在页面的浮层</div>
                                                <div class="box" id="box03">已存在但display:none的浮层</div>
                                            
  •                                             let btn01 = document.querySelector('#btn01'),
                                                    btn02 = document.querySelector('#btn02'),
                                                    btn03 = document.querySelector('#btn03'),
                                                    box01 = document.querySelector('#box01'),
                                                    box02 = document.querySelector('#box02');
                                                    box03 = document.querySelector('#box03');
                                                new axPopupPosition(btn01, box01);
                                                new axPopupPosition(btn02, box02, {
                                                    placement: 'bottom-end',
                                                    //初始化后使之可见
                                                    onInit: function () {
                                                        this.boxDom.style.visibility = 'visible';
                                                    }
                                                });
                                                box03.style.display = 'block';
                                                box03.style.visibility = 'hidden';
                                                new axPopupPosition(btn03, box03, {
                                                    placement: 'right',
                                                    //初始化后使之可见
                                                    onInit: function () {
                                                        this.boxDom.style.visibility = 'visible';
                                                    }
                                                });
                                                btn01.onclick = () => {
                                                    if (getComputedStyle(box01).visibility === 'visible') {
                                                        box01.style.visibility = 'hidden';
                                                    } else {
                                                        box01.style.visibility = 'visible';
                                                    }
                                                }
                                                btn02.onclick = () => {
                                                    if (getComputedStyle(box02).visibility === 'visible') {
                                                        box02.style.visibility = 'hidden';
                                                    } else {
                                                        box02.style.visibility = 'visible';
                                                    }
                                                }
                                                
                                                btn03.onclick = () => {
                                                    if (getComputedStyle(box03).visibility === 'visible') {
                                                        box03.style.visibility = 'hidden';
                                                    } else {
                                                        box03.style.visibility = 'visible';
                                                    }
                                                }
                                            
  •                                             .box {
                                                    width: 30rem;
                                                    height: 30rem;
                                                    padding: 1.4rem;
                                                    box-sizing: border-box;
                                                    background-color: aliceblue;
                                                    border: 1px solid #ebebeb;
                                                    position: absolute;
                                                    z-index: 9;
                                                }
                                        
                                                .box [arrow] {
                                                    width: 1.0rem;
                                                    height: 1.0rem;
                                                    box-shadow: 1px 1px 0 #ebebeb;
                                                    background-color: inherit;
                                                }
                                        
                                                .box[placement^='top'] [arrow] {
                                                    bottom: -0.5rem;
                                                    transform: rotate(45deg);
                                                }
                                        
                                                .box[placement^='bottom'] [arrow] {
                                                    top: -0.5rem;
                                                    transform: rotate(-135deg);
                                                }
                                        
                                                .box[placement^='left'] [arrow] {
                                                    right: -0.5rem;
                                                    transform: rotate(-45deg);
                                                }
                                        
                                                .box[placement^='right'] [arrow] {
                                                    left: -0.5rem;
                                                    transform: rotate(135deg);
                                                }
                                        
                                                #box02 {
                                                    visibility: hidden;
                                                }
                                                #box03 {
                                                    display: none;
                                                }
                                            

示例:手动创建浮层定位

如果浮层节点还未创建,可手动创建节点并append到body中;如果浮层存在于内存中,则先append到body中。确保浮层并不是display:none的隐藏状态则可实施定位。

本示例只是展示创建和删除,更多细节未考虑。

  •                                             <a href="###" class="ax-btn ax-primary" id="btn04">创建浮层并定位</a>
                                                <a href="###" class="ax-btn" id="btn05">清除浮层</a>
                                            
  •                                             let elem = axAddElem('div', { class: 'box' }, '手动创建的浮层'),
                                                    btn04 = document.querySelector('#btn04'),
                                                    btn05 = document.querySelector('#btn05');
                                                btn04.onclick = function () {
                                                    //追加到页面
                                                    document.body.appendChild(elem);
                                                    //定位
                                                    new axPopupPosition(this, elem);
                                                }
                                                btn05.onclick = function () {
                                                    //删除节点
                                                    elem.remove();
                                                }
                                            
  •                                             .box {
                                                    width: 30rem;
                                                    height: 30rem;
                                                    padding: 1.4rem;
                                                    box-sizing: border-box;
                                                    background-color: aliceblue;
                                                    border: 1px solid #ebebeb;
                                                    position: absolute;
                                                    z-index: 9;
                                                }
                                        
                                                .box [arrow] {
                                                    width: 1.0rem;
                                                    height: 1.0rem;
                                                    box-shadow: 1px 1px 0 #ebebeb;
                                                    background-color: inherit;
                                                }
                                        
                                                .box[placement^='top'] [arrow] {
                                                    bottom: -0.5rem;
                                                    transform: rotate(45deg);
                                                }
                                        
                                                .box[placement^='bottom'] [arrow] {
                                                    top: -0.5rem;
                                                    transform: rotate(-135deg);
                                                }
                                        
                                                .box[placement^='left'] [arrow] {
                                                    right: -0.5rem;
                                                    transform: rotate(-45deg);
                                                }
                                        
                                                .box[placement^='right'] [arrow] {
                                                    left: -0.5rem;
                                                    transform: rotate(135deg);
                                                }
                                            

示例:重新定位

为了提高页面渲染效率,减少节点创建和删除操作,可对某一个浮层进行重定位,也就是将浮层定位到其他按钮。使用change(btn,callback)方法实现重定位。

  •                                             <a href="###" class="ax-btn ax-primary" id="btn06">创建浮层并定位</a>
                                                <a href="###" class="ax-btn" id="btn07">重定位</a>
                                                <a href="###" class="ax-btn" id="btn08">清除浮层</a>
                                            
  •                                             let elem02 = axAddElem('div', { class: 'box' }, '将要重定位的浮层'),
                                                    btn06 = document.querySelector('#btn06'),
                                                    btn07 = document.querySelector('#btn07'),
                                                    btn08 = document.querySelector('#btn08'),
                                                    ins01 = new axPopupPosition(btn06, elem02);
                                                btn06.onclick = function () {
                                                    //浮层不存在于页面
                                                    if (!getComputedStyle(elem02).display || getComputedStyle(elem02).display === 'none') {
                                                        //追加到页面
                                                        document.body.appendChild(elem02);
                                                        //更新位置
                                                        ins01.update();
                                                    }
                                                }
                                                btn07.onclick = function () {
                                                    //浮层不存在于页面
                                                    if (!getComputedStyle(elem02).display || getComputedStyle(elem02).display === 'none') {
                                                        //追加到页面
                                                        document.body.appendChild(elem02);
                                                    }
                                                    ins01.change(this, (placement) => {
                                                        console.log('当前位置是:' + placement)
                                                    });
                                                }
                                                btn08.onclick = function () {
                                                    //删除节点
                                                    elem02.remove();
                                                }
                                            
  •                                             .box {
                                                    width: 30rem;
                                                    height: 30rem;
                                                    padding: 1.4rem;
                                                    box-sizing: border-box;
                                                    background-color: aliceblue;
                                                    border: 1px solid #ebebeb;
                                                    position: absolute;
                                                    z-index: 9;
                                                }
                                        
                                                .box [arrow] {
                                                    width: 1.0rem;
                                                    height: 1.0rem;
                                                    box-shadow: 1px 1px 0 #ebebeb;
                                                    background-color: inherit;
                                                }
                                        
                                                .box[placement^='top'] [arrow] {
                                                    bottom: -0.5rem;
                                                    transform: rotate(45deg);
                                                }
                                        
                                                .box[placement^='bottom'] [arrow] {
                                                    top: -0.5rem;
                                                    transform: rotate(-135deg);
                                                }
                                        
                                                .box[placement^='left'] [arrow] {
                                                    right: -0.5rem;
                                                    transform: rotate(-45deg);
                                                }
                                        
                                                .box[placement^='right'] [arrow] {
                                                    left: -0.5rem;
                                                    transform: rotate(135deg);
                                                }
                                            
已存在并显示在页面的浮层
已存在但不显示在页面的浮层
已存在但display:none的浮层