Archivo de la categoría ‘AJAX’

funcion javascript para Simular un click

Lunes, 25 de Mayo de 2009
JAVASCRIPT:
  1. /* ***********************************************************************************
  2. * SimularClick: Simular un click en un objeto.
  3. *   idObjecte : objecte sobre el cual se aplica el evento click
  4. * ***********************************************************************************/
  5.  
  6. function SimularClick(idObjete){
  7.  
  8. var nouEvent = document.createEvent("MouseEvents");
  9. nouEvent.initMouseEvent("click", true, true, window,0, 0, 0, 0, 0, false, false, false, false, 0, null);
  10.  
  11. var objecte = document.getElementById(idObjete);
  12. var canceled = !objecte.dispatchEvent(nouEvent);
  13. }

Controlar el botón de atrás o botón adelante del Navegador en Javascript AJAX (Historial de navegación)

Sábado, 31 de Enero de 2009

Hoy vamos a escribir el mejor articulo en español para dominar el control del botón de atras de los navegadores. A día de hoy es muy facil añadir funcionalidades AJAX a nuestras páginas, pero que problematicas encontramos? La primera y mas indeseable de todas es que perdemos el botón de atras del navegador, el back button, otro día trataremos la seguridad o mejor dicho la inseguridad...

Conociendo a los navegadores y el funcionamiento del historial de navegación:

Firefox 2 : El funcionamiento del botón atrás como todo el mundo sabe es sencillo. Pero cuando se guarda firefox una url en el botón de atrás? hay dos maneras:

  • Cada vez que pulsamos un link o formulario. Firefox almacena una url nueva en su historia de navegación. Este evidentemente no nos sirve porque cambiamos y recargamos la página.
  • Además y por curioso que parezca, cuando en una misma página pulsamos sobre un link, pero esta vez no uno que vaya a otra url, sinó uno que simplemente contenga un ancla para bajar o subir dentro de una misma página. Firefox 2 almacena esta url en su historial, y lo mejor de todo, no recargan la página. Utilizando estas anclas para guardar iinformación conseguiremos nuestro objetivo, saber cuando han pusaldo e botón de atras y que evento debemos actualizar.

Firefox 3: E principio se comporta igual que firefox 2. Pero ahora mismo haciendo unas pruebas, y  pese a no actualizar la url del iframe al pulsar sobre el boton atras, si actualiza el hash de la url, con lo que es posible detectar el cambio de página mediante el iframe oculto. Faltaria verificar si firefox 2 tambien actualiza el hash del iframe, cuando lo probé vi que no actualizaba la url y desisté de hacer mas pruebas. (cuando me refiero a hash hablo de la propiedad : ObjetoIframe.contentWindow.document.location.hash)

Si esto fuera verdad se podría utilizar el iframe oculto tanto para IE, como para firefox 2 y 3...

Internet explorer 6 e Internet Explorer 7: Es algo distinto que firefox por definicion... Así que este navegador solo guarda una url cada vez que recargamos una página html. Esto supone un problema porque lo que pretendemos hacer es sin recargar la página actual. Queremos conseguir que el navegador añada una entrada en su historial, para despues nostros recuperar la información necesaria y refrescar la zona que sea necesaria mediante AJAX.

Pero Internet Explorer tiene algo bueno en este caso. Si colocamos un Iframe en la página, toda url cargada en ese iframe es almacenada en el historial de navegación. Así que genial, ya tenemos como conseguir añadir url's al historial, sin recargar la página actual, pero si el iframe. Tan solo añadiremos un iframe oculto, vació, invisible con css o de tamaño 0px por 0px.  Para engañar al navegador y coseguir añadir url's al historial de navegación cada vez que invoquemos una peticion AJAX.

Además IE no es capaz de memorizar las anclas como url's disntintas motivo por el cual, al no añadir al historial ese cambio de url necesitamos necesariamente recargar una página dentro del iframe. esto hace que debamos platear una solución alternativa a la que utilizaremos en Firefox

Nota: Firefox no vuelve a recargar la página de un iframe! Así que aunque si se añade a la navegación, no recarga la pagina, y no cambia la url del iframe, lo que impide detectar cuando ha habido una variación desde javascript.

