Anfang des Jahres habe ich etwas über automatisch wachsende Textbereiche und Eingabefelder geschrieben. Die Idee war, ein <textarea> mehr wie ein <div> zu gestalten, sodass es sich in der Höhe ausdehnt, so viel wie nötig, um den aktuellen Wert aufzunehmen. Es ist fast seltsam, dass es dafür keine einfache native Lösung gibt, oder? Rückblickend auf diesen Artikel waren keine meiner Ideen besonders gut. Aber Stephen Shaws Idee, auf die ich am Ende verwiesen habe, ist tatsächlich eine sehr gute Idee dafür, daher wollte ich dem etwas Aufmerksamkeit schenken und erklären, wie sie funktioniert, denn es scheint die endgültige Antwort darauf zu sein, wie diese Benutzererfahrung umgesetzt werden kann, bis wir etwas Natives und Besseres erhalten.
Hier ist die Demo, falls Sie nur ein funktionierendes Beispiel wünschen
Der Trick besteht darin, den Inhalt des <textarea> exakt in einem Element zu replizieren, das die Höhe automatisch erweitern und seine Größe anpassen kann.
Sie haben also ein <textarea>, das die Höhe nicht automatisch erweitern kann.
Stattdessen replizieren Sie das Aussehen, den Inhalt und die Position des Elements exakt in einem anderen Element. Sie verstecken die Replikation visuell (man kann genauso gut die technisch funktionierende sichtbar lassen).
Nun sind alle drei Elemente miteinander verbunden. Welches der Kindelemente am höchsten ist, drängt das Elternelement auf diese Höhe, und das andere Kindelement folgt. Das bedeutet, dass die Mindesthöhe des <textarea> die "Grundhöhe" wird, aber wenn das replizierte Textelement höher wächst, wächst alles mit.
So clever. Ich liebe es so sehr.
Sie müssen sicherstellen, dass das replizierte Element genau das gleiche ist
Gleiche Schriftart, gleicher Abstand, gleicher Rand, gleicher Rahmen… alles. Es ist eine identische Kopie, nur visuell versteckt mit visibility: hidden;. Wenn es nicht genau dasselbe ist, wird nicht alles genau richtig zusammen wachsen.
Wir brauchen auch white-space: pre-wrap; für den replizierten Text, da Textbereiche sich so verhalten.
Das ist der seltsamste Teil
In meiner Demo verwende ich ::after für den replizierten Text. Ich bin mir nicht sicher, ob das der bestmögliche Ansatz ist oder nicht. Es fühlt sich für mich sauber an, aber ich frage mich, ob die Verwendung eines <div aria-hidden="true"> für Screenreader sicherer ist? Oder reicht vielleicht visibility: hidden; dafür? So oder so, das ist nicht der seltsame Teil. Das ist der seltsame Teil
content: attr(data-replicated-value) " ";
Da ich ein Pseudoelement verwende, ist dies die Zeile, die das data-Attribut vom Element nimmt und den Inhalt auf der Seite rendert mit diesem zusätzlichen Leerzeichen (das ist der seltsame Teil). Wenn Sie das nicht tun, fühlt sich das Endergebnis "springend" an. Ich kann nicht sagen, dass ich es vollständig verstehe, aber es scheint, als ob es das Zeilenumbruchverhalten zwischen den Textbereichs- und Textelementen besser respektiert.
Wenn Sie kein Pseudoelement verwenden möchten, ist das in Ordnung für mich, aber achten Sie auf das springende Verhalten.
Besonderen Dank an Will Earp und Martin Tillmann, die beide am selben Tag zufällig eine E-Mail schrieben, um mich daran zu erinnern, wie clever Shaws Technik ist. Hier ist ein Beispiel, das Martin mit Alpine.js und Tailwind erstellt hat und das auch irgendwie zu einer Einzeiler-Lösung wird (aber beachten Sie, dass es das springende Problem hat).
Ich bin sicher, Sie alle können sich vorstellen, wie man dies mit Vue und React und ähnlichem umsetzt, um den Zustand zwischen einem Textbereich und einem anderen Element leicht beizubehalten. Ich werde hier keine Beispiele einfügen, teils weil ich faul bin, aber hauptsächlich, weil ich denke, dass Sie verstehen sollten, wie das funktioniert. Das macht Sie klüger und lässt Sie Ihre Website besser verstehen.
Vielen Dank für das Teilen einer solchen Innovation. Der Textbereich zeigt seine vertikale Scrollleiste, während er in Firefox höher wird.
Ich habe ein
overflow: hidden;auf das<textarea>gesetzt. Das scheint für alle Browser in Ordnung zu sein und behebt die sichtbare Scrollleiste in Firefox.Nur zur Anmerkung, Jim Nielsen hat sich kürzlich mit dem Ändern der Größe von Textbereichen befasst
https://blog.jim-nielsen.com/2020/automatically-resize-a-textarea-on-user-input/
Es handelt sich jedoch um eine kleine Bibliothek, im Gegensatz zum sehr kleinen JavaScript, das für die Technik in diesem Beitrag benötigt wird. Hier ist seine Demo, die ich forkte, um einen Import von Skypack zu verwenden
Und schauen Sie sich auch seinen Beitrag an, um eine Notiz über einen interessanten Ansatz mit Web Components zu finden.
Mache diesen Trick schon seit 2 Jahren.
Der Unterschied war, dass ich
position: absolutemitleft: 0; right: 0; top: 0; bottom: 0verwendete, um die Größe des Textbereichs an sein Elternelement anzupassen; und<br />verwendete, um die Zeilenumbrüche beizubehaltenDies ist eines dieser Dinge, die idealerweise von Browsern implementiert werden sollten (vielleicht hinter einem Attribut, z.B. ).
Schon seit langer Zeit benutze ich das
size-Attribut, um<input/>s automatisch wachsen zu lassen. Sie können einen einfachenonchange-Listener verwenden und dann die Größe aufvalue.lengthsetzen.Sie können eine Demo davon in meinem Codepen sehen.
Ich habe in EmberJS mit Handlebars etwas Ähnliches gemacht
Hey, kann man nicht einfach einen Div mit dem Attribut
contenteditableeinfügen?Vielleicht! Der ursprüngliche Artikel geht ein wenig darauf ein. Ich kenne nur nicht ALLE Zugänglichkeits- und UX-Implikationen davon. Ich habe Zweifel, dass es gut genug gleich funktioniert (und sicherheitshalber "sendet" es nicht korrekt).
Die Höhe gleich der Scrollhöhe setzen?
Scrollhöhe + 2px ;)
Neat, sieht toll aus. Ich habe mich gefragt, wie man die maximale Anzahl von Zeilen damit begrenzen könnte?
Wie wäre es, einfach den Wert des
rows-Attributs zu ändern? Dies würde die Zeilenhöhe, Schriftgröße usw. dynamisch berücksichtigen.Ja, wir müssten immer noch die Anzahl der Zeichen im Auge behalten, aber es gäbe zwei Elemente weniger im DOM?
Es erscheint mir schwieriger zu wissen, genau wann man eine weitere Zeile hinzufügen muss. Aber probieren Sie es aus!
Vielen Dank für die ausgezeichnete Lösung, Chris! Für jemanden, der relativ neu in CSS Grid ist, was ist in diesem Fall der beste Weg, um zu verhindern, dass sich das Grid horizontal ausdehnt, falls ein sehr langes "Wort" in den Textbereich eingegeben wird? Im Wesentlichen möchte ich, dass die Eigenschaft
overflow-wrap: break-worddes Textbereichs greift.Ein paar hilfreiche Artikel, hoffe ich
Erstaunlich, genau das, was ich brauchte. Ich mag es nicht, jQuery zu benutzen und mag schön sauberes HTML, CSS und JavaScript.
Wie ändere ich die anfängliche Höhe des Textbereichs?
@Gellert: gute Frage. Diese Demo ist nett, aber sie funktioniert nicht sofort für Textbereiche, die bereits mit mehrzeiligem oder umbrechendem Inhalt gefüllt sind. Sie müssen also das tun, was das JavaScript beim Laden der Seite tut: über alle Ihre Textbereiche iterieren und den Datensatzwert des Elternelements auf den Wert des Textbereichs setzen
parent.dataset.replicatedValue = textarea.valueKönnen Sie bitte ein Beispiel für die anfängliche Höhe geben?
Wie setze ich den anfänglichen Textbereich auf 100 % Breite und beschränke das Wachstum nur auf Zeilen und lasse ihn nicht von der Seite überlaufen?
Grundsätzlich, wie sorge ich dafür, dass sich der Textbereich nur in der Höhe und nicht in der Breite wächst. Ich habe
overflow-wrapversucht, was das Einzige ist, woran ich denken kann.@Mike,
Siehe Chris' Kommentar oben zu "Handling Long Words and URLs"
Einfügen der im Artikel erwähnten CSS (ohne die mit Bindestrichen verbundenen) in die CSS
Scheint das Problem zu lösen, wenn es sich um Zeilenumbrüche handelt, wenn keine Trennungen/Leerzeichen im Text vorhanden sind. (Zumindest in Chrome, nicht woanders getestet), obwohl ich denke, dass es technisch nur auf das ::after angewendet werden muss.
@Chris Coyier, gibt es einen Grund, warum dies nicht das Standardverhalten in Ihrer Demo sein sollte? Es scheint das zu sein, was die meisten Leute wollen, da ein unbegrenztes horizontales Wachstum wahrscheinlich zu einem Layout-Bruch führen würde, und max-width kann vom Div, in dem dies platziert wird, gesteuert werden, was ein gewisses horizontales Wachstum ermöglicht, wenn erforderlich, ohne dass es vom Bildschirm überschwappt.
Das ist so nett!!!! Vielen Dank!!
Gibt es auch eine Implementierung mit Platzhalter?
Das ist brillant!!
Was, wenn man mit einer festen Höhe für den Textbereich beginnt, sagen wir eine geringere Höhe als Standard?
Hier ist meine Variation
Sie verwendet Flexbox anstelle von CSS Grid und hat ein verstecktes DIV hinter dem Textbereich.
Getestet auf Chrome, FireFox und Safari
Das ist eine sehr komplexe Lösung, ich habe eine einfachere gefunden.
Beim "input"-Ereignis ändern Sie die Höhe des Textbereichs auf den Wert "auto" und später ändern Sie ihn zu
textarea.scrollHeight+ "px".Denken Sie daran, die folgenden Eigenschaften in CSS zu setzen
resize: none;
overflow-y: hidden;
Ich hoffe, es hilft.
Schneller Blick in Chrome hier... schien sich für mich nicht auszudehnen.
Entschuldigung, ich habe es falsch gemacht, jetzt ist es soweit
Diese Lösung funktioniert für mich in Chrome. Ich mag die Einfachheit.
Das Einzige ist, dass, nachdem ich zu tippen beginne, der Textbereich um ein paar Pixel wächst.
Wenn ich ändere
textArea.style.height = textArea.scrollHeight + “px”
zu
textArea.style.height = textArea.scrollHeight – 4 + “px”
funktioniert es für mich richtig.
Hat jemand diese Lösung in anderen Browsern getestet?
Und ist das eine gute Lösung für die Produktion oder sollte ich weiterhin https://www.npmjs.com/package/autosize verwenden?
Ich möchte die Verwendung des NPM-autosize-Pakets durch eine einfache Lösung ersetzen, um die Anzahl der Abhängigkeiten zu verringern.
RJ, ich habe erkannt, dass der "magische" Betrag, den Sie subtrahieren "-4", der vertikale Abstand ist. Mein Textbereich hat einen 2px-Abstand drumherum, also sobald ich gemacht habe
textArea.style.height = textArea.scrollHeight – 4 + “px” hat es gelöst.
Daher muss der Wert, von dem Sie subtrahieren, die Summe aus pt+pb sein
https://codepen.io/matthias-hermsdorf/embed/PoOVYaY
Die benötigte Höhe ist im
textarea.scrollHeightgespeichert.Aber um sie zu erhalten, muss die Höhe auf
inheritgesetzt werden.Während die Höhe des Elements auf
inheritgesetzt wird, setzen einige Browser sie auf diewirklich kleine anfängliche Höhe und verkürzen dadurch den Dokumentkörper.
Um das Scrollen aufgrund der Höhenänderung zu
inheritzu verhindern, wird der Textbereich in Flexbox mit einem zweiten und unsichtbarenElement nebeneinander eingepackt. Der Heightkepper macht seine Arbeit und setzt die Höhe der Flexbox.
Nur zum Spaß, da es sich wahrscheinlich um einen Bug handelt, hier ist eine Version, die nur in Chrome ohne jegliches JS funktioniert.
Verwenden Sie sie nicht!!
Ich dachte nur, sie könnte von Interesse sein, auch wenn sie nicht verwendet werden kann.
Für Safari (iOS) musste ich Folgendes hinzufügen, da ein Textbereich einen inhärenten 1px-Rand hat
Versuche, dies in React zu implementieren und verwende kein Pseudoelement... Ich konnte nicht herausfinden, wie ich das replizieren kann, was das zusätzliche " " tut...
Hallo! Ich folge dem CSS-Rezept und erhalte, wie die Demo, doppelte Zeilen-/Reihenhöhe. Ich hätte wirklich gerne eine einzelne, saubere Zeile, bevor ein Zeilenumbruch das automatische Wachstum auslöst. Wie stelle ich die anfängliche Höhe so ein, dass sie nicht höher ist als die benötigte Textzeile?
Danke, es hat funktioniert