Programació amb el DOM i la biblioteca jQuery

La biblioteca jQuery és una de les més populars, ja que inclou tota una sèrie de funcionalitats que són bàsiques per a qualsevol aplicació web amb un mínim de complexitat; en concret:

Tot i que les versions anteriors de jQuery eren compatibles amb navegadors antics, les noves versions només admeten navegadors actuals.

  • D’una banda, simplifiquen la realització de tasques com gestionar el DOM, els events o les peticions asíncrones.
  • De l’altra, evita que hàgiu de fer servir implementacions alternatives (per a navegadors actuals) per característiques que potser no estan definides de forma estricta a l’especificació del W3C.

Concretament, pel que fa a la manipulació del DOM, jQuery ofereix una gran quantitat de mètodes que ajuden a gestionar els elements del DOM i permeten crear-ne de nous o modificar els actuals d’una manera més simple. Cal destacar especialment la potència del seu sistema de selecció, que va molt més enllà del que permeten les interfícies del DOM.

A més a més, donada la seva popularitat, és fàcil trobar centenars de connectors lliures, així com privatius, per ampliar les seves funcionalitats o per afegir fàcilment nous elements, com poden ser galeries d’imatges, carrusels… És a dir, en molts casos podreu recórrer a components completament desenvolupats i testats per utilitzar-los directament o adaptar-los a les vostres necessitats en lloc de començar des de zero.

Introducció a jQuery

El primer pas a l’hora d’utilitzar jQuery és carregar la biblioteca. La forma de fer-ho variarà segons l’entorn en el qual s’hagi d’utilitzar i les vostres preferències quant al seu allotjament.

Cal recordar que no és segur manipular el DOM abans que acabi de carregar, per aquesta raó jQuery inclou els seus propis sistemes per detectar-ho i permetre que el desenvolupador escrigui codi, que començarà a executar una vegada finalitzi la càrrega.

El primer que trobareu en començar a treballar amb jQuery és que cal distingir entre la funció jQuery() ,habitualment substituïda pel símbol $(), i els objectes jQuery, que són generats o bé per la mateixa funció o per la crida d’algun mètode sobre altres objectes jQuery.

Generalment treballareu amb la funció jQuery() per obtenir un objecte que contingui els elements seleccionats, i a partir de llavors les crides a tots els mètodes les fareu sobre l’objecte que s’ha de manipular, per exemple, cridant el mètode addClass() per afegir una classe nova als elements.

Cal recordar que tot i que l’estructura dels programes que fan servir jQuery poden semblar molt diferents dels programes en JavaScript, es tracta del mateix llenguatge. Segurament la major part de l’estranyesa es deu a la utilització del símbol $() com a àlies de la funció jQuery() i la utilització d’interfícies fluides.

Gairebé tots els mètodes dels objectes jQuery tenen un comportament fluid, és a dir, retornen el mateix objecte sobre el qual s’han invocat. Així doncs us podeu trobar amb estructures semblants a aquesta:

  1. $('p')
  2. .find('span')
  3. .addClass('vermell')
  4. .addClass('negreta')
  5. .css('font-size', '18px')
  6. .css('background-color', 'green');

Cal destacar que el mètode find() retorna un nou objecte jQuery que inclouria només els elements de tipus span descendents dels paràgrafs seleccionats inicialment i, per tant, els mètodes següents s’invocarien a partir d’aquest nou objecte i no de l’original.

Fixeu-vos també que només l’última línia inclou el punt i coma; és a dir, aquest codi es podria interpretar com si fos una sola línia:

  1. $('p').find('span').addClass('vermell').addClass('negreta').css('font-size', '18px').css('background-color', 'green');

Com es pot apreciar, el primer cas és més fàcil de llegir i d’entendre. Una altra manera d’escriure el mateix codi seria guardant el valor de la selecció inicial (un objecte jQuery en una variable):

  1. let $paragraf = $('p');
  2. let $span = $paragraf.find('span');
  3.  
  4. $span.addClass('vermell');
  5. $span.addClass('negreta');
  6. $span.css('font-size', '18px');
  7. $span.css('background-color', 'green');

Tot i que el resultat és el mateix en tots tres casos, quan es treballa amb jQuery, per convenció s’acostuma a utilitzar el format del primer exemple, que genera un codi més concís i més clar.

Carregar la biblioteca jQuery

A l’hora d’incloure la biblioteca jQuery a les vostres aplicacions, el primer pas és carregar la biblioteca. Per fer-ho, les dues opcions més habituals són descarregar la biblioteca i afegir-la juntament amb la resta de fitxers JavaScript de la vostra aplicació o fer que els clients la descarreguin a través d’un CDN.

Xarxes de lliurament de continguts (CDN)

Un CDN és una xarxa de servidors localitzats en diferents punts geogràfics però amb els mateixos continguts, de manera que quan es rep una petició del fitxer aquest és enviat des del servidor més proper fins a l’usuari. D’aquesta manera, s’augmenta la velocitat de la resposta. Podeu trobar més informació sobre les xarxes de lliurament de continguts en l’enllaç següent: www.ca.wikipedia.org/wiki/Xarxa_de_lliurament_de_continguts.

La diferència entre fer servir la versió comprimida o sense comprimir és que la primera ocupa menys espai però és inintel·ligible: com que són fitxers de text pla, no es tracta exactament d’una compressió sinó d’una minimització. Així doncs, a l’hora de desplegar l’aplicació s’ha de fer servir la versió comprimida, mentre que durant el desenvolupament és recomanable utilitzar la versió sense minimitzar per poder depurar millor el codi.

Per descarregar la biblioteca jQuery heu de visitar el web següent: www.jquery.com/download/. Entre d’altres descàrregues, hi trobareu la versió més actual comprimida i sense comprimir. Només cal descarregar-ne un dels dos, segons les vostres necessitats.

La minimització (de l’anglès minification) consisteix a eliminar tots els espais i salts de línies i substituir els noms de variables i funcions per altres amb el mínim nombre de caràcters possibles; així, totalProductes = preUnitari * quantitatProducte; es convertiria en alguna cosa semblant a a=b*c.

Una vegada descarregada la biblioteca podeu afegir-la al vostre programa com qualsevol altre fitxer de codi JavaScript. Suposant que esteu treballant amb la versió 3.5.1 i que l’heu copiat a un directori anomenat js dintre del vostre projecte, el codi seria el següent:

  1. <script src="js/biblioteques/jquery-3.5.1.js"></script>

Per evitar el bloqueig de la pàgina es recomana que tots els fitxers amb codi JavaScript, propi o biblioteques, s’afegeixin al final de la pàgina abans de tancar l’element body, en comptes de fer-ho al principi dintre de l’element head.

Un avantatge de treballar directament amb la biblioteca és que podeu utilitzar eines d’automatització com Gulp o Grunt per concatenar tots els fitxers JavaScript en un de sol i a continuació minimitzar-lo abans de fer el desplegament de l’aplicació. D’aquesta manera es redueix el nombre de fitxers i s’optimitza el temps de descàrrega de la pàgina.

Les eines d’automatització per treballar amb JavaScript requereixen tenir instal·lat Node.js i s’utilitzen a través del terminal.

D’altra banda, si feu servir un CDN, no cal descarregar el fitxer: el fitxer es descarregarà per a cada client des del servidor.

  1. <script src="https://code.jquery.com/jquery-3.5.1.js" integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc=" crossorigin="anonymous"></script>

Independentment del sistema que feu servir, sempre s’ha de carregar la biblioteca abans d’utilitzar-la, perquè si ho feu en l’ordre contrari, és molt possible que es produeixi un error quan la vulgueu fer servir.

L’atribut integrity és un codi hash criptogràfic que permet als navegadors verificar que el CDN que anem a usar no han sigut objecte de cap manipulació.

Carregar jQuery a CodePen

Atès que l’ús de la biblioteca jQuery està tan estès, moltes plataformes d’edició de codi en línia, com CodePen, ofereixen la possibilitat d’incloure’l directament sense haver d’afegir la càrrega manualment.

Fitxers externs a CodePen

A CodePen és possible afegir tant algunes de les biblioteques de JavaScript més conegudes com els vostres propis fitxers externs amb codi JavaScript. A més a més, es poden fer servir preprocessadors com Babel o TypeScript per utilitzar ES6 o TypeScript en lloc de JavaScript.

Per accedir a aquestes opcions en els vostres propis pens (nom que rep cada demostració a CodePen) només heu de fer clic al botó Settings, a continuació, en el panell de configuració, feu clic a la pestanya JS i a la caixa de cerca Add External Scripts/Pens, escrivim jQuery i la seleccionem a la llista desplegable. Podeu veure les opcions de configuració de CodePen a la figura

Figura Opcions de configuració de CodePen

Cal destacar que quan s’afegeix la biblioteca d’aquesta manera, no cal afegir el codi per carregar-la perquè es carrega automàticament; per tant, es pot procedir a treballar directament amb la biblioteca. Per aquesta raó, la càrrega de la biblioteca no es mostra en cap dels exemples: es carrega automàticament gràcies a la configuració del pen.

Primers passos amb jQuery

A banda d’indicar que s’ha de carregar la biblioteca, us heu d’assegurar que el document (més concretament el DOM) ha carregat completament. En cas contrari, és possible que es produeixin errors. Per aquesta raó la primera línia que trobareu habitualment en el codi que fa servir jQuery és el següent:

  1. $(document).ready(function() {
  2. // Aquí va el nostre codi
  3. console.log('Llestos!');
  4. });

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/jrbQVZ?editors=0011.

O també podem usar la seva forma abreviada:

  1. $(function() {
  2. // Aquí va el nostre codi
  3. console.log('Llestos!');
  4. });

D’aquesta manera us assegureu que és segur començar a treballar amb el document, ja que el DOM estarà completament carregat.

Recordeu que en cas de fer la prova en un fitxer local, heu d’incloure la càrrega de la biblioteca. En primer lloc, haureu d’afegir la càrrega de la biblioteca i, seguidament, el vostre codi d’inicialització. Així doncs, si feu servir el CDN de jQuery, el vostre codi quedaria així:

  1. <script src="https://code.jquery.com/jquery-3.5.1.js" integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc=" crossorigin="anonymous"></script>
  2.  
  3. <script>
  4. $(document).ready(function() {
  5. // Aquí va el nostre codi
  6. console.log('Llestos!');
  7. });
  8. </script>

L’efecte d’utilitzar el mètode ready() de jQuery és molt similar al detector de l’event load de JavaScript però no és el mateix. No s’han d’utilitzar mai tots dos sistemes al mateix temps perquè són incompatibles, en cas de fer servir jQuery, sempre s’ha d’utilitzar el mètode ready() que facilita la biblioteca.

Fixeu-vos en la primera línia: $(document).ready(). Aquesta línia fa dues accions diferenciades: d’una banda, es crida la funció jQuery() fent servir la drecera $(), i de l’altra, sobre l’objecte retornat s’invoca el mètode ready() passant com a argument una funció, que serà executada una vegada es carregui el DOM.

Atès que a JavaScript les funcions són objectes de primera classe, es poden referenciar fent servir variables i passar-les com a arguments a altres funcions.

És a dir, es podria haver escrit jQuery(document).ready() i hauria estat el mateix. Recordeu que el símbol $ és utilitzable com a nom de variable, tot i que no és recomanable fer-lo servir, perquè algunes biblioteques (com aquesta) el fan servir amb fins específics.

D’altra banda, per convenció, sí que s’acostuma a prefixar el nom de les variables que emmagatzemen objectes de tipus jQuery amb el símbol $. Així doncs, si una variable conté un objecte jQuery que conté, al seu torn, una llista de matrícules, és correcte que el seu nom sigui $matricules.

La funció que es passa com a argument –i serà cridada en carregar el DOM–, no cal que estigui definida com a funció anònima, es podria haver passat una variable que referenciés una funció:

  1. let inicialitzacio = function() {
  2. console.log('Llestos!');
  3. }
  4.  
  5. $(document).ready(inicialitzacio);

Podeu veure aquest exemple en el següent enllaç: codepen.io/ioc-daw-m06/pen/zKvrdx?editors=0011.

Fixeu-vos en un detall important: en cas que la funció estigui referenciada per una variable en lloc d’estar declarada directament, s’ha d’assignar la funció a aquesta variable abans d’invocar el mètode ready(). Si no es fa així, el valor de la variable es passarà com a null (a causa del hoisting: internament, la declaració de les variables es mou al principi del bloc però sense el valor assignat).

En canvi, si es passa el nom de la funció, l’ordre no importa perquè s’inclou el cos de la funció quan es produeix el hoisting:

  1. $(document).ready(inicialitzacio);
  2.  
  3. function inicialitzacio() {
  4. console.log('Llestos!');
  5. }

Podeu veure aquest exemple en el següent enllaç: codepen.io/ioc-daw-m06/pen/BLoGdx?editors=0011.

Cal destacar que només cal incloure dins de la funció d’inicialització les crides a les funcions; la declaració, en canvi, es pot realitzar en qualsevol altre lloc (per exemple, en altres fitxers) sempre que us assegureu que l’ordre de declaració i assignació és el correcte.

