2013-07-08 8 views
5

Ich versuche, die caret eines Textbereich zum Zwecke der Schaffung eines sehr leichten Rich-Textfeld zu imitieren. Ich möchte nicht so etwas wie Codemirror oder irgendeine andere massive Bibliothek verwenden, weil ich keines ihrer Features verwenden werde.Mimicng caret in ein Textfeld

Ich habe eine <pre> hinter einem Textarea mit einem transparenten Hintergrund positioniert, so dass ich einen Hervorhebungseffekt im Text simulieren kann. Ich möchte aber auch die Schriftfarbe ändern können (also nicht immer die gleiche). So versuchte ich color: transparent auf der Textarea, die mir erlaubt, den Text in irgendeiner Weise zu gestalten, die ich will, weil es nur auf dem <pre> Element hinter dem Textarea erscheint, aber das Caret verschwindet.

ich habe es bekommen ziemlich gut zu funktionieren, obwohl es nicht perfekt ist. Das Hauptproblem ist, dass, wenn Sie einen Schlüssel gedrückt halten und diesen Charakter spammen, der Caret immer um einen Charakter zurückbleibt. Nicht nur das, es scheint recht zu ressourcen schwer ..

Wenn Sie irgendwelche anderen Dinge im Code sehen, die verbessert werden müssen, fühlen Sie sich frei auf diesem zu kommentieren!

Hier ist eine Geige mit dem Code ist: http://jsfiddle.net/2t5pu/25/

Und für Sie, die nicht wollen aus irgendeinem Grund zu besuchen jsfiddle, hier ist der gesamte Code:

CSS:

textarea, #fake_area { 
    position: absolute; 
    margin: 0; 
    padding: 0; 
    height: 400px; 
    width: 600px; 
    font-size: 16px; 
    font: 16px "Courier New", Courier, monospace; 
    white-space: pre; 
    top: 0; 
    left: 0; 
    resize: none; 
    outline: 0; 
    border: 1px solid orange; 
    overflow: hidden; 
    word-break: break-word; 
    padding: 5px; 
    -webkit-box-sizing: border-box; 
    -moz-box-sizing: border-box; 
    -ms-box-sizing: border-box; 
    box-sizing: border-box; 
} 
#fake_area { 
    /* hide */ 
    opacity: 0; 
} 
#caret { 
    width: 1px; 
    height: 18px; 
    position: absolute; 
    background: #f00; 
    z-index: 100; 
} 

HTML :

<div id="fake_area"><span></span></div> 
<div id="caret"></div> 
<textarea id="textarea">test</textarea> 

JAVASCRIPT:

var fake_area = document.getElementById("fake_area").firstChild; 
var fake_caret = document.getElementById("caret"); 
var real_area = document.getElementById("textarea"); 

$("#textarea").on("input keydown keyup propertychange click", function() { 
    // Fill the clone with textarea content from start to the position of the caret. 
    // The replace /\n$/ is necessary to get position when cursor is at the beginning of empty new line. 
doStuff();  
}); 

var timeout; 
function doStuff() { 
    if(timeout) clearTimeout(timeout); 
    timeout=setTimeout(function() { 
     fake_area.innerHTML = real_area.value.substring(0, getCaretPosition(real_area)).replace(/\n$/, '\n\u0001'); 
    setCaretXY(fake_area, real_area, fake_caret, getPos("textarea")); 
    }, 10); 
} 


    function getCaretPosition(el) { 
     if (el.selectionStart) return el.selectionStart; 
     else if (document.selection) { 
      //el.focus(); 
      var r = document.selection.createRange(); 
      if (r == null) return 0; 

      var re = el.createTextRange(), rc = re.duplicate(); 
      re.moveToBookmark(r.getBookmark()); 
      rc.setEndPoint('EndToStart', re); 

      return rc.text.length; 
     } 
     return 0; 
    } 

    function setCaretXY(elem, real_element, caret, offset) { 
     var rects = elem.getClientRects(); 
     var lastRect = rects[rects.length - 1]; 

     var x = lastRect.left + lastRect.width - offset[0] + document.body.scrollLeft, 
      y = lastRect.top - real_element.scrollTop - offset[1] + document.body.scrollTop; 

     caret.style.cssText = "top: " + y + "px; left: " + x + "px"; 
     //console.log(x, y, offset); 
    } 

    function getPos(e) { 
     e = document.getElementById(e); 
     var x = 0; 
     var y = 0; 
     while (e.offsetParent !== null){ 
      x += e.offsetLeft; 
      y += e.offsetTop; 
      e = e.offsetParent; 
     } 
     return [x, y]; 
    } 

Vielen Dank im Voraus!

+0