Navegador Opera  9.10: Haciendo unas pruebas con este navegador, parece que almacena en su historial tanto los cambios de los iframes como los cambios de ancla's sobre una misma url, por lo que se podría utilizar cuaquiera de las dos variates de la solución. Este ejemplo es compatible con Opera, puesto que solo diferenciamos a IE, la variante de utilizar las anclas para Opera funciona correctamente. Por lo menos para la version 9.10.

Navegador Google Chrome en su versión 1.0.154.43 : No almacena los cambios de iframe dentro de su historial de navegación. Me sorprende, quizas al detectar que la página es igual no la guarda, no lo se... Pero por lo que parece no funcionaria ir cargando paginas en el iframe oculto. Actualmente en la versión 1.0.154.46 Sigue comportandose  de la misma manera. Nuestro ejemplo funciona correctamente en las versiones de google Chrome comentadas. Grácias a que almacena en su historial de navegación los cambios de ancla en una misma página. Así puede comportarse correctamente como si se tratara de un navegador Firefox.

Navegador Safari sobre windows versión 3.2.1: Sobre esta versión de safari el ejemplo funciona bien. Este navegador tambien recuerda la navegación de las anclas.  Safari se comporta como Google Chrome y no guarda en su historial de navegación las páginas cargadas en los iframes. Así que el botón atras cambia al ultimo enlace.

Conclusion:

La mayoria de los navegadores recuerdan en su historial de navegación las anclas pulsadas así podemos utilizar el control de botón de atras con el sistema de cambio anclas. Solo diferenciando a Internet Explorer tendremos suficiente para aplicar las dos soluciones planteadas.

Si alguien tiene tiempo y quiere probarlo sobre MAC y Linux, estaré agradecido.

LA SOLUCIÓN:

Resumen de lo necasario:

  • Un Iframe oculto en la pagina, ( para IE  que no sabe recordar anclas)
  • Un fichero dinamico (asp, php o fiticio) que esté en blanco para usar en en las urls del  iframe.
  • Una porción de codigo que gestione el cambio de urls al hacer peticions AJAX.
  • Una función de javascript (temporizador) que vigile los cambios de las urls cada segundo.
  • Una array javascritp con el historial de navegación, ( y la información necesaria para revertir los cambios y así actualizar la página convenientemente con AJAX)

La solución planteda, para conseguir emular la tecla o boton de atras de los navegadores cuando hacemos peticiones AJAX o aplicaciones de este estilo, es un sistema tan sencillo que no ocupa mas de 30 lineas de código.  El sistema se basa en el siguiente concepto. Vamos ha añadir cada vez que hagamos una peticion AJAX, un identificador concatenado a la url actual en forma de ancla.Asi podriamos hacer una variable incremental con el número de peticion y concatenarlo detras de la url. Mediante un

document.location=document.location+"#"+idNuevaPeticion

Ahora repetiremos el mismo proceso para el iframe oculto, en caso que detectemos que el navegador es IE. Con una ligera modificación. Para que Internet explorer entienda que la página es distinta se deberá concatenar una variable a la url (para no tener que inventarnos cada vez una url distinta.) Para  maor comodidad, aparte de la variable concatenaremos tambien el ancla que nos es mas facil de recoger desde javascript. Ahora quedaria algo asi:

