Autolistaus templatesta
Poista
Lisää auto
Templaten sisältö sivulla:
-
CSS
/* Esikatselualue määrittelee sekä taustan että tekstin värin, jotta pari on varmasti luettava kummassakin teemassa. */ #alue { background-color: #ffffff; color: #1a1a1a; padding: 0.5rem; } @media (prefers-color-scheme: dark) { #alue { background-color: #1e1e1e; color: #f0f0f0; } } #lista { list-style: none; padding: 0; max-width: 24rem; } /* Templaten sisältö EI osu tähän sääntöön, koska sitä ei renderöidä. Vasta kloonattu ja sivulle lisätty kopio näkyy. */ .auto { display: flex; align-items: center; gap: 0.5rem; padding: 0.3rem; border-bottom: 1px solid currentColor; } .malli { flex-grow: 1; } .valmistaja { font-size: 0.85rem; opacity: 0.8; }
HTML
<div id="alue"> <h2>Autolistaus templatesta</h2> <!-- Templaten sisältöä ei renderöidä eikä suoriteta. Kuvia ei ladata, skriptejä ei ajeta, ja elementit eivät näy sivulla. --> <template id="pohja"> <li class="auto"> <strong class="malli"></strong> <span class="valmistaja"></span> <button type="button">Poista</button> </li> </template> <ul id="lista"></ul> <p><button type="button" id="lisaa">Lisää auto</button></p> <p>Templaten sisältö sivulla: <output id="mittaus">-</output></p> </div>
Javascript
/* suorita tämä koodi Run-painikkeella */ const AUTOT = [ { malli: "Polo", valmistaja: "Volkswagen" }, { malli: "Micra", valmistaja: "Nissan" }, /* Huomaa tämä: mallin nimessä on merkkausta. textContent asettaa sen tekstinä, joten se näkyy sellaisenaan eikä siitä tule elementtejä. innerHTML olisi tässä tietoturva-aukko. */ { malli: "<img src=x onerror=alert(1)>", valmistaja: "Ei ole olemassa" } ]; const pohja = document.getElementById("pohja"); const lista = document.getElementById("lista"); const mittaus = document.getElementById("mittaus"); /* Templaten sisältö ei ole sivulla: se ei näy, eikä querySelector löydä sitä dokumentista. Sisältöön pääsee vain content-ominaisuuden kautta. */ mittaus.textContent = `dokumentista löytyy ${document.querySelectorAll("li.auto").length} kpl, ` + `templatesta ${pohja.content.querySelectorAll("li.auto").length} kpl`; console.log("template.content on", pohja.content.constructor.name); /** * Luo yhden listarivin templatesta. * * @param {Object} auto Auton tiedot * @returns {DocumentFragment} Valmis rivi */ const luoRivi = function (auto) { /* Ensimmäinen parametri true kopioi myös sisällön. Ilman sitä saisi tyhjän fragmentin. */ const rivi = pohja.content.cloneNode(true); rivi.querySelector(".malli").textContent = auto.malli; rivi.querySelector(".valmistaja").textContent = auto.valmistaja; return rivi; }; /* replaceChildren tyhjentää ja täyttää yhdellä kutsulla. Fragmentti sulaa lisäyksessä pois, joten listaan tulee vain sen lapset. */ lista.replaceChildren(...AUTOT.map(luoRivi)); console.log("listassa nyt", lista.children.length, "riviä"); /* Poisto delegoinnilla: yksi käsittelijä koko listalle. Toimii myös riveille, jotka lisätään vasta myöhemmin. */ lista.addEventListener("click", function (e) { const nappi = e.target.closest("button"); if (nappi === null) { return; } nappi.closest("li").remove(); console.log("poistettu, jäljellä", lista.children.length); }); /* Lisäyspainike osoittaa, että sama pohja kelpaa uudelleen. */ let laskuri = 0; document.getElementById("lisaa").addEventListener("click", function () { laskuri += 1; lista.append(luoRivi({ malli: `Uusi malli ${laskuri}`, valmistaja: "Tehdas" })); });
Konsoli