【问题标题】:Keep v-tooltip open when hovering over the tooltip将鼠标悬停在工具提示上时保持 v-tooltip 打开
【发布时间】:2021-06-14 22:39:27
【问题描述】:

首先,术语,“链接”是鼠标进入的区域。 “工具提示”是弹出并显示额外信息的东西。 --- 以上添加于 2020-04-29

我正在使用 Vuetify 并尝试在鼠标悬停在“工具提示”上时保持 v-tooltip 处于打开状态。 工具提示中的内容将会很丰富,并且不希望在访问者查看时自动隐藏。

<template>
<v-tooltip
  v-model="show"
  max-width="600px"
  content-class="link-tooltip-content"
  bottom>
  <template v-slot:activator="{ on }">
    <div
      :style="boxStyle"
      @mouseover="mouseover"
      @mouseleave="mouseleave"
    ></div>
  </template>
  <template v-slot:default>
    <v-card
      @mouseover="mouseover"
      @mouseleave="mouseleave"
      >
      <v-row dense>
        <v-col>
          <v-card-title class="headline">
            rich tooltip
          </v-card-title>
        </v-col>
      </v-row>
    </v-card>
  </template>
</v-tooltip>
</template>

<script>
    export default {
  data: () => ({
    show: false,
    hoverTimer: null
  }),
  methods: {
    boxStyle: function() {
      return {
        left: "100px",
        top: "100px",
        width: "100px",
        height: "100px",
        position: "absolute"
      };
    },
    mouseover: function() {
      console.log(`mouseover`);
      this.show = true;
      clearTimeout(this.hoverTimer);
    },
    mouseleave: function() {
      console.log(`mouseleave`);
      this.hoverTimer = setTimeout(() => {
        this.show = false;
      }, 3000);
    }
  }
};
</script>

但这不起作用。激活器槽(“链接”)元素上的 mouseover 和 mouseleave 事件处理程序会触发,但默认槽(“工具提示”)上的事件处理程序不会触发。

我认为原因是,因为“工具提示”内的内容被移动到了body标签下的其他地方。

问题是,如何在将鼠标悬停在“工具提示”上时保持打开状态。

我是这样移动鼠标的:

  1. 将鼠标悬停在链接上(显示工具提示)。
  2. 将鼠标移出链接并进入工具提示。 (链接和工具提示相距几个像素) 现在触发链接的 mouseleave 事件,我想在工具提示上添加一个 mouseenter 事件处理程序。 我该怎么做?

我正在考虑在工具提示上添加一个 mouseenter 事件,以便我可以clearTimeout(hoverTimer) 并保持工具提示打开。

我知道 9 年前有一个类似的问题,使用 jQuery Keep tooltip opened when the mouse is over it,但如果可能的话我不想使用 jQuery。我更喜欢 Vue 的方式。

这是一个可重现的小例子: https://www.codeply.com/p/GuFXqAAU8Y

