Arxiu de la categoria ‘Desenvolupament’

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>');
});

Amagar sense ocultar amb CSS

dimarts 12 maig 2009

Sovint ens interessa amagar contingut de la nostra pàgina a l’ull humà com pot ser el cas dels menús per a saltar a continguts de la pàgina, en l’ús de tècniques de reemplaçament de contingut per imatges o per ocultar capçaleres o elements estructurals de la pàgina. És veritat que ens interessa amagar-ho de l’ull humà, però el que no volem de cap de les maneres, és amagar-ho dels lectors de pantalla.

El típic error

Si donem una volta per la xarxa, el que sovint trobem escrit i recomanat, és l’ús de la propietat display: none per a ocultar elements. Amb això satisfem la nostra voluntat de que l’ull humà no percebi el contingut, però el problema és que sovint, els lectors de pantalla tampoc n’interpreten res.

Podríem dir que display: none significa que l’element no existeix: ni es mostra, ni s’imprimeix, ni es llegeix el seu contingut.

La solució

Una tècnica alternativa que es pot considerar és la de posicionar l’element que volem amagar de forma absoluta fora del viewport. Així, l’element existeix però no es percep per l’ull humà. El CSS que podem usar és:

.access {
    left: -9999px;
    position: absolute;
}

Amb això aconseguim el mateix efecte visual però sense ignorar a una part dels usuaris.

Tot això no és pas invenció meva, sinó que és una adaptació d’un article que vaig llegir fa uns dies.

Arguments a favor i en contra de l’ús d’iframes

dimarts 31 març 2009

Per temes de feina avui he hagut de fer un llistat d’arguments a favor i en contra de l’ús d’iframe en un desenvolupament web i m’he decidit a fer-la pública perquè tothom que vulgui pugui opinar-ne.

A favor

  • Integració d’aplicacions. Permet integrar aplicacions externes a dins de la nostra pàgina.
  • Disminució de peticions a servidor. Si tenim una part de la nostra pàgina que és costosa de construir (dins d’un entorn de pagines dinàmiques), podem posar-la dins d’un /iframe fent que no s’hagi de refrescar cada cop que canviem de pàgina (seguint una mica el model del ja deprecat element /frame).

En contra

  • Forat negre. Al poder-hi posar tot el que vulguem, un /iframe pot arribar a ser un problema de seguretat si no som nosaltres els creadors del seu contingut.
  • Accessibilitat. Existeixen navegadors que no suporten l’ús de l’element iframe.
  • Lectors de pantalla. Hi ha navegadors que per defecte donen el focus al iframe en el moment de carregar la pàgina. Això és un problema per a la correcta lectura del document pels lectors de pantalla.
  • Cercadors. Les aranyes dels cercadors no saben navegar correctament per les pàgines que usen marcs. Per tant, pot ser que el nostre lloc no sigui correctament indexat.
  • Estructura del contingut. L’ús de l’element iframe trenca l’estructura lògica del contingut de la pàgina.
  • Gran confusió al interactuar amb el navegador. Diverses interaccions de l’usuari amb el navegador es veuen greument modificades per l’ús de l’element iframe en una pàgina:
    • URL. La URL que es veu al navegador no és sovint la que correspon a la d’una pàgina creada amb marcs: si anem navegant dins del marc, la URL del navegador no canvia, però en canvi, la de la pàgina que estem veient sí que “ha canviat”.
    • Adreces d’interès. Lligat amb el punt anterior, si volem desar l’adreça de la plana, la que desarem serà la que es veu al navegador. Al tornar-hi veurem  que no és pas la mateixa plana que havíem desat (haurem perdut la navegació dins del marc). Tot i que hi ha navegadors que ens donen la possibilitat d’esbrinar l’adreça d’un marc, pot ser que l’usuari sigui no especialitzat i per tant, no sàpiga de l’existència d’aquesta possibilitat. Igualment, un usuari de la plana no ha de saber com està feta la pàgina, i per tant, saber com ha de fer-s’ho per desar l’enllaç  “correcte” a la plana en qüestió.
    • Resultats d’una cerca. Si accedim a una pàgina feta amb marcs des d’un resultat d’un cercador extern, pot dur-nos a una adreça  “incorrecte”: podem anara a l’adreça d’un marc, on veurem el contingut d’aquest però sense el contingut del seu “pare”.
  • Alçada fixa. Un iframe té alçada fixa, i per tant, pot ser que ens aparegui una barra de desplaçament lateral en cas que el contingut ocupi més espai del reservat per l’atribut @height del marc.

Alternatives

En cas que usem marcs només per temes estètics no tenim excusa per abandonar-los, ja que el seu aspecte es pot replicar 100% amb CSS.

Si usem marcs per tal de poder deixar una part de la nostra pàgina fixa mentre fem scroll de la pàgina, podem usar la propietat position: fixed de CSS tal i com s’explica en aquest article.

En canvi, si el que volem és que cert contingut de la pàgina tingui una barra lateral per poder navegar dins seu, podem usar la propietat overflow tal i com s’explica en aquest altre article.

