Jak dynamicky umísťovat prvky v DOM pomocí JavaScriptu

Jak používat JavaScript k dynamické manipulaci s prvky DOM vzhledem k ostatním prvkům DOM.

Pro tento tutoriál použijeme full-stack JavaScriptový framework CheatCode, Joystick. Joystick spojuje rozhraní front-end UI s back-endem Node.js pro vytváření aplikací.

Pro začátek budeme chtít nainstalovat Joystick přes NPM. Před instalací se ujistěte, že používáte Node.js 16+, abyste zajistili kompatibilitu (pokud se potřebujete naučit, jak nainstalovat Node.js nebo spustit více verzí na vašem počítači, přečtěte si nejprve tento tutoriál):

Terminál

npm i -g @joystick.js/cli

Tím se Joystick nainstaluje globálně do vašeho počítače. Po instalaci vytvořte nový projekt:

Terminál

joystick create app

Po několika sekundách se zobrazí zpráva o odhlášení na cd do nového projektu a spusťte joystick start :

Terminál

cd app && joystick start

Poté by vaše aplikace měla být spuštěna a my jsme připraveni začít.

Proč?

Na první pohled se to může zdát trochu hloupé. Proč bychom to chtěli dělat? Když začnete vytvářet složitější rozhraní, i když mnoho vzorů uživatelského rozhraní je nejlepší vyzkoušet pomocí CSS nejprve někdy to dělá věci složitější, než je nutné. Pokud je to případ vaší vlastní aplikace, je dobré vědět, jak používat styly prostřednictvím JavaScriptu, aby bylo možné zvládnout změny ve vašem uživatelském rozhraní, abyste se vyhnuli chaotickým nebo křehkým CSS.

Nastavení našeho testovacího případu

V tomto tutoriálu budeme pracovat s komponentou Joystick. Toto je polovina uživatelského rozhraní rámce Joystick, který jsme právě nastavili. To nám umožní rychle sestavit uživatelské rozhraní pomocí prostého HTML, CSS a JavaScriptu.

Pro začátek v aplikaci, která pro nás byla vytvořena, když jsme spustili joystick create app , otevřete /ui/pages/index/index.js soubor. Jakmile to budete mít, nahraďte obsah následujícím:

/ui/pages/index/index.js

import ui from '@joystick.js/ui';

const Index = ui.component({
  render: () => {
    return `
      <div>
      </div>
    `;
  },
});

export default Index;

Zde nahrazujeme stávající ukázkovou komponentu, která je namapována na kořenovou trasu v naší aplikaci http://localhost:2600/ (nebo jen / ) s komponentou kostry, kterou můžeme použít k sestavení našeho testovacího případu.

Dále nahradíme <div></div> vrací render() metoda (toto je kód HTML, který bude vykreslen nebo „vykreslen“ na obrazovce) se seznamem „karet“, které později dynamicky umístíme pomocí JavaScriptu:

/ui/pages/index/index.js

import ui from '@joystick.js/ui';

const Index = ui.component({
  render: () => {
    return `
      <div class="index">
        <ul class="cards">
          <li>
            <h2>Aliquam impedit ipsa adipisci et quae repellat sit.</h2>
            <p>Deleniti quibusdam quia assumenda omnis. Rerum cum et error vero enim ex. Sapiente est est ut omnis possimus temporibus in.</p>
          </li>
          <li>
            <h2>Ab recusandae minima commodi sed pariatur.</h2>
            <p>Velit in voluptatum quia consequatur fuga et repellendus ut cupiditate. Repudiandae dignissimos dolores qui. Possimus nihil laboriosam enim dolorem vitae accusantium accusamus dolor. Tenetur fuga omnis et est accusantium dolores. Possimus vitae aliquid. Vitae commodi et autem vitae rerum.</p>
          </li>
          <li>
            <h2>Voluptatem ipsa sed illum numquam aliquam sint.</h2>
            <p>Suscipit quis error dolorum sed recusandae recusandae est. Et tenetur perferendis sequi itaque similique. Porro facere qui saepe alias. Qui itaque corporis explicabo itaque. Quibusdam vel expedita odio quaerat libero veniam praesentium minus.</p>
          </li>
          <li>
            <h2>Aliquam impedit ipsa adipisci et quae repellat sit.</h2>
            <p>Deleniti quibusdam quia assumenda omnis. Rerum cum et error vero enim ex. Sapiente est est ut omnis possimus temporibus in.</p>
          </li>
          <li>
            <h2>Ab recusandae minima commodi sed pariatur.</h2>
            <p>Velit in voluptatum quia consequatur fuga et repellendus ut cupiditate. Repudiandae dignissimos dolores qui. Possimus nihil laboriosam enim dolorem vitae accusantium accusamus dolor. Tenetur fuga omnis et est accusantium dolores. Possimus vitae aliquid. Vitae commodi et autem vitae rerum.</p>
          </li>
          <li>
            <h2>Voluptatem ipsa sed illum numquam aliquam sint.</h2>
            <p>Suscipit quis error dolorum sed recusandae recusandae est. Et tenetur perferendis sequi itaque similique. Porro facere qui saepe alias. Qui itaque corporis explicabo itaque. Quibusdam vel expedita odio quaerat libero veniam praesentium minus.</p>
          </li>
          <li>
            <h2>Aliquam impedit ipsa adipisci et quae repellat sit.</h2>
            <p>Deleniti quibusdam quia assumenda omnis. Rerum cum et error vero enim ex. Sapiente est est ut omnis possimus temporibus in.</p>
          </li>
          <li>
            <h2>Ab recusandae minima commodi sed pariatur.</h2>
            <p>Velit in voluptatum quia consequatur fuga et repellendus ut cupiditate. Repudiandae dignissimos dolores qui. Possimus nihil laboriosam enim dolorem vitae accusantium accusamus dolor. Tenetur fuga omnis et est accusantium dolores. Possimus vitae aliquid. Vitae commodi et autem vitae rerum.</p>
          </li>
          <li>
            <h2>Voluptatem ipsa sed illum numquam aliquam sint.</h2>
            <p>Suscipit quis error dolorum sed recusandae recusandae est. Et tenetur perferendis sequi itaque similique. Porro facere qui saepe alias. Qui itaque corporis explicabo itaque. Quibusdam vel expedita odio quaerat libero veniam praesentium minus.</p>
          </li>
          <li>
            <h2>Aliquam impedit ipsa adipisci et quae repellat sit.</h2>
            <p>Deleniti quibusdam quia assumenda omnis. Rerum cum et error vero enim ex. Sapiente est est ut omnis possimus temporibus in.</p>
          </li>
          <li>
            <h2>Ab recusandae minima commodi sed pariatur.</h2>
            <p>Velit in voluptatum quia consequatur fuga et repellendus ut cupiditate. Repudiandae dignissimos dolores qui. Possimus nihil laboriosam enim dolorem vitae accusantium accusamus dolor. Tenetur fuga omnis et est accusantium dolores. Possimus vitae aliquid. Vitae commodi et autem vitae rerum.</p>
          </li>
          <li>
            <h2>Voluptatem ipsa sed illum numquam aliquam sint.</h2>
            <p>Suscipit quis error dolorum sed recusandae recusandae est. Et tenetur perferendis sequi itaque similique. Porro facere qui saepe alias. Qui itaque corporis explicabo itaque. Quibusdam vel expedita odio quaerat libero veniam praesentium minus.</p>
          </li>
        </ul>
      </div>
    `;
  },
});

export default Index;

Velmi jednoduché. Zde jsme přidali třídu index na stávající <div></div> a dovnitř jsme přidali <ul></ul> (neuspořádaný seznam) s třídou cards . Uvnitř jsme přidali 12 <li></li> tagy, z nichž každá představuje „kartu“ s nějakým obsahem lorem ipsum. I když je délka technicky libovolná, aby dávalo smysl tomu, co budeme implementovat níže, dává smysl mít několik položek na rozdíl od 1-2 (neváhejte si však hrát s délkou, protože náš kód bude stále fungovat ).

/ui/pages/index/index.js

import ui from '@joystick.js/ui';

const Index = ui.component({
  css: `
    .cards {
      opacity: 0;
      border-top: 1px solid #eee;
      border-bottom: 1px solid #eee;
      padding: 40px;
      overflow-x: scroll;
      display: flex;
    }

    .cards li {
      background: #fff;
      border: 1px solid #eee;
      box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
      padding: 30px;
      border-radius: 3px;
      list-style: none;
      width: 300px;
      min-width: 300px;
    }

    .cards li h2 {
      font-size: 28px;
      line-height: 36px;
      margin: 0;
    }

    .cards li p {
      font-size: 16px;
      line-height: 24px;
      color: #888;
    }

    .cards li:not(:last-child) {
      margin-right: 30px;
    }
  `,
  render: () => {
    return `
      <div class="index">
        <ul class="cards">
          <li>
            <h2>Aliquam impedit ipsa adipisci et quae repellat sit.</h2>
            <p>Deleniti quibusdam quia assumenda omnis. Rerum cum et error vero enim ex. Sapiente est est ut omnis possimus temporibus in.</p>
          </li>
          ...
        </ul>
      </div>
    `;
  },
});

