Výchozí akce prohlížeče

Mnoho událostí automaticky vede k určitým akcím provedeným prohlížečem.

Například:

  • Kliknutí na odkaz – spustí navigaci na jeho adresu URL.
  • Kliknutí na tlačítko odeslání formuláře – zahájí odeslání formuláře na server.
  • Stisknutím tlačítka myši nad textem a jeho posunutím – vyberete text.

Pokud zpracováváme událost v JavaScriptu, nemusíme chtít, aby došlo k odpovídající akci prohlížeče, a místo toho budeme chtít implementovat jiné chování.

Zabránění akcím prohlížeče

Existují dva způsoby, jak prohlížeči sdělit, že nechceme, aby jednal:

  • Hlavním způsobem je použití event objekt. Existuje metoda event.preventDefault() .
  • Pokud je handler přiřazen pomocí on<event> (ne podle addEventListener ), poté vrátí false také funguje stejně.

V tomto HTML kliknutí na odkaz nevede k navigaci; prohlížeč nic nedělá:

<a href="/" onclick="return false">Click here</a>
or
<a href="/" onclick="event.preventDefault()">here</a>

V dalším příkladu použijeme tuto techniku ​​k vytvoření nabídky využívající JavaScript.

Vrací false od handlera je výjimkou

Hodnota vrácená obslužnou rutinou události je obvykle ignorována.

Jedinou výjimkou je return false z handleru přiřazeného pomocí on<event> .

Ve všech ostatních případech return hodnota je ignorována. Konkrétně nemá smysl vracet true .

Příklad:nabídka

Zvažte nabídku webu, jako je tato:

<ul id="menu" class="menu">
 <li><a href="/html">HTML</a></li>
 <li><a href="/javascript">JavaScript</a></li>
 <li><a href="/css">CSS</a></li>
</ul>

Zde je návod, jak to vypadá s některými CSS:

Položky nabídky jsou implementovány jako HTML odkazy <a> , nikoli tlačítka <button> . Existuje několik důvodů, proč tak učinit, například:

  • Mnoho lidí rádo používá „kliknutí pravým tlačítkem“ – „otevřít v novém okně“. Pokud použijeme <button> nebo <span> , to nefunguje.
  • Vyhledávače sledují <a href="..."> odkazy při indexování.

Použijeme tedy <a> v označení. Ale normálně máme v úmyslu zpracovávat kliknutí v JavaScriptu. Měli bychom tedy zabránit výchozí akci prohlížeče.

Jako zde:

menu.onclick = function(event) {
 if (event.target.nodeName != 'A') return;

 let href = event.target.getAttribute('href');
 alert( href ); // ...can be loading from the server, UI generation etc

 return false; // prevent browser action (don't go to the URL)
};

Pokud pomineme return false , poté, co se náš kód spustí, prohlížeč provede svou „výchozí akci“ – přejde na adresu URL v href . A to tady nepotřebujeme, protože klikání řešíme sami.

Mimochodem, pomocí delegování událostí zde je naše nabídka velmi flexibilní. Můžeme přidat vnořené seznamy a stylizovat je pomocí CSS, aby se „stáhly dolů“.

Navazující akce

Některé události plynou jedna do druhé. Pokud zabráníme první události, nebude druhá.

Například mousedown na <input> pole vede k zaostření v něm a focus událost. Pokud zabráníme mousedown událost, není zaměření.

Zkuste kliknout na první <input> níže – focus událost se stane. Pokud však kliknete na druhý, nebude zaměření.

<input value="Focus works" onfocus="this.value=''">
<input onmousedown="return false" onfocus="this.value=''" value="Click me">

Je to proto, že akce prohlížeče je zrušena na mousedown . Zaostření je stále možné, pokud použijeme jiný způsob zadání vstupu. Například Tab pro přepnutí z 1. vstupu na 2. Ale už ne kliknutím myší.

Možnost „pasivního“ obslužného programu

Volitelný passive: true možnost addEventListener signalizuje prohlížeči, že handler nebude volat preventDefault() .

Proč by to mohlo být potřeba?

Existují některé události jako touchmove na mobilních zařízeních (když uživatel pohybuje prstem po obrazovce), které ve výchozím nastavení způsobují posouvání, ale tomuto posouvání lze zabránit pomocí preventDefault() v obslužné rutině.

Takže když prohlížeč takovou událost detekuje, musí nejprve zpracovat všechny obslužné rutiny a poté, pokud preventDefault se nikam nevolá, může pokračovat rolováním. To může způsobit zbytečné zpoždění a „chvění“ v uživatelském rozhraní.

passive: true options sděluje prohlížeči, že handler nezruší rolování. Poté se prohlížeč okamžitě posouvá a poskytuje maximálně plynulý zážitek a událost je mimochodem zpracována.

U některých prohlížečů (Firefox, Chrome) passive je true ve výchozím nastavení pro touchstart a touchmove události.

event.defaultPrevented

Vlastnost event.defaultPrevented je true pokud bylo zabráněno výchozí akci, a false jinak.

Existuje pro to zajímavý případ použití.

Pamatujete si, že v kapitole Probublávání a zachycení jsme mluvili o event.stopPropagation() a proč je špatné zastavit bublání?

Někdy můžeme použít event.defaultPrevented místo toho, aby signalizoval ostatním obslužným rutinám událostí, že událost byla zpracována.

Podívejme se na praktický příklad.

Ve výchozím nastavení je prohlížeč na contextmenu událost (kliknutí pravým tlačítkem myši) zobrazí kontextové menu se standardními možnostmi. Můžeme tomu zabránit a ukázat své vlastní, takto:

<button>Right-click shows browser context menu</button>

<button oncontextmenu="alert('Draw our menu'); return false">
 Right-click shows our context menu
</button>

Nyní bychom kromě této kontextové nabídky chtěli implementovat kontextovou nabídku pro celý dokument.

Po kliknutí pravým tlačítkem by se měla zobrazit nejbližší kontextová nabídka.

<p>Right-click here for the document context menu</p>
<button id="elem">Right-click here for the button context menu</button>

<script>
 elem.oncontextmenu = function(event) {
 event.preventDefault();
 alert("Button context menu");
 };

 document.oncontextmenu = function(event) {
 event.preventDefault();
 alert("Document context menu");
 };
</script>

Problém je, že když klikneme na elem , dostaneme dvě nabídky:nabídku na úrovni tlačítka a (událost se objeví) nabídku na úrovni dokumentu.

jak to opravit? Jedním z řešení je uvažovat takto:„Když zpracováváme klikání pravým tlačítkem v ovladači tlačítka, zastavme jeho bublání“ a použijte event.stopPropagation() :

<p>Right-click for the document menu</p>
<button id="elem">Right-click for the button menu (fixed with event.stopPropagation)</button>

<script>
 elem.oncontextmenu = function(event) {
 event.preventDefault();
 event.stopPropagation();
 alert("Button context menu");
 };

 document.oncontextmenu = function(event) {
 event.preventDefault();
 alert("Document context menu");
 };
</script>

Nyní nabídka na úrovni tlačítek funguje tak, jak má. Ale cena je vysoká. Navždy zakazujeme přístup k informacím o kliknutí pravým tlačítkem pro jakýkoli vnější kód, včetně čítačů, které shromažďují statistiky a tak dále. To je docela nerozumné.

Alternativním řešením by bylo zkontrolovat document handler, pokud bylo zabráněno výchozí akci? Pokud je to tak, událost byla zpracována a my na ni nemusíme reagovat.

<p>Right-click for the document menu (added a check for event.defaultPrevented)</p>
<button id="elem">Right-click for the button menu</button>

<script>
 elem.oncontextmenu = function(event) {
 event.preventDefault();
 alert("Button context menu");
 };

 document.oncontextmenu = function(event) {
 if (event.defaultPrevented) return;

 event.preventDefault();
 alert("Document context menu");
 };
</script>

Nyní také vše funguje správně. Pokud máme vnořené prvky a každý z nich má vlastní kontextovou nabídku, fungovalo by to také. Jen nezapomeňte zkontrolovat event.defaultPrevented v každém contextmenu handler.

event.stopPropagation() a event.preventDefault()

Jak jasně vidíme, event.stopPropagation() a event.preventDefault() (známé také jako return false ) jsou dvě různé věci. Nesouvisí spolu.

Architektura vnořených kontextových nabídek

Existují také alternativní způsoby implementace vnořených kontextových nabídek. Jedním z nich je mít jeden globální objekt s handlerem pro document.oncontextmenu , a také metody, které nám umožňují ukládat do něj další handlery.

Objekt zachytí každé kliknutí pravým tlačítkem, prohlédne si uložené ovladače a spustí příslušný.

Ale pak by každý kus kódu, který chce kontextovou nabídku, měl o tomto objektu vědět a používat jeho nápovědu místo vlastního contextmenu handler.

Shrnutí

Existuje mnoho výchozích akcí prohlížeče:

  • mousedown – spustí výběr (výběr provedete pohybem myši).
  • click na <input type="checkbox"> – zaškrtne/odškrtne input .
  • submit – kliknutím na <input type="submit"> nebo stisknutím klávesy Enter uvnitř pole formuláře způsobí, že k této události dojde, a prohlížeč po ní odešle formulář.
  • keydown – stisknutí klávesy může vést k přidání znaku do pole nebo k jiným akcím.
  • contextmenu – událost se stane po kliknutí pravým tlačítkem, akcí je zobrazení kontextové nabídky prohlížeče.
  • …je jich více…

Všem výchozím akcím lze zabránit, pokud chceme událost zpracovat výhradně pomocí JavaScriptu.

Chcete-li zabránit výchozí akci, použijte buď event.preventDefault() nebo return false . Druhá metoda funguje pouze pro handlery přiřazené on<event> .

passive: true možnost addEventListener sděluje prohlížeči, že akci nebude zabráněno. To je užitečné pro některé mobilní události, například touchstart a touchmove , abyste prohlížeči řekli, že by neměl čekat na dokončení všech obslužných rutin, než bude rolovat.

Pokud bylo zabráněno výchozí akci, hodnota event.defaultPrevented se změní na true , jinak je to false .

Zůstaňte sémantický, nezneužívejte

Technicky vzato, zamezením výchozích akcí a přidáním JavaScriptu můžeme přizpůsobit chování jakýchkoli prvků. Například můžeme vytvořit odkaz <a> fungovat jako tlačítko a tlačítko <button> chovat se jako odkaz (přesměrování na jinou adresu URL nebo tak).

Ale obecně bychom měli zachovat sémantický význam prvků HTML. Například <a> by měl provádět navigaci, nikoli tlačítko.

Kromě toho, že je to „jen dobrá věc“, díky tomu je váš HTML lepší z hlediska přístupnosti.

Také pokud vezmeme v úvahu příklad s <a> , pak si prosím uvědomte:prohlížeč nám umožňuje otevřít takové odkazy v novém okně (kliknutím pravým tlačítkem myši a jinými způsoby). A to se lidem líbí. Ale pokud uděláme, aby se tlačítko chovalo jako odkaz pomocí JavaScriptu a dokonce vypadalo jako odkaz pomocí CSS, pak <a> -Specifické funkce prohlížeče pro něj stále nebudou fungovat.