关于修改Bootstrap5的Dropdown为悬浮触发

484 阅读1分钟

「这是我参与2022首次更文挑战的第6天,活动详情查看:2022首次更文挑战

新年快乐!除夕夜快乐!

由于自身Blog正在开发中,所以先行记载于此。(首发于本人语雀,现搬运自此)

最近制作个人网站,决定使用Bootstrap,于是就喜闻乐见的遇到了这个问题,网上虽有解决方案,但是对于5版本来说都无法使用(至少我不行),也可能是我太菜了,不知道哪里不对。于是追了下官方的代码,找着了解决方案如下:
首先确认使用的不是min版本,且为bundle(理论上不是bundle的应该也行,不过我用的是bundle)
然后找到:

  /**
   * ------------------------------------------------------------------------
   * Data Api implementation
   * ------------------------------------------------------------------------
   */



  EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE$3, Dropdown.dataApiKeydownHandler);
  EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler);
  EventHandler.on(document, EVENT_CLICK_DATA_API$3, Dropdown.clearMenus);
  EventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus);
  EventHandler.on(document, EVENT_CLICK_DATA_API$3, SELECTOR_DATA_TOGGLE$3, function (event) {
    event.preventDefault();
    Dropdown.getOrCreateInstance(this).toggle();
  });

这边就是监听Dropdown的点击事件的地方,我们在后面追加:

  /**
   * 使Dropdown支持Hover
   */
  var mytttt;
  EventHandler.on(document, EVENT_MOUSEENTER, SELECTOR_DATA_TOGGLE$3, Dropdown.dataApiHoverHandler);
  EventHandler.on(document, EVENT_MOUSEENTER, SELECTOR_MENU, Dropdown.dataApiHoverHandler);
  EventHandler.on(document, EVENT_MOUSELEAVE, SELECTOR_MENU, Dropdown.clearMyMenus);
  EventHandler.on(document, EVENT_MOUSELEAVE, SELECTOR_DATA_TOGGLE$3, Dropdown.clearMyMenus);

然后在Dropdown类的最后面追加:

    /**
     * 使Dropdown支持Hover
     */
    static dataApiHoverHandler(event) {
      const isActive = this.classList.contains(CLASS_NAME_SHOW$6);
      const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE$3) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE$3)[0];
      console.log(getToggleButton);
      if (!isActive) {
        Dropdown.getOrCreateInstance(getToggleButton).show();
      }
      clearTimeout(mytttt);
    }

    static clearMyMenus(event) {
      const toggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE$3);
      mytttt = setTimeout(() => {
        for (let i = 0, len = toggles.length; i < len; i++) {
          const context = Dropdown.getInstance(toggles[i]);
          Dropdown.getOrCreateInstance(toggles[i]).hide();
          // 需要先去掉Dropdown.show()里面的this._element.focus();
        }
      }, 100);
    }

最后去掉Dropdown.show()里的this._element.focus();就可以了!

加入Timeout是为了放止因为1px的差距导致直接隐藏,不需要的可以去掉,有其他需求的可以直接改100那个值。

注意:Dropdown.show()里的this._element.focus();一定要去掉,不然他在失焦后再悬浮的时候会有一个白色边框,特别丑。也可以追一下focus的相关css代码,直接覆盖~

更新:

之前的代码其实会有个问题:如果鼠标快速划过两个DropDown,会使这两个DropDownList一起显示,所以优化了一下:

    /**
     * 使Dropdown支持Hover
     */
    static dataApiHoverHandler(event) {
      const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE$3) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE$3)[0];

      const toggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE$3);
      for (let i = 0, len = toggles.length; i < len; i++) {
        console.log();
        if (!toggles[i].matches(CLASS_NAME_SHOW$6)) {
          if (toggles[i] == getToggleButton) {
            Dropdown.getOrCreateInstance(getToggleButton).show();
            clearTimeout(mytttt[i]);
          }
        } else {
          if (toggles[i] == getToggleButton)
            clearTimeout(mytttt[i]);
        }
      }
    }

    static clearMyMenus(event) {
      const toggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE$3);
      for (let i = 0, len = toggles.length; i < len; i++) {
        mytttt[i] = setTimeout(() => {
          Dropdown.getOrCreateInstance(toggles[i]).hide();
          // 需要先去掉Dropdown.show()里面的this._element.focus();
        }, 100);
      }
    }
  }
  
  var mytttt = [];

替换掉之前代码的相应部分就可以了!

具体逻辑就是:把所有Timer存起来,然后清除的时候用matches判断一下,如果不是当前的元素,并且已经显示就清除掉Timer,就可以了!

其实如果不考虑性能,可以不加是否已经显示的判断,直接不管是否显示都强行把当前悬浮的元素给显示出来并清除Timer也行。

转载注明出处。