Výběr a rozsah

V této kapitole se budeme zabývat výběrem v dokumentu a také výběrem v polích formuláře, jako je <input> .

JavaScript může přistupovat ke stávajícímu výběru, vybrat/zrušit výběr uzlů DOM jako celek nebo částečně, odstranit vybraný obsah z dokumentu, zabalit jej do značky atd.

Některé recepty na běžné úkoly najdete na konci kapitoly v části „Shrnutí“. Možná to pokrývá vaše aktuální potřeby, ale mnohem více získáte, když si přečtete celý text.

Základní Range a Selection předměty jsou snadno uchopitelné a nebudete potřebovat žádné recepty, abyste je přiměli udělat to, co chcete.

Rozsah

Základním konceptem výběru je rozsah, což je v podstatě dvojice „hraničních bodů“:začátek a konec rozsahu.

A Range objekt je vytvořen bez parametrů:

let range = new Range();

Poté můžeme nastavit hranice výběru pomocí range.setStart(node, offset) a range.setEnd(node, offset) .

Jak asi tušíte, dále budeme používat Range objekty pro výběr, ale nejprve vytvořte několik takových objektů.

Částečný výběr textu

Zajímavé je, že první argument node v obou metodách může být textový uzel nebo uzel prvku a na tom závisí význam druhého argumentu.

Pokud node je textový uzel, pak offset musí být pozice v jeho textu.

Například vzhledem k prvku <p>Hello</p> , můžeme vytvořit rozsah obsahující písmena „ll“ následovně:

<p id="p">Hello</p>
<script>
 let range = new Range();
 range.setStart(p.firstChild, 2);
 range.setEnd(p.firstChild, 4);

 // toString of a range returns its content as text
 console.log(range); // ll
</script>

Zde vezmeme prvního potomka <p> (to je textový uzel) a určete pozice textu v něm:

Výběr uzlů prvků

Případně, pokud node je uzel prvku, pak offset musí být číslo dítěte.

To je užitečné pro vytváření rozsahů, které obsahují uzly jako celek a nezastavují se někde uvnitř jejich textu.

Máme například složitější fragment dokumentu:

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

Zde je jeho struktura DOM s uzly elementů i textů:

Udělejme rozsah pro "Example: <i>italic</i>" .

Jak vidíme, tato fráze se skládá z přesně dvou potomků <p> s indexy 0 a 1 :

  • Počáteční bod má <p> jako nadřazený node a 0 jako offset.

    Můžeme jej tedy nastavit jako range.setStart(p, 0) .

  • Koncový bod má také <p> jako nadřazený node , ale 2 jako offset (určuje rozsah až do, ale ne včetně offset ).

    Můžeme jej tedy nastavit jako range.setEnd(p, 2) .

Zde je ukázka. Pokud jej spustíte, uvidíte, že se text vybere:

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

<script>
 let range = new Range();

 range.setStart(p, 0);
 range.setEnd(p, 2);

 // toString of a range returns its content as text, without tags
 console.log(range); // Example: italic

 // apply this range for document selection (explained later below)
 document.getSelection().addRange(range);
</script>

Zde je flexibilnější testovací stojan, kde můžete nastavit počáteční/koncová čísla rozsahu a prozkoumat další varianty:

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

From <input id="start" type="number" value=1> – To <input id="end" type="number" value=4>
<button id="button">Click to select</button>
<script>
 button.onclick = () => {
 let range = new Range();

 range.setStart(p, start.value);
 range.setEnd(p, end.value);

 // apply the selection, explained later below
 document.getSelection().removeAllRanges();
 document.getSelection().addRange(range);
 };
</script>

Např. výběrem ve stejném <p> od offsetu 1 na 4 nám dává rozsah <i>italic</i> and <b>bold</b> :

Počáteční a koncové uzly se mohou lišit

V setStart nemusíme používat stejný uzel a setEnd . Rozsah může zahrnovat mnoho nesouvisejících uzlů. Je pouze důležité, aby konec byl v dokumentu až po začátku.

Výběr většího fragmentu

Udělejme v našem příkladu větší výběr, například takto:

Už víme, jak na to. Potřebujeme pouze nastavit začátek a konec jako relativní offset v textových uzlech.

