Entrades etiquetades ‘javascript’

Problemes amb l’append de jQuery a Internet Explorer

dilluns 25 maig 2009

De tots és ben conegut que ser el més popular de la classe no vol dir pas que siguis el més bo, ni el més educat, i encara menys, el més efectiu. Doncs això és principalment el que li passa a l’Internet Explorer.

Si treballem amb JavaScript d’una forma intensiva és normal usar alguna biblioteca que ens ajudi en algunes tasques del dia a dia d’interacció amb DOM, events i demés; i una de les biblioteques més populars actualment és jQuery (que és també la que uso jo normalment als meus projectes).

El cas que ens ocupa és una de les funcions de principals de manipulació del DOM: append. Imaginem-nos que tenim un codi semblant a aquest:

$('div.el-que-interessa').append('<span class="foo">');

Doncs bé, des de Firefox, per exemple, l’element /span sí que serà afegit, però en canvi a Internet Explorer no ho serà pas. Aquest darrer requereix que el fragment que es vulgui afegir mitjançant aquesta funció sigui XHTML. Així hauria de quedar una cosa com aquesta:

$('div.el-que-interessa').append('<span class="foo"><\/span>');

No sembla pas una cosa gaire complexa de detectar i solucionar, oi? Doncs donem-li una volta més de rosca…

Imaginem-nos que volem afegir opcions a una llista desplegable de forma dinàmica, i tenim un codi com el següent:

$.each(opcions, function(clau, valor) {
    $llista.append(new Option(valor, clau));
});

És correcte o no és correcte aquest codi? Per saber-ho, hauríem de modificar la pregunta i fer-la més o menys així: quin HTML ens retorna el constructor de la classe Option?

var opt = new Option('Catalunya', 'CT');
// opt: <option value="CT">Catalunya

Doncs bé, com podeu veure, el codi retornat per l’objecte no és pas en format XHTML, i per tant, no funcionarà a Internet Explorer. Per aconseguir el correcte funcionament, no tenim cap més opció que inserir l’opció en format text:

$.each(opcions, function(clau, valor) {
    $llista.append('<option value="' + clau + '">' + valor + '<\/option>');
});

Àmbit d’una variable a javascript

dimarts 13 gener 2009

L’àmbit d’una variable es pot definir com l’àrea del programa en que aquesta és visible i accessible.

Com a la majoria de llenguatges de programació, en javascript pots declarar una variable com a global, definida en tot l’àmbit del codi javascript, o com a local, definida només dins de l’àmbit de la funció que la defineix.

var a = 0;        // variable global
function foo() {
  var b = 1;      // variable local
}

Cal tenir sempre en compte que una variable local sempre té precedència davant d’una variable global, és a dir, si una paràmetre d’una funció o una variable declarada dins del seu àmbit té el mateix nom que una variable global, aquesta darrera queda oculta per la nova declaració.

var ambit = 'global';    // Declarem una variable global
function foo() {
  var ambit = 'local';   // Declarem una variable local
  document.write(ambit); // Usem la variable local
}
foo();                   // Imprimeix 'local'

Fins aquí tot força senzill, no? Però si us fixeu, encara no he dit res sobre la paraula reservada var.

Javascript ens deixa usar variables sense haver-les de definir amb la paraula var, però què passa si no ho fem? Doncs bé, si l’interpretador troba que s’està assignant un valor en una variable que no està declarada, la declara com a global i llavors li assigna el valor. Llavors cal tenir en compte que sempre que vulguem declarar una variable local, hem d’usar var.

ambit = 'global';              // Declarem una variable global (sense var)
function foo() {
  ambit = 'local';             // Ostres! Hem canviat el valor de la variable global!
  document.write(ambit);       // Usa la variable global
  nou_ambit = 'local';         // Declarem una nova variable global
  document.write(nou_ambit);   // Usa la nova variable global
}
foo();                         // Imprimeix 'locallocal'
document.write(ambit);         // Imprimeix 'local' (el valor s'ha canviat dins la funció)
document.write(nou_ambit);     // Imprimeix 'local' (la variable s'ha creat a la funció però amb àmbit global)

