2016-05-02 18 views
9

Tôi đang viết một công cụ đánh dấu cú pháp. Công cụ đánh dấu sẽ cập nhật đánh dấu ngay lập tức trong khi nhập văn bản và điều hướng bằng các phím mũi tên.Cách nhận vị trí con trỏ văn bản sau khi sự kiện nhấn phím đã xảy ra?

Sự cố tôi gặp phải là khi sự kiện 'nhấn phím' được kích hoạt, bạn vẫn nhận được vị trí cũ của con trỏ văn bản qua window.getSelection().

Ví dụ:

function handleKeyEvent(evt) { 
 
    console.log(evt.type, window.getSelection().getRangeAt(0).startOffset); 
 
} 
 

 
var div = document.querySelector("div"); 
 
div.addEventListener("keydown", handleKeyEvent); 
 
div.addEventListener("keypress", handleKeyEvent); 
 
div.addEventListener("input", handleKeyEvent); 
 
div.addEventListener("keyup", handleKeyEvent);
<div contenteditable="true">f<span class="highlight">oo</span></div>

Trong ví dụ, đặt dấu nháy trước chữ 'foo', sau đó nhấn (phím mũi tên bên phải).

Trong giao diện điều khiển của DevTool yêu thích của bạn, bạn sẽ thấy như sau:

keydown 0 
keypress 0 
keyup 1 

Đó 0 ngoài keypress rõ ràng là vị trí dấu nháy cũ. Nếu bạn giữ phím lâu hơn một chút, bạn sẽ nhận được một cái gì đó như thế này:

keydown 0 
keypress 0 
keydown 1 
keypress 1 
keydown 1 
keypress 1 
keydown 2 
keypress 2 
keyup 2 

vị trí dấu nháy mới như tôi sẽ nhận được nó cho 'KeyUp' hoặc 'đầu vào' Những gì tôi muốn nhận được là. Mặc dù 'keyup' được kích hoạt quá muộn (tôi muốn làm nổi bật cú pháp trong khi phím được nhấn xuống) và 'input' chỉ được kích hoạt khi có một số đầu vào (nhưng không tạo ra bất kỳ đầu vào nào).

Có sự kiện nào được kích hoạt sau khi vị trí dấu mũ đã thay đổi và không chỉ trên đầu vào? Hay tôi phải tính toán vị trí của con trỏ văn bản và nếu vậy, làm thế nào? (Tôi giả định này có thể nhận được khá phức tạp khi văn bản kết thúc tốt đẹp và bạn nhấn (phím mũi tên xuống).)

+1

Tôi nghĩ bạn sẽ gặp phải sự cố khác khi sử dụng sự kiện nhấn phím. Chrome dường như không kích hoạt sự kiện khi nhấn phím mũi tên (Firefox). Có một lỗi crom cũ trong trạng thái WontFix mô tả điều này: https://bugs.chromium.org/p/chromium/issues/detail?id=2606 – Alex

+0

