From 1109132f09d75da9a28b649c7677bb6ce07c40c0 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:41:45 -0500 Subject: initial commit --- files/es/dom/almacenamiento/index.html | 302 +++++++++++++++ files/es/dom/document.cookie/index.html | 119 ++++++ files/es/dom/index.html | 86 +++++ .../ejemplo/index.html | 415 +++++++++++++++++++++ .../index.html | 228 +++++++++++ files/es/dom/touch_events/index.html | 292 +++++++++++++++ 6 files changed, 1442 insertions(+) create mode 100644 files/es/dom/almacenamiento/index.html create mode 100644 files/es/dom/document.cookie/index.html create mode 100644 files/es/dom/index.html create mode 100644 files/es/dom/manipulando_el_historial_del_navegador/ejemplo/index.html create mode 100644 files/es/dom/manipulando_el_historial_del_navegador/index.html create mode 100644 files/es/dom/touch_events/index.html (limited to 'files/es/dom') diff --git a/files/es/dom/almacenamiento/index.html b/files/es/dom/almacenamiento/index.html new file mode 100644 index 0000000000..4b1f0295a0 --- /dev/null +++ b/files/es/dom/almacenamiento/index.html @@ -0,0 +1,302 @@ +--- +title: Almacenamiento +slug: DOM/Almacenamiento +tags: + - DOM + - JavaScript + - Referencia_DOM_de_Gecko + - Todas_las_Categorías + - para_revisar +translation_of: Web/API/Web_Storage_API +--- +

{{ ApiRef() }}

+

Introducción

+

El almacenamiento DOM (DOM Storage) es el nombre dado al conjunto de características relacionadas con el almacenamiento introducidas en la especificación de aplicaciones web 1.0 y ahora detalladas por separado en su propia especificación W3C Web Storage. El almacenamiento DOM está diseñado para facilitar una forma amplia, segura y sencilla para almacenar información alternativa a las cookies. Fue introducido por primera vez en Firefox 2 y Safari 4 .

+
+ Nota: el almacenamiento DOM no es lo mismo que mozStorage (las interfaces XPCOM de Mozilla para SQLite) o la API para guardar sesiones (una utilidad de almacenamiento XPCOM usada por extensiones).
+

Descripción

+

El mecanismo de almacenamiento DOM es el medio a través del cual pares de clave/valor pueden ser almacenadas de forma segura para ser recuperadas y utilizadas más adelante. La meta de este añadido es suministrar un método exhaustivo a través del cual puedan construirse aplicaciones interactivas (incluyendo características avanzadas tales como ser capaces de trabajar "sin conexión" durante largos períodos de tiempo).

+

Actualmente los navegadores basados en Mozilla, Internet Explorer 8+ y Safari 4 y Chrome proporcionan una implementación funcional de la especificación del almacenamiento DOM. Sin embargo, las versiones anteriores a Internet Explorer 8 poseen una característica similar llamada "userData behavior" que permite conservar datos entre múltiples sesiones.

+

El almacenamiento DOM es útil ya que ningún navegador dispone de buenos métodos para conservar cantidades razonables de datos durante un periodo de tiempo. Las cookies de los navegadores tienen una capacidad limitada y no implementan una forma de organizar datos persistentes y otros métodos (tales como almacenamiento local de Flash) necesitan un plugin externo.

+

Una de las primeras aplicaciones hechas públicas que hace uso de la nueva funcionalidad de almacenamiento DOM (además del userData Behavior de Internet Explorer) fue halfnote (una aplicación para tomar notas) escrita por Aaron Boodman. En su aplicación, Aaron enviaba notas hacia el servidor (cuando la conexión a Internet estaba disponible) y simultáneamente las guardaba en local. Esto permitía al usuario escribir notas de modo seguro incluso cuando no disponía de conexión a Internet.

+

Aunque el concepto e implementación presentada en halfnote era en comparación simple, su creación mostró las posibilidades de esta nueva generación de aplicaciones web utilizables tanto con conexión como sin ella.

+

Referencia

+

Los siguientes objetos globales existen como propiedades de cada objeto window. Esto significa que se puede acceder a ellas como sessionStorage o window.sessionStorage (esto es importante ya que se puede usar IFrames para almacenar o acceder a datos adicionales, más allá de lo que está inmediatamente incluido en la página).

+

Storage

+

Este es un constructor ( Storage ) para todos los objetos de almacenamiento ( sessionStorage y globalStorage[location.hostname]). Al hacer Storage.prototype.removeKey = function(key){ this.removeItem(this.key(key)) } podrías usar luego como atajo a la función removeItem("key") la forma localStorage.removeKey and sessionStorage.removeKey.

+

Los elementos globalStorage no son de tipo Storage , sino StorageObsolete .

+

Storage se define por la interfaz de almacenamiento WhatWG de la siguiente forma:

+
interface Storage {
+  readonly attribute unsigned long length;
+  [IndexGetter]key DOMString (in unsigned long index);
+  [NameGetter] DOMString GetItem (in DOMString key);
+  [NameSetter] void setItem (in DOMString key, in DOMString data);
+  [NameDeleter] void removeItem (in DOMString key);
+  void clear();
+};
+
+

 

+
+ Nota: aunque los valores pueden establecerse y leerse a través del método de acceso de la propiedad de JavaScript estándar, se recomienda el uso de los métodos getItem y setItem.
+
+ Nota: ten en cuenta que todo lo que guardes en cualquiera de los almacenamientos (storages) descritos en esta página se convierte en una cadena a través de su método .toString almacenado anteriormente, por lo que al intentar almacenar un objeto común, se almacenará una cadena "[object Object]" en lugar del objeto o su representación JSON. Usar los métodos de serialización y análisis de JSON nativos que proporciona el navegador es una buena forma bastante común de almacenar objetos en formato cadena.
+

sessionStorage

+

Este es un objeto global (sessionStorage) que mantiene un área de almacenamiento que está disponible durante la sesión de página. Una sesión de página existe mientras el navegador esté abierto y sobrevive a recargas o restauraciones de páginas. Abrir una página en una nueva pestaña o en una ventana provoca que se cree una nueva sesión.

+
// Guardar datos en el almacén de la sesión actual
+sessionStorage.setItem("username", "John");
+
+// Acceder a algunos datos guardados
+alert( "username = " + sessionStorage.getItem("username"));
+
+

El objeto sessionStorage es más usado para manejar datos temporales que deberían ser guardados y recuperados si el navegador es recargado accidentalmente.

+

{{ fx_minversion_note("3.5", "Antes de Firefox 3.5, los datos de sessionStorage no se restablecían automáticamente después de recuperarse de un fallo del navegador. A partir de Firefox 3.5, funciona según la especificación.") }}

+

Ejemplos:

+

Autoguardado de los contenidos de un campo de texto y, si el navegador se recarga accidentalmente, restauración del contenido del campo de texto para evitar la pérdida de datos.

+
 // Obtener el campo de texto al que vamos a seguir la pista
+ var field = document.getElementById("field");
+
+ // Ver si se tiene un valor de autoguardado
+ // (esto sólo sucede si la página es actualizada accidentalmente)
+ if ( sessionStorage.getItem("autosave")) {
+    // Restaurar los contenidos del campo de texto
+    field.value = sessionStorage.getItem("autosave");
+ }
+
+ // Comprobar los contenidos del campo de texto cada segundo
+ setInterval(function(){
+    // Y guardar los resultados en el objeto de almacenamiento de sesión
+    sessionStorage.setItem("autosave", field.value);
+ }, 1000);
+
+

