components icon indicating copy to clipboard operation
components copied to clipboard

[Textarea] Scroll position changes unexpectedly

Open MatthieuBarthel opened this issue 7 years ago • 13 comments

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

MatthieuBarthel avatar Dec 14 '17 15:12 MatthieuBarthel

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

xileftenurb avatar Jun 19 '18 20:06 xileftenurb

We have the same issue and this ticket is a blocker for us. Any updates?

fynnfeldpausch avatar Jan 30 '19 12:01 fynnfeldpausch

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;

alexmarh avatar Aug 20 '19 08:08 alexmarh

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 avatar Nov 06 '19 13:11 kevin-lozoya

@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

mmalerba avatar Nov 06 '19 22:11 mmalerba

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.

whein avatar Dec 11 '19 20:12 whein

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

xileftenurb avatar Mar 03 '20 20:03 xileftenurb

Also having an issue with this

samthe13th avatar Sep 25 '20 19:09 samthe13th

fixed by using autosize

wiedikerli avatar Oct 22 '20 09:10 wiedikerli

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

j2L4e avatar Aug 25 '21 12:08 j2L4e

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

alexandrasavina avatar Dec 08 '22 10:12 alexandrasavina

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;
          }
        });

fynnfluegge avatar Jul 06 '23 20:07 fynnfluegge

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.

ameramayreh avatar Feb 13 '24 14:02 ameramayreh