Cảm ơn bạn đã gợi ý! Đúng vậy, sự kiện 'nhấn phím 'không được kích hoạt, nhưng [nó rõ ràng được đánh dấu là lỗi thời] (https://www.w3.org/TR/uievents/#legacy-keyboardevent-event-types). Như tôi đã thấy, đặc điểm kỹ thuật nói rằng mọi người nên sử dụng sự kiện 'beforeinput' để thay thế. Và 'keydown' cũng bị bắn liên tục. Mặc dù tất cả những sự kiện đó đều bị sa thải quá sớm. Tôi bây giờ [đã gửi một vấn đề về thông số sự kiện giao diện người dùng.] (Https://github.com/w3c/uievents/issues/111) với hy vọng nhận được sự kiện thích hợp cho việc này. –

+1

Vị trí con trỏ/chọn vv vẫn là một pita để hỗ trợ trên tất cả các trình duyệt. Nó được một thời gian mà tôi cần này nhưng [rangy] (https://github.com/timdown/rangy) là rất vững chắc với các công cụ liên quan đến con trỏ .. –

Trả lời

5

Bạn có thể sử dụng setTimeout để xử lý keydown sự kiện không đồng bộ:

function handleKeyEvent(evt) { 
 
    setTimeout(function() { 
 
     console.log(evt.type, window.getSelection().getRangeAt(0).startOffset); 
 
    }, 0); 
 
} 
 

 
var div = document.querySelector("div"); 
 
div.addEventListener("keydown", handleKeyEvent);
<div contenteditable="true">This is some text</div>

Phương thức đó giải quyết vấn đề xử lý khóa. Trong ví dụ của bạn, bạn cũng có một yếu tố span bên trong div, mà làm thay đổi giá trị vị trí được trả về bởi

window.getSelection().getRangeAt(0).startOffset 
+1

Sử dụng 'setTimeout()' theo cách này trông khá hack nhưng nó hoạt động . Vì vậy, cảm ơn vì điều đó! Nếu không ai tìm thấy một giải pháp đẹp hơn vào những ngày tiếp theo, tôi sẽ chấp nhận câu trả lời của bạn. Tôi biết về việc thay đổi giá trị vị trí, tôi chỉ cung cấp một số mã đơn giản trong ví dụ của mình. Phạm vi được trả về bởi 'getRangeAt (0)' cũng cung cấp nút văn bản trong thuộc tính 'startContainer', mà' startOffset' đề cập đến, vì vậy đó không phải là vấn đề. –

+1

Gọi 'setTimeout (fn, 0)' là một kỹ thuật nổi tiếng để chạy mã sau khi ngăn xếp cuộc gọi hiện tại đã hoàn thành. Bạn có thể xem các bài viết này: [Tại sao setTimeout (fn, 0) đôi khi hữu ích?] (Http://stackoverflow.com/questions/779379/why-is-settimeoutfn-0-sometimes-useful), [setTimeout với một sự chậm trễ 0ms làm gì?] (https://www.quora.com/What-does-setTimeout-with-a-0ms-delay-do), [Hướng dẫn Javascript] (http://javascript.info/tutorial/events - và - chiều sâu); và [video giải trí] này (http://2014.jsconf.eu/speakers/philip-roberts-what-the-heck-is-the-event-loop-anyway.html). – ConnorsFan

1

Dưới đây là một giải pháp điều chỉnh vị trí bằng cách sử dụng sự kiện 'KeyDown':

function handleKeyEvent(evt) { 
 
    var caretPos = window.getSelection().getRangeAt(0).startOffset; 
 

 
    if (evt.type === "keydown") { 
 
    switch(evt.key) { 
 
     case "ArrowRight": 
 
     if (caretPos < evt.target.innerText.length - 1) { 
 
      caretPos++; 
 
     } 
 
     break; 
 

 
     case "ArrowLeft": 
 
     if (caretPos > 0) { 
 
      caretPos--; 
 
     } 
 
     break; 
 

 
     case "ArrowUp": 
 
     case "Home": 
 
     caretPos = 0; 
 
     break; 
 

 
     case "ArrowDown": 
 
     case "End": 
 
     caretPos = evt.target.innerText.length; 
 
     break; 
 

 
     default: 
 
     return; 
 
    } 
 
    } 
 
    console.log(caretPos); 
 
} 
 

 
var div = document.querySelector("div"); 
 
div.addEventListener("keydown", handleKeyEvent); 
 
div.addEventListener("input", handleKeyEvent);
<div contenteditable="true">f<span class="highlight">oo</span></div>

Thật không may giải pháp này là có một số sai sót:

  • Khi bên trong một phần tử con như số <span> trong ví dụ, nó không cung cấp chính xác startOffset cũng không chính xác startContainer.
  • Nó không thể xử lý nhiều dòng.
  • Nó không xử lý tất cả các tùy chọn điều hướng. Ví dụ. nhảy từ khôn ngoan qua Ctrl + / không được nhận dạng.

Và có thể có nhiều vấn đề khác mà tôi không nghĩ tới. Trong khi nó sẽ có thể xử lý tất cả những vấn đề đó, nó làm cho việc thực hiện rất phức tạp. Vì vậy, giải pháp đơn giản setTimeout(..., 0) được cung cấp bởi ConnorsFan chắc chắn là thích hợp hơn cho đến khi có event for caret position changes.

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