JAVASCRIPT:
  1. function EnviarFormulari(){
  2.  
  3. // 1- activar el flag de que cambiamos nosotros las urls
  4.  
  5. busquedaLanzada=true;    // variable global para determinar si hemos sido nosotros quien hemos cambiado la url o a sido el navegador provocado por un back button o boton de atras
  6.  
  7. // 2 cambiamos las urls
  8.  
  9. // Ahora segun el navegador cambiamos la url del iframe o la del url del document
  10. if (esNavegadorIE()){
  11. var rutaExterna= ObtenerRutaExterna();
  12. Obtenir("historial").src=rutaExterna + "/unFicheroVacio.php?variableEngaño=" + idHistorialActual+ "#"+idHistorialActual;
  13. }else{
  14. window.location=LimpiarUrl(window.location)+'#'+idHistorialActual;
  15. }
  16.  
  17. // 3 - Enviamos la peticon AJAX
  18.  
  19. /**   Aquí iria tu código de peticiones ajax...
  20. crear tu objeto HTTPREQUEST, definir que datos enviamos a que url, etc, etc, no afecta al control del botón de atras...
  21. */
  22.  
  23. }
  24.  
  25. /**
  26. * Existe un problema de denegacion de servicio por cross domain,
  27. * para ello es necesrio que lso javascrips esten todos llamados desde ficheros bajo el mismo protocolo y dominio,
  28. * distingue entre tudominio.com y www.tudominio.com , http://tudominio.com , https://tudominio.com tambien son considerados dominios distintos. de cara a este error.
  29. * Esta funcion recoge los parametros de la url cargada para saber como hemos de cargar los escripts
  30. */
  31.  
  32. function ObtenerRutaExterna(){
  33.  
  34. //existe el problema de across domain denegation Script
  35. var protocoloUrlPrincipal=document.location.protocol;
  36. var dominioUrlPrincipal=document.location.host;
  37.  
  38. return protocoloUrlPrincipal+"//"+dominioUrlPrincipal;
  39. }
  40. /**
  41. * Detectar IE, mediante objetos
  42. */
  43. function esNavegadorIE(){
  44. // hay muchas maneras a mi me gusta esta.
  45. // este componente solo tiene Internet Explorer apartir de la versión 5.5
  46. if (window.ActiveXObject) {
  47. return true;
  48. }
  49. else{
  50. return false;
  51. }
  52. }

Vemos ya un par de funciones necesarias una para detectar el navegador, Otra para conseguir la URL tal y como han llegado, esto sirve por si utlizas SSL y tienes dos tipos de protocolos, http://tudominio.com y https://tudominio.com.  De todos modos lo que comenta que soluciona esta función seria si quisieramos ejecutar código javascript desde el iframe al documento principal, pero finalmente no ha sido necesario, asi que se podria simplificar este paso. y poner una ruta absoluta como querais!

la funcion Obtener, tansolo hace un document.GetElementById(), en este caso del iframe.

Lo mas destacable de este codigo es la variable Historial, que hemos creado nosotros y donde nos guardaríamos toda la información necesaria para conseguir hacer la misma petición AJAX que invoca el proceso. esta variable es un array en nuestro caso, que tiene en cada posición del array correspondiente a un id incremental, la busqueda que realiza un buscador AJAX. Con ese dato tenemos suficiente para actualizar la página, para aplicaciones mas complejas, este ARRAY Historial,  deberá ser una estructura mas compleja un objeto o lo que necesite cada uno en su caso. Para cada aplicación esto variará. Abusando de memoria te podria almacenar la respuesta de la petición, para no volver a lanzar al ir atrás...

Enfin, vamos a seguir explicando el preceso

Por ahora hemos visto como conseguir que por cada petición ajax tengamos que el botón de atras del navegador se active, cambie la url visible del navegador en Firefox o cmabie la dle iframe oculto en Internet explorer. Pero ahora como recargamos nosotros la pagina que queriamos???

Bueno necesitaremos crear lo que he denominado como el Guardian de las Urls :P, en definitiva no es mas que una funcion de javascript que se llame cada tanto tiempo para determinar si ha habido un cambio en la url. Cuando este cambio se ha producido tendremos que decidir si lo hemos provocado nostros justo antes de enviar una petición mediante el codigo que hemos visto arriba o  si por el contrario alguien ha pulsado sobre el botón de atras o el botón de alante. Para ello solo sera necesaria una variable o Flag que nos indique esto. En nuestro ejemplillo es la llamada busquedaLanzada que ponemos a true justo antes de cambiar las url's!

Para mirar si la url a cambiamo respecto la ultima vez que hemos pasado a revisarla solo serán necesario 2 variables una para controlar la url de iframe y otra para controlar la url del document.

Os pongo e código usado:

JAVASCRIPT:
  1. var busquedaLanzada=false;// para controlar se se ha echo ya la petición de buscar o es un boton atras.
  2. var temporizador; // variable global que se utiliza para controlar el temporizador de revision de url
  3. var velocidadTemporizador=1000;
  4. var idHistorialActual=0; // proporciona historial de busquedas
  5. var idHistorialAnterior=0;
  6. var idHistorialAnteriorIE=0;
  7. var RUTA_EXTERNA= 'http://sintoner.com/';
  8. var historial=new Array();
  9.  
  10. function GuardianUrl(){
  11. temporitzador=setTimeout("GuardianUrl()",velocidadTemporizador); //1000= 1 seg.
  12. //repite esa funcion cada tanto tiempo. para pararlo se utilizatria un clearTimeout(variable_de_tiempo)
  13.  
  14. // consiguiendo el HASH de las rutas pagina principal e IFRAME.
  15. // HASH URL NORMAL CASO FirefoxF3
  16. var urlActual = window.location.toString();
  17. var busquedaUrl = window.location.hash.substr(1);
  18. // HASH URL IFRAME CASO IE7,IE6
  19. var urlActualIframe = document.getElementById('historial').contentWindow.document.location;
  20. var busquedaUrlIE = String(urlActualIframe.hash.substr(1));
  21.  
  22. //siempre va a pasar que: el de la ur es menor o igual al Actual + Diferente al ultimo mirado (primer if detecta si ha cambiado la url en alguno de los 2 sistemas IE o FF3)
  23. if( (busquedaUrlIE!= idHistorialAnteriorIE && idHistorialAnteriorIE> 0 ) || busquedaUrl<=idHistorialActual && idHistorialAnterior!=busquedaUrl && busquedaUrl>0 && idHistorialActual>0){
  24. if(!busquedaLanzada){
  25. //si no se ha lanzado la busqueda querrá decir que es un botón atras o alante.
  26. if(Obtenir('buscador')) {
  27. if ((busquedaUrlIE!= idHistorialAnteriorIE && idHistorialAnteriorIE> 0 )) {
  28. //caso entramos por cambio en id por IFRAME IE
  29. Obtenir('buscador').value=historial[busquedaUrlIE];
  30. }else {
  31. // caso normal, entramos por el hash de  la url superior FF3
  32. Obtenir('buscador').value=historial[busquedaUrl];
  33. }
  34.  
  35. Cargando();
  36. EnviarFormulari();
  37. }
  38. }else{
  39. //este caso es para cuando existe un cambio de url, pero ha sido provocado por nosotros, no es un boton atras! desactivamos el flag!
  40. busquedaLanzada=false;
  41. }
  42. }
  43.  
  44. // despues de lanzar o no la busqueda igualo el cambio de las urls.
  45. idHistorialAnterior=busquedaUrl;
  46. idHistorialAnteriorIE= busquedaUrlIE;
  47. }

Problematicas encontradas: Este sistema funciona al 100% en páginas totalmente AJAX, en el momento que pulsemos sobre un enlace para irnos a una página nuestra sin aplicación ajax! Pufff los navegadores borrán los historiales y no se comportará como queremos. Esto es una limitación. Pero en realidad tiene solucion. Ahora estamos guardando el historial en un Array en el cliente, se podría guardar ese arrya en el servidor y con ajax consultar lo que queremos darle. Asi, cuando se cambia de un link normal, se hace un bucle que va cargando las paginas vacias para volver a rellenar ese historial de navegación fictico que hemos montado.  Haber si tengo tiempo y hago el codigo para solucionar este error. Otra manera sería con cada petición guardar el historial en el fichero que atiende las peticiones ajax en el servidor, así tener el historial en el servidor en una sesion de php, por ejemplo...

Se podrían hacer infinidad de cosas vosotros mismos!

Para cualquier duda postear un comentario.

ejemplo funcionando en:

El buscador de  consumibles mas baratos de internet

FIN

Notas: el codigo se podría optimizar un poquito mas, pero como ya va, es sabado, estoy cansado y he perdido el tiempo contando, el genial sistema, si alguien lo mejora que nos lo envíe y lo colgaremos!

Espero sea de utilidad, yo he perdido mucho tiempo con clases que dicen controlarlo y son muy dificiles de utlizar o almenos de integrar y nunca se acaban de asimilar a lo que necesitaba...

Todo el contenido es integramente creado por mí en controlzeta.net Y aunque dejamos que pueda usarse bajo todos los fines, "exigimos" estaría bien que se haga mención al autor Cristian Martinez y se enlace a controlzeta.net!

licencia: Crative Commons