zoukankan      html  css  js  c++  java
  • js div span等节点添加focus聚焦、blur失焦事件(tabindex属性)

    场景:下拉弹框显示时,想要点击其他地方即隐藏(不使用蒙板,下拉弹框定位到点击显示的位置)

    tabindex可以使得相应的节点具有 focus 和 blur 事件

    tabindex=负值
    (通常是tabindex='-1'),表示元素是可聚焦的,但是不能通过键盘导航来访问到该元素,用JS做页面小组件内部键盘导航的时候非常有用。
    tabindex='0',
    表示元素是可聚焦的,并且可以通过键盘导航来聚焦到该元素,它的相对顺序是当前处于的DOM结构来决定的。
    tabindex=正值,
    表示元素是可聚焦的,并且可以通过键盘导航来访问到该元素;它的相对顺序按照tabindex 的数值递增而滞后获焦。如果多个元素拥有相同的 tabindex,它们的相对顺序按照他们在当前DOM中的先后顺序决定。
    注:tabindex 的最大值不应超过 32767。如果没有指定,它的默认值为 -1。

    tabindex == -1 时无法通过tab键选中该节点
    tabindex == 0 或 1 都可以通过tab键选中该节点(不同的赋值表示不同的优先级)

    template

    <template>
      <div class="dropDownBox">
        <div class="drop-button" tabindex="0" @click="clickFunc" @blur="blurFunc">
          <div> {{ data[activeIndex].text }} </div>
          <div class="icon" :class="isShowBox ? 'rotate' : ''"> </div>
    
          <div class="box" v-show="isShowBox">
            <div 
              class="item" 
              :class="activeIndex === index ? 'select-color' : ''"
              v-for="(item, index) in data"
              :key="index"
              @click="changeOption(index)"
              >
              {{ item.text }}
            </div>
          </div>
        </div>
      </div>
    </template>
    

    script方法

    export default {
      name: 'DropDownBox',
      data() {
        return {
          // 下拉框选项数据
          data: [
            {
              text: 'DOW J',
            },
            {
              text: 'S/P 500',
            },
            {
              text: 'NASDAQ',
            },
          ],
          // 是否显示下拉框
          isShowBox: false,
          // 当前选项
          activeIndex: 0,
        };
      },
      methods: {
        clickFunc() {
          this.isShowBox = !this.isShowBox;
        },
        blurFunc() {
          this.isShowBox = false;
        },
        changeOption(index) {
          if (this.activeIndex === index) {
            return;
          }
          this.activeIndex = index;
        }
      }
    }
    </script>
    

    style

    <style lang="less" scoped>
      .dropDownBox {
        .drop-button {
          position: relative;
          display: flex;
          justify-content: center;
          align-items: center;
          outline: none;
    
          .rotate {
            transform: rotate(180deg);
          }
    
          .icon {
             0;
            height: 0;
            border-left: 6px solid transparent;
            border-right: 6px solid transparent;
            border-top: 6px solid #000;
          }
          .drop-down-box-bg {
            background-color: #e6e6e6;
          }
          .select-color {
            color: #fb7299,
          }
    
          .box {
            position: absolute;
            top: 20px;
            z-index: 11;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            padding: 20px;
            background-color: #fff;
            box-shadow: 0px 3px 29px 0px rgba(59,74,116,0.14);
            border-radius: 8px;
            .item {
              font-size: 16px;
              line-height: 16px;
              font-weight: 500;
              padding: 6px 0;
            }
          }
        }
      }
    </style>
    

    完整代码

    <template>
      <div class="dropDownBox">
        <div class="drop-button" tabindex="0" @click="clickFunc" @blur="blurFunc">
          <div> {{ data[activeIndex].text }} </div>
          <div class="icon" :class="isShowBox ? 'rotate' : ''"> </div>
    
          <div class="box" v-show="isShowBox">
            <div 
              class="item" 
              :class="activeIndex === index ? 'select-color' : ''"
              v-for="(item, index) in data"
              :key="index"
              @click="changeOption(index)"
              >
              {{ item.text }}
            </div>
          </div>
        </div>
      </div>
    </template>
    
    <script>
    export default {
      name: 'DropDownBox',
      data() {
        return {
          // 下拉框选项数据
          data: [
            {
              text: 'DOW J',
            },
            {
              text: 'S/P 500',
            },
            {
              text: 'NASDAQ',
            },
          ],
          // 是否显示下拉框
          isShowBox: false,
          // 当前选项
          activeIndex: 0,
        };
      },
      methods: {
        clickFunc() {
          this.isShowBox = !this.isShowBox;
        },
        blurFunc() {
          this.isShowBox = false;
        },
        changeOption(index) {
          if (this.activeIndex === index) {
            return;
          }
          this.activeIndex = index;
        }
      }
    }
    </script>
    
    <style lang="less" scoped>
      .dropDownBox {
        .drop-button {
          position: relative;
          display: flex;
          justify-content: center;
          align-items: center;
          outline: none;
    
          .rotate {
            transform: rotate(180deg);
          }
    
          .icon {
             0;
            height: 0;
            border-left: 6px solid transparent;
            border-right: 6px solid transparent;
            border-top: 6px solid #000;
          }
          .drop-down-box-bg {
            background-color: #e6e6e6;
          }
          .select-color {
            color: #fb7299,
          }
    
          .box {
            position: absolute;
            top: 20px;
            z-index: 11;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            padding: 20px;
            background-color: #fff;
            box-shadow: 0px 3px 29px 0px rgba(59,74,116,0.14);
            border-radius: 8px;
            .item {
              font-size: 16px;
              line-height: 16px;
              font-weight: 500;
              padding: 6px 0;
            }
          }
        }
      }
    </style>
    

    下拉框滚动遮挡问题

    问题: 因为需要下拉框显示时阻止滚动,这导致显示时可能被遮挡
    解决方法: 先滚到到完整显示,再使用overflow阻止滚动,隐藏时再移除overflow

    完整代码:

    <template>
      <div class="dropDownBoxCover">
        <div class="drop-button" tabindex="0" @click="clickFunc" @blur="blurFunc">
          <div> {{ data[activeIndex].text }} </div>
          <div class="icon" :class="isShowBox ? 'rotate' : ''"> </div>
    
          <div ref="box" class="box" v-show="isShowBox">
            <div 
              class="item" 
              :class="activeIndex === index ? 'select-color' : ''"
              v-for="(item, index) in data"
              :key="index"
              @click="changeOption(index)"
              >
              {{ item.text }}
            </div>
          </div>
        </div>
        <!-- 蒙板 -->
        <div class="cover" v-show="isShowBox"></div>
      </div>
    </template>
    
    <script>
    export default {
      name: 'DropDownBoxCover',
      data() {
        return {
          // 下拉框选项数据
          data: [
            {
              text: 'DOW J',
            },
            {
              text: 'S/P 500',
            },
            {
              text: 'NASDAQ',
            },
          ],
          // 是否显示下拉框
          isShowBox: false,
          // 当前选项
          activeIndex: 0,
        };
      },
      methods: {
        // 点击显示下拉框
        clickFunc() {
          this.isShowBox = !this.isShowBox;
    
          if (this.isShowBox) {
            this.$nextTick(() => {
              // 否则拿不到 $refs.box
              this.showFullBox();
            })
            this.switchScroll(false);
          } else {
            this.switchScroll(true);
          }
        },
        // blur失焦
        blurFunc() {
          this.isShowBox = false;
    
          this.switchScroll(true);
        },
        // 切换选项
        changeOption(index) {
          if (this.activeIndex === index) {
            return;
          }
          this.activeIndex = index;
        },
        // 是否开放滚动
        switchScroll(isScroll) {
          if (isScroll) {
            document.body.style.overflow = '';
          } else {
            document.body.style.overflow = 'hidden';
          }
        },
        // 计算滚动距离并滚动显示完整
        showFullBox() {
          // 按钮距离底部距离
          const toBottom = document.documentElement.clientHeight - this.$refs.box.getBoundingClientRect().bottom;
          console.log(1, document.documentElement.clientHeight);
          console.log(2, this.$refs.box.getBoundingClientRect())
    
          if (toBottom < 0) {
            window.console.log('box 被遮挡了')
            const {scrollTop} = document.documentElement;
            // 需要滚动的距离(被遮挡的高度)
            const distance = -toBottom;
            document.documentElement.scrollTop = scrollTop + distance;
          }
        }
      }
    }
    </script>
    
    <style lang="less" scoped>
      .dropDownBoxCover {
        .drop-button {
          position: relative;
          display: flex;
          justify-content: center;
          align-items: center;
          outline: none;
    
          .rotate {
            transform: rotate(180deg);
          }
    
          .icon {
             0;
            height: 0;
            border-left: 6px solid transparent;
            border-right: 6px solid transparent;
            border-top: 6px solid #000;
          }
          .drop-down-box-bg {
            background-color: #e6e6e6;
          }
          .select-color {
            color: #fb7299,
          }
    
          .box {
            position: absolute;
            top: 20px;
            z-index: 11;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            padding: 20px;
            background-color: #fff;
            box-shadow: 0px 3px 29px 0px rgba(59,74,116,0.14);
            border-radius: 8px;
            .item {
              font-size: 16px;
              line-height: 16px;
              font-weight: 500;
              padding: 6px 0;
            }
          }
        }
    
        .cover {
          position: fixed;
          top: 0;
          left: 0;
           100%;
          height: 100%;
          z-index: 10;
          background-color: rgba(0,0,0,0.3);
        }
      }
    </style>
    
  • 相关阅读:
    剑指offer--29.从上往下打印二叉树
    剑指offer--28.栈的压入、弹出序列
    剑指offer--27.包含min函数的栈
    剑指offer--26.顺时针打印矩阵
    剑指offer--25.二叉树的镜像
    剑指offer--24.树的子结构
    剑指offer--23.合并两个排序的链表
    剑指offer--22.反转链表
    剑指offer--21.链表中倒数第k个结点
    剑指offer--20.矩形覆盖
  • 原文地址:https://www.cnblogs.com/nangezi/p/14872730.html
Copyright © 2011-2022 走看看