Chọn văn bản theo chương trình trong phần tử HTML có thể nội dung?


119

Trong JavaScript, bạn có thể lập trình chọn văn bản trong một inputhoặc textareaphần tử. Bạn có thể đặt tiêu điểm đầu vào bằng ipt.focus(), rồi chọn nội dung của nó bằng ipt.select(). Bạn thậm chí có thể chọn một phạm vi cụ thể với ipt.setSelectionRange(from,to).

Câu hỏi của tôi là: có cách nào để làm điều này trong một contenteditablephần tử không?

Tôi thấy rằng tôi có thể làm elem.focus(), đặt dấu mũ trong một contenteditablephần tử, nhưng sau đó việc chạy elem.select()không hoạt động (và cũng không setSelectionRange). Tôi không thể tìm thấy bất cứ điều gì trên web về nó, nhưng có lẽ tôi đang tìm kiếm sai ...

Nhân tiện, nếu nó tạo ra bất kỳ sự khác biệt nào, tôi chỉ cần nó hoạt động trong Google Chrome, vì đây là tiện ích mở rộng của Chrome.

Câu trả lời:


170

Nếu bạn muốn chọn tất cả nội dung của một phần tử (có thể nội dung hoặc không) trong Chrome, đây là cách thực hiện. Điều này cũng sẽ hoạt động trong Firefox, Safari 3+, Opera 9+ (có thể cả các phiên bản cũ hơn) và IE 9. Bạn cũng có thể tạo các lựa chọn theo cấp độ ký tự. Các API bạn cần là Dải DOM (thông số hiện tại là DOM Cấp 2 , xem thêm MDN ) và Lựa chọn, đang được chỉ định như một phần của thông số Dải mới ( tài liệu MDN ).

function selectElementContents(el) {
    var range = document.createRange();
    range.selectNodeContents(el);
    var sel = window.getSelection();
    sel.removeAllRanges();
    sel.addRange(range);
}

var el = document.getElementById("foo");
selectElementContents(el);

15
Để có thêm tính tương thích, bạn nên gọi selectElementContents()bằng một setTimeout()hoặc requestAnimationFrame()nếu được gọi từ một onfocus. Xem jsfiddle.net/rudiedirkx/MgASG/1/show
Rudie

@Dylan: Tôi không chắc: câu hỏi đề cập rằng OP đã được sử dụng focus().
Tim Down

4
Khả năng tương thích @Rudie cho ứng dụng nào?
yckart

Hoạt động tốt trên máy tính để bàn. Trên trình duyệt di động, không hoạt động. Không có lựa chọn nào được thực hiện. Đã thử Safari và Chrome trên iPhone iOS 11.
campbell

1
@campbell: Nó hoạt động trên Safari ít nhất là trên iOS, miễn là bạn đã có lựa chọn . Nếu không thì không, trình duyệt chỉ đơn giản là không cho phép JavaScript hiển thị lựa chọn, có lẽ vì lý do trải nghiệm người dùng.
Tim Down

34

Ngoài câu trả lời của Tim Downs , tôi đã thực hiện một giải pháp hoạt động ngay cả trong oldIE:

var selectText = function() {
  var range, selection;
  if (document.body.createTextRange) {
    range = document.body.createTextRange();
    range.moveToElementText(this);
    range.select();
  } else if (window.getSelection) {
    selection = window.getSelection();
    range = document.createRange();
    range.selectNodeContents(this);
    selection.removeAllRanges();
    selection.addRange(range);
  }
};

document.getElementById('foo').ondblclick = selectText;​

Đã thử nghiệm trên IE 8+, Firefox 3+, Opera 9+ và Chrome 2+. Ngay cả khi tôi đã thiết lập nó thành một plugin jQuery:

jQuery.fn.selectText = function() {
  var range, selection;
  return this.each(function() {
    if (document.body.createTextRange) {
      range = document.body.createTextRange();
      range.moveToElementText(this);
      range.select();
    } else if (window.getSelection) {
      selection = window.getSelection();
      range = document.createRange();
      range.selectNodeContents(this);
      selection.removeAllRanges();
      selection.addRange(range);
    }
  });
};

$('#foo').on('dblclick', function() {
  $(this).selectText();
});

... và ai là người đã được thiết lập, đây là điều tương tự cho tất cả những người nghiện cà phê:

jQuery.fn.selectText = ->
  @each ->
    if document.body.createTextRange
      range = document.body.createTextRange()
      range.moveToElementText @
      range.select()
    else if window.getSelection
      selection = window.getSelection()
      range = document.createRange()
      range.selectNodeContents @
      selection.removeAllRanges()
      selection.addRange range
    return

Cập nhật:

