DesignMode und Tastenabfrage im iFrame-body-Element
Felix Riesterer
- javascript
Liebe Forumsgemeinde,
ich möchte in einem auf designMode="on" geschalteten iFrame bei jedem Tastendruck eine Funktion ausführen lassen. Dazu dachte ich den Eventhandler onkeyup zu verwenden.
iFrame.contentWindow.document.body.onkeyup = meineFunktion;
Beim Tippen innerhalb des iFrames wird diese Funktion jedoch nicht ausgelöst... Auch ein onclick auf dieselbe Art und Weise bringt keinen Funktionsaufruf:
iFrame.contentWindow.document.body.onclick = meineFunktion;
Wer weiß Rat?
Liebe Grüße aus Ellwangen,
Felix Riesterer.
Hallo Felix,
iFrame.contentWindow.document.body.onclick = meineFunktion;
Für den IE 6 klappt es bei mir schonmal so:
document.getElementById('ziel').document.designMode="on";
document.getElementById('ziel').contentWindow.document.body.onclick=function(){alert(9);};
Grüsse
Cyx23
Lieber Cyx23,
document.getElementById('ziel').document.designMode="on";
document.getElementById('ziel').contentWindow.document.body.onclick=function(){alert(9);};
soweit war ich auch schon... aber im FF?
So langsam kommen mir Zweifel, ob das mit dem DesignMode so eine gute Idee war. Wie realisiere ich denn das Updaten meines ge-highlighteten Codes, ohne dass der Cursor entfernt bzw. an eine andere Position verändert wird?
Und wie verhindere ich Performanceeinbußen bei langen CSS-Dateien?
Momentan habe ich meinen CSS Syntax Highlighting Editor in Javascript mal hochgeladen. Wenn Du (oder jemand anderes) gute Vorschläge hat, ich bin immer dankbar dafür!
Liebe Grüße aus Ellwangen,
Felix Riesterer.
Hallo Felix,
document.getElementById('ziel').document.designMode="on";
document.getElementById('ziel').contentWindow.document.body.onclick=function(){alert(9);};
soweit war ich auch schon... aber im FF?
document.getElementById('ziel').contentWindow.document.body.setAttribute("onClick","alert(8)");
Entspr. DOM sollte es wohl noch mehr Möglichkeiten geben irgendwelche
Knoten abzufragen oder einzufügen usw..
Grüsse
Cyx23
Lieber Cyx23,
document.getElementById('ziel').contentWindow.document.body.setAttribute("onClick","alert(8)");
Entspr. DOM sollte es wohl noch mehr Möglichkeiten geben irgendwelche
Knoten abzufragen oder einzufügen usw..
ich habe nun einen extrem hässliche Würgaround gebastelt.
Meiner Beobachtung nach reagiert mein FF nur, wenn ich diese Eventhandler dynamsich per addEventListener vergebe. Bei dynamisch in Elemente des iFrames gesetzten on-xyzs feuert der nicht.
Also habe ich - wieder mal browserspezifisch - mit addEventListener (mozzy) und attachEvent (IE) die Eventhandler dynamisch vergeben. Jetzt macht der IE aber Probleme, wenn das Event den HTML-Code des aktuell angeklickten Elements ausgeben soll: alert(this.innerHTML)
Der IE sendet ein fieses "undefined", während FF brav den Code rausrückt.
Mein Würgaround sieht jetzt so aus:
function addEvent (event, handler, obj, browser) {
if(browser == "mozzy") obj.addEventListener(event, handler, false);
if(browser == "IE") obj["on" + event] = handler;
}
Liebe Grüße aus Ellwangen,
Felix Riesterer.
Mein Würgaround sieht jetzt so aus:
function addEvent (event, handler, obj, browser) {
if(browser == "mozzy") obj.addEventListener(event, handler, false);
if(browser == "IE") obj["on" + event] = handler;
}
Bleibt nur die Fage, warum beide Browser nicht reagieren, wenn man "onkeyup" für die <p>-Elemente verteilt... Bei "onkeyup" für das <body>-Element reagiert wenigstens noch der IE...
HILFE!
Liebe Grüße aus [Ellwangen](http://www.ellwangen.de/),
Felix Riesterer.
Hallo,
HILFE!
Es gibt doch bestimmt dazu was im Archiv oder sonst bei Google, etwa
cms-editor oder online-editor etc.?
An meine CMS-Geschichte komm ich gerade nicht gut heran, da müßte
auch was dabei sein, allerdings hab ich das wohl schwerpunktmäßig
für den IE gemacht.
Oder vielleicht findest du hier bei meinen Tests noch was:
<script type="text/javascript">
function melde(xxx){
alert(xxx);
}
function schreib(){
document.getElementById('ziel').contentWindow.document.body.innerHTML='<div id="zt">test</div>'
document.getElementById('ziel').contentWindow.document.getElementById('zt').setAttribute("onClick","top.melde(this.innerHTML)");
document.getElementById('ziel').contentWindow.document.getElementById('zt').setAttribute("onmouseup","top.melde(this.innerHTML)");
if(document.all)document.getElementById('ziel').contentWindow.document.getElementById('zt').onmousedown=function(){
xxx=this.innerHTML;
this.innerHTML="<b>"+xxx+"</b>";
};
if(document.all)document.getElementById('ziel').contentWindow.document.getElementById('zt').onmouseup=function(){
xxx=this.innerHTML;
setTimeout("parent.melde(xxx)",9);
};
}
</script>
<iframe id=ziel src="check.htm"></iframe>
<a href=# onclick="schreib()">xxx</a>
Grüsse
Cyx23
Wieso willst du den onclick Event auf document.body legen und nicht auf window.document?
<script type="text/javascript">
function schreib()
{
var f = window.frames['ziel'];
f.document.onclick = function()
{
alert(this.body.innerHTML);
}
}
</script>
<iframe name="ziel" src="f1.html"></iframe>
<a href="#" onclick="schreib()">xxx</a>
Struppi.
Lieber Struppi,
Wieso willst du den onclick Event auf document.body legen und nicht auf window.document?
vielen Dank für den Denkanstoß. Es hatte bisher den Anschein, dass es bei einem iFrame anders funktioniert, als bei einem regulären Frameset...
Damit werde ich mal meine Herangehensweise überdenken.
Mir macht das event-Objekt größte Probleme. Nicht nur, dass es in den verschiedenen Browsern höchst unterschiedlich umgesetzt ist, es reagieren die Browser auch z.T. nicht auf die Events oder die notierten Eventhandler. Bei "click" mag ja manches noch wie erwartet reagieren, aber bei "keyup" bzw. "keydown" reagiert Mozilla im DesignMode nicht mehr.
Ich drehe bald hohl hier! *grrrrr*
Liebe Grüße aus Ellwangen,
Felix Riesterer.
Lieber Struppi,
dein Vorschlag klappt bei mir nicht, wenn es darum geht, das Laden des iFrames abzuwarten. Ich habe folgendes Probiert, wobei der FF das Event "load" feuert, der IE aber nicht:
<html>
<head>
<title>Test</title>
<script type="text/javascript">
function init () {
var iFrame = document.createElement("iframe");
iFrame.src = "code_template.html";
iFrame.id = "iFrame";
iFrame.name = "iFrame";
document.body.appendChild(iFrame);
iFrame.onload = function() { alert("Hi!"); };
window.frames["iFrame"].onload = function() { alert("Hi!"); };
}
window.onload = init;
</script>
</head>
<body>
<h1>Test</h1>
<p>testing...</p>
</body>
</html>
Wenn ich diese Hürde nicht nehmen kann, dann scheitert mein ganzes Projekt! Irgendwie muss es aber doch gehen!!!
Liebe Grüße aus Ellwangen,
Felix Riesterer.
dein Vorschlag klappt bei mir nicht, wenn es darum geht, das Laden des iFrames abzuwarten. Ich habe folgendes Probiert, wobei der FF das Event "load" feuert, der IE aber nicht:
Ich hatte auch schon bemerkt, dass mein Vorschlag nicht wirklich was bringt. Ich werd nachher das ganze mal zuhause angucken. Ich sitze hier mal wieder vorm IE 4
Wenn ich diese Hürde nicht nehmen kann, dann scheitert mein ganzes Projekt! Irgendwie muss es aber doch gehen!!!
Denke ich auch.
Struppi.
Hallo Cyx23, hallo Felix,
document.getElementById('ziel').document.designMode="on";
document.getElementById('ziel').contentWindow.document.body.onclick=function(){alert(9);};
Das ist seltsam, denn hier steht:
http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/designmode.asp?frame=true
"You cannot execute script when the value of the designMode property is set to On."
Ich werde aber noch nicht so ganz schlau, ob sich das bei contenteditable=true ähnlich verhält.
http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/contenteditable.asp?frame=true
Gruß Gernot
Wer weiß Rat?
Ich hab jetzt mal ein bisschen rumprobiert und geschaut wie es htmlarea macht. Dort wird zwar an sich ein anderer Weg gegangen, dort wird der HTML Code mit document.write() geschrieben, hat aber mir den richtigen Weg gezeigt.
Du musst die Funktion copyCode so anpassen:
copyCode : function (toggleMode) {
var codeSource = document.getElementById(HighLighter.settings.formElement);
var iFrame = document.getElementById(HighLighter.settings.prefix + "codePreview");
var content, HTML;
if (toggleMode) {
// copy code from textarea to iframe
code = codeSource.value;
// highlight code
code = HighLighter.functions.highlight(code);
iFrame.contentWindow.document.body.innerHTML = code;
window.setTimeout(
function() {
var doc = iFrame.contentWindow.document;
if(doc.addEventListener)
{
doc.addEventListener('keydown', HighLighter.functions.update, true);
}
else if(doc.attachEvent)
{
doc.attachEvent("onkeydown" , HighLighter.functions.update);
}
}
, 50
);
} else {
// copy code from iframe to textarea
codeSource.value = HighLighter.functions.unlight(iFrame.contentWindow.document.body.innerHTML);
}
return;
},
HTMLarea verwendet dafür noch eine Funtion um auch andere Event zu registrieren.
// event handling
HTMLArea._addEvent = function(el, evname, func) {
if (HTMLArea.is_ie) {
el.attachEvent("on" + evname, func);
} else {
el.addEventListener(evname, func, true);
}
};
HTMLArea._addEvents = function(el, evs, func) {
for (var i = evs.length; --i >= 0;) {
HTMLArea._addEvent(el, evs[i], func);
}
};
Damit können mehrere Events abgefangen werden. Ich würd mir HTMLArea mal anschauen, da es von denen WYSIWYG Editoren die ich bisher analysiert habe den übersichtlichsten Code hat.
Struppi.
Lieber Struppi,
Ich hab jetzt mal ein bisschen rumprobiert und geschaut wie es htmlarea macht.
vielen Dank für Deine umfangreichen Bemühungen!
Du musst die Funktion copyCode so anpassen:
Inzwischen bin ich schon weiter. copyCode() hat jetzt einen anderen Stellenwert bekommen, da ich ja das Laden des iFrames abwarten muss, bevor ich auf Komponenten darin zugreife.
Deswegen habe ich eine Funktion preparePreview erstellt, die per onload Event ausgeführt werden soll (der sch*** IE ruft die aber nicht auf!!!), um z.B. die Stylesheets in den <head> zu schreiben. Übrigens versagt auch hier der IE auf ganzer Linie, indem er das <head>-Element erst garnicht findet: iFrameHead = iFrame.document.getElementsByTagName("head");
ergibt eine HTMLCollection der Länge 0!
HTMLarea verwendet dafür noch eine Funtion um auch andere Event zu registrieren.
Das habe ich prompt integriert!
Neuer Versuch ist hochgeladen:
http://test.felix-riesterer.de/css_syntax_hilite/index.html (Seite)
http://test.felix-riesterer.de/css_syntax_hilite/syntaxhighlighter.js (Javascript)
Liebe Grüße aus Ellwangen,
Felix Riesterer.
Deswegen habe ich eine Funktion preparePreview erstellt, die per onload Event ausgeführt werden soll (der sch*** IE ruft die aber nicht auf!!!), um z.B. die Stylesheets in den <head> zu schreiben. Übrigens versagt auch hier der IE auf ganzer Linie, indem er das <head>-Element erst garnicht findet:
iFrameHead = iFrame.document.getElementsByTagName("head");
ergibt eine HTMLCollection der Länge 0!
Das mit dem onload ist klar.
Ein iFrame ist ein window, es feuert ein onload wenn die Seite komplett geladen wurde. Das Problem ist, dass du auf das Fenster zwar zugreifen kannst, aber entweder ihm den onload Event zuweisen kannst oder eine neue Seite, beides sollte nicht gehen.
D.h. du hast nur zwei sinnvolle Möglichkeiten, entweder in der Quellseite den onload Event zu feuern oder mit timeouts zu arbeiten.
Oder, was ich für sinnvoller halte, keine neue HTML Seite zu laden, sondern die Seite einfach mit JS aufzubauen, dann kannst du auch leichter deine CSS Datei integrieren.
Struppi.
Lieber Struppi,
D.h. du hast nur zwei sinnvolle Möglichkeiten, entweder in der Quellseite den onload Event zu feuern oder mit timeouts zu arbeiten.
Oder, was ich für sinnvoller halte, keine neue HTML Seite zu laden, sondern die Seite einfach mit JS aufzubauen, dann kannst du auch leichter deine CSS Datei integrieren.
DAS werde ich tun! Auf diese Idee kam ich noch nicht. Wie genau müsste ich das dann tun?
var iFrame = document.createElement("iframe");
// iFrame.src = Highlighter.settings.template; weglassen?
iFrame.id = Highlighter.settings.prefix + "codePreview";
iFrame.name = Highlighter.settings.prefix + "codePreview";
iFrame.style.width = "100%";
iFrame.style.height = "250px";
iFrame.style.display = "none";
// neues Dokument erzeugen
var newDoc = document.createElement("html");
var newHead = document.createElement("head");
var newBody = document.createElement("body");
var newLink = document.createElement("link");
var newP = document.createElement("p");
newHead.appendChild(newLink);
newDoc.appendChild(newHead);
newDoc.appendChild(newBody);
iFrame.contentWindow.document.appendChild(newDoc);
codeSource.parentNode.insertBefore(iFrame, codeSource.nextSibling);
Sitze gerade in einer Fortbildung... Kann es jetzt also nicht ausprobieren, aber vielleicht kennst Du das schon und weißt, ob es funzt(tm)?
Liebe Grüße aus Ellwangen,
Felix Riesterer.
// neues Dokument erzeugen
var newDoc = document.createElement("html");
var newHead = document.createElement("head");
var newBody = document.createElement("body");
var newLink = document.createElement("link");
var newP = document.createElement("p");newHead.appendChild(newLink);
newDoc.appendChild(newHead);
newDoc.appendChild(newBody);
iFrame.contentWindow.document.appendChild(newDoc);
codeSource.parentNode.insertBefore(iFrame, codeSource.nextSibling);
So in etwa, vielleicht noch die richtige Schachtelung beachten.
Oder, wie es auch HTMLArea macht, mit document.write()
Sitze gerade in einer Fortbildung... Kann es jetzt also nicht ausprobieren, aber vielleicht kennst Du das schon und weißt, ob es funzt(tm)?
Nö, ich muss auch gleich weg.
Struppi.
Lieber Struppi,
alles, was mir jetzt noch fehlt, ist eine Möglichkeit, die aktuelle Cursorposition über das Code-Update zu "retten". Gerade hier beisst es bei mir gehörig aus. Ich experimentiere gerade mit document.getSelection und document.createRange herum, verstehe aber das ganze Konzept dahinter nicht!
Und dann unterscheiden sich die beiden Browser (IE vs. Mozilla(FF)) gerade in dieser Hinsicht besonders unangenehm... Mir raucht schon ganz gewaltig der Kopf! Dabei wäre ich doch dann mit meinem Projekt fertig und könnte es als alpha-Stadium bezeichnen! Performance und Kompatibilität wären dann der wahrscheinlich noch üblere nächste Schritt.
Ich habe es mal zum Testen mit Einfüge-Funktionen versehen, damit man nicht nur eine leere Textarea zum bestaunen hat. Meine neueste Version hat noch zwei gravierende Nachteile: 1. Firefox verweigert den lokalen Betrieb von der Festplatte, 2. Man muss mit der Maus bei jedem Tastendruck den Cursor zurückholen.
Liebe Grüße aus Ellwangen,
Felix Riesterer.
Lieber Struppi,
Es ist mir nun gelungen, sowohl im IE, als auch im Mozilla den iFrame zu erzeugen, als auch selbigem auf das document-Objekt einen feuernden onkeyup zu verpassen. Nun kommt das nächste Problem: *g*
Die durch das 'keyup'-Event aufgerufene Funktion bekommt als Auslöser-Element im Mozilla das <html>-Element, im IE das <body>-Element übermittelt... Naja, schöner wäre es gewesen, das aktuell bearbeitete Element übertragen zu bekommen. Diese Funktion reduziert nun den gesamten Code auf eben jenen (ohne Highlighting), um anschließend das Highlighting darauf wieder anzuwenden. Und was kommt jetzt?
Mein erster Ansatz war, den neu gehighlighteten Code in den iFrame zurückzuschreiben. Dadurch wird aber der Cursor entfernt, sodass ein sinnvolles Arbeiten daran nicht möglich ist! Wie vermeide ich das?
Hättest Du (oder jede(r) andere Leser(in) dieses Forums) eine Idee, wie man diese Aufgabe lösen könnte?
Ich habe mein Projekt auf meinem Webspace an eine neue Stelle verschoben.
Liebe Grüße aus Ellwangen,
Felix Riesterer.
Lieber Struppi,
bei dem Versuch die Cursorposition innerhalb des auf designMode geschaltenen iFrames zu behalten, verzweifle ich fast an dem Konzept, das die Browser-Hersteller mit ihrem SELECTION und RANGE Objekten entwickelt haben.
Es will mir - zunächst versuche ich mich nur am Mozilla - nicht gelingen, die aktuelle selection in einer Variablen zu "konservieren", bzw. die Referenzen auf die in ihr gespeicherten Elemente. Meine Debug-Ausgaben servieren mir jedesmal die Tatsache, dass in meinem Objekt der Wert "undefined" abgelegt wurde, während eine Abfrage der entsprechenden Eigenschaft des selection-Objektes brav die echten Werte rausrückt.
Ich schnalle es einfach nicht. Ich habe mir angesehen, wie HTMLArea oder der TinyMCE das machen, aber die fügen immer auch etwas ein, was ich ja nicht tue. Trotzdem kann ich deren Ansätze nicht umsetzen. Es ist zum Heulen!!
Die zentrale Stelle ist die Funktion handleCursor:
handleCursor : function (mode) {
// "save" or "restore" cursor position
var iFrame = window.frames[Highlighter.settings.prefix + "codePreview"];
if (Highlighter.controls.browser == "gecko") {
var sel = iFrame.getSelection();
var range = sel.getRangeAt(0);
switch (mode) {
case "save":
Highlighter.controls.selection.focusNode = sel.focusNode;
Highlighter.controls.selection.focusOffset = sel.focusOffset;
alert("speichere: " + Highlighter.controls.selection.focusNode + " " + Highlighter.controls.selection.focusOffset);
break;
case "restore":
alert("lese: " + Highlighter.controls.selection.focusNode +" " + Highlighter.controls.selection.focusOffset);
// sel.focusNode = Highlighter.controls.selection.focusNode;
// sel.focusOffset = Highlighter.controls.selection.focusOffset;
break;
}
}
if (Highlighter.controls.browser == "IE") {
var range = iFrame.document.selection.createRange();
switch (mode) {
case "save":
Highlighter.controls.selection = range.duplicate();
break;
case "restore":
range = Highlighter.controls.selection;
break;
}
}
}
Neuester Versuch:
http://test.felix-riesterer.de/css_syntax_hilite/index.html (Seite)
http://test.felix-riesterer.de/css_syntax_hilite/syntaxhighlighter.js (Javascript)
Liebe Grüße aus Ellwangen,
Felix Riesterer.
Lieber Struppi, liebe Forumsgemeinde,
es scheint, dass mein Ansatz mit dem Erhalten der Cursorposition von vorneherein zum Scheitern verurteilt ist, denn ich möchte ja zuerst die Cursorposition "speichern", danach das komplette body.innerHTML der iFrames ersetzen und anschließend die "gespeicherte" Cursorposition zurück schreiben.
Spätestens beim genaueren Studieren von </archiv/2003/7/t53330/#m295347> muss ich aber erkennen, dass die "Cursorposition" eine Textauswahl mit identischem Start- und Endepunkt ist, die sich zwar über das selection-Objekt (Syntax und Implementation ist je nach Browser etwas anders, aber im Prinzip derselbe Mechanismus) erhalten und steuern lässt, die aber an die im HTML-Code hinter dem sichtbaren Text gebundenen Elemente gebunden ist. Ersetze ich nun einfach body.innerHTML, so zerstöre ich sämtliche Referenzen zu den Elementen, auf die sich das selection-Element (und seine Range(s)) eben stützt. Daher kann ich keine Cursorposition "speichern", um sie nach dem Codeupdate wieder anzuwenden.
Das hat mich jetzt drei Tage gekostet das herauszufinden... uff!
Es bleibt mir jetzt nichts anderes übrig, als in den DOM-Baum hinauf (hinab?) zu steigen, um das Element, in dem sich der Cursor befindet, DOM-entsprechend zu ändern (falls nötig), um dann eine neue Cursorposition aufgrund der Kenntnis über das betroffene Element (und seine DOM-Umgebung) zu generieren und anzuwenden. - Mahlzeit!
Damit muss ich auch gründlich meine Code-Update-Funktionalität überdenken... Oh Mann! Das wird ja alles immer komplizierter!!
Freue mich über Anregungen und Hilfestellungen beim Umgang mit selection und range (den Tipps und Tricks-Artikel kenne ich schon)!
Liebe Grüße aus Ellwangen,
Felix Riesterer.
Lieber Struppi, liebe Forumsgemeinde,
Ich hab's natürlich gelesen, leider hab ich momentan nicht sovíel Zeit mir alles genau anzuschauen.
es scheint, dass mein Ansatz mit dem Erhalten der Cursorposition von vorneherein zum Scheitern verurteilt ist, denn ich möchte ja zuerst die Cursorposition "speichern", danach das komplette body.innerHTML der iFrames ersetzen und anschließend die "gespeicherte" Cursorposition zurück schreiben.
Ich glaub auch das du so nicht weiter kommst. Wenn ich dich richtig verstanden habe, willst du die Cursorposition nach dem Formatieren an der gleichen Stelle haben wie vorher, das wird nicht gehen, du kannst die Position nicht setzen. Du kannst lediglich an der aktuellen Position etwas einsetzen, aber nicht in einem komplett neuen Inputfeld oder in deinem Fall editierbaren Frame.
Ich kann mich irren, aber es ist ähnlich wie die Mausposition, die du mit JS auch nur lesen kannst.
Struppi.