Más información:

+ +

globalStorage

+

 

+

{{ Non-standard_header() }} Este es un objeto global ( globalStorage ) que mantiene múltiples áreas de almacenamiento privado que se pueden utilizar para almacenar los datos durante un largo período de tiempo (por ejemplo, en varias páginas y las sesiones del navegador) .

+
+ Nota: globalStorage no es de tipo Storage, sino un objeto de tipo StorageList que contiene a su vez elementos StorageObsolete.
+
// Guardar datos a los que sólo pueden acceder scripts del dominio mozilla.org
+globalStorage['mozilla.org'].setItem("snippet", "<b>Hola</b>, ¿cómo estás?");
+
+

Específicamente, el objeto globalStorage proporciona acceso a un número de diferentes objetos de almacenamiento en los que los datos pueden ser guardados. Por ejemplo, si se construye una página web que usa globalStorage en este dominio (developer.mozilla.org) se dispondría de los siguientes objetos de almacenamiento:

+ +

{{ Fx_minversion_note(3, "Firefox 2 permitía el acceso a objetos de almacenamiento superiores en la jerarquía del dominio al documento actual, pero esto ya no se permite en Firefox 3 por razones de seguridad. Además, se ha eliminado esta adición propuesta a HTML 5 de la especificación en favor de localStorage, que se implementa a partir de Firefox 3.5.") }}

+

Ejemplos:

+

Todos estos ejemplos necesitan que haya un script insertado (con el siguiente código) en cada página en la que se quiera ver el resultado.

+

Recordar el nombre un usuario para un subdominio en particular que está siendo visitado:

+
 globalStorage['developer.mozilla.org'].setItem("username", "John");
+
+

Seguir la pista al número de veces que un usuario visita todas las páginas de un dominio:

+
 // parseInt must be used since all data is stored as a string
+ globalStorage['mozilla.org'].setItem("visits", parseInt(globalStorage['mozilla.org'].getItem("visits") || 0 ) + 1);
+
+

 

+

localStorage

+

localStorage es lo mismo que globalStorage[location.hostname], excepto que está dentro del ámbito de un origen HTML5 (esquema + nombre de servidor + puerto no estándar), y que localStorage es un elemento de tipo Storage a diferencia de globalStorage[location.hostname], que es de tipo StorageObsolete . Por ejemplo, http://example.com no es capaz de acceder al mismo objeto localStorage que https://example.com pero pueden acceder al mismo objeto globalStorage. localStorage es una interfaz estándar, mientras que globalStorage no es estándar. localStorage fue introducida en Firefox 3.5.

+

Ten en cuenta que establecer una propiedad en globalStorage[location.hostname] no la establece en localStorage y extender Storage.prototype no afecta a los elementos globalStorage. Esto sólo se hace extendiendo StorageObsolete.prototype.

+
+ Nota: cuando el navegador entra en modo de navegación privada, se crea una nueva base de datos temporal para almacenar los datos locales de almacenamiento; esta base de datos se vacía cuando se sale del modo de navegación privada.
+
+
Compatibilidad
+

Los objetos Storage se han agregado recientemente al estándar, por lo que puede ocurrir que no estén presentes en todos los navegadores. Puedes solucionar esto insertando uno de los siguientes códigos al principio de tus scripts, lo que permitirá el uso del objeto localStorage object en aquellas implementaciones que de forma nativa no lo admitan.

+

Este algoritmo es una imitación exacta del objeto localStorage, pero haciendo uso de cookies.

+
if (!window.localStorage) {
+  Object.defineProperty(window, "localStorage", new (function () {
+    var aKeys = [], oStorage = {};
+    Object.defineProperty(oStorage, "getItem", {
+      value: function (sKey) { return sKey ? this[sKey] : null; },
+      writable: false,
+      configurable: false,
+      enumerable: false
+    });
+    Object.defineProperty(oStorage, "key", {
+      value: function (nKeyId) { return aKeys[nKeyId]; },
+      writable: false,
+      configurable: false,
+      enumerable: false
+    });
+    Object.defineProperty(oStorage, "setItem", {
+      value: function (sKey, sValue) {
+        if(!sKey) { return; }
+        document.cookie = escape(sKey) + "=" + escape(sValue) + "; path=/";
+      },
+      writable: false,
+      configurable: false,
+      enumerable: false
+    });
+    Object.defineProperty(oStorage, "length", {
+      get: function () { return aKeys.length; },
+      configurable: false,
+      enumerable: false
+    });
+    Object.defineProperty(oStorage, "removeItem", {
+      value: function (sKey) {
+        if(!sKey) { return; }
+        var sExpDate = new Date();
+        sExpDate.setDate(sExpDate.getDate() - 1);
+        document.cookie = escape(sKey) + "=; expires=" + sExpDate.toGMTString() + "; path=/";
+      },
+      writable: false,
+      configurable: false,
+      enumerable: false
+    });
+    this.get = function () {
+      var iThisIndx;
+      for (var sKey in oStorage) {
+        iThisIndx = aKeys.indexOf(sKey);
+        if (iThisIndx === -1) { oStorage.setItem(sKey, oStorage[sKey]); }
+        else { aKeys.splice(iThisIndx, 1); }
+        delete oStorage[sKey];
+      }
+      for (aKeys; aKeys.length > 0; aKeys.splice(0, 1)) { oStorage.removeItem(aKeys[0]); }
+      for (var iCouple, iKey, iCouplId = 0, aCouples = document.cookie.split(/\s*;\s*/); iCouplId < aCouples.length; iCouplId++) {
+        iCouple = aCouples[iCouplId].split(/\s*=\s*/);
+        if (iCouple.length > 1) {
+          oStorage[iKey = unescape(iCouple[0])] = unescape(iCouple[1]);
+          aKeys.push(iKey);
+        }
+      }
+      return oStorage;
+    };
+    this.configurable = false;
+    this.enumerable = true;
+  })());
+}
+
+
+ Nota: el tamaño máximo de datos que pueden guardarse está bastante restringido por el uso de cookies. Con este algoritmo, usa las funciones localStorage.setItem() y localStorage.removeItem() para agregar, cambiar o eliminar una clave. Usar los métodos localStorage.tuClave = tuValor; y delete localStorage.tuClave; para establecer o eliminar una clave no es muy seguro con este código. También puedes cambiar su nombre y usarlo para administrar las cookies de un documento independientemente del objeto localStorage.
+

Aquí tienes otra imitación, menos exacta, del objeto localStorage. Es mucho más simple  que el anterior, pero es compatible con los navegadores antiguos como Internet Explorer < 8. También usa cookies.

