5

Vlastní DOM elementy

Ukážeme si, jak vytvořit vlastní DOM elementy a udělat naše komopnenty interaktivní.

Přestože už dokážeme díky komponentám vytvářet vcelku hezké webové stránky, pořád to ještě nejsou opravdové aplikace. Naše komponenty totiž zatím vytvářejí pouze statické HTML a chybí jim interaktivita. V této lekci tuto situaci napravíme tím, že se naučímte vytvářet vlastní DOM elementy.

Vytváření vlastních DOM elementů

Do této chvíle naše programy vždy fungovaly tak, že všechny DOM elementy, se kterými jsme pracovali, byly vybrány ze stránky pomocí document.querySelector. Nyní si ukážeme, jak vytvořit DOM element tak říkajíc na zelené louce mimo naši stránku. Takto vytvořený element pak můžeme různě opracovávat a zapojit jej do stránky až ve chvíli, kdy je hotový.

Nový DOM element vytvoříme pomocí funkce document.createElement. Takto například v konzoli vytvoříme prázdný h1 element a uložíme si jej do proměnné.

> const h1Elm = document.createElement('h1');

V tuto chvíli máme vytvořený zcela plnoprávný DOM element, se kterým můžeme dělat všechno, co jsme s DOM elementy byli zvyklí dělat doposud: nastavovat textContent, měnit styly, měnit CSS třídy apod.

> h1Elm.textContent = 'Nadpis'
> h1Elm.className = 'title'

Důležité je, že tento element není zapojen do naší stránky. Je zatím uložen pouze v proměnné h1Elm a na stránce jej tedy není vidět. Pokud jej chceme do stránky vložit, můžeme jej například zapojit na konec nějakého elementu, který už na stránce je.

Mějme například následujicí HTML.

<body>
  <div id="app"></div>
</body>

Nyní v konzoli vybereme element #app a poté použijeme metodu appendChild, která zapojí zadaný element na konec elementu #app.

> const appElm = document.querySelector('#app');
> appElm.appendChild(h1Elm)

Po spuštění těchto příkazů bychom měli na stránce uvidět náš nadpis.

Obsah vlastních elementů

DOM elementy, které si vytvoříme pomocí document.createElement jsou zcela plnohodnotné DOM elementy. Kromě stylů a textového obsahu můžeme taky měnit jejich vnitřní obsah pomocí vlastnosti innerHTML. Takto například přidáme složitější obsah do našeho nadpisu.

> h1Elm.innerHTML = '<span class="bold">Nadpis</span><span>mojí stránky</span>'

Vidíte tedy, že si tak říkajíc na vlasním písečku můžeme vytvářet i velmi složité a obsáhldé DOM elementy a zapojit je do stránky až ve chvíli, kdy je máme správně zkonstruované. Toto se nám bude velmi hodit při vytváření komponent. O tom však až v druhé části.

Cvičení: Vlastní DOM elementy

1

Tlačítko

to dáš
  1. Vytvořte si jednoduchou stránku obsahující nám tak dobře známy element #app.
    <body>
      <div id="app"></div>
    </body>
    
  2. Otevřete si konzoli prohlížeče a pomocí funkce document.createElement si do nějaké proměnné vytvořte tlačítko button.
  3. Nastavte tomuto tlačítku textContent na ‘Mačkej’.
  4. Přidejte na tlačítko CSS třídu btn.
  5. Vyberte ze stránky element #app a vložte tlačítko na stránku pomocí metody appendChild.
  6. Stejným postupem přidejte na stránku ještě další dvě tlačítka s různými nápisy a sleduje, kam do stránky se vložily.
2

Kontakty

to dáš

Vytvořte jednoduchou webovou stránku, která zobrazuje seznam kontaktů. U každého kontaktu bychom chtěli evidovat jméno, email a telefonní číslo.

  1. Založte webovou aplikaci se soubory index.html, style.css a index.js.
  2. V souboru index.html vytvořte element #app.
  3. Uvnitř index.js pomocí document.createElement vytvořte element div a uložte jej do proměnné contact1.
  4. Na váš element přidejte CSS třídu contact.
  5. Nastavte innerHTML vašho elementu na následujicí obsah
    <span class="name">Jaromír Bystřina</span>
    <span class="phone">+420 777 666 323</span>
    <span class="email">lesni.bystrina@gmail.com</span>
    
  6. Vyberte ze stránky element #app a vložte do něj element s kontaktem.
  7. Stejným postupem přidejte na stránku ještě další dva kontakty contact2 a contact3.
  8. Nepovinně si můžete pohrát se stylováním. U každého kontaktu se vám například může hodit takováto hezká ikonka.

Interaktivní komponenty

Vytváření vlastních DOM elementů nám velmi pomůže pro přidání interaktivity do našich komponent. Začněme s jednoduchým příkladem, kdy bychom chtěli aplikaci podobnou například Facebooku, která zobrazuje příspěvky a k nim možnost je olajkovat. Základ takové aplikace si můžete stáhnout zde.

Podívejme se, jak vypadá HTML kód jednoho příspěvku.

<div class="post">
  <div class="post__text">
    Lorem ipsum dolor sit, amet consectetur adipisicing elit. Corporis quasi
    architecto, quos ea eos nihil nobis fugiat eveniet aperiam iste?
  </div>
  <button id="like-btn" class="like-btn">
    <div class="like-icon"></div>
    <div>Olajkovat</div>
  </button>
</div>

Pokud bychom chtěli vytvořit pro příspěvek komponentu, dle našich současných znalostí by mohla vypadat například takto.

const Post = (props) => {
  return `
    <div class="post">
      <div class="post__text">${props.text}</div>
      <button class="like-btn">
        <div class="like-icon"></div>
        <div>Olajkovat</div>
      </button>
    </div>
  `;
};

Nyní bychom chtěli na naše tlačíko pověsit funkci reagující na kliknutí. Jak to ale udělat, když příspěvků může být na naší stránce mnoho a není snadné se k nim odstat mimo komponentu? Zde přichází ke slovu vytváření DOM elementů. Naši komponentu přepíšeme tak, že nebude vracet řetězec obsahující HTML, nýbrž hotový DOM element.

const Post = (props) => {
  const element = document.createElement('div');
  element.className = 'post';
  element.innerHTML = `
    <div class="post__text">${props.text}</div>
    <button class="like-btn">
      <div class="like-icon"></div>
      <div>Olajkovat</div>
    </button>
  `;

  return element;
};

Díky tomu, že uvnitř funkce vytvářím DOM element, můžeme se díky němu dostat k tlačítku a napojit na něj posluchač události. Ten si vytvoříme přímo uvnitř komponenty.

const Post = (props) => {
  const element = document.createElement('div');
  element.className = 'post';
  element.innerHTML = `
    <div class="post__text">${props.text}</div>
    <button class="like-btn">
      <div class="like-icon"></div>
      <div>Olajkovat</div>
    </button>
  `;

  const likeBtn = element.querySelector('.like-btn');
  likeBtn.addEventListener('click', () => {
    likeBtn.classList.toggle('like-btn--on');
  });

  return element;
};

Všimněte si, že metodu querySelector voláme nikoliv na dokumentu, ale na našem novém elementu. Pokud tuto metodu zavoláme na DOM elementu, vybíráme pouze zevnitř tohoto elementu a nikoliv z celého dokumentu. Snadno tak vybereme naše tlačíkto a pověsíme na něj posluchače. Každý DOM element naší komponenty tak má svoje tlačíko se svým vlastním posluchačem. Veškerou logiku tak mám zabalenou uvnitř komponenty a můžene si jich na stránce vytvořit kolik chceme. Nyní však musíme použít metodu appendChild, protože naše komponenta už nevrací řetězec, ale hotový DOM element.

const post1 = Post({ text: 'Zrovna drtím JavaScript!' });
const post2 = Post({ text: 'Digitální akademie je nejvíc cool' });
const post3 = Post({ text: 'Bude ze mě velká programátorka' });