En general, una funció no sap quines variables estan definides en l’àmbit global i quines no. Així, si una funció usa una variable global en comptes d’una local sense saber-ho, pot estar modificant un valor que pot afectar a d’altres parts del programa. Per sort, la solució a tota aquesta problemàtica és ben senzilla: declareu sempre totes les variables amb var.

Però si seguim estirant del fil, podem trobar altres coses curioses relacionades amb l’àmbit de variables.

A diferència d’altres llenguatges de programació, javascript no defineix l’àmbit d’una variable a nivell de bloc (només existeixen àmbits a nivell global o local!). Així, totes les variables declarades dins d’una funció, sigui on sigui, estan definides a nivell de tota la funció. Si mirem el següent codi podrem entendre-ho:

function foo(a) {
  var i = 0;                       // i està definida a tota la funció
  if (a == 5) {
    var j = 23;                    // j està definida a tota la funció, no només en aquest bloc
    for(var k = 0; k < 20; k++) {  // k està definida a tota la funció, no només en aquest bucle
      document.write(k);
    }
    document.write(k);             // k està definida, i té valor 20
  }
  document.write(j);               // j està definida però pot ser que no estigui inicialitzada
}

Cal tenir en compte que el fet que una variable declarada dins d’una funció està definida per tota la funció pot donar-nos alguna que altra sorpresa. Per exemple:

var ambit = 'global';
function foo() {
  alert(ambit);          // Mostra 'undefined', no pas 'global'
  var ambit = 'local';   // Variable inicialitzada aquí, però definida en tot l'àmbit de la funció
  alert(ambit);          // Mostra 'local'
}
foo();

Pot semblar-nos que el primer cop que es crida a la funció alert() hauria de mostrar-nos “global” ja que la declaració de la variable local encara no s’ha executat. Però si tenim en compte les regles d’àmbit que he anat explicant, la variable local està definida en tot l’àmbit de la funció, ocultant la variable global que du el mateix nom. Però igualment, encara que la variable local està definida, aquesta no està inicialitzada fins que no s’executa la sentència var. Així, la funció foo() anterior és equivalent a la següent:

var ambit = 'global';
function foo() {
  var ambit;             // La variable local es defineix a l'inici de la funció
  alert(ambit);          // En aquest punt existeix, però té valor 'undefined'
  var ambit = 'local';   // En aquest punt la inicialitzem i li donem un valor
  alert(ambit);          // Aquí ja té valor!
}

Amb aquest exemple il·lustra perfectament el perquè és una bona pràctica el declarar totes les variables a l’inici de cada funció.

Tots els exemples que s’usen en aquesta entrada estan basats en els que es poden trobar al del llibre Javascript: The Definitive Guide

Organització i convenis en un projecte de maquetació

diumenge 28 desembre 2008

Des de fa un temps, que sempre que m’he d’enfrontar amb un nou projecte de maquetació segueixo un conjunt de normes que ens varem marcar entre els companys de feina. Ens trobàvem que sempre que havíem de fer un manteniment d’una maqueta d’algú altra, l’organització del projecte era diferent, i clar, això feia que davant d’un canvi mínim, perdéssim força temps interpretant on era cada cosa i com ho feia.

Doncs bé, aniré exposant l’organització i els convenis que jo segueixo, classificat en apartats per una fàcil comprensió.

Normes generals de tractament d’arxius

  • El nom dels arxius, tant de text com d’imatges, estarà sempre en minúscules i, en cas d’estar format per més d’una paraula, s’usarà el guió com a separador.
  • Tots els fitxers de text estaran codificats en UTF-8.
  • Els tabuladors tindran una mida de dos espais.

Estructura de directoris

