diff --git a/packages/runtime-dom/__tests__/directives/vShow.spec.ts b/packages/runtime-dom/__tests__/directives/vShow.spec.ts index 6c21e5f83f0..545e22b9c75 100644 --- a/packages/runtime-dom/__tests__/directives/vShow.spec.ts +++ b/packages/runtime-dom/__tests__/directives/vShow.spec.ts @@ -3,9 +3,11 @@ import { defineComponent, h, nextTick, - VNode + VNode, + ref, + watch } from '@vue/runtime-core' -import { render, vShow } from '@vue/runtime-dom' +import { render, Transition, vShow } from '@vue/runtime-dom' const withVShow = (node: VNode, exp: any) => withDirectives(node, [[vShow, exp]]) @@ -124,4 +126,63 @@ describe('runtime-dom: v-show directive', () => { await nextTick() expect($div.style.display).toEqual('block') }) + + // #2583 + test('the value of `display` set by v-show should not be overwritten by the style attribute when updated', async () => { + const style = ref('width: 100px') + const display = ref(false) + const component = defineComponent({ + render() { + return withVShow(h('div', { style: style.value }), display.value) + } + }) + render(h(component), root) + + const $div = root.querySelector('div') + + expect($div.style.display).toEqual('none') + + style.value = 'width: 50px' + await nextTick() + expect($div.style.display).toEqual('none') + + display.value = true + await nextTick() + expect($div.style.display).toEqual('') + }) + + // #2583, #2757 + test('the value of `display` set by v-show should not be overwritten by the style attribute when updated (with Transition)', async () => { + const style = ref('width: 100px') + const display = ref(false) + const component = defineComponent({ + setup() { + const innerValue = ref(false) + watch(display, val => { + innerValue.value = val + }) + return () => { + return h(Transition, () => + withVShow( + h('div', { style: style.value }, innerValue.value), + display.value + ) + ) + } + } + }) + render(h(component), root) + + const $div = root.querySelector('div') + + expect($div.style.display).toEqual('none') + + style.value = 'width: 50px' + await nextTick() + expect($div.style.display).toEqual('none') + + display.value = true + await nextTick() + expect($div.style.display).toEqual('') + }) }) diff --git a/packages/runtime-dom/src/directives/vShow.ts b/packages/runtime-dom/src/directives/vShow.ts index f94ac3bae3b..d9bf3af5487 100644 --- a/packages/runtime-dom/src/directives/vShow.ts +++ b/packages/runtime-dom/src/directives/vShow.ts @@ -20,7 +20,8 @@ export const vShow: ObjectDirective = { } }, updated(el, { value, oldValue }, { transition }) { - if (transition && value !== oldValue) { + if (!value === !oldValue) return + if (transition) { if (value) { transition.beforeEnter(el) setDisplay(el, true) diff --git a/packages/runtime-dom/src/modules/style.ts b/packages/runtime-dom/src/modules/style.ts index 30816b58d83..1084e5b2f8f 100644 --- a/packages/runtime-dom/src/modules/style.ts +++ b/packages/runtime-dom/src/modules/style.ts @@ -9,7 +9,14 @@ export function patchStyle(el: Element, prev: Style, next: Style) { el.removeAttribute('style') } else if (isString(next)) { if (prev !== next) { + const current = style.display style.cssText = next + // indicates that the `display` of the element is controlled by `v-show`, + // so we always keep the current `display` value regardless of the `style` value, + // thus handing over control to `v-show`. + if ('_vod' in el) { + style.display = current + } } } else { for (const key in next) {