export default Index;

Těsně nad naším render jsme do naší komponenty css přidali vlastnost což, jak byste očekávali, nám umožňuje přidat do naší komponenty nějaký styl CSS. Tyto styly dosahují toho, že nám poskytují vodorovně rolovaný seznam „karet“, které přesahují okraj prohlížeče, jako je tento:

Nyní, když máme v prohlížeči naše základní styly a značky, chceme přidat JavaScript nezbytný k dynamickému posunutí první karty v seznamu tak, aby začínala uprostřed stránky. Naším cílem je napodobit design, jako je seznam „co je nového“ na aktuálním designu Apple Store:

Abychom to mohli udělat, dále zapojíme JavaScript nezbytný jako metodu na naší komponentě Joystick.

Dynamické nastavení odsazení při načítání stránky

Než zde zpracujeme část „načtení stránky“, musíme nejprve napsat JavaScript, abychom vybrali náš seznam v DOM, vypočítali aktuální středový bod okna a poté nastavili odsazení levé strany našeho seznamu. Děláme to takto:

/ui/pages/index/index.js

import ui from '@joystick.js/ui';

const Index = ui.component({
  state: {
    defaultListPadding: '20px',
  },
  methods: {
    handleSetListPadding: (component = {}) => {
      const list = component.DOMNode.querySelector('ul.cards');
      const windowCenterPoint = window.innerWidth / 2;
      
      if (list) {
        list.style.paddingLeft = windowCenterPoint >= 400 ? `${windowCenterPoint}px` : component.state.defaultListPadding;
        list.style.opacity = 1;
      }
    },
  },
  css: `...`,
  render: () => {
    return `
      <div class="index">
        <ul class="cards">
          <li>
            <h2>Aliquam impedit ipsa adipisci et quae repellat sit.</h2>
            <p>Deleniti quibusdam quia assumenda omnis. Rerum cum et error vero enim ex. Sapiente est est ut omnis possimus temporibus in.</p>
          </li>
          ...
        </ul>
      </div>
    `;
  },
});

export default Index;

Na komponentě joysticku „metoda“ (definovaná jako funkce metody na methods vlastnost volby naší komponenty) je různá funkce na naší komponentě, kterou lze volat odkudkoli v komponentě. Zde jsme definovali handleSetListPadding jako metodu, abychom ji mohli volat, když se naše komponenta připojí na obrazovku (více o tom za chvíli).

Pro začátek přidáme argument jako component který nám automaticky předá Joystick (framework automaticky přiřadí poslední možný argument funkci jako instance komponenty – protože nemáme žádné argumenty, výchozí je první slot). Na tom component instance, dostaneme DOMNode vlastnost, která představuje vykreslený uzel DOM pro naši komponentu (v tomto případě Index komponentu, kterou vytváříme) v prohlížeči.

Z toho můžeme použít vanilkový JavaScript DOM výběr a zde to uděláme pomocí .querySelector() metoda na tomto uzlu DOM k nalezení našeho ul.cards seznam a uloží jej do proměnné list .

Dále, protože chceme nastavit odsazení levé strany tohoto seznamu tak, aby bylo středem okna, musíme vypočítat, jaká je hodnota pixelu tohoto středového bodu. K tomu můžeme použít window.innerWidth hodnotu a vydělte ji 2 (pokud je například naše okno aktuálně 1000 pixelů na šířku, windowCenterPoint stane se 500 ).

S naším list a windowCenterPoint za předpokladu, že jsme udělali najděte list prvek na stránce, chceme upravit list.style.paddingLeft hodnotu, nastaví ji na hodnotu řetězce a zřetězí hodnotu windowCenterPoint s px (Děláme to proto, že získaná hodnota je celé číslo, ale musíme nastavit naši výplň jako hodnotu pixelu).

