2013-07-08 30 views
5

Tôi đang cố gắng bắt chước dấu của một vùng văn bản với mục đích tạo ra một textarea có trọng lượng rất nhẹ. Tôi không muốn sử dụng một cái gì đó như codemirror hoặc bất kỳ thư viện lớn khác bởi vì tôi sẽ không sử dụng bất kỳ tính năng của họ.Bắt chước cẩn thận trong văn bản

Tôi có một <pre> được đặt phía sau một vùng văn bản có nền trong suốt để tôi có thể mô phỏng hiệu ứng làm nổi bật trong văn bản. Tuy nhiên, tôi cũng muốn có thể thay đổi màu chữ (vì vậy nó không phải lúc nào cũng giống nhau). Vì vậy, tôi đã thử color: transparent trên vùng văn bản cho phép tôi tạo kiểu văn bản theo bất kỳ cách nào tôi muốn bởi vì nó chỉ xuất hiện trên phần tử <pre> phía sau vùng văn bản, nhưng dấu mũ sẽ biến mất.

Tôi đã làm cho nó hoạt động khá tốt, mặc dù nó không hoàn hảo. Vấn đề chính là khi bạn giữ một chìa khóa và thư rác nhân vật đó, các caret dường như luôn luôn tụt hậu một nhân vật phía sau. Không chỉ vậy, nó có vẻ là khá tài nguyên nặng ..

Nếu bạn thấy bất kỳ thứ gì khác trong mã cần được cải thiện, hãy bình luận về điều đó!

Dưới đây là một fiddle với mã: http://jsfiddle.net/2t5pu/25/

Và đối với bạn của những người không muốn đến thăm jsfiddle vì lý do gì, sau đây là toàn bộ mã:

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

Xin cảm ơn trước!

+0

Dấu sót của bạn bị trễ do dấu sót thực sự gây ra một chút thời gian nghỉ ngơi. Hãy thử nhập vài dòng văn bản và sau đó bấm và giữ nút chuột trái trên hàng khác nhau và bạn sẽ thấy nó. Để khắc phục điều này, bạn sẽ phải đặt độ mờ của vùng văn bản thành 0. Nhưng sau đó bạn sẽ phải xử lý văn bản hiển thị chính xác. – twil

+0

bạn có thực sự cần cập nhật con trỏ 100 lần một giây không? tôi nghĩ rằng 5 hoặc 10 sẽ đủ. đó là lý do khiến ứng dụng của bạn chạy chậm ... – dandavis

+0

@dandavis: cần phải đáp ứng như ứng dụng mặc định. –

Trả lời

4

Phần tử Div có thể chỉnh sửa có giải quyết được toàn bộ vấn đề không?

Mã mà không được làm nổi bật:

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

Thật không may, điều này thực hiện một số chức năng chỉnh sửa bị mất, như Ctrl + Z ... và khi dán văn bản, ở lại caret ở đầu văn bản đã dán.

Tôi đã kết hợp mã từ các câu trả lời khác để tạo mã này, vì vậy, vui lòng cung cấp tín dụng cho họ.

EDIT: tôi đã phát hiện ra một cái gì đó thú vị ... các caret có nguồn gốc xuất hiện nếu bạn sử dụng một yếu tố contentEditable, và bên trong của nó, bạn sử dụng một yếu tố khác với phông chữ vô hình:

<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

Toàn bộ mục tiêu là cho phép tôi làm nổi bật cú pháp đơn giản. Tôi đã thử làm điều này với một div có thể chỉnh sửa được, nhưng dấu mũ sẽ nhảy xung quanh, lấy lại, v.v. Chỉ là không đáng tin cậy. –

+0

@Lindrian Hãy xem EDIT, bạn có thể tìm thấy nó thú vị ... tiếc là các dấu mũ với văn bản trong suốt chỉ làm việc trong chrome và IE, không phải trong firefox. =) –

+0

Đây có lẽ là câu trả lời hữu ích nhất cho đến nay, cảm ơn! –

2

Độ trễ là do tôi cho rằng do khóa kích hoạt doStuff một chút đã quá muộn, nhưng việc khóa bàn phím hơi quá sớm.

Hãy thử điều này thay vì sự kiện hookup jQuery (thường là tôi muốn các sự kiện để bỏ phiếu, nhưng trong trường hợp này nó có thể cung cấp cho một cảm giác tốt hơn) ...

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

... hay đây cho fiddle: http://jsfiddle.net/2t5pu/27/

+0

Tôi xin lỗi nhưng điều này là không thực sự tốt hơn nhiều so với những gì tôi đã có trước khi không may. –

2

điều này dường như hoạt động tốt và không sử dụng bất kỳ cuộc thăm dò ý kiến ​​nào, giống như tôi đã nói đến trong nhận xét.

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/

có lẽ tôi đang thiếu cái gì, nhưng tôi nghĩ rằng đây là khá vững chắc, và nó cư xử tốt hơn so với sử dụng khoảng thời gian để tạo ra các sự kiện của riêng bạn.

EDIT: thêm bộ hẹn giờ để ngăn xếp xếp chồng.

+0

Giữ phím, nó vẫn bị chậm lại. Tôi không nghĩ rằng giải pháp của tôi đáng làm việc hơn nữa. –

+0

tốt, nó siêu mịn đối với tôi. tôi cập nhật câu trả lời để đối phó với độ trễ trên các máy cũ hơn. nếu máy thực sự cũ, thậm chí textareas có thể nhận được một chút phía sau ... – dandavis

Các vấn đề liên quan