Si partim de l’arrel del nostre projecte, es poden trobar els següents directoris:

  • assets: Dins d’aquest directori trobarem tots els elements que formen part de la maqueta i que complementen a l’HTML. Cal tenir en compte que en aquest nivell, no s’hi trobarà cap arxiu, sinó només directoris on s’organitzaran els tipus de recursos (fulls d’estils, imatges, etc.).
    • css: En aquest directori trobarem tots els full d’estils del projecte.
    • img: En aquest directori trobarem totes les imatges que sigui necessàries per a visualitzar correctament la maqueta. aquestes imatges poden ser, per exemple, icones, botons, imatges de fons, etc., però mai imatges d’exemple (imatges d’una notícia, d’un contingut, etc.). Les imatges estaran sempre en format PNG.
    • js: En aquest directori trobarem tots els arxius javascript que formen part de l’aplicació; tant el codi generat com qualsevol de les biblioteques que s’usi (jQuery, protoype, etc.).
  • images: Dins d’aquest directori trobarem les imatges de contingut que s’usen per a posar exemples a la maqueta però que no formen part d’aquesta.

No s’ha dit, però els fitxers HTML estaran sempre a l’arrel del projecte. Com es veu, l’estructura està pensada perquè sigui fàcil integrar, ja que només cal agafar la carpeta assets amb tot el que conté per tal de tenir tot el que es necessita per a una correcta visualització. Tant els arxius HTML com la carpeta images, es poden descartar una vegada la integració estigui feta.

Normes a l’HTML

  • El DOCTYPE serà XHTML1.0 Strict, sempre i quan la natura de l’aplicació no en demani un de diferent.
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  • El nom de les classes i dels identificadors dels elements seran sempre en català i s’escriuran en minúscules i, en cas d’estar format per més d’una paraula, s’usarà el guió com a separador.
    <div id="columna-metadades" class="bloc">
        ...
    </div>

Normes al CSS

  • Sempre i quan la mida del projecte ho permeti, tots els estils s’escriuran en un sol fitxer que anomenarem sempre styles.css. En cas que necessitem d’un fitxer d’estils especial per a Internet Explorer, aquest s’anomenarà, styles-ie6.css (per la versió 6 del navegador), styles-ie7.css (per la versió 7 del navegador) o styles-ie.css (en cas que no calgui diferenciar de versió de navegador); i es vincularà dins de l’HTML amb comentaris condicionals.
  • L’arxiu sempre començarà amb la directiva @charset.
  • Just després de la directiva de codificació, s’introdueix una taula de continguts per tal d’organitzar l’arxiu d’estils. Les capçaleres que facin referència a la taula dins del full d’estils, seguiran el següent patró:
    • Aniran entre comentaris de CSS (/**/).
    • A la primera línia, després d’iniciar el comentari i abans del títol, hi haurà tants guions com nivell dins de la taula de continguts es trobi; és a dir, si és un títol de primer nivell tindrà només un guió, si és de segon nivell, tindrà dos guions, i així anant fent.
    • Es fa un retorn de carro, i es posen 78 guions i es tanca el comentari (tot per fer una divisió de 80 caràcters).
  • L’estil d’obertura i tancament de claus serà la que varen proposar Kerninghan-Ritchie al seu tractat sobre el llenguatge de programació C; la primera clau va just després del predicat (separant amb un espai) i la clau de tancament va just després d’un retorn de carro.
  • Els estils s’escriuen en bloc i sempre precedits per un tabulador. Dintre de cada bloc, les directives s’ordenen en ordre alfabètic ascendent.
  • Un exemple de full d’estil podria ser:
    @charset "utf-8";
    /*
      TdC:
        - Elements genèrics
        - Capçalera
          - Regió capçalera
        - Estructura del contingut
          - Sense columnes
          - Una columna + contingut
          - Dues columnes + contingut
        - Peu
    */
    
    /*- Elements genèrics
    -----------------------------------------------------------------------------*/
    body {
      color: #333;
      font-size: 62.5%;
      margin: 0 auto;
    }
    
    /*- Capçalera
    -----------------------------------------------------------------------------*/
    ...
    
    /*-- Regió capçalera
    -----------------------------------------------------------------------------*/
    ...

