11. díl - Dokončení editoru tabulek v JavaScriptu

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

V minulém tutoriálu o programování v JavaScriptu jsme rozpracovali editor tabulek v JavaScriptu. V dnešním dílu tuto webovou aplikaci dokončíme.

Index vybrané buňky mezi řádky

Abychom si techniky "orientování se" v proměnných DOM osvojili, napíšeme si 2 funkce, které později použijeme v jiných. Jednou z nich je funkce indexRadkuAktiv­niBunky(), která vrátí index řádku (pořadí od nuly), kde se nachází uživatelem vybraná buňky (aktivniBunka). Nejprve je asi třeba si uvědomit, kde to hledáme. Hledáme mezi řádky tabulky. Tabulka je <table> a řádky jsou ihned v ní (buňky jsou až v řádcích, což může mást, dávejte si na to pozor).

Kde hledáme: table.childNodes

A co hledáme, bude 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 parrentElement a ani to nám nebude stačit. Nepotřebujeme buňku, ale řádek a ten opět získáme pomocí parrentElement.

Co hledáme: activeCell.pa­rentElement.pa­rentElement

NodeList a indexOf

Jestli si vzpomínáte, tak v minulém dílu jsem zmínil, že funkce s DOM nikdy nevrací pole, ale NodeList nebo podobné. 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 OOP. 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, kde běžně sídlí (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.

Výsledný kód by tak mohl vypadat následovně:

function indexRadkuAktivniBunky() {
        var cilHledani = tabulka.childNodes;
        var 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. 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 vybrané buňky mezi sloupci

O něco jednodušší to bude s získáváním indexu mezi sloupci. Potřebujeme se dostat k řádku, kde se buňka nachází a získat její pozici. Buňku opět dostaneme pomocí parrentElement na aktivniBunka a k řádku, kterému náleží přes parrentElemen­t.parrentElemen­t.

function indexSloupceAktivniBunky() {
        var bunkyVRadku = aktivniBunka.parentElement.parentElement.childNodes;
        var 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ý

Již máme metodu, která vrátí nový řádek (element <tr>) hotovou. Umíme získat index řádku, na kterém leží vybraná buňka a umíme před něj vše vložit. Pro přehlednost si nový řádek uložíme do proměnné, index řádku stejně tak a opět se budeme prokousat DOMem. Řádky (<tr>) jsou přímo v tabulce (<table>), takže to bude jednoduché. Metodu insertBefore() voláme na table. První parametr je jasný, to je nový řádek. Druhý parametr musí být nějaký <tr>, takže opět budeme procházet table. Dále nemusíme, řádky jsou přímo v tabulce, proto rovnou z NodeListu vybereme řádek s daným indexem.

function PridejRadekNahoru() {
        var radek = vytvorRadek();
        var indexVybraneho = indexRadkuAktivniBunky();
        tabulka.insertBefore(radek, tabulka.childNodes[indexVybraneho])
}

Ve funkci, která vytváří ovládací tlačítka, dosaďme tlačítku pro přidání řádku nahoru obsluhu události 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 za aktuální + 1, abychom ho dostali na správnou pozici. Vzpomeňte si, že u toho ještě musíme ošetřovat, zda nevkládáme za poslední, protože řádek poslední + 1 neexistuje. Podmínka bude ověřovat 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 tom opačném případě přidáme pomocí inserBefore() k dalšímu řádku v pořadí.

function PridejRadekDolu() {
        var radek = vytvorRadek();
        var 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. Nyní již umíme přidávat řádky různě do tabulky, kde se nám hodí.

Přidávání řádků do tabulky v JavaScriptu

Přidání sloupce vlevo

Sloupec již bude složitější. Tabulka v HTML totiž obsahuje řádky a v těch jsou buňky. A ejhle, žá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.

Teď 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>. Tabulka.childNodes vrací pole řádků <tr> a my musíme vkládat v cyklu do každého z nich.

Kam vkládáme: tabulka.childNo­des[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 opíšeme to, kam 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.childNo­des[i].childNo­des[indexVybra­neho]

Celé to bude vypadat následovně.

function PridejSloupecDoleva() {
        var indexVybraneho = indexSloupceAktivniBunky();
        for (var 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é a 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 podobné. 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() {
        var indexVybraneho = indexSloupceAktivniBunky();
        for (var 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 vygenerujte hodně sloupců a řádků. :)

Piškvorky v naší tabulce

Možná narazíme na problém. Hrací plocha piškvorek (tabulka) má 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() {
        var 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() {
        var indexVybraneho = indexSloupceAktivniBunky();
        for (var 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;

Po tomto příkladu byste již měli skvěle ovládat DOM v JavaScriptu. Musíte si uvědomit, že všechny ty 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. Dále byste si měli všimnout, že na začátku všeho máme HTML kód, který v body nemá jediný element. Svůj editor nemažte, v některých z dalších dílu (a i v seríi pro pokročilé) budeme dále pracovat s tímto editorem. Jako ochutnávku řeknu, že bude umět vygenerovat soubor, který otevřete v Excelu. Tabulku uložíme do paměti prohlížeče, takže když se uživatel vrátí, může pokračovat tam, kde s tabulkou skončil. Už nyní si můžete např. implementovat nějaké funkce, které vás napadly navíc. Pokud si chcete své znalosti ještě procvičit, využijte našeho cvičení práce s DOM.


 

Stáhnout

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

 

  Aktivity (3)

Článek pro vás napsal Michal Žůrek (misaz)
Avatar
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.

Jak se ti líbí článek?
Celkem (8 hlasů) :
4.754.754.754.754.75


 


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

 

 

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

Avatar
Michal Žůrek (misaz):

Pak ještě zaktualizuj stránku. Některé prohlížeče to neumí najit zpětně. A jinak je to blbost, dokud nevybereš buňku, tak zadny indexVybrane proste není.

Odpovědět 16. dubna 12:17
Nesnáším {}, proto se jim vyhýbám.
Avatar
Michal
Člen
Avatar
Michal:

Po kliknutí na button "Pridaj riadok hore" mi vypise chybu :
Uncaught TypeError: Array.prototy­pe.indexOf called on null or undefined
Prikladám aj zdroják.

 
Odpovědět 18. dubna 9:43
Avatar
Odpovídá na Michal
Libor Šimo (libcosenior):

Toto som si upravil podľa stiahnutého vzoru:

function vytvorRiadok() {
         var novyRiadok = document.createElement("tr");
         var prvyRiadok = tabulka.firstElementChild;
             var bunkyPrvehoRiadku = prvyRiadok.childNodes;
             var pocetBuniekVPrvomRiadku = bunkyPrvehoRiadku.length;

         for (var i = 0; i < pocetBuniekVPrvomRiadku; i++) {
             novyRiadok.appendChild(vytvorBunku());
         }

         return novyRiadok;
}

V článku je to inakšie a aj ty to máš inakšie.
Možno je chyba tam.

Odpovědět 18. dubna 15:05
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
Odpovědět 18. dubna 15:35
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
Nezmar Hydra
Člen
Avatar
Nezmar Hydra:

Jak můžu v javě sloučit buňky tabulky?
Jejich oindexování jsem vyřešil taklenc:

var td = document.crea­teElement("td")
var tdInput = document.crea­teElement("tex­t")
var oindexuj = document.crea­teElement("ID")
x = x+1;
oindexuj.id=("tab"+x­);
tdInput.textContent = x;
td.appendChil­d(oindexuj).ap­pendChild(tdIn­put);
return td;

ale jak mu říct, aby sloučil buňky s vybranými indexy na to jsem nepřišel.
Díky za radu.

 
Odpovědět 6. října 2:50
Avatar
Odpovídá na Nezmar Hydra
Michal Žůrek (misaz):

hmmm, takhle to nepůjde chtělo by si to prustudovat základy HTML. Tvůj kód vytváří vpodstatě následující HTML.

<td>
    <ID id="tabX">
        <text>
        X
        </text>
    </ID>
</td>

a takhle tabulky v HTML nefungují.

Odpovědět 8. října 0:12
Nesnáším {}, proto se jim vyhýbám.
Avatar
Nezmar Hydra
Člen
Avatar
Odpovídá na Michal Žůrek (misaz)
Nezmar Hydra:

Ne to mi funguje fajn. mam oIDéčkovanou každou buňku a mužu si do ní přidávat co chci a kam chci. jen ten kód jsem nedal celej.
je to taklenc
function vytvorVychozi­Tabulku(vycho­ziVelikostX,vycho­ziVelikostY,I­DeckoKam,IDec­koPridel){
var tabulka;
var NUMindex = 0;
tabulka = document.crea­teElement("ta­ble");
document.getE­lementById(IDec­koKam).appendChil­d(tabulka);
for (var y = 0; y < vychoziVelikostY; y++)
{

var tr = document.crea­teElement("tr");
tabulka.appen­dChild(tr);
for (var x = 0; x < vychoziVelikostX; x++) {
NUMindex = NUMindex+1;
tr.appendChil­d(vytvorBunku(I­DeckoPridel,NU­Mindex));
}
}
}
function vytvorBunku(Tex­tIndes,index) {
var td = document.crea­teElement("td");
var tdInput = document.crea­teElement("tex­t");
var oindexuj = document.crea­teElement("ID");

oindexuj.id=(Tex­tIndes+index);
tdInput.textContent = (TextIndes+index);
td.appendChil­d(oindexuj).ap­pendChild(tdIn­put);
return td;
}

 
Odpovědět 8. října 1:28
Avatar
Odpovídá na Nezmar Hydra
Michal Žůrek (misaz):

Buňky se dají spojit tak, že první buňce dáš attribute colspan nebo rowspan s hodnotou kolik sloupců/řádků to má sloučit.

Odpovědět 8. října 11:37
Nesnáším {}, proto se jim vyhýbám.
Avatar
Jan Zamecnik
Člen
Avatar
Jan Zamecnik:

Funkce pro zjištění aktivní buňky jsem upravil následovně

function indexRadkuAktivniBunky() {
    if (aktivniBunka) {
        var cilHledani = tabulka.childNodes;
        var hledanyPrvek = aktivniBunka.parentElement.parentElement;
        return Array.prototype.indexOf.call(cilHledani, hledanyPrvek);
    } else
        return 0;
}

function indexSloupceAktivniBunky() {
    if (aktivniBunka) {
        var bunkyVRadku = aktivniBunka.parentElement.parentElement.childNodes;
        var td = aktivniBunka.parentElement
        return Array.prototype.indexOf.call(bunkyVRadku, td)
    } else
        return 0;
}

To proto, aby na začátku pokud není vybraná buňka použil index = 0

Editováno 26. října 10:15
 
Odpovědět 26. října 10:15
Avatar
Jan Zamecnik
Člen
Avatar
Jan Zamecnik:

Pro hraní piškvorek jsem přidal na začátek globální proměnou

var krizek = false;

a upravil funkci:

tdInput.onfocus = function () {
    aktivniBunka = this;
    if (krizek) {
        aktivniBunka.value = "X";
        krizek = false;
    } else {
        aktivniBunka.value = "O";
        krizek = true;
    }
};

Potom po kliknutí na buňku se přidá jednou křížek podruhé kolečko.

 
Odpovědět 26. října 10:44
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 19. Zobrazit vše