Search code examples
javascriptvue.jsdomvue-componentoverflow

How to detect whether an element inside a component is overflown in Vue?


I have a component ResultPill with a tooltip (implemented via vuikit) for the main container. The tooltip text is calculated by a getter function tooltip (I use vue-property-decorator) so the relevant bits are:

<template>
    <div class="pill"
         v-vk-tooltip="{ title: tooltip, duration: 0, cls: 'some-custom-class uk-active' }"
         ref="container"
    >
        ..some content goes here..
    </div>
</template>
<script lang="ts">
    @Component({ props: ... })
    export default class ResultPill extends Vue {
    ...
        get tooltip (): string { ..calcing tooltip here.. }
        isContainerSqueezed (): boolean {
            const container = this.$refs.container as HTMLElement | undefined;
            if(!container) return false;
            return container.scrollWidth != container.clientWidth;
        }
    ...
</script>
<style lang="stylus" scoped>
    .pill
        white-space pre
        overflow hidden
        text-overflow ellipsis
    ...
</style>

Now I'm trying to add some content to the tooltip when the component is squeezed by the container's width and hence the overflow styles are applied. Using console, I can roughly check this using $0.scrollWidth == $0.clientWidth (where $0 is the selected element), but when I start tooltip implementation with

        get tooltip (): string {
            if(this.isContainerSqueezed())
                return 'aha!'

I find that for many instances of my component this.$refs.container is undefined so isContainerSqueezed doesn't help really. Do I have to somehow set unique ref per component instance? Are there other problems with this approach? How can I check whether the element is overflown?

PS to check if the non-uniqueness of refs may affect the case, I've tried to add to the class a random id property:

        containerId = 'ref' + Math.random();

and use it like this:

         :ref="containerId"
    >
    ....
            const container = this.$refs[this.containerId] as HTMLElement | undefined;

but it didn't help: still tooltip isn't altered.

And even better, there's the $el property which I can use instead of refs, but that still doesn't help. Looks like the cause is this:

An important note about the ref registration timing: because the refs themselves are created as a result of the render function, you cannot access them on the initial render - they don’t exist yet! $refs is also non-reactive, therefore you should not attempt to use it in templates for data-binding.

(presumably the same is applicable to $el) So I have to somehow recalc tooltip on mount. This question looks like what I need, but the answer is not applicable for my case.


Solution

  • So, like I've mentioned in one of the edits, docs warn that $refs shouldn't be used for initial rendering since they are not defined at that time. So, I've made tooltip a property instead of a getter and calcuate it in mounted:

    export default class ResultPill extends Vue {
    ...
        tooltip = '';
        calcTooltip () {
            // specific logic here is not important, the important bit is this.isContainerSqueezed()
            // works correctly at this point
            this.tooltip = !this.isContainerSqueezed() ? this.mainTooltip :
                this.label + (this.mainTooltip ? '\n\n' + this.mainTooltip : '');
        }
        get mainTooltip (): string { ..previously used calculation.. }
        ...
        mounted () {
            this.calcTooltip()
        }
    }