最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Keep v-tooltip open when hovering over the tooltip - Stack Overflow

programmeradmin1浏览0评论

First, the terms, "link" is the area where the mouse enters. The "tooltip" is the thing that pops up and shows extra information. --- above added 2020-04-29

I'm using Vuetify and trying to keep the v-tooltip open when mouse is hovering over the "tooltip". The content inside the tooltip is going to be rich and don't want that to automatically hide when visitor is looking into it.

<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>

But this doesn't work. The mouseover and mouseleave event handlers on the activator slot (the "link") element does fire, but the event handlers on the default slot (the "tooltip") don't fire.

I think the reason is, because the content inside the "tooltip" is moved to somewhere else under the body tag.

The questions is, how can I keep the "tooltip" open when hovering over it.

I'm moving the mouse like this:

  1. Hover over the link (the tooltip shows up).
  2. Move the mouse out of the link and into the tooltip. (The link and tooltip is a few pixels apart) Now the mouseleave event for the link fires, and I want to add a mouseenter event handler on the tooltip. How do I do that ?

I'm thinking to add an mouseenter event on the tooltip, so that I can clearTimeout(hoverTimer) and keep the tooltip open.

I know there's a similar question from 9 years ago, using jQuery Keep tooltip opened when the mouse is over it , but I don't want to use jQuery if possible. I prefer a Vue way.

Here's a little reproducible example:

First, the terms, "link" is the area where the mouse enters. The "tooltip" is the thing that pops up and shows extra information. --- above added 2020-04-29

I'm using Vuetify and trying to keep the v-tooltip open when mouse is hovering over the "tooltip". The content inside the tooltip is going to be rich and don't want that to automatically hide when visitor is looking into it.

<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>

But this doesn't work. The mouseover and mouseleave event handlers on the activator slot (the "link") element does fire, but the event handlers on the default slot (the "tooltip") don't fire.

I think the reason is, because the content inside the "tooltip" is moved to somewhere else under the body tag.

The questions is, how can I keep the "tooltip" open when hovering over it.

I'm moving the mouse like this:

  1. Hover over the link (the tooltip shows up).
  2. Move the mouse out of the link and into the tooltip. (The link and tooltip is a few pixels apart) Now the mouseleave event for the link fires, and I want to add a mouseenter event handler on the tooltip. How do I do that ?

I'm thinking to add an mouseenter event on the tooltip, so that I can clearTimeout(hoverTimer) and keep the tooltip open.

I know there's a similar question from 9 years ago, using jQuery Keep tooltip opened when the mouse is over it , but I don't want to use jQuery if possible. I prefer a Vue way.

Here's a little reproducible example: https://www.codeply.com/p/GuFXqAAU8Y

Share Improve this question edited Apr 29, 2020 at 19:45 mash asked Apr 20, 2020 at 20:38 mashmash 4,5165 gold badges35 silver badges35 bronze badges
Add a comment  | 

3 Answers 3

Reset to default 7

Instead of using v-tooltip, I suggest you use v-menu with the open-on-hover props set to true. If you have to nudge whatever you put in the menu, make sure to set an appropriate close-delay value, so the menu doesn't close before the user reaches it.

Example: https://codepen.io/stephane303/pen/WNwdNxY

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

.v-tooltip__content has pointer-events:none set in vuetify.min.css. If you set it back to auto you allow it to be hovered.

When its hovered, its parent is hovered. And when its parent is hovered, it has a tooltip. So all you need is:

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

I made extended version of VTooltip for this purpose. Just pass interactive prop. See working example here by hovering Governance stepper: https://tzkt.io

<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>

发布评论

评论列表(0)

  1. 暂无评论