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 {
        width: 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 {
        width: 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 {
        width: 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;
      width: 100%;
      height: 100%;
      z-index: 10;
      background-color: rgba(0,0,0,0.3);
    }
  }
</style>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM