Lekce 11 - Dokončení editoru tabulek v JavaScriptu
V minulé lekci, Editor tabulek v JavaScriptu, jsme rozpracovali editor tabulek, který umožní uživateli tabulky rozšiřovat a upravovat.
V dnešním JavaScript tutoriálu si dáme velkou porci manipulace s elementy DOM. Budeme vkládat sloupce a řádky před, za, nad, pod a budeme je i mazat.
Index řádku aktivní buňky
Abychom si techniky "orientování se" v proměnných DOM osvojili,
napíšeme si 2 funkce, které později využijeme. Jednou z nich je funkce
indexRadkuAktivniBunky()
, která vrátí index řádku (pořadí od
nuly), kde se nachází uživatelem vybraná buňky (aktivniBunka
).
Nejprve si je třeba uvědomit, kde tento index získáme. Hledáme mezi řádky
tabulky. Tabulka je představována elementem <table>
a
řádky jsou ihned v něm (buňky jsou až v řádcích, což může mást,
dávejte si na to pozor).
Kde budeme hledat: tabulka.childNodes
A co hledáme, bude to trochu složitější. Hledáme řádek, ale my víme,
že v aktivniBunka
není element <td>
, ale
<input>
, který je v <td>
. Abychom se
dostali na <td>
, musíme z buňky dostat
parentElement
a ani to nám nebude stačit. Nepotřebujeme buňku,
ale řádek a ten opět získáme pomocí parentElement
.
Co hledáme:
aktivniBunka.parentElement.parentElement
NodeList
a indexOf()
Jestli si vzpomínáte, tak v minulé JS lekci jsem zmínil, že funkce
s DOM nikdy nevrací pole, ale NodeList
nebo podobné
specializované kolekce. A že tyto objekty jsou jen (prakticky) hloupější
pole. Bohužel NodeList
nemá metodu indexOf()
, která
by nám vrátila potřebný index prvku. Jsou dvě varianty. První je, že si
ho cyklem proiterujeme sami a druhá je, že použijeme metodu
indexOf()
na objektu Array
a zavoláme ji s kontextem
NodeListu
. Druhou část předchozího souvětí důkladně
pochopíte u objektově orientovaného programování. Zatím nám stačí
vědět, že zavoláme indexOf()
na poli, ale "podvrhneme" mu
NodeList
. JavaScriptu je totiž jedno s čím pracuje, pokud to
má vše co má mít a NodeList
tuto vlastnost splňuje. Metody se
volají se změněným kontextem tak, že je najdeme v prototypu objektu (v
našem případě Array.prototype
) a zavoláme na ně metodu
call()
. Jako první parametr ji předáme nový kontext a za ně
přidáme parametry metody.
Výsledný kód by tak mohl vypadat následovně:
function indexRadkuAktivniBunky() { let cilHledani = tabulka.childNodes; let hledanyPrvek = aktivniBunka.parentElement.parentElement; return Array.prototype.indexOf.call(cilHledani, hledanyPrvek); }
V cilHledani
máme to, v čem hledáme. V
hledanyPrvek
máme co hledáme. V cilHledani
je
NodeList
a hledanyPrvek
je element
<tr>
. Poté vrátíme hodnotu, kterou vrátí
indexOf()
, načež ji změníme kontext na NodeList
a
jako parametr, který má běžně, předáme onen element
<tr>
.
Index sloupce aktivní buňky
O něco jednodušší to bude se získáváním indexu sloupce aktivní
buňky. Potřebujeme se dostat k řádku, kde se buňka nachází a získat
její pozici. Buňku opět dostaneme pomocí parentElement
na
aktivniBunka
a k řádku, kterému náleží přes
parentElement.parentElement
:
function indexSloupceAktivniBunky() { let bunkyVRadku = aktivniBunka.parentElement.parentElement.childNodes; let td = aktivniBunka.parentElement; return Array.prototype.indexOf.call(bunkyVRadku, td); }
Doteď to možná bylo trochu nudné, protože se nic zajímavého nedělo. Nyní to však bude zajímavější. Již konečně začneme programovat funkce pro ovládání aplikace.
Přidání řádku nad vybraný
Metodu, která vytváří nový řádek (element <tr>
),
máme již hotovou. Umíme získat index řádku, na kterém leží vybraná
buňka, a umíme nový řádek i vložit. Pro přehlednost si nový řádek
uložíme do proměnné, index řádku stejně tak a opět se budeme
prokousávat DOMem. Řádky (<tr>
) jsou vnořené přímo
v elementu tabulky (<table>
), takže to bude jednoduché.
Metodu insertBefore()
voláme na tabulce. První parametr je
jasný, to je nový řádek. Druhý parametr musí být <tr>
,
ve kterém je vložena aktivní buňka. Dále se propracovávat nemusíme,
řádky jsou přímo v tabulce, proto rovnou z NodeListu
vybereme
řádek podle indexu řádku aktivní buňky:
function pridejRadekNahoru() { let radek = vytvorRadek(); let indexVybraneho = indexRadkuAktivniBunky(); tabulka.insertBefore(radek, tabulka.childNodes[indexVybraneho]); }
Ve funkci, která vytváří ovládací tlačítka, obslužme tlačítku pro
přidání řádku nahoru událost onclick
:
vytvorTlacitkoAVlozHo("Přidat řádek nahoru", document.body).onclick = pridejRadekNahoru;
Nyní se pustíme do trochu silnější kávy, ale vlastně to nebude nic nového.
Přidávání řádku za vybraný
Zde budeme postupovat prakticky stejně, ale doplatíme opět na to, že
JavaScript neobsahuje žádnou metodu insertAfter()
a my budeme
muset řádek vkládat před aktuální + 1, abychom ho dostali na správnou
pozici. Pamatujme, že u toho ještě musíme ošetřovat, zda takto
nevkládáme za poslední, protože řádek poslední + 1 neexistuje. Použijeme
podmínku ke zjištění, zda je poslední element tabulky
(tabulka.lastChild
) roven řádku, ve kterém je vybraná
aktuální buňka. Pokud je, tak vkládáme nakonec jednoduše metodou
appendChild()
. V opačném případě vložíme řádek pomocí
insertBefore()
před řádek za řádkem s vybranou buňkou:
function pridejRadekDolu() { let radek = vytvorRadek(); let indexVybraneho = indexRadkuAktivniBunky(); if (tabulka.lastChild == tabulka.childNodes[indexVybraneho]) { tabulka.appendChild(radek); } else { tabulka.insertBefore(radek, tabulka.childNodes[indexVybraneho + 1]); } }
Nastavte obsluhu události danému tlačítku:
vytvorTlacitkoAVlozHo("Přidat řádek dolů", document.body).onclick = pridejRadekDolu;
Nyní již umíme přidávat řádky různě do tabulky, kde se nám hodí. Funkcionalitu si můžete vyzkoušet v živé ukázce níže, nezapomeňte ovšem předtím označit nějakou buňku tabulky kliknutím do ní:
Přidání sloupce vlevo
Přidání sloupce již bude složitější. Tabulka v HTML totiž obsahuje řádky a v těch jsou buňky. A ejhle, v kódu nejsou žádné sloupce! Přidání sloupce budeme muset tedy řešit tak, že přidáme buňku do existujících řádků. Cyklem projedeme všechny řádky tabulky a do každého řádku před index vybrané buňky vložíme novou buňku.
Nyní je třeba si uvědomit co je kde a co budeme kde potřebovat. Budeme
vkládat do řádku <tr>
a ten je v
<table>
. Výraz tabulka.childNodes
vrací pole
řádků <tr>
a my musíme vkládat v cyklu do každého
z nich novou buňku.
Kam vkládáme: tabulka.childNodes[i]
Vkládáme novou buňku.
Co vkládáme: vytvorBunku()
A teď před co. Referenční element (ten, před který vkládáme) musí
být uvnitř elementu, kam vkládáme. Jednoduše tedy začneme tím, že
předáme ten samý element, do kterého vkládáme. Protože je to element
zevnitř, logicky ho nalezneme uvnitř NodeList
u
childNodes
a víme, že tento childNodes
je pole
prvků buněk <td>
(Hurá! Ty hledáme!). Index tedy (který
je pro daný cyklus konstantní) bude onen index buňky mezi sloupci. Pokud jste
se ve výkladu ztratili, zkuste si to projít níže v kódu.
Před co vkládáme:
tabulka.childNodes[i].childNodes[indexVybraneho]
Celé to bude vypadat následovně:
function pridejSloupecDoleva() { let indexVybraneho = indexSloupceAktivniBunky(); for (let i = 0; i < tabulka.childNodes.length; i++) { tabulka.childNodes[i].insertBefore(vytvorBunku(), tabulka.childNodes[i].childNodes[indexVybraneho]); } }
Pokud vám to přišlo složité nebo nepřehledné, tak než se pustíte do další části (protože ta je k orientaci ještě o něco těžší), doplňte si vhodně komentáře nebo si kód rozdělte do rozumně pojmenovaných proměnných.
Přidání sloupce vpravo
Jak již asi tušíte, bude to podobné, pouze se musíme opět vyrovnat s
chybějící metodou insertAfter()
jako jsme to udělal v metodě
pridejRadekDolu()
. Jediným rozdílem (opět) je, že musíme
ověřit, že nevkládáme na konec. Logicky dojdeme k tomu, že ověřujeme
vždy to, co vkládáme s posledním elementem rodiče ověřovaného
elementu:
function pridejSloupecDoprava() { let indexVybraneho = indexSloupceAktivniBunky(); for (let i = 0; i < tabulka.childNodes.length; i++) { if (tabulka.childNodes[i].childNodes[indexVybraneho] == tabulka.childNodes[i].lastElementChild) { tabulka.childNodes[i].appendChild(vytvorBunku()); } else { tabulka.childNodes[i].insertBefore(vytvorBunku(), tabulka.childNodes[i].childNodes[indexVybraneho + 1]); } } }
Přidejme obsluhy událostí tlačítkům pro přidávání sloupců a aplikaci vyzkoušejte:
vytvorTlacitkoAVlozHo("Přidat sloupec vlevo", document.body).onclick = pridejSloupecDoleva; vytvorTlacitkoAVlozHo("Přidat sloupec vpravo", document.body).onclick = pridejSloupecDoprava;
Aplikaci vyzkoušejte. Po náročné práci si můžete zahrát třeba
piškvorky ve vaší tabulce. Jednoduše si přidejte hodně sloupců a řádků
Možná při používání aplikace narazíme na problém. Tabulka může mít o sloupec nebo řádek více, než jsme chtěli. Přidáme funkce pro mazání řádku a sloupce.
Mazání řádku
Z tabulky odstraníme řádek, jehož element dostaneme tak, že vezmeme
všechny elementy tabulky (childNodes
) a vybereme ten s indexem
vybrané buňky:
function smazRadek() { let indexVybraneho = indexRadkuAktivniBunky(); tabulka.removeChild(tabulka.childNodes[indexVybraneho]); }
Mazání sloupce
Mazání sloupce musíme zajistit tak, že smažeme v každém řádku (cyklem) buňku, jejíž index odpovídá indexu vybrané buňky:
function smazSloupec() { let indexVybraneho = indexSloupceAktivniBunky(); for (let i = 0; i < tabulka.childNodes.length; i++) { tabulka.childNodes[i].removeChild(tabulka.childNodes[i].childNodes[indexVybraneho]); } }
Zprovozníme tlačítka a máme hotovo:
vytvorTlacitkoAVlozHo("Odstranit řádek", document.body).onclick = smazRadek; vytvorTlacitkoAVlozHo("Odstranit sloupec", document.body).onclick = smazSloupec;
Aplikaci si můžete vyzkoušet:
Po tomto příkladu byste již měli ovládat DOM v JavaScriptu. Všechna ta kouzla od vytváření tabulky až po piškvorky byly vlastně jen hříčky s několika málo elementy, které bychom běžně psali v HTML. Všimněte si, že na začátku všeho máme HTML kód, který v body nemá jediný element.
V příští lekci, Striktní operátory a přetypování v JavaScriptu, si řekneme něco o podmínkách, než se pustíme do dalšího většího tématu - práce s grafikou.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 1211x (2.4 kB)
Aplikace je včetně zdrojových kódů v jazyce JavaScript