V této kapitole probereme výbÄr textu v dokumentu i ve formuláÅových polÃch, jako je <input>.
JavaScript umà pÅistupovat k existujÃcÃmu výbÄru, vybÃrat nebo ruÅ¡it výbÄr celých DOM uzlů i jejich ÄástÃ, odstranit vybraný obsah z dokumentu, zabalit jej do znaÄky a podobnÄ.
Návody na nejÄastÄjšà úlohy najdete na konci kapitoly v Äásti âShrnutÃâ. VaÅ¡e aktuálnà potÅeby to možná pokryje, ale pokud si pÅeÄtete celý text, dozvÃte se mnohem vÃc.
Základnà objekty Range a Selection se snadno použÃvajà a nebudete pak potÅebovat žádný návod, jak je pÅinutit udÄlat to, co chcete.
Range
ZákladnÃm konceptem výbÄru je tÅÃda Range (rozsah), což je v zásadÄ dvojice âhraniÄnÃch bodůâ: zaÄátek a konec rozsahu.
Objekt tÅÃdy Range se vytváÅà bez parametrů:
let rozsah = new Range();
Pak můžeme nastavit hranice výbÄru volánÃm rozsah.setStart(uzel, pozice) a rozsah.setEnd(uzel, pozice).
Jak možná tuÅ¡Ãte, pozdÄji použijeme objekty tÅÃdy Range k výbÄru textu, ale napÅed jich nÄkolik vytvoÅme.
ÄásteÄný výbÄr textu
ZajÃmavé je, že prvnà argument uzel v obou metodách může být buÄ textový, nebo elementový uzel, a na tom pak závisà význam druhého argumentu.
Pokud je uzel textový uzel, pak pozice musà být pozice v jeho textu.
Máme-li napÅÃklad element <p>Hello</p>, můžeme vytvoÅit rozsah obsahujÃcà pÃsmena âllâ následovnÄ:
<p id="p">Hello</p>
<script>
let rozsah = new Range();
rozsah.setStart(p.firstChild, 2);
rozsah.setEnd(p.firstChild, 4);
// toString na rozsahu vrátà jeho obsah jako text
console.log(rozsah); // ll
</script>
Zde vezmeme prvnà dÃtÄ uzlu <p> (což je textový uzel) a specifikujeme pozice textu uvnitÅ nÄho:
VýbÄr elementových uzlů
Jestliže uzel je elementový uzel, pak pozice musà být poÅadà jeho dÃtÄte.
To se hodà pro vytváÅenà rozsahů, které obsahujà celé uzly a nezastavà se nÄkde uprostÅed jejich textu.
MÄjme napÅÃklad složitÄjšà fragment dokumentu:
<p id="p">Example: <i>italic</i> and <b>bold</b></p>
Zde je jeho DOM struktura s elementovými i textovými uzly:
VytvoÅme rozsah pro "Example: <i>italic</i>".
Jak vidÃme, tato vÄta se skládá z právÄ dvou dÄtà uzlu <p>, jejichž indexy jsou 0 a 1:
-
PoÄáteÄnà bod má
<p>jako rodiÄovskýuzela jeho pozice je0.Můžeme jej tedy nastavit volánÃm
rozsah.setStart(p, 0). -
Koncový bod má také
<p>jako rodiÄovskýuzel, ale jeho pozice je2(specifikuje, kde má rozsah skonÄit, alepozicenenà zahrnuta).Můžeme jej tedy nastavit volánÃm
rozsah.setStart(p, 2).
Když si spustÃte následujÃcà demo, uvidÃte, že se text vybral:
<p id="p">Example: <i>italic</i> and <b>bold</b></p>
<script>
let rozsah = new Range();
rozsah.setStart(p, 0);
rozsah.setEnd(p, 2);
// toString na rozsahu vracà jeho obsah jako text bez znaÄek
console.log(rozsah); // Example: italic
// tento rozsah aplikujeme na výbÄr v dokumentu (bude pozdÄji vysvÄtleno)
document.getSelection().addRange(rozsah);
</script>
Zde je flexibilnÄjšà zkuÅ¡ebnà pÅÃklad, v nÄmž si můžete nastavit poÄáteÄnà a koncové poÅadà a prozkoumat jiné varianty:
<p id="p">Example: <i>italic</i> and <b>bold</b></p>
Od <input id="poÄátek" type="number" value=1> â Do <input id="konec" type="number" value=4>
<button id="tlaÄÃtko">KliknutÃm vyberete</button>
<script>
tlaÄÃtko.onclick = () => {
let rozsah = new Range();
rozsah.setStart(p, poÄátek.value);
rozsah.setEnd(p, konec.value);
// aplikujeme výbÄr, bude pozdÄji vysvÄtleno
document.getSelection().removeAllRanges();
document.getSelection().addRange(rozsah);
};
</script>
NapÅÃklad výbÄr ve stejném <p> od pozice 1 do 4 nám vydá rozsah <i>italic</i> and <b>bold</b>:
V setStart a setEnd nemusÃme použÃvat tentýž uzel. Rozsah se může táhnout pÅes mnoho navzájem nesouvisejÃcÃch uzlů. Důležité je jen to, aby konec byl v dokumentu až za zaÄátkem.
VýbÄr vÄtÅ¡Ãho fragmentu
UÄiÅme v naÅ¡em pÅÃkladu vÄtšà výbÄr, napÅÃklad:
Už vÃme, jak na to. MusÃme jen nastavit zaÄátek a konec jako relativnà pozice v textových uzlech.
PotÅebujeme vytvoÅit rozsah, který:
- zaÄÃná na pozici 2 v prvnÃm dÃtÄti
<p>(vezme vÅ¡echna pÃsmena âExample: â kromÄ prvnÃch dvou), - konÄà na pozici 3 v prvnÃm dÃtÄti
<b>(vezme prvnà tÅi pÃsmena âboldâ , ale žádné dalÅ¡Ã):
<p id="p">Example: <i>italic</i> and <b>bold</b></p>
<script>
let rozsah = new Range();
rozsah.setStart(p.firstChild, 2);
rozsah.setEnd(p.querySelector('b').firstChild, 3);
console.log(rozsah); // ample: italic and bol
// použijeme tento rozsah pro výbÄr (vysvÄtlÃme pozdÄji)
window.getSelection().addRange(rozsah);
</script>
Jak vidÃte, je docela jednoduché vytvoÅit rozsah, jaký chceme.
Kdybychom chtÄli vybÃrat celé uzly, můžeme do setStart/setEnd pÅedávat elementy. Jinak můžeme pracovat na úrovni textu.
Vlastnosti objektu rozsahu
Objekt rozsahu, který jsme v naÅ¡em pÅÃkladu vytvoÅili, má následujÃcà vlastnosti:
startContainer,startOffsetâ uzel a pozice zaÄátku,- v uvedeném pÅÃkladu: prvnà textový uzel uvnitÅ
<p>a2.
- v uvedeném pÅÃkladu: prvnà textový uzel uvnitÅ
endContainer,endOffsetâ uzel a pozice konce,- v uvedeném pÅÃkladu: prvnà textový uzel uvnitÅ
<b>a3.
- v uvedeném pÅÃkladu: prvnà textový uzel uvnitÅ
collapsedâ booleovská vlastnost,true, jestliže rozsah zaÄÃná a konÄà ve stejném bodÄ (uvnitÅ rozsahu tedy nenà žádný obsah),- v uvedeném pÅÃkladu:
false.
- v uvedeném pÅÃkladu:
commonAncestorContainerâ nejbližšà spoleÄný pÅedek vÅ¡ech uzlů v rozsahu,- v uvedeném pÅÃkladu:
<p>.
- v uvedeném pÅÃkladu:
Metody pro nastavenà rozsahu
K manipulaci s rozsahy sloužà množstvà vhodných metod.
Už jsme vidÄli setStart a setEnd, nynà uvedeme jiné podobné metody.
Nastavenà zaÄátku:
setStart(uzel, pozice)nastavà zaÄátek na pozicipozicevuzelsetStartBefore(uzel)nastavà zaÄátek právÄ pÅeduzelsetStartAfter(uzel)nastavà zaÄátek právÄ zauzel
Nastavenà konce (obdobné metody):
setEnd(uzel, pozice)nastavà konec na pozicipozicevuzelsetEndBefore(uzel)nastavà konec právÄ pÅeduzelsetEndAfter(uzel)nastavà konec právÄ zauzel
Technicky mohou cokoli z toho udÄlat i setStart/setEnd, ale jiné metody jsou pohodlnÄjÅ¡Ã.
Ve vÅ¡ech tÄchto metodách může uzel být jak textový, tak elementový uzel: u textových uzlů pozice pÅeskoÄà uvedený poÄet znaků, zatÃmco u elementových uzlů uvedený poÄet dÄtských uzlů.
Existujà i dalšà metody pro vytvoÅenà rozsahu:
selectNode(uzel)nastavà rozsah tak, aby byl vybrán celýuzelselectNodeContents(uzel)nastavà rozsah tak, aby byl vybrán celý obsah uzluuzelcollapse(naZaÄátek): pokud jenaZaÄátek=true, nastavà konec=zaÄátek, jinak nastavà zaÄátek=konec, ÄÃmž se rozsah smrsknecloneRange()vytvoÅà nový rozsah se stejným zaÄátkem a koncem
Metody pro editaci rozsahu
Když je rozsah vytvoÅen, můžeme s jeho obsahem manipulovat pomocà následujÃcÃch metod:
deleteContents()â odstranà obsah rozsahu z dokumentuextractContents()â odstranà obsah rozsahu z dokumentu a vrátà jej jako DocumentFragmentcloneContents()â naklonuje obsah rozsahu a vrátà jej jako DocumentFragmentinsertNode(uzel)â vložÃuzeldo dokumentu na zaÄátek rozsahusurroundContents(uzel)â zapouzdÅà obsah rozsahu do uzluuzel. Aby to fungovalo, musà rozsah obsahovat otevÃracà i uzavÃracà znaÄku pro vÅ¡echny elementy uvnitÅ: nesmà to být ÄásteÄný rozsah, napÅ.<i>abc.
Pomocà tÄchto metod můžeme s vybranými uzly provádÄt v zásadÄ cokoli.
Zde je zkuÅ¡ebnà pÅÃklad, abyste je vidÄli v akci:
KlikánÃm na tlaÄÃtka spouÅ¡tÄjte metody na výbÄru, âresetPÅÃkladuâ jej resetuje.
<p id="p">Example: <i>italic</i> and <b>bold</b></p>
<p id="výsledek"></p>
<script>
let rozsah = new Range();
// Zde je uvedena každá pÅedvádÄná metoda:
let metody = {
deleteContents() {
rozsah.deleteContents()
},
extractContents() {
let obsah = rozsah.extractContents();
výsledek.innerHTML = "";
výsledek.append("extrahováno: ", obsah);
},
cloneContents() {
let obsah = rozsah.cloneContents();
výsledek.innerHTML = "";
výsledek.append("klonováno: ", obsah);
},
insertNode() {
let novýUzel = document.createElement('u');
novýUzel.innerHTML = "NOVà UZEL";
rozsah.insertNode(novýUzel);
},
surroundContents() {
let novýUzel = document.createElement('u');
try {
rozsah.surroundContents(novýUzel);
} catch(e) { console.log(e) }
},
resetPÅÃkladu() {
p.innerHTML = `Example: <i>italic</i> and <b>bold</b>`;
výsledek.innerHTML = "";
rozsah.setStart(p.firstChild, 2);
rozsah.setEnd(p.querySelector('b').firstChild, 3);
window.getSelection().removeAllRanges();
window.getSelection().addRange(rozsah);
}
};
for(let metoda in metody) {
document.write(`<div><button onclick="metody.${metoda}()">${metoda}</button></div>`);
}
metody.resetPÅÃkladu();
</script>
Existujà i metody pro porovnávánà rozsahů, ale ty se použÃvajà jen zÅÃdka. Kdybyste je potÅebovali, prosÃme obraÅ¥te se na specifikaci nebo manuál MDN.
VýbÄr
Range je obecný objekt pro správu rozsahů výbÄru, ale vytvoÅenà Range neznamená, že uvidÃme výbÄr na obrazovce.
Můžeme objekty Range vytváÅet, pÅedávat â samy o sobÄ vizuálnÄ nic nevybÃrajÃ.
VýbÄr v dokumentu pÅedstavuje objekt tÅÃdy Selection, který můžeme zÃskat pomocà window.getSelection() nebo document.getSelection(). VýbÄr může obsahovat nula nebo vÃce rozsahů. AlespoÅ to tvrdà specifikace API Selection. V praxi vÅ¡ak umožÅuje výbÄr vÃce rozsahů v dokumentu jedinÄ Firefox, a to pomocà Ctrl+kliknutà (na Macu Cmd+kliknutÃ).
Zde je screenshot výbÄru se tÅemi rozsahy, vytvoÅený ve Firefoxu:
Ostatnà prohlÞeÄe podporujà maximálnÄ jeden rozsah. Jak uvidÃme, nÄkteré metody tÅÃdy Selection pÅedpokládajÃ, že rozsahů může být vÃc, ale opakujeme, že ve vÅ¡ech prohlÞeÄÃch s výjimkou Firefoxu je maximálnÄ jeden.
Následuje malé demo, které zobrazà aktuálnà výbÄr (nÄco oznaÄte a kliknÄte) jako text:
Vlastnosti tÅÃdy Selection
Jak bylo ÅeÄeno, výbÄr může teoreticky obsahovat vÃce rozsahů. Objekty tÄchto rozsahů můžeme zÃskat metodou:
getRangeAt(i)â vrátà i-tý rozsah, poÄÃnaje0. Ve vÅ¡ech prohlÞeÄÃch kromÄ Firefoxu se použÃvá pouze0.
TÅÃda má i vlastnosti, jejichž použÃvánà je Äasto pohodlnÄjÅ¡Ã.
PodobnÄ jako rozsah, i objekt výbÄru má zaÄátek, nazývaný âkotvaâ (âanchorâ), a konec, nazývaný âfokusâ (âfocusâ).
Hlavnà vlastnosti výbÄru jsou:
anchorNodeâ uzel, kde výbÄr zaÄÃná,anchorOffsetâ pozice vanchorNode, kde výbÄr zaÄÃná,focusNodeâ uzel, kde výbÄr konÄÃ,focusOffsetâ pozice vefocusNode, kde výbÄr konÄÃ,isCollapsedâtrue, jestliže výbÄr nic neobsahuje (prázdný rozsah) nebo neexistuje,rangeCountâ poÄet rozsahů ve výbÄru, ve vÅ¡ech prohlÞeÄÃch kromÄ Firefoxu maximálnÄ1.
Mezi kotvou/fokusem výbÄru a zaÄátkem/koncem objektu Range je důležitý rozdÃl.
Jak vÃme, objekty Range majà vždy zaÄátek pÅed koncem.
U výbÄrů tomu tak vždy nenÃ.
Vybrat nÄco myšà je možné obÄma smÄry: âzleva dopravaâ nebo âzprava dolevaâ.
Jinými slovy, když stisknete tlaÄÃtko myÅ¡i a pak jà posunujete v dokumentu smÄrem dopÅedu, pak konec výbÄru (fokus) bude za zaÄátkem (kotvou).
NapÅÃklad když uživatel zaÄne vybÃrat myšà a postupuje od âExampleâ k âitalicâ:
â¦Ale stejný výbÄr lze provést i obrácenÄ: zaÄÃt od âitalicâ a postupovat k âExampleâ (smÄrem zpÄt), pak jeho konec (fokus) bude pÅed zaÄátkem (kotvou):
Události výbÄru
NásledujÃcà události umožÅujà sledovat výbÄr:
elem.onselectstartâ když je výbÄr zahájen specificky na elementuelem(nebo uvnitÅ nÄj). NapÅÃklad když na nÄm uživatel stiskne tlaÄÃtko myÅ¡i a zaÄne pohybovat ukazatelem.- Zákaz standardnà akce zrušà zahájenà výbÄru. ZaÄÃt výbÄr tÃmto elementem tedy pÅestane být možné, ale element bude stále možné vybrat. NávÅ¡tÄvnÃk bude jen muset zahájit výbÄr jinde.
document.onselectionchangeâ když je výbÄr zahájen nebo zmÄnÄn.- ProsÃme vÅ¡imnÄte si: tento handler lze nastavit jen na
document, sleduje vÅ¡echny výbÄry na nÄm.
- ProsÃme vÅ¡imnÄte si: tento handler lze nastavit jen na
Demo sledovánà výbÄru
Následuje malé demo, které sleduje aktuálnà výbÄr na document a zobrazà jeho hranice:
<p id="p">Vyberte mÄ: <i>italic</i> and <b>bold</b></p>
Od <input id="výbÄrOd" disabled> â Do <input id="výbÄrDo" disabled>
<script>
document.onselectionchange = function() {
let výbÄr = document.getSelection();
let {anchorNode, anchorOffset, focusNode, focusOffset} = výbÄr;
// anchorNode a focusNode jsou obvykle textové uzly
výbÄrOd.value = `${anchorNode?.data}, index ${anchorOffset}`;
výbÄrDo.value = `${focusNode?.data}, index ${focusOffset}`;
};
</script>
Demo kopÃrovánà výbÄru
KopÃrovat vybraný obsah je možné dvÄma způsoby:
- Můžeme jej pomocÃ
document.getSelection().toString()zÃskat jako text. - Chceme-li zkopÃrovat úplný DOM, tj. chceme zachovat formátovánÃ, můžeme zÃskat pÅÃsluÅ¡né rozsahy pomocÃ
getRangeAt(...). ObjektRangeobsahuje metoducloneContents(), která naklonuje jeho obsah a vrátà jej jako objekt tÅÃdyDocumentFragment, který můžeme vložit jinam.
Následuje demo kopÃrovánà vybraného obsahu jako text i jako DOM uzly:
<p id="p">Vyberte mÄ: <i>italic</i> and <b>bold</b></p>
Klonováno: <span id="klonováno"></span>
<br>
Jako text: <span id="jakoText"></span>
<script>
document.onselectionchange = function() {
let výbÄr = document.getSelection();
klonováno.innerHTML = jakoText.innerHTML = "";
// Naklonujeme DOM uzly z rozsahů (zde podporujeme vÃcenásobný výbÄr)
for (let i = 0; i < výbÄr.rangeCount; i++) {
klonováno.append(výbÄr.getRangeAt(i).cloneContents());
}
// ZÃskáme výbÄr jako text
jakoText.innerHTML += výbÄr;
};
</script>
Metody výbÄru
S výbÄrem můžeme pracovat pomocà pÅidávánà a odstraÅovánà rozsahů:
getRangeAt(i)â vrátà i-tý rozsah, poÄÃnaje0. Ve vÅ¡ech prohlÞeÄÃch kromÄ Firefoxu se použÃvá pouze0.addRange(rozsah)â pÅidározsahdo výbÄru. Jestliže už výbÄr má pÅidÄlený rozsah, vÅ¡echny prohlÞeÄe kromÄ Firefoxu toto volánà ignorujÃ.removeRange(rozsah)â odstranÃrozsahz výbÄru.removeAllRanges()â odstranà vÅ¡echny rozsahy.empty()â totéž jakoremoveAllRanges.
Existujà i pohodlnÄjšà metody, které umožÅujà manipulovat s rozsahem výbÄru pÅÃmo, bez mezilehlého objektu Range:
collapse(uzel, pozice)â nahradà vybraný rozsah novým, který zaÄÃná a konÄà v zadaném uzluuzelna pozicipozice,setPosition(uzel, pozice)â totéž jakocollapse,collapseToStart()â smrskne výbÄr (nahradà jej prázdným rozsahem) na jeho zaÄátek,collapseToEnd()â smrskne výbÄr na jeho konec,extend(uzel, pozice)â pÅesune fokus výbÄru na zadanýuzelna pozicipozice,setBaseAndExtent(kotvaUzel, kotvaPozice, fokusUzel, fokusPozice)â nahradà vybraný rozsah zadaným zaÄátkemkotvaUzel/kotvaPozicea koncemfokusUzel/fokusPozice. VeÅ¡kerý obsah mezi nimi bude vybrán,selectAllChildren(uzel)â vybere vÅ¡echny dÄti uzluuzel,deleteFromDocument()â odstranà vybraný obsah z dokumentu,containsNode(uzel, umožnitÄásteÄnýVýbÄr = false)â ovÄÅÃ, zda výbÄr obsahujeuzel(je-li druhý argumenttrue, staÄÃ, když obsahuje jen jeho Äást).
Pro vÄtÅ¡inu úloh tyto metody vyhovujà a nenà tÅeba pÅistupovat k podkladovému objektu Range.
NapÅÃklad vybereme celý obsah odstavce <p>:
<p id="p">Vyberte mÄ: <i>italic</i> and <b>bold</b></p>
<script>
// výbÄr od 0. dÃtÄte znaÄky <p> do poslednÃho dÃtÄte
document.getSelection().setBaseAndExtent(p, 0, p, p.childNodes.length);
</script>
Totéž za použità rozsahů:
<p id="p">Vyberte mÄ: <i>italic</i> a <b>bold</b></p>
<script>
let rozsah = new Range();
rozsah.selectNodeContents(p); // nebo selectNode(p), chceme-li vybrat i znaÄku <p>
document.getSelection().removeAllRanges(); // zruÅ¡Ãme existujÃcà výbÄr, je-li nÄjaký
document.getSelection().addRange(rozsah);
</script>
Jestliže v dokumentu již nÄjaký výbÄr existuje, napÅed jej vyprázdnÄte volánÃm removeAllRanges() a teprve pak pÅidávejte rozsahy. Jinak budou vÅ¡echny prohlÞeÄe kromÄ Firefoxu nové rozsahy ignorovat.
Výjimkou jsou nÄkteré metody výbÄru, které nahrazujà existujÃcà výbÄr, napÅÃklad setBaseAndExtent.
VýbÄr v ovládacÃch prvcÃch formuláÅů
Elementy formuláÅů, napÅ. input a textarea, poskytujà speciálnà API pro výbÄr, které neobsahuje objekty Selection nebo Range. Protože vstupnà hodnota je Äistý text a nikoli HTML kód, nejsou takové objekty zapotÅebà a vÅ¡echno je pak mnohem snazÅ¡Ã.
Vlastnosti:
input.selectionStartâ pozice zaÄátku výbÄru (zapisovatelná),input.selectionEndâ pozice konce výbÄru (zapisovatelná),input.selectionDirectionâ smÄr výbÄru, jeden z"forward"(dopÅedu),"backward"(dozadu) nebo"none"(žádný, napÅ. pokud byl výbÄr proveden dvojitým kliknutÃm myÅ¡i).
Události:
input.onselectâ spustà se, když je nÄco vybráno.
Metody:
-
input.select()â vybere vÅ¡echen obsah textového ovládacÃho prvku (mÃstoinputmůže býttextarea), -
input.setSelectionRange(zaÄátek, konec, [smÄr])â zmÄnà výbÄr tak, aby se táhl od pozicezaÄátekdo pozicekonecv zadaném smÄru (nepovinný), -
input.setRangeText(náhrada, [zaÄátek], [konec], [režimVýbÄru])â nahradà text v zadaném rozsahu novým textem.Pokud jsou uvedeny nepovinné argumenty
zaÄátekakonec, nastavujà zaÄátek a konec rozsahu, jinak se použije uživatelský výbÄr.Poslednà argument,
režimVýbÄru, stanovuje, jak bude výbÄr nastaven po nahrazenà textu. Možné hodnoty jsou:"select"â bude vybrán novÄ vložený text."start"â rozsah výbÄru se smrskne právÄ pÅed vložený text (kurzor bude hned pÅed nÃm)."end"â rozsah výbÄru se smrskne právÄ za vložený text (kurzor bude hned za nÃm)."preserve"â pokusà se zachovat výbÄr. Tato hodnota je standardnÃ.
PodÃvejme se nynà na tyto metody v akci.
PÅÃklad: sledovánà výbÄru
NapÅÃklad následujÃcà kód sleduje výbÄr pomocà události onselect:
<textarea id="area" style="width:80%;height:60px">
VýbÄr v tomto textu aktualizuje hodnoty pod nÃm.
</textarea>
<br>
Od <input id="výbÄrOd" disabled> â Do <input id="výbÄrDo" disabled>
<script>
area.onselect = function() {
výbÄrOd.value = area.selectionStart;
výbÄrDo.value = area.selectionEnd;
};
</script>
ProsÃme vÅ¡imnÄte si:
- Událost
onselectse spustÃ, když je nÄco vybráno, ale ne tehdy, když je výbÄr odstranÄn. - Událost
document.onselectionchangeby se podle specifikace nemÄla spouÅ¡tÄt pÅi výbÄrech uvnitÅ formuláÅového ovládacÃho prvku, protože ty se nevztahujà k výbÄru a rozsahům vdocument. NÄkteré prohlÞeÄe ji generujÃ, ale nemÄli bychom se na to spoléhat.
PÅÃklad: pohyb kurzoru
Vlastnosti selectionStart a selectionEnd můžeme mÄnit a tÃm nastavovat výbÄr.
Důležitý krajnà pÅÃpad je, když se selectionStart a selectionEnd navzájem rovnajÃ. Pak specifikujà právÄ pozici kurzoru. Nebo, jinak ÅeÄeno, když nenà nic zvoleno, výbÄr je smrsknut na pozici kurzoru.
NastavenÃm selectionStart a selectionEnd na stejnou hodnotu tedy pohybujeme kurzorem.
PÅÃklad:
<textarea id="area" style="width:80%;height:60px">
Když na mÄ vstoupÃte, kurzor bude na pozici 10.
</textarea>
<script>
area.onfocus = () => {
// setTimeout s nulovou prodlevou se spustà poté, co skonÄà akce prohlÞeÄe "focus"
setTimeout(() => {
// můžeme nastavit jakýkoli výbÄr
// bude-li start=end, kurzor se pÅesune právÄ na toto mÃsto
area.selectionStart = area.selectionEnd = 10;
});
};
</script>
PÅÃklad: modifikace výbÄru
K modifikaci výbÄru můžeme použÃt metodu input.setRangeText(). Můžeme samozÅejmÄ naÄÃst selectionStart/End a pÅi znalosti výbÄru zmÄnit odpovÃdajÃcà podÅetÄzec hodnoty value, ale setRangeText je silnÄjšà a Äasto i vhodnÄjÅ¡Ã.
Tato metoda je trochu složitÄjÅ¡Ã. V nejjednoduššà formÄ s jednÃm argumentem nahradà uživatelem vybraný ÅetÄzec a odstranà výbÄr.
NapÅÃklad zde bude uživatelův výbÄr obklopen *...*:
<input id="vstup" style="width:200px" value="Vyberte zde text a stisknÄte tlaÄÃtko">
<button id="tlaÄÃtko">Obklopit výbÄr hvÄzdiÄkami *...*</button>
<script>
tlaÄÃtko.onclick = () => {
if (vstup.selectionStart == vstup.selectionEnd) {
return; // nebylo nic vybráno
}
let výbÄr = vstup.value.slice(vstup.selectionStart, vstup.selectionEnd);
vstup.setRangeText(`*${výbÄr}*`);
};
</script>
DalÅ¡Ãmi argumenty můžeme nastavit zaÄátek a konec rozsahu.
V tomto pÅÃkladu najdeme ve vstupnÃm textu "TOTO", nahradÃme je a ponecháme nahrazujÃcà text vybraný:
<input id="vstup" style="width:200px" value="Nahradit TOTO v textu">
<button id="tlaÄÃtko">Nahradit TOTO</button>
<script>
tlaÄÃtko.onclick = () => {
let pozice = vstup.value.indexOf("TOTO");
if (pozice >= 0) {
vstup.setRangeText("*TOTO*", pozice, pozice + 4, "select");
vstup.focus(); // nastavenÃm fokusu zviditelnÃme výbÄr
}
};
</script>
PÅÃklad: vloženà textu na pozici kurzoru
Jestliže nenà nic vybráno nebo jestliže v setRangeText použijeme stejný zaÄátek a konec, pak bude pouze vložen nový text a nic se neodstranÃ.
Pomocà setRangeText můžeme také nÄco vložit âna pozici kurzoruâ.
NásledujÃcà tlaÄÃtko vložà "AHOJ" na pozici kurzoru a umÃstà kurzor hned za nÄj. Pokud výbÄr nenà prázdný, bude nahrazen (porovnánÃm selectionStart!=selectionEnd to můžeme zjistit a pak mÃsto toho udÄlat nÄco jiného):
<input id="vstup" style="width:200px" value="Text Text Text Text Text">
<button id="tlaÄÃtko">Vložit "AHOJ" na pozici kurzoru</button>
<script>
tlaÄÃtko.onclick = () => {
vstup.setRangeText("AHOJ", vstup.selectionStart, vstup.selectionEnd, "end");
vstup.focus();
};
</script>
Zákaz výbÄru
K tomu, abychom zakázali nÄco vybrat, máme tÅi možnosti:
-
PoužÃt CSS vlastnost
user-select: none.<style> #elem { user-select: none; } </style> <div>Lze vybrat <div id="elem">Nelze vybrat</div> Lze vybrat</div>TÃm se zabránà tomu, aby výbÄr zaÄÃnal na
elem, ale uživatel může zahájit výbÄr jinde aelemdo nÄj zahrnout.Pak se
elemstane souÄástÃdocument.getSelection(), takže k výbÄru ve skuteÄnosti dojde, ale jeho obsah bude pÅi operaci kopÃrovánà a vloženà obvykle ignorován. -
Zakázat standardnà akci v událostech
onselectstartnebomousedown.<div>Lze vybrat <div id="elem">Nelze vybrat</div> Lze vybrat</div> <script> elem.onselectstart = () => false; </script>TÃm se zakáže zahájit výbÄr na
elem, ale návÅ¡tÄvnÃk jej může zahájit na jiném elementu a pak rozÅ¡ÃÅit naelem.To se hodÃ, když je na stejné akci, která spouÅ¡tà výbÄr (napÅ.
mousedown), jiný handler události. TÃm tedy zakážeme výbÄr, abychom se vyhnuli konfliktu, ale stále umožnÃme kopÃrovat obsahelem. -
Můžeme také pomocÃ
document.getSelection().empty()odstranit výbÄr poté, co k nÄmu dojde. To se vÅ¡ak použÃvá zÅÃdka, neboÅ¥ to způsobuje nechtÄné blikánÃ, když se výbÄr objevà a zmizÃ.
Odkazy
- Specifikace DOMu: Range
- API Selection
- Specifikace HTML: API pro výbÄry v textových ovládacÃch prvcÃch
ShrnutÃ
Probrali jsme dvÄ různá API pro výbÄr:
- Pro dokument: objekty
SelectionaRange. - Pro
input,textarea: dalšà metody a vlastnosti.
Druhé uvedené API je velmi jednoduché, neboť pracuje s textem.
PravdÄpodobnÄ nejÄastÄji použÃvané návody jsou:
- ZÃskánà výbÄru:
let výbÄr = document.getSelection(); let klonovaný = /* element, do kterého chceme naklonovat vybrané uzly */; // pak aplikujeme metody Range na výbÄr.getRangeAt(0) // nebo jako zde na vÅ¡echny rozsahy, chceme-li podporovat vÃcenásobný výbÄr for (let i = 0; i < výbÄr.rangeCount; i++) { klonovaný.append(výbÄr.getRangeAt(i).cloneContents()); } - Nastavenà výbÄru:
let výbÄr = document.getSelection(); // pÅÃmo: výbÄr.setBaseAndExtent(...od...do...); // nebo můžeme vytvoÅit rozsah a: výbÄr.removeAllRanges(); výbÄr.addRange(rozsah);
A nakonec ke kurzoru. Pozice kurzoru v editovatelných prvcÃch, napÅ. <textarea>, je vždy na zaÄátku nebo na konci výbÄru. Pomocà výbÄru můžeme zjistit pozici kurzoru nebo pÅemÃstit kurzor nastavenÃm elem.selectionStart a elem.selectionEnd.
KomentáÅe
<code>, pro nÄkolik Åádků je obalte znaÄkou<pre>, pro vÃce než 10 Åádků vložte odkaz na pÃskoviÅ¡tÄ (plnkr, jsbin, codepenâ¦)