vue组件开发——将弹层放置于 body 内,不受父级元素影响,在浏览器窗口改变或滚动时,依然跟随目标元素

问题描述

elementUI和iview中select选择框都有属性配置:是否将弹层放置于 body 内,它将不受父级样式影响,从而达到更好的效果。如果父级弹框设置了overflow: hidden,弹层也能正常展示并且超出父级弹框。
但项目开发中经常会遇到需要自定义重写select的情况,也需要支持这种效果。

vue组件开发——将弹层放置于 body 内,不受父级元素影响,在浏览器窗口改变或滚动时,依然跟随目标元素_第1张图片

1.将组件中需要动态展示的DOM添加到body中
  • {{item.label}}
.select-option-wrap{
    position: absolute;
    min-width: 200px;
    max-height: 200px;
    min-height: 32px;
    left: 0;
    z-index: 1500;
    visibility: hidden;
  }
computed: {
    matchDom () { // 匹配框,需要相对于body
      return this.$el.querySelector('.select-option-wrap')
    },
    matchParent () { // 匹配框父级
      return this.$el.querySelector('.select-model')
    }
  }
 mounted () {
    this.$nextTick(() => {
      const body = document.querySelector('body')
      // 将匹配DOM添加到body中
      if (body.append) { // 在IE11中 document.appendChild会报错: javascript runtime error:HierarchyRequestError
        body.append(this.matchDom)
      } else {
        body.appendChild(this.matchDom)
      }
    })
  },
2.计算当前匹配DOM相对于body的位置
checkTransfer () {
      if (this.isFocus) { // 聚焦时,需要计算当前匹配DOM的位置
        let bodyHeight = document.documentElement.clientHeight // body 可视区域高度
        let matchHeight = this.matchDom.clientHeight // 匹配DOM的高度
        let rect = this.matchParent.getBoundingClientRect() // 取出匹配父级DOM的矩形对象
        // getBoundingClientRect.bottom为元素下边与页面上边的距离,所以元素下边与页面下边距离 = 页面高度 - getBoundingClientRect.bottom
        let bottom = bodyHeight - rect.bottom
        this.matchDom.style.visibility = 'visible'
        this.matchDom.style.left = rect.left + 'px' // 匹配DOM的left与父级一致
        if (bottom >= matchHeight) { // 父级距离页面下边的高度大于等于匹配DOM的高度,则往下展示
          this.matchDom.style.bottom = 'auto'
          this.matchDom.style.top = (rect.top + rect.height) + 'px' // 匹配DOM的top = 父级矩形对象top + 父级的高度
        } else { // 父级距离页面下边的高度小玉匹配DOM的高度,则往上展示
          this.matchDom.style.top = 'auto'
          this.matchDom.style.bottom = (bottom + rect.height) + 'px' // 匹配DOM的bottom = 父级矩形对象bottom + 父级的高度
        }
      } else { // 不聚焦则直接隐藏
        this.matchDom.style.visibility = 'hidden'
      }
    }
watch: {
    isFocus () {
      this.checkTransfer()
    }
  }
3.监听浏览器窗口大小改变和滚动事件,动态改变匹配DOM的位置
mounted () {
    // 组件监听页面resize只能用addEventListener,否则不会生效
    window.addEventListener('resize', this.checkTransfer, false)
    // 监听scroll事件的事件传递必须使用捕获阶段,让外部元素事件先触发
    document.addEventListener('scroll', this.checkTransfer, true)
  }
beforeDestroy () {
    // 当DOM元素与事件拥有不同的生命周期时,倘若不remove掉eventListener就有可能导致内存泄漏
    window.removeEventListener('resize', this.checkTransfer, false)
    document.removeEventListener('scroll', this.checkTransfer, true)
  },
效果

vue组件开发——将弹层放置于 body 内,不受父级元素影响,在浏览器窗口改变或滚动时,依然跟随目标元素_第2张图片


附上完整代码





你可能感兴趣的:(javascript,前端,vue.js,css3,css)