Lekce 11 - Dokončení editoru tabulek v JavaScriptu

JavaScript Základní konstrukce Dokončení editoru tabulek v JavaScriptu American English version English version

Unicorn College ONEbit hosting 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]);
}

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 546x (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?
15 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.
Miniatura
Předchozí článek
Editor tabulek v JavaScriptu
Miniatura
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 (41)

Avatar
Fero M
Člen
Avatar
Fero M:20. února 19:57

Děkuji

 
Odpovědět 20. února 19:57
Avatar
Fero M
Člen
Avatar
Fero M:20. února 20:16

A mohu se prosím zeptat v jakém programu to programujete?

 
Odpovědět 20. února 20:16
Avatar
Odpovídá na Fero M
Michal Žůrek - misaz:20. února 20:40

Já ve Visual Studiu 2017.

Odpovědět 20. února 20:40
Nesnáším {}, proto se jim vyhýbám.
Avatar
Fero M
Člen
Avatar
Odpovídá na Michal Žůrek - misaz
Fero M:20. února 20:58

No já to mám v Pspadu a přes F10 mi to píše úplné blbosti (něco jako připište středník mezi (např:. let ;cilHledani)) Nevím vůbec co mám dělat. :(

 
Odpovědět 20. února 20:58
Avatar
Odpovídá na Fero M
Michal Žůrek - misaz:20. února 21:07

Nepoužívej integrovaný prohlížeč v PSPadu. ten je tak minimálně 15 let neaktualizovaný.

Odpovědět 20. února 21:07
Nesnáším {}, proto se jim vyhýbám.
Avatar
Fero M
Člen
Avatar
Odpovídá na Michal Žůrek - misaz
Fero M:20. února 21:10

a mohu třeba netbeans? a Xampp

 
Odpovědět 20. února 21:10
Avatar
Fero M
Člen
Avatar
Fero M:23. února 13:17

Dobryr den mohli byste mi prosím napsat jak vypadá výsledný kód těchto tabulek?

 
Odpovědět 23. února 13:17
Avatar
Odpovídá na Fero M
Michal Žůrek - misaz:23. února 13:50

Pod článkem si ho můžete stáhnout.

Odpovědět 23. února 13:50
Nesnáším {}, proto se jim vyhýbám.
Avatar
Fero M
Člen
Avatar
Fero M:23. února 19:54

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

 
Odpovědět 23. února 19:54
Avatar
Tomáš Chocenský:11. června 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. června 14:56
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 51. Zobrazit vše