I'm trying to do an editable ponent with Vue 2. It supposed to use the contenteditable
attribute in any tag, replacing a normal input. I want to give it a placeholder functionality in order to show a value when none is provided by the user, but I can't seem to get it working.
I'm watching the current value of the ponent and setting data.isEmpty
to true
when no user content is present. The ponent should then show the placeholder value, but currently it shows nothing.
If I console.log
the result of the render
method, it will show the placeholder child node was instantiated correctly, but for some reason it just won't show on the final HTML.
Here's a JSFiddle: /
And an embedded snippet for those who prefer it:
Vueponent('editable-content', {
props: {
initial: {
type: String
},
placeholder: {
type: String,
required: false
}
},
data() {
return {
value: this.initial,
isEmpty: this.initial === ''
}
},
render: function(createElement) {
const self = this
return createElement(
'div', {
attrs: {
contenteditable: true
},
on: {
input: function(event) {
self.value = event.target.innerHTML
self.$emit('edited', event.target.value)
}
}
},
this.isEmpty ? this.placeholder : this.value
)
},
watch: {
value(to, from) {
this.isEmpty = to === ''
}
}
})
new Vue({
el: '#app',
ponents: [
'editable-content'
]
})
<script src=".3.0/vue.min.js"></script>
<div id="app">
<editable-content initial="Initial value" placeholder="Placeholder" />
</div>
I'm trying to do an editable ponent with Vue 2. It supposed to use the contenteditable
attribute in any tag, replacing a normal input. I want to give it a placeholder functionality in order to show a value when none is provided by the user, but I can't seem to get it working.
I'm watching the current value of the ponent and setting data.isEmpty
to true
when no user content is present. The ponent should then show the placeholder value, but currently it shows nothing.
If I console.log
the result of the render
method, it will show the placeholder child node was instantiated correctly, but for some reason it just won't show on the final HTML.
Here's a JSFiddle: https://jsfiddle/dy27fa8t/
And an embedded snippet for those who prefer it:
Vue.ponent('editable-content', {
props: {
initial: {
type: String
},
placeholder: {
type: String,
required: false
}
},
data() {
return {
value: this.initial,
isEmpty: this.initial === ''
}
},
render: function(createElement) {
const self = this
return createElement(
'div', {
attrs: {
contenteditable: true
},
on: {
input: function(event) {
self.value = event.target.innerHTML
self.$emit('edited', event.target.value)
}
}
},
this.isEmpty ? this.placeholder : this.value
)
},
watch: {
value(to, from) {
this.isEmpty = to === ''
}
}
})
new Vue({
el: '#app',
ponents: [
'editable-content'
]
})
<script src="https://cdnjs.cloudflare./ajax/libs/vue/2.3.0/vue.min.js"></script>
<div id="app">
<editable-content initial="Initial value" placeholder="Placeholder" />
</div>
Share
Improve this question
asked May 1, 2017 at 14:43
Tomas ButelerTomas Buteler
4,1374 gold badges32 silver badges43 bronze badges
4
-
When you say, "when none is provided by the user" do you mean when there is no
initial
attribute bound on the ponent? – Bert Commented May 1, 2017 at 14:53 -
Well, that too. But I'm mostly referring to the user going to edit the element and deleting all characters. I'm basically trying to reproduce the placeholder on an HTML
input
, so when no value is set the placeholder shows up. – Tomas Buteler Commented May 1, 2017 at 14:58 -
1
It has something to do with it re-using the DOM element and the DOM element having focus. What I mean is, if you change the value without it having focus (like with setTimeout) it sets the placeholder value. Likewise, if you add a unique
key
on each render, it will set the placeholder, but you lose focus on the DOM element. – Bert Commented May 1, 2017 at 15:25 - I've tested this and you're right. Bummer. Sure hope there's a workaround, because losing focus on every edit sort of defeats the purpose of having this ponent... – Tomas Buteler Commented May 1, 2017 at 15:33
3 Answers
Reset to default 2Apparently rendering a contenteditable
doesn't work in the intuitive way. Instead, set the innerHTML
directly with the placeholder when the content is empty. Then on keydown
(before the input event), if the content is currently marked empty, remove the placeholder. On keyup
(after the input event), if the div still has no content, mark it empty again (this is so things like shift key don't clear the placeholder).
I took the liberty of making it v-model
patible and styling the placeholder.
Vue.ponent('editable-content', {
props: {
value: {
type: String
},
placeholder: {
type: String,
required: false
}
},
data() {
return {
isEmpty: this.value === ''
};
},
methods: {
setEmpty() {
this.$el.innerHTML = `<div contenteditable="false" class="placeholder">${this.placeholder}</div>`;
this.isEmpty = true;
},
clearEmpty() {
this.$el.innerHTML = '';
this.isEmpty = false;
}
},
mounted() {
if (this.$el.innerHTML === '') {
this.setEmpty();
}
},
watch: {
value(newValue) {
if (newValue === '') {
this.setEmpty();
}
}
},
render: function(createElement) {
return createElement(
'div', {
attrs: {
contenteditable: true
},
on: {
keydown: () => {
if (this.isEmpty) {
this.clearEmpty();
}
},
input: (event) => {
this.$emit('input', event.target.textContent);
},
keyup: () => {
if (this.$el.innerHTML === '') {
this.setEmpty();
}
}
}
},
this.value
)
}
});
new Vue({
el: '#app',
data: {
startingBlank: '',
editedValue: 'initial value'
},
ponents: [
'editable-content'
]
})
.placeholder {
color: rgba(0,0,0, 0.5);
}
<script src="//cdnjs.cloudflare./ajax/libs/vue/2.3.0/vue.min.js"></script>
<div id="app">
<editable-content v-model="startingBlank" placeholder="Placeholder"></editable-content>
<editable-content v-model="editedValue" placeholder="Placeholder"></editable-content>
</div>
In the end I settled for a mixed JS and CSS solution using the :empty
pseudo-class. A Vue-only workaround just seemed too unwieldy, so this felt like a healthy promise. I don't even feel the need to keep track of the value
anymore.
Worth noting that with single-file ponents I can use scoped CSS so it's even better as the CSS is essential to the ponents core functionality.
Vue.ponent('editable-content', {
props: {
initial: {
type: String
},
placeholder: {
type: String,
required: false
}
},
data() {
return {
value: this.initial
}
},
render: function(createElement) {
const self = this
return createElement(
'div', {
attrs: {
contenteditable: true,
'data-placeholder': this.placeholder
},
on: {
input: function(event) {
self.$emit('edited', event.target.value)
}
}
},
this.value
)
}
})
new Vue({
el: '#app',
ponents: [
'editable-content'
]
})
[data-placeholder]:empty::after {
content: attr(data-placeholder);
}
<script src="https://cdnjs.cloudflare./ajax/libs/vue/2.3.0/vue.min.js"></script>
<div id="app">
<editable-content initial="Initial value" placeholder="Placeholder" />
</div>
If you don't pass the initial
prop into the ponent it will be undefined. So the check you should make is to see if is undefined:
data() {
return {
value: this.initial,
isEmpty: typeof this.initial === 'undefined'
}
},
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745349583a4623749.html
评论列表(0条)