Vuetify: 1.2.9
Vue: 2.5.17
Browsers: Android Chrome 69.0.3497.100
OS: Android 8.1
Do not scroll to the top of the text area when a space is inserted/space bar is pressed

https://codepen.io/wistudent/pen/XxaNOP
(It contains the same code as the auto-grow example from https://vuetifyjs.com/en/components/textarea#examples )
The issue is caused by this part in VTextarea
calculateInputHeight () {
const input = this.$refs.input
if (input) {
input.style.height = 0
const height = input.scrollHeight
const minHeight = parseInt(this.rows, 10) * parseFloat(this.rowHeight)
// This has to be done ASAP, waiting for Vue
// to update the DOM causes ugly layout jumping
input.style.height = Math.max(minHeight, height) + 'px'
}
}
Setting input.style.height to the newly calculated height after setting it to 0 doesn't happen fast enough in Chrome on Android when pressing a character key and space bar alternately. Because the textarea has focus, Chrome on Android will scroll to keep the cursor in the visible area. So if the height is set to 0, chrome scrolls up.
Just removing input.style.height = 0 fixes the jumping but breaks the shrinking of the textarea because then the scrollHeight will not be updated.
Removing the focus at the beginning and setting it again after the height was updated prevents the scrolling but breaks the software keyboard (holding backspace to delete multiple characters doesn't work anymore.)
I see two possible solutions to this problem: Either somehow prevent the scrolling while updating the height or using a different approach to calculate the minimal height of the textarea. One approach I saw used a second, hidden textarea with style.height = 0 to get the minimal height with scrollHeight.
I don't think this problem is limited to Android Chrome. I could reproduce it on IE 11 and Chrome for Desktop. It really makes the auto-grow feature unusable as soon as there is a vertical scroll bar in the main window.
I am not sure why but I can't reproduce in chrome and IE for desktop in the codepen linked to the issue. Also can't reproduce in the vuetify doc for the textarea component. So there must be some interactions with other components to trigger this problem...
If anyone is looking for a workaround, this is what I came up with for a textarea that fills the whole viewport:
<template>
<div>
<v-form>
<div
id="editor-container"
@click="setFocusToTextarea()"
>
<v-textarea
id="editor"
v-model="code"
hide-details
no-resize
loading="false"
style="float: left; width: 100%; overflow-y: hidden; padding: 12px;"
:rows="getNumberOfLines(code)"
/>
</div>
</v-form>
</div>
</template>
<style scoped>
#editor-container {
height: 100%;
min-height: calc(100vh - 48px - 60px - 56px - 32px); /* fits container to remaining page height between nav bars */
float: left;
width: 100%;
overflow-y: auto;
cursor: text; /* [1] */
box-shadow: inset 0px 0px 8px 0px rgba(0, 0, 0, 0.2); /* [1] */
}
</style>
<script>
export default {
name: 'editor',
data () {
return: {
code: ''
}
},
methods: {
getNumberOfLines (text) {
/** replacement for the 'auto-grow' property of v-textarea
* as this feature has issue with maintaining the scroll
* position of the textarea.
* Source: https://github.com/vuetifyjs/vuetify/issues/5314
*/
if (text && typeof text === 'string') {
return text.replace(/\r\n/g, '\n').split('\n').length // replace makes sure, that this works with line breaks of different OS
}
},
setFocusToTextarea () { /* [1] */
const textLength = this.$refs.sheet.$el.querySelector('textarea').value.length
if (this.$refs.sheet.$el.querySelector('textarea') !== document.activeElement) {
this.$refs.sheet.$el.querySelector('textarea').focus()
this.$refs.sheet.$el.querySelector('textarea').setSelectionRange(textLength, textLength)
}
}
}
}
</script>
I wouldn't call this perfect as it still doesn't maintain scroll positions on mobile devices as I would like to have it, but there's much less scroll jumping than with 'auto-grow'.
The styles and function marked with /' [1] */ help to create a fake impression of having a full-height textarea even if the number of lines don't actually fill the height of the container.
You can see the effect by giving the v-textarea and the #editor-container a different background color.
What I didn't figure out is how to style the v-textarea from the <style> block. When analyzing the page, I can see that the _textarea_ correctly has the id _#editor_ but #editor { /* styles here */ } doesn't have any effect. So I styled it directly with the style property which works.
Cheers
Fred
If anyone is looking for a workaround, this is what I came up with for a textarea that fills the whole viewport:
<template> <div> <v-form> <div id="editor-container" @click="setFocusToTextarea()" > <v-textarea id="editor" v-model="code" hide-details no-resize loading="false" style="float: left; width: 100%; overflow-y: hidden; padding: 12px;" :rows="getNumberOfLines(code)" /> </div> </v-form> </div> </template> <style scoped> #editor-container { height: 100%; min-height: calc(100vh - 48px - 60px - 56px - 32px); /* fits container to remaining page height between nav bars */ float: left; width: 100%; overflow-y: auto; cursor: text; /* [1] */ box-shadow: inset 0px 0px 8px 0px rgba(0, 0, 0, 0.2); /* [1] */ } </style> <script> export default { name: 'editor', data () { return: { code: '' } }, methods: { getNumberOfLines (text) { /** replacement for the 'auto-grow' property of v-textarea * as this feature has issue with maintaining the scroll * position of the textarea. * Source: https://github.com/vuetifyjs/vuetify/issues/5314 */ if (text && typeof text === 'string') { return text.replace(/\r\n/g, '\n').split('\n').length // replace makes sure, that this works with line breaks of different OS } }, setFocusToTextarea () { /* [1] */ const textLength = this.$refs.sheet.$el.querySelector('textarea').value.length if (this.$refs.sheet.$el.querySelector('textarea') !== document.activeElement) { this.$refs.sheet.$el.querySelector('textarea').focus() this.$refs.sheet.$el.querySelector('textarea').setSelectionRange(textLength, textLength) } } } } </script>I wouldn't call this perfect as it still doesn't maintain scroll positions on mobile devices as I would like to have it, but there's much less scroll jumping than with 'auto-grow'.
The styles and function marked with /' [1] */ help to create a fake impression of having a full-height textarea even if the number of lines don't actually fill the height of the container.
You can see the effect by giving the v-textarea and the #editor-container a different background color.
What I didn't figure out is how to style the v-textarea from the
<style>block. When analyzing the page, I can see that the _textarea_ correctly has the id _#editor_ but#editor { /* styles here */ }doesn't have any effect. So I styled it directly with the style property which works.Cheers
Fred
Thanks for your solution. It solves my problem bro
For anyone interested in a different approach using another hidden textarea, here you go (don't know how well this works on mobile):
<template>
<div style="position: relative">
<v-textarea
class="v-textarea--auto-grow"
no-resize
ref="textArea"
v-model="dataText"
/>
<v-textarea
auto-grow
class="pa-3"
ref="textAreaHidden"
style="visibility: hidden;position: absolute; width: 100%; height: 0; overflow: hidden"
v-model="dataText"
/>
</div>
</template>
<script>
export default {
data() {
return {
dataText: "some text",
};
},
watch: {
dataText: {
immediate: true,
handler() {
if (this.$refs.textArea && this.$refs.textAreaHidden) {
this.$refs.textArea.$refs.input.style.height =
this.$refs.textAreaHidden.$refs.input.scrollHeight + "px";
}
},
},
},
mounted() {
setTimeout(() => {
this.$refs.textArea.$refs.input.style.height =
this.$refs.textAreaHidden.$refs.input.scrollHeight + "px";
}, 1000);
},
};
</script>
Most helpful comment
I don't think this problem is limited to Android Chrome. I could reproduce it on IE 11 and Chrome for Desktop. It really makes the auto-grow feature unusable as soon as there is a vertical scroll bar in the main window.
I am not sure why but I can't reproduce in chrome and IE for desktop in the codepen linked to the issue. Also can't reproduce in the vuetify doc for the textarea component. So there must be some interactions with other components to trigger this problem...