Transition component does not work with “visibility”
Version
3.0.2
Reproduction link
https://codesandbox.io/s/competent-hermann-b1s5q
Steps to reproduce
see https://codesandbox.io/s/competent-hermann-b1s5q
What is expected?
The transition
element of vue only works with display:none
but not visibility:hidden
.
Is there any way to make it work with visibility
?
I want to get the clientWidth
of the element before it shows up, with display:none
I can't get that value.
It's very easy to add a modifier like v-show.visibility
, if it's OK to have such a modifier, I would like to send a PR.
For the benefit of anyone else reading this, there is a corresponding Stack Overflow question here:
I believe this is really a feature request rather than a bug report. Currently it is possible to hide an element in a transition-safe way using v-show
. However, that forces the hiding to be via display: none
. In cases where you want to hide an element by other means, for example visibility
, you currently need to recreate the logic in v-show
. It would be nice to have a way to reuse the transition logic in v-show
without having to copy/paste/maintain it in a totally separate directive.
Thanks @skirtles-code ! Really helpful solution on the Stack Overflow.
It would be nice to have a way to reuse the transition logic in v-show without having to copy/paste/maintain it in a totally separate directive.
Yeah, absolutely right. Or may be it could be a strategy function something like this.
<template>
<div v-show:strategy="setDisplay">foobar</div>
</template>
<script lang="ts">
interface ElementDisplay {
new(el: HTMLElement): void
show()
hide()
}
class VisibilityStrategy implements ElementDisplay {
private el: HTMLElement;
constructor(el: HTMLElement) {
this.el = el
}
show() {
this.el.style.visibility = 'visible'
this.el.style.opacity = '1'
this.el.classList.remove('is-hidden')
}
hide() {
this.el.style.visibility = 'hidden'
this.el.style.opacity = '0'
this.el.classList.add('is-hidden')
}
}
export default {
data() {
return {
setDisplay: VisibilityStrategy
}
}
}
</script>
then this function can be replaced by the ElementDisplay
instance.
But a simple v-show.visibility
modifier is enough to handle most of the use cases I think.
I find that this issue should be added a bug
label @LinusBorg , here is my analysis:
Suppose the user-defined direcitve looks like following(similar to v-show
), pay attention to the comments(step 1
& step 2
) and now let's look at the classes change.
const visibility = {
beforeMount(el, { value }, { transition }) {
el._vod = el.style.visibility === '' ? 'hidden' : el.style.visibility
if (transition && value) {
transition.beforeEnter(el)
} else {
el.style.visibility = value || 'hidden'
}
},
updated(el, { value, oldValue }, { transition }) {
if (!value === !oldValue) {
return
}
if (value) {
// step 1
transition.beforeEnter(el)
el.style.visibility = 'visible'
// step 2
transition.enter(el)
} else {
transition.leave(el, () => {
el.style.visibility = 'hidden'
})
}
}
}
The following code is part of Transition.ts.
// step 1: transition.beforeEnter
onBeforeEnter(el) {
onBeforeEnter && onBeforeEnter(el)
// add *-enter-active
addTransitionClass(el, enterActiveClass)
// add *-enter-from
addTransitionClass(el, enterFromClass)
}
// step 2: transition.enter
const makeEnterHook = (isAppear: boolean) => {
return (el: Element, done: () => void) => {
const hook = isAppear ? onAppear : onEnter
const resolve = () => finishEnter(el, isAppear, done)
hook && hook(el, resolve)
nextFrame(() => {
// remove *-enter-from
removeTransitionClass(el, isAppear ? appearFromClass : enterFromClass)
// add *-enter-to
addTransitionClass(el, isAppear ? appearToClass : enterToClass)
if (!(hook && hook.length > 1)) {
if (enterDuration) {
setTimeout(resolve, enterDuration)
} else {
whenTransitionEnds(el, type, resolve)
}
}
})
}
}
// step 3: transition end
const finishEnter = (el: Element, isAppear: boolean, done?: () => void) => {
// remove *-enter-to
removeTransitionClass(el, isAppear ? appearToClass : enterToClass)
// remove *-enter-active
removeTransitionClass(el, isAppear ? appearActiveClass : enterActiveClass)
done && done()
}
We can see classes change processes:
- When we use
v-show
it's ok, because the DOM was inserted after add*-enter-active & *-enter-from
, so that the DOM initial status is*-enter-from
- When we use
v-visibility
, the initial status is certain (status a) and status was changed when we add*-enter-active & *-enter-from
(status b), but now DOM initial status is not*-enter-from
.
Wait for confirmation and i can fix it.
Disclaimer: I currently don't have a complete understanding of the transition mechanics in play here.
That being said, I don't see why I should label this a bug when so far, we clearly only support v-if
and v-show
.
The fact that you were not able to implement a custom directive with the existing internal APIs does not make an unsupported feature a bug.
This still is, essentially, a feature request.