ivorysmoker: JS - getRangeAt: DIV:Zeilen (Absätze) verbinden möglich?

Hallo zusammen

Ich bin momentan an ein Problem gestossen wo ich leider nicht weiter komme, daher frage ich einmal in die Runde für eine konfortable Lösung.

Erklärung:

Wichtig vorerst, wen beim DIV nur ein Zeile geschrieben wird funktioniert dieser Script tip top.
Macht man jetzt aber im Edierbaren Div ein Enter wird ja der Text auf eine neue Zeile getan, hier liegt das Problem.

Nehmen wir an im Div steht:

<div>
Hallo wie geht es?
Danke mir gehts super!
</div>

Wenn jetzt also [geht] makiert wird, würde die funktion getRangeAt(0) eine 10 Liefern.
Genau so sollte es auch sein!
Makiert man jetzt aber z.b [gehts] auf der 2ten Zeile gibt die funktion wieder 10 aus, da diese wieder von dem Anfang der Zeile anfängt zu zählen. Was natürlich falsch ist.

Nach meiner Vorstellung sollten die Zeilen im Script vereint werden, damit das Resultat bei [gehts] c.a 25 wäre. Bzw ist es möglich wen ein User mit Enter mehrer Zeilen macht diese als eine im Script anzusehen?

Ich bedanke mich schon einmal für eure Hilfe!

Gruss Ivorysmoker

  
//Als Bsp habe ich nicht alle replaces hier im Script aufgelistet, weil dies sonst eine unübersichtliche List wäre.  
  
//OriginalText = Liefert den Editerte Inhalt vom Div  
  