Per exemple, es podrien invocar múltiples funcions dintre de la funció inicialització() mentre que es tingui la garantia que aquestes han estat invocades una vegada s’ha completat la càrrega del DOM (com succeeix quan s’invoca inicialització()):

  1. let inicialitzacio = function() {
  2. mostrarMissatge('DOM carregat');
  3. iniciarAplicacio();
  4. }
  5.  
  6. let mostrarMissatge = function(missatge) {
  7. console.log(missatge);
  8. }
  9.  
  10. let iniciarAplicacio = function() {
  11. console.log("Iniciant l'aplicació...");
  12. }
  13.  
  14. $(document).ready(inicialitzacio);

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/PGPxQR?editors=0011.

L'objecte 'jQuery'

Quan es parla de jQuery cal diferenciar entre la biblioteca, la funció i els objectes de tipus jQuery.

En el primer cas, es fa referència a la biblioteca en conjunt, per exemple: ‘carregar jQuery’ significa que s’ha de carregar la biblioteca, ja sigui com un fitxer de codi JavaScript o a través d’un CDN.

En el segon cas, fa referència a la funció jQuery(), que pot invocar-se com a jQuery() o $(). Aquesta funció permet, d’una banda, crear objectes de tipus jQuery, i de l’altra, accedir a una sèrie de mètodes genèrics, com per exemple each(), que permet iterar sobre un array.

Recordeu que a JavaScript les funcions també són objectes i, per consegüent, poden contenir propietats i mètodes.

En el tercer cas, un objecte de tipus jQuery fa referència a un objecte retornat per la funció, sigui generat o com a resultat d’una cerca i que hi pot tenir associats un o més elements. En qualsevol cas es tracta sempre com una col·lecció d’elements.

Una peculiaritat dels objectes de tipus jQuery és que quan s’invoquen els seus mètodes afecten, generalment, tota la col·lecció. Per exemple, si un objecte conté una col·lecció amb tots els enllaços de la pàgina i s’hi afegeix la detecció de l’event click, aquesta detecció afectarà tots els elements d’una tacada, no cal recórrer la col·lecció i afegir-lo d’un en un. Fixeu-vos en l’exemple següent:

  1. <h1>Primer títol</h1>
  2. <p>Primer paràgraf</p>
  3. <h1>Segon títol</h1>
  4. <p>Segon paràgraf</p>
  5. <h1>Tercer títol</h1>
  6. <p>Tercer paràgraf</p>
  7.  
  8. <script>
  9. let inicialitzacio = function() {
  10. let $paragrafs = $('p');
  11. $paragrafs.hide();
  12. }
  13.  
  14. $(document).ready(inicialitzacio);
  15. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/amvxrY?editors=1010.

Com es pot apreciar, dins de la funció inicialització() s’invoca la funció jQuery() passant com a paràmetre p; això indica que s’ha de fer una cerca de tots els elements de tipus p. Així doncs, el retorn de la funció és un nou objecte jQuery que conté tots els elements de tipus p.

Seguidament, es crida al mètode hide() d’aquest objecte per amagar tots els paràgrafs. Fixeu-vos que no cal iterar sobre els elements de la col·lecció, simplement cridant el mètode ja s’aplica el canvi a tots ells.

El mètode hide() dels objectes jQuery amaguen tots els objectes continguts, mentre que el mètode show() els mostra.

Si proveu de mostrar l’objecte jQuery per la consola, veureu que té algunes característiques similars als arrays, ja que es tracta d’una col·lecció d’elements. Afegiu el codi següent al codi anterior, dins de la funció inicializació():

  1. console.log($paragrafs);

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/rrxvgj?editors=1011 i el resultat de la consola a la figura.

Figura
Objecte jQuery a la consola

Per visualitzar el resultat s’ha de fer servir la consola de les eines de desenvolupador del navegador, ja que la consola de CodePen no mostra correctament la informació.

Fixeu-vos que hi ha els tres elements seleccionats amb el seu índex corresponent: ‘0’, ‘1’, ‘2’ i, seguidament, el valor de la propietat length. Si desplegueu qualsevol dels objectes niats, veureu tota l’estructura interna de cadascun d’aquests elements.

Tot i que aquestes propietats i mètodes es gestionen a través de la interfície proporcionada per la biblioteca, és interessant saber com es pot consultar aquesta informació directament al vostre navegador.

En alguns casos necessitareu accedir directament a algun dels elements seleccionats per l’objecte jQuery. Es poden obtenir tant tots els elements com un element en particular. En tots dos casos s’accedeix a través del mètode get():

  • Si es passa un nombre com a argument, es retornarà l’element en aquesta posició.
  • Si no es passa cap argument, es retornarà un array amb tots els elements.

Alternativament, es pot accedir directament a l’element com si es tractés d’un array (per exemple, $paragrafs[1]). D’altra banda, no hi ha cap garantia que l’accés directe a l’índex d’un objecte jQuery continuï funcionant en versions futures, per consegüent, es desaconsella fer-lo servir.

Vegeu a continuació un exemple que inclou els tres mètodes d’accés i els mostra per la consola:

  1. <p>Primer paràgraf</p>
  2. <p>Segon paràgraf</p>
  3. <p>Tercer paràgraf</p>
  4.  
  5. <script>
  6. let inicialitzacio = function() {
  7. let $paragrafs = $('p');
  8. console.log("Element (primer paràgraf):", $paragrafs.get(0));
  9. console.log("Element (últim paràgraf):", $paragrafs[2]);
  10. console.log("Col·lecció d'elements (tots els paràgrafs):", $paragrafs.get());
  11. }
  12.  
  13. $(document).ready(inicialitzacio);
  14. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/gwAOVv?editors=1011.

Malauradament alguns dels objectes mostrats són massa complexos per a la consola de CodePen, així que haureu de consultar el resultat a la consola de les eines de desenvolupador del vostre navegador.

Com es pot comprovar, tant $paragrafs.get(0) com $paragrafs[2] retornen un element del DOM, mentre que paragrafs.get() retorna un array amb tots els elements. Cal destacar que el retorn del mètode get() (o l’accés directe) són elements del DOM, no objectes jQuery.

Així doncs, no es poden invocar mètodes de jQuery sobre aquests elements. Per poder manipular-los individualment utilitzant la biblioteca s’hauran de convertir en objectes jQuery, passant-los com argument a la funció jQuery():

  1. <style>
  2. .vermell {
  3. color: red;
  4. }
  5. </style>
  6.  
  7. <p>Primer paràgraf</p>
  8. <p>Segon paràgraf</p>
  9. <p>Tercer paràgraf</p>
  10.  
  11. <script>
  12. let inicialitzacio = function() {
  13. let $paragrafs = $('p');
  14. let $paragraf2 = $($paragrafs.get(1));
  15. $paragraf2.addClass('vermell');
  16. }
  17.  
  18. $(document).ready(inicialitzacio);
  19. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/NRNPpJ.

Fixeu-vos que primer s’han cercat tots els paràgrafs i, seguidament, s’ha tornat a cridar la funció jQuery() passant com a argument el segon element dels seleccionats. El resultat és un nou objecte jQuery que conté només aquest element i, per tant, es pot manipular.

Selectors

Per generar un objecte jQuery que contingui una llista d’elements ja existents, s’ha d’invocar la funció jQuery() passant com a argument un selector. Aquest selector pot ser de diferents tipus, no està limitat als selectors de CSS com en el cas dels mètodes que formen part de l’especificació del DOM.

Tot i que la funcionalitat és similar a la dels mètodes querySelector() i querySelectorAll() de les interfícies del DOM Document i Elements, en comptes d’un element o una col·lecció d’elements, aquest mètode sempre retorna un objecte jQuery. Aquest objecte conté tots els elements que coincideixen amb la selecció. Fins i tot en cas que no hi hagi cap element coincident, es retornarà un objecte però amb longitud 0 (propietat length).

Els tipus de selectors disponibles es poden dividir en dos grans grups:

Llista de selectors

Podeu trobar una llista completa dels selectors en l’enllaç següent: www.goo.gl/jn30Ta.

  • Selectors CSS: aquests selectors es corresponen exactament amb els selectors CSS, inclosos els selectors afegits a CSS3.
  • Selectors propis de jQuery: aquests són selectors específics de jQuery, que funcionen com dreceres per a seleccions molt comunes, com per exemple :image –per seleccionar totes les imatges– o :input –per seleccionar tots els elements d’entrada de dades (input, textarea, select i button).

Cal destacar que mentre que la funció jQuery() fa la cerca a tot el document, hi ha un altre mètode que forma part de tots els objectes jQuery i permet cercar, només, entre els elements descendents: és el mètode find().

Selectors CSS

Aquest tipus de selectors són els més utilitzats perquè ofereixen les mateixes possibilitats que CSS i, per tant, la majoria de desenvolupadors web les coneixen.

Podeu trobar més informació sobre els selectors CSS a l’apartat “Programació amb el DOM (Document Object Model)” d’aquesta unitat.

Per demostrar el funcionament d’aquests selectors s’afegirà una classe CSS que pintarà el fons de l’element de color vermell. Vegeu-ne un primer exemple, en el qual se seleccionen tots els elements amb el tag p:

El mètode addClass() de la biblioteca jQuery afegeix la classe passada com a argument als elements seleccionats.

  1. <style>
  2. .vermell {
  3. background-color: red
  4. }
  5. </style>
  6.  
  7. <h1>Primer títol</h1>
  8. <p>Primer paràgraf</p>
  9. <h1>Segon títol</h1>
  10. <p>Segon paràgraf</p>
  11. <h1>Tercer títol</h1>
  12. <p>Tercer paràgraf</p>
  13.  
  14. <script>
  15. let inicialitzacio = function() {
  16. let $paragrafs = $('p');
  17. $paragrafs.addClass('vermell');
  18. }
  19.  
  20. $(document).ready(inicialitzacio);
  21. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/LRGVYg.

Com es pot apreciar, el fons de tots els paràgrafs es pinta de color vermell, perquè s’ha afegit la classe vermell a cadascun. A més a més, podeu fer servir les eines de desenvolupador per inspeccionar el codi HTML de la pàgina i comprovar que, efectivament, s’ha afegit la classe.

Per descomptat, la selecció no es limita als elements, és possible combinar els selectors per filtrar per classes o identificadors:

  1. <style>
  2. .vermell {
  3. background-color: red
  4. }
  5.  
  6. .verd {
  7. background-color: green
  8. }
  9. </style>
  10.  
  11. <div>
  12. <h1>Primer títol</h1>
  13. <p>Primer paràgraf</p>
  14. <h1 class="central">Segon títol</h1>
  15. <p class="central">Segon paràgraf</p>
  16. <h1>Tercer títol</h1>
  17. <p id="tercer">Tercer paràgraf</p>
  18. </div>
  19. <p class="central">Aquest no canvia de color</p>
  20.  
  21. <script>
  22. let inicialitzacio = function() {
  23. let $central = $('div .central');
  24. $central.addClass('vermell');
  25.  
  26. let $tercer = $('#tercer');
  27. $tercer.addClass('verd');
  28. }
  29.  
  30. $(document).ready(inicialitzacio);
  31. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/ORrVNy.

Fixeu-vos que el selector div .central ha seleccionat tots els elements amb la classe central descendents d’un element de tipus div i, per consegüent, l’últim paràgraf no s’ha inclòs. D’altra banda, com era d’esperar, el fons de l’element amb identificador tercer s’ha pintat de color verd.

De la mateixa manera, es poden utilitzar els selectors d’atribut; això permet seleccionar elements segons quines siguin les característiques dels seus atributs:

  • Si un atribut es troba present: [atribut].
  • Si l’atribut conté un valor concret: [atribut=“valor”].
  • Si el seu valor comença pel valor: [atribut^=“valor”].
  • Si el seu valor acaba pel valor: [atribut$=“valor”].
  • Si l’atribut conté el valor: [atribut*=“valor”].

A continuació podeu comprovar el funcionament de dos d’aquests selectors. Primerament se cerquen tots els enllaços a la Wikipedia del document via el seu atribut href, i seguidament se cerquen només els de llengua anglesa (inclouen el subdomini en):

  1. <style>
  2. .vermell {
  3. background-color: red
  4. }
  5.  
  6. .resaltat {
  7. font-weight: bold;
  8. }
  9. </style>
  10.  
  11. <div>
  12. <h1>Primer títol</h1>
  13. <p>Primer paràgraf <a href="https://ca.wikipedia.org/wiki/Aut%C3%B2mat_finit">Enllaç a Automàt finit</a></p>
  14. <h1 class="central">Segon títol</h1>
  15. <p class="central">Segon paràgraf <a href="https://en.wikipedia.org/wiki/Finite-state_machine">Enllaç a Finite-state machines</a></p>
  16. <h1>Tercer títol</h1>
  17. <p id="tercer">Tercer paràgraf</p>
  18. </div>
  19. <p class="central">Aquest no canvia de color <a href="#">Un altre enllaç</a></p>
  20.  
  21. <script>
  22. let inicialitzacio = function() {
  23. let $wikipedia = $('[href*="wikipedia"]');
  24. $wikipedia.addClass('vermell');
  25.  
  26. let $angles = $('[href^="https://en.wikipedia.org"]');
  27. $angles.addClass('resaltat');
  28. }
  29.  
  30. $(document).ready(inicialitzacio);
  31. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/JRGdyx.

