Klikkaa nimeä
Etunimi
Sukunimi
CSS
label { display: block; } fieldset { display: none; } #luvut { display: block; } .koodi { width: 98%; max-width: 98%; }
HTML
<p>Klikkaa nimeä</p> <form action="#"> <label>Etunimi <input type="text" name="etunimi" value="" /></label> <label>Sukunimi <input type="text" name="sukunimi" value="" /></label> <fieldset id="luvut"></fieldset> <p><input type="submit" value="Tallenna muutokset" /></p> </form> <ul id="listaus"> </ul>
Javascript
// tietorakenteeseen on lisätty taulukko. Muokkaaminen osaa dynaamisesti // hallita erimittaiset taulukot. Käytetään jo uudelleen aiemmin luotuja // rakenteita eli ei turhaan poisteta ja luoda uudelleen input-kenttiä. "use strict"; // mallitietorakenne let henkilot = [ {"etunimi": "Tommi", "sukunimi": "Lahtonen", "luvut": [1,2,3,4,5,6,7,8]}, {"etunimi": "Matti", "sukunimi": "Meikäläinen", "luvut": [10,20,30,40,50]}, {"etunimi": "Maija", "sukunimi": "Möttönen", "luvut": [1,2,4,8,16,32]}, ]; // sivulla käytetty html-koodi //<p>Klikkaa nimeä</p> //<form action="#"> //<label>Etunimi <input type="text" name="etunimi" value="" /></label> //<label>Sukunimi <input type="text" name="sukunimi" value="" /></label> //<fieldset id="luvut"></fieldset> //<p><input type="submit" value="Tallenna muutokset" /></p> //</form> //<ul id="listaus"> //</ul> // nämä ovat nyt globaaleja muuttujia, mutta koko sovellus voitaisiin // tehdä omaksi luokaksi tai nämä muuttujat tallentaa esim. lomakkeen // attribuuteiksi. Vaihtoehtoja on monia let listaus = document.getElementById("listaus"); let lomake = document.forms[0]; let fieldset = document.getElementById("luvut"); let muokattava_henkilo = {}; let alkuperainen_henkilo = muokattava_henkilo; // alustetaan henkilölistaus. Pyritään rakentamaan // viitteiden avulla siten, että minimoidaan turhat ja toistuvat haut DOM-puusta // samoin minimoidaan tietorakenteen läpikäyminen for(let henkilo of henkilot) { let li = document.createElement("li"); let etunimi = document.createTextNode( henkilo["etunimi"] ); let sukunimi = document.createTextNode( henkilo["sukunimi"] ); li.appendChild ( etunimi ); li.appendChild ( document.createTextNode(" ") ); li.appendChild ( sukunimi ); // sisempi lista lukuja varten let luvut = document.createElement("ul"); li.appendChild(luvut); for( let luku of henkilo.luvut ) { let lukuli = document.createElement("li"); lukuli.textContent = luku; luvut.appendChild(lukuli); } // tallennetaan li-objektiin viite tietorakenteessa olevaan objektiin li.henkilo = henkilo; // tallennetaan objektin sisältöä listauksessa vastaavien dom-nodejen viitteet myös objektiin // jos alkuperäistä objektia ei saa muuttaa niin pitää tehdä tätä varten oma // tietorakenne ja tallentaa vastaavat tiedot sinne henkilo["listaus"] = { "etunimi": etunimi, "sukunimi": sukunimi, "luvut": luvut }; li.addEventListener("click", muokkaa); listaus.appendChild(li); } function muokkaa(e) { // huomaa ero currentTarget vs target // currentTarget on elementti, joka käsittelee tapahtuman // target on elementti, jota klikattiin let henkilo = e.currentTarget.henkilo; // tehdään kopio muokattavista tiedoista // muokkaus on aina syytä tehdä kopioon ja muutokset päivitetään // tietorakenteeseen vasta, kun ne hyväksytään painikkeella // älä koskaan muokkaa suoraan alkuperäistä rakennetta, koska virheiden // peruminen tulee mahdottomaksi muokattava_henkilo["etunimi"] = henkilo["etunimi"]; muokattava_henkilo["sukunimi"] = henkilo["sukunimi"]; // tehdään kopio luvut-taulukosta. Huom! pelkkä sijoitus ei tässä riitä, koska // se tallentaisi vain viitteen samaan taulukkoon! // jos taulukko sisältäisi objekteja tai muita taulukoita niin ne pitää // vielä erikseen kopioida // https://medium.com/javascript-in-plain-english/how-to-deep-copy-objects-and-arrays-in-javascript-7c911359b089 muokattava_henkilo["luvut"] = Array.from( henkilo["luvut"] ); // tallennetaan alkuperäisen henkilön viite alkuperainen_henkilo = henkilo; // hyödynnetään lomakkeen ominaisuutta jossa lomake-elementit näkyvät suoraan lomakkeen attribuutteina // elementin name on sama kuin attribuutin name // tässä on varottava käyttämästä samoja nimiä kuin form-objektin olemassa olevilla attribuuteilla ja funktioilla on lomake["etunimi"].value = muokattava_henkilo["etunimi"]; lomake.sukunimi.value = muokattava_henkilo["sukunimi"]; // luodaan lukuja varten omat input-elementit // jos inputeja on jo olemassa niin käytetään niitä fieldset.style.display = "block"; // laitetaan fieldsetiin talteen viite luvut-taulukkoon fieldset.luvut = henkilo.luvut; let i = 0; for(; i<henkilo.luvut.length; i++) { let luku = henkilo.luvut[i]; let input = fieldset.children[i]; // jos inputeja on liian vähän niin luodaan lisää if ( !input ) { input = document.createElement("input"); input.size = "3"; fieldset.appendChild(input); } // luku voi olla 0, joka on falsy: siksi vertailu undefined-arvoon input.value = (luku === undefined) ? "" : luku; // tallennetaan luvut-taulukon indeksi omana ominaisuutena, jotta // yksi yhteinen käsittelijä tietää mihin kohtaan taulukkoa kirjoittaa input.indeksi = i; // asetetaan näkyville input.style.display = "inline-block"; // Käytetään yhtä ja samaa käsittelijäfunktiota kaikille kentille. // Se lukee indeksin kentältä itseltään, joten omaa funktiota ei // tarvitse luoda jokaiselle kentälle erikseen. // // HUOM. aiemmassa versiossa tässä luotiin uusi funktio joka kerta ja // yritettiin poistaa edellinen removeEventListener-metodilla. Se ei // toimi: poistoon on annettava täsmälleen sama funktio-olio kuin // lisättäessä, ja uusi funktio on aina eri olio. Käsittelijät siis // kasautuivat kenttiin. Nyt funktio on sama, joten toistuva // addEventListener-kutsu ei lisää sitä uudestaan. input.addEventListener("input", paivita_luku); } // piilotetaan mahdolliset ylimääräiset inputit for( ;i<fieldset.children.length; i++) { fieldset.children[i].style.display = "none"; } } // yksi yhteinen käsittelijä kaikille lukukentille function paivita_luku(e) { const kentta = e.currentTarget; muokattava_henkilo["luvut"][kentta.indeksi] = kentta.value; } // tallennetaan muutokset oikeaan henkilöobjektiin // tässä voisi vielä tehdä halutessaan tarkistuksia lomake.addEventListener("submit", function (e) { e.preventDefault(); alkuperainen_henkilo["etunimi"] = muokattava_henkilo["etunimi"]; alkuperainen_henkilo["sukunimi"] = muokattava_henkilo["sukunimi"]; // nyt riittää pelkkä sijoitus, koska korvataan alkuperäiseen taulukkoon ollut viite uudella alkuperainen_henkilo["luvut"] = muokattava_henkilo["luvut"]; // päivitetään listaus vastaamaan uusia tietoja alkuperainen_henkilo["listaus"]["etunimi"].textContent = muokattava_henkilo["etunimi"]; alkuperainen_henkilo["listaus"]["sukunimi"].textContent = muokattava_henkilo["sukunimi"]; for( let i=0; i< muokattava_henkilo["luvut"].length; i++ ) { let rivi = alkuperainen_henkilo["listaus"]["luvut"].children[i]; rivi.textContent = muokattava_henkilo["luvut"][i]; } // tyhjätään lomake lomake.reset(); fieldset.style.display = "none"; }); // päivitetään yksittäisen kentän muutokset henkilöobjektin klooniin // tässä voisi myös tehdä tarkistuksia syötteille ja asettaa virheilmoituksia lomake.etunimi.addEventListener("input", function(e) { muokattava_henkilo["etunimi"] = e.target.value; }); lomake.sukunimi.addEventListener("input", function(e) { muokattava_henkilo["sukunimi"] = e.target.value; });
Konsoli