Đến nội dung chính
sang.id.vn
Quay lại

Bẫy float và fixed positioning trong contenteditable

English

Mình gặp mấy vấn đề này khi xây dựng rich text editor. Cả float lẫn position: fixed bên trong contenteditable đều hỏng theo những cách không dễ nhận ra.

float trong contenteditable

Đặt ảnh float bên trong contenteditable div:

<div contenteditable="true">
  <img src="photo.jpg" style="float: left; margin: 0 12px 12px 0; width: 150px" />
  <p>Text sẽ bao quanh ảnh...</p>
</div>

Hỏng theo nhiều cách: con trỏ không đặt đúng vị trí gần float, vùng chọn hiển thị lung tung, Backspace có thể xóa hoặc bỏ qua ảnh, Enter tạo các phần tử rỗng chồng quanh nó.

Cách sửa: Bọc phần tử float trong contenteditable="false":

<div contenteditable="true">
  <div contenteditable="false" style="float: left; margin: 0 12px 12px 0">
    <img src="photo.jpg" style="width: 150px" />
  </div>
  <p>Text bao quanh ảnh, nhưng ảnh là vùng không chỉnh sửa được.</p>
</div>

Hoặc dùng CSS Grid thay vì float:

<div contenteditable="true">
  <div style="display: grid; grid-template-columns: 150px 1fr; gap: 12px">
    <img src="photo.jpg" style="width: 150px" contenteditable="false" />
    <div><p>Text chỉnh sửa được bên cạnh ảnh.</p></div>
  </div>
</div>

position: fixed trong contenteditable

Đặt toolbar bên trong contenteditable div:

<div contenteditable="true" style="min-height: 500px">
  <div style="position: fixed; top: 0; background: #fff; padding: 8px; z-index: 10">
    <button onmousedown="event.preventDefault()" onclick="document.execCommand('bold')">B</button>
    <button onmousedown="event.preventDefault()" onclick="document.execCommand('italic')">I</button>
  </div>
  <p style="margin-top: 50px">Nội dung chỉnh sửa ở đây...</p>
</div>

Toolbar trở nên chỉnh sửa được — người dùng có thể gõ vào hoặc xóa các nút toolbar. Click vào nút cũng có thể cướp focus khỏi editor.

Cách sửa: Chuyển toolbar ra ngoài thành phần tử anh em:

<div style="position: fixed; top: 0; background: #fff; padding: 8px; z-index: 10">
  <button onmousedown="event.preventDefault()" onclick="document.execCommand('bold')">B</button>
  <button onmousedown="event.preventDefault()" onclick="document.execCommand('italic')">I</button>
</div>

<div contenteditable="true" style="margin-top: 50px; min-height: 500px">
  <p>Nội dung chỉnh sửa ở đây...</p>
</div>

onmousedown="event.preventDefault()" trên các nút rất quan trọng — ngăn click cướp focus, giữ nguyên vùng chọn text của editor khi execCommand chạy.


Share this post on:

Bài trước
Big Bang vs. Kinh tế chia sẻ — Một phép so sánh bất ngờ hoàn hảo
Bài tiếp
Contenteditable lồng nhau: Dùng Shadow DOM để chuyển editing host