November Black Friday C# týden
BlackFriday je tu! Využij jedinečnou příležitost a získej až 80 % znalostí navíc zdarma! Více zde
Pouze tento týden sleva až 80 % na e-learning týkající se C#

Lekce 11 - Dokončení editoru tabulek v JavaScriptu

Unicorn College Tento obsah je dostupný zdarma v rámci projektu IT lidem.
Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulé lekci, Editor tabulek v JavaScriptu, jsme rozpracovali editor tabulek. V dnešním JavaScript tutoriálu tuto webovou aplikaci dokončíme.

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);
}

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]);
}
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

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í.

Editor tabulek
localhost

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ř NodeListu 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ů :)

Editor tabulek
localhost

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:

Editor tabulek
localhost

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, Obrázky a kreslení na canvas v JavaScriptu, se budeme věnovat práci s obrázky.


 

Stáhnout

Staženo 698x (2.4 kB)
Aplikace je včetně zdrojových kódů v jazyce JavaScript

 

 

Článek pro vás napsal Michal Žůrek - misaz
Avatar
Jak se ti líbí článek?
20 hlasů
Autor se věnuje tvorbě aplikací pro počítače, mobilní telefony, mikroprocesory a tvorbě webových stránek a webových aplikací. Nejraději programuje ve Visual Basicu a TypeScript. Ovládá HTML, CSS, JavaScript, TypeScript, C# a Visual Basic.
Předchozí článek
Editor tabulek v JavaScriptu
Všechny články v sekci
Základní konstrukce jazyka JavaScript
Miniatura
Následující článek
Cvičení k 11. lekci JavaScriptu
Aktivity (12)

 

 

Komentáře
Zobrazit starší komentáře (49)

Avatar
Fero M
Člen
Avatar
Fero M:23.2.2018 19:54

Tož, díky vám moc - chyba nalezena!

 
Odpovědět
23.2.2018 19:54
Avatar
Tomáš Chocenský:11.6.2018 14:56

ahoj
můžete mi poradit jaký je rozdil mezi timto ...

function pridejSloupecDoleva() {
        let indexVybraneho = indexSloupceAktivniBunky();
        for (let i = 0; i < tabulka.childNodes.length; i++) {
                tabulka.childNodes[i].insertBefore(vytvorBunku(), tabulka.childNodes[i].childNodes[indexVybraneho]);
        }
}

a timto kodem ...

function pridejSloupecDoleva() {
    let bunka = vytvorBunku();
    let indexVybraneho = indexSloupceAktivniBunky();
    for (let i = 0; i < tabulka.childNodes.length; i++) {
        //pred vybranou bunku v kazdem z radku
        tabulka.childNodes[i].insertBefore(bunka, tabulka.childNodes[i].childNodes[indexVybraneho]);
    }
}

?
Jen jsem uložil funkci do proměnné (jak tomu bylo i v předchozích funkcích) a myslím že by to mělo fungovat pořád stejně.
Ale z mě nepochopitelných důvodů mi to začne přidávat ty sloupce(buňky) jen v posledním řádku.
Proč?

 
Odpovědět
11.6.2018 14:56
Avatar
Odpovídá na Tomáš Chocenský
Roman Havelka:20. ledna 19:07

Vloz si "let bunka" do cyklu.

/**
* Prida sloupec vlevo od oznacene bunky
*/
function pridejSloupec­Doleva() {

let indexVybraneho = indexSloupceAk­tivniBunky();
for (let i = 0; i < tabulka.childNo­des.length; i++) {
let bunka = vytvorBunku();
tabulka.childNo­des[i].insertBe­fore(bunka, tabulka.childNo­des[i].childNo­des[indexVybra­neho]);
}
}

 
Odpovědět
20. ledna 19:07
Avatar
Markmann
Člen
Avatar
Markmann:27. června 13:22

Zdravím! Pomůžete, prosím?
Zatím jsem pracoval bez pomocí IDE, který by mi pomáhal hledat chyby. Teď jsem zkusil Visual Studio Code, ale ten říká, že nenašel žádnou chybu. Problém nastal, když jsem za:
vytvorTlacitko­AVlozHo("přidat řádek nahoru",documen­t.body) napsal .onclick=pridej­RadekNahoru;
Vypsaly se pouze první 2 buňky a tabulky zmizela.
Hledám, hledám, ale nevím si rady.
Rád bych poslal texťáky, ale jde jen jeden obrázek, zbytečně bych tady zabíral místo, věřte že jsem jen přidal .onclick=pridej­RadekNahoru;(nor­málně se nevypsalo nic, když tam byla chyba, ale teď se vypsali dva buttony)

Editováno 27. června 13:23
 
Odpovědět
27. června 13:22
Avatar
Markmann
Člen
Avatar
Odpovídá na Markmann
Markmann:27. června 14:08

Chybu jsem našel, chyběla mi metoda vytvorRadek xD

 
Odpovědět
27. června 14:08
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
Markmann
Člen
Avatar
Odpovídá na Markmann
Markmann:27. června 14:41

Tak ne, problém přetrvává. Teď jsem ale spustil nástroj pro vývojáře v chromu a aspoň to hází nějaké errory. Přikládám soubor, co mi to píše. Jsem zmaten. Proč navíc za pridejRadekNahoru nejsou závorky? Není to funkce, která není v proměnné?

Editováno 27. června 14:44
 
Odpovědět
27. června 14:41
Avatar
Markmann
Člen
Avatar
Markmann:27. června 14:48

A proč, když spustím vzorový a kliknu na tlačítka přidej řádku, tak se nic nestane(nevidím rozšíření té tabulky)? V nástrojích pro vývojáře to hlásí chybu viz příloha.

 
Odpovědět
27. června 14:48
Avatar
Jan Tesař
Člen
Avatar
Jan Tesař:29. července 17:16

Ahoj, proč ve funkci vytvorTlacitko­AVlozHo(popisek, rodic) při jejím volání do "rodic" nelze napsat nic jiného než document.body, např. document.p nebo document.body.p (samozřejmě daný tag v indexu vytvořím), nebo, pokud to nelze proč daný parametr u funkce vůbec používáme a nenapíšem document.body přímo do funkce a v parametru necháme jen popisek?

Děkuji

 
Odpovědět
29. července 17:16
Avatar
Jozef Lysík
Člen
Avatar
Jozef Lysík:23. října 16:05

Čaute.
Príkazy tabulka.lastChild a tabulka.lastE­lementChild vracajú to isté? Napríklad v podmienkach kde sa testuje posledný riadok alebo stĺpec fungujú obidva zápisy.
Ďakujem

 
Odpovědět
23. října 16:05
Avatar
Odpovídá na Jozef Lysík
Jakub Podskalský:23. října 17:29

lastElementChild vrátí poslední element (elementem se myslí tag). lastElement vrátí poslední node, tím může být třeba text nebo komentář.

<div>
    <button>tlačítko</button>
    <p>paragraph</p>
    Já jsem text node
</div>

lastChild by vrátil ten poslední text node, lastElementChild p element.

 
Odpovědět
23. října 17:29
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 59. Zobrazit vše