Contenido

Javascript no obstructivo, Manual de buenas maneras

15 oct

+ 58

Esto pretende ser una traducción (de las mias) de un fabuloso artículo de Christian Heilmann.

Javascript es una herramienta maravillosa para mejorar la usabilidad de las aplicaciones web. Es una capa extra entre la estructura (HTML) y el diseño(CSS). Javascript nos permite alterar el comportamiento de un elemento.

Uno de los problemas con los que nos encontramos a la hora de desarrollar aplicaciones web con javascript son problemas de accesibilidad derivados al no contemplar la posibilidad de que un usuario nos visite con un navegador que no interprete este lenguaje. Una técnica para corregir este problema sería el separar el javascript de las otras 2 capas del desarrollo web (estructura y diseño), esto recibe el nombre de Javascript no obstructivo o Javascript Accesible.

La frase, “¡¡Divide y vencerás!!” se adapta perféctamente a esta idea, en la cual separaremos cada capa en su respectivo fichero, de forma que en cuanto a mantenimiento (la etapa más costosa y larga del desarrollo de una aplicación) e incluso la comprensión de la aplicación se verán afectadas positivamente en cualquier aplicación.

Operación limpieza


El desarrollo web ha cambiado en los últimos años, estamos dejando de mezclar la presentación de la estructura, lo que nos está facilitando el trabajo de mantenimiento, modificación y mejora de nuestras aplicaciones. El código que hace años usabamos esta dando lugar a otro más complejo y fácil de mantener.

HTML:
<table border="0" cellpadding="0" width="100%" cellspacing="5">
<tr>
 <td><font face="Arial" size="-2">Lorem Ipsum</font></td>
</tr>
</table>

Este código con todos los atributos referentes a la presentación en el código nos obliga a recorrer toda la aplicación para modificar cualquier aspecto que deseemos modificar. Por este motivo, nuestro código empieza a coger un poco de sensatez y nos permite separar las 2 capas, diseño y estructura.

CSS:
td.content {font-family:Arial,sans-serif;font-size:12px;}
HTML:
<table cellpadding="0" 
style="width:100%;border:none" cellspacing="5">
<tr>
 <td class="content">Lorem Ipsum</td>
</tr>
</table>

Y aún podemos ir más allá…

CSS:
body {font-family:Arial,sans-serif;font-size:12px;}
p {padding:5px;}

HTML:
<p>Lorem Ipsum</p>

La misma evolución ha sufrido el desarrollo con Javascript.

Reglas del Javascript no obstructivo

1. Nunca, bajo ninguna circunstancia incluyas javascript directamente en el documento.

Una de las mayores ventajas del Javascript es que al igual que CSS permite la ejecución en ficheros externos. Esto nos permite incluir los ficheros que necesitemos para cada páginas, ajustando al máximo el peso de la aplicación. Además si has de cambiar alguna funcionalidad de la aplicación únicamente has de modificar el fichero JS en cuestión.

Pero para poder disfrutar de esta propiedad siempre tendremos que hacer referencia a nuestro/s javascript desde el fichero HTML, añadiendo entre los tags <head></head> la siguiente línea haciendo alusión  al fichero que deseamos adherir a nuestro proyecto.


<script type="text/javascript" src="scripts.js"></script>

2. Javascript es una mejora, no un sistema de seguridad.

Nosotros solo podemos usar Javascript como una mejora, debemos de pensar que no siempre podremos disponer de él y que agentes externos pueden deshabilitarlo sin darnos opción a activarlo, asi que hemos de programar pensando en que cualquier persona en este problema tambien debe poder usar nuestra aplicación.

Para ello podemos ver un ejemplo de javascript en el que vemos como valida un formulario.


HTML:
<form action="index.php" onsubmit="return checkform(this)">
 <p><label for="login">Login:</label>
 <input type="text" name="login" id="login" /></p>
 <p><label for="pw">Password:</label>
 <input type="password" name="pw" id="pw" /></p>
 <p><input type="submit" value="send" /></p>
</form>
Javascript:
function checkform(f)
{
 var error='';
 error+=f.login.value==''?'\nlogin':'';
 error+=f.pw.value==''?'\npassword':'';
 if (error!='')
 {
  alert('Please enter the following:'+error);
 } 
 return error=='';
}

Esto es perfectamente válido, nos permite controlar el formulario sin perjudicar a los usuarios que no tiene Javascript. De modo que en caso de no tenerlo activado únicamente tendremos que tener en cuenta que en la página de recepción de datos tendremos que validarlos directamente en el servidor, devolviendo al usuario a la página anterior en caso de que no sean válidos.

Otra forma de afrontar el problema sería la siguiente.


HTML:
<form action="index.php">
 <p><label for="login">Login:</label>
 <input type="text" name="login" id="login" /></p>
 <p><label for="pw">Password:</label>
 <input type="password" name="pw" id="pw" /></p>
 <p><input type="button" onclick="checkform()" value="send" /></p>
</form>
Javascript:
function checkform()
{
 var f=document.forms[0];
 var error='';
 error+=f.login.value==''?'\nlogin':'';
 error+=f.pw.value==''?'\npassword':'';
 if (error!='')
 {
  alert('Please enter the following:'+error);
 } else {
  f.submit();
 }
}

De esta forma estamos condenando al fracaso nuestra aplicación ya que si el navegador de nuestro usuario no soporta la llamada onclick, no podrá realizar un submit ya que nuestro boton es del tipo button.