Conclusions

A data d’avui, i a no ser que sigui algun motiu de rendiment o d’integració, l’ús de marcs no està justificat en un inici, ja que provoca més desavantatges que no pas avantatges. Sovint la necessitat d’aquest element recau més per un tema gràfic que no pas per un tema funcional, cosa que com ja hem vist, es pot resoldre amb l’ús de fulls d’estils.

Ús de selectors CSS atípics: els selectors d’atribut

dijous 5 febrer 2009

Quan parlem de selectors CSS sovint ens venen al cap o als ulls expressions d’aquest tipus:

* {...}
e {...}
e f {...}
e > f {...}
e, f {...}

Però dins l’àmbit de selectors, hi ha tot un món de possibilitats que ens poden ajudar molt al nostre dia a dia (sinó, doneu-li un cop d’ull a l’apartat de selectors de l’especificació de CSS21 o de CSS3).

Del gran conjunt de selectors, en aquesta entrada vull parlar una mica i posar diversos exemples de com treure-li profit als selectors d’atribut. Abans de continuar, i per variar una mica, aquest tipus de selectors no funcionen en Internet Explorer 6 o anterior.

Els selectors d’atribut són els següents:

Patró Significat
E[foo] Un element E amb un atribut “foo”
E[foo="bar"] Un element E amb un atribut “foo” i el seu valor és exactament “bar”
E[foo~="bar"] Un element E amb un atribut “foo” que és una llista de valors separats per espais i un d’aquests és “bar”
E[foo^="bar"] Un element E amb un atribut “foo” i el seu valor comença exactament per “bar”
E[foo$="bar"] Un element E amb un atribut “foo” i el seu valor acaba exactament per “bar”
E[foo*="bar"] Un element E amb un atribut “foo” i el seu valor conté la cadena “bar”
E[foo|="bar"] un element E amb un atribut “foo” que és una llista de valors separats per guions i comença per “bar”

Per exemplificar l’ús d’aquest tipus de selectors, prendrem com a cas d’ús la personalització d’enllaços en diferents situacions.

Per exemple, i tal i com es fa en aquest bloc amb els enllaços, podríem voler adjuntar de forma visible pel lector quin és l’idioma de la pàgina enllaçada. Aquí ho fem amb la següent regla:

a[hreflang]:after {
  content: " ["attr(hreflang)"]";
  font-size: 0.8em;
}

Seguint l’exemple dels idiomes, podríem voler marcar l’enllaç d’un cert idioma amb una icona gràfica al seu voltant. Per exemple, en el cas del català, ho podríem fer de la següent manera:

a[hreflang="ca"] {
  background: url(icona-catala.png) no-repeat right center;
  padding-right: 1em;
}

Seguint amb aquests exemples, imaginem-nos que tenim un enllaç on el seu atribut rel té el conjunt de valors friend met co-worker muse i volem caracteritzar els amics amb una icona especial al costat de l’enllaç. Doncs bé, ho podríem fer amb el següent codi:

a[rel~="friend"] {
  background: url(icona-amic.png) no-repeat right center;
  padding-right: 1em;
}

Si ens interessés destacar amb alguna icona, tots els enllaços que tinguessin com a destí el W3C, podríem fer servir el següent codi:

a[href^="http://www.w3c.org"] {
  background: url(icona-w3c.png) no-repeat right center;
  padding-right: 1em;
}

O per exemple, si volguéssim posar-li una icona del format del fitxers enllaçat, podríem usar el següent codi pel cas del PDF:

a[href$=".pdf"] {
  background: url(icona-pdf.png) no-repeat right center;
  padding-right: 1em;
}

Seguint amb tot això, si volem tenir un enllaç caracteritzat si el seu atribut title o l’href conté la paraula “css”, podem usar el següent codi:

a[title*="css"][href*="css"]:after {
  color: #000;
  content: " (enllaç sobre CSS)";
  font-size: 0.8em;
}

I per acabar amb el darrer selector i tornant amb els exemples de la caracterització d’enllaços segons el seu idioma, podríem voler caracteritzar tots els enllaços en anglès amb una icona, independentment de si és anglès americà (en-US) o el general (en):

a[hreflang|="en"] {
  background: url(icona-angles.png) no-repeat right center;
  padding-right: 1em;
}

Amb tots aquests exemples, es pot veure que gràcies a aquest selector, se’ns obra tot un món de possibilitats a l’hora de caracteritzar diversos elements.

Com a curiositat, fa un temps, quan encara no havia sortit Internet Explorer 7 i degut a que les versions anteriors no reconeixien aquest selector, els maquetadors més radicals usaven aquest selector per a tenir dues versions diferents d’un lloc: una per a Internet Explorer i l’altre per als demés navegadors. Això ho podien aconseguir fent regles d’aquest estil:


div#contenidor {
  /* estils per a IE6 i anteriors */
}

div[id="contenidor"} {
  /* estils per als altres navegadors */
}

No digueu que no és curiós, oi?

À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