2013-07-08 23 views
5

Próbuję naśladować opiekunkę textarea w celu stworzenia bardzo lekkiego, bogatego textarea. Nie chcę używać czegoś takiego jak codemirror lub jakiejkolwiek innej masywnej biblioteki, ponieważ nie będę używać żadnej z jej funkcji.Dotyk Mimicng w textarea

Mam <pre> umieszczony za tekstem z przezroczystym tłem, dzięki czemu mogę symulować efekt podświetlania w tekście. Jednak chcę też móc zmienić kolor czcionki (aby nie zawsze była taka sama). Więc próbowałem color: transparent na textarea, który pozwala mi na styl tekstu w dowolny sposób chcę, ponieważ pojawia się tylko na element <pre> za textarea, ale karetka znika.

Dostałem go do pracy dość dobrze, chociaż nie jest doskonały. Głównym problemem jest to, że kiedy trzymasz klucz i spamujesz tę postać, karetka zawsze pozostaje w tyle za jedną postacią. Co więcej, wydaje się, że jest dość zasobny ...

Jeśli zauważysz w kodzie coś, co wymaga poprawy, możesz to skomentować!

Oto skrzypce z kodem: http://jsfiddle.net/2t5pu/25/

A dla tych, którzy nie chcą, aby odwiedzić jsfiddle z jakiegokolwiek powodu, oto cały kod:

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]; 
    } 

Z góry dziękuję!

+0

Twoja kurtyna pozostaje w tyle z powodu prawdziwej apatii z powodu małej repozycji czasu. Spróbuj wprowadzić kilka linii tekstu, a następnie kliknij i przytrzymaj lewy przycisk myszy w innym rzędzie, a zobaczysz go. Aby temu zaradzić, musisz ustawić przezroczystość pola tekstowego na 0. Ale wtedy będziesz musiał radzić sobie z poprawnym wyświetlaniem tekstu. – twil

+0

czy naprawdę trzeba zaktualizować kursor 100 razy na sekundę? Myślę, że wystarczyłoby 5 lub 10. to dlatego twoja aplikacja działa wolno ... – dandavis

+0

@dandavis: musi być tak responsywny jak domyślny. –

Odpowiedz

4

Czy edytowalny element Div nie rozwiązuje całego problemu?

Kod, który ma podświetlanie:

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(); 
     } 
    } 
}); 

Niestety, ten wykonany niektórych funkcji edycyjnych zostaną utracone, jak Ctrl + Z ... i podczas wklejania tekstu, pobyty daszka na początku wklejonego tekstu.

Połączyłem kod z innych odpowiedzi, aby uzyskać ten kod, więc proszę, daj im kredyt.

EDIT: Odkryłem coś ciekawego ... rodzimy daszek pojawia się w przypadku korzystania z contentEditable elementu, a wewnątrz niego użyć innego elementu z niewidoczne czcionki:

<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

Cały cel polegał na umożliwieniu mi prostego wyróżniania składni. Próbowałem to zrobić z contenteditable div, ale karetka przeskoczyłaby, dostała reset, itp. Po prostu nie była niezawodna. –

+0

@Lindrian Spójrz na EDIT, może Cię to zainteresować ... niestety karetka z przezroczystym tekstem działa tylko w chrome i IE, a nie w firefoxie. =) –

+0

Jest to prawdopodobnie najbardziej przydatna odpowiedź do tej pory, dzięki! –

2

Opóźnienie to myślę, ze względu na to, że keyup uruchamia doStuff nieco za późno, ale kluczowy moment jest nieco za krótki.

Spróbuj zamiast podłączania zdarzenia jQuery (normalnie wolałabym wydarzenia na odpytywanie, ale w tym przypadku to może dać lepsze wyczucie) ...

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")); 
    } 
} 

... albo tu na skrzypce: http://jsfiddle.net/2t5pu/27/

+0

Przykro mi, ale to nie jest o wiele lepsze niż to, co miałem wcześniej. –

2

to wydaje się działać świetnie i nie używa żadnych ankiet, tak jak mówiłem w komentarzach.

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/

Może jestem czegoś brakuje, ale myślę, że to jest dość solidna, a zachowuje się lepiej niż przy użyciu przedziałów tworzyć własne wydarzenia.

EDYCJA: dodano zegar, aby zapobiec układaniu w stos.

+0

Przytrzymaj klawisz, nadal pozostaje w tyle. Nie sądzę, że moje rozwiązanie jest warte dalszej pracy. –

+0

cóż, jest dla mnie super gładka. zaktualizowałem odpowiedź, aby poradzić sobie z opóźnieniem na starszych komputerach. jeśli maszyna jest naprawdę stara, nawet teksty mogą być nieco za sobą ... – dandavis