Selection and Range

JavaScript Selection and Range (JavaScript选择和范围)

In this chapter, we are going to explore selection in the document and in form fields such as <input>.

JavaScript is capable of getting the existing selection, selecting or deselecting partially or totally, and so on. (JavaScript能够获得现有的选择,部分或全部选择或取消选择,依此类推。)

Range

Range (范围)

Range is the basic concept of selection. It encompasses a pair of boundary points such as range start and range end. (范围是选择的基本概念。它包含一对边界点,例如范围开始和范围结束。)

Every point is represented as a parent DOM node along with the relative offset from the beginning. Once the parent node is an element node, the offset will be a child number. (每个点都表示为父DOM节点,以及从一开始的相对偏移量。一旦父节点是一个元素节点,偏移量将是一个子数字。)

Let’s start at creating a range, like this:

let r = new Range();

The next step is setting the selection of boundaries with range.setStart(node, offset) and range.setEnd(node, offset) . (下一步是使用range.setStart (node, offset)和range.setEnd (node, offset)设置边界的选择。)

A fragment of HTML will look as follows:

<p id="pId">Example:<b>bold</b> and <i>italic</i></p>

While selecting “Example: <b>bold</b>”, the two first children of <p> will be:

<!DOCTYPE html>
<html>
 <head>
   <title>Title of the Document</title>
 </head>
 <body>
   <p id="pId">Example: <b>bold</b> and <i>italic</i></p>
   From
(�?)
   <input id="start" type="number" value=0> – To
   <input id="end" type="number" value=2>
   <button id="buttonID">Click to select</button>
   <script>
     const button = document.getElementById('buttonID')