3. Checkea la disponibilidad de un objeto antes de acceder a él.

Muchos de los errores en Javascript se deben a que se intenta acceder a elementos o métodos que no existen en un determinado momento del periodo de ejecución. Para solucionar esto basta con realizar un pequeña comprobación antes de utilizar dicho elemento o método.


Javascript:
function color(o,col)
{
 if(o)
 {
  o.style.background=col;
 }	
}

Antes de usar el elemento o, comprobamos que exista, así conseguimos una robustez en nuestra aplicación que el usuario agradecerá. Esta técnica es muy usada para el famoso cross-browsing.

4. Crea un Javascript no especifico para un navegador

Quizas la regla más complicada de todas, pero la más importante. Al igual que tenemos que tener en cuenta navegadores sin Javascript hemos de tener en cuenta todos los navegadores, para ello usaremos la regla anterior como la mejor baza a nuestro favor.


Javascript:
function doDOMstuff()
{
 if(document.getElementById && document.createTextNode)
 {
  [...]
 }
}

Comprobamos la existencia del método getElementById y createTextNode del objeto document antes de usarlo, así nos aseguramos una victoria al lanzar esta función.

5. No uses variables de otros scripts.

Cuando creemos una función o funcionalidad debemos estar seguros de usar variables locales para dicha función o funcionalidad. De esta forma nos prevenimos de que otras funciones o funcionalidades modifiquen nuestras variables.


Javascript:
var i=42; // global variable
function dothings()
{
 for (i=0;i<200;i++) // i gets changed
 {
  // some code 
 }
}
function dothingsright()
{
 var i; // define i locally
 for (i=0;i<200;i++)
 {
  // some code 
 }
}
alert(i); // = 42
dothingsright()
alert(i) // = 42 (due to local definition)
dothings()
alert(i) // = 200, oops!

6. Mantén los efectos de ratón de forma independiente

Tener en cuenta que el javascript esté activo o no no es suficiente. Hemos de tener en cuenta que hay usuarios que no pueden utilizar el ratón para navegar como normalmente se hace, esto obliga a usar el teclado para realizar todas las tareas que requiera nuestra aplicación. Un problema típico es el onchange o onblur en los elementos de un formulario ya que el teclado ejecuta estos eventos de forma distinta que el ratón.


HTML:
<form>
<p>
<label for="go2">Go to:</label>
<select name="go2" id="go2" onchange="send(this)">
 <option value="index.html">Home</option>
 <option value="chapter1.html">Operation Cleanout</option>
 <option value="chapter2.html">Reaching things</option>
</select>
</p>
</form>
Javascript:
function send(f)
{
 var chosen;
 chosen=f.options[f.selectedIndex].value;
 self.location=chosen;
}    

El problema que tenemos aqui es que cada vez que hacemos nos desplazamos hacia abajo desde nuestro teclado, estamos realizando la función send(), no siendo el resultado deseado (en FF parece no pasar). Los usuarios con experiencia sabrán que usan alt+abajo podrán omitir este evento.

La forma de solucionar esto es delegando la obtención del valor al evento onsubmit, de forma que podremos realizar cuantos cambios deseemos sobre nuestro formulario y únicamente lanzará la función al realizar el submit.


HTML:
<form action="send.php" method="post" onsubmit="return send(this)">
<p>
<label for="go2">Go to:</label>
<select name="go2" id="go2">
 <option value="index.html">Home</option>
 <option value="chapter1.html">Operation Cleanout</option>
 <option value="chapter2.html">Reaching things</option>
</select>
<input type="submit" value="go" />
</p>
</form>
Javascript:
function send(f)
{
 var chosen;
 chosen=f.go2.options[f.go2.selectedIndex].value;
 self.location=chosen;
 return false;
}    
PHP:
<?PHP if(isset($_POST['go2'])){
 header('Location:'.$_POST['go2']);
}?>

La opción de PHP sería para los casos en los que el navegador no permite la ejecución de código javascript.

¿Que pasa con onkeypress?

Según la W3C, en estos casos.

Otherwise, if you must use device-dependent attributes, provide redundant input mechanisms (i.e., specify two handlers for the same element):

  • Use “onmousedown” with “onkeydown”.
  • Use “onmouseup” with “onkeyup”
  • Use “onclick” with “onkeypress”

Esto suena perfecto en la teoría pero en la vida real el evento onkeypress está mal soportado por diferentes navegadores. Los usuarios que dependen del teclado para navegar normalmente tienen una configuración para simular los clicks, intro o espacio. Entonces esto nos lanza el evento onclick.

Cómo alcanzar lo que deseamos cambiar

Para un desarrollador javascript inexperto, el HTML es un patio de juegos.


HTML:
<a href="index.html" 
onmouseover="image1.src='1on.gif'" 
onmouseout="image1.src='1off.gif'">
<img src="1off.gif" name="image1" border="0" height="150" width="150" 
alt="home"></a> 

O incluso puede ser un poco más avanzado…


HTML:
<a href="index.html" 
onmouseover="roll('home',1)" 
onmouseout="roll('home',0)">
<img src="home.gif" name="home" border="0" 
height="150" width="150" 
alt="home"></a> 
Javascript:
// preloading image
homeoff = new Image(); 
homeoff.src = 'home.gif';
homeon = new Image(); 
homeon.src = 'homeoff.gif';
function roll(imgName,a) 
{
 imgState=a==0?eval(imgName + 'on.src'):eval(imgName + 'off.src');
 document.images[imgName].src = imgState;
}

En cualquier caso los eventos son llamados desde el HTML y si la función cambia de nombre, tendremos que cambiarlo en cada documento.Para ello debemos saber que usar y cuando usarlo, por ejemplo para este rollover, usaremos CSS que cumple nuestra misión sin alterar en nada la accesibilidad ni el resultado.


HTML:
<a href="index.html"><img src="home.gif" id="home" alt="home"></a>

Subir por los ramas del árbol del nodo

Cada fichero XML (en ellos se incluye el xHTML) es como un árbol. Un nodo una parte de este árbol, y al igual que en un arbol para subir a un rama en la copa del arbol tendremos que pasar por todas las ramas que nos encontremos por el camino, no podemos saltarnos ninguna. En javascript disponemos de una buena serie de herramientas que nos permiten recorrer este árbol poder manipular las propiedades de un elemento de dicho árbol.

Funciones para alcanzar un elemento en la página

getElementById(‘elementID’)
Devuelve el elemento con el ID asignado
getElementsByTagName(‘tag’)
Devuelve todos los elemento con la etiqueta (tag)

Funciones para recorrer las cercanias de un elemento

childNodes
Devuelve un array con todos los hijos que cuelguen del elemento. Tambien disponemos de firstChild y lastChild, que son versiones reducidas de childNodes[0] y childNodes[this.childNodes.length-1].
parentNode
Nos devuele el elemento padre
nextSibling
El siguiente elemento al mismo nivel dentro del árbol.
previousSibling
El elemento anterior al mismo nivel dentro del árbol.

Atributos y funciones para elementos

attributes
Devuelve un array con todos los atributos del elemento.¿Como no? No funciona con Internet Explorer 6 o inferior.
data
Devuelve o asigna los datos textuales del nodo
nodeName
Devuelve el nombre del nodo (El nombre de elemento HTML)
nodeType
Devuelve el tipo de nodo
  1. Es un elemento nodo
  2. Un atriburo
  3. Un texto 
nodeValue
Devuelve o asigna el value de un nodo.
getAttribute(attribute)
Devuelve el valor del atributo demandado.

Conociendo estas funciones y atributos, podremos mejorar nuestro código y hacer algo parecido a esto.


HTML:
<a href="index.html"><img src="home.gif" class="roll" alt="home"></a>
Javascript:
function findimg()
{
 var imgs,i;
// loop through all images of the document
 imgs=document.getElementsByTagName('img');
 for(i=0;i<imgs.length;i++)
 {
// test if the class 'roll' exists
  if(/roll/.test(imgs[i].className))
  {
// add the function roll to the image onmouseover and onmouseout and send
// the image itself as an object
   imgs[i].onmouseover=function(){roll(this);};
   imgs[i].onmouseout=function(){roll(this);};
  }
 }
}
function roll(o)
{
 var src,ftype,newsrc;
// get the src of the image, and find out the file extension
 src = o.src;
 ftype = src.substring(src.lastIndexOf('.'), src.length);
// check if the src already has an _on and delete it, if that is the case 
 if(/_on/.test(src))
 {
  newsrc = src.replace('_on','');
 }else{
// else, add the _on to the src 
  newsrc = src.replace(ftype, '_on'+ftype);
 }
 o.src=newsrc;
}
window.onload=function(){
 findimg();
}

Bien por el momento, aunque no hemos olvidado de una cosa, ahora esta imagen realizará un roll-over al pasar el ratón por encima, pero tenemos que pensar en los usuarios sin ratón. Para conseguir esto debemos checkear si el enlace de alrededor tiene el focus o no. Para ello usaremos el comando parentNode de nuestro objeto de la siguiente forma.

Javascript:
function findimg()
{
 var imgs,i;
// Loop through all images, check if they contain the class roll
 imgs=document.getElementsByTagName('img');
 for(i=0;i<imgs.length;i++)
 {
  if(/roll/.test(imgs[i].className))
  {
// add the function roll to the parent Element of the image
  imgs[i].parentNode.onmouseover=function(){roll(this);};
  imgs[i].parentNode.onmouseout=function(){roll(this);};
  imgs[i].parentNode.onfocus=function(){roll(this);};
  imgs[i].parentNode.onblur=function(){roll(this);};
  }
 }
}
function roll(o)
{
 var i,isnode,src,ftype,newsrc,nownode;
// loop through all childNodes
 for (i=0;i<o.childNodes.length;i++)
 {
  nownode=o.childNodes[i];
// if the node is an element and an IMG set the variable and exit the loop
  if(nownode.nodeType==1 && /img/i.test(nownode.nodeName))
  {
   isnode=i;
   break;
  }
 }
// check src and do the rollover
 src = o.childNodes[isnode].src;
 ftype = src.substring(src.lastIndexOf('.'), src.length);
 if(/_on/.test(src))
 {
  newsrc = src.replace('_on','');
 }else{
  newsrc = src.replace(ftype, '_on'+ftype);
 }
 o.childNodes[isnode].src=newsrc;
}

window.onload=function(){
 findimg();
}

Creando y destruyendo contenido

Una de las fortalezas de DOM es que no es únicamente de lectura, y esto nos permite modificar más aún nuestra aplicación. Para ello disponemos de una serie de funciones y métodos que nos facilitarán el trabajo.

Crear un nodo 

createElement(element)
Crea un nuevo elemento
createTextNode(string)
Crea un elemento de texto con el valor string

Alterando un contenido existente

setAttribute(attribute,value)
Añade un nuevo valor al atributo del objeto
appendChild(child)
Añade un nodo hijo a la lista de childNode del objeto, el hijo debe de ser un objeto, no un texto.
cloneNode()
Copia el nodo completo con todos sus hijos.
hasChildNodes()
Comprueba que el nodo no se un nodo hoja, osea que tenga hijos
insertBefore(newchild,oldchild)
Crea un nuevo hijo (newchild) antes del hijo antiguo (oldchild)
removeChild(oldchild)
Borra un hijo.
replaceChild(newchild,oldchild)
Reemplaza el viejo hijo (oldchild) por el nuevo (newchild).
removeAttribute(attribute)
Elimina un atributo del objeto.

Vamos a ver un ejemplo práctico y accesible. Imaginar que queremos hacer una lista en la cual tendremos enlaces a imagenes, si no disponemos de Javascript abriremos una ventana nueva para ver la imagen seleccionada.


HTML:
<ul id="imglist">
 <li><a href="home.gif" target="_blank">Home 
 (new window)</a></li>
 <li><a href="home_on.gif" target="_blank">Home active 
 (new window)</a></li>
 <li><a href="jscsshtml.gif" target="_blank">HTML-CSS-Javascript 
 (new window)</a></li>
</ul>

De esta forma tenemos una válida de mostrar una lista de imagenes, al hacer click sobre el enlace el target actuará y mostrará la imagen en una ventana nueva. Pero, ¿si disponemos de javascript? ¿por que no podemos enriquecer el aspecto?

Si disponemos de Javascript y DOM vamos a hacer lo siguiente:

  • Eliminar el texto (new window) de los links
  • Añadir al evento onclick la llamada a la función popw()

Javascript:
function imgpop()
{
 var il,imga,imgatxt;

// get all LIs in imagelist, loop over them 
 il=document.getElementById('imglist').getElementsByTagName('li');
 for(i=0;i<il.length;i++)
 {

// grab first link in the LI
 imga=il[i].getElementsByTagName('a')[0];

// delete the wording (new window) in the link text 
// (which is the nodeValue of the first node)  
 imgatxt=imga.firstChild;
 imgatxt.nodeValue=imgatxt.nodeValue.replace(/ \(new window\)/,'');

// add the event handlers to call popw();
 imga.onclick=function(){return popw(this);}
 //imga.onkeypress=function(){return popw(this);}
 }
}

La función debe:

  • Mostrar la imagen debajo del link.
  • Ocultar la imagen en caso de ya estar visible.
  • Hacer que la imagen desaparezca al hacer click sobre ella.


Javascript:
function popw(o)
{
 var newimg;
// if there is already an image in the parentNode (li) 
 if(o.parentNode.getElementsByTagName('img').length>0)
 {
// delete it
  o.parentNode.removeChild(o.parentNode.getElementsByTagName('img')[0]);
 } else {
// else, create a new image and add a handler that removes it when you 
// click it
  newimg=document.createElement('img');
  newimg.style.display='block';
  newimg.onclick=function(){this.parentNode.removeChild(this);};
  newimg.src=o.href;
  o.parentNode.appendChild(newimg)
 }
 return false;
}

Podeis ver el ejemplo de mano del autor.Activar y desactivar el javascript para ver las diferencias.

¿Como llamar a las funciones 

 La parte más importante de la separación del Javascript de nuestro HTML es la forma en la que llamamos a las funciones, para ello hasta ahora usabamos los métodos que incorporan los tags HTML.


HTML:
<body onload="foo();">

Esto debe cambiar, y aprovechandonos de que sabemos como obtener un objeto concreto a partir del DOM de nuestro HTML, podemos asignar estos eventos desde javascript sin tener que influir en nuestro HTML.


Javascript:
window.onload=foo;

or

window.onload=function(){
 foo();
 bar();
 baz();
}

Pero aún podemos mejorarlo más aún, y para ello usaremos los gestores de eventos.


Javascript:
function addEvent(obj, evType, fn){ 
 if (obj.addEventListener){ 
   obj.addEventListener(evType, fn, false); 
   return true; 
 } else if (obj.attachEvent){ 
   var r = obj.attachEvent("on"+evType, fn); 
   return r; 
 } else { 
   return false; 
 } 
}
addEvent(window, 'load', foo);
addEvent(window, 'load', bar);

Esto nos permite desligar por completo nuestra capa de estrucutura de la funcional de forma eficiente y estandarizada. Obviamente, con IE en Mac tendremos problemas con esta función.

Separacion de CSS y Javascript 

Quizas en los ejemplos anteriores no se está usando la forma más apropiada, pero es bastante clara para demostrar lo que queremos expresar. La idea desde un principio es separar de forma bien definida las 3 capas que influjen en una aplicación web. Por eso debemos evitar código parecidos a estos.


Javascript:
if(!document.getElementById('errormsg')){
  var em=document.createElement('p');
  em.id='errormsg';
  em.style.border='2px solid #c00';
  em.style.padding='5px';
  em.style.width='20em';
  em.appendChild(document.createTextNode('Please enter or change the fields marked with a '))
  i=document.createElement('img');
  i.src='img/alert.gif';
  i.alt='Error';
  i.title='This field has an error!';
  em.appendChild(i);
}