//Replaced alle Tags in wörter.  
var OriginalText = OriginalText.replace(/<br>/g, " replacerOnbr ");  
  
                var makiert = document.getSelection();  
  
 //Selektiert das makierte und liest die Position aus.  
                var range = document.getSelection().getRangeAt(0);  
 //Startet die Suche und gibt die Position in die Variable.  
                var ausgabe = range.startOffset;  
 //Teilt den String in 2 Teile  
                var spliterOriginal = OriginalText.substr(0, ausgabe);  
                var spliter = OriginalText.substr(ausgabe);  
 //Prüfen ob es makiert wurde (Dies sollte natürlich etwas weiter oben sein)  
                if(makiert == ""){  
 //Im Fehlerfall alert Box  
                    alert("Sie müssen zuerst einen Text Makieren");  
                    return;  
                }else{  
 //Ansonst.. schreibe die Tags um das Maskierte Wort.  
                var changer = regexfirst+makiert+regexnext;  
                var str = spliter;  
  
                var OriginalTextEnd = str.replace(makiert, changer);  
 //Replaced alle HTML Tags wieder in die entsprechenden Tags.  
                var spliterOriginal = spliterOriginal.replace(/replacerOnbr/g, "<br>");  
                var OriginalTextEnd = OriginalTextEnd.replace(/replacerOnbr/g, "<br>");  
//Schreibe in das DIV  
                document.getElementById("newest").innerHTML= spliterOriginal+OriginalTextEnd;  

  
<div id="newest" onClick="this.contentEditable='true'" style="float:left; display: block; border: 2px solid #333; white-space: pre-line; word-wrap: break-word; background-color: #F5F5F5;  min-height: 300px; width: 1000px;"><?php readfile("newtext.txt");?></div>  

  1. Hallo,

    Nehmen wir an im Div steht:

    <div>
    Hallo wie geht es?
    Danke mir gehts super!
    </div>

    Das steht ja aber nicht im DIV, wenn das contentEditable ist und Enter gedrückt wurde. Da sind ja dann <br> drin, was Du ja offensichtlich lt. Code auch weißt. Mit jedem <br> beginnt ein neuer startContainer.

    Siehe:
    https://developer.mozilla.org/en-US/docs/Web/API/Selection.getRangeAt
    https://developer.mozilla.org/en-US/docs/Web/API/range

    Du musst also mit diesen Containern arbeiten. Bsp.:

      
    <div id="newest" onClick="this.contentEditable='true'">  
    Hallo wie geht es?  
    Danke mir gehts super!  
    </div>  
      
    <input type="button" id="button">  
      
    <pre id="monitor"></pre>  
      
    <script>  
      
    [code lang=javascript]document.getElementById("button").onclick = function(e) {  
      
     document.getElementById("monitor").innerHTML = document.getElementById("newest").innerHTML.replace("<","&lt;");  
      
     var sel = window.getSelection();  
      
     var ersteSelRange = sel.getRangeAt(0);  
      
     if (!ersteSelRange.collapsed) {  
      
      var startContainer = ersteSelRange.startContainer;  
      var endContainer = ersteSelRange.endContainer;  
      
      if (startContainer == endContainer) {  
      
       var posStart = ersteSelRange.startOffset;  
       var posEnd = ersteSelRange.endOffset;  
      
       var startContainerText = startContainer.textContent;  
      
       var ersteSelRangeText = ersteSelRange.toString();  
      
       startContainer.textContent = startContainerText.substr(0, posStart) + "||" + ersteSelRangeText + "||" +  
      
    startContainerText.substr(posEnd);  
      
      }  
     }  
    };
    

    </script>
    [/code]

    Allerdings frage ich mich, was das werden soll. Was soll da bei Dir genau vor und nach der markierung eingefügt werden?

    viele Grüße

    Axel

    1. Hallo Alex,

      Vielen Dank ersteinmal für dein Code schnippsel, dies hat mir sehr weiter geholfen.

      Das steht ja aber nicht im DIV, wenn das contentEditable ist und Enter gedrückt wurde. Da sind ja dann <br> drin, was Du ja offensichtlich lt. Code auch weißt. Mit jedem <br> beginnt ein neuer startContainer.

      Ja darum habe ich alle <br> in zeichen umgewandelt damit eig nur 1 Container exisitiert, natürlich nur im Hintergrund. Der User bekommt von der sache nichts mit.

      Allerdings frage ich mich, was das werden soll. Was soll da bei Dir genau vor und nach der markierung eingefügt werden?
      viele Grüße
      Axel

      Ich bastle im moment einen Editor zusammen der mir bei meiner Arbeit etwas erleichern soll. Mein derzeiter Editor besteht mehr oder weniger aus PHP was natürlich einiges einschränkt. Ich wollte zudem eine Echtzeitvorschau bieten. Somit sieht man direkt wie gross der Text in wirklichkeit ist und nicht wie bis anhin nur Code ;)

      Eine kleine Frage hätte ich noch an dich:

      Vorweg dies beantwortet direkt deine Frage:

      Leider ist mir aufgefallen wen ich die Tags über diese Funktion integriere, gibt der mir im Div Container als bsp. <px7>Makiert</px7> aus.

      In meiner alten Funktion sind die Tag's in der anzeige Box direkt verschwunden und der Text wurde grösser.
      Die CSS-Style angaben sind natürlich gesetzt und funktioniert eig auch einwandfrei.

      Jetzt wird dies leider nicht mehr als Tag gesehen was mich schon etwas irritiert.
      Lediglich wurde ja das einfügen bei X Position verändert und ja nicht die RegEx ausgaben.

      Hättest du hierzu eine Idee?
      Liegt möglicherweise ein Formatierungsproblem vor?
      Werden Tags von der startContainer Funktion anders interpretiert?

      regexfirst + ersteSelRangeText + regexnext = <px7>MyText</px7> // Wird im Container direkt so ausgegeben. Korrekt solltes es ein kleines MyText sein.

      startContainer.textContent = startContainerText.substr(0, posStart) + regexfirst + ersteSelRangeText + regexnext +
      startContainerText.substr(posEnd);

      Ich bedanke mich schon zum voraus und wünsche ein schönes Weekend!

      1. Hallo,

        Ich bastle im moment einen Editor zusammen der mir bei meiner Arbeit etwas erleichern soll. Mein derzeiter Editor besteht mehr oder weniger aus PHP was natürlich einiges einschränkt. Ich wollte zudem eine Echtzeitvorschau bieten. Somit sieht man direkt wie gross der Text in wirklichkeit ist und nicht wie bis anhin nur Code ;)

        Ja, sowas dachte ich mir, deshalb die Frage. Das wird aber, so wie Du das machen willst, meiner Meinung nach nicht gut gelingen. Mit JavaScript Rich-Text Editing haben sich schon erfahrene Programmierer beschäftigt und dabei viele mögliche Probleme bereits erkannt und berücksichtigt. Das muss man nicht alles neu erfinden.

        Schau Dir mal folgende Lösungen an:
        http://jquery-plugins.net/tag/rich-text-editor

        Leider ist mir aufgefallen wen ich die Tags über diese Funktion integriere, gibt der mir im Div Container als bsp. <px7>Makiert</px7> aus.

        Ja, es wird ja auch textContent erzeugt, keine Elemente.

        In meiner alten Funktion sind die Tag's in der anzeige Box direkt verschwunden und der Text wurde grösser.
        Die CSS-Style angaben sind natürlich gesetzt und funktioniert eig auch einwandfrei.

        Ja, weil Du dort das innerHTML neu aufgebaut hattest. Wobei es in HTML aber kein PX7-Element gibt. Ganz sauber ist das also nicht und damit ist nicht sichergestellt, dass es immer funktioniert. Und an das innerHTML des Range.startContainer kommt man nicht direkt ran.

        Hättest du hierzu eine Idee?

        Nein, keine umfassende und praktisch einsetzbare. Da haben sich, wie gesagt, schon erfahrenere Programmierer intensiv mit beschäftigt und praxistaugliche Lösungen gefunden, die man nur nutzen muss.

        Man kann natürlich PX7-Elemente erzeugen und einhängen. Das geht sogar relativ simpel. Die Links zur MDN-Doku hatte ich ja bereits gepostet.

          
        <div id="newest" contenteditable="true">  
        Hallo wie geht es?  
        Danke mir gehts super!  
        </div>  
          
        <input type="button" id="button" value="do">  
          
        <pre id="monitor"></pre>  
          
        <script>  
          
        [code lang=javascript]document.getElementById("button").onclick = function(e) {  
          
         var sel = window.getSelection();  
          
         var ersteSelRange = sel.getRangeAt(0);  
          
         var XMLElementPX7 = document.createElement("px7");  
          
         try {  
          ersteSelRange.surroundContents(XMLElementPX7);  
         } catch(error) {  
          alert(error);  
         }  
          
         var newestTextContent = document.createTextNode(document.getElementById("newest").innerHTML);  
         document.getElementById("monitor").innerHTML = "";  
         document.getElementById("monitor").appendChild(newestTextContent);  
          
        };  
        
        

        </script>
        [/code]
        aber das geht nur solange gut, bis man die Verschachtelungsregeln verletzt. Das müsste man dann programmtechnisch abfangen. usw. usw. bis man dann irgendwann einen kompletten Editor neu progammiert hat, den es sogar *besser* schon lange gibt.

        viele Grüße

        Axel

        1. Hallo Axel,

          Ich sehe das eher als Javascript lern Phase, es muss nicht perfekt kommen ;)

          Ja, sowas dachte ich mir, deshalb die Frage. Das wird aber, so wie Du das machen willst, meiner Meinung nach nicht gut gelingen. Mit JavaScript Rich-Text Editing haben sich schon erfahrene Programmierer beschäftigt und dabei viele mögliche Probleme bereits erkannt und berücksichtigt. Das muss man nicht alles neu erfinden.

          Ich erfinde gerne alles neu ;) Ich habe es so am liebsten so wie ich es mir vorstelle ;)

          Ich konnte das Problem ganz einfach und Simpel lösen :) Es wird wie du sagtest kein Element erzeugt sondern ein Asci-Code. Was wiederum als Text dargestellt wird.

          Falls dich die Lösung noch intressiert:
          Da PHP den Teil mit dem Speichern löst, habe ich alle Zeichen decodiert. Und voila es leuft ;)
          Es funktioniert leider noch nicht perfekt aber zumindest so wie gedacht ;)

          Ja, es wird ja auch textContent erzeugt, keine Elemente.

          Ja, weil Du dort das innerHTML neu aufgebaut hattest. Wobei es in HTML aber kein PX7-Element gibt. Ganz sauber ist das also nicht und damit ist nicht sichergestellt, dass es immer funktioniert. Und an das innerHTML des Range.startContainer kommt man nicht direkt ran.

          Hättest du hierzu eine Idee?
          Nein, keine umfassende und praktisch einsetzbare. Da haben sich, wie gesagt, schon erfahrenere Programmierer intensiv mit beschäftigt und praxistaugliche Lösungen gefunden, die man nur nutzen muss.

          Vielen Dank für den Code-Schnippsel. Ich bin froh konnte ich dieses Problem ohne JS lösen ;)
          Das mit den Tag-Elementen ist eine hübsche Idee, nur wenn ich das so machen müsste dan wäre ich ewig am Programmieren.

          Code entfernt!

          aber das geht nur solange gut, bis man die Verschachtelungsregeln verletzt. Das müsste man dann programmtechnisch abfangen. usw. usw. bis man dann irgendwann einen kompletten Editor neu progammiert hat, den es sogar *besser* schon lange gibt.
          viele Grüße

          Axel

          Ich bedanke mich herzlich für deine Unterstüzung!
          Wünsche ein schönes Weekend!

          Lg Ivory

        2. Hallo Axel,

          Leider bin ich noch auf ein Problem gestossen und wollte einmal bei dir nachfragen ob du dazu eine Lösung hättest oder einen Anreiz.

          Nehmen wir wieder ein einfaches Beispiel zur erklärung:

          <div>
          Das ist die Zeile 1
          Das ist die Zeile 2
          Das ist die Zeile 3
          </div>

          Also der StartContainer liest ja Zeilenweise,
          makiert man jetzt folgende Zeile:

          Das ist die Zeile1

          Gibt die erste Position 0 aus und die End Pos 16. Soweit sogut!

          Makiert man jetzt aber 2 Zeilen auf einmal, hat die Funktion ein Problem.
          Ich denke das ist weil man ja Zeilenweise ausliesst.

          Meine Idee zur Problembehebung:

          Als Beispiel wäre Zeile 1 und 2 makiert.

          Das ist die Zeile 1
          Das ist die Zeile 2

          Jetzt fängt der Code ja bei Zeile1 an zu suchen, hier sollte bereits der Startparameter 0 übergeben werden, springt der Code jetzt aber zu Zeile2 werden die StartParameter und EndParameter nicht gefunden. Da es ja bei einer neuen Zeile wieder von 0 anfängt...

          Das Resultat der Parameter sollte bei diesem Bsp. Start: 0 End: 32 c.a ausgeben.

          Hat man hier die möglichkeit auszulesen in welcher Zeile sich das makierte befindet und wie viele Zeilen schon übersprungen wurden sowie wie viele Zeichen es waren.
          Dann könnte man diesen Fehler beheben.

          Zeilenweise ersetzen funktioniert einwandfrei ohne Probleme! Auch bei doppelten wörter etc...

          Ich bedanke mich schon zum voraus für deine Bemühungen und wünsche einen angenehmen Nachmittag.

            
          var sel = window.getSelection();  
                          if(sel == ""){  
                              alert("Makieren Sie den gewünschten Text");  
                              return;  
                          }  
                          var ersteSelRange = sel.getRangeAt(0);  
            
                          if (!ersteSelRange.collapsed) {  
            
                              var startContainer = ersteSelRange.startContainer;  
                              var endContainer = ersteSelRange.endContainer;  
                          if (startContainer == endContainer) {  
            
                              var posStart = ersteSelRange.startOffset;  
                              var posEnd = ersteSelRange.endOffset;  
                               alert('StartPosition'+posStart);  
                               alert('StartPosition'+posEnd);  
                              var startContainerText = startContainer.textContent;  
            
                              var ersteSelRangeText = ersteSelRange.toString();  
                              //alert('RegEx Ausgabe: 'regexfirst+regexnext);  
                              startContainer.textContent = startContainerText.substr(0, posStart) + regexfirst + ersteSelRangeText + regexnext +  
                              startContainerText.substr(posEnd);  
            }  
           }  
          
          
          1. Das Problem wurde behoben! Endschuldige da war ein denk Fehler von mir.

            <br> Fail ;)

            Hallo Axel,

            Leider bin ich noch auf ein Problem gestossen und wollte einmal bei dir nachfragen ob du dazu eine Lösung hättest oder einen Anreiz.

            Nehmen wir wieder ein einfaches Beispiel zur erklärung:

            <div>
            Das ist die Zeile 1
            Das ist die Zeile 2
            Das ist die Zeile 3
            </div>

            Also der StartContainer liest ja Zeilenweise,
            makiert man jetzt folgende Zeile:

            Das ist die Zeile1

            Gibt die erste Position 0 aus und die End Pos 16. Soweit sogut!

            Makiert man jetzt aber 2 Zeilen auf einmal, hat die Funktion ein Problem.
            Ich denke das ist weil man ja Zeilenweise ausliesst.

            Meine Idee zur Problembehebung:

            Als Beispiel wäre Zeile 1 und 2 makiert.

            Das ist die Zeile 1
            Das ist die Zeile 2

            Jetzt fängt der Code ja bei Zeile1 an zu suchen, hier sollte bereits der Startparameter 0 übergeben werden, springt der Code jetzt aber zu Zeile2 werden die StartParameter und EndParameter nicht gefunden. Da es ja bei einer neuen Zeile wieder von 0 anfängt...

            Das Resultat der Parameter sollte bei diesem Bsp. Start: 0 End: 32 c.a ausgeben.

            Hat man hier die möglichkeit auszulesen in welcher Zeile sich das makierte befindet und wie viele Zeilen schon übersprungen wurden sowie wie viele Zeichen es waren.
            Dann könnte man diesen Fehler beheben.

            Zeilenweise ersetzen funktioniert einwandfrei ohne Probleme! Auch bei doppelten wörter etc...

            Ich bedanke mich schon zum voraus für deine Bemühungen und wünsche einen angenehmen Nachmittag.

            var sel = window.getSelection();
                            if(sel == ""){
                                alert("Makieren Sie den gewünschten Text");
                                return;
                            }
                            var ersteSelRange = sel.getRangeAt(0);

            if (!ersteSelRange.collapsed) {

            var startContainer = ersteSelRange.startContainer;
                                var endContainer = ersteSelRange.endContainer;
                            if (startContainer == endContainer) {

            var posStart = ersteSelRange.startOffset;
                                var posEnd = ersteSelRange.endOffset;
                                 alert('StartPosition'+posStart);
                                 alert('StartPosition'+posEnd);
                                var startContainerText = startContainer.textContent;

            var ersteSelRangeText = ersteSelRange.toString();
                                //alert('RegEx Ausgabe: 'regexfirst+regexnext);
                                startContainer.textContent = startContainerText.substr(0, posStart) + regexfirst + ersteSelRangeText + regexnext +
                                startContainerText.substr(posEnd);
              }
            }

            
            >   
            >   
            >   
            >   
            >