const appElm = document.querySelector('#app');
appElm.appendChild(post1);
appElm.appendChild(post2);
appElm.appendChild(post3);

Označení položek v nákupním seznamu

Díky tomu, že už umíme psát interaktivní komponenty, můžeme uživateli umožnit nějakou položku ze seznamu označit jako koupenou, aby uživatel mohl sledovat, co už koupil a co ještě zbývá. Ke každé položce tedy přidáme tlačíko pro označení. Kvůli tomu bude potřeba přepsat naše komponenty tak, aby vracely DOM elementy.

Kód naší aplikace pro nákupní seznam si můžete stáhnout zde. Máme zde vytvořeny dvě komponenty ShoppingItem a ShoppingList.

const ShoppingItem = (props) => {
  return `
    <div class="item">
      <span class="item__name">${props.name}</span>
      <span class="item__amount">${props.amount}</span>
    </div>
  `;
};

const ShoppingList = (props) => {
  let result = '<div class="shopping-list">';

  for (let i = 0; i < props.items.length; i += 1) {
    result += ShoppingItem(props.items[i]);
  }

  result += '</div>';
  return result;
};

Komponentu ShoppingItem převedeme na DOM element a přidáme do ní tlačítko.

const ShoppingItem = (props) => {
  const element = document.createElement('div');
  element.className = 'item';
  element.innerHTML = `
    <span class="item__name">${props.name}</span>
    <span class="item__amount">${props.amount}</span>
    <button class="btn-done">koupeno</button>
  `;

  return element;
};

Nyní chceme přidat funkci pro kliknutí, která na náš element přidá CSS třídu item--done.

const ShoppingItem = (props) => {
  const element = document.createElement('div');
  element.className = 'item';
  element.innerHTML = `
    <span class="item__name">${props.name}</span>
    <span class="item__amount">${props.amount}</span>
    <button class="btn-delete">koupeno</button>
  `;

  const deleteBtn = element.querySelector('.btn-delete');
  deleteBtn.addEventListener('click', () => {
    element.classList.add('item--done');
  });

  return element;
};

Nyní musíme upravit komponentu ShoppingList.

const ShoppingList = (props) => {
  const element = document.createElement('div');
  element.className = 'shopping-list';

  for (let i = 0; i < props.items.length; i += 1) {
    element.appendChild(ShoppingItem(props.item));
  }

  return element;
};

Nakonec musíme ještě upravit použití komponenty ShoppingList, protože už nevrací řetězec ale DOM element.

mainElm.innerHTML = '';
mainElm.appendChild(ShoppingList({ items: list }));

Všimněte si, že je třeba nejdříve vyprázdnit element mainElm, abychom do něj mohli vložit nový obsah pomocí appendChild.

Cvičení: Interaktivní komponenty

3

Žárovky

to dáš

Stáhněte si základ stránky, která zobrazuje obyčejnou žárovku.

  1. Vytvořte komponentu Bulb, která vytvoří DOM element pro žárovku.
  2. Na tento element uvnitř komponenty připojte posluchač událost click. Tento žárovku při kliknutí rozsvítí přidáním třídy bulb-on.
  3. Do stránky přidejte element #app. V JavaScriptu jej vyberte a pomocí appendChild do něj vložte tři žárovky. Vyzkoušejte, že je dokážete na stránce nezávisle rozsvítit.
4

Kostky 1

to dáš

Stáhněte si webpack projekt obsahující několik hracích kostek.

  1. Podle vzoru uvedeného v souboru index.html vytvořte komponentu Dice. Zatím pro ni nemusíte vytvářet speciální složku. Pro tuto chvíli její kód vytvořte v hlavním index.js. Tato komponenta bude ve svých props očekávat vlastnost side, která udává číslo zobrazené na kostce. Napište komponentu tak, aby používala DOM elementy.
  2. Vložte na stránku několik kostek s různými hodnotami.
  3. Zařiďte, že když uživatel klikne na tlačítko hodit některé kostky, tato kostka zobrazí náhodné číslo mezi 1 a 6. K vygenerování náhodného čísla využijte již připravenou funkci roll.