【问题讨论】:

    标签: javascript vue.js vuetify.js


    【解决方案1】:

    我建议你使用v-menu,而不是使用v-tooltip,并将open-on-hover 属性设置为true。 如果您必须轻推您在菜单中放置的任何内容,请确保设置适当的 close-delay 值,以便菜单在用户到达之前不会关闭。

    示例: https://codepen.io/stephane303/pen/WNwdNxY

        <v-menu open-on-hover right offset-x nudge-right="20" close-delay="100">
    

    【讨论】:

      【解决方案2】:

      .v-tooltip__contentvuetify.min.css 中设置了pointer-events:none。如果将其设置回auto,则允许将其悬停。

      当它悬停时,它的父级也悬停。当它的父级悬停时,它有一个工具提示。所以你只需要:

      .v-tooltip__content {
        pointer-events: auto;
      }
      

      【讨论】:

      • 我认为这个问题存在误解。我编辑了这个问题。我认为当工具提示在链接上时您的回答是有意义的,但事实并非如此。在我的情况下,它相距几个像素。
      • @mash,我认为您的问题需要minimal reproducible example 重现该问题。仅通过查看代码并不能真正解决 UX 问题。您实际上需要一种方法来重现 UX,以便提出改进并作为测试任何潜在解决方案的方法。
      • 更新问题并添加示例!
      • @mash,我误读了你的问题。你问的和我想的相反。更新了答案以匹配您的问题。幸好我让你创建一个 sn-p。
      【解决方案3】:

      为此,我制作了 VTooltip 的扩展版本。只需通过 interactive 道具。通过将“创建者”悬停在列表项中查看工作示例:https://tzkt.io/KT1RJ6PbjHpwc3M5rw5s2Nbmefwbuwbdxton/tokens

      <script>
      /**
       * Extends VTooltip with interactivity
       * @see https://material-ui.com/components/tooltips/#interactive
       */
      
      import { VTooltip } from 'vuetify/lib';
      
      export default {
          extends: VTooltip,
          props: {
              interactive: {
                  type: Boolean,
                  default: false,
              },
              closeDelay: {
                  type: [Number, String],
                  default: 50,
              },
          },
          computed: {
              // I'm not 100% sure in this, but it works
              calculatedLeft() {
                  const originalValue = VTooltip.options.computed.calculatedLeft.call(this);
                  if (!this.interactive) return originalValue;
                  const { left, right } = this;
                  let value = parseInt(originalValue);
                  if (left || right) {
                      value += right ? -10 : 10;
                  }
                  return `${value}px`;
              },
              calculatedTop() {
                  const originalValue = VTooltip.options.computed.calculatedTop.call(this);
                  if (!this.interactive) return originalValue;
                  const { top, bottom } = this;
                  let value = parseInt(originalValue);
                  if (top || bottom) {
                      value += bottom ? -10 : 10;
                  }
                  return `${value}px`;
              },
              styles() {
                  const originalValue = VTooltip.options.computed.styles.call(this);
                  if (!this.interactive) return originalValue;
                  const {
                      top, bottom, left, right,
                  } = this;
                  let paddingDirection;
                  if (bottom) paddingDirection = 'top';
                  else if (top) paddingDirection = 'bottom';
                  else if (right) paddingDirection = 'left';
                  else if (left) paddingDirection = 'right';
                  return {
                      ...originalValue,
                      [`padding-${paddingDirection}`]: `${10}px`,
                  };
              },
          },
          methods: {
              onTooltipMouseenter(e) {
                  if (this.interactive) {
                      this.clearDelay();
                      this.isActive = true;
                  }
                  this.$emit('tooltip:mouseenter', e);
              },
              onTooltipMouseleave(e) {
                  if (this.interactive) {
                      this.clearDelay();
                      this.runDelay('close');
                  }
                  this.$emit('tooltip:mouseleave', e);
              },
              genContent() {
                  const content = this.$createElement('div', this.setBackgroundColor(this.color, {
                      style: this.contentStyles,
                      staticClass: 'v-tooltip__content',
                      class: {
                          [this.contentClass]: true,
                          menuable__content__active: this.isActive,
                      },
                  }), this.getContentSlot());
                  return this.$createElement('div', {
                      style: this.styles,
                      attrs: this.getScopeIdAttrs(),
                      class: {
                          'v-tooltip__wrapper': true,
                          'v-tooltip__wrapper--fixed': this.activatorFixed,
                      },
                      directives: [{
                          name: 'show',
                          value: this.isContentActive,
                      }],
                      on: {
                          mouseenter: this.onTooltipMouseenter,
                          mouseleave: this.onTooltipMouseleave,
                      },
                      ref: 'content',
                  }, [content]);
              },
              genActivatorListeners() {
                  const listeners = VTooltip.options.methods.genActivatorListeners.call(this);
      
                  if (this.interactive) {
                      if (listeners.mouseenter) {
                          listeners.mouseenter = (e) => {
                              this.getActivator(e);
                              this.clearDelay();
                              if (!this.isActive) {
                                  this.runDelay('open');
                              }
                          };
                      }
                  }
      
                  return listeners;
              },
          },
      
      };
      </script>
      
      <style lang="scss">
      .v-tooltip__wrapper {
          position: absolute;
          &--fixed {
              position: fixed;
          }
          .v-tooltip__content {
              position: static;
          }
      }
      </style>
      
      

      【讨论】:

        猜你喜欢
        • 2011-06-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-12-30
        • 1970-01-01
        • 2023-03-26
        • 1970-01-01
        相关资源
        最近更新 更多