Potřebujeme vytvořit rozsah, který:

  • začíná od pozice 2 v <p> první dítě (s výjimkou všech prvních písmen „Příklad: ")
  • končí na pozici 3 v <b> první dítě (přičemž první tři písmena „bol d“, ale ne více):
<p id="p">Example: <i>italic</i> and <b>bold</b></p>

<script>
 let range = new Range();

 range.setStart(p.firstChild, 2);
 range.setEnd(p.querySelector('b').firstChild, 3);

 console.log(range); // ample: italic and bol

 // use this range for selection (explained later)
 window.getSelection().addRange(range);
</script>

Jak vidíte, je poměrně snadné vytvořit řadu, co chceme.

Pokud bychom chtěli vzít uzly jako celek, můžeme předat prvky v setStart/setEnd . Jinak můžeme pracovat na úrovni textu.

Vlastnosti rozsahu

Objekt range, který jsme vytvořili ve výše uvedeném příkladu, má následující vlastnosti:

  • startContainer , startOffset – uzel a offset začátku,
    • ve výše uvedeném příkladu:první textový uzel uvnitř <p> a 2 .
  • endContainer , endOffset – uzel a offset konce,
    • ve výše uvedeném příkladu:první textový uzel uvnitř <b> a 3 .
  • collapsed – boolean, true pokud rozsah začíná a končí ve stejném bodě (takže v rozsahu není žádný obsah),
    • ve výše uvedeném příkladu:false
  • commonAncestorContainer – nejbližší společný předek všech uzlů v rozsahu,
    • ve výše uvedeném příkladu:<p>

Metody výběru rozsahu

Existuje mnoho pohodlných metod pro manipulaci s rozsahy.

setStart jsme již viděli a setEnd , zde jsou další podobné metody.

Nastavit začátek rozsahu:

  • setStart(node, offset) nastavit začátek na:pozici offset v node
  • setStartBefore(node) nastavit začátek na:těsně před node
  • setStartAfter(node) nastavit začátek na:hned po node

Nastavit konec rozsahu (podobné metody):

  • setEnd(node, offset) nastavit konec na:pozici offset v node
  • setEndBefore(node) nastavit konec na:těsně před node
  • setEndAfter(node) nastavit konec na:hned za node

Technicky setStart/setEnd může dělat cokoli, ale více metod poskytuje větší pohodlí.

Ve všech těchto metodách node může být textový nebo elementový uzel:pro textové uzly offset přeskočí tolik znaků, zatímco u uzlů prvků tolik podřízených uzlů.

Ještě více metod pro vytváření rozsahů:

  • selectNode(node) nastavit rozsah pro výběr celého node
  • selectNodeContents(node) nastavit rozsah pro výběr celého node obsah
  • collapse(toStart) pokud toStart=true set end=start, jinak set start=end, čímž se rozsah sbalí
  • cloneRange() vytvoří nový rozsah se stejným začátkem/koncem

Metody úpravy rozsahu

Jakmile je rozsah vytvořen, můžeme s jeho obsahem manipulovat pomocí těchto metod:

  • deleteContents() – odebrat obsah rozsahu z dokumentu
  • extractContents() – odebrat obsah rozsahu z dokumentu a vrátit se jako DocumentFragment
  • cloneContents() – klonovat obsah rozsahu a vrátit se jako DocumentFragment
  • insertNode(node) – vložte node do dokumentu na začátek rozsahu
  • surroundContents(node) – zalomit node kolem rozsahu obsahu. Aby to fungovalo, musí rozsah obsahovat otevírací i uzavírací značky pro všechny prvky v něm:žádné částečné rozsahy jako <i>abc .

Pomocí těchto metod můžeme s vybranými uzly dělat v podstatě cokoli.

Zde je testovací stojan, abyste je viděli v akci:

Click buttons to run methods on the selection, "resetExample" to reset it.

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

<p id="result"></p>
<script>
 let range = new Range();

 // Each demonstrated method is represented here:
 let methods = {
 deleteContents() {
 range.deleteContents()
 },
 extractContents() {
 let content = range.extractContents();
 result.innerHTML = "";
 result.append("extracted: ", content);
 },
 cloneContents() {
 let content = range.cloneContents();
 result.innerHTML = "";
 result.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) { console.log(e) }
 },
 resetExample() {
 p.innerHTML = `Example: <i>italic</i> and <b>bold</b>`;
 result.innerHTML = "";

 range.setStart(p.firstChild, 2);
 range.setEnd(p.querySelector('b').firstChild, 3);

 window.getSelection().removeAllRanges();
 window.getSelection().addRange(range);
 }
 };

 for(let method in methods) {
 document.write(`<div><button onclick="methods.${method}()">${method}</button></div>`);
 }

 methods.resetExample();