Ihre caret hinkt aufgrund realen caret für ein wenig repiod Zeit apearing. Versuchen Sie, einige Zeilen Text einzugeben, und klicken Sie dann mit der linken Maustaste auf eine andere Zeile und halten Sie sie gedrückt, um sie zu sehen. Um dies zu überwinden, müssen Sie die Deckkraft des Textbereichs auf 0 setzen. Aber dann müssen Sie sich damit befassen, Text korrekt anzuzeigen. – twil

+0

müssen Sie den Cursor wirklich 100 Mal pro Sekunde aktualisieren? Ich würde denken, 5 oder 10 würde ausreichen. es ist, warum Ihre Anwendung langsam ... – dandavis

+0

@dandavis ausgeführt wird: muss man so reagieren, als Standard sein. –

Antwort

4

Kann ein editierbares Div-Element nicht das gesamte Problem lösen?

Code, der die Markierung tut:

http://jsfiddle.net/masbicudo/XYGgz/3/

var prevText = ""; 
var isHighlighting = false; 
$("#textarea").bind("paste drop keypress input textInput DOMNodeInserted", function (e){ 
    if (!isHighlighting) 
    { 
     var currentText = $(this).text(); 
     if (currentText != prevText) 
     { 
      doSave(); 
      isHighlighting = true; 
      $(this).html(currentText 
        .replace(/\bcolored\b/g, "<font color=\"red\">colored</font>") 
        .replace(/\bhighlighting\b/g, "<span style=\"background-color: yellow\">highlighting</span>")); 
      isHighlighting = false; 
      prevText = currentText; 
      doRestore(); 
     } 
    } 
}); 

Leider ist dies einige Bearbeitungsfunktionen vorgenommen werden verloren, wie Strg + Z ... und wenn Text einfügen, die caret Aufenthalte am Anfang des eingefügten Textes.

Ich habe Code aus anderen Antworten kombiniert, um diesen Code zu erzeugen, so wenden Sie sich bitte, geben ihnen Kredit.

EDIT: ich entdeckt habe, etwas interessantes ... die native caret erscheint, wenn Sie ein contentEditable Element verwenden, und in der es Sie ein anderes Element verwenden, um mit der unsichtbare Schrift:

<div id="textarea" contenteditable style="color: red"><div style="color: transparent; background-color: transparent;">This is some hidden text.</div></div> 

http://jsfiddle.net/masbicudo/qsRdg/4/

+0

Das gesamte Ziel war es mir zu erlauben, einfache Syntax-Hervorhebung zu tun. Ich versuchte es mit einem zufriedenen Div, aber der Caret würde herumspringen, zurückgesetzt werden usw. War einfach nicht zuverlässig. –

+0

@Lindrian am EDIT Werfen Sie einen Blick, können Sie es interessant finden ... leider die caret mit transparentem Text nur in Chrom und IE gearbeitet, nicht in Firefox. =) –

+0

Dies ist wahrscheinlich die hilfreichste Antwort bisher, danke! –

2

Die Verzögerung ist ich zum keyup aufgrund denken zu spät, die doStuff eine Bitansteuerung, aber die keydown ist eine zu früh Bit.

Versuchen Sie, diese anstelle des jQuery Ereignis hookup (normalerweise würde ich Ereignisse Polling bevorzugen, aber in diesem Fall könnte es ein besseres Gefühl geben) ...

setInterval(function() { doStuff(); }, 10); // 100 checks per second 

function doStuff() { 
    var newHTML = real_area.value.substring(0, getCaretPosition(real_area)).replace(/\n$/, '\n\u0001'); 
    if (fake_area.innerHTML != newHTML) { 
     fake_area.innerHTML = newHTML; 
     setCaretXY(fake_area, real_area, fake_caret,       getPos("textarea")); 
    } 
} 

... oder hier für die Geige: http://jsfiddle.net/2t5pu/27/

+0

Es tut mir leid, aber das ist nicht wirklich viel besser als das, was ich vorher hatte. –

2

dies scheint sehr gut zu funktionieren und verwendet keine Umfragen, wie nur etwa sprach ich in den Kommentaren.

var timer=0; 
$("#textarea").on("input keydown keyup propertychange click paste cut copy mousedown mouseup change", function() { 
    clearTimeout(timer); 
    timer=setTimeout(update, 10);  
}); 

http://jsfiddle.net/2t5pu/29/

vielleicht bin ich etwas fehlt, aber ich denke, das ist ziemlich solide ist, und es verhält sich besser als Intervalle mit Ihren eigenen Veranstaltungen zu erstellen.

EDIT: hat einen Timer que Stapel zu verhindern.

+0

Halten Sie eine Taste gedrückt, sie bleibt immer noch zurück. Ich denke nicht, dass meine Lösung es wert ist, weiter zu arbeiten. –

+0

Nun, es ist super glatt für mich. Ich habe die Antwort aktualisiert, um mit Lag auf älteren Maschinen umzugehen. wenn die Maschine wirklich alt ist, auch Textbereiche leicht hinter bekommen können ... – dandavis