+
if (!window.localStorage) {
+  window.localStorage = {
+    getItem: function (sKey) {
+      if (!sKey || !this.hasOwnProperty(sKey)) { return null; }
+      return unescape(document.cookie.replace(new RegExp("(?:^|.*;\\s*)" + escape(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*"), "$1"));
+    },
+    key: function (nKeyId) { return unescape(document.cookie.replace(/\s*\=(?:.(?!;))*$/, "").split(/\s*\=(?:[^;](?!;))*[^;]?;\s*/)[nKeyId]); },
+    setItem: function (sKey, sValue) {
+      if(!sKey) { return; }
+      document.cookie = escape(sKey) + "=" + escape(sValue) + "; path=/";
+      this.length = document.cookie.match(/\=/g).length;
+    },
+    length: 0,
+    removeItem: function (sKey) {
+      if (!sKey || !this.hasOwnProperty(sKey)) { return; }
+      var sExpDate = new Date();
+      sExpDate.setDate(sExpDate.getDate() - 1);
+      document.cookie = escape(sKey) + "=; expires=" + sExpDate.toGMTString() + "; path=/";
+      this.length--;
+    },
+    hasOwnProperty: function (sKey) { return (new RegExp("(?:^|;\\s*)" + escape(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=")).test(document.cookie); }
+  };
+  window.localStorage.length = (document.cookie.match(/\=/g) || window.localStorage).length;
+}
+
+
+ Nota: el tamaño máximo de datos que pueden guardarse está bastante restringido por el uso de cookies. Con este algoritmo, usa las funciones localStorage.getItem(), localStorage.setItem() y localStorage.removeItem() para agregar, cambiar o eliminar una clave. Usar los métodos localStorage.tuClave para obtener, establecer o eliminar una clave no es muy seguro con este código. También puedes cambiar su nombre y usarlo para administrar las cookies de un documento independientemente del objeto localStorage.
+

Lugar de almacenamiento y borrado de datos

+

Los datos de almacenamiento DOM se guardan en el archivo webappsstore.sqlite de la carpeta del perfil.

+ +

Consulta también borrar la caché de recursos en modo sin conexión .

+
+

Más información

+ +

Ejemplos

+ +

Compatibilidad de los navegadores

+

{{ CompatibilityTable() }}

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CaracterísticaChromeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
localStorage43.5810.504
sessionStorage52810.504
globalStorage{{ CompatNo() }}2{{ CompatNo() }}{{ CompatNo() }}{{ CompatNo() }}
+
+
+ + + + + + + + + + + + + + + + + + + +
CaracterísticaAndroidFirefox Mobile (Gecko)IE PhoneOpera MobileSafari Mobile
Compatibilidad básica{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}
+
+

Todos los navegadores tienen diferentes niveles de capacidad tanto para local- como para sessionStorage. Aquí puedes ver un resumen detallado de todas las capacidades de almacenamiento de los distintos navegadores.

+

Contenido relacionado

+ +

{{ HTML5ArticleTOC () }}

+

 

+

 

+

{{ languages( { "en": "en/DOM/Storage", "fr": "fr/DOM/Storage", "ja": "ja/DOM/Storage", "pl": "pl/DOM/Storage", "zh-cn": "cn/DOM/Storage" } ) }}

diff --git a/files/es/dom/document.cookie/index.html b/files/es/dom/document.cookie/index.html new file mode 100644 index 0000000000..791ae788f2 --- /dev/null +++ b/files/es/dom/document.cookie/index.html @@ -0,0 +1,119 @@ +--- +title: document.cookie +slug: DOM/document.cookie +tags: + - NeedsContent +translation_of: Web/API/Document/cookie +--- +

{{ApiRef("DOM")}}

+ +

Resumen

+ +

Con document.cookie se obtienen y definen las cookies asociadas con el documento.

+ +

Sintaxis

+ +

Leer todas las cookies accesibles desde una localización

+ +
todasLasCookies = document.cookie;
+
+ +

En el código anterior todasLasCookies es una cadena que contiene una lista de todas las cookies separadas por punto y coma (en pares clave=valor). Tenga en cuenta que clave y valor pueden estar rodeadas por espacios en blanco (caracteres espacio y tabulación): de hecho RFC 6265 especifica que debe haber un espacio en blanco después de cada punto y coma (;), pero algunos agentes de usuario no son muy estrictos con esto.

+ + + +
document.cookie = nuevaCookie;
+ +

En el código anterior, nuevacookie es una cadena de la forma clave=valor. Tenga en cuenta que solo se puede crear o actualizar una cookie de cada vez mediante este método. Considere también que:

+ + + +
{{ gecko_callout_heading("6.0") }}
+ +
Nótese que previamente a Gecko 6.0 {{ geckoRelease("6.0") }}, rutas que contenían comillas eran tratadas como si las comillas fueran parte de la cadena, en lugar de considerarse como un delimitador de la ruta actual. Esto ya ha sido arreglado.
+ +

Ejemplos

+ +

Ejemplo # 1: Uso sencillo

+ +
document.cookie = "nombre=oeschger";
+document.cookie = "comida_preferida=tripa";
+function alertCookie() {
+  alert(document.cookie); // visualizar: nombre=oeschger;comida favorita=tripa
+
+}
+ +
<button onclick="alertCookie()">Mostrar cookies</button>
+ +

{{EmbedLiveSample('Example_1_Simple_usage', 200, 36)}}

+ + + +
document.cookie = "test1=Hola";
+document.cookie = "test2=Mundo";
+
+var cookieValor = document.cookie.replace(/(?:(?:^|.*;\s*)test2\s*\=\s*([^;]*).*$)|^.*$/, "$1");
+
+function alertCookieValue() {
+  alert(cookieValor);
+}
+
+ +
<button onclick="alertCookieValue()">Mostrar valor de cookie</button>
+ +

{{EmbedLiveSample('Example_2_Get_a_sample_cookie_named_test2', 200, 36)}}

+ +

Ejemplo #3: Hacer algo una sola vez

+ +

De manera a usar el siguiente código, favor remplace todas las veces la palabra hacerAlgoUnaSolaVez (el nombre de la cookie) con un nombre personalizado.

+ +
function hazUnaVez() {
+  if (document.cookie.replace(/(?:(?:^|.*;\s*)hacerAlgoUnaSolaVez\s*\=\s*([^;]*).*$)|^.*$/, "$1") !== "true") {
+    alert("Hacer algo aquí!");
+    document.cookie = "hacerAlgoUnaSolaVez=true; expires=Fri, 31 Dec 9999 23:59:59 GMT";
+  }
+}
+ +
<button onclick="dhacerUnaVez()">Solo hacer algo una vez</button>
+ +

{{EmbedLiveSample('Example_3_Do_something_only_once', 200, 36)}}

+ +

Seguridad

+ +

Es importante mencionar que la restricción path no protege contra la lectura no autorizada de cookies de una ruta distinta. Puede ser fácilmente resuelto mediante DOM (por ejemplo creando un iframe oculto con la ruta de la cookie y accediendo a la propiedad contentDocument.cookie del iframe). La única manera de proteger el acceso a cookies es ocupando un dominio o subdominio diferente, debido a la política de mismo origen.

+ +

Notas

+ + + +

Especificación

+ +

DOM Level 2: HTMLDocument.cookie

diff --git a/files/es/dom/index.html b/files/es/dom/index.html new file mode 100644 index 0000000000..b1a5452a8e --- /dev/null +++ b/files/es/dom/index.html @@ -0,0 +1,86 @@ +--- +title: DOM +slug: DOM +tags: + - DOM + - Todas_las_Categorías +translation_of: Web/API/Document_Object_Model +--- +
+ Acerca del Modelo de Objetos del Documento
+ Un par de cosas básicas sobre DOM y Mozilla.
+
+

El Modelo de Objetos del Documento (DOM) es un API para documentos HTML y XML. Proporciona una representación estructural del documento, permitiendo la modificación de su contenido o su presentación visual. Esencialmente, comunica las páginas web con los scripts o los lenguajes de programación.

+
+

DOM es un estándar del W3C

+ + + + + + + +
+

Documentación

+
+
+ Introducción a la manipulación DOM
+
+ Introducción a los métodos de manipulación DOM mediante Javascript
+
+
+
+ Especificación del DOM Nivel 1
+
+ El objetivo de la especificación DOM es definir una interfaz programable para HTML y XML.
+
+
+
+ Uso del núcleo del nivel 1 del DOM
+
+ Es un potente modelo de objetos para modificar el árbol de contenidos de los documentos.
+
+
+
+ Los niveles del DOM
+
+ Una descripción de los niveles del DOM y el soporte ofrecido por Mozilla a cada uno de ellos.
+
+
+
+ DHTML Demostraciones del uso de DOM/Style
+
+ Contiene una lista de ejemplos DOM basados en sus diversas características. La lista incluye demostraciones para Eventos DOM, DOM Core, DOM HTML y mucho mas.
+
+
+
+ The Document Object Model in Mozilla.org (en)
+
+ Una versión más antigua acerca de DOM se encuentra en mozilla.org.
+
+

enlaces a ninguna parte: ; DOM y JavaScript: <small>"¿Qué está haciendo que? ¿En un script embebido en mi página web, el cual usa DOM y Javascript?"</small>  ; Modificando dinámicamente las interfaces de usuario en XUL: <small>Fundamentos de manipulación con XUL UI y métodos DOM.</small>  ; Espacios en blanco en el DOM: <small>Una solución al problema de ignorar los espacios en blanco cuando se interactúa con el DOM.</small>  ; Tablas HTML con JavaScript e interfaces DOM: <small>Una descripción de algunos métodos de gran alcance, fundamentales para el nivel 1 en el uso de DOM y de cómo utilizarlo con Javascript .</small> fin de enlaces a ninguna parte Ver todos

+
+

Comunidad

+
    +
  • En la comunidad Mozilla... en inglés
  • +
+

{{ DiscussionList("dev-tech-dom", "mozilla.dev.tech.dom") }}

+

Ver todos

+

Herramientas

+ +

Ver todos

+

Temas relacionados

+
+
+ • AJAXCSSXMLJavaScript
+
+

 

+
+

fin de tabla

+

Categorías

+

Interwiki Language Links

+

{{ languages( { "de": "de/DOM", "en": "en/DOM", "fr": "fr/DOM", "hu": "hu/DOM", "ja": "ja/DOM", "ko": "ko/DOM", "nl": "nl/DOM", "pl": "pl/DOM", "pt": "pt/DOM", "ru": "ru/DOM", "zh-cn": "cn/DOM", "zh-tw": "zh_tw/DOM" } ) }} 

diff --git a/files/es/dom/manipulando_el_historial_del_navegador/ejemplo/index.html b/files/es/dom/manipulando_el_historial_del_navegador/ejemplo/index.html new file mode 100644 index 0000000000..1971f1348f --- /dev/null +++ b/files/es/dom/manipulando_el_historial_del_navegador/ejemplo/index.html @@ -0,0 +1,415 @@ +--- +title: Ejemplo de Navegación usando Ajax +slug: DOM/Manipulando_el_historial_del_navegador/Ejemplo +translation_of: Web/API/History_API/Example +--- +

This is an example of an AJAX web site composed only of three pages (first_page.php, second_page.php and third_page.php). To see how it works, please, create the following files (or git clone https://github.com/giabao/mdn-ajax-nav-example.git ):

+ +
Note: For fully integrating the {{HTMLElement("form")}} elements within this mechanism, please take a look at the paragraph Submitting forms and uploading files.
+ +

first_page.php:

+ +
+
<?php
+    $page_title = "First page";
+
+    $as_json = false;
+    if (isset($_GET["view_as"]) && $_GET["view_as"] == "json") {
+        $as_json = true;
+        ob_start();
+    } else {
+?>
+<!doctype html>
+<html>
+<head>
+<?php
+        include "include/header.php";
+        echo "<title>" . $page_title . "</title>";
+?>
+</head>
+
+<body>
+
+<?php include "include/before_content.php"; ?>
+
+<p>This paragraph is shown only when the navigation starts from <strong>first_page.php</strong>.</p>
+
+<div id="ajax-content">
+<?php } ?>
+
+    <p>This is the content of <strong>first_page.php</strong>.</p>
+
+<?php
+    if ($as_json) {
+        echo json_encode(array("page" => $page_title, "content" => ob_get_clean()));
+    } else {
+?>
+</div>
+
+<p>This paragraph is shown only when the navigation starts from <strong>first_page.php</strong>.</p>
+
+<?php
+        include "include/after_content.php";
+        echo "</body>\n</html>";
+    }
+?>
+
+
+ +

second_page.php:

+ +
+
<?php
+    $page_title = "Second page";
+
+    $as_json = false;
+    if (isset($_GET["view_as"]) && $_GET["view_as"] == "json") {
+        $as_json = true;
+        ob_start();
+    } else {
+?>
+<!doctype html>
+<html>
+<head>
+<?php
+        include "include/header.php";
+        echo "<title>" . $page_title . "</title>";
+?>
+</head>
+
+<body>
+
+<?php include "include/before_content.php"; ?>
+
+<p>This paragraph is shown only when the navigation starts from <strong>second_page.php</strong>.</p>
+
+<div id="ajax-content">
+<?php } ?>
+
+    <p>This is the content of <strong>second_page.php</strong>.</p>
+
+<?php
+    if ($as_json) {
+        echo json_encode(array("page" => $page_title, "content" => ob_get_clean()));
+    } else {
+?>
+</div>
+
+<p>This paragraph is shown only when the navigation starts from <strong>second_page.php</strong>.</p>
+
+<?php
+        include "include/after_content.php";
+        echo "</body>\n</html>";
+    }
+?>
+
+
+ +

third_page.php:

+ +
+
<?php
+    $page_title = "Third page";
+    $page_content = "<p>This is the content of <strong>third_page.php</strong>. This content is stored into a php variable.</p>";
+
+    if (isset($_GET["view_as"]) && $_GET["view_as"] == "json") {
+        echo json_encode(array("page" => $page_title, "content" => $page_content));
+    } else {
+?>
+<!doctype html>
+<html>
+<head>
+<?php
+        include "include/header.php";
+        echo "<title>" . $page_title . "</title>";
+?>
+</head>
+
+<body>
+
+<?php include "include/before_content.php"; ?>
+
+<p>This paragraph is shown only when the navigation starts from <strong>third_page.php</strong>.</p>
+
+<div id="ajax-content">
+<?php echo $page_content; ?>
+</div>
+
+<p>This paragraph is shown only when the navigation starts from <strong>third_page.php</strong>.</p>
+
+<?php
+        include "include/after_content.php";
+        echo "</body>\n</html>";
+    }
+?>
+
+
+ +

css/style.css:

+ +
#ajax-loader {
+    position: fixed;
+    display: table;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+}
+
+#ajax-loader > div {
+    display: table-cell;
+    width: 100%;
+    height: 100%;
+    vertical-align: middle;
+    text-align: center;
+    background-color: #000000;
+    opacity: 0.65;
+}
+
+ +

include/after_content.php:

+ +
<p>This is the footer. It is shared between all ajax pages.</p>
+
+ +

include/before_content.php:

+ +
<p>
+[ <a class="ajax-nav" href="first_page.php">First example</a>
+| <a class="ajax-nav" href="second_page.php">Second example</a>
+| <a class="ajax-nav" href="third_page.php">Third example</a>
+| <a class="ajax-nav" href="unexisting.php">Unexisting page</a> ]
+</p>
+
+
+ +

include/header.php:

+ +
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<script type="text/javascript" src="js/ajax_nav.js"></script>
+<link rel="stylesheet" href="css/style.css" />
+
+ +

js/ajax_nav.js:

+ +

(before implementing it in a working environment, please read the note about the const statement compatibility)

+ +
+
"use strict";
+
+const ajaxRequest = new (function () {
+
+    function closeReq () {
+        oLoadingBox.parentNode && document.body.removeChild(oLoadingBox);
+        bIsLoading = false;
+    }
+
+    function abortReq () {
+        if (!bIsLoading) { return; }
+        oReq.abort();
+        closeReq();
+    }
+
+    function ajaxError () {
+        alert("Unknown error.");
+    }
+
+    function ajaxLoad () {
+        var vMsg, nStatus = this.status;
+        switch (nStatus) {
+            case 200:
+                vMsg = JSON.parse(this.responseText);
+                document.title = oPageInfo.title = vMsg.page;
+                document.getElementById(sTargetId).innerHTML = vMsg.content;
+                if (bUpdateURL) {
+                    history.pushState(oPageInfo, oPageInfo.title, oPageInfo.url);
+                    bUpdateURL = false;
+                }
+                break;
+            default:
+                vMsg = nStatus + ": " + (oHTTPStatus[nStatus] || "Unknown");
+                switch (Math.floor(nStatus / 100)) {
+                    /*
+                    case 1:
+                        // Informational 1xx
+                        console.log("Information code " + vMsg);
+                        break;
+                    case 2:
+                        // Successful 2xx
+                        console.log("Successful code " + vMsg);
+                        break;
+                    case 3:
+                        // Redirection 3xx
+                        console.log("Redirection code " + vMsg);
+                        break;
+                    */
+                    case 4:
+                        /* Client Error 4xx */
+                        alert("Client Error #" + vMsg);
+                        break;
+                    case 5:
+                        /* Server Error 5xx */
+                        alert("Server Error #" + vMsg);
+                        break;
+                    default:
+                        /* Unknown status */
+                        ajaxError();
+                }
+        }
+        closeReq();
+    }
+
+    function filterURL (sURL, sViewMode) {
+        return sURL.replace(rSearch, "") + ("?" + sURL.replace(rHost, "&").replace(rView, sViewMode ? "&" + sViewKey + "=" + sViewMode : "").slice(1)).replace(rEndQstMark, "");
+    }
+
+    function getPage (sPage) {
+        if (bIsLoading) { return; }
+        oReq = new XMLHttpRequest();
+        bIsLoading = true;
+        oReq.onload = ajaxLoad;
+        oReq.onerror = ajaxError;
+        if (sPage) { oPageInfo.url = filterURL(sPage, null); }
+        oReq.open("get", filterURL(oPageInfo.url, "json"), true);
+        oReq.send();
+        oLoadingBox.parentNode || document.body.appendChild(oLoadingBox);
+    }
+
+    function requestPage (sURL) {
+        if (history.pushState) {
+            bUpdateURL = true;
+            getPage(sURL);
+        } else {
+            /* Ajax navigation is not supported */
+            location.assign(sURL);
+        }
+    }
+
+    function processLink () {
+        if (this.className === sAjaxClass) {
+            requestPage(this.href);
+            return false;
+        }
+        return true;
+    }
+
+    function init () {
+        oPageInfo.title = document.title;
+        for (var oLink, nIdx = 0, nLen = document.links.length; nIdx < nLen; document.links[nIdx++].onclick = processLink);
+    }
+
+    const
+
+        /* customizable constants */
+        sTargetId = "ajax-content", sViewKey = "view_as", sAjaxClass = "ajax-nav",
+
+        /* not customizable constants */
+        rSearch = /\?.*$/, rHost = /^[^\?]*\?*&*/, rView = new RegExp("&" + sViewKey + "\\=[^&]*|&*$", "i"), rEndQstMark = /\?$/,
+        oLoadingBox = document.createElement("div"), oCover = document.createElement("div"), oLoadingImg = new Image(),
+        oPageInfo = {
+            title: null,
+            url: location.href
+        }, oHTTPStatus = /* http://www.iana.org/assignments/http-status-codes/http-status-codes.xml */ {
+            100: "Continue",
+            101: "Switching Protocols",
+            102: "Processing",
+            200: "OK",
+            201: "Created",
+            202: "Accepted",
+            203: "Non-Authoritative Information",
+            204: "No Content",
+            205: "Reset Content",
+            206: "Partial Content",
+            207: "Multi-Status",
+            208: "Already Reported",
+            226: "IM Used",
+            300: "Multiple Choices",
+            301: "Moved Permanently",
+            302: "Found",
+            303: "See Other",
+            304: "Not Modified",
+            305: "Use Proxy",
+            306: "Reserved",
+            307: "Temporary Redirect",
+            308: "Permanent Redirect",
+            400: "Bad Request",
+            401: "Unauthorized",
+            402: "Payment Required",
+            403: "Forbidden",
+            404: "Not Found",
+            405: "Method Not Allowed",
+            406: "Not Acceptable",
+            407: "Proxy Authentication Required",
+            408: "Request Timeout",
+            409: "Conflict",
+            410: "Gone",
+            411: "Length Required",
+            412: "Precondition Failed",
+            413: "Request Entity Too Large",
+            414: "Request-URI Too Long",
+            415: "Unsupported Media Type",
+            416: "Requested Range Not Satisfiable",
+            417: "Expectation Failed",
+            422: "Unprocessable Entity",
+            423: "Locked",
+            424: "Failed Dependency",
+            425: "Unassigned",
+            426: "Upgrade Required",
+            427: "Unassigned",
+            428: "Precondition Required",
+            429: "Too Many Requests",
+            430: "Unassigned",
+            431: "Request Header Fields Too Large",
+            500: "Internal Server Error",
+            501: "Not Implemented",
+            502: "Bad Gateway",
+            503: "Service Unavailable",
+            504: "Gateway Timeout",
+            505: "HTTP Version Not Supported",
+            506: "Variant Also Negotiates (Experimental)",
+            507: "Insufficient Storage",
+            508: "Loop Detected",
+            509: "Unassigned",
+            510: "Not Extended",
+            511: "Network Authentication Required"
+        };
+
+    var
+
+        oReq, bIsLoading = false, bUpdateURL = false;
+
+    oLoadingBox.id = "ajax-loader";
+    oCover.onclick = abortReq;
+    oLoadingImg.src = "data:image/gif;base64,R0lGODlhEAAQAPIAAP///wAAAMLCwkJCQgAAAGJiYoKCgpKSkiH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQJCgAAACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkECQoAAAAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkECQoAAAAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkECQoAAAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQJCgAAACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQJCgAAACwAAAAAEAAQAAADMgi63P7wCRHZnFVdmgHu2nFwlWCI3WGc3TSWhUFGxTAUkGCbtgENBMJAEJsxgMLWzpEAACH5BAkKAAAALAAAAAAQABAAAAMyCLrc/jDKSatlQtScKdceCAjDII7HcQ4EMTCpyrCuUBjCYRgHVtqlAiB1YhiCnlsRkAAAOwAAAAAAAAAAAA==";
+    oCover.appendChild(oLoadingImg);
+    oLoadingBox.appendChild(oCover);
+
+    onpopstate = function (oEvent) {
+        bUpdateURL = false;
+        oPageInfo.title = oEvent.state.title;
+        oPageInfo.url = oEvent.state.url;
+        getPage();
+    };
+
+    window.addEventListener ? addEventListener("load", init, false) : window.attachEvent ? attachEvent("onload", init) : (onload = init);
+
+    // Public methods
+
+    this.open = requestPage;
+    this.stop = abortReq;
+    this.rebuildLinks = init;
+
+})();
+
+
+ +
Note: The current implementation of const (constant statement) is not part of ECMAScript 5. It is supported in Firefox & Chrome (V8) and partially supported in Opera 9+ and Safari. It is not supported in Internet Explorer 6-9, or in the preview of Internet Explorer 10. const is going to be defined by ECMAScript 6, but with different semantics. Similar to variables declared with the let statement, constants declared with const will be block-scoped. We used it only for didactic purpose. If you want a full browser compatibility of this library, please replace all the const statements with the var statements.
+ +

For more information, please see: Manipulating the browser history.

+ +

See also

+ + diff --git a/files/es/dom/manipulando_el_historial_del_navegador/index.html b/files/es/dom/manipulando_el_historial_del_navegador/index.html new file mode 100644 index 0000000000..eb2c0b3fdd --- /dev/null +++ b/files/es/dom/manipulando_el_historial_del_navegador/index.html @@ -0,0 +1,228 @@ +--- +title: Manipulando el historial del navegador +slug: DOM/Manipulando_el_historial_del_navegador +tags: + - HTML5 + - historial + - para_revisar +translation_of: Web/API/History_API +--- +

El objeto DOM {{ domxref("window") }} proporciona acceso al historial del navegador a través del objeto {{ domxref("window.history", "history") }} . Este da acceso a métodos y propiedades útiles que permiten avanzar y retroceder a través del historial del usuario, así como --a partir de HTML5-- manipular el contenido del historial.

+ +

Viajando a través del historial

+ +

Retroceder y avanzar a través del historial del usuario utilizando los métodos back(), forward() y go().

+ +

Moviéndose hacia adelante y hacia atrás

+ +

Para moverte hacia atrás, solo debes hacer:

+ +
window.history.back();
+
+ +

Esto actuará exactamente como si el usuario hiciera clic en el botón "atrás" en la barra de herramientas del navegador.

+ +

De manera similar, puedes moverte hacia adelante (como si el usuario hiciera clic en en el botón "adelante"), de esta forma:

+ +
window.history.forward();
+
+ +

Moverse a un punto específico del historial

+ +

Puedes usar el método go() para cargar una página desde el historial de la sesión, identificada por su poscición relativa a la página actual  (Siendo la página actual, por supuesto, relativa al índice 0).

+ +

Para moverse atrás una página (equivalente a llamar back()):

+ +
window.history.go(-1);
+
+ +

Para moverse una página hacia adelante, como si se llamara a forward():

+ +
window.history.go(1);
+
+ +

De manera similar, puedes avanzar 2 páginas pasando 2 y así sucesivamente.

+ +

Otro uso para go() es el de actualizar la página ya sea pasando 0 como parámetro o ninguno.

+ +
// Cada una de las siguientes
+// instrucciones actualiza la página
+window.history.go(0);
+window.history.go();
+ +

Puedes obtener el número de páginas en la pila del historial consultando el valor de la propiedad length:

+ +
var numeroDeEntradas = window.history.length;
+
+ +
Nota: Internet Explorer admite el paso de cadenas de URL como parámetro para go(); esto no es estándar y no está implementado en Gecko.
+ +

Añadiendo y modificando entradas del historial

+ +

{{ gecko_minversion_header("2") }}

+ +

HTML5 introduce los métodos history.pushState() y history.replaceState(), los cuales te permiten añadir y modificar entradas del historial, respectivamente. Estos métodos trabajan en conjunto con el evento {{ domxref("window.onpopstate") }}.

+ +

Hacer uso de history.pushState() cambia el referer que es utilizado en la cabecera HTTP por los objetos XMLHttpRequest que hayan sido creados luego de cambiar el estado. El referer utilizará la URL del documento cuyo objeto window sea this al momento de la creación del objeto XMLHttpRequest.

+ +

Ejemplo

+ +

Supongamos que http://mozilla.org/foo.html ejecuta el siguiente JavaScript:

+ +
var stateObj = { foo: "bar" };
+history.pushState(stateObj, "page 2", "bar.html");
+
+ +

Esto causará que la barra de URL muestre http://mozilla.org/bar.html, pero no provocará que el navegador carge bar.html ni tampoco que verifique si bar.html existe.

+ +

Supongamos ahora que el usuario navega hacia http://google.com, y despúes hace clic en Atrás.  En este punto, la barra de URL mostrará http://mozilla.org/bar.html, y la página tendrá un evento popstate cuyo state object contiene una copia de stateObj. La página en si se verá como foo.html, aunque la página podria modificar su contenido durante el evento popstate event.

+ +

Si hacemos clic en "atrás" nuevamente, la URL cambiará a http://mozilla.org/foo.html, y el documento generará otro evento popstate event, esta vez con un state object nulo. Aquí también, ir atrás no cambia el contenido del documento con respecto al paso anterior, aunque el documento permite actualizar su contenido manualmente después de recibir el evento popstate.

+ +

El método pushState()

+ +

pushState() toma tres parámetros: un objeto estado, un título (el cual es normalmente ignorado) y (opcionalmente) una URL.  Vamos a examinar cada uno de estos tres parametros en más detalle:

+ + + +

En un sentido, llamar pushState() es similar a asignar window.location = "#foo"en tanto que también se va a crear y activar otra entrada al historial asociada con el documento actual. Pero pushState() tiene las siguientes ventajas:

+ + + +

Hay que tener en cuenta que pushState() nunca dispara un evento hashchange, incluso si la nueva URL difiere de la antigua URL únicamente en su hash.

+ +

En un documento XUL, crea el elemento XUL específico.

+ +

En otros documentos, crea un elemento con un namespace de URI nulo (null).

+ +

El método replaceState()

+ +

history.replaceState() trabaja exactamente igual a history.pushState() excepto que replaceState() modifica la entrada al historial actual en lugar de crear una nueva.

+ +

replaceState() es particularmente útil si deseas actualizar el objeto estado o la URL del la actual entrada al historial en respuesta a alguna acción del usuario.

+ +

El evento popstate

+ +

Un evento popstate es dirigido a la ventana cada vez que la entrada al historial cambia. Si la entrada al historial es activada y fue creada por un llamado a pushState o afectada por una llamada a replaceState, la propiedad state del evento popstate contiene una copia del historial de entradas del objeto estado.

+ +

Ver {{ domxref("window.onpopstate") }} para un ejemplo de uso.

+ +

Leyendo el estado actual

+ +

Cuando la página carga, debería tener un objeto de estado no nulo. Esto podría ocurrir, por ejemplo, si la página establece un object de estado (usando pushState() o replaceState()) y entonces el usuario reinicia su navegador. Cuando la página carga de nuevo, la página recibirá el evento onload, pero no el evento popstate.  Sin embargo, si lees la propiedad history.state, obtendrás el objeto estado que habrías tenido si se hubiera lanzado el evento apopstate.

+ +

Puedes leer el estado del historial actual sin tener que esperar un evento popstate usando la propiedad  history.state de esta manera:

+ +
var currentState = history.state;
+ +

Ejemplos

+ +

Para un ejemplo completo de un sitio AJAX, ver: Ejemplo de navegación AJAX.

+ +

Especificaciones

+ + + + + + + + + + + + + + + + + + + +
EspecificaciónEstado Comentario
{{SpecName('HTML WHATWG', "browsers.html#history", "History")}}{{Spec2('HTML WHATWG')}}No hay cambios desde {{SpecName("HTML5 W3C")}}.
{{SpecName('HTML5 W3C', "browsers.html#history", "History")}}{{Spec2('HTML5 W3C')}}Definición inicial
+ +

Compatibilidad entre navegadores

+ +

{{ CompatibilityTable() }}

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CaracterísticaChromeFirefox (Gecko)Internet ExplorerOperaSafari
replaceState, pushState5{{ CompatGeckoDesktop("2.0") }}1011.505.0
history.state18{{ CompatGeckoDesktop("2.0") }}1011.506.0
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CaracterísticaAndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
replaceState, pushState{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}
history.state{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}
+ +

Ver también

+ + diff --git a/files/es/dom/touch_events/index.html b/files/es/dom/touch_events/index.html new file mode 100644 index 0000000000..02a4a4eacb --- /dev/null +++ b/files/es/dom/touch_events/index.html @@ -0,0 +1,292 @@ +--- +title: Eventos de toque +slug: DOM/Touch_events +tags: + - DOM + - Event + - Mobile + - NeedsMobileBrowserCompatTable + - eventos +translation_of: Web/API/Touch_events +--- +

Con el fin de proporcionar soporte de calidad para usuarios de interfaces táctiles, los eventos táctiles dan la posibilidad de interpretar la actividad de los dedos en pantallas táctiles o trackpads.

+ +

Definiciones

+ +
+
Superficie
+
La superficie sensible al tacto. Esta puede ser una pantalla o un trackpad.
+
+ +
+
Punto de toque
+
Un punto de contacto con la superficie. Esto podría ser un dedo (o un codo, oreja, nariz, o lo que sea, pero probablemente un dedo) o un stylus.
+
+ +

Interfaces

+ +
+
{{ domxref("TouchEvent") }}
+
Representa un evento que ocurre cuando el estado de los toques en la superficie cambian.
+
{{ domxref("Touch") }}
+
Represeta un único punto de contacto entre el usuario y la superficie táctil.
+
{{ domxref("TouchList") }}
+
Representa varios puntos de toque: esto se utiliza cuando el usuario tiene, por ejemplo, varios dedos en la superficie al mismo tiempo.
+
{{ domxref("DocumentTouch") }}
+
Contiene varios métodos para crear objetos de {{domxref("Touch")}} y {{domxref("TouchList")}}.
+
+ +

Ejemplo

+ +

Este ejemplo muestra múltiples puntos de toques al mismo tiempo, permitiendo al usuario dibujar en un {{ HTMLElement("canvas") }} con más de un dedo a la vez. Esto funciona solamente en un navegador que soporte eventos táctiles.

+ +
Nota: El texto de abajo usa el término "dedo" cuando describe el contacto con la superficie, pero esto podría ser, por supuesto, también un stylus u otro método de contacto.
+ +

Configurando los eventos de manipulación

+ +

Cuando la página carga, la función startup() mostrada a continuación es llamada por nuestro atributo onload del elemento {{ HTMLElement("body") }}.

+ +
function startup() {
+  var el = document.getElementsByTagName("canvas")[0];
+  el.addEventListener("touchstart", handleStart, false);
+  el.addEventListener("touchend", handleEnd, false);
+  el.addEventListener("touchcancel", handleCancel, false);
+  el.addEventListener("touchleave", handleLeave, false);
+  el.addEventListener("touchmove", handleMove, false);
+}
+
+ +

Esto simplemente configura todos los detectores de eventos para nuestro elemento {{ HTMLElement("canvas") }}, por lo que podremos manejar todos los eventos de toque cuando se produzcan.

+ +

Seguimiento de nuevos toques

+ +

Cuando un evento touchstart ocurre, indicando que un nuevo toque sobre la superficie se ha producido, la función handleStart() de a continuación es llamada.

+ +
function handleStart(evt) {
+  evt.preventDefault();
+  var el = document.getElementsByTagName("canvas")[0];
+  var ctx = el.getContext("2d");
+  var touches = evt.changedTouches;
+
+  for (var i=0; i<touches.length; i++) {
+    ongoingTouches.push(touches[i]);
+    var color = colorForTouch(touches[i]);
+    ctx.fillStyle = color;
+    ctx.fillRect(touches[i].pageX-2, touches[i].pageY-2, 4, 4);
+  }
+}
+
+ +

Esto llama a {{ domxref("event.preventDefault()") }} para mantener al navegador procesando el evento de toque (esto también previene que un evento del ratón o mouse sea entregado). Entonces obtenemos el contexto y lanzamos la lista de puntos de contacto cambiados de la propiedad {{ domxref("TouchEvent.changedTouches") }} del evento.

+ +

Después de ello, iteramos sobre todos los objetos {{ domxref("Touch") }} de la lista, insertándolo en una matriz de puntos de toque activos y dibujando el punto de inicio como un pequeño rectángulo; estamos usando una línea de 4 pixeles de ancho, por tanto estamos dibujando un cuadrado de 4 por 4 píxeles como punto de consistencia.

+ +

Dibujando mientras los eventos de toque se mueven

+ +

Cada vez que uno o más dedos se mueven, un evento touchmove es entregado, resultando en una llamada a nuestra función handleMove(). Su responsabilidad en este ejemplo es actualizar la información de toque cacheada y dibujar una línea desde la posición previa a la posición actual en cada toque.

+ +
function handleMove(evt) {
+  evt.preventDefault();
+  var el = document.getElementsByTagName("canvas")[0];
+  var ctx = el.getContext("2d");
+  var touches = evt.changedTouches;
+
+  ctx.lineWidth = 4;
+
+  for (var i=0; i<touches.length; i++) {
+    var color = colorForTouch(touches[i]);
+    var idx = ongoingTouchIndexById(touches[i].identifier);
+
+    ctx.fillStyle = color;
+    ctx.beginPath();
+    ctx.moveTo(ongoingTouches[idx].pageX, ongoingTouches[idx].pageY);
+    ctx.lineTo(touches[i].pageX, touches[i].pageY);
+    ctx.closePath();
+    ctx.stroke();
+    ongoingTouches.splice(idx, 1, touches[i]);  // swap in the new touch record
+  }
+}
+
+ +

Esto se repite también en los toques cambiados, pero mira en nuestra matriz de información de toques cacheados la información previa de cada toque con el fin de determinar los puntos de inicio para cada nuevo segmento de línea de toques que será dibujada. Esto se hace mirando cada propiedad de los toques de {{ domxref("Touch.identifier") }} . Esta propiedad es un único entero para cada toque, y sigue siendo consistente para cada evento durante la duración del contacto de cada dedo con la superficie.

+ +

Esto nos permite conseguir las coordenadas de la posición previa de cada toque y usar el método apropiado de contexto para dibujar un segmento de línea uniendo dos posiciones a la vez.

+ +

Después de dibujar la línea, llamamos a  Array.splice() para reemplazar la información previa sobre el punto de toque con la información actual de la matriz ongoingTouches.

+ +

Manejando el final de un toque

+ +

Cuando el usuario levanta un dedo de la superficie, un evento touchend es enviado.  De igual manera, si el dedo se desliza fuera de nuestro lienzo, obtenemos un evento touchleave. Manejamos ambos casos de la misma manera: llamando a la función handleEnd() de abajo. Su trabajo es dibujar el último segmento de línea para cada toque que ha finalizado y remueve el punto de toque de la lista de toques en marcha.

+ +
function handleEnd(evt) {
+  evt.preventDefault();
+  var el = document.getElementsByTagName("canvas")[0];
+  var ctx = el.getContext("2d");
+  var touches = evt.changedTouches;
+
+  ctx.lineWidth = 4;
+
+  for (var i=0; i<touches.length; i++) {
+    var color = colorForTouch(touches[i]);
+    var idx = ongoingTouchIndexById(touches[i].identifier);
+
+    ctx.fillStyle = color;
+    ctx.beginPath();
+    ctx.moveTo(ongoingTouches[i].pageX, ongoingTouches[i].pageY);
+    ctx.lineTo(touches[i].pageX, touches[i].pageY);
+    ongoingTouches.splice(i, 1);  // remove it; we're done
+  }
+}
+
+ +

Esto es muy similar a la función previa; la única diferencia real es que cuando llamamos a  Array.splice(), simplemente remueve la antigua entrada de la lista de toques en marcha, sin añadir la información actualizada. El resultado es que detenemos el seguimiento del punto de toque.

+ +

Manejando los toques cancelados

+ +

Si el dedo del usuario se equivoca en la Interfaz del navegador, o el toque necesita ser cancelado, el evento touchcancel es enviado, y llamamos a la función handleCancel() abajo.

+ +
function handleCancel(evt) {
+  evt.preventDefault();
+  var touches = evt.changedTouches;
+
+  for (var i=0; i<touches.length; i++) {
+    ongoingTouches.splice(i, 1);  // remove it; we're done
+  }
+}
+
+ +

Dado que la idea es cancelar el toque inmediatamente, simplemente lo removemos de la lista de toques en marcha sin dibujar un segmento de línea final.

+ +

Funciones de conveniencia

+ +

Este ejemplo usa dos funciones de convenience que deben mirarse brevemente para ayudar a que el resto del código sea más claro.

+ +

Seleccionando un color para cada toque

+ +

Con el fin de hacer que cada dibujo de toque se vea diferente, la función  colorForTouch() es usada para elegir un color basado en el identificador único de toque. Este identificador estará siempre entre 0 y un valor menos que el número de toques activos. Puesto que es muy improbable que alguna persona con más de 16 dedos use este demo, convertimos estos directamente en colores de escalas grises.

+ +
function colorForTouch(touch) {
+  var id = touch.identifier;
+  id = id.toString(16); // make it a hex digit
+  return "#" + id + id + id;
+}
+
+ +

El resultado de esta función es un string o cadena que puede ser usada cuando se llame a funciones {{ HTMLElement("canvas") }} para configurar los colores de dibujos. Por ejemplo, para un valor {{ domxref("Touch.identifier") }}  de 10, el resultado string o cadena es "#aaa".

+ +

Encontrando un toque continuo

+ +

La función ongoingTouchIndexById() abajo explora mediante la matriz ongoingTouches para encontrar el toque que coincida con el identificador dado, luego devuelve los índices de toques a la matriz.

+ +
function ongoingTouchIndexById(idToFind) {
+  for (var i=0; i<ongoingTouches.length; i++) {
+    var id = ongoingTouches[i].identifier;
+
+    if (id == idToFind) {
+      return i;
+    }
+  }
+  return -1;    // not found
+}
+
+ +

Ver ejemplo en vivo

+ +

Consejos adicionales

+ +

Esta sección provee consejos adicionales sobre como manejar los eventos de toques en tu aplicación web.

+ +

Manejando los clics

+ +

Ya que la llamada preventDefault() en un touchstart o el primer evento touchmove de una serie impide que los eventos correspondientes eventos del mouse o ratón se disparen, es común llamar a preventDefault() en touchmove en lugar de touchstart. De esta manera, los eventos del ratón pueden todavía ser disparados y cosas como enlaces siguen funcionando. Alternativamente, algunos frameworks tienen que referirse a eventos de toque como eventos de ratón para este mismo propósito. (Este ejemplo es muy simplificado y podria resultar en un extraño comportamiento. Solo se diseñó como guía).

+ +
function onTouch(evt) {
+  evt.preventDefault();
+  if (evt.touches.length > 1 || (evt.type == "touchend" && evt.touches.length > 0))
+    return;
+
+  var newEvt = document.createEvent("MouseEvents");
+  var type = null;
+  var touch = null;
+  switch (event.type) {
+    case "touchstart": type = "mousedown"; touch = event.changedTouches[[0];
+    case "touchmove":  type = "mousemove"; touch = event.changedTouches[[0];
+    case "touchend":   type = "mouseup"; touch = event.changedTouches[0];
+  }
+  newEvt.initMouseEvent(type, true, true, event.originalTarget.ownerDocument.defaultView, 0,
+    touch.screenX, touch.screenY, touch.clientX, touch.clientY,
+    evt.ctrlKey, evt.altKey, evt.shirtKey, evt.metaKey, 0, null);
+  event.originalTarget.dispatchEvent(newEvt);
+}
+
+ +

Llamando a preventDefault() solo en un segundo toque

+ +

Una cosa para prevenir cosas como pinchZoom en una página es llamar a preventDefault() en el segundo toque de una serie. Este comportamiento no está bien definido en los eventos de toque, y resulta en diferentes comportamientos en diferentes navegadores (osea iOS evitará el zoom o acercamiento pero permitirá vista panorámica con ambos dedos. Android permitirá zoom o acercamiento pero no una panorámica. Opera and Firefox actualmente evita panorámica y zoom o acercamiento). Actualmente, no se recomienda depender de ningún comportamiento en particular en este caso, si no mas bien depender de una meta vista para evitar el zoom.

+ +
+
+ +

Compatibilidad de navegadores

+ +

{{ CompatibilityTable() }}

+ +
+ + + + + + + + + + + + + + + + + + + +
CaracterísticaChromeFirefox (Gecko)Internet ExplorerOperaSafari
Soporte básico{{ CompatChrome("22.0") }}{{ CompatGeckoDesktop("18.0") }}{{ CompatNo() }}{{ CompatNo() }}{{ CompatNo() }}
+
+ +
+ + + + + + + + + + + + + + + + + + + + + +
CaracterísticaAndroidChrome for AndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Soporte básico{{ CompatVersionUnknown() }}{{ CompatUnknown() }}{{ CompatGeckoMobile("6.0") }}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatVersionUnknown() }}
+
+ +

Notas de Gecko

+ +

La preferencia dom.w3c_touch_events.enabled puede ser utilizada para activar o desactivar el soporte de eventos de toque estándares; por defecto, están activados.

+ +
+

{{ gecko_callout_heading("12.0") }}

+ +

Antes de Gecko 12.0 {{ geckoRelease("12.0") }}, Gecko no soportaba multi-toques; solo un toque cada vez era reportado.

+
+ +
Note: Antes de Gecko 6.0 {{ geckoRelease("6.0") }}, Gecko ofrecía una API de eventos de toque propietaria. Está API está obsoleta actualmente; deberías cambiar a esta."
-- cgit v1.2.3-54-g00ecf