</script>

Existují také metody pro porovnání rozsahů, ale ty se používají jen zřídka. Až je budete potřebovat, nahlédněte do specifikace nebo manuálu MDN.

Výběr

Range je obecný objekt pro správu rozsahů výběru. I když, vytvoření Range neznamená, že na obrazovce vidíme výběr.

Můžeme vytvořit Range předměty, předávejte je – samy o sobě nic vizuálně nevybírají.

Výběr dokumentu je reprezentován Selection objekt, který lze získat jako window.getSelection() nebo document.getSelection() . Výběr může zahrnovat nula nebo více rozsahů. Alespoň to říká specifikace Selection API. V praxi však pouze Firefox umožňuje vybrat více rozsahů v dokumentu pomocí Ctrl+klik (Cmd+kliknutí pro Mac).

Zde je snímek obrazovky výběru se 3 rozsahy vytvořený ve Firefoxu:

Ostatní prohlížeče podporují maximálně 1 rozsah. Jak uvidíme, některé z Selection metody naznačují, že může existovat mnoho rozsahů, ale opět ve všech prohlížečích kromě Firefoxu je maximálně 1.

Zde je malá ukázka, která ukazuje aktuální výběr (něco vyberte a klikněte) jako text:

Vlastnosti výběru

Jak bylo řečeno, výběr může teoreticky obsahovat více rozsahů. Tyto objekty rozsahu můžeme získat pomocí metody:

  • getRangeAt(i) – získat i-tý rozsah počínaje 0 . Ve všech prohlížečích kromě Firefoxu pouze 0 se používá.

Existují také vlastnosti, které často poskytují lepší pohodlí.

Podobně jako rozsah má objekt výběru začátek, který se nazývá „kotva“ a konec, který se nazývá „focus“.

Hlavní vlastnosti výběru jsou:

  • anchorNode – uzel, kde začíná výběr,
  • anchorOffset – offset v anchorNode kde výběr začíná,
  • focusNode – uzel, kde výběr končí,
  • focusOffset – offset v focusNode kde výběr končí,
  • isCollapsedtrue pokud výběr nevybírá nic (prázdný rozsah) nebo neexistuje.
  • rangeCount – počet rozsahů ve výběru, maximálně 1 ve všech prohlížečích kromě Firefoxu.
Konec/začátek výběru vs rozsah

Ve srovnání s Range existují důležité rozdíly mezi ukotvením/zaměřením výběru začátek/konec.

Jak víme, Range objekty mají vždy svůj začátek před koncem.

U výběrů to není vždy případ.

Výběr něčeho pomocí myši lze provést oběma směry:buď „zleva doprava“ nebo „zprava doleva“.

Jinými slovy, když se stiskne tlačítko myši a poté se posune v dokumentu dopředu, jeho konec (zaměření) bude za jeho začátkem (kotvou).

Např. pokud uživatel začne vybírat myší a přejde z „Příklad“ na „kurzíva“:

…Ale stejný výběr lze provést i zpětně:počínaje „kurzívou“ po „Příklad“ (směr zpět), jeho konec (zaměření) bude před začátkem (kotvou):

Výběr událostí

Na sledování výběru jsou události:

  • elem.onselectstart – když spustí výběr konkrétně na prvek elem (nebo uvnitř něj). Například, když na něm uživatel stiskne tlačítko myši a začne pohybovat ukazatelem.
    • Zabráněním výchozí akci zrušíte začátek výběru. Zahájení výběru z tohoto prvku je tedy nemožné, ale prvek je stále volitelný. Návštěvník musí začít s výběrem odjinud.
  • document.onselectionchange – kdykoli se výběr změní nebo začne.
    • Upozornění:tento obslužný program lze nastavit pouze na document , sleduje všechny výběry v něm.