Esto además de no ser nada claro, es un coñazo a la hora de ponerte a modificar cualquier opción o añadir cualquier mejora. Por eso hemos de delegar es tema del aspecto a los CSS. Para ello usaremos el nexo entre la capa del diseño y la capa funcional, los tags.

Todos los tags tienen 2 atributos como mínimo, ID y class. ID se refiere al nombre único que le damos a un elemento concreto de la imagen, los ID’s nunca deben repetirse en la página. Despues tenemos class, que asigna un aspecto a nuestro elemento, en cierta manera el class engloba a todos los elementos designados dentro de dicha clase.

Sintaxis de multiples clases 

Los elementos pueden tener más de una clase, simplemente hay que separarlas por espacios. Esto esta soportado por los navegadores más modernos. IE en Mac no le gusta mucho cuando una clase conteniene el nombre de otra.

Aplicando clases via Javascript

Para cambiar la clase de un elemento debemos atacar al atributo className del objeto.


HTML:
<ul id="nav">
  [...]
</ul>
Javascript:
if(document.getElementById && document.createTextNode)
{
  if(document.getElementById('nav'))
  {
    document.getElementById('nav').className='isDOM';
  }
}

Esto no origina que nuestro CSS pueda tener dos estados, uno el que ataque al ID directamente #nav y otro que ataque al ID más la clase #nav.isDOM


ul#nav{
  [...]
}
ul#nav.isDOM{
  [...]
}

Usando el operador += podremos añadir una o más clases a nuestro elemento.


if(document.getElementById && document.createTextNode)
{
  var n=document.getElementById('nav');
  if(n)
  {
    n.className+=n.className?' isDOM':'isDOM';
  }
}

28 comentarios, 30 referencias

+