Normes al javascript

  • Sempre i quan la mida del projecte ho permeti, el javascript anirà tot dins d’un sol fitxer amb nom script.js. En cas de necessitar-ne més, el criteri de nomenclatura haurà de ser racional.
  • El nom de les classes i dels identificadors dels elements que s’usin només per al javascript, seguiran les mateixes normes d’escriptura, però amb la diferència que seran en anglès per poder diferenciar-les de les que són pròpiament d’estructura.
  • Tant les variables com les funcions s’ecriuran en CamelCase. En el meu cas, tant el nom de les variables com els de les funcions els escriurem en angès.
  • No s’integraran biblioteques al projecte sempre i quan el seu ús no estigui justificat, és a dir, per a fer manipulacions mínimes del DOM no cal carregar jQuery. Passa el mateix amb l’ús d’extensions de les biblioteques: només s’usaran si es justifiquen molt els seus beneficis.
  • Tot el codi estarà documentat seguint JSDoc. Un bon programador sap que la documentació és útil, però l’abús d’aquesta ocasiona problemes.

Cal tenir present que aquest conjunt de normes són les que uso jo; no són ni un estàndard ni la millor manera de fer-ho, sinó una forma de fer-ho.

Si algú té cap conveni o norma que creieu que pot ser interessant, deixeu-ho en un comentari i així podem anar complementant-ho tot plegat.

Estils que depenen de javascript

dimarts 9 desembre 2008

Hi ha moments a la vida d’un programador d’interfícies web (mal anomenats sovint com a maquetadors) que vol crear un component que tingui una certa aparença segons si el navegador amb que es visualitza té el javascript habilitat o inhabilitat.

Posem un exemple. Imaginem-nos que estem creant un botó d’imprimir “especial”. El que volem aconseguir és que al fer clic a la icona d’exportació de la nostra pàgina, es desplegui un menú d’opcions on ens permeti obtenir la vista actual en PDF, en XML o en HTML sense estils (per posar un exemple). Com som bona gent i ens preocupa de debò l’accessibilitat, volem que, en cas de tenir el javascript inhabilitat o no disposar de suport per aquest, el que es visualitzi sigui el literal “Exportar a “, i la llista d’opcions que he explicat abans, una darrera l’altra. Total, que el nostre component ha de tenir dues representacions diferents, és a dir, dos conjunts d’estils ben diferents.

I com fer que un component tingui dues representacions? Doncs bé, jo explicaré aquí la tècnica que jo uso al meu dia a dia.

El que farem serà marcar el document amb una classe en cas que tinguem javascript habilitat, i així, des del nostre full d’estils, podrem diferenciar entre una representació i l’altra de la següent forma:

.element-que-interessa {
    /* Aquí van els estils per l'element en cas de no disposar de suport javascript */
    ...
}

.marca-document .element-que-interessa {
    /* Aquí van els estils per l'element en cas de disposar de javascript */
    ...
}

Una vegada explicada la idea, anem a l’acció. Personalment, el que jo faig és marcar l’etiqueta /html amb la classe js al principi del fitxer javascript:

document.documentElement.className = "js";

I llavors, al full d’estil, usar aquesta classe per a diferenciar-ho. Aplicat al nostre exemple:

.menu-exportacio {
    /* estils de la representació quan no hi ha javascript */
}

.js .menu-exportacio {
    /* estils per a la representació amb javascript */
}

Cal tenir present que aquesta tècnica pot ser útil quan, per exemple, volem amagar alguna porció del nostre document en cas de disposar de javascript (per exemple les àncores al contingut, barra lateral i demés) o fins i tot, per a diferenciar els estils que s’aplicaran a elements introduïts al document de forma dinàmica mitjançant javascript.