Ukázka sledování výběru

Zde je malá ukázka. Sleduje aktuální výběr na document a ukazuje jeho hranice:

<p id="p">Select me: <i>italic</i> and <b>bold</b></p>

From <input id="from" disabled> – To <input id="to" disabled>
<script>
 document.onselectionchange = function() {
 let selection = document.getSelection();

 let {anchorNode, anchorOffset, focusNode, focusOffset} = selection;

 // anchorNode and focusNode are text nodes usually
 from.value = `${anchorNode?.data}, offset ${anchorOffset}`;
 to.value = `${focusNode?.data}, offset ${focusOffset}`;
 };
</script>

Ukázka kopírování výběru

Existují dva přístupy ke kopírování vybraného obsahu:

  1. Můžeme použít document.getSelection().toString() získat jako text.
  2. Jinak chcete-li zkopírovat celý DOM, např. pokud potřebujeme pokračovat ve formátování, můžeme získat základní rozsahy pomocí getRangesAt(...) . A Range objekt má zase cloneContents() metoda, která naklonuje jeho obsah a vrátí se jako DocumentFragment objekt, který můžeme vložit jinam.

Zde je ukázka kopírování vybraného obsahu jako textu i jako uzlů DOM:

<p id="p">Select me: <i>italic</i> and <b>bold</b></p>

Cloned: <span id="cloned"></span>
<br>
As text: <span id="astext"></span>

<script>
 document.onselectionchange = function() {
 let selection = document.getSelection();

 cloned.innerHTML = astext.innerHTML = "";

 // Clone DOM nodes from ranges (we support multiselect here)
 for (let i = 0; i < selection.rangeCount; i++) {
 cloned.append(selection.getRangeAt(i).cloneContents());
 }

 // Get as text
 astext.innerHTML += selection;
 };
</script>

Metody výběru

S výběrem můžeme pracovat přidáváním/odebíráním rozsahů:

  • getRangeAt(i) – získat i-tý rozsah počínaje 0 . Ve všech prohlížečích kromě Firefoxu pouze 0 se používá.
  • addRange(range) – přidejte range k výběru. Všechny prohlížeče kromě Firefoxu volání ignorují, pokud má výběr již přidružený rozsah.
  • removeRange(range) – odstranit range z výběru.
  • removeAllRanges() – odebrat všechny rozsahy.
  • empty() – alias na removeAllRanges .

Existují také praktické metody pro přímou manipulaci s rozsahem výběru, bez mezilehlého Range volání:

  • collapse(node, offset) – nahradit vybraný rozsah novým, který začíná a končí na daném node , na pozici offset .
  • setPosition(node, offset) – alias na collapse .
  • collapseToStart() – sbalit (nahradit prázdným rozsahem) na začátek výběru,
  • collapseToEnd() – sbalit na konec výběru,
  • extend(node, offset) – přesunout výběr na daný node , pozice offset ,
  • setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset) – nahradit rozsah výběru daným začátkem anchorNode/anchorOffset a konec focusNode/focusOffset . Je vybrán veškerý obsah mezi nimi.
  • selectAllChildren(node) – vyberte všechny potomky node .
  • deleteFromDocument() – odebrat vybraný obsah z dokumentu.
  • containsNode(node, allowPartialContainment = false) – zkontroluje, zda výběr obsahuje node (částečně, pokud je druhý argument true )

Pro většinu úloh jsou tyto metody v pořádku, není třeba přistupovat k základnímu Range objekt.

Například výběrem celého obsahu odstavce <p> :

<p id="p">Select me: <i>italic</i> and <b>bold</b></p>

<script>
 // select from 0th child of <p> to the last child
 document.getSelection().setBaseAndExtent(p, 0, p, p.childNodes.length);
</script>

Totéž s použitím rozsahů:

<p id="p">Select me: <i>italic</i> and <b>bold</b></p>

<script>
 let range = new Range();
 range.selectNodeContents(p); // or selectNode(p) to select the <p> tag too

 document.getSelection().removeAllRanges(); // clear existing selection if any
 document.getSelection().addRange(range);
</script>
Chcete-li něco vybrat, nejprve odstraňte existující výběr

