Generant finestres modals amb Javascript pur i el seu CSS a l’gusto17.7.17

Generant finestres modals amb Javascript pur i el seu CSS a l’gust

⧉ 17.7.17 ⚑ @JorgeMg ⚑ convidat ⚑ javascript ✎ 16 de COLABORACIÓN AUTOR INVITADO

Nota de l’Editor: el post que et disposes a llegir és obra de Jorge MG publicat en EsCss com a article d’Autor Convidat . En ell trobaràs la manera de crear modals amb el mínim de javascript pur (sense necessitat de llibreria alguna) i així poder aplicar els estils que creguis convenients o necessitis.
Com tots els articles d’autoria aliena queda fora de la llicència Beerware de l’ bloc. Qualsevol aspecte relacionat amb això has de tractar directament amb el seu autor.
Després d’una apropiació indeguda per part d’algú, aquest post i l’script estan com a Copyright (C) 2016 Jorge Mg (@itsjorgemg a Twitter)

finestres modals

Les funcions de JavaScript alert(), confirm() i prompt() són molt útils per mostrar o demanar dades a l’hora de desenvolupar una pàgina web. Això sí, en el moment de producció, el seu principal inconvenient és que el seu disseny i estil depèn de el navegador, i no podem modificar-lo.

Per solucionar aquest aspecte, crearem la nostra pròpia finestra modal amb JavaScript amb el mateix funcionament que les generades pel navegador, però amb l’avantatge de poder afegir estils via CSS.

Si busques per Internet, segur que trobaràs milers de realitzacions com aquesta, però és possible que depenguin d’altres llibreries o tinguin multitud d’opcions que mai vas a utilitzar. Així que l’objectiu d’aquest article és saber com muntar la teva pròpia finestra modal, pas a pas, amb el mínim codi possible (1 única funció JavaScript) i amb les opcions que necessitis.

si vols que alguna cosa surti tal com tu vols, fes-ho tu mateix.

Per cert, si trobes a faltar alguna opció, o decideixes ampliar aquesta demo, t’animo a que ho expliquis en un comentari.

Paràmetres i opcions de la finestra

Abans de començar a escriure el codi, cal plantejar les funcions que podrà fer. D’aquesta manera, per a executar la funció que mostrarà la finestra caldrà indicar els següents paràmetres:

  • id: un identificador per poder incloure una finestra dins d’una altra
  • data: un objecte JSON amb les següents propietats:
    • title: títol de la finestra que es mostrarà en la seva capçalera
    • width: ample de el panell, en píxels
    • height: alçada de el panell, en vh. Per exemple, el valor 50 farà que l’altura de la modal sigui la meitat que la de la pantalla (viewport). Tant l’ample com l’alt estaran controlats des CSS per impedir que la modal sigui més gran que el viewport, sigui quin sigui el valor indicat en aquestes propietats
    • content: missatge que mostrarà la finestra. Pot ser codi HTML. Des CSS podrem especificar algunes regles per quan es mostri únicament una imatge o un iframe en la modal
  • ok: opcionalment, podem mostrar un botó de l’tipus “D’acord” o “OK “. Aquest paràmetre ha de ser una matriu, el primer ítem serà el títol de el botó i el segon, una funció que s’executarà a l’prémer el botó
  • cancel: opcional, amb el mateix funcionament que l’anterior, però de el tipus “Cancel”. També ha de ser una matriu de el tipus:
  • input: opcional, per mostrar un quadre de text, de la mateixa manera que la funció prompt(). Aquest paràmetre ha de ser una matriu d’un únic element: el placeholder de l’input. Perquè es pugui mostrar aquest input, cal indicar el paràmetre “ok”, ja que el text escrit es rebrà com a paràmetre de la funció

Generant la finestra i els seus botons

Amb aquest plantejament, ja podem començar. La modal no estarà escrita en l’HTML, així que primer de tot caldrà crear-via JavaScript. Després d’obrir-se una vegada, la modal ja existirà i per tant, no caldrà tornar-la a generar. Comento el codi per anar indicant pas a pas què és el que estem fent.