Com es pot apreciar, el primer selector ([href*=“wikipedia”]), selecciona només els enllaços que contenen la paraula wikipedia/ als quals s’afegeix la classe vermell, mentre que el segon ([href^=“https://en.wikipedia.org”]) selecciona només els que pertanyen al subdomini lligat a la llengua anglesa de la Wikipedia.

D’altra banda, l’enllaç Un altre enllaç no forma part de cap dels dos objectes jQuery, ja que encara que conté l’atribut href el seu valor no compleix les condicions de cap dels dos selectors.

Cal destacar que l’element que enllaça amb la pàgina anglesa forma part de les col·leccions d’elements de tots dos objectes (referenciats per $wikipedia i $angles), de manera que qualsevol mètode cridat sobre un o altre l’afectarà.

Per exemple, si afegiu el codi següent al final de la funció d’inicialització, s’amagaran tots dos enllaços:

$wikipedia.hide();

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/ozbXQv.

En canvi, si es crida el mateix mètode sobre $angles, s’amagarà només l’enllaç a la pàgina anglesa:

$angles.hide();

Podeu veure aquest exemple en el següent enllaç: codepen.io/ioc-daw-m06/pen/VKZLqY.

Així doncs, es pot concloure que l’element que enllaça amb la pàgina anglesa forma part de la col·lecció d’elements seleccionats per tots dos objectes jQuery.

També es poden fer servir pseudoclasses com first-child o last-child, com es pot comprovar en l’exemple següent:

  1. <style>
  2. .vermell {
  3. background-color: red;
  4. }
  5.  
  6. .verd {
  7. background-color: green;
  8. }
  9. </style>
  10.  
  11. <ul>
  12. <li>Primer element</li>
  13. <li>Segon element</li>
  14. <li>
  15. <ul>
  16. <li>Primer subelement</li>
  17. <li>Segon subelement</li>
  18. <li>Últim subelement</li>
  19. </ul>
  20. </li>
  21. <li>Últim element</li>
  22. </ul>
  23.  
  24. <script>
  25. let inicialitzacio = function() {
  26. let $primers = $('li:first-child');
  27. $primers.addClass('vermell');
  28.  
  29. let $ultims = $('li:last-child');
  30. $ultims.addClass('verd');
  31. }
  32.  
  33. $(document).ready(inicialitzacio);
  34. </script>

Podeu veure aquest exemple en l’ellaç següent: codepen.io/ioc-daw-m06/pen/GjoJZj.

Cal destacar que, tot i que no s’han mostrat exemples complexos, jQuery admet tot tipus de combinacions, incloent-hi la combinació amb els seus propis selectors.

Selectors propis de jQuery: seleccions especials

L’ús de selectors a jQuery no està limitat als selectors de CSS, ja que disposa d’un joc propi de selectors que simplifiquen encara més la selecció dels elements.

Llista de selectors de jQuery

Podeu trobar una llista completa dels selectors propis de jQuery en l’enllaç següent: www.goo.gl/zzvN1g.

Entre les dreceres genèriques que s’apliquen a tots els elements, la biblioteca disposa de les següents:

  • :first i :last: selecciona només el primer element o només l’últim, respectivament, de la selecció. Cal distingir entre la funcionalitat d’aquestes dreceres i la dels selectors de CSS first-child i last-child, ja que en aquest cas es tracta del primer o últim element que formaria part de la selecció, i no pas dels descendents concrets d’algun altre.
  • :header: selecciona totes les capçaleres, per exemple h1, h2, etc.
  • :even i :odd: selecciona els elements parells i els senars, respectivament.

Vegeu a continuació un exemple amb els selectors first i last que us ajudarà a entendre la diferència amb els selectors de CSS first-child i last-child:

  1. <style>
  2. .vermell {
  3. color: red;
  4. }
  5.  
  6. .verd {
  7. color: green;
  8. }
  9.  
  10. .negreta {
  11. font-weight: bold;
  12. }
  13.  
  14. .cursiva {
  15. font-style: italic;
  16. }
  17. </style>
  18.  
  19. <ul>
  20. <li>Primer element</li>
  21. <li>Segon element</li>
  22. <li>
  23. <ul>
  24. <li>Primer sub-element</li>
  25. <li>Segon sub-element</li>
  26. <li>Últim sub-element</li>
  27. </ul>
  28. </li>
  29. <li>Últim element</li>
  30. </ul>
  31.  
  32. <script>
  33. let inicialitzacio = function() {
  34. let $primers = $('li:first-child');
  35. $primers.addClass('vermell');
  36.  
  37. let $ultims = $('li:last-child');
  38. $ultims.addClass('verd');
  39.  
  40. let $primer = $('li:first');
  41. $primer.addClass('negreta');
  42.  
  43. let $ultim = $('li:last');
  44. $ultim.addClass('cursiva');
  45. }
  46.  
  47. $(document).ready(inicialitzacio);
  48. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/PGkjJO.

Com es pot apreciar, mentre que els selectors CSS han seleccionat els elements que són primer i últim element respecte al seu element pare, els selectors propis només han seleccionat el primer i l’últim element de la selecció, que en aquest cas correspon a tots els elements amb l’etiqueta li.

A continuació podeu veure un exemple d’utilització del selector header. Fixeu-vos que és possible tant seleccionar totes les capçaleres com combinar-lo amb un altre selector CSS per obtenir una selecció més concreta:

  1. <style>
  2. .vermell {
  3. color: red;
  4. }
  5.  
  6. .cursiva {
  7. font-style: italic;
  8. }
  9. </style>
  10.  
  11. <h1>Títol 1</h1>
  12. <p>Paràgraf 1</p>
  13. <div>
  14. <h2>Títol 2</h1>
  15. <p>Paràgraf 2</p>
  16. <p>Paràgraf 3</p>
  17. <h3>Títol 3</h3>
  18. <p>Paràgraf 4</p>
  19. </div>
  20.  
  21. <script>
  22. let inicialitzacio = function() {
  23. let $titols = $(':header');
  24. $titols.addClass('vermell');
  25.  
  26. let $titolsEnContenidor = $('div :header');
  27. $titolsEnContenidor.addClass('cursiva')
  28. }
  29.  
  30. $(document).ready(inicialitzacio);
  31. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/kkPwvd.

Els selectors odd i even serveixen per afegir efectes o comportaments diferents d’elements alterns, per exemple les files o columnes d’una taula. En aquest exemple s’han fet servir diferents colors per mostrar-ne el comportament, però cal tenir en compte que si només necessiteu canviar el format, el més recomanable és fer-ho directament a través de fulls d’estil i no pas modificant el document dinàmicament com s’ha fet aquí:

  1. <style>
  2. table {
  3. border-collapse: collapse;
  4. }
  5.  
  6. td {
  7. text-align: center;
  8. padding: 2px;
  9. }
  10.  
  11. th {
  12. padding: 5px;
  13. }
  14. .blau {
  15. background-color: #1b95e0;
  16. }
  17.  
  18. .gris {
  19. background-color: gray;
  20. }
  21.  
  22. .negreta {
  23. font-weight: bold;
  24. }
  25. </style>
  26.  
  27. <table>
  28. <tr>
  29. <th>Columna 1</th>
  30. <th>Columna 2</th>
  31. <th>Columna 3</th>
  32. <th>Columna 4</th>
  33. </tr>
  34. <tr>
  35. <td>Fila 1</td>
  36. <td>Fila 1</td>
  37. <td>Fila 1</td>
  38. <td>Fila 1</td>
  39. </tr>
  40. <tr>
  41. <td>Fila 2</td>
  42. <td>Fila 2</td>
  43. <td>Fila 2</td>
  44. <td>Fila 2</td>
  45. </tr>
  46. <tr>
  47. <td>Fila 3</td>
  48. <td>Fila 3</td>
  49. <td>Fila 3</td>
  50. <td>Fila 3</td>
  51. </tr>
  52. <tr>
  53. <td>Fila 4</td>
  54. <td>Fila 4</td>
  55. <td>Fila 4</td>
  56. <td>Fila 4</td>
  57. </tr>
  58. </table>
  59. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/wzrZXX.

Fixeu-vos que tot i que odd correspon als elements senars, el primer element marcat com a senar no és la primera fila ni la primera columna. Això es deu al fet que els elements comencen a comptar-se a partir de 0 i, per tant, la primera fila i columna corresponen a even (parell).

Selectors propis de jQuery: formularis

Un altre tipus de selectors que també ofereix la biblioteca faciliten treballar amb formularis:

  • :input: selecciona tots els elements de tipus input, textarea, select i button.
  • :text: selecciona tots els elements input de tipus text.
  • :checkbox: selecciona tots els elements amb tipus checkbox.
  • :radio: selecciona tots els elements de tipus radio.
  • :button: selecciona tots els botons i elements amb tipus button.
  1. <style>
  2. .tabular {
  3. margin-left: 25px;
  4. }
  5.  
  6. div {
  7. margin: 0 auto;
  8. width: 300px;
  9. border: 1px solid black;
  10. }
  11.  
  12. .caselles {
  13. float: right;
  14. }
  15. </style>
  16.  
  17. <div>
  18. <h1>Formulari</h1>
  19. <input type="text" name="nom" />Nom<br> <input type="text" name="cognom" />Cognom<br>
  20. <h2>Descripció</h2>
  21. <textarea name="descripcio"></textarea><br> <input type="checkbox" name="acceptar" />Acceptar condicions<br>
  22. <input type="radio" name="color" value="blau" checked>blau<br>
  23. <input type="radio" name="color" value="vermell">vermell
  24. </div>
  25.  
  26. <script>
  27. let inicialitzacio = function() {
  28. let $texts = $(':text');
  29. $texts.val(12345678);
  30.  
  31. let $input = $(':input');
  32. $input.addClass('tabular');
  33.  
  34. let $radio = $(':radio');
  35. $radio.addClass('caselles');
  36.  
  37. let $checkbox = $(':checkbox');
  38. $checkbox.addClass('caselles');
  39. }
  40.  
  41. $(document).ready(inicialitzacio);
  42. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/ZpQJZW.

El mètode val() dels objectes jQuery permet consultar o establir la propietat value d’un element.

En aquest exemple es modifica –via codi– el valor dels camps de text (invocant el mètode val()), s’afegeix un marge a l’esquerra de tots els elements englobats pel selector :input (input i textarea) i, finalment, es converteixen les caselles i els botons de selecció en flotants a la dreta afegint la classe caselles.

Fixeu-vos que per afegir la classe caselles tant a les caselles com als botons de ràdio s’ha hagut de fer individualment. Afortunadament jQuery ofereix el mètode add(), que permet concatenar seleccions. Canvieu el codi JavaScript de l’exemple anterior pel següent per comprovar-ho:

  1. let inicialitzacio = function() {
  2. let $texts = $(':text');
  3. $texts.val(12345678);
  4.  
  5. let $input = $(':input');
  6. $input.addClass('tabular');
  7.  
  8. let $caselles = $(':radio').add(':checkbox');
  9. $caselles.addClass('caselles');
  10. }
  11.  
  12. $(document).ready(inicialitzacio);

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/EZZqqp.

Com es pot apreciar, l’objecte jQuery referenciat per $caselles ara conté tant la selecció de :radio com la selecció de :checkbox i, consegüentment, quan s’invoca el mètode addClass(), aquest afecta tots dos tipus d’elements.

Però es pot simplificar encara més. Els objectes jQuery tenen una interfície fluida, és a dir, molts dels seus mètodes retornen sempre el mateix objecte jQuery, de manera que es poden invocar tots els mètodes un darrere de l’altre. A més a més, no cal fer cap operació addicional amb l’objecte, ni tan sols cal emmagatzemar la referència. Vegeu una implementació més concisa del mateix exemple; substituïu el codi JavaScript pel següent:

  1. let inicialitzacio = function() {
  2. $(':text').val(12345678);
  3. $(':input').addClass('tabular');
  4.  
  5. $(':radio')
  6. .add(':checkbox')
  7. .addClass('caselles');
  8. }
  9.  
  10. $(document).ready(inicialitzacio);

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/EgPwPE.

Fixeu-vos que no s’ha fet servir cap variable, s’han invocat els mètodes necessaris directament. En el cas del selector per a les caselles, s’ha posat la invocació a cada mètode en una línia diferent, en aquest cas només hi ha dues invocacions i es podria haver escrit tot en la mateixa línia, però en casos més complexos la visibilitat millora molt si es fa d’aquesta manera.

Cal destacar els dos elements següents –especialment interessants–, ja que cap dels dos es pot substituir per selectors CSS, ni requereixen que sigui descendent directe:

  • :has(element): selecciona els elements que continguin com a descendent element.
  • :contains(text): selecciona els elements que continguin el text.

Vegeu com funciona en l’exemple següent:

  1. <style>
  2. .vermell {
  3. color: red;
  4. }
  5.  
  6. .negreta {
  7. font-weight: bold;
  8. }
  9.  
  10. span {
  11. font-style: italic;
  12. }
  13. </style>
  14.  
  15. <div>Que deu ésser feta al cavaller. Volgué anar havent dolor e contricció de. La Comtessa e als servidors la. Ésser atesa sens mitjà de virtuts Los cavallers. Actes frescs de nostres dies.</div>
  16. <p> D'or ab les armes sues e de la Comtessa. <a href="#">Lo virtuós Comte</a> en edat avançada de cinquanta-cinc. Als servidors la sua partida En la fèrtil. Ho pres ab molta impaciència.</p>
  17. <p> Romans: d'Escipió d'Anibal de Pompeu d'Octovià. Longitud de molts dies E com entre los altres insignes.</p>
  18. <p> Comte en edat avançada de cinquanta-cinc anys. Fama d'aquell no deu preterir per longitud de. Havia rei o fill de rei.</p>
  19. <p> Experiència mostra la debilitat de la nostra memòria sotsmetent fàcilment. Cavaller pare de cavalleria lo comte Guillem. De <span><a href="#">Marc Antoni</a></span> e de molts altres.</p>
  20.  
  21. <script>
  22. let inicialitzacio = function() {
  23. $('p:has(a)').addClass('vermell');
  24. $('p:contains(Comte)').addClass('negreta');
  25. }
  26.  
  27. $(document).ready(inicialitzacio);
  28. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/zKAEwa.

Primerament, se seleccionen només els elements de tipus paràgraf (p) que continguin un element de tipus a i, seguidament, s’afegeix la classe vermell, de manera que el text d’aquests elements passa a ser vermell. Fixeu-vos que en l’últim paràgraf l’enllaç no és un descendent directe però, tal com s’espera, se selecciona correctament.

A continuació, se cerquen tots els paràgrafs que continguin la paraula Comte. Com que s’ha indicat expressament que només s’ha de cercar als paràgrafs, la primera línia no se’n veu afectada, ja que es tracta d’un element de tipus div. Com en el cas anterior, no és necessari que sigui descendent directe, i per això s’aplica correctament a la quarta línia.

En tots dos casos es pot passar el paràmetre amb cometes o sense; així doncs, es podrien substituir pel codi següent:

  1. $('p:has("a")').addClass('vermell');
  2. $('p:contains("Comte")').addClass('negreta');

S’ha de tenir en compte que aquests selectors no són tan eficients com els selectors CSS. Tot i així, en la majoria dels casos, aquesta pèrdua d’eficiència és inapreciable.

En els casos en què l’eficiència sigui crítica es recomana descompondre la selecció en dues parts. Primerament, s’invoca la funció jQuery() amb el selector CSS (que està optimitzat) i sobre l’objecte retornat s’invoca el mètode filter(), passant com a argument els selectors propis, que actuaran com a filtre. Podeu comprovar-ho canviant el codi JavaScript de l’exemple anterior pel següent:

El mètode 'filter'

Podeu trobar més informació sobre el mètode filter() en l’enllaç següent: www.api.jquery.com/filter/.

  1. let inicialitzacio = function() {
  2. $('p').filter(':has("a")').addClass('vermell');
  3. $('p').filter(':contains("Comte")').addClass('negreta');
  4. }
  5.  
  6. $(document).ready(inicialitzacio);

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/amdLRX.

Com es pot apreciar, el comportament és el mateix però internament el càlcul és més eficient perquè en lloc de fer la selecció a partir de tots els elements de la pàgina, es realitza sobre un petit subconjunt, el dels paràgrafs.

Relacions entre elements: 'parent()', 'first()', 'last()', 'prev()', 'next()', 'parents()' i 'siblings()'

El mètode parent() permet accedir al pare de l’element.

  1. <div id="contenidor">
  2. <p id="primer">Primer paràgraf</p>
  3. </div>
  4.  
  5. <script>
  6. let primer = $('#primer');
  7. console.log(primer.parent().attr("id"));
  8. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/eYdyeZL.

Com es pot apreciar, l’identificador retornat és contenidor, el corresponent a l’element pare. Aquest comportament permet recórrer l’arbre ascendentment; per exemple, es podria accedir al pare del pare: primer.parent().parent().

Els mètodes first() i last() retornen l’objecte jQuery del primer i últim element d’una llista d’objectes jQuery, com es pot comprovar en l’exemple següent:

  1. <ul id="llistat">
  2. <li id="primer">Primer item</li>
  3. <li id="segon">Segon item</li>
  4. <li id="tercer">Tercer item</li>
  5. <li id="quart">Quart item</li>
  6. </ul>
  7.  
  8. <script>
  9. console.log($('li').first().text());
  10. console.log($('li').last().text());
  11. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/XWjVzMO?editors=1011.

Per altra banda, prev() i next() permeten accedir als elements germans, anterior i posterior respectivament, d’un mateix element:

  1. <div id="contenidor">
  2. <p id="primer">Primer paràgraf</p>
  3. <p id="segon">Segon paràgraf</p>
  4. <p id="tercer">Tercer paràgraf</p>
  5. </div>
  6.  
  7. <script>
  8. let segon = $('#segon');
  9. console.log(segon.prev().text());
  10. console.log(segon.next().text());
  11. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/MWjrOEP?editors=1011.

En executar aquest exemple es mostrarà correctament el contingut textual dels elements anterior i posterior, és a dir, “Primer paràgraf” i “Tercer paràgraf”.

El mètode parents() ens retorna un llistat amb tots els ascendents (pare, pare del pare, pare del pare del pare,…) de l’element sobre el qual l’apliquem (no solament el pare directe com en el cas de parent()). Aquest mètode, i tots els que ens retornen un llistat d’elements, permet realitzar un filtratge posant un selector CSS com a paràmetre del mètode, de manera que solament ens retornarà, en aquest cas, aquells ascendents que coincideixen amb aquest selector.

En el següent exemple podeu comprovar el seu funcionament:

  1. <ul class="nivell-1">
  2. <li class="item-i">I</li>
  3. <li class="item-ii">II
  4. <ul class="nivell-2">
  5. <li class="item-a">A</li>
  6. <li class="item-b">B
  7. <ul class="nivell-3">
  8. <li class="item-1">1</li>
  9. <li class="item-2">2</li>
  10. <li class="item-3">3</li>
  11. </ul>
  12. </li>
  13. <li class="item-c">C</li>
  14. </ul>
  15. </li>
  16. <li class="item-iii">III</li>
  17. </ul>
  18.  
  19. <script>
  20. //Ascendents sense filtratge
  21. $ascendents = $('.item-2').parents();
  22. let long = $ascendents.length;
  23. for (let i=0; i<long; i++){
  24. console.log($ascendents.eq(i).attr('class'));
  25. }
  26. //Ascendents amb filtratge
  27. $ascendents = $('.item-2').parents('ul');
  28. long = $ascendents.length;
  29. for (let i=0; i<long; i++){
  30. console.log($ascendents.eq(i).attr('class'));
  31. }
  32. </script>

Podeu veure aquest exemple en l’enllaç següent:codepen.io/ioc-daw-m06/pen/JjRMOqG?editors=1011.

En la primera part de l’exemple podeu comprovar que ens retorna el valor de l’atribut class de tots els ascendents de l’element, no solament de les etiquetes ul, sinó també dels li. En la segona part, filtrem solament per aquells ascendents que són elements de tipus ul.

Finalment, el mètode siblings() ens retorna tots els elements germans de l’element al qual l’apliquem. I en aquest cas també ens deixarà filtrar aquests resultats posant un selector CSS com a paràmetre del mètode.

En el següent exemple podeu comprovar el seu funcionament:

  1. <ul>
  2. <li>item 1</li>
  3. <li>item 2</li>
  4. <li class="tercer-item">item 3</li>
  5. <li>item 4</li>
  6. <li>item 5</li>
  7. </ul>
  8. <script>
  9. $( "li.tercer-item" ).siblings().css( "background-color", "red" );
  10. </script>

Podeu veure aquest exemple en l’enllaç següent:codepen.io/ioc-daw-m06/pen/yLapqoq?editors=1010.

Obtenció de nodes descendents: 'children()' i 'eq()'

Per altra banda, el mètode children() ens permet accedir a la llista d’elements continguts (o fills) i conèixer la quantitat d’aquests a través de la propietat length.

Encara que la llista de nodes es pot tractar com un array, cal anar amb compte amb la manera com ho fem, ja que si ho fem de la manera que estem acostumats, és a dir, usant els claudàtors [], el que se’ns retornarà no serà un objecte jQuery, sinó un element del DOM i, per tant, no li podrem aplicar els mètodes de la biblioteca jQuery, sinó els bàsics de JavaScript. Per a que se’ns retorni un element de l’array com a objecte jQuery, haurem d’usar el mètode eq(index).

En el següent exemple es reflexa aquesta diferència:

  1. <ul id="llistat">
  2. <li id="primer">Primer item</li>
  3. <li id="segon">Segon item</li>
  4. <li id="tercer">Tercer item</li>
  5. <li id="quart">Quart item</li>
  6. </ul>
  7.  
  8. <script>
  9. let $items = $('#llistat').children();
  10. let long = $items.length;
  11. for(let i=0; i<long; i++){
  12. console.log($items.eq(i).text());
  13. console.log($items[i].text());
  14. }
  15. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/LYReOmV?editors=1011.

Fixeu-vos que en aquest exemple, quan usem el mètode text() sobre l’objecte jQuery obtingut amb el mètode eq(), no dona cap problema, però quan ho volem fer amb l’element del DOM retornat quan usem els claudàtors, no reconeix aquest mètode. Per solventar-ho, si realment volem un element del DOM, hauríem d’usar la propietat textContent de JavaScript.

Aquest mètode també ens permet posar un selector CSS entre els parèntesis, de manera que filtrarà i ens retornarà aquells descendents que coincideixen amb aquest selector. En l’exemple anterior $(‘llistat’).children('#segon’) solament ens retornarà l’element li amb l’identificador corresponent.

Cercar entre els elements seleccionats: 'filter' i 'find'

Quan s’utilitza la funció jQuery() la selecció es fa a partir de l’arrel del document; en moltes situacions el que interessa és obtenir una selecció a partir d’un objecte jQuery ja existent. Per portar a terme aquesta acció els objectes jQuery disposen de dos mètodes: filter() i find().

La diferència entre els mètodes filter() i find() és que el primer aplica el selector només entre els elements que formen part de la selecció actual; mentre que el segon aplica el selector als elements descendents.

Tots dos mètodes retornen un nou objecte jQuery amb el resultat de la selecció. Vegeu una demostració d’aquesta diferència en l’exemple següent:

  1. <style>
  2. .filtrat {
  3. color: red;
  4. }
  5.  
  6. .cercat {
  7. font-weight: bold;
  8. }
  9. </style>
  10.  
  11. <ul class="principal">
  12. <li>Element 1</li>
  13. <ul>Subllista
  14. <li>Element 1.1</li>
  15. <li>Element 1.2</li>
  16. </ul>
  17.  
  18. <li>Element 2</li>
  19. </ul>
  20. <ul class="principal">
  21. <li>Element 1</li>
  22. <ul>Subllista
  23. <li>Element 1.1</li>
  24. <li>Element 1.2</li>
  25. </ul>
  26.  
  27. <li>Element 3</li>
  28. </ul>
  29.  
  30. <script>
  31. let inicialitzacio = function() {
  32. let $llista = $('ul');
  33.  
  34. $filtrat = $llista.filter('li');
  35. $filtrat.addClass('filtrat');
  36.  
  37. $cercat = $llista.find('li');
  38. $cercat.addClass('cercat');
  39.  
  40. console.log('Elements a la llista:', $llista.length);
  41. console.log('Elements \'li\' filtrats:', $filtrat.length);
  42. console.log('Elements \'li\' cercats:', $cercat.length);
  43. }
  44.  
  45. $(document).ready(inicialitzacio);
  46. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/KgVRBd?editors=1111.

Fixeu-vos en els elements seleccionats per a cadascun dels objectes jQuery:

  • $llista: 4 elements seleccionats, corresponents als 4 elements ul del document.
  • $filtrat: 0 elements seleccionats. Encara que pot sorprendre, cal tenir en compte que a $llista no es troba cap element de tipus li; així doncs, el resultat és correcte.
  • $cercat: 8 elements seleccionats. En aquest cas, se cerca entre cadascun dels elements descendents d‘ul i selecciona els elements de tipus li que contenen.

Cal destacar que aquests mètodes són disponibles a tots els objectes jQuery i, per consegüent, no és necessari que els elements es trobin afegits al DOM. És a dir, es poden crear nous elements lligats a objectes jQuery i fer operacions de cerca sobre ells:

  1. let inicialitzacio = function() {
  2. $nousElements = $('<h1>Títol 1</h1><p>Paràgraf 1</p><p>Paràgraf 2</p><h2>Títol 2</h2><p>Paràgraf 3</p>');
  3.  
  4. $titols = $nousElements.filter(':header');
  5. $paragrafs = $nousElements.filter('p');
  6.  
  7. console.log('Nombre de títols:', $titols.length);
  8. console.log('Nombre de paràgrafs:', $paragrafs.length);
  9. }
  10.  
  11. $(document).ready(inicialitzacio);

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/qabYQx?editors=0011.

Com podeu veure en aquest exemple, tot i que els nodes generats per la funció jQuery() només es troben a la memòria, és possible invocar el mètode filter() per cercar les capçaleres i els paràgrafs.

Quan a la funció jQuery es passa una cadena de codi HTML com a argument, es crea un nou objecte que conté com a selecció els elements corresponents a aquest codi.

Crear i manipular elements

Un dels punts forts de jQuery és la facilitat amb la qual es poden crear nous elements i modificar-los. De la mateixa manera que les interfícies del DOM permeten crear branques i manipular-les abans d’afegir-les al document, jQuery permet treballar amb aquests objectes mentre es troben a la memòria.

D’aquesta manera, és possible generar un objecte jQuery a partir d’un fragment de codi HTML (per exemple, enviat des d’un servidor), fer modificacions als seus atributs o classes, clonar-lo, afegir-lo a un altre objecte jQuery i, finalment, afegir aquesta nova branca a l’arbre que forma el document.

Cal destacar que, al contrari de quan es treballa directament amb les interfícies del DOM, jQuery permet afegir nous estils i atributs d’una forma molt intuïtiva a través dels seus propis mètodes.

Crear nous elements

La creació de nous elements amb jQuery és molt simple, només cal passar com a paràmetre de la funció la cadena de codi HTML i la biblioteca retornarà un nou objecte jQuery que contindrà com a selecció aquests elements generats correctament.

Per exemple, per crear un nou paràgraf només cal passar el seu codi corresponent:

El mètode append() permet afegir els elements continguts en un objecte jQuery a un altre objecte jQuery.

  1. let inicialitzacio = function() {
  2. let $paragraf = $('<p>Paràgraf generat dinàmicament</p>');
  3. $(document.body).append($paragraf);
  4. }
  5.  
  6. $(document).ready(inicialitzacio);

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/JEWPow?editors=0010.

Com es pot apreciar, primer s’ha creat un nou objecte jQuery referenciat per $paragraf, i seguidament s’ha seleccionat l’element body (obtingut a través de la propietat del document) i s’hi ha afegit aquest nou element.

Vegeu-ne a continuació un exemple una mica més complex, però que està fet aplicant la mateixa mecànica:

  1. let inicialitzacio = function() {
  2. let $taula = $('<table><tr><th>nom</th><th>cognom</th></tr><tr><td>Josep</td><td>Campmany</td></tr><tr><td>Maria</td><td>Torres</td></tr></table>');
  3. $(document.body).append($taula);
  4. }
  5.  
  6. $(document).ready(inicialitzacio);

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/bwEKZE?editors=0010.

Tot i que s’ha fet servir el mateix sistema, aquest cop la codificació és molt menys entenedora. En cas d’haver d’afegir-hi més files, el codi s’aniria fent cada vegada més complicat i més difícil de depurar. Atès que aquest paràmetre no és més que una cadena de text, podem confeccionar-la pas a pas.

Una possible solució seria aplicar el disseny descendent, creant tot un seguit de funcions que permetin confeccionar el codi per generar la taula:

  1. let generarTaula = function(alumnes) {
  2. let html = '<table>' + generarCapcalera();
  3.  
  4. for (let i = 0; i < alumnes.length; i++) {
  5. html += generarFila(alumnes[i]);
  6. }
  7.  
  8. html += '</table>';
  9. return html;
  10. }
  11.  
  12. let generarCapcalera = function() {
  13. let capcalera = '<tr>';
  14. capcalera += '<th>nom</th>';
  15. capcalera += '<th>cognom</th>';
  16. capcalera += '</tr>';
  17. return capcalera;
  18. }
  19.  
  20. let generarFila = function(alumne) {
  21. let fila = '<tr>';
  22. fila += '<td>' + alumne.nom + '</td>';
  23. fila += '<td>' + alumne.cognom + '</td>';
  24. fila += '</tr>';
  25. return fila;
  26. }
  27.  
  28. let inicialitzacio = function() {
  29. let alumnes = [
  30. {nom: 'Josep', cognom: 'Campmany'},
  31. {nom: 'Maria', cognom: 'Torres'},
  32. {nom: 'Alex', cognom: 'Puig'},
  33. {nom: 'Ana', cognom: 'Perez'}
  34. ];
  35.  
  36. let html = generarTaula(alumnes);
  37. let $taula = $(html);
  38. $(document.body).append($taula);
  39. }
  40.  
  41. $(document).ready(inicialitzacio);

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/ALEdk?editors=0010J.

Com podeu veure, la llargària del codi ha augmentat considerablement, però la complexitat s’ha reduït i s’ha millorat l’escalabilitat. Per exemple, si en lloc de quatre alumnes en fossin quaranta, el codi seria el mateix modificant només el diccionari de dades. A més a més, el codi de cada funció és molt clar, de manera que és molt fàcil modificar-lo.

En primer lloc s’ha afegit un diccionari de dades que conté les dades dels alumnes, d’aquesta manera es podria canviar l’origen d’aquestes dades, per exemple, carregant-les d’un fitxer extern sense haver de modificar la resta del programa.

Dintre de la funció d’inicialització es genera el codi HTML cridant la funció generarTaula() i passant-hi com a argument el diccionari de dades. Dintre d’aquesta funció es genera una cadena de text amb el codi per generar una taula, al qual s’afegeix el codi de la capçalera cridant la funció generarCapcalera().

Seguidament es recorren tots els elements del diccionari de dades afegint el codi per cada fila cridant la funció generarFila() amb les dades de cada alumne. Finalment es tanca l’etiqueta de la taula i es retorna el codi complet, que és convertit en un objecte jQuery i afegit al cos del document.

Aquesta és només una de les tècniques que ofereix jQuery per compondre una branca d’elements; en cada cas s’ha de valorar quin és el sistema que millor s’adapta a la vostra aplicació.

Un altre sistema per generar nous objectes jQuery és passar com a argument elements del DOM:

  1. <style>
  2. .vermell {
  3. color: red;
  4. }
  5.  
  6. .negreta {
  7. font-weight: bold;
  8. }
  9. </style>
  10.  
  11. <p>Primer paràgraf</p>
  12. <p id="segon">Segon paràgraf</p>
  13. <p>Tercer paràgraf</p>
  14.  
  15. <script>
  16. let inicialitzacio = function() {
  17. let paragrafs = document.getElementsByTagName('p');
  18. let paragraf2 = document.getElementById('segon');
  19.  
  20. let $paragrafs = $(paragrafs);
  21. let $paragraf2 = $(paragraf2);
  22. $paragrafs.addClass('vermell');
  23. $paragraf2.addClass('negreta');
  24. }
  25.  
  26. $(document).ready(inicialitzacio);
  27. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/rrZaYO.

Com es pot apreciar, primer de tot s’han obtingut els nodes fent servir els mètodes de les interfícies del DOM (una col·lecció de nodes en el primer cas, i un únic node en el segon). A continuació, s’han generat els dos objectes jQuery passant els elements com a argument, i finalment, s’ha afegit la classe corresponent a cadascun: vermell a tots els paràgrafs i negreta al segon.

Manipulació de classes: 'addClass', 'removeClass' i 'toggleClass'

A banda de l’addició de classes als elements, jQuery ofereix mètodes per eliminar-les i per activar-les/desactivar-les. Tots tres casos són fàcils de fer servir: només cal invocar el mètode sobre l’objecte jQuery passant com a argument el nom de la classe:

  1. <style>
  2. .vermell {
  3. color: red;
  4. }
  5.  
  6. .verd {
  7. color: green;
  8. }
  9.  
  10. .negreta {
  11. font-weight: bold;
  12. }
  13. </style>
  14.  
  15. <p class="negreta">Paràgraf 1: originalment en negreta</p>
  16. <p class="vermell">Paràgraf 2: originalment vermell</p>
  17.  
  18. <script>
  19. let inicialitzacio = function() {
  20. let $paragrafs = $('p');
  21.  
  22. $paragrafs.removeClass('vermell');
  23. $paragrafs.addClass('verd');
  24. $paragrafs.toggleClass('negreta');
  25. }
  26.  
  27. $(document).ready(inicialitzacio);
  28. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/QKyBdA.

Com es pot apreciar no es produeix cap error en eliminar la classe vermell de tots els paràgrafs, tot i que el primer paràgraf no conté aquesta classe. Com és d’esperar, quan s’afegeix la classe verd, s’afegeix correctament a tots dos paràgrafs.

Cal parar especial atenció al comportament d’invocar toggleClass: es determina si s’ha d’activar o desactivar segons l’estat concret de cada element, és a dir, en el primer cas estava activat i s’elimina, mentre que en el segon cas s’afegeix.

Modificar continguts: 'val', 'html' i 'text'

A l’hora de modificar el contingut d’un element s’ha de distingir entre modificar el seu valor (aplicable a elements de formularis), el contingut textual o el codi HTML (quan conté altres elements).

Per consultar i modificar aquests continguts, jQuery ofereix els mètodes val() per als elements de formularis, html() com a equivalent a la propietat innerHTML i text() per manipular els continguts textuals.

El funcionament és molt simple: aquests mètodes s’invoquen a partir de l’objecte jQuery que contingui els elements que es volen modificar. Si es vol consultar el valor, s’han d’invocar sense passar cap argument; en canvi, si es volen modificar, s’ha de passar com a argument el valor, codi o text per substituir.

  1. <label>Nom: </label>
  2. <input type="text" value="Pere" />
  3. <p>Paràgraf 1</p>
  4. <p>Paràgraf 2</p>
  5.  
  6. <script>
  7. let inicialitzacio = function() {
  8. let $text = $('input');
  9. let $paragraf1 = $('p:first');
  10. let $paragraf2 = $('p:last');
  11.  
  12. console.log($text.val());
  13. $text.val('Maria');
  14.  
  15. console.log($paragraf1.html());
  16. $paragraf1.html("<ul><li>Element 1</li><li>Element 2</li></ul>");
  17.  
  18. console.log($paragraf2.text());
  19. $paragraf2.text("El contingut textual d'aquest paràgraf ha estat modificat");
  20. }
  21.  
  22. $(document).ready(inicialitzacio);
  23. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/qakBLk?editors=1011.

Fixeu-vos que el resultat mostrat per la consola es correspon amb el valor original, mentre que al document es mostren els continguts actualitzats: “Maria” com a valor del quadre de text, una llista de dos elements com a contingut del primer paràgraf, i una frase diferent com a contingut del segon paràgraf.

Modificar estils: 'css'

A banda de treballar amb classes, la biblioteca jQuery també permet manipular directament els estils CSS d’un element de forma molt més simple que si ho heu de fer directament amb les interfícies del DOM.

Per afegir, modificar o eliminar un estil només cal invocar el mètode css() de l’objecte jQuery que contingui els elements que cal modificar:

  • Afegir estils: es passa com a argument el nom de la propietat CSS i el valor que s’ha d’assignar.
  • Eliminar estils: es passa com a argument el nom de la propietat CSS i una cadena buida com a valor.
  • Consultar el valor d’una propietat CSS: es passa com a argument el nom de la propietat CSS.

A continuació podeu veure un exemple en què es consulten les propietats CSS d’un element, se n’afegeixen de noves i se n’eliminen:

Si volem modificar la visibilitat d’un element, jQuery disposa de tres mètodes: show(), hide() i toggle(), que modifiquen l’estil display a block o none de la següent manera: show() el modifica a display, hide() a none i toggle() el modifica de manera que es visibilitzi l’element si no estava visible i a l’inrevés. Opcionalment podem passar a tots tres com a paràmetre la velocitat en mil·lisegons a la qual volem que l’element aparegui o desaparegui, segons el mètode cridat.

  1. <p id="paragraf" style="color: red; font-weight: bold">Paràgraf</p>
  2.  
  3. <script>
  4. let mostrarEstils = function (element) {
  5. let estil = element.style;
  6. let $element = $(element)
  7. for (let i=0; i<estil.length; i++) {
  8. console.log (estil[i] + ':' + $element.css(estil[i]) + ";");
  9. }
  10. }
  11.  
  12. let inicialitzacio = function() {
  13. let paragraf = document.getElementById('paragraf');
  14. let $paragraf = $(paragraf);
  15.  
  16. console.log('-- Estil original --');
  17. mostrarEstils(paragraf);
  18.  
  19. $paragraf.css('font-size', '30px');
  20. $paragraf.css('color', '');
  21. $paragraf.css('font-weight', 'lighter');
  22.  
  23. console.log('-- Estil modificat --');
  24. mostrarEstils(paragraf);
  25. }
  26.  
  27. $(document).ready(inicialitzacio);
  28. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/VKaPNP?editors=1011.

Com es pot apreciar, jQuery no ofereix una solució simple per mostrar els estils. En lloc d’utilitzar el mètode getComputedStyle() s’ha cridat el mètode css() per a cadascuna de les propietats, que retorna el mateix resultat.

Fixeu-vos que tant per afegir mètodes com per actualitzar només cal passar el nom i el valor de la propietat, mentre que per eliminar-la s’ha de passar una cadena buida.

Manipular atributs: 'attr' i 'prop'

La biblioteca jQuery ofereix dos mètodes diferents per tractar amb atributs:

  • El mètode attr(), que serveix per consultar i manipular els atributs en general.
  • El mètode prop(), que serveix per consultar i modificar propietats del DOM com checked, selected o disabled.

Una diferència important entre attr() i prop() és que si es fa servir el primer per obtenir el valor d’una propietat com checked, el valor retornat és una cadena de text; en canvi, amb el segon mètode el valor de retorn és un booleà, que correspon a l’estat de l’element (marcat o no marcat).

S’ha de tenir en compte que en cas que l’objecte jQuery tingui seleccionats múltiples elements, aquests mètodes retornen el valor del primer element.

  1. <label id="1"><input type="checkbox" checked="checked"/>Casella A</label>
  2. <label id="2"><input type="checkbox" />Casella B</label>
  3.  
  4. <script>
  5. let $etiquetes = $('label');
  6. let $etiqueta1 = $('#1');
  7. let $etiqueta2 = $('#2');
  8. let $caselles = $(':checkbox');
  9.  
  10. console.log('Atribut id de les etiquetes:', $etiquetes.attr('id'));
  11.  
  12. console.log('Canviant id per "A"...');
  13. $etiquetes.attr('id', 'A');
  14.  
  15. console.log('Atribut id de la etiqueta1:', $etiqueta1.attr('id'));
  16. console.log('Atribut id de la etiqueta2:', $etiqueta2.attr('id'));
  17.  
  18. console.log('Es troben les caselles marcades?', $caselles.prop('checked'));
  19.  
  20. console.log('Es troben les caselles marcades?', $caselles.attr('checked'));
  21.  
  22. console.log('Desmarquem totes les caselles');
  23. $caselles.prop('checked', '');
  24. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/GjjmwZ?editors=1011.

Com es pot apreciar, tant el mètode attr() com el mètode prop() retornen només un valor, els corresponents al primer element seleccionat. D’altra banda, en canviar l‘id per A, aquest s’aplica a totes les etiquetes.

Fixeu-vos com el valor retornat per prop() és diferent d‘attr() quan es passa checked com a argument. En el primer cas retorna un booleà, mentre que en el segon retorna el valor. Com en el cas de les etiquetes, el valor retornat en tots dos casos és el corresponent al primer element de la selecció, i en canviar el valor de la propietat, aquest canvi s’aplica a totes les caselles.

Així doncs, si es vol obtenir el valor de cadascun o es volen manipular els elements individualment s’ha d’iterar sobre ells, per exemple fent servir el mètode each() dels objectes jQuery:

  1. <label id="1"><input type="checkbox" checked="checked"/>Casella A</label>
  2. <label id="2"><input type="checkbox" />Casella B</label>
  3.  
  4. <script>
  5. let $etiquetes = $('label');
  6. let $caselles = $(':checkbox');
  7.  
  8. let mostrarId = function ($items) {
  9. $items.each(function(index) {
  10. console.log('Atribut id de l\'etiqueta ' + index + ':', this.id);
  11. });
  12. }
  13.  
  14. let afegirTextAId = function ($items, text) {
  15. $items.each(function() {
  16. this.id += text;
  17. });
  18. }
  19.  
  20. let mostrarMarcada = function ($items) {
  21. $items.each(function(index) {
  22. console.log('Casella ' + index + ' marcada?', this.checked);
  23. });
  24. }
  25.  
  26. mostrarId($etiquetes);
  27. console.log('Afegint text als Ids...');
  28. afegirTextAId($etiquetes, 'casella');
  29. mostrarId($etiquetes);
  30.  
  31. console.log('Estat de les caselles');
  32. mostrarMarcada($caselles);
  33. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/Kggqam?editors=1011.

La biblioteca ofereix dos mètodes each() amb funcions diferents. Si es crida a partir de la funció jQuery (jQuery.each()), aquest itera sobre l’array o la col·lecció passada com a argument; en canvi, si es crida aquest mètode a un objecte jQuery, el mètode each() itera sobre cadascun dels elements seleccionats.

Gràcies al mètode each() dels objectes jQuery, és molt fàcil iterar sobre tots els elements. Es passa al mètode each() una funció com argument i aquesta serà invocada una vegada per cada element, fent servir com a context de la funció l’element corresponent. Opcionalment es pot afegir un paràmetre a la funció, que rebrà l’índex de l’element (0 pel primer, 1 pel segon, 2 pel tercer…).

Cal recalcar que el context d’execució de la funció és l’element seleccionat, per consegüent, this fa referència a aquest element, que és un node del DOM. És a dir, no és un objecte jQuery.

Recordeu que cada element és el context en el que es realitza la acció; ni executa la acció (això ho fa la funció) ni és el destinatari (encara que es pot modificar l’element).

A continuació podeu trobar un exemple en què es modifica l’identificador dels elements de tipus label i es mostra per la consola del navegador si les caselles es troben o no marcades. En aquest exemple s’ha accedit directament a les seves propietats per simplificar, però és possible convertir aquests nodes en objectes jQuery:

  1. <label id="1"><input type="checkbox" checked="checked"/>Casella A</label>
  2. <label id="2"><input type="checkbox" />Casella B</label>
  3.  
  4. <script>
  5. let $etiquetes = $('label');
  6. let $caselles = $(':checkbox');
  7.  
  8. let mostrarId = function($items) {
  9. $items.each(function(index) {
  10. let $element = $(this);
  11.  
  12. console.log('Atribut id de l\'etiqueta ' + index + ':', $element.attr('id'));
  13. });
  14. }
  15.  
  16. let afegirTextAId = function($items, text) {
  17. $items.each(function() {
  18. let $element = $(this);
  19. $element.attr('id', $element.attr('id') + text);
  20. });
  21. }
  22.  
  23. let mostrarMarcada = function($items) {
  24. $items.each(function(index) {
  25. let $element = $(this);
  26. console.log('Casella ' + index + ' marcada?', $element.prop('checked'));
  27. });
  28. }
  29.  
  30. mostrarId($etiquetes);
  31. console.log('Afegint text als Ids...');
  32. afegirTextAId($etiquetes, 'casella');
  33. mostrarId($etiquetes);
  34.  
  35. console.log('Estat de les caselles');
  36. mostrarMarcada($caselles);
  37. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/rrrwdJ?editors=1011.

Recordeu que una de les maneres de crear un objecte jQuery és passar un element directament a la funció; així doncs, es passa l’element que serveix de context a la funció (this) i es genera un nou objecte jQuery a partir del qual es poden cridar els mètodes pertinents.

Fixeu-vos que en aquest cas concret la implementació s’ha complicat més i l’eficiència és menor que a l’exemple anterior, però pot haver-hi situacions en les quals sigui més pràctic treballar dintre de la funció amb objectes jQuery. S’ha de valorar cada cas i fer servir una implementació o altra segons les vostres necessitats.

Manipular el DOM

La biblioteca jQuery destaca tant per la facilitat amb la qual es poden seleccionar elements com per la facilitat per crear-ne nous. Concretament, per generar nous elements, ofereix les següents opcions, segons el tipus de paràmetre passat:

  • cadena de text que sembli HTML: $('<p>') o $('<p id=“paragraf2”><em>Paràgraf amb èmfasi</em></p>').
  • Node HTML existent (clona un element ja existent node): $(element).
  1. <div id="contenidor"></div>
  2.  
  3. <script>
  4. let $contenidor = $('#contenidor');
  5.  
  6. let $nouParagraf = $('<p>');
  7. $nouParagraf.html('Paràgraf normal');
  8.  
  9. let $nouParagrafEmfasi = $('<p id="paragraf2"><em>Paràgraf amb èmfasi</em></p>');
  10.  
  11. $contenidor.append($nouParagraf);
  12. $contenidor.append($nouParagrafEmfasi);
  13.  
  14. let paragraf3= document.createElement('p');
  15. paragraf3.innerHTML = 'Paràgraf per clonar';
  16.  
  17. let $paragrafClonat = $(paragraf3);
  18. $contenidor.append($paragrafClonat);
  19. </script>

Podeu veure aquest exemple l’enllaç següent: codepen.io/ioc-daw-m06/pen/ALLaAm?editors=1010.

En tots dos casos el retorn de la funció és un objecte jQuery que embolcalla el nou element (o elements). S’ha de tenir en compte que aquests nous elements encara no formen part del document, sinó que només es troben a la memòria.

Per afegir-los al DOM, la biblioteca ofereix diversos mètodes. Aquests mètodes s’han de cridar a partir d’un objecte que serà el contenidor dels elements, passant com a paràmetre el nou element que s’ha d’afegir:

  • prepend: afegeix l’element al principi de la llista de descendents directes del contenidor.
  • append: afegeix l’element al final de la llista descendents directes del contenidor.
  • before: afegeix l’element al mateix nivell que el contenidor, però davant seu.
  • after: afegeix l’element al mateix nivell que el contenidor, però darrere seu.

És a dir, els mètodes prepend() i append() col·loquen el nou element dins del contenidor, mentre que els mètodes after() i before() el col·loquen fora d’aquest, davant i darrere respectivament.

  1. <ul>
  2. <li>Element 1</li>
  3. <li>Element 2</li>
  4. <li>Element 3</li>
  5. </ul>
  6.  
  7. <script>
  8. let $llista = $('ul');
  9.  
  10. let $nouItem1 = $('<li>Element append</li>');
  11. $llista.append($nouItem1);
  12.  
  13. let $nouItem2 = $('<li>Element prepend</li>');
  14. $llista.prepend($nouItem2);
  15.  
  16. let $nouItem3 = $('<ul><li>Element before</li></ul>');
  17. $llista.before($nouItem3);
  18.  
  19. let $nouItem4 = $('<ul><li>Element after</li></ul>');
  20. $llista.after($nouItem4);
  21. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/vXXjpx?editors=1010.

Fixeu-vos que una vegada s’ha generat l’element, encara que no s’hagi afegit al document ja es troba a la memòria. I com que es tracta d’un objecte jQuery, es poden invocar pràcticament tots els seus mètodes, per exemple per afegir classes:

  1. <style>
  2. .vermell {
  3. color: red;
  4. }
  5. </style>
  6.  
  7. <div id="contenidor"></div>
  8.  
  9. <script>
  10. let $contenidor = $('#contenidor');
  11. let $paragraf = $('<p>Paragraf <span>creat</span> i <span>manipulat</span> a la memòria</p>');
  12.  
  13. $paragraf.find('span').addClass('vermell');
  14.  
  15. $contenidor.append($paragraf);
  16. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/wzzjxj.

Com podeu veure, s’afegeix la classe vermell als elements de tipus span abans d’afegir-lo al document; d’aquesta manera, l’operació s’executa correctament.

Quant a l’eliminació d’elements, jQuery ofereix dos mètodes: remove() i empty(). El primer elimina tots els elements seleccionats per l’objecte jQuery a partir del qual s’invoca, mentre que el segon elimina els descendents d’aquests nodes (és a dir, els buida):

  1. <ul id="llista1">
  2. <li>Element A</li>
  3. <li>Element B</li>
  4. <li>Element C</li>
  5. </ul>
  6.  
  7. <ul id="llista2">
  8. <li>Element 1</li>
  9. <li>Element 2</li>
  10. <li>Element 3</li>
  11. </ul>
  12.  
  13. <script>
  14. let $llista1 = $('#llista1');
  15. let $llista2 = $('#llista2');
  16.  
  17. $llista1.remove();
  18. $llista2.empty();
  19.  
  20. console.log("Existeix l'element amb id llista1:", document.getElementById('llista1'));
  21.  
  22. console.log("Existeix l'element amb id llista2:", document.getElementById('llista2'));
  23. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/VKKxNW?editors=1011.

Fixeu-vos que mentre que la primera llista ha deixat d’existir, la segona continua a la pàgina (tot i que no es veu perquè és buida) però s’han eliminat tots els seus descendents.

Utilitzar Events amb jQuery

Entre els avantatges d’utilitzar jQuery a l’hora d’afegir la detecció d’esdeveniments cal destacar-ne dos:

  • Es pot assignar la detecció de múltiples esdeveniments amb una sola línia.
  • La detecció d’esdeveniments s’aplica a tots els elements seleccionats.

Per afegir un detector, s’ha de generar primer un objecte jQuery: se seleccionen els elements als quals voleu afegir-lo, i seguidament s’invoca el mètode on(), passant com a arguments una cadena amb el nom dels elements que cal detectar i la funció que es farà servir de callback (funció que serà cridada internament).

Cal que recordeu que tot i que als exemples s’acostuma a fer servir funcions anònimes, es pot passar qualsevol tipus de funció i, per tant, podria ser una funció amb nom definida en altre punt de l’aplicació o una variable que referenciés una funció o un mètode.

  1. <ul id="llista1">
  2. <li>Element A</li>
  3. <li>Element B</li>
  4. <li>Element C</li>
  5. </ul>
  6.  
  7. <ul id="llista2">
  8. <li>Element 1</li>
  9. <li>Element 2</li>
  10. <li>Element 3</li>
  11. </ul>
  12.  
  13. <script>
  14. let $llista1 = $('#llista1 li');
  15. let $llista2 = $('#llista2 li');
  16.  
  17. $llista1.on('click mouseenter mouseout', function () {
  18. $(this).toggleClass('vermell');
  19. });
  20.  
  21. $llista2.dblclick(function () {
  22. $(this).remove();
  23. });
  24. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/gwwKmB.

Com es pot apreciar, s’ha afegit la detecció per als events click, mouseenter i mouseout als elements de la primera llista, de manera que aquests es posen de color vermell quan hi entra el cursor o quan es fa clic sobre ells, mentre que en sortir es fa l’acció inversa.

D’altra banda, a la segona llista s’ha afegit la detecció de l’event dblclick, que es dispara en fer doble clic sobre algun dels elements, i elimina aquest element de la llista en disparar-se.

Cal destacar que el context de la funció (this) fa referència a l’element en el qual s’ha detectat l’esdeveniment; així doncs, a la primera llista es genera un objecte jQuery a partir d’aquest i s’activa o desactiva la classe vermell. De la mateixa manera, en el segon cas, per evitar incompatibilitats, es genera l’objecte i s’invoca el mètode remove() per eliminar-lo.

Mètode remove() al DOM

Existeix un mètode remove() a l’especificació del DOM que es pot cridar directament sobre l’element, però no és compatible amb navegadors antics, particularment amb versions d’Internet Explorer anteriors a Edge.

Fixeu-vos que en el segon cas, en lloc d’invocar el mètode on() i passar una cadena de text amb el nom de l’event, s’ha invocat dblclick(). Això és possible perquè jQuery ofereix una llarga llista de mètodes que poden invocar-se com dreceres (click, dblclick, load, etc.), és a dir, pot invocar-se aquest mètode passant com a únic argument la funció que serà invocada en detectar-se l’esdeveniment. Com és d’esperar, quan es fan servir dreceres hi ha una limitació important: només es pot detectar un únic event.

Dreceres d'events

Podeu trobar una llista completa de les dreceres d’events en l’enllaç següent: goo.gl/BJMUZh.

En cas que necessiteu eliminar la detecció d’esdeveniments només cal fer servir el mètode off():

  1. <style>
  2. div {
  3. width: 100px;
  4. height: 100px;
  5. float: left;
  6. background-color: grey;
  7. margin: 1px;
  8. }
  9. .vermell {
  10. background-color:red;
  11. }
  12. </style>
  13.  
  14. <h1>Selecciona un quadre</h1>
  15. <div></div>
  16. <div></div>
  17. <div></div>
  18. <div></div>
  19. <div></div>
  20.  
  21. <script>
  22. $quadres = $('div');
  23.  
  24. $quadres.click(function() {
  25. $(this).addClass('vermell');
  26. $quadres.off();
  27. $('h1').html('Quadre seleccionat');
  28. });
  29. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/amBKLm.

Com es pot apreciar, una vegada es clica qualsevol dels quadres, el quadre en qüestió es mostra de color vermell (s’aplica la classe vermell), i s’elimina la detecció d’esdeveniments de tots els quadres invocant el mètode off() sense passar-hi cap argument.

Una altra opció que ofereix el mètode off() és eliminar només alguns dels events de la detecció, com es pot comprovar en l’exemple següent:

  1. <style>
  2. .vermell {
  3. color:red;
  4. }
  5. </style>
  6.  
  7. <ul>
  8. <li>Element A</li>
  9. <li>Element B</li>
  10. <li>Element C</li>
  11. </ul>
  12.  
  13. <script>
  14. let $elements = $('li');
  15.  
  16. $elements.on('click mouseenter mouseout', function () {
  17. $(this).toggleClass('vermell');
  18. });
  19.  
  20. $elements.on('click', function(function() {
  21. $(this).off('mouseenter mouseout');
  22. })
  23. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/LRRBzq.

Fixeu-vos que s’han afegit dos detectors diferents per a l’event click i tots dos es criden en fer clic sobre qualsevol dels elements. El primer s’encarrega de canviar el color de la classe, mentre que el segon elimina la detecció d’events per a mouseenter i mouseout. És a dir, una vegada es fa clic sobre un element aquest només respondrà a l’event click.

Alternativament es podria haver fet servir la drecera click en lloc del mètode on() per al segon detector; el resultat hauria estat el mateix:

  1. $elements.click( function() {
  2. $(this).off('mouseenter mouseout');
  3. })

Crear connectors per jQuery

En el context de la biblioteca jQuery, un connector (plug-in, en anglès) és un component que afegeix noves funcionalitats a la biblioteca. És a dir, una vegada tenim el connector carregat, aquest s’afegeix automàticament a la biblioteca i és accessible per a tots els objectes jQuery.

Les galeries d’imatges o els carrusels d’anuncis o productes són exemples habituals de l’ús d’aquests connectors, però es poden trobar tot tipus de connectors en diferents repositoris, a més a més dels que podeu crear vosaltres mateixos.

Un dels espais web on es poden trobar aquests repositoris és al lloc web d’npm (www.npmjs.com). Aquí es pot localitzar fàcilment tot tipus de programari en JavaScript, tant per als navegadors com per al servidor (per utilitzar amb Node.js).

El gestor de paquets npm està basat en Node.js i per fer-lo servir s’ha de tenir istal·lat.

Node.js és una tecnologia que permet utilitzar JavaScript al servidor.

Fixeu-vos que només una part d’aquests paquets són connectors de jQuery; per localitzar-los es recomana fer servir aquest enllaç: www.goo.gl/Lc9c6Z, que filtra directament els components que inclouen la paraula jquery-plugin. Si proveu d’utilitzar el cercador del mateix lloc, veureu que el resultat és diferent.

Per descomptat, l’ús d’aquest gestor de paquets és opcional i podeu utilitzar els vostres propis connectors directament o descarregar-los de qualsevol altre repositori de tercers sense cap problema. Però en aquest últim cas és preferible fer servir una font fiable per evitar trobar-vos que s’està manipulant la vostra aplicació de forma malintencionada.

A continuació veureu com es pot crear el vostre propi connector per a jQuery, de manera que pugui ser utilitzat per tots els components de la vostra aplicació sense haver de fer-lo global.

En primer lloc, cal tenir clar quin serà el comportament que ha de tenir. En aquest cas s’ha decidit que les característiques del connector seran les següents:

  • L’objectiu del connector és facilitar l’addició (i eliminació) d’elements a una o més llistes.
  • S’afegiran dos botons (+ i -) i un títol que es mostrarà a la part superior de la llista.
  • En clicar el botó + s’afegirà un quadre de text en l’última posició de la llista que indicarà que s’ha d’escriure un text.
  • Quan es perdi el focus, si es troba algun valor, s’establirà aquest com a text de la llista.
  • El quadre de text s’eliminarà o es reemplaçarà en qualsevol cas.
  • Si s’ha entrat un valor, s’afegirà automàticament un nou quadre de text al final de la llista.
  • Sempre que s’afegeixi un nou quadre, aquest rebrà el focus.
  • Si es clica el botó -, s’eliminarà l’últim element de la llista.

Fent una primera implementació d’aquesta funcionalitat es pot obtenir un codi similar al següent:

  1. <style>
  2. ul {
  3. list-style-type: none;
  4. padding: 0;
  5. width: 200px;
  6. float: left;
  7. margin: 5px;
  8. }
  9.  
  10. ul div {
  11. border-bottom: 1px gray dotted;
  12. padding-bottom: 2px;
  13. }
  14. </style>
  15.  
  16. <ul>
  17. </ul>
  18. <ul>
  19. </ul>
  20. <ul>
  21. </ul>
  22.  
  23. <script>
  24. $llistes = $('ul');
  25.  
  26. $llistes.each(function() {
  27. let $barraEines = $('<div>Gestor Llistes </div>');
  28. let $botoMes = $('<button>+</button>');
  29. let $botoMenys = $('<button>-</button>');
  30.  
  31. $barraEines.append($botoMes);
  32. $barraEines.append($botoMenys);
  33.  
  34. let $contenidor = $(this);
  35. $contenidor.prepend($barraEines);
  36.  
  37. $botoMes.click(function() {
  38. let $quadre = $('<li><input type="text" placeholder="Escriu aquí..."/></li>');
  39.  
  40. $quadre.on('change focusout', function() {
  41. let valor = $quadre.find('input').val();
  42.  
  43. if (valor) {
  44. $quadre.html(valor);
  45. $botoMes.trigger('click');
  46. } else {
  47. // Si no hi ha cap contingut, s'elimina
  48. $quadre.remove();
  49. }
  50. });
  51. $contenidor.append($quadre);
  52. $quadre.find('input').focus();
  53. });
  54.  
  55. $botoMenys.click(function() {
  56. $contenidor.find('li:last-child').remove();
  57. });
  58. });
  59. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/qaazvw.

Primerament es fa una selecció de tots els elements de tipus llista, i a continuació s’itera sobre aquests elements per afegir la funcionalitat a totes les llistes trobades.

Es crea un contenidor per a la barra d’eines que inclou el títol Gestor Llistes seguit dels dos botons + i -, als quals se’ls afegeix la detecció de l’event click.

En clicar el primer botó es crea un nou element a la llista que inclou un quadre d’entrada de text i que detecta els events change i focusout, de manera que si aquest canvia o perd el focus és substituït per un text pla –si s’ha entrat algun valor– o l’elimina –en cas contrari–.

Addicionalment, en crear-se l’element de la llista, s’estableix el focus en el quadre d’entrada i això permet introduir dades de forma més fluida. D’altra banda, una vegada es detecta el canvi i s’estableix com a text, es genera automàticament un nou element a la llista per omplir (per simplificar s’ha fet disparant l’event click sobre el botó +).

El mètode trigger() permet disparar un esdeveniment manualment.

En clicar sobre el botó - s’elimina l’última fila de la llista fins que no en queda cap. No cal fer cap comprovació addicional.

Fixeu-vos que també s’ha afegit el codi CSS per formatar les llistes. Aquest codi s’hauria d’incloure amb el connector, de manera que es concretés més per no interferir en l’estructura de la pàgina on es faria servir el connector, per exemple, afegint-hi alguna classe pròpia. Substituïu les declaracions de $contenidor i de la $barraEines respectivament al codi JavaScript per les següents:

  1. let $contenidor = $(this);
  2. $contenidor.addClass('gestor-llistes');
  3.  
  4. let $barraEines = $('<div>Gestor Llistes </div>');
  5. $barraEines.addClass('barra-eines');

I substituïu el codi CSS pel següent:

ul.gestor-llistes {
  list-style-type: none;
  padding: 0;
  width: 200px;
  float: left;
  margin: 5px;
}

ul.gestor-llistes div.barra-eines {
  border-bottom: 1px gray dotted;
  padding-bottom: 2px;
}

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/QKKZRz.

Tot i que el comportament és el mateix, ja no hi haurà conflictes quan es faci servir el connector. El següent pas és convertir-lo en un connector de jQuery. Per fer-ho només s’ha d’afegir una funció anomenada arrodonir a $.fn, i a partir d’aquest moment aquesta funció passarà a estar disponible a tots els objectes jQuery. Reemplaceu el codi JavaScript pel següent:

  1. $(document).ready(function() {
  2. $('ul').gestionar();
  3. });
  4.  
  5. $.fn.gestionar = function() {
  6.  
  7. this.each(function() {
  8. let $contenidor = $(this);
  9. $contenidor.addClass('gestor-llistes');
  10.  
  11. let $barraEines = $('<div>Gestor Llistes </div>');
  12. $barraEines.addClass('barra-eines');
  13.  
  14. let $botoMes = $('<button>+</button>');
  15. let $botoMenys = $('<button>-</button>');
  16.  
  17. $barraEines.append($botoMes);
  18. $barraEines.append($botoMenys);
  19.  
  20. $contenidor.prepend($barraEines);
  21.  
  22. $botoMes.click(function() {
  23. let $quadre = $('<li><input type="text" placeholder="Escriu aquí..."/></li>');
  24.  
  25. $quadre.on('change focusout', function() {
  26. let valor = $quadre.find('input').val();
  27.  
  28. if (valor) {
  29. $quadre.html(valor);
  30. $botoMes.trigger('click');
  31. } else {
  32. // Si no hi ha cap contingut s'elimina
  33. $quadre.remove();
  34. }
  35. });
  36. $contenidor.append($quadre);
  37. $quadre.find('input').focus();
  38. });
  39.  
  40. $botoMenys.click(function() {
  41. $contenidor.find('li:last-child').remove();
  42. });
  43. });
  44. }

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/pENzom.

Com es pot apreciar, només hi ha tres canvis molt lleugers:

  • Es crida el mètode gestionar() directament sobre l’objecte jQuery que conté la selecció de les llistes.
  • S’ha posat tot el codi del connector dins de la funció $.fn.gestionar(): això és el que possibilita cridar la funció des de qualsevol objecte jQuery.
  • En lloc d’iterar sobre $llistes es fa sobre this, ja que en el context del connector this fa referència a l’objecte jQuery des del qual s’ha cridat, és a dir, el que selecciona totes les llistes en el nostre cas.

Per millorar el connector es pot implementar de forma que accepti arguments per flexibilitzar encara més la seva utilitat. Per exemple, per afegir un títol i missatge del quadre de text personalitzat:

  1. <style>
  2. ul.gestor-llistes {
  3. list-style-type: none;
  4. padding: 0;
  5. width: 200px;
  6. float: left;
  7. margin: 5px;
  8. }
  9.  
  10. ul.gestor-llistes div.barra-eines {
  11. border-bottom: 1px gray dotted;
  12. padding-bottom: 2px;
  13. }
  14. </style>
  15.  
  16. <ul id="Nom">
  17. </ul>
  18. <ul id="Generic">
  19. </ul>
  20. <ul id="Curs">
  21. </ul>
  22.  
  23. <script>
  24. $(document).ready(function() {
  25. $('ul#Nom').gestionar('Noms', 'Introdueix un nom');
  26. $('ul#Curs').gestionar('Curs', 'Introdueix un curs');
  27. $('ul#Generic').gestionar();
  28. });
  29.  
  30. $.fn.gestionar = function(titol, placeholder) {
  31. if (!titol) {
  32. titol = "Gestor de llistes";
  33. }
  34. if (!placeholder) {
  35. placeholder = "Escriu aquí..."
  36. }
  37.  
  38. this.each(function() {
  39. let $contenidor = $(this);
  40. $contenidor.addClass('gestor-llistes');
  41.  
  42. let $barraEines = $('<div>' + titol + ' </div>');
  43. $barraEines.addClass('barra-eines');
  44.  
  45. let $botoMes = $('<button>+</button>');
  46. let $botoMenys = $('<button>-</button>');
  47.  
  48. $barraEines.append($botoMes);
  49. $barraEines.append($botoMenys);
  50.  
  51. $contenidor.prepend($barraEines);
  52.  
  53. $botoMes.click(function() {
  54. let $quadre = $('<li><input type="text" placeholder="' + placeholder + '"/></li>');
  55.  
  56. $quadre.on('change focusout', function() {
  57. let valor = $quadre.find('input').val();
  58.  
  59. if (valor) {
  60. $quadre.html(valor);
  61. $botoMes.trigger('click');
  62. } else {
  63. // Si no hi ha cap contingut s'elimina
  64. $quadre.remove();
  65. }
  66. });
  67. $contenidor.append($quadre);
  68. $quadre.find('input').focus();
  69. });
  70.  
  71. $botoMenys.click(function() {
  72. $contenidor.find('li:last-child').remove();
  73. });
  74. });
  75.  
  76. }
  77. </script>

Podeu veure aquest exemple en l’enllaç següent: codepen.io/ioc-daw-m06/pen/PGbYpd.

Només ha calgut afegir els paràmetres a la funció gestionar() i utilitzar aquests valors internament: el paràmetre titol per substituir el títol que mostra el connector i el paràmetre placeholder com a atribut placeholder del quadre de text.

Un altre canvi que s’ha fet és afegir un identificador per a cada llista al codi HTML. Això no és cap requisit, s’ha fet així per simplificar la selecció individual i mostrar com cadascuna de les llistes aplica el mateix connector d’una manera personalitzada, gràcies a la parametrització.

Model-vista-controlador (MVC)

El model-vista-controlador és un patró de disseny complex utilitzat per implementar interfícies d’usuari, que separa l’aplicació en tres parts, com es pot apreciar a la figura:

  • El model: encarregat de gestionar les dades i la lògica de l’aplicació.
  • Les vistes: representacions de la informació que es mostra a l’usuari. Hi pot haver diferents vistes per a una mateixa informació.
  • Els controladors: encarregats de gestionar l’entrada de dades des de les vistes i fer les operacions necessàries sobre el model.
Figura Reprentació típica dels elements del patró MVC

Fixeu-vos que mentre que la vista generalment estarà programada amb HTML, CSS i JavaScript i s’executa en el client (el navegador), el model i el controlador poden trobar-se en un servidor remot i fer servir un llenguatge diferent de JavaScript, per exemple PHP o Java. Per tant, la implementació d’aquest patró pot requerir la utilització de diferents tecnologies.

Tot i que a cop d’ull sembla senzill, és molt fàcil confondre quin component pertany a cada part, ja que en programació es treballa amb conceptes força abstractes. Vegeu els següents dos exemples d’aplicació d’aquest patró:

Exemple de sistema d'autenticació

Un exemple molt simple seria aplicar aquest patró a un sistema d’autenticació; la divisió de les tres parts seria la següent:

  • Model: encarregat de comprovar si la informació d’un usuari i la seva contrasenya són correctes, si l’usuari és actiu, etc.
  • Controlador: rep l’entrada del client (nom d’usuari i contrasenya) i passa aquestes dades al model que retornarà si són correctes o no. En qualsevol cas es retornarà una resposta al model (o es generarà una nova vista) amb aquesta informació (accés concedit o denegat).
  • Vista: un formulari per autenticar l’usuari amb dos quadres de text (un per al nom d’usuari i un altre per a la contrasenya), i un botó per enviar la petició al controlador.

D’aquesta manera, es podrien afegir nous sistemes d’autenticació sense haver de tocar el model, només afegint nous mètodes al controlador (o nous controladors) i les vistes necessàries per interactuar amb el sistema. Per exemple, des de la vista es podria fer una petició asíncrona al controlador i que aquest, com a resposta, retornés un objecte JSON amb la informació de l’intent (èxit o error) i, en conseqüència, la vista mostraria el missatge corresponent.

Un exemple de com es podrien utilitzar diferents vistes a partir d’una mateixa informació seria el següent:

Exemple d'utilització de múltiples vistes

Suposeu que treballeu en una aplicació que gestiona les dades d’un institut i entre aquestes dades es troba la informació de tots els alumnes i els cursos.

Aquesta informació és proporcionada pel model, es realitzen les consultes a través del controlador i com a resposta es podrien generar diferents vistes (segons l’entrada enviada al controlador). Per exemple, es podrien generar les següents vistes:

  • Una vista que mostri el llistat de tots els alumnes de l’institut ordenats alfabèticament.
  • Una vista que mostri la mateixa informació però agrupada per cursos.
  • Una vista que mostri una gràfica amb la informació demogràfica dels alumnes.
  • Una vista que mostri un mapa amb xinxetes indicant la localització del domicili de cada alumne.

Cal destacar que aquest és un concepte avançat i no és recomanable que els programadors novells intentin implementar-lo a les seves aplicacions (excepte a les més senzilles). Afortunadament hi ha molts entorns de treball (frameworks) que implementen aquest patró i són molt utilitzats en el desenvolupament d’aplicacions complexes com Angular o Ember i biblioteques especialitzades com React.

L’avantatge d’utilitzar un entorn de treball és que permet començar a treballar més ràpidament, ja que inclouen tots els elements necessaris per crear una aplicació. En canvi, si es fa servir una biblioteca com React, serà necessari afegir altres biblioteques per afegir certes funcions (o desenvolupar-les vosaltres mateixos).

Angular

Angular (www.angular.io) és un dels entorns de treball més utilitzats i el matenen, principalment, enginyers de Google. Una vegada s’ha afegit ja es pot començar a treballar-hi directament, ja que inclou totes les funcionalitats per desenvolupar aplicacions complexes, sense requerir cap altra biblioteca.

Tot i que es pot programar amb ES5, els desenvolupadors d’Angular recomanen utilitzar TypeScript (una variant de ES6). Així doncs, cal fer servir alguna eina d’automatització com Gulp o Grunt per generar el codi final (que ha d’estar en ES5 per fer que sigui compatible amb els navegadors actuals).

Typescript

Podeu trobar més informació sobre TypeScript en l’enllaç següent: www.typescriptlang.org.

Gulp i Grunt

Gulp (www.gulpjs.com) i Grunt (www.gruntjs.com) són eines d’automatització que treballen sota Node.js.

Aquest entorn de treball forma part del que es coneix com a MEAN stack (un stack és un ‘conjunt de tecnologies’) que consisteix en l’ús de MongoDB (base de dades NoSQL), Express.js (servidor web per a Node.js), Angular a l’entorn client i Node.js com a entorn d’execució al servidor.

Entre les aplicacions webs desenvolupades amb Angular hi ha www.youtube.com, www.freelancer.com, www.telegram.org i www.udemy.com.

Popularitat de les diverses tecnologies

Podeu trobar més informació sobre quines són les tecnologies més populars i quins llocs web les utilitzen en l’enllaç següent: www.goo.gl/9nkkNN.

Ember

Ember (www.emberjs.com) és un altre entorn de treball molt utilitzat, tot i que molt lluny de la popularitat d’Angular. Aquest fa servir un sistema de convenció sobre configuració, és a dir, en lloc d’haver de configurar tota l’aplicació, s’han de respectar una sèrie de normes (noms de fitxers, rutes, controladors, etc.) i automàticament són reconeguts.

Un dels inconvenients és que la seva corba d’aprenentatge és més gran que en el cas d’Angular, i un altre és que el sistema de plantilles que utilitza incrusta etiquetes script per tot el document, ja que és així com fa la substitució de codi per continguts de text (tot i que això està previst que canviï en el futur).

Entre les aplicacions webs desenvolupades amb aquest entorn de treball es troben www.twitch.tv, www.digitalocean.com, www.heroku.com i www.sitepoint.com.

React

A diferència d’Angular i Ember, React (facebook.github.io/react) no és un entorn de treball, sinó una biblioteca desenvolupada per Facebook. El seu punt fort és el desenvolupament d’aplicacions d’una sola pàgina (precisament, Facebook el va desenvolupar amb aquest objectiu).

És a dir, en cas de requerir comportaments més complexos (com per exemple l’encaminament de pàgines), s’ha de recórrer a altres biblioteques complementàries. D’altra banda, com que es tracta d’una biblioteca, és molt més fàcil d’integrar en altres projectes o fins i tot combinar-la amb altres entorns de treball.

Entre les aplicacions webs desenvolupades amb aquesta biblioteca hi ha www.facebook.com, www.whatsapp.com, www.imdb.com, www.instagram.com i www.netflix.com.

Components Web

La idea al darrere dels components web és poder crear nous elements de la interfície que siguin reutilitzables i s’integrin perfectament al DOM. Aquests components inclouen tant el format com els detectors d’esdeveniments i qualsevol codi necessari per funcionar.

Especificació del W3C dels components web

Podeu trobar tota la informació referent a l’especificació del W3C dels components web al següent enllaç: www.goo.gl/OOsGNx.

Tot i que els components web actualment no són viables, és important conèixer-ne les característiques i saber com s’han implementat components similars.

Per exemple, seria possible crear un component “FitxaAlumne” que mostrés tota la informació d’un alumne correctament formatada a partir de les seves dades, i que aquest es mostrés al document HTML només afegint-hi l’etiqueta corresponent (similar a <fitxaalumne></fitxaalumne>).

Cal destacar que mentre molts entorns de treball permeten crear elements personalitzats, en inspeccionar el codi es pot comprovar que el que s’ha fet és reemplaçar o embolcallar l’etiqueta original amb tota una sèrie d’elements incrustats per l’entorn de treball per donar format i afegir els detectors d’esdeveniments. En canvi, utilitzant components web només es mostraria l’etiqueta corresponent.

Aquests components consisteixen en l’aplicació de quatre tecnologies diferents; malauradament, no estan disponibles en tots els navegadors d’escriptori i menys encara en els navegadors de dispositius mòbils. Les tecnologies són les següents (totes es consideren experimentals):

Estat de les tecnologies

Podeu trobar l’estat de les tecnologies utilitzades pels components web als navegadors més populars en l’enllaç següent: www.goo.gl/5aUF9E.

  • Custom elements: capacitat de crear nous elements i etiquetes HTML personalitzats.
  • HTML Templates: utilització de plantilles directament al codi HTML, que no són mostrades al navegador però es poden afegir via JavaScript.
  • Shadow DOM: permet encapsular el codi JavaScript i CSS d’un component web per evitar que aquest es barregi amb el de la resta de l’aplicació.
  • HTML Imports: capacitat d’importar altres fitxers HTML.

Així doncs, per poder utilitzar qualsevol d’aquestes tecnologies és molt probable que hàgiu d’activar el mode experimental del vostre navegador i, consegüentment, no es poden utilitzar en aplicacions webs que hagin d’emprar altres usuaris.

Cal destacar que encara que fa anys que s’hi treballa, encara no existeix una definició definitiva i per aquesta raó se n’han fet diferents interpretacions.

Per exemple, Mozilla Firefox no implementarà HTML Imports perquè els consideren insegurs. Google va presentar Google I/O 2013 Polymer, la primera versió del seu entorn de treball basat en les tecnologies dels components web. I d’altra banda, els entorns de treball Angular (directrius), Ember (components) i React (components) ofereixen una funcionalitat similar a la dels components web.

Google Polymer

Podeu trobar més informació sobre Google Polymer en l’enllaç següent: www.polymer-project.org/1.0.

Anar a la pàgina anterior:
Exercicis d'autoavaluació
Anar a la pàgina següent:
Activitats