Nếu bạn muốn chọn toàn bộ trang hoặc nội dung của vùng có thể chỉnh sửa (được gắn cờ contentEditable), bạn có thể làm điều đó đơn giản hơn nhiều bằng cách chuyển sang designModevà sử dụng document.execCommand:

Có một điểm khởi đầu tốt ở MDNmột tài liệu nhỏ .

var selectText = function () {
  document.execCommand('selectAll', false, null);
};

(hoạt động tốt trong IE6 +, Opera 9+, Firefoy 3+, Chrome 2+) http://caniuse.com/#search=execCommand


10

Vì tất cả các câu trả lời hiện có đều liên quan đến divcác phần tử, nên tôi sẽ giải thích cách thực hiện với spans.

Có một sự khác biệt nhỏ khi chọn phạm vi văn bản trong a span. Để có thể chuyển chỉ mục bắt đầu và kết thúc văn bản, bạn phải sử dụng một Textnút, như được mô tả ở đây :

Nếu startNode là một Node thuộc loại Text, Comment hoặc CDATASection, thì startOffset là số ký tự từ đầu startNode. Đối với các loại Node khác, startOffset là số nút con giữa thời điểm bắt đầu của startNode.

var e = document.getElementById("id of the span element you want to select text in");
var textNode = e.childNodes[0]; //text node is the first child node of a span

var r = document.createRange();
var startIndex = 0;
var endIndex = textNode.textContent.length;
r.setStart(textNode, startIndex);
r.setEnd(textNode, endIndex);

var s = window.getSelection();
s.removeAllRanges();
s.addRange(r);

Thực sự nên là: r.setStart(e.firstChild,0); r.setEnd(e.lastChild,e.lastChild.textContent.length); Tất nhiên bạn nên kiểm tra rằng e.firstChild thực sự không phải là null.
Yorick

2
Không có sự khác biệt giữa việc thực hiện lựa chọn trong a <div>và một <span>phần tử. Ít nhất, không phải như bạn mô tả.
Tim Down

Có sự khác biệt giữa div và span, trong một số trường hợp, giải pháp cho div không hoạt động ngay trong span. Ví dụ, nếu bạn chọn văn bản lập trình với div giải pháp sau đó dán nội dung mới, nó sẽ thay thế không phải toàn bộ văn bản, chỉ là một phần và có sự khác biệt giữa chrome và firefox
neosonne

6

Rangy cho phép bạn thực hiện việc này trên nhiều trình duyệt với cùng một mã. Rangy là một triển khai trên nhiều trình duyệt của các phương thức DOM cho các lựa chọn. Nó đã được thử nghiệm tốt và làm cho việc này ít đau hơn rất nhiều. Tôi từ chối chạm vào nội dung phù hợp nếu không có nó.

Bạn có thể tìm thấy rangy ở đây:

http://code.google.com/p/rangy/

Với rangy trong dự án của bạn, bạn luôn có thể viết điều này, ngay cả khi trình duyệt là IE 8 trở lên và có một API gốc hoàn toàn khác cho các lựa chọn:

var range = rangy.createRange();
range.selectNodeContents(contentEditableNode);
var sel = rangy.getSelection();
sel.removeAllRanges();
sel.addRange(range);

Trong đó "contentEditableNode" là nút DOM có thuộc tính contenteditable. Bạn có thể tìm nạp nó như thế này:

var contentEditable = document.getElementById('my-editable-thing');

Hoặc nếu jQuery đã là một phần của dự án của bạn và bạn thấy nó thuận tiện:

var contentEditable = $('.some-selector')[0];

Dự án Rangy đã chuyển sang Github ngay bây giờ: github.com/timdown/rangy
tanius

4

Cách làm hiện đại là như thế này. Thêm chi tiết về MDN

document.addEventListener('dblclick', (event) => {
  window.getSelection().selectAllChildren(event.target)
})
<div contenteditable="true">Some text</div>


Cảm ơn, điều này hoạt động tuyệt vời! Fwiw, trang MDN đó đánh dấu công nghệ này là thử nghiệm. Nhưng nó hoạt động trong phiên bản hiện tại của Chrome và FF vào tháng 6 năm 2020.
JohnK

2

[Đã cập nhật để sửa lỗi]

Dưới đây là một ví dụ được điều chỉnh từ câu trả lời này có vẻ hoạt động tốt trong Chrome - Chọn phạm vi trong div có thể nội dung

var elm = document.getElementById("myText"),
    fc = elm.firstChild,
    ec = elm.lastChild,
    range = document.createRange(),
    sel;
elm.focus();
range.setStart(fc,1);
range.setEnd(ec,3);
sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);

HTML là:

<div id="myText" contenteditable>test</div>
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.