ng-elastic
ng-elastic copied to clipboard
large textarea input that extends below the input keyboard and also can not scroll the page below that large text area and keep it there.
While entering in text on Android so that the textarea extends below the keyboard, the text is hidden under the keyboard. You can scroll the page to see where you are typing, but then it will continue below the keyboard again. Often, the textarea will scroll to the top while typing. When finished enetring in text and you click outside of the text area, the page scrolls to the top and I can not access anything under that textarea. I can scroll the page down, but as i release my finger, it scrolls back to the top.
~Would you be able to test out the patch #13 to see if this solves your issue?~
EDIT: this won't work, please see below instead.
I've released a new version of this module under a new name, ng-elastic. I believe this should fix this issue.
Please see these instructions for information on how to migrate to the new version. If you could test this out and let me know if the issue is resolve, it would be much appreciated.
Thanks!
This patch has fixed many issues that I was having. The textarea does not scroll to the top while typing anymore and I can now scroll to the bottom of the page. However, as the text area expands enough to reach the android keyboard. the bottom couple of text rows are still under the keyboard. When deleting text, the textarea does not shrink back, but stays expanded. Good job so far though. Thanks for the good work.
@TheHibbs thanks for testing this, I'll see what I can do to get this working on Android.
@fiznool i can confirm the problem on Android and IOS
i am using ionic and tried latest release of this repo i mean ng-elastic and i can confirm some issues
on IOS: when textarea grow up to be under the keyboard it will be really under the keyboard :) i mean it's not making small scroll down to can see what are u typing but actually this practice from IOS it's self they are not scrolling automatically u should scroll ur self but the problem with this directive we can't scroll our self because if u tried to scroll down ur self it will back to top after any character u type so the last line will disappear again
on Android: it's small better than ios so u can see what are u typing but not all the time :) if the textarea grow up more and more as my trial about 15 lines , the problem will appear and it's almost same of IOS i mean the last line disappear after every character u type
thanks for the good work and i hope u can fix this issues as soon as possible
@fiznool after diving into this i figured the jumping happening because that we make the height of the textarea auto to can calc the height value we should set , but that making the page jump so the last line of the textarea will be under the keyboard
so i decided to make a clone from the textarea and make it absolute so it's not effect the scroll area and calc the height we should set from it so the main textarea will not effect
so my final directive code looks like this:
import { ElementRef, HostListener, Directive, AfterViewInit } from '@angular/core';
@Directive({
selector: '[my-fz-elastic]'
})
export class MyElasticDirective implements AfterViewInit {
private textareaEl: HTMLTextAreaElement;
private clonedTextareaEl: HTMLTextAreaElement;
constructor(
private element: ElementRef,
) {}
ngAfterViewInit() {
if (this.isTextarea(this.element.nativeElement)) {
this.setupTextarea(this.element.nativeElement);
return;
}
const children: HTMLElement[] = Array.from(this.element.nativeElement.children) as HTMLElement[];
const textareaEl = children.find(el => this.isTextarea(el));
if (textareaEl) {
this.setupTextarea(textareaEl as HTMLTextAreaElement);
return;
}
throw new Error('The `fz-elastic` attribute directive must be used on a `textarea` or an element that contains a `textarea`.');
}
@HostListener('input')
onInput(): void {
// This is run whenever the user changes the input.
this.adjust();
}
private isTextarea(el: HTMLElement) {
return el.tagName === 'TEXTAREA';
}
private setupTextarea(textareaEl: HTMLTextAreaElement) {
this.textareaEl = textareaEl;
// Set some necessary styles
const style = this.textareaEl.style;
style.overflow = 'hidden';
style.resize = 'none';
//clone the input to calc the height
this.clonedTextareaEl = <HTMLTextAreaElement>this.textareaEl.cloneNode(false);
this.clonedTextareaEl.tabIndex = -1;
this.clonedTextareaEl.style.height = 'auto';
this.clonedTextareaEl.style.opacity = '0';
this.clonedTextareaEl.style.position = 'absolute';
this.clonedTextareaEl.style.top = '0';
this.clonedTextareaEl.style.zIndex = '-100';
this.textareaEl.parentElement.style.position = 'relative';
this.textareaEl.parentNode.appendChild(this.clonedTextareaEl);
// Ensure we adjust the textarea if
// content is already present
this.adjust();
}
private adjust(): void {
if (!this.textareaEl || !this.clonedTextareaEl) {
return;
}
this.clonedTextareaEl.value = this.textareaEl.value;
this.textareaEl.style.height = this.clonedTextareaEl.scrollHeight + "px";
}
}
and i actually removed blocks that i didn't see it's necessary in my case
NOTE: i tested this with ionic only and it's working perfect
finally i hope this can help some one :)
@alaa-alshamy this is fantastic work. Thanks so much!
I will try to look at this soon. It's the last thing on my TODO list before v1 final is released.
@fiznool i have small update to fix bug in ios webview to force that webview to make render
the change is after this line in the adjust function:
this.clonedTextareaEl.value = this.textareaEl.value;
just add this line :
this.clonedTextareaEl.style.zIndex = (this.clonedTextareaEl.style.zIndex == '-100' ? '-101' : '-100');// this just to force the ios webview to make render
@fiznool i made some changes on the directive also :) because i faced some issues with making the cloned textarea absolute in WKWebView in IOS, so i decided to not using absolute and make the cloned textarea's height = 0
so my final directive code looks like this:
import { ElementRef, HostListener, Directive, AfterViewInit } from '@angular/core';
@Directive({
selector: '[my-fz-elastic]'
})
export class MyElasticDirective implements AfterViewInit {
private textareaEl: HTMLTextAreaElement;
private defaultTextareaElHeight: number;
private clonedTextareaEl: HTMLTextAreaElement;
constructor(
private element: ElementRef,
) {}
ngAfterViewInit() {
if (this.isTextarea(this.element.nativeElement)) {
this.setupTextarea(this.element.nativeElement);
return;
}
const children: HTMLElement[] = Array.from(this.element.nativeElement.children) as HTMLElement[];
const textareaEl = children.find(el => this.isTextarea(el));
if (textareaEl) {
this.setupTextarea(textareaEl as HTMLTextAreaElement);
return;
}
throw new Error('The `fz-elastic` attribute directive must be used on a `textarea` or an element that contains a `textarea`.');
}
@HostListener('input')
onInput(): void {
// This is run whenever the user changes the input.
this.adjust();
}
private isTextarea(el: HTMLElement) {
return el.tagName === 'TEXTAREA';
}
private setupTextarea(textareaEl: HTMLTextAreaElement) {
this.textareaEl = textareaEl;
this.defaultTextareaElHeight = this.textareaEl.clientHeight;
// Set some necessary styles
const style = this.textareaEl.style;
style.overflow = 'hidden';
style.resize = 'none';
//clone the input to calc the height
this.clonedTextareaEl = <HTMLTextAreaElement>this.textareaEl.cloneNode(false);
this.clonedTextareaEl.tabIndex = -1;
this.clonedTextareaEl.disabled = true;
this.clonedTextareaEl.style.height = '0';
// remove margin top and bottom because it does not matter in calculating the height of textarea and they make useless space
// we just need margin right and left
this.clonedTextareaEl.style.marginTop = '0';
this.clonedTextareaEl.style.marginBottom = '0';
this.textareaEl.parentNode.appendChild(this.clonedTextareaEl);
// Ensure we adjust the textarea if
// content is already present
this.adjust();
}
private adjust(): void {
if (!this.textareaEl || !this.clonedTextareaEl) {
return;
}
this.clonedTextareaEl.value = this.textareaEl.value;
this.textareaEl.style.height = Math.max(this.clonedTextareaEl.scrollHeight, this.defaultTextareaElHeight) + "px";
}
}
also facing the same issue and would love to see this get into ng-elastic!
@fiznool @alaa-alshamy if it helps i can turn this into a pull request..?
Sorry for the radio silence on this. I've started merging in the changes from @alaa-alshamy but the code snippet above was taken from an older version of the library, so it's taking a little longer to incorporate it in.
I will try to get this incorporated soon, but in the meantime @shanesmith if you want to submit a PR then please go ahead!
I pushed the original edits above to a new branch (fix-gh15). It needs work as the cloned textarea can be seen below the original one (see gif below). There is also some jumpy movement on the text when a newline is entered (can't see this on the gif as the frame rate was too slow to pick it up).
If anyone would like to take this on further please let me know, otherwise I should be able to take another look in the next week or two.

Thanks for getting back to me.
The cloned area showing at the bottom could perhaps be a min-height styling, or maybe some padding if it has box-sizing: content-box rather than border-box.
The jumpy movement seems to be caused by the setTimeout() here. I've tried removing it and the texterea seems to behave as expected after some quick testing, do you know why it was added?
I'll likely have time to submit a PR soon when we fix these remaining issues. =)
@fiznool
for that cloned textarea can be seen below the original one, it's because there's padding top and bottom and border top and bottom, but we can't remove those as we did with margins because if we did the calculation will fail, so the safest way to fix this in my opinion that make the cloned text area opacity: 0, this will make useless space below the original text area but i think it's not a big trouble, And the other solution is using absolute position but it's making some issues in ios WKWebView as i said before
for the jumpy movement, i agree with @shanesmith, so it's caused by the timeout
i made plnkr has the latest changes u can check it here: https://plnkr.co/edit/BIHIlnjOHPHT9dFMk3Zk?p=preview
Is it possible that the position absolute solution is failing due to a negative z-index? Or because it is a sibling of the real textarea?
The angular1 library uses a cloned textarea which is added as a child to the body, and positioned off the page completely. Maybe this is an approach to try?
https://github.com/monospaced/angular-elastic/blob/4c2c75e413e668b4117bf2b3741272b2ab9e24c5/elastic.js#L52
On Wed, 23 Aug 2017 at 19:10, alaa-alshamy [email protected] wrote:
@fiznool https://github.com/fiznool for that cloned textarea can be seen below the original one, it's because there's padding top and bottom and border top and bottom, but we can't remove those as we did with margins because if we did the calculation will fail, so the safest way to fix this in my opinion that make the cloned text area opacity: 0, this will make useless space below the original text area but i think it's not a big trouble, And the other solution is using absolute position but it's making some issues in ios WKWebView as i said before
for the jumpy movement, i agree with @shanesmith https://github.com/shanesmith, so it's caused by the timeout
i made plnkr has the latest changes u can check it here: https://plnkr.co/edit/BIHIlnjOHPHT9dFMk3Zk?p=preview
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/fiznool/ng-elastic/issues/15#issuecomment-324417412, or mute the thread https://github.com/notifications/unsubscribe-auth/AAyGf4ZfqcKZZfnCgLIi-XbV0ZsTfYZwks5sbGsIgaJpZM4M6aWe .
Position absolute was also the approach I first had in mind.... @alaa-alshamy what issues were you seeing on WKWebView?
@fiznool @shanesmith
actually i forgot the issues that i faced there :) it was long time, but i will try to remember