Pokud již výběr dokumentu existuje, nejprve jej vyprázdněte pomocí removeAllRanges() . A pak přidejte rozsahy. Jinak všechny prohlížeče kromě Firefoxu nové rozsahy ignorují.

Výjimkou jsou některé metody výběru, které nahrazují stávající výběr, například setBaseAndExtent .

Výběr v ovládacích prvcích formuláře

Prvky formuláře, například input a textarea poskytnout speciální API pro výběr, bez Selection nebo Range objektů. Protože vstupní hodnotou je čistý text, nikoli HTML, takové objekty nejsou potřeba, vše je mnohem jednodušší.

Vlastnosti:

  • input.selectionStart – pozice začátku výběru (zapisovatelné),
  • input.selectionEnd – poloha konce výběru (zapisovatelný),
  • input.selectionDirection – směr výběru, jeden z:„dopředu“, „dozadu“ nebo „žádný“ (pokud je například vybráno dvojitým kliknutím myši),

Události:

  • input.onselect – spustí se, když je něco vybráno.

Metody:

  • input.select() – vybere vše v textovém ovládacím prvku (může být textarea místo input ),

  • input.setSelectionRange(start, end, [direction]) – změňte výběr na rozsah z pozice start do end , v daném směru (volitelné).

  • input.setRangeText(replacement, [start], [end], [selectionMode]) – nahradit rozsah textu novým textem.

    Nepovinné argumenty start a end , pokud je k dispozici, nastavte začátek a konec rozsahu, jinak se použije uživatelský výběr.

    Poslední argument, selectionMode , určuje, jak bude výběr nastaven po nahrazení textu. Možné hodnoty jsou:

    • "select" – nově vložený text bude vybrán.
    • "start" – rozsah výběru se sbalí těsně před vloženým textem (kurzor bude těsně před ním).
    • "end" – rozsah výběru se sbalí hned za vložený text (kurzor bude hned za ním).
    • "preserve" – pokusí se zachovat výběr. Toto je výchozí.

Nyní se podívejme na tyto metody v akci.

Příklad:výběr sledování

Tento kód například používá onselect výběr události ke sledování:

<textarea id="area" style="width:80%;height:60px">
Selecting in this text updates values below.
</textarea>
<br>
From <input id="from" disabled> – To <input id="to" disabled>

<script>
 area.onselect = function() {
 from.value = area.selectionStart;
 to.value = area.selectionEnd;
 };
</script>

Poznámka:

  • onselect spouští, když je něco vybráno, ale ne, když je výběr odstraněn.
  • document.onselectionchange událost by se podle specifikace neměla spouštět pro výběry uvnitř ovládacího prvku formuláře, protože nesouvisí s document výběr a rozsahy. Některé prohlížeče jej generují, ale neměli bychom na něj spoléhat.

Příklad:pohyb kurzoru

Můžeme změnit selectionStart a selectionEnd , který nastaví výběr.

Důležitý okrajový případ je, když selectionStart a selectionEnd sobě rovni. Pak je to přesně pozice kurzoru. Nebo pro přeformulování, když není nic vybráno, výběr se sbalí na pozici kurzoru.

Tedy nastavením selectionStart a selectionEnd na stejnou hodnotu přesuneme kurzor.

Například:

<textarea id="area" style="width:80%;height:60px">
Focus on me, the cursor will be at position 10.
</textarea>

<script>
 area.onfocus = () => {
 // zero delay setTimeout to run after browser "focus" action finishes
 setTimeout(() => {
 // we can set any selection
 // if start=end, the cursor is exactly at that place
 area.selectionStart = area.selectionEnd = 10;
 });
 };
</script>

Příklad:úprava výběru

Pro úpravu obsahu výběru můžeme použít input.setRangeText() metoda. Samozřejmě můžeme číst selectionStart/End a se znalostí výběru změňte odpovídající podřetězec value , ale setRangeText je výkonnější a často pohodlnější.

To je poněkud složitá metoda. Ve své nejjednodušší formě s jedním argumentem nahradí uživatelem vybraný rozsah a odstraní výběr.

Například zde bude výběr uživatele zabalen do *...* :

<input id="input" style="width:200px" value="Select here and click the button">
<button id="button">Wrap selection in stars *...*</button>

