0

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!

Kuzzy
  • 562
  • 1
  • 9
  • 24

2 Answers2

1

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>
Decade Moon
  • 32,968
  • 8
  • 81
  • 101
  • Nope, result is the same `contenteditable=false`. It works fine with common attributes but not with `contenteditable` – Kuzzy Oct 25 '21 at 06:32
  • I've updated my answer with a demo, inspect the DOM to verify the attribute is/isn't present. – Decade Moon Oct 25 '21 at 22:33
  • I took a look at your codesandbox and discovered Vue has special behavior when *updating* the `contenteditable` attribute (which is why my suggestion only works when setting the attribute initially; it won't be removed in subsequent updates). – Decade Moon Oct 25 '21 at 22:54
  • Thanks for trying to help, I've solved this with `this.$el.removeAttribute('contenteditable')` for deletion and `this.contenteditable = true` to set it back. It looks like not Vue.js way, but `contenteditable` is really special attribute, so, just need to keep it in mind. – Kuzzy Oct 26 '21 at 11:29
  • Why are you messing with the DOM manually? My updated answer solves the problem correctly without resorting to DOM hacks. – Decade Moon Oct 26 '21 at 23:28
  • Oh, I didn’t notice that you changed `attrs` to `domProps` in updated answer. Yeah, it did the trick in much cleaner way. Thank you! – Kuzzy Oct 28 '21 at 12:18
0

Assume that your div is as below

<div id="editable"></div>

then you toggle contenteditable using the below approach

document.getElementById('editable').contentEditable = "true"

OR

document.getElementById('editable').setAttribute('contentEditable',"true")

If you want this div to be contentEditable at the begining then add the above line inside mounted.

Amaarockz
  • 4,348
  • 2
  • 9
  • 27