data=data || {}; // si no existe data, creamos un objeto vacío para evitar posteriores erroresid="modal-"+id; // añadimos "modal-" a la id para evitar conflictos con otros elementosif (document.getElementById(id)==null) { // solo hace falta crearla si no existe var d=document.createElement("div"); d.className="jmgmodal"; // clase para estilizarla vía CSS d.id=id; // creamos el panel interior var p=document.createElement("div"); p.className="panel"; // creamos los componentes de la cabecera: título y botón de cerrar var t=document.createElement("div"); t.className="title"; var cl=document.createElement("div"); cl.className="close"; cl.innerHTML='×'; // cerramos y vaciamos la modal al pulsar el botón X cl.addEventListener('click',function(ev) { ev.preventDefault(); var dTop=this.parentNode.parentNode; dTop.classList.remove("visible"); dTop.querySelector(".panel .content").innerHTML=''; }); // creamos la caja donde se mostrará el contenido var ct=document.createElement("div"); ct.className="content"; // también añadimos un pie, para añadir los posibles botones var f=document.createElement("div"); f.className="footer"; /* finalmente, añadimos "t", "cl", "ct" y "f" (título, botón cerrar, div contenido y div footer) a "p" (panel interior), éste lo añadimos a "d" (div principal, para oscurecer el fondo), y "d" lo añadimos al body de la página */ p.appendChild(t);p.appendChild(cl);p.appendChild(ct);p.appendChild(f); d.appendChild(p); document.body.appendChild(d);}

Bàsicament consisteix a jugar amb document.createElement() i elmt.appendChild() juntament amb els atributs de cada element per aconseguir l’estructura desitjada.

Ja tenim la finestra creada. Ara cal omplir tots els seus components amb les dades corresponents.

// guardamos cada componente en una variablevar mod=document.getElementById(id),p=mod.querySelector(".panel"),t=mod.querySelector(".panel .title"),ct=mod.querySelector(".panel .content"),f=mod.querySelector(".panel .footer");if (f==null) { // puede ocurrir que el footer no exista, así que lo volvemos a crear mod.classList.remove("nofooter"); var f=document.createElement("div"); f.className="footer"; p.appendChild(f);}// rellenamos los datost.innerHTML=data.title || '';ct.innerHTML=data.content || '';f.innerHTML='';// comprobamos que el número es válido antes de añadirloif (!isNaN(data.width)) p.style.maxWidth=data.width+'px';if (!isNaN(data.height)) p.style.maxHeight=data.height+'vh';

D’ençà, cal destacar un parell d’aspectes. El primer és l’ús de variable=valor || ''. Això assigna el valor a la variable, però, en cas que aquest valor sigui invàlid (null o undefined), es farà servir el següent valor indicat després de la doble barra vertical.En aquest cas, es quedarà en blanc, però seria recomanable canviar-lo pel títol de la teva pàgina, per exemple.

El segon aspecte és l’ús de la funció isNaN(). Aquesta funció retorna true si el paràmetre indicat no és un nombre (ja que NaN significa Not A Number). Per tant, a l’negar-ho sabem que la condició només s’executarà quan el valor de l’alt i l’ample sigui vàlid.

A continuació, vam seguir afegint els botons que apareixeran en el footer:

// si es necesario, creamos el botón "Aceptar"if (ok && ok.length>1) { // la variable "param" se usará para devolver el valor del input // si no hubiera input, el valor será null var param={value:null}; // si es necesario, creamos un párrafo nuevo con su input if (input && input.length>0) { var ph=document.createElement("p"); ph.className="action"; var txt=document.createElement("input"); txt.className="action"; txt.setAttribute("placeholder",input); txt.addEventListener('keydown',function(ev) { // pulsar "enter" es equivalente a pulsar "aceptar" if (ev.keyCode==13 || ev.key=="Enter") { ev.preventDefault(); mod.classList.remove("visible"); ok(param.value); } }); // añadimos el input al párrafo, y éste al contenido ph.appendChild(txt); ct.appendChild(ph); // guardamos la referencia al input param=ct.querySelector("p.action > input.action"); /* ponemos el foco al input, pero esperamos unos milisegundos para que se genere */ setTimeout(function(){ param.focus(); },100); } // creamos el botón OK var bOk=document.createElement("button"); bOk.className="action"; bOk.innerHTML=ok; bOk.addEventListener('click',function(ev) { /* al pulsar en él, se cierra la ventana y se ejecuta la función indicada, devolviendo el valor del input (si existe) o null */ ev.preventDefault(); mod.classList.remove("visible"); ok(param.value); }); // añadimos el botón al footer f.appendChild(bOk);}

Fins aquí ja tenim el botó “D’acord” i l’input, si cal crear-los. Com el quadre de text no forma part de cap formulari, a l’prémer Intro, no passarà res. Això ho solucionem afegint l’esdeveniment keydown, on vam comprovar si la tecla premuda és l’Intro, i en aquest cas tanquem la finestra i executem la funció corresponent a el botó OK, passant-li com a paràmetre el valor de l’input.

A més , per comoditat de l’usuari, posem el focus en l’input només obrir la finestra. Li posem un retard de 100 mil·lisegons (valor totalment arbitrari) perquè doni temps a que la finestra estigui creada i oberta, ja que si ho fem immediatament l’element encara no està visible a la pàgina.

A l’executar la funció de el botó OK, li passem el paràmetre param.value. Si no hi ha input, aquest valor serà null, mentre que quan sí ho faig, el valor correspondrà a el text introduït. D’aquesta manera, no hem de preocupar de controlar si cal passar o no el paràmetre i evitem possibles errors.

El codi per crear el botó Cancel és molt similar a l’anterior, però el fet de no utilitzar cap paràmetre el fa més simple.

// hacemos lo mismo con el botón Cancelar, si existeif (cancel && cancel.length>1) { var bCancel=document.createElement("button"); bCancel.className="action"; bCancel.innerHTML=cancel; bCancel.addEventListener('click',function(ev) { ev.preventDefault(); mod.classList.remove("visible"); cancel(); }); f.appendChild(bCancel);}// si no hay ningún botón, se elimina el footerif (f.innerHTML=='') { p.removeChild(f); mod.classList.add("nofooter");}/* esperamos unos milisegundos para que se genere,y añadimos la clase .visible para mostrarla desde CSS */setTimeout(function(){ mod.classList.add("visible");},50);

Finalment, cal comprovar si s’han creat els botons. Si no és així, eliminem el footer, ja que no es va a usar, i ho indiquem amb la classe “nofooter”, que farem servir des CSS. Fet això, només queda afegir la classe “visible”, que mostrarà la finestra. El motiu pel qual el retardem 50 mil·lisegons és, igual que abans amb l’input, perquè esperem al fet que la finestra s’hagi generat.

Demo i consideracions finals

Només falta ficar tot el codi en una funció, que executarem quan vulguem obrir la finestra. Aquest és el resultat i demo de les opcions de què disposa la finestra modal:

Veure the Pen jmgModal window by Kseso (@Kseso) on CodePen.

A la part de l’CSS no hi ha res de nou a destacar (si de cas, s’admeten millores de disseny …). Bàsicament, el més important és la classe .visible, que controlarà l’obertura de la finestra. Els selectors usats es poden optimitzar, però he preferit deixar-los així per a una millor comprensió de l’element a què apunten.

Sobre el funcionament de la finestra, he optat per mantenir sempre operatiu el botó X de tancament perquè sigui el menys intrusiva per a l’usuari.

Deixa un comentari

L'adreça electrònica no es publicarà. Els camps necessaris estan marcats amb *