addEvent für bestimmte Tags immer und überall
SorkenKind mech
- javascript
Mahlzeit!
zur Vorgeschichte: Ich stelle gerade eine Seite von "Quirks" auf "HTML5" um, sozusagen ...
Ein "Feature" war bei der textarea das Overflow:auto, was im IE verursachte, dass das Textfeld sich in der höhe stets dem Inhalt angepasst hat.
Nun scheine ich ja um JS nicht mehr herum zu kommen, um dieses Verhalten "nachzustellen"
also habe ich nun folgendes script:
function xy(obj)
{
obj.style.overflow='scroll'; // chrome workaround
obj.style.height=obj.scrollHeight + 'px';
obj.style.overflow='hidden'; // chrome workaround
}
so, das funktioniert schonmal in IE, FF und Chrome - mehr als Ausreichend für das aktuelle Ziel
Problem: Ich möchte nun natürlich nicht bei jedem Textfeld ein onKeyUp oder ähnliches rein setzen, zumindest nicht manuell ...
ich könnte jetzt alle getElementsByTagName() durchlaufen ... aber was ist, wenn ein neues Textfeld dynamisch generiert wird?
Dann habe ich die Idee gelesen, einfach einen allgemeinen Eventhandler zu bastelt, und bei klick oder Focus und passendem tag die Funktion aufzurufen ... nett, man hätte das gleiche prblem wie zuvor, plus dass man jedes element einmal aktivieren müsste, damit es anfänglich einmal korrekt dargestellt wird ...
hat hier jemand eine Idee wie ich sicherstelle dass
sicherlich könnte ich einfach jede sekunde einmal alle Elemente durchlaufen, aber das ist ja nicht grad sinn du zweck der Sache ;)
vielen Dank für Eure Ideen schonmal im voraus ;)=
PS: JQuery ist keine Alternative!
PPS: eine reine CSS-Lösung wäre mir immernoch lieber ;(
LG euer SorgenKind mech
Liebes SorkenKind mech,
function xy(obj)
{
obj.style.overflow='scroll'; // chrome workaround
obj.style.height=obj.scrollHeight + 'px';
obj.style.overflow='hidden'; // chrome workaround
}
setzt voraus, dass man für jedes <textarea>-Element diese Funktion aufruft und das Elementobjekt als Argument übergibt. Wie Du schon geschrieben hast, keine "schöne" Lösung.
> Dann habe ich die Idee gelesen, einfach einen allgemeinen Eventhandler zu bastelt, und bei klick oder Focus und passendem tag die Funktion aufzurufen ... nett, man hätte das gleiche prblem wie zuvor, plus dass man jedes element einmal aktivieren müsste, damit es anfänglich einmal korrekt dargestellt wird ...
Hier irrst Du. Was, wenn das <body>-Element einen onclick-Handler hätte, der wissen will, in welchem Element dieses Event ursprünglich aufgerufen wurde? Das Klick-Event wird ja in der Baumstruktur bis zum höchstmöglichen Elternelement weitergereicht (event bubbling), wenn man das nicht aktiv im Programmcode verhindert.
Du brauchst also eine Funktion, die das ursprünglich angeklickte Element daraufhin prüft, ob es eine <textarea> war. Danach handelst Du dynamisch:
~~~javascript
document.body.onclick = function (e) {
var o;
e = e || window.event; // Krücke für IE
o = e.target || e.srcElement; // W3C | IE
if (o.tagName == "TEXTAREA") {
// ...
}
}
Liebe Grüße,
Felix Riesterer.
Liebes SorkenKind mech,
function xy(obj)
{
obj.style.overflow='scroll'; // chrome workaround
obj.style.height=obj.scrollHeight + 'px';
obj.style.overflow='hidden'; // chrome workaround
}
>
> setzt voraus, dass man für jedes <textarea>-Element diese Funktion aufruft und das Elementobjekt als Argument übergibt. Wie Du schon geschrieben hast, keine "schöne" Lösung.
>
> > Dann habe ich die Idee gelesen, einfach einen allgemeinen Eventhandler zu basteln, und bei klick oder Focus und passendem tag die Funktion aufzurufen ... nett, man hätte das gleiche problem wie zuvor, plus dass man jedes element einmal aktivieren müsste, damit es anfänglich einmal korrekt dargestellt wird ...
>
> Hier irrst Du. [...]
huhu
vielleicht habe ich mich falsch ausgedrückt, das mit dem bubbling ist mir klar, das ding ist halt nur, dass die Funktion halt erst aufgerufen wird, wenn etwas mit dem entsprechenden element passiert, sprich ein Event ausgelöst wird.
eine textarea mit vorbelegtem Inhalt wie das antwortfeld, in das ich gerade schreibe, würde jedoch kein Event auslösen und sich dementsprechend bei aufruf der Seite nicht automatisch in der höhe anpassen.
auch ein textarea, welches dynamisch erzeugt wird und schon einen gewissen Inhalt hat, würde hier nicht betroffen sein, es würde einfach klein bleiben
hast du da eine "schöne" lösung für?
>
> Liebe Grüße,
>
> Felix Riesterer.
Hi!
Dann habe ich die Idee gelesen, einfach einen allgemeinen Eventhandler zu bastelt, und bei klick oder Focus und passendem tag die Funktion aufzurufen ...
Ja, richtig. Es ist aber nicht nötig, auf den Fokus zu reagieren. Diese Kontextinformation ist zwar nötig, ist aber bei Texteingabe in das Feld bekannt.
- die Funktion für jedes vorhandes textarea-element nach laden der seite einmal aufrufe
Wie du sagst, einmal beim Laden der Seite mit getElementsByTagName('textarea'), querySelector('.klasse') o.ä. alle gewünschten Elemente suchen und ihre Größe anpassen.
- die Funktion bei Änderung des Inhaltes der textarea aufgerufen wird
keyup- und keypress-Events steigen auf (Bubbling). Du kannst sie zentral bei document oder document.body überwachen. Das nennt sich Event Delegation.
var resizeHandler = function(element) {
// was auch immer nötig ist
};
var resizeTextareaOnKeyup = function(event) {
// Kommt der Event von einer textarea?
if (event.target.nodeName === 'TEXTAREA') {
resizeHandler(event.target);
};
};
document.addEventListener('keyup', resizeTextareaOnKeyup, false);
(wie immer ungetestet)
Siehe auch
http://molily.de/js/event-handling-fortgeschritten.html#dom-events
http://molily.de/js/event-handling-objekt.html#currenttarget-target
Man könnte auch mit den Events input oder change arbeiten, um die Logik robuster zu machen.
- die Funktion auch bei dynamisch hinzugefügten textarea greift
Da mit Bubbling gearbeitet wird und die Events zentral beim document überwacht werden, wirkt sich das auch auf nachträglich eingefügte Elemente aus.
PS: JQuery ist keine Alternative!
jQuery ist keine Alternative zu JavaScript, sondern lediglich eine Bibliothek, mit der du vieles, was sich auch in reinem JavaScript umsetzen lässt, einfacher schreiben kannst.
PPS: eine reine CSS-Lösung wäre mir immernoch lieber ;(
Suchst du vielleicht eher contenteditable anstelle einer textarea?
Mathias
Hi!
huhu ;)
Man könnte auch mit den Events input oder change arbeiten, um die Logik robuster zu machen.
- die Funktion auch bei dynamisch hinzugefügten textarea greift
Da mit Bubbling gearbeitet wird und die Events zentral beim document überwacht werden, wirkt sich das auch auf nachträglich eingefügte Elemente aus.
in der tat, sofern das textarea jedoch mit Inhalt vorbelegt ist, müsste ich wieder bei Erstellung dessen die Funktion "manuell" aufrufen um den "startwert" zu erhalten, was ich nach Möglichkeit vermeiden möchte
PS: JQuery ist keine Alternative!
jQuery ist keine Alternative zu JavaScript, sondern lediglich eine Bibliothek, mit der du vieles, was sich auch in reinem JavaScript umsetzen lässt, einfacher schreiben kannst.
sorry, damit war nicht gemeint, dass es keine alternative zu JS ist, sondern einfach nur, dass ich es vermeiden möchte ;)
PPS: eine reine CSS-Lösung wäre mir immernoch lieber ;(
Suchst du vielleicht eher contenteditable anstelle einer textarea?
jain, ansich ist mir das bekannt und ich finde es auch wunderbar, mir fehlt jedoch die Möglichkeit, entsprechenden Inhalt über ein Formular ohne jede weitere JS-Auswertung zu versenden
im Moment denke ich darüber nach, dass ja jedes objekt mit Standardwerten gefüttert wird, manche werden vererbt, manche sind einfach vorgeschrieben, viele können überschrieben werden (bei CSS beispielsweise)
könnte man nicht einfach dem browser sagen, dass jedes element vom typ textarea andere standardwerte hat als normalerweise üblich?
ich vergleiche das mal mit prototype bei JavaScript - ich habe da zwar nicht so viel Ahnung, aber wenn ich das richtig verstehe, kann ich ja auch dort Methoden und Attribute von Objekten "überschreiben"
demnach würde man einfach sagen, dass bei einer textarea das Attribut "onKeyUp", was ja dann als eventhandler verarbeitet wird, nicht mir "" vorbelegt wird, sondern mit "xy(this)"
ist sowas möglich?
Mathias
LG SorgenKinde mech
Liebes SorkenKind mech,
was hindert Dich denn daran, bei jeder Textarea den Inhalt zu ermitteln und dann die Größenanpassung basierend auf dem value-Wert durchzuführen? Wenn das in Deiner Resize-Funktionalität jedes Mal berechnet wird, kannst Du auch bein Initialisieren (also alle textarea-Elemente einmal durch den Eventhandler jagen) diese Anpassung vornehmen. An molilys Beispiel angelehnt:
var resizeHandler = function (textarea) {
content = textarea.value;
// was auch immer nötig ist
};
var resizeTextareaOnKeyup = function (e) {
var o;
e = e || window.event;
o = o.target || o.srcElement;
// event von <textarea>?
if (o.nodeName === 'TEXTAREA') {
resizeHandler(o);
};
};
// init
for (var i in document.getElementsByTagName("textarea")) {
resizeHandler(i);
}
document.addEventListener('keyup', resizeTextareaOnKeyup, false);
Ich habe nix getestet, insbesondere die for-in-Schleife nicht.
Liebe Grüße,
Felix Riesterer.
Liebes SorkenKind mech,
huhu nochmal ;)
was hindert Dich denn daran, bei jeder Textarea den Inhalt zu ermitteln und dann die Größenanpassung basierend auf dem value-Wert durchzuführen?
ähm ... du überrumpelst mich gerade mit der frage, da dies eigentlich garnicht thematisiert ist, aber dennoch: wenn ich über den value gehe, dass müsste ich ja anzahl zeilenumbrüche, verwendete Schriftart, Zeilenhöhe etc berücksichtigen, daher gehe ich über scrollHeight - einfach, unkompliziert, funzt - zumindest in den Browsern, die ich unterstützen möchte
Wenn das in Deiner Resize-Funktionalität jedes Mal berechnet wird, kannst Du auch bein Initialisieren (also alle textarea-Elemente einmal durch den Eventhandler jagen) diese Anpassung vornehmen.
ich hatte eben gehofft das irgendwie vermeiden zu können, sprich eine schönere Lösung zu finden ...
und auch die "Lösung", die molilys vorschlug würde nicht alle Probleme erschlagen ... sicherlich würde zu anfang erstmal alle textarea-Elemente verarbeitet werden, auch nachträglich hinzugefügte Elemente würden berücksichtigt, jedoch nicht deren initialwert
sprich füge ich nachträglich ein textarea in das DOM ein, welches bereits einen großen text beinhaltet, würde dieses nicht resized werden, zumindest nicht bevor ich mit dem Textfeld irgendwas mache (drauf klicken, etc)
ich habe mich (für den Moment) für diese Lösung entschieden, auch wenn mich das Interval dabei stört:
var count_textarea_auto_height=0;
var textarea_resize = function(event) {
resize_textarea_fkt(event.target);
}
function resize_textarea_fkt(obj)
{
obj.style.overflow='scroll'; // chrome workaround
obj.style.height=obj.scrollHeight + 'px';
obj.style.overflow='hidden'; // chrome workaround
}
function textarea_auto_height()
{
objs=document.getElementsByTagName('textarea');
if(objs.length!=count_textarea_auto_height)
{
count_textarea_auto_height=objs.length;
for(i=0;i<objs.length;i++)
{
obj=objs[i];
if(!obj.getAttribute("tah"))
{
obj.addEventListener('keyup', textarea_resize, false);
resize_textarea_fkt(obj);
obj.setAttribute("tah",true);
}
}
}
}
window.setInterval("textarea_auto_height()",1000);
schöner habe ich es leider für den Moment noch nicht hinbekommen, um wirklich alle Bedürfnisse abzudecken ...
Liebe Grüße,
Felix Riesterer.
Hallo,
sprich füge ich nachträglich ein textarea in das DOM ein, welches bereits einen großen text beinhaltet, würde dieses nicht resized werden, zumindest nicht bevor ich mit dem Textfeld irgendwas mache (drauf klicken, etc)
Klar, so etwas ist höchstens mit einem MutationObserver möglich.
Wenn du eine textarea einfügst, dann solltest du an exakt dieser Codestelle die Höhenanpassung vornehmen. Es sei denn, es sind hundert Codestellen. Selbst dann solltest du dir eine DOM-Abstraktion bauen, die solche »Nachbehandlung« ermöglicht.
ich habe mich (für den Moment) für diese Lösung entschieden, auch wenn mich das Interval dabei stört
Zurecht. Das ist keine saubere Vorgehensweise. Das ist eher »von hinten durch die Brust ins Auge«. Es tauchen ja nicht plötzlich und willkürlich textareas im Dokument auf, sondern du hast es höchstwahrscheinlich unter Kontrolle.
Was den Codestil angeht: Setze dich einmal mit dem Unterschied zwischen <http://de.selfhtml.org/javascript/sprache/variablen.htm@title=globalen und lokalen Variablen> auseinander. Ferner kannst du setInterval direkt eine Funktion übergeben: setInterval(textarea_auto_height, 1000)
. Ein String geht auch, ist aber unnötig komplex, da der JavaScript-Parser angeworfen werden muss und eval() darauf ausgeführt wird.
Mathias
Hallo,
Klar, so etwas ist höchstens mit einem MutationObserver möglich.
das ist ja interessant ... schaue ich mir mal an, danke!
ich habe mich (für den Moment) für diese Lösung entschieden, auch wenn mich das Interval dabei stört
Zurecht. Das ist keine saubere Vorgehensweise. Das ist eher »von hinten durch die Brust ins Auge«. Es tauchen ja nicht plötzlich und willkürlich textareas im Dokument auf, sondern du hast es höchstwahrscheinlich unter Kontrolle.
ja deswegen gefällt mir das ja nicht ... das Problem ist nur, dass ein über 10 jahre altes projekt ist, was ständig weiter geschrieben worden ist - theoretisch müsste man es neu schreiben, aber dazu fehlt wie immer die zeit ;)
Was den Codestil angeht: Setze dich einmal mit dem Unterschied zwischen <http://de.selfhtml.org/javascript/sprache/variablen.htm@title=globalen und lokalen Variablen> auseinander.
öhm der untschied ist mir wohl bewusst, weiß leider aber in diesem zusammenhang nicht, woraus du hinaus willst ... ok die eine könnte ich auch lokal definieren, aber dann müsste sie ja bei jedem funktionsaufruf neu deklariert werden ... und die andere muss ja global sein, damit ich immer den vergleich zu vorher habe
Ferner kannst du setInterval direkt eine Funktion übergeben:
setInterval(textarea_auto_height, 1000)
. Ein String geht auch, ist aber unnötig komplex, da der JavaScript-Parser angeworfen werden muss und eval() darauf ausgeführt wird.
oki danke ;)
Mathias
LG dat SorkenKind mech
Liebes SorkenKind mech,
wenn Du dynamisch eine <textarea> hinzufügst, dann solltest Du das über eine spezialisierte selbstgeschriebene Funktion machen, keinesfalls einfach dort, wo nötig, ein document.createElement() und el.appendChild() aufrufen. In Deiner spezialisierten Funktion (nennen wir sie der Einfachheit halber "addTextArea") kannst Du dann die resize-Funktion direkt einbauen, damit sie "automatisch" bei der Erzeugung mit ausgeführt wird:
var addTextArea = function (parentNode, value) {
var ta;
if (parentNode) {
ta = document.createElement("textarea");
ta.value = value;
parentNode.appendChild(ta);
// resize after 100 ms
window.setTimeout(function () {
resizeHandler(ta);
}, 100);
}
};
Liebe Grüße,
Felix Riesterer.