components
components copied to clipboard
[Textarea] Scroll position changes unexpectedly
Bug, feature request, or proposal:
Bug
What is the expected behavior?
When filling a textarea, we should see what we are writing :-)
What is the current behavior?
Under some conditions, writing inside a textarea with autoSize directive makes the page scroll position changes (we get scrolled up ? sorry for my english).
What are the steps to reproduce?
To reproduce my issue:
- With latest firefox (tested on macOS), open https://angular-material2-issue-pnr2sj.stackblitz.io/
- Scroll down the page.
- Enter the textarea and press any key.
- Your scroll position changes on the page, most of the textarea content isn't visible anymore, which is very annoying for writing.
It also happens on iOS 11 when the textarea is inside a parent with scroll overflow (not the case on the stackblitz example, but I can create another one if needed). I cannot reproduce it on Chrome and I haven't tested other browsers.
I believe the issue is related to this line, the parent's height get shorter for an instant which explains we get scrolled up.
Which versions of Angular, Material, OS, TypeScript, browsers are affected?
Latest Angular and Material
Any new or workaround for this issue?
I cannot use very long textarea (for prettyfied JSON, by example) because of this.
the scrolling up happen with firefox for every character, and for chrome when adding a new line. it also happen for both when I programmaticaly change the value of the textarea.
thank you
We have the same issue and this ticket is a blocker for us. Any updates?
I'm having a really terrible solution, but it works (CSS3, haven't checked if polyfill works). Just add following line to the css rule of scrollable container: scroll-padding-bottom: 1rem;
It look like Firefox puts the focus on the row where it is being written. But in Angular, the textareas not have 'rows' attribute, so only have one row and it puts the focus on the first line.
If we put textarea.setAttribute('rows', Math.floor(height / this._cachedLineHeight));
after
https://github.com/angular/components/blob/fcd9bf73cfea05ae20f28120014ad55a604602dd/src/cdk/text-field/autosize.ts#L226
we have solved the bug on Firefox, but on Chrome this calc: https://github.com/angular/components/blob/fcd9bf73cfea05ae20f28120014ad55a604602dd/src/cdk/text-field/autosize.ts#L222
is incorrect, unlike on Firefox. The issue is when have less lines that before, not decreases the height. But when have more lines that before, it's fine, increases the height.
I tried put textarea.removeAttribute('rows');
after
https://github.com/angular/components/blob/fcd9bf73cfea05ae20f28120014ad55a604602dd/src/cdk/text-field/autosize.ts#L217
but I have same problem that before: Scroll position change.
I think that we need to do the calc in another way or distinguish in some way between browsers.
What do you think?
@kevin-lozoya I would prefer to find a method that works on all browsers, but if that is not possible we can try special-casing certain browsers. If you have a suggestion for a better way to calculate it, please feel free to send a PR and mention me on it. This calculation always seemed kind of hacky, but was the closest I was able to get after looking at it for a while
I found that this issue could be avoided by removing the placeholder logic:
https://github.com/angular/components/blob/fcd9bf73cfea05ae20f28120014ad55a604602dd/src/cdk/text-field/autosize.ts#L210 https://github.com/angular/components/blob/fcd9bf73cfea05ae20f28120014ad55a604602dd/src/cdk/text-field/autosize.ts#L218 https://github.com/angular/components/blob/fcd9bf73cfea05ae20f28120014ad55a604602dd/src/cdk/text-field/autosize.ts#L227
AFAIK this is only an option if your placeholder text will always fit on 1 line in the textarea.
It there any update on this issue? It does not seem to have any workaround for now, as all solution proposed need to modify the cdk direcly.
thank you
Also having an issue with this
fixed by using autosize
Had a similar issue where this happened on creation of the textarea. Leaving this here as it seems related.
The measuring process temporarily adds a hidden clone of the textarea as a sibling. It might interfere with your layout causing the scroll position change. In my case, wrapping every textarea in a div with position: relative
fixes/works around the issue.
https://github.com/angular/components/blob/c2a20c4a035ef57bf598fd78bc7284c180b34c78/src/cdk/text-field/autosize.ts#L205
Find your own workaround:
- Open Devtools
- find webpack:///./node_modules/@angular/cdk/ivy_ngcc/fesm2015/text-field.js
- find 'appendChild(textareaClone)' and set a breakpoint there
- when it's hit step one line further to see the layout break
- adjust layout/css to unbreak it
- profit
I faced with similar issue in Chrome.
I fixed it with workaround without using cdk:
this.textarea.nativeElement.style.marginBottom = '${this.textarea.nativeElement.clientHeight}px'; this.textarea.nativeElement.style.overflow = 'hidden'; this.textarea.nativeElement.style.height = '0px'; this.textarea.nativeElement.style.height = this.textarea.nativeElement.scrollHeight + 'px'; this.textarea.nativeElement.style.marginBottom = '0px';
So I suppose there is need to add the same margin as it is for Firefox: #https://github.com/angular/components/blob/15.0.x/src/cdk/text-field/autosize.ts#L242
My workaround is to intercept Enter
and tailor the content string accordingly:
textarea.addEventListener('keydown', (event) => {
// if the key code is 13 (ENTER)
if (event.keyCode === 13) {
event.preventDefault(); // prevent usual browser behavour
var currentPos = textarea.selectionStart;
var value = textarea.value;
var newValue =
value.substr(0, currentPos) + '\n' + value.substr(currentPos);
textarea.value = newValue;
textarea.selectionEnd = currentPos + 1;
}
});
I have fixed this by temporary set the parent's min-height to its offsetHeight before adding the measuring class:
const previousParentMinHeight = element.parentElement.style.minHeight;
element.parentElement.style.minHeight = `${element.parentElement.offsetHeight}px`;
// Reset the textarea height to auto in order to shrink back to its default size.
// Also temporarily force overflow:hidden, so scroll bars do not interfere with calculations.
element.classList.add(measuringClass);
// The measuring class includes a 2px padding to workaround an issue with Chrome,
// so we account for that extra space here by subtracting 4 (2px top + 2px bottom).
const scrollHeight = element.scrollHeight;
element.classList.remove(measuringClass);
element.parentElement.style.minHeight = previousParentMinHeight;
It worked perfectly with me. Is there any concerns about this fix?
If not, I can submit a pull request for it.
Thanks.