Search code examples
vue.jsvue-render-function

Vue.js remove contenteditable attribute in render function


it seems should be a simple solution but something went wrong...

I need to conditionally remove contenteditable attribute from element:

<!-- first state -->
<div contenteditable="true"></div>

<!-- second state -->
<div></div>

I've tried:

data() {
    return {
      contenteditable: true,
      title: "Title",
    };
  },
  methods: {
    handleClick() {
      this.contenteditable = null;
      this.title = null;
    },
  },
  render(createElement) {
    const options = {
      attrs: {
        id: "ID",
        title: this.title,
        contenteditable: this.contenteditable,
        // Not working either
        //...(this.contenteditable && { contenteditable: true }),
      },
      domProps: {
        innerHTML: "<p>Test</p>",
      },
      on: {
        click: this.handleClick,
      },
    };

    return createElement("div", options);
  },

Sandbox

Result is: <div contenteditable="false"></div>, but I need to remove attribute completely. For example title attribute works as expected, it not renders if it's value was set to null. I've also tried to make attrs object fully reactive, like:

data() {
  return {
    attrs: {
      title: 'Title',
      contenteditable: true,
    }
  }
},
methods: {
  handleClick() {
    Vue.delete(this.attrs, 'contenteditable')
    Vue.delete(this.attrs, 'title')
  },
},
render(createElement) {
    const options = {
      attrs: this.attrs,
      domProps: {
        innerHTML: "<p>Test</p>",
      },
      on: {
        click: this.handleClick,
      },
    };

    return createElement("div", options);
  },

Sandbox

...but with no luck. Any ideas how to solve this?

Thanks!


Solution

  • Vue 2 treats the contenteditable attribute as a special case here, it will not remove the attribute if the new attribute value is undefined. This is fixed (or planned) in Vue 3.

    You can bypass this behavior and set it with domProps instead:

    domProps: {
      contentEditable: this.contenteditable ? 'true' : 'inherit'
    }
    

    Here is a demo:

    new Vue({
      el: '#app',
      data() {
        return {
          contenteditable: false
        }
      },
      render(h) {
        return h('div', [
          h('button', {
            on: {
              click: () => this.contenteditable = !this.contenteditable
            }
          }, `contenteditable: ${this.contenteditable}`),
          h('div', {
            domProps: {
              contentEditable: this.contenteditable ? 'true' : 'inherit'
            }
          }, 'Text')
        ])
      }
    })
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
    
    <div id="app">
    </div>