Všimněte si, že zde vytvoříme paddingLeft podmíněná hodnota na základě hodnoty windowCenterPoint . Pokud je hodnota větší než 400 , chceme jej nastavit jako paddingLeft . Pokud není , chceme se vrátit k výchozí hodnotě výplně (to zajistí, že karty omylem úplně neodsuneme z obrazovky pro menší výřezy). Pro uložení tohoto výchozího nastavení jsme přidali state vlastnost k možnostem naší komponenty, což je objekt obsahující výchozí hodnoty pro stav naší komponenty. Zde jsme přiřadili defaultListPadding na řetězec '20px' který používáme jako "jiný" v našem windowCenterPoint >= 400 ternární.

Dále, těsně pod naší výzvou k nastavení list.style.paddingLeft také se ujistěte, že jsme nastavili list.style.opacity k 1. Proč? No, v našem css který jsme nastavili dříve, nastavíme náš seznam na opacity: 0; ve výchozím stavu. Jedná se o „trik“, jak zabránit tomu, aby náš seznam vizuálně přeskakoval na stránce během pomalého načítání stránky (shoda nebo chyba v závislosti na rychlosti připojení). Tím se odstraní jakýkoli potenciál pro vizuální závadu, která by uživatele rušila.

I když máme svůj kód napsaný, v současnosti to nic neudělá. Aby to fungovalo, musíme naši metodu skutečně zavolat.

Volání handleSetListPadding při změně velikosti připojení a okna

Tato část je docela jednoduchá, zde je kód, jak to udělat:

/ui/pages/index/index.js

import ui from '@joystick.js/ui';

const Index = ui.component({
  state: {
    defaultListPadding: '20px',
  },
  lifecycle: {
    onMount: (component = {}) => {
      component.methods.handleSetListPadding();

      window.addEventListener('resize', () => {
        component.methods.handleSetListPadding();
      });
    },
  },
  methods: {
    handleSetListPadding: (component = {}) => {
      const list = component.DOMNode.querySelector('ul.cards');
      const windowCenterPoint = window.innerWidth / 2;
      
      if (list) {
        list.style.paddingLeft = windowCenterPoint >= 400 ? `${windowCenterPoint}px` : component.state.defaultListPadding;
        list.style.opacity = 1;
      }
    },
  },
  css: `...`,
  render: () => {
    return `
      <div class="index">
        <ul class="cards">
          <li>
            <h2>Aliquam impedit ipsa adipisci et quae repellat sit.</h2>
            <p>Deleniti quibusdam quia assumenda omnis. Rerum cum et error vero enim ex. Sapiente est est ut omnis possimus temporibus in.</p>
          </li>
          ...
        </ul>
      </div>
    `;
  },
});

export default Index;

Přidání další možnosti do naší komponenty lifecycle , objektu, který je mu předán, přiřadíme vlastnost onMount která je nastavena na funkci, kterou joystick zavolá, jakmile se HTML naší komponenty vykreslí do prohlížeče. Stejně jako u našeho handleSetListPadding Joystick automaticky předá component instance pro všechny dostupné metody životního cyklu.

Zde používáme component instance pro přístup k našemu handleSetListPadding zavoláním pomocí component.methods.handleSetListPadding() . Kromě toho musíme vzít v úvahu také změnu velikosti prohlížeče uživatelem a jak to ovlivní střed okna. Vše, co musíme udělat, je přidat posluchač události na window pro resize událost a ve zpětném volání, které je voláno, když je tato událost zjištěna, další volání na component.methods.handleSetListPadding() .

Funguje to, protože načítáme hodnotu window.innerWidth v době hovoru pro handleSetListPadding funkce. Zde tedy, protože tuto hodnotu dostáváme po došlo ke změně velikosti, můžeme věřit, že window.innerWidth bude obsahovat aktuální šířku a ne šířku, kterou jsme měli při načtení stránky.

A je to! Když nyní načteme naši stránku do prohlížeče, měli bychom být schopni změnit velikost a vidět, jak naše první karta posunula levý okraj tak, aby byla zarovnána do středu okna.

Zabalení

V tomto tutoriálu jsme se naučili, jak dynamicky manipulovat s DOM pomocí JavaScriptu. Naučili jsme se, jak dynamicky umístit prvek pomocí jeho CSS pomocí DOM style vlastnost na prvku seznamu. Také jsme se naučili, jak se spolehnout na window událost resize pro přepočítání středu našeho prohlížeče, kdykoli se změní šířka prohlížeče.