(const button = document.getElementById ('buttonID'))
     button.onclick = () => {
       let range = new Range();
       range.setStart(pId, Number(document.getElementById('start').value));
       range.setEnd(pId, Number(document.getElementById('end').value));
       // apply a selection explained later
(//应用稍后解释的选择)
       document.getSelection().removeAllRanges();
       document.getSelection().addRange(range);
     };
   </script>
 </body>
</html>

Now, let’s see a more flexible test stand:

<!DOCTYPE html>
<html>
 <head>
   <title>Title of the Document</title>
 </head>
 <body>
   <p id="pId">Example: <b>bold</b> and <i>italic</i></p>
   From
(�?)
   <input id="start" type="number" value=0> – To
   <input id="end" type="number" value=5>
   <button id="buttonID">Click to select</button>
   <script>
     button.onclick = () => {
       let range = new Range();
       range.setStart(pId, start.value);
       range.setEnd(pId, end.value);
       // apply a selection explained later
(//应用稍后解释的选择)
       document.getSelection().removeAllRanges();
       document.getSelection().addRange(range);
     };
   </script>
 </body>
</html>

For example, if you select from 1 to 4, it will give the range: <i>italic</i> and <b>bold</b>.

Note that the same node shouldn’t be used in both setStart and setEnd. The most important thing is that the end should be after the start. (请注意,同一个节点不应同时用于setStart和setEnd。最重要的是,结束应该在开始之后。)

Selecting Text Nodes Parts

The partial selection of the text will look as follows:

<p id="pId">Example:<b>bold</b> and <i>italic</i></p>

It is necessary to generate a range, which will start from position 2 in the first child of <p> ( it takes all except the two first letters of “Instance”).

Then, it should end at the position 3 in the first child of <i> (takes the first three letters: “italic”).

Here is how the example looks like:

<!DOCTYPE html>
<html>
 <head>
   <title>Title of the Document</title>
 </head>
 <body>
   <p id="pId">Example: <i>italic</i> and <b>bold</b></p>
   <script>
     let range = new Range();
     range.setStart(pId.firstChild, 1);
     range.setEnd(pId.querySelector('b')
(range.setEnd (pId.querySelector ('b'))
       .firstChild, 3);
     alert(range); // xample: bold and italic
     // use this range for selection, explained later
(//使用此范围进行选择,稍后解释)
     window.getSelection().addRange(range);
   </script>
 </body>
</html>

Range Methods

Range Methods (范围方法)

Multiple convenience methods exist for manipulating ranges. (存在多种操作范围的便利方法。)

Set range start includes:

  • setStart(node, offset) (- setStart (node, offset))

  • setStartBefore(node) (- setStartBefore (节点))

  • setStartAfter(node) (- setStartAfter (节点))

Similar methods are included in set range end:

  • setEnd(node, offset) (- setEnd (node, offset))

  • setEndBefore(node) (- setEndBefore (节点))

  • setEndAfter(node) (- setEndAfter (节点))

Node can be both an element node or a text. Offset skips that many characters for text nodes. For element nodes, it skips that many child nodes. (节点可以是元素节点或文本。偏移跳过文本节点的多个字符。对于元素节点,它跳过那么多子节点。)

For manipulating within the range, you can use the following methods:

  • Removal of the range content from the document can be implemented with deleteContents(). (-可以使用deleteContents ()从文档中删除范围内容。)

  • Removal of the range content from the document and returning as DocumentFragment - with extractContents(). (-从文档中删除范围内容并作为DocumentFragment返回-带有extractContents ()。)

  • Cloning the range content and returning as DocumentFragment - with cloneContents(). (-使用cloneContents ()克隆范围内容并作为DocumentFragment返回。)

  • Inserting node into the document at the range start - with insertNode(node). (-使用insertNode (node)将节点插入到范围开始处的文档中。)

  • Wrapping node around the range content - with surroundContents(node). (-使用surroundContents (node)将节点包裹在范围内容周围。)

The methods above will allow you to do basically anything inside the selected nodes. (上述方法基本上允许您在所选节点内执行任何操作。)

Here is how they will look like in action:

<!DOCTYPE html>
<html>
 <head>
   <title>Title of the Document</title>
 </head>
 <body>
   //Click the buttons to start the selection methods, “resetExample” to reset it.
(//单击按钮启动选择方法,单击“resetExample”重置。)
   <p id="pId">Example: <b>bold</b> and <i>italic</i></p>
   <p id="resultId"></p>
   <script>
     let range = new Range();
     // Each illustrated method is shown here:
     let methods = {
       deleteContents() {
           range.deleteContents()
(range.deleteContents ())
         },
         extractContents() {
           let content = range.extractContents();
           resultId.innerHTML = "";
           resultId.append("extracted: ", content);
         },
         cloneContents() {
           let content = range.cloneContents();
           resultId.innerHTML = "";
           resultId.append("cloned: ", content);
         },
         insertNode() {
           let newNode = document.createElement('u');
           newNode.innerHTML = "New Node";
           range.insertNode(newNode);
         },
         surroundContents() {
           let newNode = document.createElement('u');
           try {
             range.surroundContents(newNode);
           } catch(e) {
             alert(e)
(alert (e))
           }
         },
         resetExample() {
           pId.innerHTML = `Example:  <b>bold</b> and <i>italic</i>`;
           resultId.innerHTML = "";
           range.setStart(pId.firstChild, 1);
           range.setEnd(pId.querySelector('i').firstChild, 3);
           window.getSelection().removeAllRanges();
           window.getSelection().addRange(range);
         }
     };
     for(let m in methods) {
       document.write(`<div><button onclick="methods.${m}()">${m}</button></div>`);
     }
     methods.resetExample();
   </script>
 </body>
</html>

Selection

Selection (选择)

Range is a generic object that helps to manage selection ranges. Objects like that can be generated, passed around, but they can’t select anything visually on their own. (范围是一个通用对象,有助于管理选择范围。像这样的对象可以生成、传递,但它们不能自己在视觉上选择任何东西。)

The document selection can be carried out with the Selection object. It can be obtained as window.getSelection() or document.getSelection(). (文档选择可以使用选择对象进行。它可以作为window.getSelection ()或document.getSelection ()获取。)

Any selection can contain zero or more selections. (任何选择都可以包含零个或多个选择。)

Selection Properties

Selection Properties (选择属性)

Like a range, a selection can have a start (known as “anchor”) and an end (known as “focus”). (与范围一样,选择可以具有开始(称为“锚点” )和结束(称为“焦点” )。)

The primary selection properties include:

  • anchorNode

  • anchorOffset

  • focusNode

  • focusOffset

  • rangeCount

There are multiple ways of selecting the content. It depends on the user agent: hotkeys, mouse, and so on.

For example, a mouse allows creating the same selection in two directions: right-to-left and left-to-right.

In case the start (anchor) goes in the document before the end (focus), that kind of selection has a forward direction. (如果开始(锚点)在结束(焦点)之前进入文档,则这种选择具有前进方向。)

So, this is the most essential difference between the selection and range. For range objects, the start can never be after the end. (因此,这是选择和范围之间最本质的区别。对于范围对象,开始不能在结束之后。)

Selection Events

Selection Events (甄选活动)

For keeping track of selection are used events such as:

  • elem.onselectstart: it occurs when a selection begins with elem. The selection won’t start if the default action is prevented.

  • document.onselectionchange: occurs at any time selection is changed. An important note: you can set the handler merely on the document.

Selection Tracking Demo

Let’s check out a demo that shows the dynamic changes of the selection boundaries:

<!DOCTYPE html>
<html>
 <head>
   <title>Title of the Document</title>
 </head>
 <body>
   <p id="pId">Select: <b>bold</b> and <i>italic</i> </p>
   From
(�?)
   <input id="fromId" disabled> – To
   <input id="toId" disabled>
   <script>
     document.onselectionchange = function() {
       let {
         anchorNode, anchorOffset, focusNode, focusOffset
(anchorNode、anchorOffset、focusNode、focusOffset)
       } = document.getSelection();
       from.value = `${anchorNode && anchorNode.data}:${anchorOffset}`;
       to.value = `${focusNode && focusNode.data}:${focusOffset}`;
     };
   </script>
 </body>
</html>

Selection Getting Demo

Now, let’s see what is necessary to do for getting the whole selection. First of all, as text, it is required to call document.getSelection().toString(). Then, as DOM, to receive the underlined ranges, calling the cloneContents() method. (现在,让我们看看需要做些什么才能获得整个选择。 首先,作为文本,需要调用document.getSelection () .toString ()。 然后,作为DOM ,接收带下划线的范围,调用cloneContents ()方法。)

Here is how the demo looks like:

<!DOCTYPE html>
<html>
 <head>
   <title>Title of the Document</title>
 </head>
 <body>
   <p id="p">Select: <b>bold</b> and <i>italic</i></p>
   Clone: <span id="cloneId"></span>
   <br> Text: <span id="textId"></span>
   <script>
     document.onselectionchange = function() {
       let select = document.getSelection();
       cloneId.innerHTML = textId.innerHTML = "";
       // Clone DOM nodes from ranges (multiple selection is supported here)
(//从范围克隆DOM节点(此处支持多选))
       for(let i = 0; i < select.rangeCount; i++) {
         cloneId.append(select.getRangeAt(i).cloneContents());
       }
       // Get as text
(//以文本形式获取)
       textId.innerHTML += select;
     };
   </script>
 </body>
</html>

Selection Methods

Selection Methods (选择方法)

It’s better to start at the methods for adding and removing ranges. Among them are:

  • getRangeAt(i) (- getRangeAt (i))

  • addRange(range) (- addRange (range))

  • removeRange(range) (- removeRange (range))

  • removeAllRanges() (- removeAllRanges ())

  • empty() (- 空缺 -)

There exist methods for manipulating the selection range without using Range. Here they are:

  • collapse(node, offset) (- collapse (node, offset))

  • setPosition(node, offset) (- setPosition (node, offset))

  • collapseToStart() (- collapseToStart ())

  • collapseToEnd() (- collapseToEnd ())

  • extend(node,offset) (- extend (node, offset))

  • setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset) (- setBaseAndExtent (anchorNode, anchorOffset, focusNode, focusOffset))

  • selectAllChildren(node) (- selectAllChildren (节点))

  • deleteFromDocument() (- deleteFromDocument ())

  • containsNode(node, allowPartialContainment = false) (- containsNode (node, allowPartialContainment = false))

The Selection methods can be called for different tasks without even using the underlying Range object. (即使不使用基础Range对象,也可以为不同的任务调用Selection方法。)

Let’s try to select the <p> paragraph’s whole contents:

<!DOCTYPE html>
<html>
 <head>
   <title>Title of the Document</title>
 </head>
 <body>
   <p id="pId">Select: <b>bold</b> and <i>italic</i></p>
   <script>
     // select from the <p> 0th child  to the last child
     document.getSelection().setBaseAndExtent(pId, 0, pId, pId.childNodes.length);
   </script>
 </body>
</html>

The same action can be taken with ranges, too:

<!DOCTYPE html>
<html>
 <head>
   <title>Title of the Document</title>
 </head>
 <body>
   <p id="pId">Select: <b>bold</b> and <i>italic</i></p>
   <script>
     let r = new Range();
     r.selectNodeContents(pId); // or selectNode(p) to select the <p> tag
     document.getSelection().removeAllRanges(); // clear existing selection 
     document.getSelection().addRange(r);
   </script>
 </body>
</html>

Selection in Form Controls

Selection in Form Controls (表单控件中的选择)

For the form elements such as textarea and input, there is a specific API for selection. It allows acting without using the Range and Selection objects. There is no need in those objects, as the input value is a pure text. (对于textarea和input等表单元素,有一个特定的选择API。它允许在不使用Range和Selection对象的情况下操作。这些对象没有必要,因为输入值是纯文本。)

The properties are input.selectionStart, input.selectionEnd, and input.selectionDirection. (属性为input.selectionStart、input.selectionEnd和input.selectionDirection。)

Once something is selected the input.onselect event occurs. (一旦选择了某些内容,就会发生input.onselect事件。)

Now, let’s get to the methods:

  • input.select()– selecting everything inside the text control (- input.select () –选择文本控件内的所有内容)

  • input.setSelectionRange(start, end, [direction]) – changing the selection for spanning from position start until the end, in the specific direction. (- input.setSelectionRange (start, end, [direction]) –在特定方向上更改从位置开始到结束的选择。)

  • input.setRangeText(replacement, [start], [end], [selectionMode]) – replacing a part of text the with the new one. (- input.setRangeText (replacement, [start], [end], [selectionMode]) –将部分文本替换为新文本。)

The selectionMode argument specifies how the selection is going to be set after replacing the text. Here are the main possible values: “select”, “start”, “end”, and “preserve”.

Further, you can find the methods above in action. (此外,您可以找到上述方法的实际操作。)

Tracking Selection (Example)

For tracking selection, the following code applies onselect, like this:

<!DOCTYPE html>
<html>
 <head>
   <title>Title of the Document</title>
 </head>
 <body>
   <textarea id="txtId" style="width:60%;height:50px">
     The selection in this text updates values below.
(此文本中的选择更新了下面的值。)
   </textarea>
   <br> From
   <input id="from" disabled> – To
   <input id="to" disabled>
   <script>
     textId.onselect = function() {
       from.value = txtId.selectionStart;
       to.value = txtId.selectionEnd;
     };
   </script>
 </body>
</html>

Here, there are specific facts to be emphasized:

  • the onselect event occurs when something is selected but not when it’s removed. (-选择某些内容时发生onselect事件,但删除该内容时不发生onselect事件。)

  • the document.onselectionchange event may not occur for selections within a form control. (-表单控件内的选择可能不会发生document.onselectionchange事件。)

Moving Cursor (Example)

When you set selectionStart and selectionEnd, the cursor is moved. (设置selectionStart和selectionEnd时,光标将移动。)

For instance:

<!DOCTYPE html>
<html>
 <head>
   <title>Title of the Document</title>
 </head>
 <body>
   <textarea id="txtId" style="width:60%;height:50px">
     Focus on me, the cursor will be at position 5.
(专注在我身上,光标将位于5号位置。)
   </textarea>
   <script>
     textId.onfocus = () => {
       // zero delay setTimeout to run after browser action "focus"  finishes
(//浏览器操作“focus”完成后运行的零延迟setTimeout)
       setTimeout(() => {
         // if start=end, the cursor is in this place
(//如果start = end ,则光标位于此位置)
         txtId.selectionStart = txtId.selectionEnd = 5;
       });
     };
   </script>
 </body>
</html>

Modifying Selection (Example)

The input.setRangeText() method is used for modifying the selection content. However, it is a complicated method. It can replace the user selected range and remove the selection in its simplest one-argument form. (Input.setRangeText ()方法用于修改选择内容。 然而,这是一个复杂的方法。 它可以替换用户选择的范围,并以最简单的单参数形式删除所选内容。)

Here is how it will look like:

<!DOCTYPE html>
<html>
 <head>
   <title>Title of the Document</title>
 </head>
 <body>
   <input id="inputId" style="width:200px" value="Select here and click the button">
   <button id="buttonId">Selection in stars /*...*/</button>
   <script>
     buttonId.onclick = () => {
       if(inputId.selectionStart == inputId.selectionEnd) {
         return; // nothing is selected
       }
       let selected = inputId.value.slice(inputId.selectionStart, inputId.selectionEnd);
       inputId.setRangeText(`/*${selected}*/`);
     };
   </script>
 </body>
</html>

Insert at Cursor (Example)

In case, you have inserted nothing or apply start and end within setRangeText , the new text will be inserted without the removal of anything. Also, you can use setRangeText .for inserting something at the cursor. Let’s take a look at an example:

<!DOCTYPE html>
<html>
 <head>
   <title>Title of the Document</title>
 </head>
 <body>
   <input id="inputId" style="width:200px" value="Text Text Text Text Text">
   <button id="buttonId">Insert "WELCOME" at cursor</button>
   <script>
     buttonId.onclick = () => {
       inputId.setRangeText("WELCOME", inputId.selectionStart, inputId.selectionEnd, "end");
       inputId.focus();
     };
   </script>
 </body>
</html>

Summary

Summary (概要)

In this chapter, we demonstrated two APIs that are used for selection. Within the framework of the first one we explored Selection and Range objects used for document. Several additional methods covered in this chapter are used for textarea and input . (在本章中,我们演示了两个用于选择的API。 在第一个框架中,我们探讨了用于文档的选择和范围对象。 本章涵盖的几种其他方法用于文本区域和输入。)

The second API is simpler and is used mainly for texts. The most commonly used receipts are considered getting the selection and setting the selection. And, finally, we learn about the cursor. Its position in <textarea> is constantly either at the start or at the end of the selection. It can be used for getting the cursor position or moving the cursor with elem.selectionStart and elem.selectionEnd .



请遵守《互联网环境法规》文明发言,欢迎讨论问题
扫码反馈

扫一扫,反馈当前页面

咨询反馈
扫码关注
返回顶部