#

  1. meneame.net 16/10/2006 01:10
  2. Javascript no obstrusivo at Asesorías Web 16/10/2006 03:10
  3. Fernando Arconada » Reflexión Digital » Archivo del Blog » Javascript accesible o no obstructivo 16/10/2006 04:10
  4. Gestión de eventos en Javascript - aNieto2K 16/10/2006 04:10
  5. Detectar los navegadores sin Javascript - aNieto2K 16/10/2006 11:10
  6. Gabriel Lanzani » Javascript No Obstructivo 18/10/2006 06:10
  7. Javascript no obstructivo | DESARROLLO DE SOFTWARE 19/10/2006 05:10
  8. Javascript no obstructivo | DISEÑO WEB 19/10/2006 05:10
  9. Un pequeño javascript, ¿para salvarnos del SPAM? - aNieto2K 20/10/2006 12:10
  10. Nueve reglas para mejorar tu Javascript - aNieto2K 21/10/2006 02:10
  11. Fade a imagenes en el evento onLoad - aNieto2K 05/11/2006 04:11
  12. Las 10 mejores funciones de Javascript - aNieto2K 25/11/2006 01:11
  13. Aprendiendo a usar MooTools (II) - aNieto2K 09/12/2006 01:12
  14. Cancelar eventos mediante Javascript - aNieto2K 18/12/2006 12:12
  15. 5 reglas del desarrollo web - aNieto2K 29/12/2006 02:12
  16. MengTracker, más accesible y en Castellano - aNieto2K 04/01/2007 06:01
  17. Javascript llevado al máximo extremo - aNieto2K 12/01/2007 12:01
  18. aNieto2K | Hagamos un acordeón con MooTools 29/03/2007 06:03
  19. Y erasé una vez Javascript | aNieto2K 06/06/2007 12:06
  20. backdraft » Blog Archive » Cristalab Perú 2007 : notas y recursos web 10/06/2007 03:06
  21. Las principales diferencias entre HTML5 y HTML4 | aNieto2K 16/06/2007 11:06
  22. 10 reglas para los desarrolladores de librerías Javascript | aNieto2K 16/08/2007 11:08
  23. Protejete del SPAM con un Drag&Drop | aNieto2K 24/10/2007 04:10
  24. 7 reglas de oro para hacer un javascript no obstructivo | aNieto2K 14/11/2007 10:11
  25. 7 reglas de oro para hacer un javascript no obstructivo « antonio. 17/11/2007 09:11
  26. jQuery: Evitar la modificación del contenido de un inputbox | yorch @ web [in] 17/09/2008 06:09
  27. Detectar los eventos disponibles en el navegador de tu usuario | aNieto2K 03/04/2009 06:04
  28. Lenguaje de la Web : HTML 5 « Gnu/Linux 29/05/2009 11:05
  29. Diferencias entre las especificaciones de HTML 4 y HTML 5 :Blog actyonad 03/07/2009 09:07
  30. Dos artículos sobre javascript de obligada lectura « MicroJuanan 18/10/2009 09:10
  • Lo malo de esto es que las funciones para recorrer el árbol y modificar el contenido son leeeeeeeeeeeeentas, muy lentas :-(
    Yo muchas veces tengo que recurrir al innerHTML para que la cosa vaya bien, pero creo que no es muy correcto, ¿no?

  • Interesante, y mucho, y muy bien tanto redactado como traducido.

  • Buenas,

    2) gracias Pedrolo.

    1) Chavalina, realmente la W3C recomienda el uso de innerHTML ya que es mucho más fácil, aunque tenemos una serie de inconvenientes que en algunos casos nos puede dar algun que otro dolor de cabeza. Por ejemplo, no recibes ninguna referencia del elemento creado con él, ya que el valor es más bien un string que un objeto.

    Echalé un vistazo a estos enlaces, aún no he tenido tiempo de leerlo, así que no puedo ayudarte mucho más.

    innerHTML en Mozilla
    innerHTML vs DOM
    innerHTML

  • Muy interesante el artículo. Yo añadiría otro punto:

    EVITAR SIEMPRE QUE SEA POSIBLE LA FUNCIÓN: document.write()

    Complica el diseño, la depuración, produce resultados casi imprevisibles… Imaginad lo que es depurar una aplicación de servidor que genera código de manera dinámica, y que encima ese código sea JavaScript que, a su vez, incluye sentencias document.write … Horrible, y lo sé por experiencia

  • 1. Nunca, bajo ninguna circunstancia incluyas javascript directamente en el documento.

    Muy estricto en este punto. Para funciones únicas a una página en concreto (no usadas en ninguna otra), se podría considerar más que correcto el incluir dichas funciones en la misma. Esto evita al servidor tener que leer dos ficheros (y por lo tanto hacer como mínimo un seek extra). Además el ancho de banda permanecería igual que si el fichero estuviera separado.

    Con todo, si el servidor posee algún sistema de caché siempre será más fácil tener en cache la página con su javascript embebido que tener dos ficheros distintos.

    Esto también es aplicable a las CSS. Pero repito, siempre que las funciones (si hablamos de javascript) no se vayan a usar en otras páginas.

    Por lo demás, todo muy bueno.

    Felicidades.

  • kyroii, no estoy de acuerdo.

    Principalmente por que las conexiones cada vez más, estan haciendo que el problema del peso sea secundario, las páginas de hace años pesaban mucho menos que ahora debido a que las conexiones no eran tan altas. Ahora una página web puede pesar 100kb (es más últimamente parece más que norma) y no tardar ni siquiera la mitad de tiempo que tardaban hace 5 años 10kb.

    Por otra parte, aunque tenga que hacer un seek más, creo que vale la pena emplear un 1kb de RAM del servidor más en que envie un fichero con una sola función (o 3000), con el fin de poder hacer llegar a todo el mundo el contenido. Quizas por esa función estes limitando el contenido o la funcionalidad a un 1% de los que entran en la web. Y si lo puedes evitar por mínimo gasto de procesamiento, creo que se puede asumir.

    Luego cada uno lo hará como quiera, pero evidentemente si separamos la funcionalidad en un fichero a parte para aplicaciones grandes, ¿por que no debemos hacerlo en las pequeñas?

  • Hola,

    sobre el peso de las páginas, éste sería exactamente el mismo. Mi razonamiento no iba por ahí. Si tienes una página de 30KB y javascript embebido de 2KB, va a pesar lo mismo si lo separas en un fichero.

    La cuestión principal es que todos sabemos que lo más lento dentro de un ordenador es el disco duro. Si tenemos dos ficheros, hay que hacer dos seeks como mínimo y a mayor cantidad de seeks, menos IOPS.

    Así que, si por ejemplo tenemos una página que va a usar unas funciones javascript que ninguna otra va a usar… ¿por qué no incluirlas en la misma página? Eso sí, bien separaditas del contenido, nada de guarradas entre el código.

    ¿Qué más da tener la funciones en un fichero js a tenerlas entre las header (para este caso)? Esta claro que si estas funciones se van a usar en otras páginas sería tonto no separarlo en otro fichero pero… en este caso en concreto al menos se podría considerar no-incorrecto, y si se me permite, beneficioso :)

    ¡un saludo!

  • Kyroii, estoy de acuerdo en que quizas la carga del servidor suba un 1% o menos al realizar un seek, pero creo que la posibilidad de realizar una compra en …. de un usuario con navegador sin JS les parece un plato demasiado suculento para preocuparse por un 1%.

    Estamos en una época en la que la tecnología es muy superior a los lenguajes de programación web, se puede dejar la mano un poco ancha para hacerle llegar la web a todo el mundo.

    Otra cosa sería, que estuvieramos pensando en desarrollo de drivers o protocolos varios, que queramos optimizar a conciencia, donde la gran mayoría no va a meter mano.

    Son dos filosofías de programar muy distintas.

    Un saludo.

  • Hola a todos,

    si que es cierto que las buenos modos piden incluir en un fichero externo el código JS que vaya a ser reutilizado por varios páginas pero en el caso de kyroii estoy de acuerdo cuando dice:
    Así que, si por ejemplo tenemos una página que va a usar unas funciones javascript que ninguna otra va a usar… ¿por qué no incluirlas en la misma página?
    Si es un sólo documento el que necesita el código JS no me parece mala idea :-)

  • Nosoynadie, realmente no es mala idea, pero no se recomienda.

    Luego, cada proyecto, cada persona y cada web es un mundo, pero opino que es más facil de mantener un fichero JS externo que una página con JS incrustado por medio.

    Por poner un ejemplo, en mi curro metemos las funciones que solo se usan en una página dentro de la misma, y cuando alguien tiene que modificar una página de este estilo las pasa canutas (a no ser que haya sido el que las haya insertado).

    A mi personalmente me parece más claro tener un fichero externo con el javascript aunque sean 3 funciones que no se usan en otro sitio. Por esa misma premisa, prefiero tener 50 ficheros JS en vez de uno con todas las funciones JS dentro de uno. Tambien he de reconocer mi desconocimiento del coste de lectura a nivel de sistema, ya que esa rama de la informática más bien se me escapa, soy de gestion :P

    Un saludo.

  • Sí, cada web es un mundo. Supongo que si la web en cuestión no tiene muchas visitas la mejor opción es fichero a parte. Seguro que es más fácil de mantener, aunque no debería ser mucho más fácil que dentro de la página (siempre que las cosas estén bien hechas, claro)

    Los accesos a disco cuestan mucho. Mucho. Además, acabo de pensar que con un fichero a parte casi seguro que son 3 seeks ya que normalmente los js se suelen meter en algun directorio junto con los demás. Así que tienes la lectura/seek del html, directorio (que al fin y al cabo es un fichero más) y fichero js.

    Supongo que no hay ciencia exacta pero si la web no tiene muchas visitas y el servidor no se satura podemos tener todo en ficheros js a parte para que sea todo un poco más mantenible y claro. Por otro lado, si queremos optimizar los accesos de los usuarios a la web, incluir las funciones propias y únicas de esa web en las header sería beneficioso (esto incluso se puede extender con las css que sean propias y únicas)

    Saludos

  • Hablando esta tarde a la hora de la comida con un compañero, su opinion para mi es ley!!!, hemos llegado a la conclusión que es más una metodología que la mejor opción. En el afán de separar las 3 capas, se opta por esto y no se tiene en cuenta el consumo de servidor. ¿que es mejor o peor?, eso no puedo discutirlo por que no conozco el consumo y si es significativamente muy grande. Pero si tienes una web con varios ficheros JS para todas las páginas, ¿por que no usar la misma filosofía para todo? No sé, quizas como programador sin experiencia en sistemas no me preocupe el precio de tiempo de procesador a pagar.

    Por cierto, tengo vagos recuerdos de la Universidad sobre esto de los discos duros. Eran una tabla de inodos con la ruta de los ficheros y que si accedes a un fichero directamente con la ruta únicamente iba hacia el fichero que ya estaba refernciado en la “tabla de inodos” y únicamente tenía que abrirlo y enviarlo al navegador.

    Que alguien me corrija si me he ido por la ramas, o me he inventado la mitad. Me interesa mucho el tema.

  • Yo soy de sistemas xD

    En efecto, es más una metodología. Los recursos “extra” por tenerlo separado es relativo ya que depende de la cantidad de clientes que se conecten a la web a la vez, si son muchos o pocos. Incluso el “mucho” o “poco” también es relativo para cada caso.

    Lo que quería dar a entender es que para algunos casos puede ser beneficioso tenerlo todo junto y que en ese sentido el primer punto era demasiado extricto :)

    Y sobre el sistema de ficheros… pff… xD. En la universidad una de las prácticas era programar un sistema de ficheros pero la verdad es que ya no me acuerdo mucho del tema. Pero lo que recuerdo es esto:

    En un sistema de ficheros hay inodos, éstos apuntan a bloques (“trozos” de disco de una cantidad fija en tamaño). Los inodos no guardan la ruta de fichero, son sólo un apuntador a bloques del disco. Sin embargo pueden guardar información sobre permisos, ultimos accesos al fichero, etc.

    Si por ejemplo, queremos leer el fichero /prue/ba/hola.txt primero leeremos el directorio raíz (que no es más que otro fichero [directorio == fichero marcado como directorio] apuntado por un inodo, el cual conocemos gracias a la información del superbloque).

    Leemos el bloque (o bloques) del directorio raiz “/”. En un directorio lo que se guarda es un listado de objetos que hay dentro del mismo su número de inodo). Vemos si dentro está “prue”; si está y es un directorio, con su número de inodo leemos su/s bloque/s.

    Si en los mismos hay información sobre la existencia de “ba” y es un directorio entonces con su número de inodo leemos su/s bloque/s.

    Si en los mismos hay información sobre la existencia de “hola.txt” y no es un directorio entonces con su número de inodo leemos su/s bloque/s y así hemos recuperado la información.

    Como ves, se hacen muchos accesos a disco y esto es bastante lento así que muchos sistemas de ficheros incorporan un buffer-cache que guarda rutas completas (por ejemplo /prue/ba/hola.txt) y el inodo del fichero (en nuestro caso el inodo del hola.txt que está dentro de /prue/ba/) Así que si quisieramos acceder a /prue/ba/hola.txt miraríamos si tenemos esta ruta en la caché; si es así, perfecto: conocemos directamente el inodo de hola.txt y recuperamos directamente la información (quizás sea eso lo que recordabas ¿?). Sino, hay que hacer todo lo descrito anteriormente, ir paso a paso. El problema del buffer/cache (como todo en esta vida) es que es finita.

    Bueno, esto es más o menos cómo _creo_ que funcionaba un sistema de ficheros basado en i-nodos. Pero vamos tampoco me hagas mucho caso :P. Lo mejor será mirar fuera.

    PD: Lo que es seguro es que los inodos no guardan rutas de fichero ya que si por ejemplo quisieras listar el contenido de un determinado directorio te tendrías que leer todos los inodos para saber que hay en el mismo. Cuando en realidad lo que se hace es un “cat”/”type” del directorio.

    Un saludo.

  • O.O Menudo post!!

    Me ha quedado muy claro, además me has recordado la práctica famosa del sistema de ficheros, yo tb tuve que hacer uno :D

    Recuerdo que, no me hagas mucho caso que intento recordar (chirrido en mi mente…), existe una tabla de inodos que contiene todos los ficheros y el inodo en donde poder encontrarlo, ¿puede ser? ¿vamos a tener que usar google? xDD

    Bueno, sea lo que sea, parece lógico pensar que mientras menos llamadas a disco hagamos mejoraremos el tiempo de respuesta (mucho o poco, pero se mejorará).

    ¿Si es estricto o no?… yo creo que no, simplemente es un manual de malas maneras,… es como el carnet de conducir, la línea continua en ningun caso se ha de atravesa,… excepto…. aqui pasará igual, aunque el excepto lo pondrá cada programador.

  • No la harías en la UIB en la asignatura de ASO a manos de Ricardo Galli, ¿no? xD

  • Hola a todos, me parece muy interesante el articulo, es bastante util pero no consigo
    pasar el objeto a la funcion manejadora del evento con attachEvent, hay os dejo el codigo por si le quereis echar un vistazo:

    
    function addEvent(obj,evType,evHandler){
    if(obj.addEventListener){
    obj.addEventListener(evType,evHandler,false)
    return true
    }
    else if(obj.attachEvent){
    var r = obj.attachEvent(”on”   evType,evHandler)
    return r;
    }
    else{
    return false;
    }
    }
    
    function prueba(e){
    this.id //Funciona en mozilla…
    
    //Aqui no consigo hacer referencia al objeto 
    }
    var my_obj = document.getElementById(”my_id”)
    addEvent(my_obj,’onmouseover’,prueba);
    

    Estaria muy agradecido si me pudieseis ayudar gracias.

  • Angel: Creo que el problema está en tu penúltima línea:

      addEvent(my_obj,’onmouseover’,prueba);

    el evento es mouseover SIN EL on COMO PREFIJO (otra cosa distinta que que se use con ‘on’ cuando lo indicas en html)

    y otra, las últimas dos líneas de código ¿dónde están?

       var my_obj = document.getElementById(”my_id”)
        addEvent(my_obj,’onmouseover’,prueba);

    me explico: éstas líneas deben ir dentro de una función y ésta ejecutarse en el evento onload (o load si usas el addEvent). ¿Por qué? Porque si no es así, el objeto que intentas referenciar no existe en el momento de ejecutarse ese código (el navegador aún no procesó los elementos del body del html y no existe; el evento onload/load de la página se ejecuta despues de procesar todo el documento y en ese momento tu objeto ”my_id” ya existirá). Vamos, yo lo substituiría por:

        function init()
        {
          var my_obj = document.getElementById(”my_id”)
          addEvent(my_obj,’onmouseover’,prueba);
        }
       addEvent(document,’load’,init);  // no recuerdo si el objeto del evento es  document  o  window , tendrás que probarlo ;-)
    

    Espero que te sirva de ayuda y vaya todo correcto ;-) Aburiño!

  • p3k, el evento que quieres usar es del objeto window.

  • Hola,

    Los ficheros JS son muy interesantes en la programacion javascript, ademas de por tener separado el codigo y ser mas facil de mantener, tambien porque los .js se descargan en la caché del navegador de cada uno. Con lo cual, las paginas HTML pueden reducir mucho su peso, ya que el .js se descarga una sola vez y se puede usar en muchas.

    Lo mismo pasa con los .css, si tenemos los estilos en ficheros separados, se facilita mucho el mantenimiento y solo se cargaran una vez. Mucho mas comodo que plagar los HTML de STYLE, y por la misma razon de antes el tamaño de las paginas HTML puede ser bastante menor.

    Un saludo.

  • cito:

    Reglas del Javascript no obstructivo

    1. Nunca, bajo ninguna circunstancia incluyas javascript directamente en el documento.

    —> y por qué dejas que google lo haga con tu página? ;-)

    Un saludo y gran artículo.

  • #39 Si lees un poco más te encontrarás…
    Cada proyecto es un mundo…

    Partiendo de esta premisa hay que elegir la mejor opción para el proyecto concreto que queremos hacer, en mi caso necesito costearme el servidor y permito pongo código de google en la página por que no hay otra forma de ponerlo. Aparte de eso, intento no usar javascript en ningun sitio más.

  • que buen manual heeeeeeee

    exelente, lop boy a checar por completo muy detenidamenet ay cosas muy interezantes.

    bueno es todo

    nos vemosen el futuro…!

  • Volviendo al tema de llevar JS en un fichero aparte, el hecho de hacerlo de esa manera implica que el navegador cliente lo guarda en cache, en cambio embebido en el codigo ese codigo js seguiria cargandose con lapagina cada vez que esta llamada. Este es otro motivo de eficiencia por el cual es recomendable llevar el js en un fichero externo. Haces el esfuerzo de la carga inicial pero despues esta cacheado.

  • @Manuelito: Exacto, ese un gran argumento a favor de separar el código :D Gracias

  • Que bien que enseñen a limpiar los códigos de javascript!!!

    En nuestro blog también tratamos temas relacionados con diseño web, por si se quieren dar una vuelta:

    http://stylemesh.blogspot.com/

    :)

  • Obstructivo no es una palabra. Duele a los ojos… @_@

Comentar

#

Me reservo el derecho de eliminar y/o modificar los comentarios que contengan lenguaje inapropiado, spam u otras conductas no apropiadas en una comunidad civilizada. Si tu comentario no aparece, puede ser que akismet lo haya capturado, cada día lo reviso y lo coloco en su lugar. Siento las molestias.