<script>
button.onclick = () => {
 if (input.selectionStart == input.selectionEnd) {
 return; // nothing is selected
 }

 let selected = input.value.slice(input.selectionStart, input.selectionEnd);
 input.setRangeText(`*${selected}*`);
};
</script>

S více argumenty můžeme nastavit rozsah start a end .

V tomto příkladu najdeme "THIS" ve vstupním textu jej nahraďte a ponechte vybranou náhradu:

<input id="input" style="width:200px" value="Replace THIS in text">
<button id="button">Replace THIS</button>

<script>
button.onclick = () => {
 let pos = input.value.indexOf("THIS");
 if (pos >= 0) {
 input.setRangeText("*THIS*", pos, pos + 4, "select");
 input.focus(); // focus to make selection visible
 }
};
</script>

Příklad:vložit na kurzor

Pokud není vybráno nic, nebo použijeme rovné start a end v setRangeText , pak se nový text pouze vloží, nic se neodstraní.

Můžeme také vložit něco „na kurzor“ pomocí setRangeText .

Zde je tlačítko, které vloží "HELLO" na pozici kurzoru a umístí kurzor hned za něj. Pokud výběr není prázdný, bude nahrazen (můžeme to zjistit porovnáním selectionStart!=selectionEnd a místo toho udělejte něco jiného):

<input id="input" style="width:200px" value="Text Text Text Text Text">
<button id="button">Insert "HELLO" at cursor</button>

<script>
 button.onclick = () => {
 input.setRangeText("HELLO", input.selectionStart, input.selectionEnd, "end");
 input.focus();
 };
</script>

Zrušení výběru

Chcete-li, aby něco nebylo možné vybrat, existují tři způsoby:

  1. Použijte vlastnost CSS user-select: none .

    <style>
    #elem {
     user-select: none;
    }
    </style>
    <div>Selectable <div id="elem">Unselectable</div> Selectable</div>

    To neumožňuje, aby výběr začínal na elem . Uživatel však může začít s výběrem jinde a zahrnout elem do toho.

    Potom elem se stane součástí document.getSelection() , takže k výběru skutečně dojde, ale jeho obsah je při kopírování a vkládání obvykle ignorován.

  2. Zabránit výchozí akci v onselectstart nebo mousedown události.

    <div>Selectable <div id="elem">Unselectable</div> Selectable</div>
    
    <script>
     elem.onselectstart = () => false;
    </script>

    To zabrání zahájení výběru v elem , ale návštěvník jej může začít na jiném prvku a poté rozšířit na elem .

    To se hodí, když je na stejné akci jiný obslužný program události, který spouští výběr (např. mousedown ). Takže deaktivujeme výběr, abychom se vyhnuli konfliktu, a stále povolíme elem obsah ke kopírování.

  3. Můžeme také vymazat výběr post-factum poté, co se to stane s document.getSelection().empty() . To se používá zřídka, protože to způsobí nežádoucí blikání, když se výběr objeví-zmizí.

Odkazy

  • Specifikace DOM:Rozsah
  • Rozhraní API pro výběr
  • Specifikace HTML:Rozhraní API pro výběr ovládacích prvků textu

Shrnutí

Pokryli jsme dvě různá rozhraní API pro výběry:

  1. Pro dokument:Selection a Range objekty.
  2. Pro input , textarea :další metody a vlastnosti.

Druhé API je velmi jednoduché, protože pracuje s textem.

Nejpoužívanější recepty jsou asi:

  1. Získání výběru:
    let selection = document.getSelection();
    
    let cloned = /* element to clone the selected nodes to */;
    
    // then apply Range methods to selection.getRangeAt(0)
    // or, like here, to all ranges to support multi-select
    for (let i = 0; i < selection.rangeCount; i++) {
     cloned.append(selection.getRangeAt(i).cloneContents());
    }
  2. Nastavení výběru:
    let selection = document.getSelection();
    
    // directly:
    selection.setBaseAndExtent(...from...to...);
    
    // or we can create a range and:
    selection.removeAllRanges();
    selection.addRange(range);

A nakonec o kurzoru. Pozice kurzoru v upravitelných prvcích, jako je <textarea> je vždy na začátku nebo na konci výběru. Můžeme jej použít k získání pozice kurzoru nebo k přesunutí kurzoru nastavením elem.selectionStart a elem.selectionEnd .