Không phải lúc nào nhà phát triển cũng dễ dàng tích hợp các tính năng chỉnh sửa nâng cao vào ứng dụng web của họ. Nền tảng web cung cấp khả năng chỉnh sửa cho cả tài liệu văn bản thuần tuý và tài liệu HTML bằng cách sử dụng các phần tử như <input>
và <textarea>
hoặc bằng cách áp dụng thuộc tính contenteditable
cho các phần tử. Tuy nhiên, các chức năng cơ bản của các loại phần tử này thường không đủ để nhà phát triển đạt được mục tiêu trong ứng dụng của họ.
Nhà phát triển thường triển khai thành công chế độ xem trình chỉnh sửa tuỳ chỉnh của riêng họ để triển khai chức năng mà người dùng cần. Chế độ xem trình chỉnh sửa có thể được tạo bằng một DOM phức tạp hoặc thậm chí bằng một phần tử <canvas>
. Tuy nhiên, vì cách duy nhất để nhà phát triển nhận được dữ liệu nhập văn bản là cần có một phần tử có thể chỉnh sửa được lấy làm tâm điểm, nên họ vẫn cần đặt một phần tử contenteditable
ẩn ở đâu đó trên trang.
Kết quả là mặc dù người dùng có vẻ như đang trực tiếp chỉnh sửa nội dung trong chế độ xem trình chỉnh sửa tuỳ chỉnh của ứng dụng, nhưng nhà phát triển thực sự đang nhận dữ liệu đầu vào bằng trình xử lý sự kiện trong phần tử ẩn, sau đó phản ánh dữ liệu đó vào chế độ xem trình chỉnh sửa hiển thị. Điều này có thể dẫn đến sự cố khi nhà phát triển phải đối phó với hành vi chỉnh sửa mặc định của trình duyệt trong phần tử contenteditable
ẩn.
Để giải quyết loại vấn đề này, nhóm Microsoft Edge đã thúc đẩy việc chuẩn hoá EditContext, một API nền tảng web mới cho phép nhà phát triển nhận trực tiếp dữ liệu nhập văn bản mà không bị ràng buộc với các hành vi chỉnh sửa mặc định của trình duyệt.
Ví dụ thực tế
Ví dụ: khi người dùng đang cộng tác trong Word Online. Người dùng có thể cùng chỉnh sửa và xem các thay đổi cũng như vị trí con trỏ của người khác. Tuy nhiên, nếu một cộng tác viên đang sử dụng cửa sổ Trình chỉnh sửa phương thức nhập (IME) để soạn văn bản tiếng Nhật, thì trình chỉnh sửa của họ sẽ không được cập nhật để hiển thị các thay đổi của người dùng khác cho đến khi người dùng IME hoàn tất nội dung soạn. Điều này là do việc thay đổi khu vực của DOM đang được chỉnh sửa trong khi có một thành phần IME đang hoạt động có thể khiến thành phần đó bị huỷ sớm. Ứng dụng phải đợi cho đến khi cửa sổ IME đóng để cập nhật chế độ xem, điều này có thể gây ra độ trễ và cản trở việc cộng tác.
Để mang lại trải nghiệm tốt hơn cho cả nhà phát triển và người dùng, nhà phát triển cần có cách tách biệt hoạt động nhập văn bản khỏi chế độ xem DOM HTML. API EditContext là giải pháp cho vấn đề này.
Kiến thức cơ bản về EditContext
Với EditContext, bạn có thể nhận dữ liệu đầu vào văn bản và thành phần trực tiếp thông qua giao diện API EditContext, thay vì thông qua việc quan sát các thay đổi đối với DOM. Điều này cho phép kiểm soát chặt chẽ hơn cách xử lý dữ liệu đầu vào, thậm chí cho phép thêm khả năng chỉnh sửa vào phần tử <canvas>
.
Việc liên kết một thực thể EditContext với một phần tử sẽ giúp phần tử đó có thể chỉnh sửa:
// This will be our editable element.
const element = document.querySelector('#editor-element');
// Creating the EditContext object.
const editContext = new EditContext();
// Associating the EditContext object with our DOM element.
// The element is now focusable and can receive text input.
element.editContext = editContext;
// In order to render the text typed by the user onto the
// page, as well as the user's selection, you'll need to
// receive the input in a textupdate event callback.
editContext.addEventListener('textupdate', event => {
element.textContent = editContext.text;
// For brevity, the code to render the selection
// isn't shown here.
renderSelection(event.selectionStart, event.selectionEnd);
});
Trách nhiệm của tác giả
Việc sử dụng API EditContext giúp dễ dàng hỗ trợ các phương thức nhập nâng cao như cửa sổ soạn thư IME, bộ chọn biểu tượng cảm xúc và các nền tảng nhập khác của hệ điều hành. Để thực hiện tất cả những việc này trong phần tử có thể chỉnh sửa, API EditContext cần một số thông tin. Ngoài việc hiển thị văn bản và lựa chọn, bạn còn phải làm một số việc khác khi sử dụng API EditContext.
Quản lý cạnh của một vùng có thể chỉnh sửa hoặc nếu lựa chọn của người dùng thay đổi
Gọi các phương thức updateControlBounds()
và updateSelectionBounds()
để thông báo cho thực thể EditContext bất cứ khi nào kích thước của vùng có thể chỉnh sửa hoặc lựa chọn của người dùng thay đổi. Điều này giúp nền tảng quyết định vị trí hiển thị cửa sổ IME và giao diện người dùng chỉnh sửa khác dành riêng cho nền tảng.
// It's necessary to provide bounds information because EditContext
// is generic enough to work with any type of web editor, even
// <canvas>-based editors. The API doesn't make any assumptions as
// to how the editor is implemented or how the selection is rendered.
// Bounds are given in the client coordinate space.
const controlBound = editorElement.getBoundingClientRect();
const selection = document.getSelection();
const selectionBound = selection.getRangeAt(0).getBoundingClientRect();
editContext.updateControlBounds(controlBound);
editContext.updateSelectionBounds(selectionBound);
Quản lý vị trí của giao diện người dùng của trình chỉnh sửa
Theo dõi sự kiện characterboundsupdate
và gọi updateCharacterBounds()
để phản hồi nhằm giúp nền tảng quyết định vị trí hiển thị cửa sổ IME và giao diện người dùng chỉnh sửa dành riêng cho nền tảng khác.
Áp dụng định dạng
Theo dõi sự kiện textformatupdate
và áp dụng định dạng do sự kiện chỉ định cho chế độ xem trình chỉnh sửa. Các kiểu văn bản này được IME sử dụng khi soạn một số ngôn ngữ nhất định. Ví dụ: IME tiếng Nhật sẽ sử dụng đường gạch dưới để cho biết phần văn bản nào đang được soạn.
Xử lý hành vi chỉnh sửa văn bản đa dạng thức
Theo dõi sự kiện beforeinput
để xử lý mọi hành vi chỉnh sửa văn bản đa dạng thức mà bạn muốn hỗ trợ, chẳng hạn như phím tắt để in đậm hoặc in nghiêng văn bản hoặc áp dụng tính năng kiểm tra chính tả.
Quản lý các thay đổi trong lựa chọn của người dùng
Khi lựa chọn của người dùng thay đổi do nhập bằng bàn phím hoặc chuột, bạn cần thông báo cho thực thể EditContext về thay đổi đó. Điều này là cần thiết vì API EditContext có thể áp dụng cho nhiều trường hợp sử dụng, bao gồm cả trình chỉnh sửa được hiển thị bằng phần tử <canvas>
mà trình duyệt không thể tự động phát hiện các thay đổi về lựa chọn.
document.addEventListener('selectionchange', () => {
const selection = document.getSelection();
// EditContext doesn't handle caret navigation, so all the caret navigation/selection that happens
// in DOM space needs to be mapped to plain text space by the author and passed to EditContext.
// This example code assumes the editable area only contains text under a single node.
editContext.updateSelection(selection.anchorOffset, selection.focusOffset);
});
Nếu phần tử bạn đang sử dụng với EditContext là phần tử <canvas>
, bạn cũng cần triển khai các hành vi chọn và điều hướng con nháy, chẳng hạn như di chuyển qua văn bản bằng các phím mũi tên. Ngoài ra, tính năng kiểm tra chính tả tích hợp của trình duyệt chỉ hoạt động trong các phần tử không phải <canvas>
.
EditContext so với contenteditable
EditContext là một lựa chọn tuyệt vời nếu bạn đang triển khai một trình chỉnh sửa đầy đủ tính năng và muốn có toàn quyền kiểm soát cách xử lý hoạt động nhập văn bản, hoặc nếu bạn đang thêm các tính năng nâng cao như đồng chỉnh sửa với nhiều người dùng. Tuy nhiên, với tất cả các yêu cầu trước đó để sử dụng EditContext, nếu tất cả những gì bạn cần là hỗ trợ chỉnh sửa văn bản đơn giản, thì bạn vẫn có thể muốn sử dụng các phần tử <input>
, <textarea>
hoặc thuộc tính contenteditable
.
Hướng đến tương lai
Nhóm Microsoft Edge đã triển khai EditContext trong Chromium thông qua hoạt động cộng tác với các kỹ sư Chrome và đang phân phối trong bản phát hành 121 (tháng 1 năm 2024) của cả Chrome và Edge. Hiện tại, API này chỉ có trong các trình duyệt dựa trên Chromium, nhưng bạn có thể đọc vị trí của Mozilla và WebKit trên API EditContext.
Chúng tôi muốn giúp các nhà phát triển web dễ dàng tạo ra trải nghiệm chỉnh sửa tuỳ chỉnh mạnh mẽ trên web. Chúng tôi tin rằng EditContext API sẽ giúp đạt được điều này bằng cách giải quyết các thách thức hiện có và cung cấp cách trực tiếp hơn để xử lý hoạt động nhập văn bản.
Nếu bạn muốn tìm hiểu thêm về API này, hãy tham khảo tài liệu của MDN. Để gửi ý kiến phản hồi về thiết kế của API, hãy mở một vấn đề trong kho lưu trữ GitHub của API EditContext. Để báo cáo lỗi khi triển khai API, hãy gửi lỗi tại crbug.com.