Subscribe on changes!

Inconsistent custom directive behaviour with element innerHTML

avatar
Dec 19th 2022

Vue version

3.2.45

Link to minimal reproduction

https://sfc.vuejs.org/#eNqFkk1v2zAMhv8KIeyQYLGVdNvFc4IO3WEDttO6m4DWlZnGrfUBfXgdDP/3Unaapi3a3iTy1fNSJHv2zdq8i8gKVnrpGhvAY4h2I3SjrHEBenC4hQG2zigQjLSCfRVaaGm0DyBN1AEdrJNstpofpXwwluLE+5kkXdXOZnNYb6AXGh4e5hSOCB/XsKKnwwJOlsvlROEc/iSEi1o3+hqqbTJaLYkohSbseaPQxLCnyhYrd3BK5vMFqRMu0bZRy9AYDXXjkE4dnlVte1XJ2xm286mmFgPsgmqpamzzRmt0P85//6Jq4EmA8pcXH/okHS4uU92Pv+7OInmr7w8upB3Z0dZVwLp46b+g98Qo+TQAaj1dAirb0gO6AZR289enFihD8EruEPx/Haq7AkpvKw1dJkfX7ADf9P1hNsNAbJJtSp4Ge8TrsvG778H2urVge6RgxHoNGfCOhv8+MuleR5b8qAVswaZ1zFRl8xtvNC3s2FaxT3jBiqnRKTZuaUGHXQjWF5xHbW+vc2kUP6Ucp5UKtDxZbdTpp/wk//yF140Px/EcvcqunPnnaUlvCE9jOsA5BTt0mUNdo0vFv2H2TPvE8FnuhWnypOUa2HAPP1dHlQ==

Steps to reproduce

  1. Create a ref with a value that changes over time (using setInterval or something similar).
  2. Render the ref in a span using curly braces.
  3. Render the ref in another span using v-text.
  4. Create a custom directive that updates an element's DOM using innerHTML. It uses the updated hook.
  5. Use the custom directive on previously created spans.

What is expected?

Consistent rendering behaviour between both spans.

What is actually happening?

The span using curly braces looks like it performs DOM manipulation on the old DOM. This behaviour seems like it should occur when using beforeUpdate hook instead of updated.

The span using v-text performs DOM manipulation on the latest version of the element. This feels correct based on the fact it is called on the updated hook.

System Info

No response

Any additional comments?

I found a related issue (https://github.com/vuejs/core/issues/7207) - the comments on it that argue against manipulating the DOM seem to run counter to what's outlined in the docs (https://vuejs.org/guide/reusability/custom-directives.html#directive-hooks). I'd like to understand whether DOM manipulation is or isn't recommended when using custom directives.

avatar
Dec 19th 2022

You can manipulate the DOM as long as you don't mess with the parts Vue controls.

In your example, the "mustache" part is a DOM text node inside of a span. That text node is controlled by Vue, its content is being updated by Vue with the mustache code's value on each re-render.

But after you did replace this text node for the first time with your directive's use of innerHTML, Vue will continue to update the old text node that is no longer in the document because you replaced it, because Vue is not aware of the change you made.

I'd like to understand whether DOM manipulation is or isn't recommended when using custom directives.

Things you can touch:

  • add content to empty elements.
  • mess with element content that you previously added to empty elements.
  • add/change/remove attributes/DOM properties that you did not also use in the template
  • call a method on a DOM element, i.e. element.focus() on an input
  • add/remove event listeners, trigger custom events, etc.

Things you should not touch:

  • anything that Vue also touches on that element in your template.
avatar
Dec 20th 2022

Brillant, thanks @LinusBorg for the clarification!