From a55b575e8089ee6cab7c5c262a7e6db55d0e34d6 Mon Sep 17 00:00:00 2001 From: Florian Merz Date: Thu, 11 Feb 2021 14:46:50 +0100 Subject: unslug es: move --- .../web/api/animation/animaci\303\263n/index.html" | 106 -- files/es/web/api/animation/animation/index.html | 106 ++ files/es/web/api/animation/currenttime/index.html | 114 ++ files/es/web/api/animation/finished/index.html | 120 ++ files/es/web/api/animation/terminado/index.html | 120 -- files/es/web/api/animation/tiempoactual/index.html | 114 -- .../web/api/api_de_almacenamiento_web/index.html | 146 --- .../usando_la_api_de_almacenamiento_web/index.html | 272 ---- files/es/web/api/api_del_portapapeles/index.html | 76 -- files/es/web/api/battery_status_api/index.html | 39 + .../api/canvas_api/a_basic_ray-caster/index.html | 59 + files/es/web/api/canvas_api/index.html | 170 +++ .../manipulating_video_using_canvas/index.html | 126 ++ .../tutorial/advanced_animations/index.html | 380 ++++++ .../tutorial/applying_styles_and_colors/index.html | 726 +++++++++++ .../tutorial/basic_animations/index.html | 333 +++++ .../api/canvas_api/tutorial/basic_usage/index.html | 146 +++ .../tutorial/compositing/ejemplo/index.html | 295 ----- .../tutorial/compositing/example/index.html | 295 +++++ .../canvas_api/tutorial/drawing_shapes/index.html | 513 ++++++++ .../canvas_api/tutorial/drawing_text/index.html | 67 + .../hit_regions_and_accessibility/index.html | 99 ++ files/es/web/api/canvas_api/tutorial/index.html | 61 + .../tutorial/optimizing_canvas/index.html | 19 + .../pixel_manipulation_with_canvas/index.html | 301 +++++ files/es/web/api/clipboard_api/index.html | 76 ++ files/es/web/api/console/tabla/index.html | 212 ---- files/es/web/api/console/table/index.html | 212 ++++ files/es/web/api/crypto/getrandomvalues/index.html | 75 ++ files/es/web/api/document/abrir/index.html | 109 -- files/es/web/api/document/async/index.html | 33 - files/es/web/api/document/cookie/index.html | 119 ++ files/es/web/api/document/crearatributo/index.html | 91 -- .../es/web/api/document/createattribute/index.html | 91 ++ files/es/web/api/document/createevent/index.html | 35 + files/es/web/api/document/getselection/index.html | 13 - files/es/web/api/document/open/index.html | 109 ++ .../document/pointerlockchange_event/index.html | 80 ++ .../web/api/document/pointerlockelement/index.html | 105 -- files/es/web/api/document/stylesheets/index.html | 22 - .../api/document_object_model/events/index.html | 72 ++ .../api/document_object_model/examples/index.html | 367 ++++++ .../how_to_create_a_dom_tree/index.html | 130 ++ files/es/web/api/document_object_model/index.html | 91 ++ .../document_object_model/introduction/index.html | 248 ++++ .../index.html | 50 + .../index.html | 337 +++++ .../using_the_w3c_dom_level_1_core/index.html | 93 ++ .../document_object_model/whitespace/index.html | 476 +++++++ .../documentorshadowroot/getselection/index.html | 13 + .../pointerlockelement/index.html | 105 ++ .../documentorshadowroot/stylesheets/index.html | 22 + files/es/web/api/domstring/binary/index.html | 31 + .../web/api/domstring/cadenas_binarias/index.html | 31 - files/es/web/api/element/accesskey/index.html | 23 - files/es/web/api/element/blur_event/index.html | 156 +++ files/es/web/api/element/name/index.html | 80 -- .../web/api/element/ongotpointercapture/index.html | 134 -- .../api/element/onlostpointercapture/index.html | 133 -- files/es/web/api/element/onwheel/index.html | 31 - .../web/api/elementcssinlinestyle/style/index.html | 52 + files/es/web/api/elementoshtmlparavideo/index.html | 202 --- files/es/web/api/event/createevent/index.html | 35 - .../es/web/api/fetch_api/basic_concepts/index.html | 73 ++ .../web/api/fetch_api/conceptos_basicos/index.html | 73 -- files/es/web/api/fetch_api/using_fetch/index.html | 377 ++++++ .../web/api/fetch_api/utilizando_fetch/index.html | 377 ------ files/es/web/api/formdata/index.html | 84 ++ .../api/formdata/using_formdata_objects/index.html | 137 ++ files/es/web/api/geolocation_api/index.html | 232 ++++ .../ongotpointercapture/index.html | 134 ++ .../onlostpointercapture/index.html | 133 ++ .../api/globaleventhandlers/onunload/index.html | 45 - .../web/api/globaleventhandlers/onwheel/index.html | 31 + files/es/web/api/history_api/example/index.html | 415 +++++++ files/es/web/api/history_api/index.html | 228 ++++ .../file_drag_and_drop/index.html | 117 ++ files/es/web/api/html_drag_and_drop_api/index.html | 57 + .../recommended_drag_types/index.html | 144 +++ files/es/web/api/htmlelement/accesskey/index.html | 23 + .../api/htmlelement/animationend_event/index.html | 81 ++ files/es/web/api/htmlelement/dataset/index.html | 132 -- files/es/web/api/htmlelement/focus/index.html | 164 --- files/es/web/api/htmlelement/style/index.html | 52 - .../htmlelement/transitioncancel_event/index.html | 163 +++ .../api/htmlelement/transitionend_event/index.html | 183 +++ .../api/htmlmediaelement/abort_event/index.html | 68 + .../api/htmlorforeignelement/dataset/index.html | 132 ++ .../web/api/htmlorforeignelement/focus/index.html | 164 +++ files/es/web/api/htmlvideoelement/index.html | 202 +++ .../basic_concepts_behind_indexeddb/index.html | 216 ++++ .../index.html | 216 ---- .../api/indexeddb_api/usando_indexeddb/index.html | 1308 -------------------- .../api/indexeddb_api/using_indexeddb/index.html | 1308 ++++++++++++++++++++ files/es/web/api/media_streams_api/index.html | 445 +++++++ files/es/web/api/navigator/geolocation/index.html | 106 ++ .../navigatorgeolocation/geolocation/index.html | 106 -- files/es/web/api/navigatorgeolocation/index.html | 107 -- .../eventos_online_y_offline/index.html | 91 -- .../online_and_offline_events/index.html | 91 ++ files/es/web/api/node/elementopadre/index.html | 46 - files/es/web/api/node/insertarantes/index.html | 172 --- files/es/web/api/node/insertbefore/index.html | 172 +++ files/es/web/api/node/nodoprincipal/index.html | 34 - files/es/web/api/node/parentelement/index.html | 46 + .../usando_la_api_de_notificaciones/index.html | 294 ----- .../using_the_notifications_api/index.html | 294 +++++ files/es/web/api/pointer_lock_api/index.html | 284 +++++ .../web/api/push_api/using_the_push_api/index.html | 433 ------- files/es/web/api/randomsource/index.html | 111 -- .../obtenervaloresaleatorios/index.html | 75 -- files/es/web/api/server-sent_events/index.html | 99 ++ .../using_server-sent_events/index.html | 231 ++++ files/es/web/api/storage/localstorage/index.html | 136 -- files/es/web/api/subtlecrypto/encrypt/index.html | 142 --- files/es/web/api/touch_events/index.html | 292 +++++ files/es/web/api/vibration_api/index.html | 155 +++ files/es/web/api/web_audio_api/index.html | 510 ++++++++ .../checking_authenticity_with_password/index.html | 30 - .../using_the_web_speech_api/index.html | 301 +++++ .../uso_de_la_web_speech_api/index.html | 301 ----- files/es/web/api/web_storage_api/index.html | 146 +++ .../using_the_web_storage_api/index.html | 272 ++++ .../web_workers_api/using_web_workers/index.html | 633 ++++++++++ .../creating_3d_objects_using_webgl/index.html | 133 ++ .../objetos_3d_utilizando_webgl/index.html | 133 -- .../tutorial/using_textures_in_webgl/index.html | 209 ++++ .../wtilizando_texturas_en_webgl/index.html | 209 ---- .../web/api/webrtc_api/session_lifetime/index.html | 21 + .../api/webrtc_api/taking_still_photos/index.html | 159 +++ .../escribiendo_servidor_websocket/index.html | 244 ---- .../index.html | 246 ---- .../writing_websocket_server/index.html | 244 ++++ .../writing_websocket_servers/index.html | 246 ++++ .../web/api/window/beforeunload_event/index.html | 215 ++++ .../api/window/domcontentloaded_event/index.html | 148 +++ files/es/web/api/window/load_event/index.html | 125 ++ files/es/web/api/window/url/index.html | 102 -- files/es/web/api/windowbase64/atob/index.html | 111 -- .../base64_codificando_y_decodificando/index.html | 345 ------ files/es/web/api/windowbase64/index.html | 109 -- .../api/windoweventhandlers/onunload/index.html | 45 + .../api/windoworworkerglobalscope/atob/index.html | 111 ++ .../clearinterval/index.html | 43 + .../cleartimeout/index.html | 63 + .../setinterval/index.html | 692 +++++++++++ .../settimeout/index.html | 340 +++++ .../web/api/windowtimers/clearinterval/index.html | 43 - .../web/api/windowtimers/cleartimeout/index.html | 63 - files/es/web/api/windowtimers/index.html | 119 -- .../es/web/api/windowtimers/setinterval/index.html | 692 ----------- .../es/web/api/windowtimers/settimeout/index.html | 340 ----- files/es/web/api/xmldocument/async/index.html | 33 + .../es/web/api/xmlhttprequest/formdata/index.html | 84 -- .../api/xmlhttprequest/loadend_event/index.html | 91 ++ 155 files changed, 18411 insertions(+), 9668 deletions(-) delete mode 100644 "files/es/web/api/animation/animaci\303\263n/index.html" create mode 100644 files/es/web/api/animation/animation/index.html create mode 100644 files/es/web/api/animation/currenttime/index.html create mode 100644 files/es/web/api/animation/finished/index.html delete mode 100644 files/es/web/api/animation/terminado/index.html delete mode 100644 files/es/web/api/animation/tiempoactual/index.html delete mode 100644 files/es/web/api/api_de_almacenamiento_web/index.html delete mode 100644 files/es/web/api/api_de_almacenamiento_web/usando_la_api_de_almacenamiento_web/index.html delete mode 100644 files/es/web/api/api_del_portapapeles/index.html create mode 100644 files/es/web/api/battery_status_api/index.html create mode 100644 files/es/web/api/canvas_api/a_basic_ray-caster/index.html create mode 100644 files/es/web/api/canvas_api/index.html create mode 100644 files/es/web/api/canvas_api/manipulating_video_using_canvas/index.html create mode 100644 files/es/web/api/canvas_api/tutorial/advanced_animations/index.html create mode 100644 files/es/web/api/canvas_api/tutorial/applying_styles_and_colors/index.html create mode 100644 files/es/web/api/canvas_api/tutorial/basic_animations/index.html create mode 100644 files/es/web/api/canvas_api/tutorial/basic_usage/index.html delete mode 100644 files/es/web/api/canvas_api/tutorial/compositing/ejemplo/index.html create mode 100644 files/es/web/api/canvas_api/tutorial/compositing/example/index.html create mode 100644 files/es/web/api/canvas_api/tutorial/drawing_shapes/index.html create mode 100644 files/es/web/api/canvas_api/tutorial/drawing_text/index.html create mode 100644 files/es/web/api/canvas_api/tutorial/hit_regions_and_accessibility/index.html create mode 100644 files/es/web/api/canvas_api/tutorial/index.html create mode 100644 files/es/web/api/canvas_api/tutorial/optimizing_canvas/index.html create mode 100644 files/es/web/api/canvas_api/tutorial/pixel_manipulation_with_canvas/index.html create mode 100644 files/es/web/api/clipboard_api/index.html delete mode 100644 files/es/web/api/console/tabla/index.html create mode 100644 files/es/web/api/console/table/index.html create mode 100644 files/es/web/api/crypto/getrandomvalues/index.html delete mode 100644 files/es/web/api/document/abrir/index.html delete mode 100644 files/es/web/api/document/async/index.html create mode 100644 files/es/web/api/document/cookie/index.html delete mode 100644 files/es/web/api/document/crearatributo/index.html create mode 100644 files/es/web/api/document/createattribute/index.html create mode 100644 files/es/web/api/document/createevent/index.html delete mode 100644 files/es/web/api/document/getselection/index.html create mode 100644 files/es/web/api/document/open/index.html create mode 100644 files/es/web/api/document/pointerlockchange_event/index.html delete mode 100644 files/es/web/api/document/pointerlockelement/index.html delete mode 100644 files/es/web/api/document/stylesheets/index.html create mode 100644 files/es/web/api/document_object_model/events/index.html create mode 100644 files/es/web/api/document_object_model/examples/index.html create mode 100644 files/es/web/api/document_object_model/how_to_create_a_dom_tree/index.html create mode 100644 files/es/web/api/document_object_model/index.html create mode 100644 files/es/web/api/document_object_model/introduction/index.html create mode 100644 files/es/web/api/document_object_model/locating_dom_elements_using_selectors/index.html create mode 100644 files/es/web/api/document_object_model/traversing_an_html_table_with_javascript_and_dom_interfaces/index.html create mode 100644 files/es/web/api/document_object_model/using_the_w3c_dom_level_1_core/index.html create mode 100644 files/es/web/api/document_object_model/whitespace/index.html create mode 100644 files/es/web/api/documentorshadowroot/getselection/index.html create mode 100644 files/es/web/api/documentorshadowroot/pointerlockelement/index.html create mode 100644 files/es/web/api/documentorshadowroot/stylesheets/index.html create mode 100644 files/es/web/api/domstring/binary/index.html delete mode 100644 files/es/web/api/domstring/cadenas_binarias/index.html delete mode 100644 files/es/web/api/element/accesskey/index.html create mode 100644 files/es/web/api/element/blur_event/index.html delete mode 100644 files/es/web/api/element/name/index.html delete mode 100644 files/es/web/api/element/ongotpointercapture/index.html delete mode 100644 files/es/web/api/element/onlostpointercapture/index.html delete mode 100644 files/es/web/api/element/onwheel/index.html create mode 100644 files/es/web/api/elementcssinlinestyle/style/index.html delete mode 100644 files/es/web/api/elementoshtmlparavideo/index.html delete mode 100644 files/es/web/api/event/createevent/index.html create mode 100644 files/es/web/api/fetch_api/basic_concepts/index.html delete mode 100644 files/es/web/api/fetch_api/conceptos_basicos/index.html create mode 100644 files/es/web/api/fetch_api/using_fetch/index.html delete mode 100644 files/es/web/api/fetch_api/utilizando_fetch/index.html create mode 100644 files/es/web/api/formdata/index.html create mode 100644 files/es/web/api/formdata/using_formdata_objects/index.html create mode 100644 files/es/web/api/geolocation_api/index.html create mode 100644 files/es/web/api/globaleventhandlers/ongotpointercapture/index.html create mode 100644 files/es/web/api/globaleventhandlers/onlostpointercapture/index.html delete mode 100644 files/es/web/api/globaleventhandlers/onunload/index.html create mode 100644 files/es/web/api/globaleventhandlers/onwheel/index.html create mode 100644 files/es/web/api/history_api/example/index.html create mode 100644 files/es/web/api/history_api/index.html create mode 100644 files/es/web/api/html_drag_and_drop_api/file_drag_and_drop/index.html create mode 100644 files/es/web/api/html_drag_and_drop_api/index.html create mode 100644 files/es/web/api/html_drag_and_drop_api/recommended_drag_types/index.html create mode 100644 files/es/web/api/htmlelement/accesskey/index.html create mode 100644 files/es/web/api/htmlelement/animationend_event/index.html delete mode 100644 files/es/web/api/htmlelement/dataset/index.html delete mode 100644 files/es/web/api/htmlelement/focus/index.html delete mode 100644 files/es/web/api/htmlelement/style/index.html create mode 100644 files/es/web/api/htmlelement/transitioncancel_event/index.html create mode 100644 files/es/web/api/htmlelement/transitionend_event/index.html create mode 100644 files/es/web/api/htmlmediaelement/abort_event/index.html create mode 100644 files/es/web/api/htmlorforeignelement/dataset/index.html create mode 100644 files/es/web/api/htmlorforeignelement/focus/index.html create mode 100644 files/es/web/api/htmlvideoelement/index.html create mode 100644 files/es/web/api/indexeddb_api/basic_concepts_behind_indexeddb/index.html delete mode 100644 files/es/web/api/indexeddb_api/conceptos_basicos_detras_de_indexeddb/index.html delete mode 100644 files/es/web/api/indexeddb_api/usando_indexeddb/index.html create mode 100644 files/es/web/api/indexeddb_api/using_indexeddb/index.html create mode 100644 files/es/web/api/media_streams_api/index.html create mode 100644 files/es/web/api/navigator/geolocation/index.html delete mode 100644 files/es/web/api/navigatorgeolocation/geolocation/index.html delete mode 100644 files/es/web/api/navigatorgeolocation/index.html delete mode 100644 files/es/web/api/navigatoronline/eventos_online_y_offline/index.html create mode 100644 files/es/web/api/navigatoronline/online_and_offline_events/index.html delete mode 100644 files/es/web/api/node/elementopadre/index.html delete mode 100644 files/es/web/api/node/insertarantes/index.html create mode 100644 files/es/web/api/node/insertbefore/index.html delete mode 100644 files/es/web/api/node/nodoprincipal/index.html create mode 100644 files/es/web/api/node/parentelement/index.html delete mode 100644 files/es/web/api/notifications_api/usando_la_api_de_notificaciones/index.html create mode 100644 files/es/web/api/notifications_api/using_the_notifications_api/index.html create mode 100644 files/es/web/api/pointer_lock_api/index.html delete mode 100644 files/es/web/api/push_api/using_the_push_api/index.html delete mode 100644 files/es/web/api/randomsource/index.html delete mode 100644 files/es/web/api/randomsource/obtenervaloresaleatorios/index.html create mode 100644 files/es/web/api/server-sent_events/index.html create mode 100644 files/es/web/api/server-sent_events/using_server-sent_events/index.html delete mode 100644 files/es/web/api/storage/localstorage/index.html delete mode 100644 files/es/web/api/subtlecrypto/encrypt/index.html create mode 100644 files/es/web/api/touch_events/index.html create mode 100644 files/es/web/api/vibration_api/index.html create mode 100644 files/es/web/api/web_audio_api/index.html delete mode 100644 files/es/web/api/web_crypto_api/checking_authenticity_with_password/index.html create mode 100644 files/es/web/api/web_speech_api/using_the_web_speech_api/index.html delete mode 100644 files/es/web/api/web_speech_api/uso_de_la_web_speech_api/index.html create mode 100644 files/es/web/api/web_storage_api/index.html create mode 100644 files/es/web/api/web_storage_api/using_the_web_storage_api/index.html create mode 100644 files/es/web/api/web_workers_api/using_web_workers/index.html create mode 100644 files/es/web/api/webgl_api/tutorial/creating_3d_objects_using_webgl/index.html delete mode 100644 files/es/web/api/webgl_api/tutorial/objetos_3d_utilizando_webgl/index.html create mode 100644 files/es/web/api/webgl_api/tutorial/using_textures_in_webgl/index.html delete mode 100644 files/es/web/api/webgl_api/tutorial/wtilizando_texturas_en_webgl/index.html create mode 100644 files/es/web/api/webrtc_api/session_lifetime/index.html create mode 100644 files/es/web/api/webrtc_api/taking_still_photos/index.html delete mode 100644 files/es/web/api/websockets_api/escribiendo_servidor_websocket/index.html delete mode 100644 files/es/web/api/websockets_api/escribiendo_servidores_con_websocket/index.html create mode 100644 files/es/web/api/websockets_api/writing_websocket_server/index.html create mode 100644 files/es/web/api/websockets_api/writing_websocket_servers/index.html create mode 100644 files/es/web/api/window/beforeunload_event/index.html create mode 100644 files/es/web/api/window/domcontentloaded_event/index.html create mode 100644 files/es/web/api/window/load_event/index.html delete mode 100644 files/es/web/api/window/url/index.html delete mode 100644 files/es/web/api/windowbase64/atob/index.html delete mode 100644 files/es/web/api/windowbase64/base64_codificando_y_decodificando/index.html delete mode 100644 files/es/web/api/windowbase64/index.html create mode 100644 files/es/web/api/windoweventhandlers/onunload/index.html create mode 100644 files/es/web/api/windoworworkerglobalscope/atob/index.html create mode 100644 files/es/web/api/windoworworkerglobalscope/clearinterval/index.html create mode 100644 files/es/web/api/windoworworkerglobalscope/cleartimeout/index.html create mode 100644 files/es/web/api/windoworworkerglobalscope/setinterval/index.html create mode 100644 files/es/web/api/windoworworkerglobalscope/settimeout/index.html delete mode 100644 files/es/web/api/windowtimers/clearinterval/index.html delete mode 100644 files/es/web/api/windowtimers/cleartimeout/index.html delete mode 100644 files/es/web/api/windowtimers/index.html delete mode 100644 files/es/web/api/windowtimers/setinterval/index.html delete mode 100644 files/es/web/api/windowtimers/settimeout/index.html create mode 100644 files/es/web/api/xmldocument/async/index.html delete mode 100644 files/es/web/api/xmlhttprequest/formdata/index.html create mode 100644 files/es/web/api/xmlhttprequest/loadend_event/index.html (limited to 'files/es/web/api') diff --git "a/files/es/web/api/animation/animaci\303\263n/index.html" "b/files/es/web/api/animation/animaci\303\263n/index.html" deleted file mode 100644 index 770036b76d..0000000000 --- "a/files/es/web/api/animation/animaci\303\263n/index.html" +++ /dev/null @@ -1,106 +0,0 @@ ---- -title: Animation() -slug: Web/API/Animation/Animación -tags: - - Animacion -translation_of: Web/API/Animation/Animation ---- -

{{ SeeCompatTable() }}{{ APIRef("Web Animations API") }}

- -

El constructor Animation() de Web Animations API devuelve una instancia del objeto Animation.

- -

Sintaxis

- -
var animation = new Animation([effect][, timeline]);
- -

Parámetros

- -
-
effect {{optional_inline}}
-
El efecto objetivo, es un objeto de la interfaz {{domxref("AnimationEffectReadOnly")}} ,para asignarlo a la animación. Aunque en el futuro podrían ser asignados otros efectos como SequenceEffect o GroupEffect, el único efecto disponible actualmente es {{domxref("KeyframeEffect")}}. Este puede ser null (valor por defecto) para indicar que no debe aplicarse ningún efecto.
-
timeline {{optional_inline}}
-
Especifica el  timeline con el que asociar la animación, como un objeto de tipo basado en la interfaz {{domxref("AnimationTimeline")}} . Actualmente el único tipo de línea de tiempo disponible es {{domxref("DocumentTimeline")}}, pero en el futuro habrá lineas de tiempo asociadas , por ejemplo, con gestos o desplazamiento. El valor por defecto es {{domxref("Document.timeline")}}, pero puede ser establecido en null.
-
- -

Ejemplos

- -

En el ejemplo Follow the White Rabbit , el constructor Animation() es usado para crear una Animation para el rabbitDownKeyframes utilizando el timeline del documento:

- -
var rabbitDownAnimation = new Animation(rabbitDownKeyframes, document.timeline);
-
- -

Especificaciones

- - - - - - - - - - - - - - -
SpecificationStatusComment
{{SpecName('Web Animations', '#dom-animation-animation', 'Animation()' )}}{{Spec2('Web Animations')}}Editor's draft.
- -

Compatibilidad del navegador

- -
{{CompatibilityTable}}
- -
- - - - - - - - - - - - - - - - - - - -
FeatureChromeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
Basic support{{CompatNo}}{{CompatGeckoDesktop(48)}}[1]{{CompatNo}}{{CompatNo}}{{CompatNo}}
-
- -
- - - - - - - - - - - - - - - - - - - -
FeatureAndroidFirefox Mobile (Gecko)IE PhoneOpera MobileSafari Mobile
Basic support{{CompatNo}}{{CompatGeckoMobile(48)}} [1]{{CompatNo}}{{CompatNo}}{{CompatNo}}
-
- -

[1] La Web Animations API solo está habilitada de manera predeterminada en Firefox Developer Edition y Nightly builds. Puedes habilitarla en compilaciones de lanzamiento estableciendo la preferencia de dom.animations-api.core.enabled en true, y puede desactivarse en cualquier versión de Firefox estableciendo esta preferencia en false.

- -

Ver también

- - diff --git a/files/es/web/api/animation/animation/index.html b/files/es/web/api/animation/animation/index.html new file mode 100644 index 0000000000..770036b76d --- /dev/null +++ b/files/es/web/api/animation/animation/index.html @@ -0,0 +1,106 @@ +--- +title: Animation() +slug: Web/API/Animation/Animación +tags: + - Animacion +translation_of: Web/API/Animation/Animation +--- +

{{ SeeCompatTable() }}{{ APIRef("Web Animations API") }}

+ +

El constructor Animation() de Web Animations API devuelve una instancia del objeto Animation.

+ +

Sintaxis

+ +
var animation = new Animation([effect][, timeline]);
+ +

Parámetros

+ +
+
effect {{optional_inline}}
+
El efecto objetivo, es un objeto de la interfaz {{domxref("AnimationEffectReadOnly")}} ,para asignarlo a la animación. Aunque en el futuro podrían ser asignados otros efectos como SequenceEffect o GroupEffect, el único efecto disponible actualmente es {{domxref("KeyframeEffect")}}. Este puede ser null (valor por defecto) para indicar que no debe aplicarse ningún efecto.
+
timeline {{optional_inline}}
+
Especifica el  timeline con el que asociar la animación, como un objeto de tipo basado en la interfaz {{domxref("AnimationTimeline")}} . Actualmente el único tipo de línea de tiempo disponible es {{domxref("DocumentTimeline")}}, pero en el futuro habrá lineas de tiempo asociadas , por ejemplo, con gestos o desplazamiento. El valor por defecto es {{domxref("Document.timeline")}}, pero puede ser establecido en null.
+
+ +

Ejemplos

+ +

En el ejemplo Follow the White Rabbit , el constructor Animation() es usado para crear una Animation para el rabbitDownKeyframes utilizando el timeline del documento:

+ +
var rabbitDownAnimation = new Animation(rabbitDownKeyframes, document.timeline);
+
+ +

Especificaciones

+ + + + + + + + + + + + + + +
SpecificationStatusComment
{{SpecName('Web Animations', '#dom-animation-animation', 'Animation()' )}}{{Spec2('Web Animations')}}Editor's draft.
+ +

Compatibilidad del navegador

+ +
{{CompatibilityTable}}
+ +
+ + + + + + + + + + + + + + + + + + + +
FeatureChromeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
Basic support{{CompatNo}}{{CompatGeckoDesktop(48)}}[1]{{CompatNo}}{{CompatNo}}{{CompatNo}}
+
+ +
+ + + + + + + + + + + + + + + + + + + +
FeatureAndroidFirefox Mobile (Gecko)IE PhoneOpera MobileSafari Mobile
Basic support{{CompatNo}}{{CompatGeckoMobile(48)}} [1]{{CompatNo}}{{CompatNo}}{{CompatNo}}
+
+ +

[1] La Web Animations API solo está habilitada de manera predeterminada en Firefox Developer Edition y Nightly builds. Puedes habilitarla en compilaciones de lanzamiento estableciendo la preferencia de dom.animations-api.core.enabled en true, y puede desactivarse en cualquier versión de Firefox estableciendo esta preferencia en false.

+ +

Ver también

+ + diff --git a/files/es/web/api/animation/currenttime/index.html b/files/es/web/api/animation/currenttime/index.html new file mode 100644 index 0000000000..acd56f9170 --- /dev/null +++ b/files/es/web/api/animation/currenttime/index.html @@ -0,0 +1,114 @@ +--- +title: Animation.currentTime +slug: Web/API/Animation/tiempoActual +tags: + - Animacion +translation_of: Web/API/Animation/currentTime +--- +

{{APIRef("Web Animations")}}{{SeeCompatTable}}

+ +

La propiedad Animation.currentTime de la Web Animations API devuelve y establece el valor del tiempo de la animación en milisegundos, tanto si se está ejecutando como en pausa.

+ +

Si la animación carece de un {{domxref("AnimationTimeline", "timeline")}}, está inactiva, o no ha sido reproducida todavía, el valor devuelto por currentTime es null.

+ +

Sintaxis

+ +
var currentTime = Animation.currentTime;
+Animation.currentTime = newTime;
+ +

Valor

+ +

Un número que representa el tiempo actual en milisegundos, o null para desactivar la animación.

+ +

Ejemplos

+ +

En el juego Drink Me/Eat Me , la altura de Alicia está animada, por lo que puede cambiar de pequeña a grande y de grande a pequeña. Al inicio del juego, su altura de fija estableciendo el currentTime (tiempoActual) a la mitad de la duración total delKeyframeEffect:

+ +
aliceChange.currentTime = aliceChange.effect.timing.duration / 2;
+ +

Un modo más genérico para encontrar la marca del 50% en una animación sería :

+ +
animation.currentTime =
+  animation.effect.getComputedTiming().delay +
+  animation.effect.getComputedTiming().activeDuration / 2;
+ +

Especificaciones

+ + + + + + + + + + + + + + +
SpecificationStatusComment
{{SpecName('Web Animations', '#dom-animation-currenttime', 'currentTime')}}{{Spec2("Web Animations")}} 
+ +

Compatibilidad del navegador

+ +
{{CompatibilityTable}}
+ +
+ + + + + + + + + + + + + + + + + + + +
FeatureChromeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
Basic support{{CompatChrome(39.0)}}{{CompatGeckoDesktop(48)}}[1]{{CompatNo}}{{CompatNo}}{{CompatNo}}
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + +
FeatureAndroidAndroid WebviewFirefox Mobile (Gecko)IE PhoneOpera MobileSafari MobileChrome for Android
Basic support{{CompatNo}}{{CompatChrome(39.0)}}{{CompatGeckoMobile(48)}}[1]{{CompatNo}}{{CompatNo}}{{CompatNo}}{{CompatChrome(39.0)}}
+
+ +

[1] La Web Animations API solo está habilitada por defecto en Firefox Developer Edition y Nightly builds. Puedes habilitarlo en versiones Beta estableciendo la preferencia dom.animations-api.core.enabled en true, y puede ser deshabilitado en cualquier versión de Firefox, estableciendo esta preferencia en false.

+ +

Ver también

+ + diff --git a/files/es/web/api/animation/finished/index.html b/files/es/web/api/animation/finished/index.html new file mode 100644 index 0000000000..5d321ba012 --- /dev/null +++ b/files/es/web/api/animation/finished/index.html @@ -0,0 +1,120 @@ +--- +title: Animation.finished +slug: Web/API/Animation/terminado +tags: + - API + - Animacion + - Animaciones Web + - Experimental + - Reference +translation_of: Web/API/Animation/finished +--- +

{{ SeeCompatTable() }}{{ APIRef("Web Animations") }}

+ +

La propiedad de solo-lectura de Animation.finished de la  Web Animations API devuelve un {{jsxref("Promise")}} que se resuelve una vez que la animación a terminado de reproducirse.

+ +
+

Una vez que la reproducción de la animación abandona el estado  finished  ( es decir, la reproducción se está ejecutando otra vez ), Un nuevo Promise es creado para esta propiedad. El nuevo Promise será resuelto cuando se haya completado la actual secuencia de la animación.

+
+ +

Sintaxis

+ +
var animationsPromise = Animation.finished;
+
+ +

Valor

+ +

Un objeto {{jsxref("Promise")}} que se resuelve cuando haya finalizado la reproducción de la animación.

+ +

Ejemplos

+ +

El siguiente código espera a que todas las animaciones que se ejecutan en el elemento elem hayan terminado, después elimina el elemento del arbol del DOM:

+ +
Promise.all(
+  elem.getAnimations().map(
+    function(animation) {
+      return animation.finished
+    }
+  )
+).then(
+  function() {
+    return elem.remove();
+  }
+);
+ +

Especificaciones

+ + + + + + + + + + + + + + +
SpecificationStatusComment
{{SpecName('Web Animations', '#dom-animation-finished', 'Animation.finished' )}}{{Spec2('Web Animations')}}Editor's draft.
+ +

Compatibilidad del navegador

+ +
{{CompatibilityTable}}
+ +
+ + + + + + + + + + + + + + + + + + + +
FeatureChromeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
Basic support{{CompatNo}}{{CompatGeckoDesktop(48)}}[1]{{CompatNo}}{{CompatNo}}{{CompatNo}}
+
+ +
+ + + + + + + + + + + + + + + + + + + +
FeatureAndroidFirefox Mobile (Gecko)IE PhoneOpera MobileSafari Mobile
Basic support{{CompatNo}}{{CompatGeckoDesktop(48)}}[1]{{CompatNo}}{{CompatNo}}{{CompatNo}}
+
+ +

[1] La Web Animations API solo está habilitada por defecto en Firefox Developer Edition y Nightly builds. Puedes habilitarlo en versiones Beta y de lanzamiento estableciendo la preferencia dom.animations-api.core.enabled en true, y puedes deshabilitarlo en cualquier versión de Firefox estableciendo esta propiedad en false.

+ +

Ver también

+ + diff --git a/files/es/web/api/animation/terminado/index.html b/files/es/web/api/animation/terminado/index.html deleted file mode 100644 index 5d321ba012..0000000000 --- a/files/es/web/api/animation/terminado/index.html +++ /dev/null @@ -1,120 +0,0 @@ ---- -title: Animation.finished -slug: Web/API/Animation/terminado -tags: - - API - - Animacion - - Animaciones Web - - Experimental - - Reference -translation_of: Web/API/Animation/finished ---- -

{{ SeeCompatTable() }}{{ APIRef("Web Animations") }}

- -

La propiedad de solo-lectura de Animation.finished de la  Web Animations API devuelve un {{jsxref("Promise")}} que se resuelve una vez que la animación a terminado de reproducirse.

- -
-

Una vez que la reproducción de la animación abandona el estado  finished  ( es decir, la reproducción se está ejecutando otra vez ), Un nuevo Promise es creado para esta propiedad. El nuevo Promise será resuelto cuando se haya completado la actual secuencia de la animación.

-
- -

Sintaxis

- -
var animationsPromise = Animation.finished;
-
- -

Valor

- -

Un objeto {{jsxref("Promise")}} que se resuelve cuando haya finalizado la reproducción de la animación.

- -

Ejemplos

- -

El siguiente código espera a que todas las animaciones que se ejecutan en el elemento elem hayan terminado, después elimina el elemento del arbol del DOM:

- -
Promise.all(
-  elem.getAnimations().map(
-    function(animation) {
-      return animation.finished
-    }
-  )
-).then(
-  function() {
-    return elem.remove();
-  }
-);
- -

Especificaciones

- - - - - - - - - - - - - - -
SpecificationStatusComment
{{SpecName('Web Animations', '#dom-animation-finished', 'Animation.finished' )}}{{Spec2('Web Animations')}}Editor's draft.
- -

Compatibilidad del navegador

- -
{{CompatibilityTable}}
- -
- - - - - - - - - - - - - - - - - - - -
FeatureChromeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
Basic support{{CompatNo}}{{CompatGeckoDesktop(48)}}[1]{{CompatNo}}{{CompatNo}}{{CompatNo}}
-
- -
- - - - - - - - - - - - - - - - - - - -
FeatureAndroidFirefox Mobile (Gecko)IE PhoneOpera MobileSafari Mobile
Basic support{{CompatNo}}{{CompatGeckoDesktop(48)}}[1]{{CompatNo}}{{CompatNo}}{{CompatNo}}
-
- -

[1] La Web Animations API solo está habilitada por defecto en Firefox Developer Edition y Nightly builds. Puedes habilitarlo en versiones Beta y de lanzamiento estableciendo la preferencia dom.animations-api.core.enabled en true, y puedes deshabilitarlo en cualquier versión de Firefox estableciendo esta propiedad en false.

- -

Ver también

- - diff --git a/files/es/web/api/animation/tiempoactual/index.html b/files/es/web/api/animation/tiempoactual/index.html deleted file mode 100644 index acd56f9170..0000000000 --- a/files/es/web/api/animation/tiempoactual/index.html +++ /dev/null @@ -1,114 +0,0 @@ ---- -title: Animation.currentTime -slug: Web/API/Animation/tiempoActual -tags: - - Animacion -translation_of: Web/API/Animation/currentTime ---- -

{{APIRef("Web Animations")}}{{SeeCompatTable}}

- -

La propiedad Animation.currentTime de la Web Animations API devuelve y establece el valor del tiempo de la animación en milisegundos, tanto si se está ejecutando como en pausa.

- -

Si la animación carece de un {{domxref("AnimationTimeline", "timeline")}}, está inactiva, o no ha sido reproducida todavía, el valor devuelto por currentTime es null.

- -

Sintaxis

- -
var currentTime = Animation.currentTime;
-Animation.currentTime = newTime;
- -

Valor

- -

Un número que representa el tiempo actual en milisegundos, o null para desactivar la animación.

- -

Ejemplos

- -

En el juego Drink Me/Eat Me , la altura de Alicia está animada, por lo que puede cambiar de pequeña a grande y de grande a pequeña. Al inicio del juego, su altura de fija estableciendo el currentTime (tiempoActual) a la mitad de la duración total delKeyframeEffect:

- -
aliceChange.currentTime = aliceChange.effect.timing.duration / 2;
- -

Un modo más genérico para encontrar la marca del 50% en una animación sería :

- -
animation.currentTime =
-  animation.effect.getComputedTiming().delay +
-  animation.effect.getComputedTiming().activeDuration / 2;
- -

Especificaciones

- - - - - - - - - - - - - - -
SpecificationStatusComment
{{SpecName('Web Animations', '#dom-animation-currenttime', 'currentTime')}}{{Spec2("Web Animations")}} 
- -

Compatibilidad del navegador

- -
{{CompatibilityTable}}
- -
- - - - - - - - - - - - - - - - - - - -
FeatureChromeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
Basic support{{CompatChrome(39.0)}}{{CompatGeckoDesktop(48)}}[1]{{CompatNo}}{{CompatNo}}{{CompatNo}}
-
- -
- - - - - - - - - - - - - - - - - - - - - - - -
FeatureAndroidAndroid WebviewFirefox Mobile (Gecko)IE PhoneOpera MobileSafari MobileChrome for Android
Basic support{{CompatNo}}{{CompatChrome(39.0)}}{{CompatGeckoMobile(48)}}[1]{{CompatNo}}{{CompatNo}}{{CompatNo}}{{CompatChrome(39.0)}}
-
- -

[1] La Web Animations API solo está habilitada por defecto en Firefox Developer Edition y Nightly builds. Puedes habilitarlo en versiones Beta estableciendo la preferencia dom.animations-api.core.enabled en true, y puede ser deshabilitado en cualquier versión de Firefox, estableciendo esta preferencia en false.

- -

Ver también

- - diff --git a/files/es/web/api/api_de_almacenamiento_web/index.html b/files/es/web/api/api_de_almacenamiento_web/index.html deleted file mode 100644 index 26a858d254..0000000000 --- a/files/es/web/api/api_de_almacenamiento_web/index.html +++ /dev/null @@ -1,146 +0,0 @@ ---- -title: API de almacenamiento web -slug: Web/API/API_de_almacenamiento_web -tags: - - API - - API de almacenamiento web - - Almacenamiento web - - Storage - - localStorage - - sessionStorage -translation_of: Web/API/Web_Storage_API ---- -

{{DefaultAPISidebar("Web Storage API")}}

- -

La API de almacenamiento web proporciona los mecanismos mediante los cuales el navegador puede almacenar información de tipo clave/valor, de una forma mucho más intuitiva que utilizando cookies.

- -

Almacenamiento web, conceptos y uso

- -

Los dos mecanismos en el almacenamiento web son los siguientes:

- - - -

Estos mecanismos están disponibles mediante las propiedades Window.sessionStorage y  Window.localStorage (dicho con más precisión, en navegadores con soporte, el objeto Window implementa los objetos  WindowLocalStorage y WindowSessionStorage, en los cuales se basan las propiedades localStorage y sessionStorage). Al invocar uno de éstos, se creará una instancia del objeto Storage, a través del cual los datos pueden ser creados, recuperados y eliminados. sessionStorage y localStorage utilizan un objeto de almacenamiento diferente según su origen — funcionan y son controlados por separado.

- -
-

Nota: Acceder al Almacenamiento web desde IFrames de terceros está prohibido si el usuario tiene deshabilitadas las cookies de terceros (Firefox implementa este comportamiento a partir de la versión 43).

-
- -
-

Nota: El almacenamiento web no es lo mismo que mozStorage (interfaces Mozilla's XPCOM para SQLite) o la Session store API (una utilidad de almacenamiento XPCOM usada por extensiones).

-
- -

Interfaces de almacenamiento web

- -
-
{{domxref("Storage")}}
-
Permite crear, recuperar y eliminar datos de un dominio y tipo de almacenamiento (sesión o local) específicos.
-
{{domxref("Window")}}
-
La API de almacenamiento web extiende el objeto {{domxref("Window")}} con dos nuevas propiedades — {{domxref("Window.sessionStorage")}} y {{domxref("Window.localStorage")}} — que proveen acceso a la sesión actual del dominio y a objetos {{domxref("Storage")}} locales, respectivamente. También ofrece un manejador de evento {{domxref("Window.onstorage")}} que se dispara cuando un área de la memoria cambia (por ejemplo, cuando se almacena un nuevo elemento).
-
{{domxref("StorageEvent")}}
-
El evento storage se dispara en el objeto Window de un documento cuando un área de la memoria cambia.
-
- -

Ejemplos

- -

Para ilustrar algunos usos típicos del almacenamiento web, hemos creado un ejemplo simple, llamado Demo de almacenamiento web. La página de inicio proporciona controles que puedes utilizar para personalizar el color, la tipografía y la imagen decorativa. Cuando seleccionas una opción diferente, la página se actualiza instantáneamente; además, tus opciones se almacenan en localStorage, de forma que si abandonas la página y la vuelves a cargar, tus opciones son recordadas.

- -

También creamos una página de salida del evento — si cargas esta página en otra pestaña y luego haces cambios a tus opciones en la página de inicio, verás que se muestra la información almacenada actualizada puesto que se dispara un evento {{event("StorageEvent")}}.

- -

Especificaciones

- - - - - - - - - - - - - - -
EspecificaciónEstadoComentario
{{SpecName('Web Storage')}}{{Spec2('Web Storage')}} 
- -

Compatibilidad de navegadores

- -

{{ CompatibilityTable() }}

- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
CaracterísticaChromeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
localStorage43.5810.504
sessionStorage52810.504
-
- -
- - - - - - - - - - - - - - - - - - - -
CaracteristicaAndroidFirefox Mobile (Gecko)IE PhoneOpera MobileSafari Mobile
Basic support2.1{{ CompatUnknown }}811iOS 3.2
-
- -

Todos los navegadores tienen distintos niveles de capacidad tanto para localStorage como para sessionStorage. Aquí está una análisis detallado de todas las capacidades de almacenamiento de diferentes navegadores.

- -
-

Nota: Desde iOS 5.1, Safari Mobile almacena los datos de localStorage en la carpeta de caché, la cual está sujeta a limpiezas ocasionales, a petición del sistema operativo, típicamente cuando el espacio es reducido.

-
- - - -

La mayoría de los navegadores de hoy en día soportan una opción de privacidad llamada  'Modo incógnito', 'Navegación privada', o algo similar, que básicamente se asegura de que la sesión de navegación no deje rastros después de que el navegador se cierra. Esto es fundamentalmente incompatible con el almacenamiento web por obvias razones. Por ello, muchos navegadores están experimentando con diferentes escenarios para lidiar con esta incompatibilidad.

- -

La mayoría de los navegadores han optado por una estrategia donde las API de almacenamiento siguen disponibles y aparentemente completamente funcionales, con la única gran diferencia de que todos los datos almacenados son eliminados después de cerrar el navegador. Para estos navegadores aún hay diferentes interpretaciones sobre qué debería hacerse con los datos almacenados existentes (de una sesión de navegación normal). ¿Deberían de estar disponibles para lectura cuando esté en modo privado? Entonces, hay algunos navegadores, sobre todo Safari, que han optado por una solución donde el almacenamiento está disponible, pero vacío, y tiene un cupo de 0 bytes asignado, por lo que se vuelve imposible usar esta memoria para escribir datos.

- -

Los desarrolladores deberían de estar conscientes de estas diferentes implementaciones y tenerlas en cuenta a la hora de desarrollar aplicaciones web que depende de la API de almacenamiento web. Para más información, échale un vistazo a esta entrada de blog WHATWG que trata específicamente con este tema.

- -

Ver también

- - diff --git a/files/es/web/api/api_de_almacenamiento_web/usando_la_api_de_almacenamiento_web/index.html b/files/es/web/api/api_de_almacenamiento_web/usando_la_api_de_almacenamiento_web/index.html deleted file mode 100644 index 086af18610..0000000000 --- a/files/es/web/api/api_de_almacenamiento_web/usando_la_api_de_almacenamiento_web/index.html +++ /dev/null @@ -1,272 +0,0 @@ ---- -title: Usando la API de almacenamiento web -slug: Web/API/API_de_almacenamiento_web/Usando_la_API_de_almacenamiento_web -tags: - - API - - API de almacenamiento web - - Guía - - localStorage - - sessionStorage -translation_of: Web/API/Web_Storage_API/Using_the_Web_Storage_API ---- -
{{DefaultAPISidebar("Web Storage API")}}
- -
-

La API de almacenamiento web proporciona los mecanismos mediante los cuales el navegador puede almacenar información de tipo clave/valor, de una forma mucho más intuitiva que utilizando cookies.

- -

Este artículo proporciona una guía general de cómo usar esta tecnología.

-
- -

Conceptos básicos

- -

Los objetos de almacenamiento son simples almacenes de clave/valor, similares a objetos, pero que permanecen intactos cuando la página se recarga. Las claves y los valores siempre son cadenas de texto (fíjate que las claves con enteros se convierten automáticamente a cadenas, tal y como lo hacen los objetos). Puedes acceder a estos valores como un objeto, o con los métodos {{domxref("Storage.getItem()")}} y {{domxref("Storage.setItem()")}}. Estas tres líneas modifican el valor de colorSetting de la misma manera:

- -
localStorage.colorSetting = '#a4509b';
-localStorage['colorSetting'] = '#a4509b';
-localStorage.setItem('colorSetting', '#a4509b');
- -
-

Nota: Se recomiendo usar la API de almacenamiento web (setItem, getItem, removeItem, key, length) para prevenir las dificultades asociadas al uso de simples objetos como almacenes de valores llave/valor.

-
- -

Los dos mecanismos en el almacenamiento web son los siguientes:

- - - -

Estos mecanismos están disponibles mediante las propiedades Window.sessionStorage y  Window.localStorage (dicho con más precisión, en navegadores con soporte, el objeto Window implementa los objetos  WindowLocalStorage y WindowSessionStorage, en los cuales se basan las propiedades localStorage y sessionStorage). Al invocar uno de éstos, se creará una instancia del objeto Storage, a través del cual los datos pueden ser creados, recuperados y eliminados. sessionStorage y localStorage utilizan un objeto de almacenamiento diferente según su origen — funcionan y son controlados por separado.

- -

Así que, por ejemplo, si en un inicio se llama a localStorage en un documento, esto devolverá un objeto {{domxref("Storage")}}; llamar a sessionStorage en un documento devolverá un objeto {{domxref("Storage")}} diferente. Ambos objetos se pueden manipular de la misma forma, pero separados.

- -

Detectar la característica localStorage

- -

Para poder usar localStorage, debemos de verificar que tiene soporte y que está disponible en la sesión del buscador actual.

- -

Probar la disponibilidad

- -
-

Nota: esta API está disponible en las versiones actuales de todos los navegadores principales. La prueba de disponibilidad es necesaria sólo si debes soportar navegadores muy antiguos, como Internet Explorer 6 o 7, o en las circunstancias limitadas descritas más abajo.

-
- -

Los buscadores que soportan localStorage tienen una propiedad en el objeto window que se llama localStorage. Sin embargo, por varias razones, el sólo asegurarse de que la propiedad existe puede arrojar excepciones. El que localStorage exista no es garantía suficiente de que en verdad esté disponible, puesto que varios buscadores ofrecen configuraciones que lo inhabilitan. Así que un buscador puede soportar localStorage, pero puede no hacerlo disponible para los scripts en la página. Un ejemplo de esto es Safari, que en el modo de búsqueda privada ofrece un objeto localStorage vacío con un cupo de 0, por lo que es inutilizable. Sin embargo, es posible que aún así obtengamos un QuotaExceededError legítimo, lo que significa que ya usamos todo el espacio de almacenamiento disponible, aunque el almacenamiento esté, de hecho, disponible. Nuestra detección de la característica debe de tomar en cuenta estos escenarios.

- -

Esta función detecta si localStorage tiene soporte y está disponible:

- -
function storageAvailable(type) {
-    try {
-        var storage = window[type],
-            x = '__storage_test__';
-        storage.setItem(x, x);
-        storage.removeItem(x);
-        return true;
-    }
-    catch(e) {
-        return e instanceof DOMException && (
-            // everything except Firefox
-            e.code === 22 ||
-            // Firefox
-            e.code === 1014 ||
-            // test name field too, because code might not be present
-            // everything except Firefox
-            e.name === 'QuotaExceededError' ||
-            // Firefox
-            e.name === 'NS_ERROR_DOM_QUOTA_REACHED') &&
-            // acknowledge QuotaExceededError only if there's something already stored
-            storage.length !== 0;
-    }
-}
- -

Y aquí se muestra cómo usarla:

- -
if (storageAvailable('localStorage')) {
-  // Yippee! We can use localStorage awesomeness
-}
-else {
-  // Too bad, no localStorage for us
-}
- -

También puedes probar sessionStorage invocando storageAvailable('sessionStorage').

- -

Aquí puedes ver una breve historia de la detección de la característica localStorage.

- -

Ejemplo

- -

Para ilustrar un uso típico de almacenamiento web, creamos un ejemplo simple que llamamos Demo de almacenamiento web. La página de inicio proporciona unos controles que se pueden usar para personalizar el color, la tipografía y la imagen decorativa:

- -

Cuando seleccionas una opción diferente, la página se actualiza instantáneamente; además, tus opciones se almacenan en localStorage, de forma que si abandonas la página y la vuelves a cargar, tus opciones son recordadas.

- -

También creamos una página de salida del evento — si cargas esta página en otra pestaña y luego haces cambios a tus opciones en la página de inicio, verás que se muestra la información almacenada actualizada puesto que se dispara un {{domxref("StorageEvent")}}.

- -

- -
-

Nota: Puedes ver las páginas de ejemplo usando los links de arriba y también puedes ver el código fuente.

-
- -

Probar si la memoria tiene valores

- -

En el inicio de main.js, probamos si el objeto ya tiene valores (es decir, si la página ya fue visitada):

- -
if(!localStorage.getItem('bgcolor')) {
-  populateStorage();
-} else {
-  setStyles();
-}
- -

El método {{domxref("Storage.getItem()")}} se usa para obtener un dato de la memoria; en este caso, estamos probando si el dato bgcolor existe; si no, corremos populateStorage() para añadir los valores personalizados actuales a la memoria. Si ya hay valores guardados, corremos setStyles() para actualizar el estilo de la página con los valores almacenados.

- -

Nota: También puedes usar {{domxref("Storage.length")}} para probar si el objeto de almacenamiento está vació o no.

- -

Obtener valores de la memoria

- -

Como dijimos arriba, los valores se pueden recuperar de la memoria usando {{domxref("Storage.getItem()")}}. Este método usa la llave del dato como argumento y devuelve el valor. Por ejemplo:

- -
function setStyles() {
-  var currentColor = localStorage.getItem('bgcolor');
-  var currentFont = localStorage.getItem('font');
-  var currentImage = localStorage.getItem('image');
-
-  document.getElementById('bgcolor').value = currentColor;
-  document.getElementById('font').value = currentFont;
-  document.getElementById('image').value = currentImage;
-
-  htmlElem.style.backgroundColor = '#' + currentColor;
-  pElem.style.fontFamily = currentFont;
-  imgElem.setAttribute('src', currentImage);
-}
- -

Aquí, en las primeras tres líneas tomamos los valores del almacenamiento local. Después, fijamos los valores mostrados en los elementos del formulario a esos valores, de forma que se mantengan sincronizados cuando recargues la página. Finalmente, actualizamos los estilos y la imagen decorativa en la página para que tus opciones personalizadas vuelvan a aparecer al recargar.

- -

Guardar valores en la memoria

- -

{{domxref("Storage.setItem()")}} se usa tanto para crear nuevos datos como para actualizar valores existentes (si el dato ya existía). Este método recibe dos argumentos: la llave del dato que se va a crear/modificar y el valor que se va a guardar.

- -
function populateStorage() {
-  localStorage.setItem('bgcolor', document.getElementById('bgcolor').value);
-  localStorage.setItem('font', document.getElementById('font').value);
-  localStorage.setItem('image', document.getElementById('image').value);
-
-  setStyles();
-}
- -

La función populateStorage() define tres elementos en el almacenamiento local — el color de fondo, la tipografía y la ruta de almacenamiento de la imagen. Luego corre la función setStyles() para actualizar el estilo de la página, etc.

- -

También incluimos un manejador onchange para cada elemento del formulario, de manera que los datos y los estilos son actualizados cada vez que un valor del formulario cambia:

- -
bgcolorForm.onchange = populateStorage;
-fontForm.onchange = populateStorage;
-imageForm.onchange = populateStorage;
- -

Responder a cambios en la memoria con el evento StorageEvent

- -

El evento {{domxref("StorageEvent")}} se dispara siempre que se hace un cambio al objeto {{domxref("Storage")}} (nota que este evento no se dispara para cambios en sessionStorage). Este evento no va a trabajar en la misma página en la que se están haciendo los cambios, sino que es una manera para que las otras páginas del dominio que usan la memoria sincronicen los cambios que se están haciendo. Las páginas en otros dominios no pueden acceder a los mismos objetos de almacenamiento.

- -

En la página de eventos (ver events.js) el único JavaScript es el siguiente:

- -
window.addEventListener('storage', function(e) {
-  document.querySelector('.my-key').textContent = e.key;
-  document.querySelector('.my-old').textContent = e.oldValue;
-  document.querySelector('.my-new').textContent = e.newValue;
-  document.querySelector('.my-url').textContent = e.url;
-  document.querySelector('.my-storage').textContent = JSON.stringify(e.storageArea);
-});
- -

Aquí añadimos un detector de evento al objeto window que se dispara cuando el objeto {{domxref("Storage")}} asociado con el origen actual cambia. Como puedes ver arriba, el objeto asociado a este evento tiene varias propiedades que contienen información útil — la llave del dato que cambió, el valor anterior al cambio, el nuevo valor tras el cambio, la URL del documento que cambió la memoria y el objeto de almacenamiento mismo (que volvimos una cadena para que pudieras ver su contenido).

- -

Borrar registros

- -

El almacenamiento web también provee un par de métodos simples para remover datos. No los usamos en nuestro demo, pero se pueden añadir de manera muy simple a tu proyecto:

- - - -

Especificaciones

- - - - - - - - - - - - - - -
EspecificaciónEstadoComentario
{{SpecName('HTML WHATWG', 'webstorage.html#webstorage')}}{{Spec2('HTML WHATWG')}}
- -

Compatibilidad de navegadores

- -

{{ CompatibilityTable() }}

- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
CaracterísticaChromeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
localStorage43.5810.504
sessionStorage52810.504
-
- -
- - - - - - - - - - - - - - - - - - - -
CaracterísticaAndroidFirefox Mobile (Gecko)IE PhoneOpera MobileSafari Mobile
Basic support2.1{{ CompatUnknown }}811iOS 3.2
-
- -

Todos los navegadores tienen distintos niveles de capacidad tanto para localStorage como para sessionStorage. Aquí está una análisis detallado de todas las capacidades de almacenamiento de diferentes navegadores.

- -
-

Nota: Desde iOS 5.1, Safari Mobile almacena los datos de localStorage en la carpeta de caché, la cual está sujeta a limpiezas ocasionales, a petición del sistema operativo, típicamente cuando el espacio es reducido.

-
- -

Ver también

- - diff --git a/files/es/web/api/api_del_portapapeles/index.html b/files/es/web/api/api_del_portapapeles/index.html deleted file mode 100644 index 53e43f6bd5..0000000000 --- a/files/es/web/api/api_del_portapapeles/index.html +++ /dev/null @@ -1,76 +0,0 @@ ---- -title: API del portapapeles -slug: Web/API/API_del_portapapeles -translation_of: Web/API/Clipboard_API ---- -
{{DefaultAPISidebar("Clipboard API")}}
- -

La API del portapapeles permite acceder los comandos del portapapeles (cortar, copiar y pegar), así como leer y escribir de manera asíncrona el portapapeles del sistema. Acceder al contenido del portapapeles está sujeta a la API de permisos: El permiso clipboard-write es concedido automáticamente a las páginas cuando están en la pestaña activa. El permiso clipboard-read debe ser solicitado, lo que se puede hacer intentando leer directamente el portapapeles.

- -

Esta API está diseñada para reemplazar el acceso al portapapeles usando {{domxref("document.execCommand()")}}.

- -

Accediendo al portapapeles

- -

En vez de instanciar un objeto Clipboard, se puede acceder al portapapeles del sistema a través de la variable global {{domxref("Navigator.clipboard")}}:

- -
navigator.clipboard.readText().then(
-  clipText => document.querySelector(".editor").innerText += clipText);
- -

Esta pieza de código lee el texto que hay en el portapapeles y lo añade al primer elemento que tenga la clase editor. Desde que {{domxref("Clipboard.readText", "readText()")}} (y también {{domxref("Clipboard.read", "read()")}}, de hecho) devuelve una cadena de texto vacía si el contenido del portapapeles no es texto, este código es seguro.

- -

Interfaces

- -
-
{{domxref("Clipboard")}} {{securecontext_inline}}
-
Proporciona una interfaz para leer y escribir texto y datos. La especificación se refiere a esto como 'Async Clipboard API.'
-
{{domxref("ClipboardEvent")}} {{securecontext_inline}}
-
Representa la información del evento que se ha disparado. Los eventos que se pueden disparar son: {{domxref("Element/cut_event", "cortar")}}, {{domxref("Element/copy_event", "copiar")}}, y {{domxref("Element/paste_event", "pegar")}}. La especificación se refiere a esto como 'Clipboard Event API'.
-
{{domxref("ClipboardItem")}} {{securecontext_inline}}
-
Representa uno de los objetos del portapapeles, usado en la lectura y escritura de datos.
-
- -

Especificaciones

- - - - - - - - - - - - - - -
EspecificaciónEstadoObservaciones
{{SpecName('Clipboard API')}}{{Spec2('Clipboard API')}}Definición primitiva.
- -

Compatibilidad con navegadores

- -

Clipboard

- -
- - -

{{Compat("api.Clipboard")}}

- -

ClipboardEvent

- - - -

{{Compat("api.ClipboardEvent")}}

- -

ClipboardItem

- - - -

{{Compat("api.ClipboardItem")}}

- -

Véase también

- - -
diff --git a/files/es/web/api/battery_status_api/index.html b/files/es/web/api/battery_status_api/index.html new file mode 100644 index 0000000000..9cdc7259be --- /dev/null +++ b/files/es/web/api/battery_status_api/index.html @@ -0,0 +1,39 @@ +--- +title: API de Estado de Bateria +slug: WebAPI/Estado_de_Bateria +tags: + - API + - Apps + - Firefox OS + - Mobile +translation_of: Web/API/Battery_Status_API +--- +

La API de Estado de Batería, también conocida como "Battery API", provee información acerca del sistema de carga de la batería y permite notificar mediante eventos que son enviados cuando el nivel de la batería cambia. Este puede ser usado para ajustar el uso de recursos por parte de una aplicación y evitar un gasto innecesario de energía cuando la batería esta baja o para guardar cambios en un archivo antes de que la batería se agote y así prevenir perdida de información.

+

La API de Estado de Batería se puede extender mediante {{domxref("window.navigator")}} con una propiedad {{domxref("window.navigator.battery")}} que pertenece al objeto {{domxref("BatteryManager")}} y agrega algunos nuevos eventos que usted puede recibir en el monitor de estado de la batería.

+

Ejemplo

+

En este ejemplo, observamos los cambios en el estado de la carga (este o no conectado y cargando) y  en el nivel de la batería. Esto se hace escuchando el evento {{event("chargingchange")}} y el evento {{event("levelchange")}} respectivamente.

+
var battery = navigator.battery || navigator.mozBattery || navigator.webkitBattery;
+
+function updateBatteryStatus() {
+  console.log("Battery status: " + battery.level * 100 + " %");
+
+  if (battery.charging) {
+    console.log("Battery is charging");
+  }
+}
+
+battery.addEventListener("chargingchange", updateBatteryStatus);
+battery.addEventListener("levelchange", updateBatteryStatus);
+updateBatteryStatus();
+
+

Vea también: El ejemplo en las especificaciones

+

Especificaciones

+

{{page("/en-US/docs/Web/API/BatteryManager","Specifications")}}

+

Compatibilidad del Navegador

+

{{page("/en-US/docs/Web/API/BatteryManager","Browser_compatibility")}}

+

Vea también

+ diff --git a/files/es/web/api/canvas_api/a_basic_ray-caster/index.html b/files/es/web/api/canvas_api/a_basic_ray-caster/index.html new file mode 100644 index 0000000000..7917541554 --- /dev/null +++ b/files/es/web/api/canvas_api/a_basic_ray-caster/index.html @@ -0,0 +1,59 @@ +--- +title: A basic ray-caster +slug: Web/HTML/Canvas/A_basic_ray-caster +tags: + - Avanzado + - Canvas + - Ejemplo + - Espanol(2) + - Gráficos(2) + - HTML + - Necesita traducción + - Web +translation_of: Web/API/Canvas_API/A_basic_ray-caster +--- +
{{CanvasSidebar}}
+ +
+

Este artículo proporciona un interesante ejemplo de la vida real usando el elemento {{HTMLElement("canvas")}} para renderizar un sencillo entorno 3D usando una técnica de render llamada ray-casting.

+
+ +

{{EmbedGHLiveSample("canvas-raycaster/index.html", 900, 300)}}

+ +

Abrir en una nueva ventana

+ +

¿Por qué?

+ +

 

+ +

Después de darme cuenta, para mi satisfacción, de que el ingenioso elemento <canvas> sobre el que había estado leyendo, no sólo iba a recibir soporte por parte de Firefox, sino que ya estaba soportado por la versión actual de Safari, así que tenía que ponerme manos a la obra y hacer un pequeño experimento.

+ +

El tutorial y el resumen que encontré en la MDN son geniales, pero nadie había escrito nada (todavía) sobre animación, así que pensé que sería una buena oportunidad para portar un sencillo raycaster en el que había trabajado hacía tiempo y comprobar qué tipo de rendimiento podía esperar de un buffer de pixeles controlado por JavaScript.

+ +

¿Cómo?

+ +

La idea es sencilla, usar {{domxref("WindowTimers.setInterval","setInterval()")}} con un retraso arbitrario que se corresponde con la velocidad de fotogramas deseada. Por cada llamada al intervalo se ejecutará una función que volverá a pintar el elemento <canvas>  en el que se muestra la vista actual del usuario. Sé que podría haber empezado con un ejemplo más simple, pero creo que el tutorial sobre <canvas> vale para eso, y quería ver si podía hacer esto.

+ +

Continuemos, por cada actualización, el raycaster comprueba si has presionado alguna tecla, si no has presionado ninguna se mantendrán los cálculos para ahorrar tiempo de ejecución. Si se ha presionado alguna tecla, el <canvas> es borrado, el suelo y el cielo son pintados, la posición y la orientación de la cámara son actualizados, y los rayos son lanzados. Según van intersectando los rayos con las paredes se van pintando líneas verticales de pared del color de la pared con la que han colisionado, mezcladas con una versión más oscura del color en función de la distancia a la que se encuentra la pared. La altura de la línea vertical depende directamente de la distancia a la que el rayo es interesectado, dibujándose centrada con respecto la línea del horizonte.

+ +

El código que he terminado usando es una amalgama regurgitada del código que aparece en los capítulos sobre raycasting del libro de André LaMothe Tricks of the Game Programming Gurus y una versión en java de un raycaster que encontré online, a su vez pasado por el filtro de mi pulsión a renombrar todo para que tenga sentido para mí y todo el chapuceo necesario para que las cosas funcionen bien.

+ +

Resultados

+ +

El <canvas> en Safari 2.0.1 rindió sorprendentemente bien. Renderizando columnas de 8 pixeles de ancho, puedo correr una ventana de 320 x 240 a 24 FPS en mi Apple Mini. Firefox 1.5 Beta 1 es incluso más rápido; puedo correr una ventana de 320 x 240 a 24 FPS con columnas de 4 píxeles de ancho. No es exactamente un nuevo miembro de la familia de ID Software, pero es bastante decente teniendo en cuenta que es un entorno completamente interpretado, y que no me he tenido que preocupar de reservar memoria, ni modos de vídeo o escribir rutinas en ensamblador. Aún así el código intenta ser lo más eficiente posible, usando tablas de acceso rápido para valores precalculados, pero no soy ningún gurú de la optimización, así que seguramente haya varias cosas que se puedan escribir más rápido.

+ +

Además, deja bastante que desear en términos de intentar convertir esto en un motor de juego - no hay texturas en las paredes, no hay sprites, no hay puertas, ni siquiera hay teletransportadores que te permitan ir a otro nivel. Pero tengo bastante confianza en que todas esas cosas se podrían añadir con el suficiente tiempo disponible. La API de canvas soporta copiado de píxeles de imágenes, así que tener texturas parece bastante factible. Dejaré esto para otro artículo, seguramente para otra persona. =)

+ +

El ray-caster

+ +

El gente tan maravillosa que hay aquí han copiado manualmente mis archivos así vosotros podéis echarle un vistazo, y para vuestro disfrute he puesto los archivos individuales como listados de código (ver abajo).

+ +

¡Así que aquí lo tenéis, arrancad Safari 1.3+ o Firefox 1.5+ o cualquier otro navegador que soporte el elemento <canvas> y a disfrutar!
+
+ input.js | Level.js | Player.js | RayCaster.html | RayCaster.js | trace.css | trace.js

+ +

Ver también

+ + diff --git a/files/es/web/api/canvas_api/index.html b/files/es/web/api/canvas_api/index.html new file mode 100644 index 0000000000..dfdde2bf63 --- /dev/null +++ b/files/es/web/api/canvas_api/index.html @@ -0,0 +1,170 @@ +--- +title: API Canvas +slug: Web/HTML/Canvas +tags: + - API + - Canvas + - JavaScript + - Referencia + - introducción +translation_of: Web/API/Canvas_API +--- +
{{CanvasSidebar}}
+ +

Añadido en HTML5, el elemento HTML {{HTMLElement("canvas")}} se puede usar para dibujar gráficos mediante scripting en JavaScript. Por ejemplo, se puede usar para hacer gráficas, composiciones fotográficas, crear animaciones, o incluso procesado o renderizado de vídeo en tiempo real.

+ +

Las aplicaciones de Mozilla soportan <canvas> desde Gecko 1.8 (es decir, Firefox 1.5). El elemento fue inicialmenmte presentado por Apple para el Dashboard de OS X y Safari. Internet Explorer soporta <canvas> desde la versión 9 en adelante; para versiones anteriores de IE, se puede añadir soporte para <canvas> a una página incluyendo un script del proyecto de Google Explorer Canvas. Google Chrome y Opera 9 también soportan <canvas>.

+ +

El elemento <canvas> también se usa en WebGL para dibujar gráficos 3D con aceleración por hardware en páginas web.

+ +

Ejemplo

+ +

Esto es un trozo de código que usa el método {{domxref("CanvasRenderingContext2D.fillRect()")}}.

+ +

HTML

+ +
<canvas id="canvas"></canvas>
+
+ +

JavaScript

+ +
var canvas = document.getElementById('canvas');
+var ctx = canvas.getContext('2d');
+
+ctx.fillStyle = 'green';
+ctx.fillRect(10, 10, 100, 100);
+
+ +

Edita este código para ver tus cambios en tiempo real en este canvas:

+ + + +

{{ EmbedLiveSample('Código_editable', 700, 360) }}

+ +

Referencia

+ +
+ +
+ +

Las interfaces relacionadas con WebGLRenderingContext están en WebGL.

+ +

{{domxref("CanvasCaptureMediaStream")}} está relacionado..

+ +

Guías y tutoriales

+ +
+
Tutorial Canvas
+
Tutorial exhaustivo que cubre tanto el uso básico de <canvas> como sus características avanzadas.
+
Fragmentos de código: Canvas
+
Algunos fragmentos de código orientados al desarrollador de extensiones usando <canvas>.
+
Demo: Un laberinto básico
+
Una demo de una animación de laberinto usando canvas.
+
Dibujar objetos DOM en un canvas
+
Cómo dibujar contenido DOM, como elementos HTML, en un canvas.
+
Manipulación de vídeo usando canvas
+
Combinando {{HTMLElement("video")}} y {{HTMLElement("canvas")}} para manipular datos de vídeo en tiempo real.
+
+ +

Recursos

+ +

Genéricos

+ + + +

Librerías

+ + + +

Especificaciones

+ + + + + + + + + + + + + + + + +
EspecificaciónEstadoComentarios
{{SpecName('HTML WHATWG', 'scripting.html#the-canvas-element', '<canvas>')}}{{Spec2('HTML WHATWG')}} 
+ +

Ver también

+ + diff --git a/files/es/web/api/canvas_api/manipulating_video_using_canvas/index.html b/files/es/web/api/canvas_api/manipulating_video_using_canvas/index.html new file mode 100644 index 0000000000..a3ae61673d --- /dev/null +++ b/files/es/web/api/canvas_api/manipulating_video_using_canvas/index.html @@ -0,0 +1,126 @@ +--- +title: Manipular video por medio de canvas +slug: Web/HTML/anipular_video_por_medio_de_canvas +tags: + - Canvas + - Firefox 3.5 + - Video + - para_revisar +translation_of: Web/API/Canvas_API/Manipulating_video_using_canvas +--- +

{{ fx_minversion_header (3.5) }}

+

Al combinar las capacidades del elemento video introducido en Firefox 3.5 con un elemento canvas , puedes manipular los datos de video en tiempo real para incorporar una variedad de efectos visuales que se mostrarán en el video.  Este artículo, adaptado de esta entrada del blog de Paul Rouget, muestra cómo realizar una inserción croma (también conocida como el "efecto pantalla verde") utilizando el código JavaScript.

+

Ver este ejemplo en vivo .

+

El contenido del documento

+

El documento XHTML que se utiliza para representar este contenido se muestra a continuación.

+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style>
+      body {
+        background: black;
+        color:#CCCCCC;
+      }
+      #c2 {
+        background-image: url(foo.png);
+        background-repeat: no-repeat;
+      }
+      div {
+        float: left;
+        border :1px solid #444444;
+        padding:10px;
+        margin: 10px;
+        background:#3B3B3B;
+      }
+    </style>
+    <script type="text/javascript;version=1.8" src="main.js"></script>
+  </head>
+
+  <body onload="processor.doLoad()">
+    <div>
+      <video id="video" src="video.ogv" controls="true"/>
+    </div>
+    <div>
+      <canvas id="c1" width="160" height="96"/>
+      <canvas id="c2" width="160" height="96"/>
+    </div>
+  </body>
+</html>
+
+

Los puntos clave a tener en cuenta son:

+
    +
  1. Este documento establece dos elemento canvas , con los identificadores de c1 y c2 .  Canvas c1 se utiliza para mostrar la imagen actual del video original, mientras que c2 se utiliza para mostrar el video después de realizar la manipulación con el efecto croma; c2 se carga previamente con la imagen fija que se utilizará para sustituir el fondo verde en el video.
  2. +
  3. El código JavaScript es importado de un script llamado main.js ; este script utiliza JavaScript 1.8 características, de modo que la versión se especifica en la línea 22 al importar la secuencia de comandos.
  4. +
  5. Cuando se carga el documento, se ejecuta el método processor.doLoad() de main.js.
  6. +
+

El código JavaScript

+

El código JavaScript en main.js consta de tres métodos.

+

Inicializar el reproductor de croma

+

El método doLoad() se llama cuando el documento XHTML se carga inicialmente.  La función de este método es preparar las variables necesarias para el código de procesamiento del croma y la creación de un detector de eventos para que podamos detectar cuándo inicia el usuario la reproducción del video.

+
  doLoad: function() {
+    this.video = document.getElementById("video");
+    this.c1 = document.getElementById("c1");
+    this.ctx1 = this.c1.getContext("2d");
+    this.c2 = document.getElementById("c2");
+    this.ctx2 = this.c2.getContext("2d");
+    let self = this;
+    this.video.addEventListener("play", function() {
+        self.width = self.video.videoWidth / 2;
+        self.height = self.video.videoHeight / 2;
+        self.timerCallback();
+      }, false);
+  },
+
+

Este código obtiene referencias a los elementos del documento XHTML que son de particular interés, a saber, el elemento video y los dos elementos canvas.  También recupera las referencias a los contextos gráficos para cada uno de los dos elementos canvas.  Estos serán utilizados cuando estamos haciendo de verdad el efecto croma.

+

Luego addEventListener() es llamado para empezar a ver el elemento video de forma que podamos obtener una notificación cuando el usuario presione el botón de reproducción en el video.  En respuesta al usuario que inicia la reproducción, el código obtiene la anchura y la altura de video, reduciendo a la mitad cada uno (vamos a reducir a la mitad el tamaño del video, cuando llevamos a cabo la manipulación efecto croma). A continuación, llama al método timerCallback() para iniciar el visionado del video y la computación del efecto visual.

+

La devolución de llamada del temporizador

+

La devolución de llamada del temporizador se llama inicialmente cuando el video comienza a reproducirse (cuando tiene lugar el evento "reproducir"), a continuación, toma la responsabilidad por la que se establece a sí mismo para ser llamado periódicamente, a fin de poner en marcha el efecto croma para cada imagen.

+
  timerCallback: function() {
+    if (this.video.paused || this.video.ended) {
+      return;
+    }
+    this.computeFrame();
+    let self = this;
+    setTimeout(function () {
+        self.timerCallback();
+      }, 0);
+  },
+
+

Lo primero que la devolución de llamada hace es comprobar si el video se está aún reproduciendo, y si no lo está, la devolución de llamada regresa inmediatamente sin hacer nada.

+

A continuación, llama al método computeFrame(), que lleva a cabo la manipulación del efecto croma en la imagen de video actual.

+

Lo último que la devolución de llamada hace es llamar a setTimeout() para programarse para ser llamado lo más pronto posible.  En el mundo real, es probable que programes esto en función de la velocidad de fotogramas del video.

+

Manipulación de los datos de la imagen del video

+

El método computeFrame(), que se muestra a continuación, se encarga de ir a buscar realmente un fotograma de datos y realizar el efecto croma.

+
  computeFrame: function() {
+    this.ctx1.drawImage(this.video, 0, 0, this.width, this.height);
+    let frame = this.ctx1.getImageData(0, 0, this.width, this.height);
+    let l = frame.data.length / 4;
+
+    for (let i = 0; i < l; i++) {
+      let r = frame.data[i * 4 + 0];
+      let g = frame.data[i * 4 + 1];
+      let b = frame.data[i * 4 + 2];
+      if (g > 100 && r > 100 && b < 43)
+        frame.data[i * 4 + 3] = 0;
+    }
+    this.ctx2.putImageData(frame, 0, 0);
+    return;
+  }
+
+

Cuando esta rutina es llamada, el elemento video muestra el fotograma de datos de video más reciente, que tiene este aspecto:

+

video.png

+

En la línea 2, ese fotograma de video se copia al contexto gráfico ctx1 del primer lienzo, especificando como alto y ancho los valores que previamente guardamos para dibujar el fotograma a mitad de tamaño.  Ten en cuenta que puedes pasar simplemente el elemento de video al método drawImage() del contexto para dibujar el fotograma de video actual en dicho contexto.  El resultado es:

+

sourcectx.png

+

Línea 3 obtiene una copia de los datos gráficos del actual fotograma de video llamando al método getImageData() en el primer contexto.  Esto proporciona los datos de imagen en píxeles de 32 bits sin procesar que podemos después manipular.  Línea 4 calcula el número de píxeles de la imagen al dividir entre cuatro el tamaño total de los datos de la imagen del fotograma.

+

El bucle for que comienza en la línea 6 explora a través de los píxeles del fotograma, extrayendo los valores rojo, verde y azul para cada píxel, y compara los valores frente a números predeterminados que se utilizan para detectar la pantalla verde que se reemplazará por la imagen de fondo fija importada de foo.png .

+

Cada píxel de datos de imagen del fotograma que se encuentra que está dentro de los parámetros que se consideran parte de la pantalla verde tiene su valor alfa reemplazado por un cero, lo que indica que el píxel es totalmente transparente.  Como resultado, la imagen final tiene toda el área de pantalla verde 100% transparente, de modo que cuando se dibuja en el contexto de destino en la línea 13, el resultado es una superposición sobre el fondo estático.

+

La imagen resultante tiene este aspecto:

+

output.png

+

Esto se hace en varias ocasiones mientras el video se reproduce, de manera que fotograma tras fotograma se procesa y se muestra con el efecto de croma.

+

Ver este ejemplo en vivo .

+

Consulta también

+ +

{{ languages ( { "en": "En/Manipulating_video_using_canvas" } ) }}

diff --git a/files/es/web/api/canvas_api/tutorial/advanced_animations/index.html b/files/es/web/api/canvas_api/tutorial/advanced_animations/index.html new file mode 100644 index 0000000000..15ab72ee7f --- /dev/null +++ b/files/es/web/api/canvas_api/tutorial/advanced_animations/index.html @@ -0,0 +1,380 @@ +--- +title: Advanced animations +slug: Web/Guide/HTML/Canvas_tutorial/Advanced_animations +tags: + - Canvas + - Tutoria + - graficos +translation_of: Web/API/Canvas_API/Tutorial/Advanced_animations +--- +
{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Basic_animations", "Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas")}}
+ +
+

En el último capítulo hicimos unas animaciones básicas y nos familiarizamos con varias maneras de mover cosas. En esta parte examinaremos la moción misma y agregaremos la física para hacer nuestras animaciones más avanzadas.

+
+ +

Dibujar una bola

+ +

Vamos a usar una bola para nuestro estudio de la animación, entonces primero dibujamos la bola dentro del canvas. El siguente código lo configurará.

+ +
<canvas id="canvas" width="600" height="300"></canvas>
+
+ +

Como siempre, necesitamos un entorno para dibujar. Para dibujar la bola, creamos un contenido ball lo cual contiene propiedades y un método draw().

+ +
var canvas = document.getElementById('canvas');
+var ctx = canvas.getContext('2d');
+
+var ball = {
+  x: 100,
+  y: 100,
+  radius: 25,
+  color: 'blue',
+  draw: function() {
+    ctx.beginPath();
+    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
+    ctx.closePath();
+    ctx.fillStyle = this.color;
+    ctx.fill();
+  }
+};
+
+ball.draw();
+ +

Nada especial aquí; la bola es en realidad un circulo sencillo y se dibuja con el método {{domxref("CanvasRenderingContext2D.arc()", "arc()")}}.

+ +

Agregar velocidad

+ +

Ya que tenemos una bola, estamos listos agregar una animación básica así como aprendimos en el último capítulo de esta tutoría. De nuevo, {{domxref("window.requestAnimationFrame()")}} nos ayuda controlar la animación. La bola empieza moverse por agregar un vector de velocidad a la posición. Para cada fotograma, también {{domxref("CanvasRenderingContext2D.clearRect", "clear", "", 1)}} el canvas para quitar los circulos viejos de los fotogramas anteriores.

+ +
var canvas = document.getElementById('canvas');
+var ctx = canvas.getContext('2d');
+var raf;
+
+var ball = {
+  x: 100,
+  y: 100,
+  vx: 5,
+  vy: 2,
+  radius: 25,
+  color: 'blue',
+  draw: function() {
+    ctx.beginPath();
+    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
+    ctx.closePath();
+    ctx.fillStyle = this.color;
+    ctx.fill();
+  }
+};
+
+function draw() {
+  ctx.clearRect(0,0, canvas.width, canvas.height);
+  ball.draw();
+  ball.x += ball.vx;
+  ball.y += ball.vy;
+  raf = window.requestAnimationFrame(draw);
+}
+
+canvas.addEventListener('mouseover', function(e) {
+  raf = window.requestAnimationFrame(draw);
+});
+
+canvas.addEventListener('mouseout', function(e) {
+  window.cancelAnimationFrame(raf);
+});
+
+ball.draw();
+
+ +

Límites

+ +

Si no probamos los límites, de repente nuestra bola se agota el canvas. Necesitamos verificar si las posiciones x e y están fuera de las dimensiones del canvas y invertir la direción de los vectores de velocidad. Para hacer eso, agregamos los siguentes pasos al método draw:

+ +
if (ball.y + ball.vy > canvas.height || ball.y + ball.vy < 0) {
+  ball.vy = -ball.vy;
+}
+if (ball.x + ball.vx > canvas.width || ball.x + ball.vx < 0) {
+  ball.vx = -ball.vx;
+}
+ +

Primera demo

+ +

Veamos como se ve en acción hasta este punto. Dirige el ratón dentro del canvas para empezar la animación.

+ + + +

{{EmbedLiveSample("First_demo", "610", "310")}}

+ +

Aceleración

+ +

Para convertir la moción en más auténtica, puedes jugar con la velocidad, así por ejemplo:

+ +
ball.vy *= .99;
+ball.vy += .25;
+ +

Esto reduce el vector vertical de velocidad para cada fotograma para que la bola termina rebotando en el suelo.

+ + + +

{{EmbedLiveSample("Second_demo", "610", "310")}}

+ +

Efecto de rezagar

+ +

Hasta este punto hemos limpiado los fotogramas anteriores con el método {{domxref("CanvasRenderingContext2D.clearRect", "clearRect")}}. Si reemplazas este método con un semi-transparente {{domxref("CanvasRenderingContext2D.fillRect", "fillRect")}}, puedes facilmente crear un efecto de rezagar.

+ +
ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
+ctx.fillRect(0, 0, canvas.width, canvas.height);
+ + + +

{{EmbedLiveSample("Third_demo", "610", "310")}}

+ +

Agregar control de ratón

+ +

Para controlar la bola, podemos hacerla seguir nuestro ratón usando el evento mousemove, por ejemplo. El evento click solta la bola y la deja rebotar de nuevo.

+ + + +
var canvas = document.getElementById('canvas');
+var ctx = canvas.getContext('2d');
+var raf;
+var running = false;
+
+var ball = {
+  x: 100,
+  y: 100,
+  vx: 5,
+  vy: 1,
+  radius: 25,
+  color: 'blue',
+  draw: function() {
+    ctx.beginPath();
+    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
+    ctx.closePath();
+    ctx.fillStyle = this.color;
+    ctx.fill();
+  }
+};
+
+function clear() {
+  ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
+  ctx.fillRect(0,0,canvas.width,canvas.height);
+}
+
+function draw() {
+  clear();
+  ball.draw();
+  ball.x += ball.vx;
+  ball.y += ball.vy;
+
+  if (ball.y + ball.vy > canvas.height || ball.y + ball.vy < 0) {
+    ball.vy = -ball.vy;
+  }
+  if (ball.x + ball.vx > canvas.width || ball.x + ball.vx < 0) {
+    ball.vx = -ball.vx;
+  }
+
+  raf = window.requestAnimationFrame(draw);
+}
+
+canvas.addEventListener('mousemove', function(e) {
+  if (!running) {
+    clear();
+    ball.x = e.clientX;
+    ball.y = e.clientY;
+    ball.draw();
+  }
+});
+
+canvas.addEventListener('click', function(e) {
+  if (!running) {
+    raf = window.requestAnimationFrame(draw);
+    running = true;
+  }
+});
+
+canvas.addEventListener('mouseout', function(e) {
+  window.cancelAnimationFrame(raf);
+  running = false;
+});
+
+ball.draw();
+
+ +

Mueve la bola usando el ratón y suéltala haciendo click.

+ +

{{EmbedLiveSample("Adding_mouse_control", "610", "310")}}

+ +

Breakout

+ +

Este capítulo corto sólo explica algunas técnicas para crear animaciones más avanzadas. ¡Hay muchos más!  ¿Qué tal agregar una raqueta, algunos ladrillos, y convertir esta demo en un partido Breakout? Visita nuestra área de Game development para mayor información.

+ +

Vea también

+ + + +

{{PreviousNext("Web/API/Canvas_API/Tutorial/Basic_animations", "Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas")}}

diff --git a/files/es/web/api/canvas_api/tutorial/applying_styles_and_colors/index.html b/files/es/web/api/canvas_api/tutorial/applying_styles_and_colors/index.html new file mode 100644 index 0000000000..ab76918132 --- /dev/null +++ b/files/es/web/api/canvas_api/tutorial/applying_styles_and_colors/index.html @@ -0,0 +1,726 @@ +--- +title: Applying styles and colors +slug: Web/Guide/HTML/Canvas_tutorial/Applying_styles_and_colors +translation_of: Web/API/Canvas_API/Tutorial/Applying_styles_and_colors +--- +
{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Drawing_shapes", "Web/API/Canvas_API/Tutorial/Drawing_text")}}
+ +
+

En el capítulo acerca de dibujar formas, usamos unicamente los estilos de linea y de relleno por defecto. Aqui exploraremos las opciones del canvas que tenemos a nuestra disposición para hacer los dibujos un tanto más atractivos. Aprenderas como agregar diferentes colores, estilos de linea, gradiantes, patrones y sombras a tus dibujos.

+
+ +

Colors

+ +

Hasta ahrora nosotros solo me hemos visto metodos en el contexto de dibujo. Si quisieramos aplicar colores a las formas, hay dos importantes propiedades que podemos usar: fillStyle y strokeStyle.

+ +
+
{{domxref("CanvasRenderingContext2D.fillStyle", "fillStyle = color")}}
+
Configura el estilo cuando se rellenan las formas.
+
{{domxref("CanvasRenderingContext2D.strokeStyle", "strokeStyle = color")}}
+
Configura el estilo al contorno perimetral de las formas.
+
+ +

color es una cadena que representa  un CSS {{cssxref("<color>")}}, un objeto gradiante, o un objeto patron. Miraremos en objetos de gradientes y patrones mas tarde. Por defecto, el color del trazo y del relleno son configurados en negro (valor de color CSS #000000).

+ +
+

Nota:  Cuando configuras la propiedad  strokeStyle y/o fillStyle, el nuevo valor llega a ser el vador por defecto para los dibujos posteriores. Para cada forma que tu quieras en un color difrente, necesitaras reasignar las propiedades anteriores.

+
+ +

Las cadenas validas que tu pueden entrar deberian, segun la especificación, ser valores de  {{cssxref("<color>")}} CSS. En el siguiente ejemplo, describimos en mismo color.

+ +
// these all set the fillStyle to 'orange'
+
+ctx.fillStyle = 'orange';
+ctx.fillStyle = '#FFA500';
+ctx.fillStyle = 'rgb(255, 165, 0)';
+ctx.fillStyle = 'rgba(255, 165, 0, 1)';
+
+ +

A fillStyle example

+ +

En este ejemplo, usamos dos bucles for para dibujar una cuadrícula de rectangulos, cada uno de diferente color. La imagen resultante deberia ser similar a la de la screenhot. No hay nada demasiado espectacular en el proceso. Usamos dos variables i y j para generar un unico color RGB para cada celda cuadrada, modificando las componentes rojo y verde. El canal azul tiene un valor fijo. Modificando los canales, puedes generar todo tipo de paletas. Incrementando los paos, puedes lograr algo similar que se parezca a las paletas de colores que Photoshop usa.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  for (var i = 0; i < 6; i++) {
+    for (var j = 0; j < 6; j++) {
+      ctx.fillStyle = 'rgb(' + Math.floor(255 - 42.5 * i) + ', ' +
+                       Math.floor(255 - 42.5 * j) + ', 0)';
+      ctx.fillRect(j * 25, i * 25, 25, 25);
+    }
+  }
+}
+ + + +

The result looks like this:

+ +

{{EmbedLiveSample("A_fillStyle_example", 160, 160, "https://mdn.mozillademos.org/files/5417/Canvas_fillstyle.png")}}

+ +

A strokeStyle example

+ +

Este ejemplo es similar al de arriba, pero usa la propiedad  strokeStyle para cambiar el color del contorno de las formas. Usamos el método  arc() para dibujar circulos en lugar de celdas cuadradas.

+ +
  function draw() {
+    var ctx = document.getElementById('canvas').getContext('2d');
+    for (var i = 0; i < 6; i++) {
+      for (var j = 0; j < 6; j++) {
+        ctx.strokeStyle = 'rgb(0, ' + Math.floor(255 - 42.5 * i) + ', ' +
+                         Math.floor(255 - 42.5 * j) + ')';
+        ctx.beginPath();
+        ctx.arc(12.5 + j * 25, 12.5 + i * 25, 10, 0, Math.PI * 2, true);
+        ctx.stroke();
+      }
+    }
+  }
+
+ + + +

The result looks like this:

+ +

{{EmbedLiveSample("A_strokeStyle_example", "180", "180", "https://mdn.mozillademos.org/files/253/Canvas_strokestyle.png")}}

+ +

Transparency

+ +

Ademas de dibujar formas opacas en el canvas, podemos dibujar formas semi-transparentes(o translucidas). Esto se logra bien configurando la propiedad  globalAlpha o asignando un color semi-transparente al estilo del trazo u  u/y al de relleno.

+ +
+
{{domxref("CanvasRenderingContext2D.globalAlpha", "globalAlpha = transparencyValue")}}
+
Applies the specified transparency value to all future shapes drawn on the canvas. The value must be between 0.0 (fully transparent) to 1.0 (fully opaque). This value is 1.0 (fully opaque) by default.
+
+ +

The globalAlpha property can be useful if you want to draw a lot of shapes on the canvas with similar transparency, but otherwise it's generally more useful to set the transparency on individual shapes when setting their colors.

+ +

Debido qaque las propiedades  strokeStyle y fillStyle aceptan valores de color rgba de CSS, podemos usar la siguiente notacion para asignar una color transparente a ellos.

+ +
// Assigning transparent colors to stroke and fill style
+
+ctx.strokeStyle = 'rgba(255, 0, 0, 0.5)';
+ctx.fillStyle = 'rgba(255, 0, 0, 0.5)';
+
+ +

The rgba() function is similar to the rgb() function but it has one extra parameter. The last parameter sets the transparency value of this particular color. The valid range is again between 0.0 (fully transparent) and 1.0 (fully opaque).

+ +

A globalAlpha example

+ +

In this example, we'll draw a background of four different colored squares. On top of these, we'll draw a set of semi-transparent circles. The globalAlpha property is set at 0.2 which will be used for all shapes from that point on. Every step in the for loop draws a set of circles with an increasing radius. The final result is a radial gradient. By overlaying ever more circles on top of each other, we effectively reduce the transparency of the circles that have already been drawn. By increasing the step count and in effect drawing more circles, the background would completely disappear from the center of the image.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  // draw background
+  ctx.fillStyle = '#FD0';
+  ctx.fillRect(0, 0, 75, 75);
+  ctx.fillStyle = '#6C0';
+  ctx.fillRect(75, 0, 75, 75);
+  ctx.fillStyle = '#09F';
+  ctx.fillRect(0, 75, 75, 75);
+  ctx.fillStyle = '#F30';
+  ctx.fillRect(75, 75, 75, 75);
+  ctx.fillStyle = '#FFF';
+
+  // set transparency value
+  ctx.globalAlpha = 0.2;
+
+  // Draw semi transparent circles
+  for (var i = 0; i < 7; i++) {
+    ctx.beginPath();
+    ctx.arc(75, 75, 10 + 10 * i, 0, Math.PI * 2, true);
+    ctx.fill();
+  }
+}
+ + + +

{{EmbedLiveSample("A_globalAlpha_example", "180", "180", "https://mdn.mozillademos.org/files/232/Canvas_globalalpha.png")}}

+ +

An example using rgba()

+ +

In this second example, we do something similar to the one above, but instead of drawing circles on top of each other, I've drawn small rectangles with increasing opacity. Using rgba() gives you a little more control and flexibility because we can set the fill and stroke style individually.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+
+  // Draw background
+  ctx.fillStyle = 'rgb(255, 221, 0)';
+  ctx.fillRect(0, 0, 150, 37.5);
+  ctx.fillStyle = 'rgb(102, 204, 0)';
+  ctx.fillRect(0, 37.5, 150, 37.5);
+  ctx.fillStyle = 'rgb(0, 153, 255)';
+  ctx.fillRect(0, 75, 150, 37.5);
+  ctx.fillStyle = 'rgb(255, 51, 0)';
+  ctx.fillRect(0, 112.5, 150, 37.5);
+
+  // Draw semi transparent rectangles
+  for (var i = 0; i < 10; i++) {
+    ctx.fillStyle = 'rgba(255, 255, 255, ' + (i + 1) / 10 + ')';
+    for (var j = 0; j < 4; j++) {
+      ctx.fillRect(5 + i * 14, 5 + j * 37.5, 14, 27.5);
+    }
+  }
+}
+ + + +

{{EmbedLiveSample("An_example_using_rgba()", "180", "180", "https://mdn.mozillademos.org/files/246/Canvas_rgba.png")}}

+ +

Line styles

+ +

There are several properties which allow us to style lines.

+ +
+
{{domxref("CanvasRenderingContext2D.lineWidth", "lineWidth = value")}}
+
Sets the width of lines drawn in the future.
+
{{domxref("CanvasRenderingContext2D.lineCap", "lineCap = type")}}
+
Sets the appearance of the ends of lines.
+
{{domxref("CanvasRenderingContext2D.lineJoin", "lineJoin = type")}}
+
Sets the appearance of the "corners" where lines meet.
+
{{domxref("CanvasRenderingContext2D.miterLimit", "miterLimit = value")}}
+
Establishes a limit on the miter when two lines join at a sharp angle, to let you control how thick the junction becomes.
+
{{domxref("CanvasRenderingContext2D.getLineDash", "getLineDash()")}}
+
Returns the current line dash pattern array containing an even number of non-negative numbers.
+
{{domxref("CanvasRenderingContext2D.setLineDash", "setLineDash(segments)")}}
+
Sets the current line dash pattern.
+
{{domxref("CanvasRenderingContext2D.lineDashOffset", "lineDashOffset = value")}}
+
Specifies where to start a dash array on a line.
+
+ +

You'll get a better understanding of what these do by looking at the examples below.

+ +

A lineWidth example

+ +

This property sets the current line thickness. Values must be positive numbers. By default this value is set to 1.0 units.

+ +

The line width is the thickness of the stroke centered on the given path. In other words, the area that's drawn extends to half the line width on either side of the path. Because canvas coordinates do not directly reference pixels, special care must be taken to obtain crisp horizontal and vertical lines.

+ +

In the example below, 10 straight lines are drawn with increasing line widths. The line on the far left is 1.0 units wide. However, the leftmost and all other odd-integer-width thickness lines do not appear crisp, because of the path's positioning.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  for (var i = 0; i < 10; i++) {
+    ctx.lineWidth = 1 + i;
+    ctx.beginPath();
+    ctx.moveTo(5 + i * 14, 5);
+    ctx.lineTo(5 + i * 14, 140);
+    ctx.stroke();
+  }
+}
+
+ + + +

{{EmbedLiveSample("A_lineWidth_example", "180", "180", "https://mdn.mozillademos.org/files/239/Canvas_linewidth.png")}}

+ +

Obtaining crisp lines requires understanding how paths are stroked. In the images below, the grid represents the canvas coordinate grid. The squares between gridlines are actual on-screen pixels. In the first grid image below, a rectangle from (2,1) to (5,5) is filled. The entire area between them (light red) falls on pixel boundaries, so the resulting filled rectangle will have crisp edges.

+ +

+ +

If you consider a path from (3,1) to (3,5) with a line thickness of 1.0, you end up with the situation in the second image. The actual area to be filled (dark blue) only extends halfway into the pixels on either side of the path. An approximation of this has to be rendered, which means that those pixels being only partially shaded, and results in the entire area (the light blue and dark blue) being filled in with a color only half as dark as the actual stroke color. This is what happens with the 1.0 width line in the previous example code.

+ +

To fix this, you have to be very precise in your path creation. Knowing that a 1.0 width line will extend half a unit to either side of the path, creating the path from (3.5,1) to (3.5,5) results in the situation in the third image—the 1.0 line width ends up completely and precisely filling a single pixel vertical line.

+ +
+

Note: Be aware that in our vertical line example, the Y position still referenced an integer gridline position—if it hadn't, we would see pixels with half coverage at the endpoints (but note also that this behavior depends on the current lineCap style whose default value is butt; you may want to compute consistent strokes with half-pixel coordinates for odd-width lines, by setting the lineCap style to square, so that the outer border of the stroke around the endpoint will be automatically extended to cover the whole pixel exactly).

+ +

Note also that only start and final endpoints of a path are affected: if a path is closed with closePath(), there's no start and final endpoint; instead, all endpoints in the path are connected to their attached previous and next segment using the current setting of the lineJoin style, whose default value is miter, with the effect of automatically extending the outer borders of the connected segments to their intersection point, so that the rendered stroke will exactly cover full pixels centered at each endpoint if those connected segments are horizontal and/or vertical). See the next two sections for demonstrations of these additional line styles.

+
+ +

For even-width lines, each half ends up being an integer amount of pixels, so you want a path that is between pixels (that is, (3,1) to (3,5)), instead of down the middle of pixels.

+ +

While slightly painful when initially working with scalable 2D graphics, paying attention to the pixel grid and the position of paths ensures that your drawings will look correct regardless of scaling or any other transformations involved. A 1.0-width vertical line drawn at the correct position will become a crisp 2-pixel line when scaled up by 2, and will appear at the correct position.

+ +

A lineCap example

+ +

The lineCap property determines how the end points of every line are drawn. There are three possible values for this property and those are: butt, round and square. By default this property is set to butt.

+ +

+ +
+
butt
+
The ends of lines are squared off at the endpoints.
+
round
+
The ends of lines are rounded.
+
square
+
The ends of lines are squared off by adding a box with an equal width and half the height of the line's thickness.
+
+ +

In this example, we'll draw three lines, each with a different value for the lineCap property. I also added two guides to see the exact differences between the three. Each of these lines starts and ends exactly on these guides.

+ +

The line on the left uses the default butt option. You'll notice that it's drawn completely flush with the guides. The second is set to use the round option. This adds a semicircle to the end that has a radius half the width of the line. The line on the right uses the square option. This adds a box with an equal width and half the height of the line thickness.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  var lineCap = ['butt', 'round', 'square'];
+
+  // Draw guides
+  ctx.strokeStyle = '#09f';
+  ctx.beginPath();
+  ctx.moveTo(10, 10);
+  ctx.lineTo(140, 10);
+  ctx.moveTo(10, 140);
+  ctx.lineTo(140, 140);
+  ctx.stroke();
+
+  // Draw lines
+  ctx.strokeStyle = 'black';
+  for (var i = 0; i < lineCap.length; i++) {
+    ctx.lineWidth = 15;
+    ctx.lineCap = lineCap[i];
+    ctx.beginPath();
+    ctx.moveTo(25 + i * 50, 10);
+    ctx.lineTo(25 + i * 50, 140);
+    ctx.stroke();
+  }
+}
+
+ + + +

{{EmbedLiveSample("A_lineCap_example", "180", "180", "https://mdn.mozillademos.org/files/236/Canvas_linecap.png")}}

+ +

A lineJoin example

+ +

The lineJoin property determines how two connecting segments (of lines, arcs or curves) with non-zero lengths in a shape are joined together (degenerate segments with zero lengths, whose specified endpoints and control points are exactly at the same position, are skipped).

+ +

There are three possible values for this property: round, bevel and miter. By default this property is set to miter. Note that the lineJoin setting has no effect if the two connected segments have the same direction, because no joining area will be added in this case.

+ +

+ +
+
round
+
Rounds off the corners of a shape by filling an additional sector of disc centered at the common endpoint of connected segments. The radius for these rounded corners is equal to half the line width.
+
bevel
+
Fills an additional triangular area between the common endpoint of connected segments, and the separate outside rectangular corners of each segment.
+
miter
+
Connected segments are joined by extending their outside edges to connect at a single point, with the effect of filling an additional lozenge-shaped area. This setting is effected by the miterLimit property which is explained below.
+
+ +

The example below draws three different paths, demonstrating each of these three lineJoin property settings; the output is shown above.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  var lineJoin = ['round', 'bevel', 'miter'];
+  ctx.lineWidth = 10;
+  for (var i = 0; i < lineJoin.length; i++) {
+    ctx.lineJoin = lineJoin[i];
+    ctx.beginPath();
+    ctx.moveTo(-5, 5 + i * 40);
+    ctx.lineTo(35, 45 + i * 40);
+    ctx.lineTo(75, 5 + i * 40);
+    ctx.lineTo(115, 45 + i * 40);
+    ctx.lineTo(155, 5 + i * 40);
+    ctx.stroke();
+  }
+}
+
+ + + +

{{EmbedLiveSample("A_lineJoin_example", "180", "180", "https://mdn.mozillademos.org/files/237/Canvas_linejoin.png")}}

+ +

A demo of the miterLimit property

+ +

As you've seen in the previous example, when joining two lines with the miter option, the outside edges of the two joining lines are extended up to the point where they meet. For lines which are at large angles with each other, this point is not far from the inside connection point. However, as the angles between each line decreases, the distance (miter length) between these points increases exponentially.

+ +

The miterLimit property determines how far the outside connection point can be placed from the inside connection point. If two lines exceed this value, a bevel join gets drawn instead. Note that the maximum miter length is the product of the line width measured in the current coordinate system, by the value of this miterLimit property (whose default value is 10.0 in the HTML {{HTMLElement("canvas")}}), so the miterLimit can be set independently from the current display scale or any affine transforms of paths: it only influences the effectively rendered shape of line edges.

+ +

More exactly, the miter limit is the maximum allowed ratio of the extension length (in the HTML canvas, it is measured between the outside corner of the joined edges of the line and the common endpoint of connecting segments specified in the path) to half the line width. It can equivalently be defined as the maximum allowed ratio of the distance between the inside and outside points of jonction of edges, to the total line width. It is then equal to the cosecant of half the minimum inner angle of connecting segments below which no miter join will be rendered, but only a bevel join:

+ + + +

Here's a little demo in which you can set miterLimit dynamically and see how this effects the shapes on the canvas. The blue lines show where the start and endpoints for each of the lines in the zig-zag pattern are.

+ +

If you specify a miterLimit value below 4.2 in this demo, none of the visible corners will join with a miter extension, but only with a small bevel near the blue lines; with a miterLimit above 10, most corners in this demo should join with a miter far away from the blue lines, and whose height is decreasing between corners from left to right because they connect with growing angles; with intermediate values, the corners on the left side will only join with a bevel near the blue lines, and the corners on the right side with a miter extension (also with a decreasing height).

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+
+  // Clear canvas
+  ctx.clearRect(0, 0, 150, 150);
+
+  // Draw guides
+  ctx.strokeStyle = '#09f';
+  ctx.lineWidth   = 2;
+  ctx.strokeRect(-5, 50, 160, 50);
+
+  // Set line styles
+  ctx.strokeStyle = '#000';
+  ctx.lineWidth = 10;
+
+  // check input
+  if (document.getElementById('miterLimit').value.match(/\d+(\.\d+)?/)) {
+    ctx.miterLimit = parseFloat(document.getElementById('miterLimit').value);
+  } else {
+    alert('Value must be a positive number');
+  }
+
+  // Draw lines
+  ctx.beginPath();
+  ctx.moveTo(0, 100);
+  for (i = 0; i < 24 ; i++) {
+    var dy = i % 2 == 0 ? 25 : -25;
+    ctx.lineTo(Math.pow(i, 1.5) * 2, 75 + dy);
+  }
+  ctx.stroke();
+  return false;
+}
+
+ + + +

{{EmbedLiveSample("A_demo_of_the_miterLimit_property", "400", "180", "https://mdn.mozillademos.org/files/240/Canvas_miterlimit.png")}}

+ +

Using line dashes

+ +

The setLineDash method and the lineDashOffset property specify the dash pattern for lines. The setLineDash method accepts a list of numbers that specifies distances to alternately draw a line and a gap and the lineDashOffset property sets an offset where to start the pattern.

+ +

In this example we are creating a marching ants effect. It is an animation technique often found in selection tools of computer graphics programs. It helps the user to distinguish the selection border from the image background by animating the border. In a later part of this tutorial, you can learn how to do this and other basic animations.

+ + + +
var ctx = document.getElementById('canvas').getContext('2d');
+var offset = 0;
+
+function draw() {
+  ctx.clearRect(0, 0, canvas.width, canvas.height);
+  ctx.setLineDash([4, 2]);
+  ctx.lineDashOffset = -offset;
+  ctx.strokeRect(10, 10, 100, 100);
+}
+
+function march() {
+  offset++;
+  if (offset > 16) {
+    offset = 0;
+  }
+  draw();
+  setTimeout(march, 20);
+}
+
+march();
+ +

{{EmbedLiveSample("Using_line_dashes", "120", "120", "https://mdn.mozillademos.org/files/9853/marching-ants.png")}}

+ +

Gradients

+ +

Just like any normal drawing program, we can fill and stroke shapes using linear and radial gradients. We create a {{domxref("CanvasGradient")}} object by using one of the following methods. We can then assign this object to the fillStyle or strokeStyle properties.

+ +
+
{{domxref("CanvasRenderingContext2D.createLinearGradient", "createLinearGradient(x1, y1, x2, y2)")}}
+
Creates a linear gradient object with a starting point of (x1, y1) and an end point of (x2, y2).
+
{{domxref("CanvasRenderingContext2D.createRadialGradient", "createRadialGradient(x1, y1, r1, x2, y2, r2)")}}
+
Creates a radial gradient. The parameters represent two circles, one with its center at (x1, y1) and a radius of r1, and the other with its center at (x2, y2) with a radius of r2.
+
+ +

For example:

+ +
var lineargradient = ctx.createLinearGradient(0, 0, 150, 150);
+var radialgradient = ctx.createRadialGradient(75, 75, 0, 75, 75, 100);
+
+ +

Once we've created a CanvasGradient object we can assign colors to it by using the addColorStop() method.

+ +
+
{{domxref("CanvasGradient.addColorStop", "gradient.addColorStop(position, color)")}}
+
Creates a new color stop on the gradient object. The position is a number between 0.0 and 1.0 and defines the relative position of the color in the gradient, and the color argument must be a string representing a CSS {{cssxref("<color>")}}, indicating the color the gradient should reach at that offset into the transition.
+
+ +

You can add as many color stops to a gradient as you need. Below is a very simple linear gradient from white to black.

+ +
var lineargradient = ctx.createLinearGradient(0, 0, 150, 150);
+lineargradient.addColorStop(0, 'white');
+lineargradient.addColorStop(1, 'black');
+
+ +

A createLinearGradient example

+ +

In this example, we'll create two different gradients. As you can see here, both the strokeStyle and fillStyle properties can accept a canvasGradient object as valid input.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+
+  // Create gradients
+  var lingrad = ctx.createLinearGradient(0, 0, 0, 150);
+  lingrad.addColorStop(0, '#00ABEB');
+  lingrad.addColorStop(0.5, '#fff');
+  lingrad.addColorStop(0.5, '#26C000');
+  lingrad.addColorStop(1, '#fff');
+
+  var lingrad2 = ctx.createLinearGradient(0, 50, 0, 95);
+  lingrad2.addColorStop(0.5, '#000');
+  lingrad2.addColorStop(1, 'rgba(0, 0, 0, 0)');
+
+  // assign gradients to fill and stroke styles
+  ctx.fillStyle = lingrad;
+  ctx.strokeStyle = lingrad2;
+
+  // draw shapes
+  ctx.fillRect(10, 10, 130, 130);
+  ctx.strokeRect(50, 50, 50, 50);
+
+}
+
+ + + +

The first is a background gradient. As you can see, we assigned two colors at the same position. You do this to make very sharp color transitions—in this case from white to green. Normally, it doesn't matter in what order you define the color stops, but in this special case, it does significantly. If you keep the assignments in the order you want them to appear, this won't be a problem.

+ +

In the second gradient, we didn't assign the starting color (at position 0.0) since it wasn't strictly necessary, because it will automatically assume the color of the next color stop. Therefore, assigning the black color at position 0.5 automatically makes the gradient, from the start to this stop, black.

+ +

{{EmbedLiveSample("A_createLinearGradient_example", "180", "180", "https://mdn.mozillademos.org/files/235/Canvas_lineargradient.png")}}

+ +

A createRadialGradient example

+ +

In this example, we'll define four different radial gradients. Because we have control over the start and closing points of the gradient, we can achieve more complex effects than we would normally have in the "classic" radial gradients we see in, for instance, Photoshop (that is, a gradient with a single center point where the gradient expands outward in a circular shape).

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+
+  // Create gradients
+  var radgrad = ctx.createRadialGradient(45, 45, 10, 52, 50, 30);
+  radgrad.addColorStop(0, '#A7D30C');
+  radgrad.addColorStop(0.9, '#019F62');
+  radgrad.addColorStop(1, 'rgba(1, 159, 98, 0)');
+
+  var radgrad2 = ctx.createRadialGradient(105, 105, 20, 112, 120, 50);
+  radgrad2.addColorStop(0, '#FF5F98');
+  radgrad2.addColorStop(0.75, '#FF0188');
+  radgrad2.addColorStop(1, 'rgba(255, 1, 136, 0)');
+
+  var radgrad3 = ctx.createRadialGradient(95, 15, 15, 102, 20, 40);
+  radgrad3.addColorStop(0, '#00C9FF');
+  radgrad3.addColorStop(0.8, '#00B5E2');
+  radgrad3.addColorStop(1, 'rgba(0, 201, 255, 0)');
+
+  var radgrad4 = ctx.createRadialGradient(0, 150, 50, 0, 140, 90);
+  radgrad4.addColorStop(0, '#F4F201');
+  radgrad4.addColorStop(0.8, '#E4C700');
+  radgrad4.addColorStop(1, 'rgba(228, 199, 0, 0)');
+
+  // draw shapes
+  ctx.fillStyle = radgrad4;
+  ctx.fillRect(0, 0, 150, 150);
+  ctx.fillStyle = radgrad3;
+  ctx.fillRect(0, 0, 150, 150);
+  ctx.fillStyle = radgrad2;
+  ctx.fillRect(0, 0, 150, 150);
+  ctx.fillStyle = radgrad;
+  ctx.fillRect(0, 0, 150, 150);
+}
+
+ + + +

In this case, we've offset the starting point slightly from the end point to achieve a spherical 3D effect. It's best to try to avoid letting the inside and outside circles overlap because this results in strange effects which are hard to predict.

+ +

The last color stop in each of the four gradients uses a fully transparent color. If you want to have a nice transition from this to the previous color stop, both colors should be equal. This isn't very obvious from the code because it uses two different CSS color methods as a demonstration, but in the first gradient #019F62 = rgba(1,159,98,1).

+ +

{{EmbedLiveSample("A_createRadialGradient_example", "180", "180", "https://mdn.mozillademos.org/files/244/Canvas_radialgradient.png")}}

+ +

Patterns

+ +

In one of the examples on the previous page, we used a series of loops to create a pattern of images. There is, however, a much simpler method: the createPattern() method.

+ +
+
{{domxref("CanvasRenderingContext2D.createPattern", "createPattern(image, type)")}}
+
Creates and returns a new canvas pattern object. image is a {{domxref("CanvasImageSource")}} (that is, an {{domxref("HTMLImageElement")}}, another canvas, a {{HTMLElement("video")}} element, or the like. type is a string indicating how to use the image.
+
+ +

The type specifies how to use the image in order to create the pattern, and must be one of the following string values:

+ +
+
repeat
+
Tiles the image in both vertical and horizontal directions.
+
repeat-x
+
Tiles the image horizontally but not vertically.
+
repeat-y
+
Tiles the image vertically but not horizontally.
+
no-repeat
+
Doesn't tile the image. It's used only once.
+
+ +

We use this method to create a {{domxref("CanvasPattern")}} object which is very similar to the gradient methods we've seen above. Once we've created a pattern, we can assign it to the fillStyle or strokeStyle properties. For example:

+ +
var img = new Image();
+img.src = 'someimage.png';
+var ptrn = ctx.createPattern(img, 'repeat');
+
+ +
+

Note: Like with the drawImage() method, you must make sure the image you use is loaded before calling this method or the pattern may be drawn incorrectly.

+
+ +

A createPattern example

+ +

In this last example, we'll create a pattern to assign to the fillStyle property. The only thing worth noting is the use of the image's onload handler. This is to make sure the image is loaded before it is assigned to the pattern.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+
+  // create new image object to use as pattern
+  var img = new Image();
+  img.src = 'https://mdn.mozillademos.org/files/222/Canvas_createpattern.png';
+  img.onload = function() {
+
+    // create pattern
+    var ptrn = ctx.createPattern(img, 'repeat');
+    ctx.fillStyle = ptrn;
+    ctx.fillRect(0, 0, 150, 150);
+
+  }
+}
+
+ + + +

{{EmbedLiveSample("A_createPattern_example", "180", "180", "https://mdn.mozillademos.org/files/222/Canvas_createpattern.png")}}

+ +

Shadows

+ +

Using shadows involves just four properties:

+ +
+
{{domxref("CanvasRenderingContext2D.shadowOffsetX", "shadowOffsetX = float")}}
+
Indicates the horizontal distance the shadow should extend from the object. This value isn't affected by the transformation matrix. The default is 0.
+
{{domxref("CanvasRenderingContext2D.shadowOffsetY", "shadowOffsetY = float")}}
+
Indicates the vertical distance the shadow should extend from the object. This value isn't affected by the transformation matrix. The default is 0.
+
{{domxref("CanvasRenderingContext2D.shadowBlur", "shadowBlur = float")}}
+
Indicates the size of the blurring effect; this value doesn't correspond to a number of pixels and is not affected by the current transformation matrix. The default value is 0.
+
{{domxref("CanvasRenderingContext2D.shadowColor", "shadowColor = color")}}
+
A standard CSS color value indicating the color of the shadow effect; by default, it is fully-transparent black.
+
+ +

The properties shadowOffsetX and shadowOffsetY indicate how far the shadow should extend from the object in the X and Y directions; these values aren't affected by the current transformation matrix. Use negative values to cause the shadow to extend up or to the left, and positive values to cause the shadow to extend down or to the right. These are both 0 by default.

+ +

The shadowBlur property indicates the size of the blurring effect; this value doesn't correspond to a number of pixels and is not affected by the current transformation matrix. The default value is 0.

+ +

The shadowColor property is a standard CSS color value indicating the color of the shadow effect; by default, it is fully-transparent black.

+ +
+

Note: Shadows are only drawn for source-over compositing operations.

+
+ +

A shadowed text example

+ +

This example draws a text string with a shadowing effect.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+
+  ctx.shadowOffsetX = 2;
+  ctx.shadowOffsetY = 2;
+  ctx.shadowBlur = 2;
+  ctx.shadowColor = 'rgba(0, 0, 0, 0.5)';
+
+  ctx.font = '20px Times New Roman';
+  ctx.fillStyle = 'Black';
+  ctx.fillText('Sample String', 5, 30);
+}
+
+ + + +

{{EmbedLiveSample("A_shadowed_text_example", "180", "100", "https://mdn.mozillademos.org/files/2505/shadowed-string.png")}}

+ +

We will look at the font property and fillText method in the next chapter about drawing text.

+ +

Canvas fill rules

+ +

When using fill (or {{domxref("CanvasRenderingContext2D.clip", "clip")}} and {{domxref("CanvasRenderingContext2D.isPointInPath", "isPointinPath")}}) you can optionally provide a fill rule algorithm by which to determine if a point is inside or outside a path and thus if it gets filled or not. This is useful when a path intersects itself or is nested.
+
+ Two values are possible:

+ + + +

In this example we are using the evenodd rule.

+ +
function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+  ctx.beginPath();
+  ctx.arc(50, 50, 30, 0, Math.PI * 2, true);
+  ctx.arc(50, 50, 15, 0, Math.PI * 2, true);
+  ctx.fill('evenodd');
+}
+ + + +

{{EmbedLiveSample("Canvas_fill_rules", "110", "110", "https://mdn.mozillademos.org/files/9855/fill-rule.png")}}

+ +

{{PreviousNext("Web/API/Canvas_API/Tutorial/Drawing_shapes", "Web/API/Canvas_API/Tutorial/Drawing_text")}}

diff --git a/files/es/web/api/canvas_api/tutorial/basic_animations/index.html b/files/es/web/api/canvas_api/tutorial/basic_animations/index.html new file mode 100644 index 0000000000..94c66fb05d --- /dev/null +++ b/files/es/web/api/canvas_api/tutorial/basic_animations/index.html @@ -0,0 +1,333 @@ +--- +title: Animaciones básicas +slug: Web/Guide/HTML/Canvas_tutorial/Basic_animations +tags: + - Canvas + - HTML5 + - Intermedio + - Tutorial + - graficos +translation_of: Web/API/Canvas_API/Tutorial/Basic_animations +--- +
{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Compositing", "Web/API/Canvas_API/Tutorial/Advanced_animations")}}
+ +
+

Ya que estamos usando JavaScript para controlar elementos {{HTMLElement("canvas")}} , también es muy fácil hacer animaciones (interactivas). En este capitulo veremos como hacer algunas animaciones básicas.

+
+ +

Probablemente la mayor limitación es que una vez que se dibuja una forma, se mantiene de esa manera. Si necesitamos moverlo tenemos que volver a dibujarlo y todo lo que se dibujó antes. Se necesita mucho tiempo para volver a dibujar estructuras complejas y el rendimiento depende en gran medida de la velocidad de la computadora en la que se ejecuta.

+ +

Pasos básicos de animación

+ +

Estos son los pasos que necesitas para dibujar un cuadro:

+ +
    +
  1. Limpiar el canvas
    + A menos que las formas que vas a dibujar llenen el canvas completo (por ejemplo, una imagen de fondo), debes borrar cualquier forma que haya dibujado previamente. La forma más fácil de hacerlo es usar el método {{domxref("CanvasRenderingContext2D.clearRect", "clearRect()")}}.
  2. +
  3. Guardar el estado del canvas
    + Si estás cambiando alguna configuración (como estilos, transformaciones, etc.) que afecte el estado del canvas y deseas asegurarte de que se utiliza el estado original cada vez que se dibuja una figura, debes guardar ese estado original. 
  4. +
  5. Dibujar formas animadas
    + El paso en el que realizas el renderizado del cuadro actual.
  6. +
  7. Restaurar el estado del canvas
    + Si has guardado el estado, restáuralo antes de dibujar un nuevo cuadro.
  8. +
+ +

Controlando una animación

+ +

Las formas se dibujan en el canvas utilizando los métodos de canvas directamente o llamando a funciones personalizadas. En circunstancias normales, solo vemos que estos resultados aparecen en el canvas cuando el script termina de ejecutarse. Por ejemplo, no es posible hacer una animación desde un bucle for.

+ +

Eso significa que necesitamos una forma de ejecutar nuestras funciones de dibujo durante un período de tiempo. Hay dos formas de controlar una animación como esta.

+ +

Actualizaciones Programadas

+ +

Primero {{domxref("window.setInterval()")}}, {{domxref("window.setTimeout()")}}, y {{domxref("window.requestAnimationFrame()")}} son funciones que pueden ser usadas para llamar una función especifica en un periodo de tiempo establecido.

+ +
+
{{domxref("WindowTimers.setInterval", "setInterval(function, delay)")}}
+
Ejecuta una función especificada por function cada delay milisegundos.
+
{{domxref("WindowTimers.setTimeout", "setTimeout(function, delay)")}}
+
Ejecuta una función especificada por function dentro de delay milisegundos.
+
{{domxref("Window.requestAnimationFrame()", "requestAnimationFrame(callback)")}}
+
Comunica al navegador que  deseas iniciar una animación y requieres que el navegador llame a las funciones especificas para actualizar la misma antes de la siguiente escena.
+
+ +

Si no quieres ninguna interacción del usuario puedes usar la función setInterval() que repite la ejecución del código suministrado. Si lo que queremos es hacer un juego, podríamos usar eventos de teclado o el mouse para controlar la animación y usar setTimeout(). Al establecer los {{domxref("EventListener")}}, capturamos cualquier interacción del usuario y ejecutamos nuestras funciones de animación.

+ +
+

En los siguiente ejemplo,usaremos el método para controlar animaciones {{domxref("window.requestAnimationFrame()")}}. El método requestAnimationFrame provee formas amigables y mas eficientes  para animar llamando cada marco de animación cuando el sistema esta listo para dibujar. La cantidad de devoluciones de llamadas suele ser 60 veces por segundo y podría ser reducido a menor periodo cuando se corre en un segundo plano. Para mas información acerca de los ciclos de animación, especialmente para juegos, Ver el Articulo Anatomía de un videojuego en nuestra GameZona de desarrollo de Juegos.

+
+ +

Un sistema solar animado

+ +

Este ejemplo animado es un pequeño modelo de nuestro sistema solar.

+ +
var sun = new Image();
+var moon = new Image();
+var earth = new Image();
+function init(){
+  sun.src = 'https://mdn.mozillademos.org/files/1456/Canvas_sun.png';
+  moon.src = 'https://mdn.mozillademos.org/files/1443/Canvas_moon.png';
+  earth.src = 'https://mdn.mozillademos.org/files/1429/Canvas_earth.png';
+  window.requestAnimationFrame(draw);
+}
+
+function draw() {
+  var ctx = document.getElementById('canvas').getContext('2d');
+
+  ctx.globalCompositeOperation = 'destination-over';
+  ctx.clearRect(0,0,300,300); // limpiar canvas
+
+  ctx.fillStyle = 'rgba(0,0,0,0.4)';
+  ctx.strokeStyle = 'rgba(0,153,255,0.4)';
+  ctx.save();
+  ctx.translate(150,150);
+
+  // La tierra
+  var time = new Date();
+  ctx.rotate( ((2*Math.PI)/60)*time.getSeconds() + ((2*Math.PI)/60000)*time.getMilliseconds() );
+  ctx.translate(105,0);
+  ctx.fillRect(0,-12,50,24); // Sombra
+  ctx.drawImage(earth,-12,-12);
+
+  // La luna
+  ctx.save();
+  ctx.rotate( ((2*Math.PI)/6)*time.getSeconds() + ((2*Math.PI)/6000)*time.getMilliseconds() );
+  ctx.translate(0,28.5);
+  ctx.drawImage(moon,-3.5,-3.5);
+  ctx.restore();
+
+  ctx.restore();
+
+  ctx.beginPath();
+  ctx.arc(150,150,105,0,Math.PI*2,false); // Órbita terrestre
+  ctx.stroke();
+
+  ctx.drawImage(sun,0,0,300,300);
+
+  window.requestAnimationFrame(draw);
+}
+
+init();
+
+ + + +

{{EmbedLiveSample("Un_sistema_solar_animado", "310", "310", "https://mdn.mozillademos.org/files/202/Canvas_animation1.png")}}

+ +

Un reloj animado

+ +

Este ejemplo dibuja una reloj animado, mostrando la hora actual.

+ +
function clock(){
+  var now = new Date();
+  var ctx = document.getElementById('canvas').getContext('2d');
+  ctx.save();
+  ctx.clearRect(0,0,150,150);
+  ctx.translate(75,75);
+  ctx.scale(0.4,0.4);
+  ctx.rotate(-Math.PI/2);
+  ctx.strokeStyle = "black";
+  ctx.fillStyle = "white";
+  ctx.lineWidth = 8;
+  ctx.lineCap = "round";
+
+  // Aguja de la hora
+  ctx.save();
+  for (var i=0;i<12;i++){
+    ctx.beginPath();
+    ctx.rotate(Math.PI/6);
+    ctx.moveTo(100,0);
+    ctx.lineTo(120,0);
+    ctx.stroke();
+  }
+  ctx.restore();
+
+  // Aguja del minuto
+  ctx.save();
+  ctx.lineWidth = 5;
+  for (i=0;i<60;i++){
+    if (i%5!=0) {
+      ctx.beginPath();
+      ctx.moveTo(117,0);
+      ctx.lineTo(120,0);
+      ctx.stroke();
+    }
+    ctx.rotate(Math.PI/30);
+  }
+  ctx.restore();
+
+  var sec = now.getSeconds();
+  var min = now.getMinutes();
+  var hr  = now.getHours();
+  hr = hr>=12 ? hr-12 : hr;
+
+  ctx.fillStyle = "black";
+
+  // Escribimos la hora
+  ctx.save();
+  ctx.rotate( hr*(Math.PI/6) + (Math.PI/360)*min + (Math.PI/21600)*sec )
+  ctx.lineWidth = 14;
+  ctx.beginPath();
+  ctx.moveTo(-20,0);
+  ctx.lineTo(80,0);
+  ctx.stroke();
+  ctx.restore();
+
+  // escribimos los minutos
+  ctx.save();
+  ctx.rotate( (Math.PI/30)*min + (Math.PI/1800)*sec )
+  ctx.lineWidth = 10;
+  ctx.beginPath();
+  ctx.moveTo(-28,0);
+  ctx.lineTo(112,0);
+  ctx.stroke();
+  ctx.restore();
+
+  // escribimos los segundos
+  ctx.save();
+  ctx.rotate(sec * Math.PI/30);
+  ctx.strokeStyle = "#D40000";
+  ctx.fillStyle = "#D40000";
+  ctx.lineWidth = 6;
+  ctx.beginPath();
+  ctx.moveTo(-30,0);
+  ctx.lineTo(83,0);
+  ctx.stroke();
+  ctx.beginPath();
+  ctx.arc(0,0,10,0,Math.PI*2,true);
+  ctx.fill();
+  ctx.beginPath();
+  ctx.arc(95,0,10,0,Math.PI*2,true);
+  ctx.stroke();
+  ctx.fillStyle = "rgba(0,0,0,0)";
+  ctx.arc(0,0,3,0,Math.PI*2,true);
+  ctx.fill();
+  ctx.restore();
+
+  ctx.beginPath();
+  ctx.lineWidth = 14;
+  ctx.strokeStyle = '#325FA2';
+  ctx.arc(0,0,142,0,Math.PI*2,true);
+  ctx.stroke();
+
+  ctx.restore();
+
+  window.requestAnimationFrame(clock);
+}
+
+window.requestAnimationFrame(clock);
+ + + +

{{EmbedLiveSample("Un_reloj_animado", "180", "180", "https://mdn.mozillademos.org/files/203/Canvas_animation2.png")}}

+ +

Un panorama en bucle

+ +

En este ejemplo, una foto panorámica avanza de izquierda a derecha. Donde usaremos una imagen del Parque Nacional de Yosemite que tomamos de Wikipedia, pero tu podrías usar cualquier imagen que sea mas grande que el canvas.

+ +
var img = new Image();
+
+// Variables de usuario - personalizar estas para cambiar la imagen cuando inicie el desplazamiento
+// dirección y velocidad.
+
+img.src = 'https://mdn.mozillademos.org/files/4553/Capitan_Meadows,_Yosemite_National_Park.jpg';
+var CanvasXSize = 800;
+var CanvasYSize = 200;
+var speed = 30; //más bajo es más rápido
+var scale = 1.05;
+var y = -4.5; //desplazamiento vertical
+
+// Programa principal
+
+var dx = 0.75;
+var imgW;
+var imgH;
+var x = 0;
+var clearX;
+var clearY;
+var ctx;
+
+img.onload = function() {
+    imgW = img.width * scale;
+    imgH = img.height * scale;
+
+    if (imgW > CanvasXSize) {
+        // imagen más grande que canvas
+        x = CanvasXSize - imgW;
+    }
+    if (imgW > CanvasXSize) {
+        // ancho de imagen más grande que canvas
+        clearX = imgW;
+    } else {
+        clearX = CanvasXSize;
+    }
+    if (imgH > CanvasYSize) {
+        // altura de la imagen más grande que canvas
+        clearY = imgH;
+    } else {
+        clearY = CanvasYSize;
+    }
+
+    // obtener contexto de canvas
+    ctx = document.getElementById('canvas').getContext('2d');
+
+    // establecer frecuencia de actualización
+    return setInterval(draw, speed);
+}
+
+function draw() {
+    ctx.clearRect(0, 0, clearX, clearY); // clear the canvas
+
+    // si la imagen es <= tamaño de Canvas
+    if (imgW <= CanvasXSize) {
+        // reiniciar, comenzar desde el principio
+        if (x > CanvasXSize) {
+            x = -imgW + x;
+        }
+        // dibujar image1 adicional
+        if (x > 0) {
+            ctx.drawImage(img, -imgW + x, y, imgW, imgH);
+        }
+        // dibujar image2 adicional
+        if (x - imgW > 0) {
+            ctx.drawImage(img, -imgW * 2 + x, y, imgW, imgH);
+        }
+    }
+
+    // la imagen es > tamaño de Canvas
+    else {
+        // reiniciar, comenzar desde el principio
+        if (x > (CanvasXSize)) {
+            x = CanvasXSize - imgW;
+        }
+        // dibujar image adicional
+        if (x > (CanvasXSize-imgW)) {
+            ctx.drawImage(img, x - imgW + 1, y, imgW, imgH);
+        }
+    }
+    // dibujar imagen
+    ctx.drawImage(img, x, y,imgW, imgH);
+    // cantidad para moverse
+    x += dx;
+}
+ +

Debajo esta el elemento {{HTMLElement("canvas")}} en el cual va la imagen se va ha desplazar. Nota que el ancho y el alto especificado aquí son las variables CanvasXZSize y CanvasYSize.

+ +
<canvas id="canvas" width="800" height="200"></canvas>
+ +

{{EmbedLiveSample("Un_panorama_en_bucle", "830", "230")}}

+ +

Otros ejemplos

+ +
+
Un ray-caster básico
+
Un buen ejemplo de como hacer animaciones usando como control el teclado.
+
Animaciones avanzadas
+
Vamos a echar un vistazo a algunas técnicas de animación avanzadas y física en el próximo capítulo.
+
+ +

{{PreviousNext("Web/API/Canvas_API/Tutorial/Compositing", "Web/API/Canvas_API/Tutorial/Advanced_animations")}}

diff --git a/files/es/web/api/canvas_api/tutorial/basic_usage/index.html b/files/es/web/api/canvas_api/tutorial/basic_usage/index.html new file mode 100644 index 0000000000..17136d7a7e --- /dev/null +++ b/files/es/web/api/canvas_api/tutorial/basic_usage/index.html @@ -0,0 +1,146 @@ +--- +title: Uso básico de Canvas +slug: Web/Guide/HTML/Canvas_tutorial/Basic_usage +translation_of: Web/API/Canvas_API/Tutorial/Basic_usage +--- +
{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial", "Web/API/Canvas_API/Tutorial/Dibujando_formas")}}
+ +
+

Comenzamos este tutorial observando el elemento  {{HTMLElement("canvas")}}. Al final de esta página, sabrás como configurar el entorno 2D de canvas y habrás dibujado el primer ejemplo en tu navegador.

+
+ +

El elemento <canvas>

+ +
<canvas id="tutorial" width="150" height="150"></canvas>
+
+ +

A primera vista, un elemento {{HTMLElement("canvas")}} es parecido al elemento {{HTMLElement("img")}}, con la diferencia que este no tiene los atributos src y alt. El elemento <canvas> tiene solo dos atributos - {{htmlattrxref("width", "canvas")}} y {{htmlattrxref("height", "canvas")}}. Ambos son opcionales y pueden ser definidos usando propiedades DOM. Cuando los atributos ancho y alto no estan especificados, el lienzo se inicializara con 300 pixels ancho y 150 pixels de alto. El elemento puede ser arbitrariamente redimensionado por CSS, pero durante el renderizado la imagen es escalada para ajustarse al tamaño de su layout. Si el tamaño del CSS no respeta el ratio del canvas inicial, este aparecerá distorsionado.

+ +
+

Nota: Si su renderizado se ve distorsionado, pruebe especificar los atributos width y height explícitamente en los atributos del <canvas> , y no usando CSS.

+
+ +

El atributo id no está especificado para el elemento  <canvas> pero es uno de los atributos globales de HTML el cual puede ser aplicado a cualquier elemento HTML (como class por ejemplo). Siempre es buena idea proporcionar un id porque esto hace más fácil identificarlo en un script.

+ +

El elemento <canvas> puede ser estilizado como a cualquier imagen normal (margin, border, background, etc). Estas reglas, sin embargo, no afectan a lo dibujado sobre el canvas. Mas adelante veremos cómo se hace esto en un capítulo dedicado en este tutorial. Cuando no tenemos reglas de estilo aplicadas al canvas, este será completamente transparente.

+ +
+

Contenido alternativo

+ +

El elemento <canvas> se diferencia de un tag {{HTMLElement("img")}} en que, como los elementos {{HTMLElement("video")}}, {{HTMLElement("audio")}} o {{HTMLElement("picture")}}, es fácil definir contenido alternativo (fallback content) para mostrarse en navegadores viejos que no soporten el elemento <canvas>, como versiones de Internet Explorer previas a la versión 9 o navegadores de texto. Siempre debes proporcionar contenido alternativo para mostrar en estos navegadores.

+ +

Proporcionar contenido alternativo es muy explicito: solo debemos insertar el contenido alterno dentro del elemento <canvas>. Los navegadores que no soporten <canvas> ignoraran el contenedor y mostrarán el contenido indicado dentro de este. Navegadores que soporten <canvas> ignorarán el contenido en su interior (de las etiquetas), y mostrarán el canvas normalmente.

+ +

Por ejemplo, podremos proporcionar un texto descriptivo del contenido del canvas o proveer una imagen estática del contenido rederizado. Nos podría quedar algo así:

+ +
<canvas id="stockGraph" width="150" height="150">
+  current stock price: $3.15 +0.15
+</canvas>
+
+<canvas id="clock" width="150" height="150">
+  <img src="images/clock.png" width="150" height="150" alt=""/>
+</canvas>
+
+ +

Etiqueta </canvas> requerida

+ +

De manera distinta al elemento {{HTMLElement("img")}}, el elemento {{HTMLElement("canvas")}} requiere cerrar la etiqueta  (</canvas>).

+ +
+

Note: Aunque las versiones anteriores del navegador Safari de Apple no requeria el cierre de la etiqueta, la especificacion indica que es necesaria, asi que tu deberias incluir esta para asegurarte la compatibilidad. Aquellas versiones de Safari (anteriores versiones a 2.0) renderizaran el contenido de regreso agregandolo al canvas mismo a no ser que utilice trucos de CSS para enmascararlo. Afortunadamente, los usuarios de aquellas versiones de Safari son raros hoy en dia.

+
+ +

Si el contenido alternativo no se necesita, un simple <canvas id="foo" ...></canvas> es completamente compatible con todos los navegadores que soportan canvas.

+ +

El contexto de renderización

+ +

{{HTMLElement("canvas")}} crea un lienzo de dibujo fijado que expone uno o mas contextos renderizados, los cuales son usados para crear y manipular el contenido mostrado. Nos enfocaremos en renderizacion de contextos 2D. Otros contextos deberan proveer diferentes tipos de renderizaciones; por ejemplo, WebGL usa un 3D contexto ("experimental-webgl") basado sobre OpenGL ES.

+ +

El canvas esta inicialmente en blanco. Para mostrar alguna cosa, un script primero necesita acceder al contexto a renderizar y dibujar sobre este. El elemento  {{HTMLElement("canvas")}} tiene un method llamado  getContext(), usado para obtener el contexto a renderizar y sus funciones de dibujo. getContext() toma un parametro, el tipo de contexto. Para graficos 2D, como los que cubre este tutorial, su especificacion es "2d".

+ +
var canvas = document.getElementById('tutorial');
+var ctx = canvas.getContext('2d');
+
+ +

La primera linea regresa el nodo DOM para el elemento {{HTMLElement("canvas")}} llamando al metodo  {{domxref("document.getElementById()")}}. Una vez tu tienes el elemento nodo, tu puedes acceder al contexto de dibujo usando su metodo getContext().

+ +
+

Comprobando soporte

+ +

El contenido de regreso que es mostrado en navegadores los cuales no soportan {{HTMLElement("canvas")}}. Para los Scripts puede tambien comprobarse su soporte desde la programacion por un simple test para la presencia del metodo getContext(). Con un trozo de codigo parecido al que viene debajo:

+ +
var canvas = document.getElementById('tutorial');
+
+if (canvas.getContext){
+  var ctx = canvas.getContext('2d');
+  // drawing code here
+} else {
+  // canvas-unsupported code here
+}
+
+
+
+ +

Un esqueleto de plantilla

+ +

Aqui esta una plantilla minimalista, la cual usaremos como punto de partida para posteriores ejemplos.

+ +
<html>
+  <head>
+    <title>Canvas tutorial</title>
+    <script type="text/javascript">
+      function draw(){
+        var canvas = document.getElementById('tutorial');
+        if (canvas.getContext){
+          var ctx = canvas.getContext('2d');
+        }
+      }
+    </script>
+    <style type="text/css">
+      canvas { border: 1px solid black; }
+    </style>
+  </head>
+  <body onload="draw();">
+    <canvas id="tutorial" width="150" height="150"></canvas>
+  </body>
+</html>
+
+ +

El script incluye una funcion llamada draw(), la cual es ejecutada una vez finalizada la carga de la pagina; este esta hecho usando el evento load del documento. Esta funcion, o una parecida, podria tambien ser llamada usando {{domxref("window.setTimeout()")}}, {{domxref("window.setInterval()")}}, o cualquier otro manejador de evento, a lo largo de que la pagina esta siendo cargada la primera vez.

+ +

Aqui esta como la plantilla se ve en acción:

+ +

{{EmbedLiveSample("Un_esqueleto_de_plantilla", 160, 160)}}

+ +

Un simple ejemplo

+ +

Para comenzar, daremos un vistazo a un simple ejemplo que dibuja dos rectangulos que se intersectan, uno de los cuales tiene alpha transparencia. Exploraremos como esto trabaja en mas detalle en posteriores ejemplos.

+ +
<html>
+ <head>
+  <script type="application/javascript">
+    function draw() {
+      var canvas = document.getElementById("canvas");
+      if (canvas.getContext) {
+        var ctx = canvas.getContext("2d");
+
+        ctx.fillStyle = "rgb(200,0,0)";
+        ctx.fillRect (10, 10, 55, 50);
+
+        ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
+        ctx.fillRect (30, 30, 55, 50);
+      }
+    }
+  </script>
+ </head>
+ <body onload="draw();">
+   <canvas id="canvas" width="150" height="150"></canvas>
+ </body>
+</html>
+
+ +

Este ejemplo quedaría así:

+ +

{{EmbedLiveSample("Un_simple_ejemplo", 160, 160, "https://mdn.mozillademos.org/files/228/canvas_ex1.png")}}

+ +

{{PreviousNext("Web/Guide/HTML/Canvas_tutorial", "Web/Guide/HTML/Canvas_tutorial/Dibujando_formas")}}

diff --git a/files/es/web/api/canvas_api/tutorial/compositing/ejemplo/index.html b/files/es/web/api/canvas_api/tutorial/compositing/ejemplo/index.html deleted file mode 100644 index b85b83238c..0000000000 --- a/files/es/web/api/canvas_api/tutorial/compositing/ejemplo/index.html +++ /dev/null @@ -1,295 +0,0 @@ ---- -title: Ejemplo de composición -slug: Web/API/Canvas_API/Tutorial/Compositing/Ejemplo -tags: - - Canvas - - Ejemplo - - HTML - - HTML5 - - Tutorial - - graficos -translation_of: Web/API/Canvas_API/Tutorial/Compositing/Example ---- -
{{CanvasSidebar}}
- -

Este programa de ejemplo muestra una cantidad de operaciones de composición. La salida se ve así:

- -

{{EmbedLiveSample("Ejemplo_de_composición", "100%", 7250)}}

- -

Ejemplo de composición

- -

Este código establece los valores globales utilizados por el resto del programa.

- -
var canvas1 = document.createElement("canvas");
-var canvas2 = document.createElement("canvas");
-var gco = [ 'source-over','source-in','source-out','source-atop',
-            'destination-over','destination-in','destination-out','destination-atop',
-            'lighter', 'copy','xor', 'multiply', 'screen', 'overlay', 'darken',
-            'lighten', 'color-dodge', 'color-burn', 'hard-light', 'soft-light',
-            'difference', 'exclusion', 'hue', 'saturation', 'color', 'luminosity'
-          ].reverse();
-var gcoText = [
-'Esta es la configuración predeterminada y dibuja nuevas formas sobre el contenido del canvas existente.',
-'La nueva forma se dibuja solo donde la nueva forma y el canvas de destino se superponen. Todo lo demás se hace transparente.',
-'La nueva forma se dibuja donde no se superpone al contenido del canvas existente.',
-'La nueva forma solo se dibuja donde se solapa con el contenido del canvas existente.',
-'Se dibujan nuevas formas detrás del contenido del canvas existente',
-'El contenido del canvas existente se conserva donde la nueva forma y el contenido del canvas existente se superponen. Todo lo demás se hace transparente.',
-'El contenido existente se mantiene donde no se superpone a la nueva forma.',
-'El canvas existente solo se conserva donde se solapa con la nueva forma. La nueva forma se dibuja detrás del contenido del canvas.',
-'Donde ambas formas se superponen, el color se determina agregando valores de color.',
-'Solo se muestra la nueva forma.',
-'Las formas se hacen transparentes donde ambas se superponen y se dibujan de manera normal en cualquier otro lugar.',
-'Los píxeles de la capa superior se multiplican con el píxel correspondiente de la capa inferior. El resultado es una imagen más oscura. ',
-'Los píxeles están invertidos, multiplicados e invertidos nuevamente. Una imagen más clara es el resultado (opuesto a multiplicar).',
-'Una combinación de multiplicar y pantalla. Las partes oscuras en la capa base se oscurecen y las partes claras se vuelven más claras.',
-'Conserva los píxeles más oscuros de ambas capas.',
-'Conserva los píxeles más claros de ambas capas.',
-'Divide la capa inferior por la capa superior invertida.',
-'Divide la capa inferior invertida por la capa superior, y luego invierte el resultado.',
-'Una combinación de multiplicar y pantalla como superposición, pero con la parte superior y la capa inferior intercambiado.',
-'Una versión más suave de la luz dura. Negro puro o blanco no da como resultado negro o blanco puro.',
-'Resta la capa inferior de la capa superior o viceversa para obtener siempre un valor positivo.',
-'Al igual que la diferencia, pero con menor contraste.',
-'Conserva la luma y el croma de la capa inferior, mientras adopta el tono de la capa superior.',
-'Conserva la luma y el tono de la capa inferior, mientras adopta el croma de la capa superior.',
-'Conserva la luma de la capa inferior, mientras adopta el matiz y el croma de la capa superior.',
-'Conserva el tono y el croma de la capa inferior, mientras adopta la luma de la capa superior.'
-          ].reverse();
-var width = 320;
-var height = 340;
-
- -

Programa principal

- -

Cuando se carga la página, este código se ejecuta para configurar y ejecutar el ejemplo:

- -
window.onload = function() {
-    // lum en sRGB
-    var lum = {
-        r: 0.33,
-        g: 0.33,
-        b: 0.33
-    };
-    // redimensionar canvas
-    canvas1.width = width;
-    canvas1.height = height;
-    canvas2.width = width;
-    canvas2.height = height;
-    lightMix()
-    colorSphere();
-    runComposite();
-    return;
-};
-
- -

Y este código, runComposite(), maneja la mayor parte del trabajo, dependiendo de una serie de funciones de utilidad para hacer las partes difíciles.

- -
function createCanvas() {
-    var canvas = document.createElement("canvas");
-    canvas.style.background = "url("+op_8x8.data+")";
-    canvas.style.border = "1px solid #000";
-    canvas.style.margin = "5px";
-    canvas.width = width/2;
-    canvas.height = height/2;
-    return canvas;
-}
-
-function runComposite() {
-    var dl = document.createElement("dl");
-    document.body.appendChild(dl);
-    while(gco.length) {
-        var pop = gco.pop();
-        var dt = document.createElement("dt");
-        dt.textContent = pop;
-        dl.appendChild(dt);
-        var dd = document.createElement("dd");
-        var p = document.createElement("p");
-        p.textContent = gcoText.pop();
-        dd.appendChild(p);
-
-        var canvasToDrawOn = createCanvas();
-        var canvasToDrawFrom = createCanvas();
-        var canvasToDrawResult = createCanvas();
-
-        var ctx = canvasToDrawResult.getContext('2d');
-        ctx.clearRect(0, 0, width, height)
-        ctx.save();
-        ctx.drawImage(canvas1, 0, 0, width/2, height/2);
-        ctx.globalCompositeOperation = pop;
-        ctx.drawImage(canvas2, 0, 0, width/2, height/2);
-        ctx.globalCompositeOperation = "source-over";
-        ctx.fillStyle = "rgba(0,0,0,0.8)";
-        ctx.fillRect(0, height/2 - 20, width/2, 20);
-        ctx.fillStyle = "#FFF";
-        ctx.font = "14px arial";
-        ctx.fillText(pop, 5, height/2 - 5);
-        ctx.restore();
-
-        var ctx = canvasToDrawOn.getContext('2d');
-        ctx.clearRect(0, 0, width, height)
-        ctx.save();
-        ctx.drawImage(canvas1, 0, 0, width/2, height/2);
-        ctx.fillStyle = "rgba(0,0,0,0.8)";
-        ctx.fillRect(0, height/2 - 20, width/2, 20);
-        ctx.fillStyle = "#FFF";
-        ctx.font = "14px arial";
-        ctx.fillText('existing content', 5, height/2 - 5);
-        ctx.restore();
-
-        var ctx = canvasToDrawFrom.getContext('2d');
-        ctx.clearRect(0, 0, width, height)
-        ctx.save();
-        ctx.drawImage(canvas2, 0, 0, width/2, height/2);
-        ctx.fillStyle = "rgba(0,0,0,0.8)";
-        ctx.fillRect(0, height/2 - 20, width/2, 20);
-        ctx.fillStyle = "#FFF";
-        ctx.font = "14px arial";
-        ctx.fillText('new content', 5, height/2 - 5);
-        ctx.restore();
-
-        dd.appendChild(canvasToDrawOn);
-        dd.appendChild(canvasToDrawFrom);
-        dd.appendChild(canvasToDrawResult);
-
-        dl.appendChild(dd);
-    }
-};
-
- -

Funciones de utilidad

- -

El programa se basa en una serie de funciones de utilidad.

- -
var lightMix = function() {
-    var ctx = canvas2.getContext("2d");
-    ctx.save();
-    ctx.globalCompositeOperation = "lighter";
-    ctx.beginPath();
-    ctx.fillStyle = "rgba(255,0,0,1)";
-    ctx.arc(100, 200, 100, Math.PI*2, 0, false);
-    ctx.fill()
-    ctx.beginPath();
-    ctx.fillStyle = "rgba(0,0,255,1)";
-    ctx.arc(220, 200, 100, Math.PI*2, 0, false);
-    ctx.fill()
-    ctx.beginPath();
-    ctx.fillStyle = "rgba(0,255,0,1)";
-    ctx.arc(160, 100, 100, Math.PI*2, 0, false);
-    ctx.fill();
-    ctx.restore();
-    ctx.beginPath();
-    ctx.fillStyle = "#f00";
-    ctx.fillRect(0,0,30,30)
-    ctx.fill();
-};
-
- -
var colorSphere = function(element) {
-    var ctx = canvas1.getContext("2d");
-    var width = 360;
-    var halfWidth = width / 2;
-    var rotate = (1 / 360) * Math.PI * 2; // por grado
-    var offset = 0; // scrollbar offset
-    var oleft = -20;
-    var otop = -20;
-    for (var n = 0; n <= 359; n ++) {
-        var gradient = ctx.createLinearGradient(oleft + halfWidth, otop, oleft + halfWidth, otop + halfWidth);
-        var color = Color.HSV_RGB({ H: (n + 300) % 360, S: 100, V: 100 });
-        gradient.addColorStop(0, "rgba(0,0,0,0)");
-        gradient.addColorStop(0.7, "rgba("+color.R+","+color.G+","+color.B+",1)");
-        gradient.addColorStop(1, "rgba(255,255,255,1)");
-        ctx.beginPath();
-        ctx.moveTo(oleft + halfWidth, otop);
-        ctx.lineTo(oleft + halfWidth, otop + halfWidth);
-        ctx.lineTo(oleft + halfWidth + 6, otop);
-        ctx.fillStyle = gradient;
-        ctx.fill();
-        ctx.translate(oleft + halfWidth, otop + halfWidth);
-        ctx.rotate(rotate);
-        ctx.translate(-(oleft + halfWidth), -(otop + halfWidth));
-    }
-    ctx.beginPath();
-    ctx.fillStyle = "#00f";
-    ctx.fillRect(15,15,30,30)
-    ctx.fill();
-    return ctx.canvas;
-};
-
- -
// HSV (1978) = H: Hue / S: Saturation / V: Value
-Color = {};
-Color.HSV_RGB = function (o) {
-    var H = o.H / 360,
-        S = o.S / 100,
-        V = o.V / 100,
-        R, G, B;
-    var A, B, C, D;
-    if (S == 0) {
-        R = G = B = Math.round(V * 255);
-    } else {
-        if (H >= 1) H = 0;
-        H = 6 * H;
-        D = H - Math.floor(H);
-        A = Math.round(255 * V * (1 - S));
-        B = Math.round(255 * V * (1 - (S * D)));
-        C = Math.round(255 * V * (1 - (S * (1 - D))));
-        V = Math.round(255 * V);
-        switch (Math.floor(H)) {
-            case 0:
-                R = V;
-                G = C;
-                B = A;
-                break;
-            case 1:
-                R = B;
-                G = V;
-                B = A;
-                break;
-            case 2:
-                R = A;
-                G = V;
-                B = C;
-                break;
-            case 3:
-                R = A;
-                G = B;
-                B = V;
-                break;
-            case 4:
-                R = C;
-                G = A;
-                B = V;
-                break;
-            case 5:
-                R = V;
-                G = A;
-                B = B;
-                break;
-        }
-    }
-    return {
-        R: R,
-        G: G,
-        B: B
-    };
-};
-
-var createInterlace = function (size, color1, color2) {
-    var proto = document.createElement("canvas").getContext("2d");
-    proto.canvas.width = size * 2;
-    proto.canvas.height = size * 2;
-    proto.fillStyle = color1; // top-left
-    proto.fillRect(0, 0, size, size);
-    proto.fillStyle = color2; // top-right
-    proto.fillRect(size, 0, size, size);
-    proto.fillStyle = color2; // bottom-left
-    proto.fillRect(0, size, size, size);
-    proto.fillStyle = color1; // bottom-right
-    proto.fillRect(size, size, size, size);
-    var pattern = proto.createPattern(proto.canvas, "repeat");
-    pattern.data = proto.canvas.toDataURL();
-    return pattern;
-};
-
-var op_8x8 = createInterlace(8, "#FFF", "#eee");
diff --git a/files/es/web/api/canvas_api/tutorial/compositing/example/index.html b/files/es/web/api/canvas_api/tutorial/compositing/example/index.html new file mode 100644 index 0000000000..b85b83238c --- /dev/null +++ b/files/es/web/api/canvas_api/tutorial/compositing/example/index.html @@ -0,0 +1,295 @@ +--- +title: Ejemplo de composición +slug: Web/API/Canvas_API/Tutorial/Compositing/Ejemplo +tags: + - Canvas + - Ejemplo + - HTML + - HTML5 + - Tutorial + - graficos +translation_of: Web/API/Canvas_API/Tutorial/Compositing/Example +--- +
{{CanvasSidebar}}
+ +

Este programa de ejemplo muestra una cantidad de operaciones de composición. La salida se ve así:

+ +

{{EmbedLiveSample("Ejemplo_de_composición", "100%", 7250)}}

+ +

Ejemplo de composición

+ +

Este código establece los valores globales utilizados por el resto del programa.

+ +
var canvas1 = document.createElement("canvas");
+var canvas2 = document.createElement("canvas");
+var gco = [ 'source-over','source-in','source-out','source-atop',
+            'destination-over','destination-in','destination-out','destination-atop',
+            'lighter', 'copy','xor', 'multiply', 'screen', 'overlay', 'darken',
+            'lighten', 'color-dodge', 'color-burn', 'hard-light', 'soft-light',
+            'difference', 'exclusion', 'hue', 'saturation', 'color', 'luminosity'
+          ].reverse();
+var gcoText = [
+'Esta es la configuración predeterminada y dibuja nuevas formas sobre el contenido del canvas existente.',
+'La nueva forma se dibuja solo donde la nueva forma y el canvas de destino se superponen. Todo lo demás se hace transparente.',
+'La nueva forma se dibuja donde no se superpone al contenido del canvas existente.',
+'La nueva forma solo se dibuja donde se solapa con el contenido del canvas existente.',
+'Se dibujan nuevas formas detrás del contenido del canvas existente',
+'El contenido del canvas existente se conserva donde la nueva forma y el contenido del canvas existente se superponen. Todo lo demás se hace transparente.',
+'El contenido existente se mantiene donde no se superpone a la nueva forma.',
+'El canvas existente solo se conserva donde se solapa con la nueva forma. La nueva forma se dibuja detrás del contenido del canvas.',
+'Donde ambas formas se superponen, el color se determina agregando valores de color.',
+'Solo se muestra la nueva forma.',
+'Las formas se hacen transparentes donde ambas se superponen y se dibujan de manera normal en cualquier otro lugar.',
+'Los píxeles de la capa superior se multiplican con el píxel correspondiente de la capa inferior. El resultado es una imagen más oscura. ',
+'Los píxeles están invertidos, multiplicados e invertidos nuevamente. Una imagen más clara es el resultado (opuesto a multiplicar).',
+'Una combinación de multiplicar y pantalla. Las partes oscuras en la capa base se oscurecen y las partes claras se vuelven más claras.',
+'Conserva los píxeles más oscuros de ambas capas.',
+'Conserva los píxeles más claros de ambas capas.',
+'Divide la capa inferior por la capa superior invertida.',
+'Divide la capa inferior invertida por la capa superior, y luego invierte el resultado.',
+'Una combinación de multiplicar y pantalla como superposición, pero con la parte superior y la capa inferior intercambiado.',
+'Una versión más suave de la luz dura. Negro puro o blanco no da como resultado negro o blanco puro.',
+'Resta la capa inferior de la capa superior o viceversa para obtener siempre un valor positivo.',
+'Al igual que la diferencia, pero con menor contraste.',
+'Conserva la luma y el croma de la capa inferior, mientras adopta el tono de la capa superior.',
+'Conserva la luma y el tono de la capa inferior, mientras adopta el croma de la capa superior.',
+'Conserva la luma de la capa inferior, mientras adopta el matiz y el croma de la capa superior.',
+'Conserva el tono y el croma de la capa inferior, mientras adopta la luma de la capa superior.'
+          ].reverse();
+var width = 320;
+var height = 340;
+
+ +

Programa principal

+ +

Cuando se carga la página, este código se ejecuta para configurar y ejecutar el ejemplo:

+ +
window.onload = function() {
+    // lum en sRGB
+    var lum = {
+        r: 0.33,
+        g: 0.33,
+        b: 0.33
+    };
+    // redimensionar canvas
+    canvas1.width = width;
+    canvas1.height = height;
+    canvas2.width = width;
+    canvas2.height = height;
+    lightMix()
+    colorSphere();
+    runComposite();
+    return;
+};
+
+ +

Y este código, runComposite(), maneja la mayor parte del trabajo, dependiendo de una serie de funciones de utilidad para hacer las partes difíciles.

+ +
function createCanvas() {
+    var canvas = document.createElement("canvas");
+    canvas.style.background = "url("+op_8x8.data+")";
+    canvas.style.border = "1px solid #000";
+    canvas.style.margin = "5px";
+    canvas.width = width/2;
+    canvas.height = height/2;
+    return canvas;
+}
+
+function runComposite() {
+    var dl = document.createElement("dl");
+    document.body.appendChild(dl);
+    while(gco.length) {
+        var pop = gco.pop();
+        var dt = document.createElement("dt");
+        dt.textContent = pop;
+        dl.appendChild(dt);
+        var dd = document.createElement("dd");
+        var p = document.createElement("p");
+        p.textContent = gcoText.pop();
+        dd.appendChild(p);
+
+        var canvasToDrawOn = createCanvas();
+        var canvasToDrawFrom = createCanvas();
+        var canvasToDrawResult = createCanvas();
+
+        var ctx = canvasToDrawResult.getContext('2d');
+        ctx.clearRect(0, 0, width, height)
+        ctx.save();
+        ctx.drawImage(canvas1, 0, 0, width/2, height/2);
+        ctx.globalCompositeOperation = pop;
+        ctx.drawImage(canvas2, 0, 0, width/2, height/2);
+        ctx.globalCompositeOperation = "source-over";
+        ctx.fillStyle = "rgba(0,0,0,0.8)";
+        ctx.fillRect(0, height/2 - 20, width/2, 20);
+        ctx.fillStyle = "#FFF";
+        ctx.font = "14px arial";
+        ctx.fillText(pop, 5, height/2 - 5);
+        ctx.restore();
+
+        var ctx = canvasToDrawOn.getContext('2d');
+        ctx.clearRect(0, 0, width, height)
+        ctx.save();
+        ctx.drawImage(canvas1, 0, 0, width/2, height/2);
+        ctx.fillStyle = "rgba(0,0,0,0.8)";
+        ctx.fillRect(0, height/2 - 20, width/2, 20);
+        ctx.fillStyle = "#FFF";
+        ctx.font = "14px arial";
+        ctx.fillText('existing content', 5, height/2 - 5);
+        ctx.restore();
+
+        var ctx = canvasToDrawFrom.getContext('2d');
+        ctx.clearRect(0, 0, width, height)
+        ctx.save();
+        ctx.drawImage(canvas2, 0, 0, width/2, height/2);
+        ctx.fillStyle = "rgba(0,0,0,0.8)";
+        ctx.fillRect(0, height/2 - 20, width/2, 20);
+        ctx.fillStyle = "#FFF";
+        ctx.font = "14px arial";
+        ctx.fillText('new content', 5, height/2 - 5);
+        ctx.restore();
+
+        dd.appendChild(canvasToDrawOn);
+        dd.appendChild(canvasToDrawFrom);
+        dd.appendChild(canvasToDrawResult);
+
+        dl.appendChild(dd);
+    }
+};
+
+ +

Funciones de utilidad

+ +

El programa se basa en una serie de funciones de utilidad.

+ +
var lightMix = function() {
+    var ctx = canvas2.getContext("2d");
+    ctx.save();
+    ctx.globalCompositeOperation = "lighter";
+    ctx.beginPath();
+    ctx.fillStyle = "rgba(255,0,0,1)";
+    ctx.arc(100, 200, 100, Math.PI*2, 0, false);
+    ctx.fill()
+    ctx.beginPath();
+    ctx.fillStyle = "rgba(0,0,255,1)";
+    ctx.arc(220, 200, 100, Math.PI*2, 0, false);
+    ctx.fill()
+    ctx.beginPath();
+    ctx.fillStyle = "rgba(0,255,0,1)";
+    ctx.arc(160, 100, 100, Math.PI*2, 0, false);
+    ctx.fill();
+    ctx.restore();
+    ctx.beginPath();
+    ctx.fillStyle = "#f00";
+    ctx.fillRect(0,0,30,30)
+    ctx.fill();
+};
+
+ +
var colorSphere = function(element) {
+    var ctx = canvas1.getContext("2d");
+    var width = 360;
+    var halfWidth = width / 2;
+    var rotate = (1 / 360) * Math.PI * 2; // por grado
+    var offset = 0; // scrollbar offset
+    var oleft = -20;
+    var otop = -20;
+    for (var n = 0; n <= 359; n ++) {
+        var gradient = ctx.createLinearGradient(oleft + halfWidth, otop, oleft + halfWidth, otop + halfWidth);
+        var color = Color.HSV_RGB({ H: (n + 300) % 360, S: 100, V: 100 });
+        gradient.addColorStop(0, "rgba(0,0,0,0)");
+        gradient.addColorStop(0.7, "rgba("+color.R+","+color.G+","+color.B+",1)");
+        gradient.addColorStop(1, "rgba(255,255,255,1)");
+        ctx.beginPath();
+        ctx.moveTo(oleft + halfWidth, otop);
+        ctx.lineTo(oleft + halfWidth, otop + halfWidth);
+        ctx.lineTo(oleft + halfWidth + 6, otop);
+        ctx.fillStyle = gradient;
+        ctx.fill();
+        ctx.translate(oleft + halfWidth, otop + halfWidth);
+        ctx.rotate(rotate);
+        ctx.translate(-(oleft + halfWidth), -(otop + halfWidth));
+    }
+    ctx.beginPath();
+    ctx.fillStyle = "#00f";
+    ctx.fillRect(15,15,30,30)
+    ctx.fill();
+    return ctx.canvas;
+};
+
+ +
// HSV (1978) = H: Hue / S: Saturation / V: Value
+Color = {};
+Color.HSV_RGB = function (o) {
+    var H = o.H / 360,
+        S = o.S / 100,
+        V = o.V / 100,
+        R, G, B;
+    var A, B, C, D;
+    if (S == 0) {
+        R = G = B = Math.round(V * 255);
+    } else {
+        if (H >= 1) H = 0;
+        H = 6 * H;
+        D = H - Math.floor(H);
+        A = Math.round(255 * V * (1 - S));
+        B = Math.round(255 * V * (1 - (S * D)));
+        C = Math.round(255 * V * (1 - (S * (1 - D))));
+        V = Math.round(255 * V);
+        switch (Math.floor(H)) {
+            case 0:
+                R = V;
+                G = C;
+                B = A;
+                break;
+            case 1:
+                R = B;
+                G = V;
+                B = A;
+                break;
+            case 2:
+                R = A;
+                G = V;
+                B = C;
+                break;
+            case 3:
+                R = A;
+                G = B;
+                B = V;
+                break;
+            case 4:
+                R = C;
+                G = A;
+                B = V;
+                break;
+            case 5:
+                R = V;
+                G = A;
+                B = B;
+                break;
+        }
+    }
+    return {
+        R: R,
+        G: G,
+        B: B
+    };
+};
+
+var createInterlace = function (size, color1, color2) {
+    var proto = document.createElement("canvas").getContext("2d");
+    proto.canvas.width = size * 2;
+    proto.canvas.height = size * 2;
+    proto.fillStyle = color1; // top-left
+    proto.fillRect(0, 0, size, size);
+    proto.fillStyle = color2; // top-right
+    proto.fillRect(size, 0, size, size);
+    proto.fillStyle = color2; // bottom-left
+    proto.fillRect(0, size, size, size);
+    proto.fillStyle = color1; // bottom-right
+    proto.fillRect(size, size, size, size);
+    var pattern = proto.createPattern(proto.canvas, "repeat");
+    pattern.data = proto.canvas.toDataURL();
+    return pattern;
+};
+
+var op_8x8 = createInterlace(8, "#FFF", "#eee");
diff --git a/files/es/web/api/canvas_api/tutorial/drawing_shapes/index.html b/files/es/web/api/canvas_api/tutorial/drawing_shapes/index.html new file mode 100644 index 0000000000..3467533e93 --- /dev/null +++ b/files/es/web/api/canvas_api/tutorial/drawing_shapes/index.html @@ -0,0 +1,513 @@ +--- +title: Dibujando formas con canvas +slug: Web/Guide/HTML/Canvas_tutorial/Dibujando_formas +tags: + - Canvas + - HTML + - HTML Canvas + - HTML5 + - Intermedio + - Tutorial + - graficos +translation_of: Web/API/Canvas_API/Tutorial/Drawing_shapes +--- +
{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Basic_usage", "Web/API/Canvas_API/Tutorial/Applying_styles_and_colors")}}
+ +
+

Ahora que hemos preparado nuestro entorno canvas, podemos entrar en detalles de como dibujar en el canvas. Al final de este artículo, habrás aprendido  como dibujar rectángulos, triángulos, líneas, arcos y curvas, dándote familiaridad con algunas figuras básicas. Trabajar con rutas es esencial cuando dibujamos objetos sobre el canvas y veremos como se puede hacer eso.

+
+ +

La cuadrícula

+ +

Antes de que podamos empezar a dibujar, necesitamos hablar sobre la cuadrícula del canvas o el espacio de coordenadas. La plantilla HTML de la página anterior tenía un elemento canvas con un 'height' y un 'width' de 150 píxeles. A la derecha, puedes ver este canvas con la cuadrícula por defecto superpuesta. Normalmente una unidad en la cuadrícula corresponde a un píxel en el elemento canvas. El origen de esta cuadrícula está posicionado en la esquina superior izquierda (coordenada (0,0)). Todos los elementos estan posicionados de manera relativa a este punto, así que la posición de la esquina superior izquierda del cuadrado azul es de 'x' pixeles desde la izquierda y 'y' pixeles desde arriba (coordenada (x,y)). Mas tarde en este tutorial veremos como trasladar el punto de origen a una posicion diferente, girar la cuadrícula e incluso darle una escala diferente. Por ahora nos dedicaremos a lo mas común.

+ +

Dibujando rectángulos

+ +

A diferencia de SVG, {{HTMLElement("canvas")}} solo soporta una forma primitiva: rectangulos. Todas las otras formas deben ser creadas por la combinación de uno o más trazos, listas de puntos conectados por líneas. Afortunadamente, tenemos una variedad de funciones para dibujar trazos  que hacen posible componer formas muy complejas.

+ +
+

Primero veamos el rectángulo. Aquí hay tres funciones que podemos usar en el canvas para dibujarlos:

+ +
+
fillRect(x, y, width, height)
+
Dibuja un rectángulo relleno.
+
strokeRect(x, y, width, height)
+
Dibuja el contorno de un rectángulo.
+
clearRect(x, y, width, height)
+
Borra un área rectangular especificada, dejándola totalmente transparente.
+
+ +

Cada una de estas tres funciones toma los mismos parámetros. X e Y especifican la posición del canvas (en relación con el origen) desde la esquina superior izquierda del rectángulo. Tambien especifica los parámetros de anchura y altura que proporcionan el tamaño del rectángulo.

+ +

A continuación se muestra la función draw() de la página anterior, pero ahora haciendo uso de estas tres funciones.

+ +

Ejemplo de forma rectangular

+ + + +
function draw() {
+  var canvas = document.getElementById('canvas');
+  if (canvas.getContext) {
+    var ctx = canvas.getContext('2d');
+
+    ctx.fillRect(25,25,100,100);
+    ctx.clearRect(45,45,60,60);
+    ctx.strokeRect(50,50,50,50);
+  }
+}
+ +

El resultado de este ejemplo se muestra a continuación.

+ +

{{EmbedLiveSample("Rectangular_shape_example", 160, 160, "https://mdn.mozillademos.org/files/245/Canvas_rect.png")}}

+ +

La función fillRect() dibuja un cuadrado grande negro de 100 píxeles en cada lado. La función clearRect() luego borra un cuadrado de 60x60 píxeles del centro, y luego strokeRect() es llamado para crear un contorno rectangular de 50x50 píxeles dentro del cuadrado borrado.

+ +

En las próximas páginas veremos dos métodos alternativos para clearRect(), y también veremos cómo cambiar el color y el trazo de diferentes formas.

+ +

A diferencia de las funciones de trazo que veremos en la próxima sección, las tres funciones del rectángulo dibujan inmediatamente en el canvas.

+ +

Dibujando trazos

+ +

Crear formas mediante trazos requiere algunos pasos adicionales.

+ +
    +
  1. Primero, se crea el trazo.
  2. +
  3. A continuación, se usan comandos de dibujo para dibujar dentro del trazo.
  4. +
  5. Después, se cierra el trazo.
  6. +
  7. Una vez el trazo ha sido creado, se le puede dar contorno o relleno para renderizarlo.
  8. +
+ +

Estas son las funciones que se usan para llevar a cabo estos pasos:

+ +
+
beginPath()
+
Crea un nuevo trazo. Una vez creado, los comandos de dibujo futuros son aplicados dentro del trazo y usados para construir el nuevo trazo hacia arriba.
+
closePath()
+
Cierra el trazo de tal forma que los comandos de dibujo futuros son, una vez más redireccionados al contexto.
+
stroke()
+
Dibuja el contorno de la forma.
+
fill()
+
Dibuja una forma solida rellenando el área del trazo.
+
+ +

El primer paso para crear un trazo es llamar la función beginPath(). Internamente, los trazos son guardados como una lista de subtrazos (lineas, arcos, etc) los cuales juntos crean una forma. Todo tiempo que sea llamado este método la lista es reseteada y podemos empezar a dibujar nuevas formas.

+ +
Nota: Cuando el trazo actual este vacio, como aparece inmediatamente despues de llamar la función beginPath(), o en un canvas nuevo, el primer comando para la construcción del trazo es siempre tratada como un moveTo(), independientemente de cual es el trazo actual. Por esta razón casi siempre querrás específicamente setear tu posición de inicio despues de resetear un trazo.
+ +

El segundo paso es llamar los métodos que específican los trazos a crear. Los veremos en seguida.

+ +

El tercero, y un paso opcional, es llamar a la función closePath(). Este método trata de cerrar la forma dibujando una linea recta desde el punto actual al inicio. Si la forma ya ha sido cerrada o hay solamente un punto en la lista, la función hace nada.

+ +
Nota: Cuando llamas a la función fill(), cualquier forma abierta es cerrada automaticamente, de tal forma que no tendrás que llamar a la función closePath(). Este no es el caso cuando llamas a la función stroke().
+ +

Dibujando un triangulo

+ +

Por ejemplo, el código para dibujar un triangulo luciría como el siguiente:

+ + + +
function draw() {
+  var canvas = document.getElementById('canvas');
+  if (canvas.getContext){
+    var ctx = canvas.getContext('2d');
+
+    ctx.beginPath();
+    ctx.moveTo(75,50);
+    ctx.lineTo(100,75);
+    ctx.lineTo(100,25);
+    ctx.closePath();
+    ctx.fill();
+  }
+}
+
+ +

El resultado lucirá así:

+ +

{{EmbedLiveSample("Drawing_a_triangle", 160, 160)}}

+ +

Moviendo la pluma

+ +

Una función muy útil, la cual realmente no dibuja algo pero convierte parte de la lista de trazos descrita arriba, es la función moveTo(). Puedes, probablemente, pensar mejor de esta como levantar el lápiz o la pluma de un punto en un pedazo de papel y ponerlo en el siguiente punto.

+ +
+
moveTo(x, y)
+
Mueve la pluma a las coordenadas específicadas por x e y.
+
+ +

Cuando el canvas es inicializado ó la función beginPath() es llamada, querrás usar la función moveTo() para colocar el punto de inicio en alguna otra parte. Podríamos usar moveTo() para dibujar trazos sin conectar. Toma un vistazo a la cara sonriente de abajo. He marcado los lugares donde use el método moveTo() (las líneas rojas).

+ +

Para intentar esto por tí mismo, puedes usar el pequeño código de abajo. Solo pégalo dentro de la función draw() que vimos antes.

+ + + +
function draw() {
+  var canvas = document.getElementById('canvas');
+  if (canvas.getContext){
+    var ctx = canvas.getContext('2d');
+
+    ctx.beginPath();
+    ctx.arc(75,75,50,0,Math.PI*2,true); // Círculo externo
+    ctx.moveTo(110,75);
+    ctx.arc(75,75,35,0,Math.PI,false);   // Boca (contra reloj)
+    ctx.moveTo(65,65);
+    ctx.arc(60,65,5,0,Math.PI*2,true);  // Ojo izquierdo
+    ctx.moveTo(95,65);
+    ctx.arc(90,65,5,0,Math.PI*2,true);  // Ojo derecho
+    ctx.stroke();
+  }
+}
+
+ +

El resultado luce así:

+ +

{{EmbedLiveSample("Moving_the_pen", 160, 160, "https://mdn.mozillademos.org/files/252/Canvas_smiley.png")}}

+ +

Si quisieras ver las líneas conectadas, puedes remover las líneas de código que llaman moveTo().

+ +
+

Nota: Para aprender más sobre la función arc(), vea los {{anch("Arcs")}} a continuación.

+
+ +

Líneas

+ +

Para dibujar lineas rectas usa el método lineTo().

+ +
+
lineTo(x, y)
+
Dibuja una línea desde la posición actual del dibujo a la posición específicada por x e y.
+
+ +

Este método toma dos argumentos x e y, los cuales son las coordenadas del punto final de la linea. El punto de inicio es dependiente de los trazos previamente dibujados, donde el punto final del trazo anterior es el punto inicial para el siguiente, etc. El punto de inicio también puede ser cambiado usando el método moveTo().

+ +

El ejemplo siguiente dibuja dos triángulos, uno rellenado y el otro contorneado.

+ + + +
function draw() {
+  var canvas = document.getElementById('canvas');
+  if (canvas.getContext){
+    var ctx = canvas.getContext('2d');
+
+    // Triángulo rellenado
+    ctx.beginPath();
+    ctx.moveTo(25,25);
+    ctx.lineTo(105,25);
+    ctx.lineTo(25,105);
+    ctx.fill();
+
+    // Triángulo contorneado
+    ctx.beginPath();
+    ctx.moveTo(125,125);
+    ctx.lineTo(125,45);
+    ctx.lineTo(45,125);
+    ctx.closePath();
+    ctx.stroke();
+  }
+}
+
+ +

Esto comienza llamando a beginPath() para empezar una nueva forma. Entonces usamos el método moveTo() para mover el punto de inicio a la posición deseada. Debajo de esto dos líneas son dibujadas lo cual pinta dos lados del triángulo.

+ +

{{EmbedLiveSample("Lines", 160, 160, "https://mdn.mozillademos.org/files/238/Canvas_lineTo.png")}}

+ +

Te darás cuenta de la diferencia entre el triángulo rellenado y el contorneado. Esto es, como se menciona arriba, porque las formas son automáticamente cerradas cuando un trazo es rellenado, pero no cuando esta contorneado. Si sacamos el closePath() para el triángulo contorneado, solamente dos líneas serian dibujadas, no un triángulo completo.

+ +

Arcos

+ +

Para dibujar arcos o circulos usamos el método arc(). También puedes usar arcTo(), pero su implementación es un poco menos confiable, así que no lo cubriremos aquí.

+ +
+
arc(x, y, radius, startAngle, endAngle, anticlockwise)
+
Dibuja un arco.
+
+ +

Este método toma cinco parámetros: x e y son las coordenadas del centro del círculo en el cual el arco debería ser dibujado. radius se explica por sí solo. Los parámetros startAngle y endAngle definen el punto de inicio y punto final del arco en radianes a lo largo de la curva del círculo. Estos son medidos desde el eje x. El parámetro anticlockwise es un valor Booleano el cual cuando es verdadero (true) dibuja el arco al contrario de las manecillas del reloj, de lo contrario el arco es dibujado al sentido de las manecillas del reloj.

+ +
+

Nota: Los ángulos en la función del arco (arc) son medidos en radianes, no en grados. Para convertir grados a radianes puedes usar la siguiente expresión en Javascript: radians = (Math.PI/180)*degrees.

+
+ +

El siguiente ejemplo es un poco más complejo que otros que hemos visto arriba. Esto dibuja 12 diferentes arcos, todos con diferentes ángulos y rellenos.

+ +

Las dos sentencias for son para iterar a través de las filas y columnas de los arcos. Para cada arco, empezamos un nuevo trazo llamando beginPath(). En el código, cada uno de los parámetros para el arco estan en una variable para su entendimiento, pero no es necesario esto en la vida real.

+ +

Las coordenadas x e y deberían ser suficientemente claras. radius y startAngle estan arreglados. El endAngle inicia en 180 grados (la mitad de un círculo) en la primera columna y es incrementado por pasos de 90 grados, culminando en un círculo completo en la última columna.

+ +

El parámetro clockwise resulta, en la primera y tercera fila siendo dibujado como un arco al sentido de las manecillas de reloj y la segunda y cuarta fila como arcos al contrario de las manecillas de reloj. Finalmente, la estructura if hace los arcos contorneados a la mitad desde arriba y los arcos hacia abajorellenados a la mitad.

+ + + +
function draw() {
+  var canvas = document.getElementById('canvas');
+  if (canvas.getContext){
+    var ctx = canvas.getContext('2d');
+
+    for(var i=0;i<4;i++){
+      for(var j=0;j<3;j++){
+        ctx.beginPath();
+        var x              = 25+j*50;               // Coordenada x
+        var y              = 25+i*50;               // Coordenada y
+        var radius         = 20;                    // Radio del arco
+        var startAngle     = 0;                     // Punto inicial del círculo
+        var endAngle       = Math.PI+(Math.PI*j)/2; // Punto final del círculo
+        var anticlockwise  = i%2==0 ? false : true; // Sentido de las manecillas del reloj y contrario a ellas
+
+        ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);
+
+        if (i>1){
+          ctx.fill();
+        } else {
+          ctx.stroke();
+        }
+      }
+    }
+  }
+}
+
+{{EmbedLiveSample("Arcs", 160, 210, "https://mdn.mozillademos.org/files/204/Canvas_arc.png")}} + +

Curvas Bezier curvas cuadráticas

+ +

El siguiente tipo de trazos disponibles son las  curvas Bézier, en sus dos variantes, cúbicas y cuadráticas. Son usadas generalmente para dibujar complejas formas orgánicas.

+ +
+
quadraticCurveTo(cp1x, cp1y, x, y)
+
Dibuja una curva cuadrática de Bézier desde la posición actual de la pluma hasta el punto final especificado por x e y, utilizando el punto de control especificado por cp1x y cp1y.
+
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
+
Dibuja una curva cúbica de Bézier desde la posición actual de la pluma hasta el punto final especificado por x e y, utilizando los puntos de control especificados por (cp1x, cp1y) y (cp2x, cp2y).
+
+ +

La diferencia entre estos puede describirse mejor utilizando la imagen de la derecha. Una curva cuadrática de Bézier tiene un punto inicial y un punto final (puntos azules) y un solo punto de control (indicado por el punto rojo), mientras que una curva cúbica de Bézier utiliza dos puntos de control.

+ +

Los parámetros x e y de ambos métodos son las coordenadas del punto final. cp1x y cp1y son las coordenadas del primer punto de control, y cp2x y cp2y son las coordenadas del segundo punto de control.

+ +

El uso de curvas cuadráticas y cúbicas Bézier puede ser bastante difícil, ya que a diferencia del software de dibujo vectorial como Adobe Illustrator, no tenemos respuesta visual directa en cuanto a lo que estamos haciendo. Esto hace bastante difícil dibujar formas complejas. En el siguiente ejemplo, vamos a dibujar algunas formas orgánicas simples, pero si tienes el tiempo y, sobre todo, la paciencia, se pueden crear formas mucho más complejas.

+ +

No hay nada muy difícil en estos ejemplos. En ambos casos vemos una sucesión de curvas que se dibujan que finalmente dan lugar a una forma completa.

+ +

Curvas de Bezier cuadraticas

+ +

Este ejemplo usa multiples curvas cuadraticas de Bézier para renderizar un globo de voz.

+ + + +
function draw() {
+  var canvas = document.getElementById('canvas');
+  if (canvas.getContext) {
+    var ctx = canvas.getContext('2d');
+
+    // Quadratric curves example
+    ctx.beginPath();
+    ctx.moveTo(75,25);
+    ctx.quadraticCurveTo(25,25,25,62.5);
+    ctx.quadraticCurveTo(25,100,50,100);
+    ctx.quadraticCurveTo(50,120,30,125);
+    ctx.quadraticCurveTo(60,120,65,100);
+    ctx.quadraticCurveTo(125,100,125,62.5);
+    ctx.quadraticCurveTo(125,25,75,25);
+    ctx.stroke();
+  }
+}
+
+ +

{{EmbedLiveSample("Quadratic_Bezier_curves", 160, 160, "https://mdn.mozillademos.org/files/243/Canvas_quadratic.png")}}

+ +

Curvas cúbicas Bezier

+ +

Este ejemplo dibuja un corazon usanco curvas cúbicas de Bézier.

+ + + +
function draw() {
+  var canvas = document.getElementById('canvas');
+  if (canvas.getContext){
+    var ctx = canvas.getContext('2d');
+
+    // Quadratric curves example
+    ctx.beginPath();
+    ctx.moveTo(75,40);
+    ctx.bezierCurveTo(75,37,70,25,50,25);
+    ctx.bezierCurveTo(20,25,20,62.5,20,62.5);
+    ctx.bezierCurveTo(20,80,40,102,75,120);
+    ctx.bezierCurveTo(110,102,130,80,130,62.5);
+    ctx.bezierCurveTo(130,62.5,130,25,100,25);
+    ctx.bezierCurveTo(85,25,75,37,75,40);
+    ctx.fill();
+  }
+}
+
+ +

{{EmbedLiveSample("Cubic_Bezier_curves", 160, 160, "https://mdn.mozillademos.org/files/207/Canvas_bezier.png")}}

+ +

Rectangles

+ +

In addition to the three methods we saw in {{anch("Drawing rectangles")}}, which draw rectangular shapes directly to the canvas, there's also the rect() method, which adds a rectangular path to a currently open path.

+ +
+
rect(x, y, width, height)
+
Draws a rectangle whose top-left corner is specified by (x, y) with the specified width and height.
+
+ +

When this method is executed, the moveTo() method is automatically called with the parameters (0,0). In other words, the current pen position is automatically reset to the default coordinates.

+ +

Making combinations

+ +

So far, each example on this page has used only one type of path function per shape. However, there's no limitation to the number or types of paths you can use to create a shape. So in this final example, let's combine all of the path functions to make a set of very famous game characters.

+ + + +
function draw() {
+  var canvas = document.getElementById('canvas');
+  if (canvas.getContext){
+    var ctx = canvas.getContext('2d');
+
+    roundedRect(ctx,12,12,150,150,15);
+    roundedRect(ctx,19,19,150,150,9);
+    roundedRect(ctx,53,53,49,33,10);
+    roundedRect(ctx,53,119,49,16,6);
+    roundedRect(ctx,135,53,49,33,10);
+    roundedRect(ctx,135,119,25,49,10);
+
+    ctx.beginPath();
+    ctx.arc(37,37,13,Math.PI/7,-Math.PI/7,false);
+    ctx.lineTo(31,37);
+    ctx.fill();
+
+    for(var i=0;i<8;i++){
+      ctx.fillRect(51+i*16,35,4,4);
+    }
+
+    for(i=0;i<6;i++){
+      ctx.fillRect(115,51+i*16,4,4);
+    }
+
+    for(i=0;i<8;i++){
+      ctx.fillRect(51+i*16,99,4,4);
+    }
+
+    ctx.beginPath();
+    ctx.moveTo(83,116);
+    ctx.lineTo(83,102);
+    ctx.bezierCurveTo(83,94,89,88,97,88);
+    ctx.bezierCurveTo(105,88,111,94,111,102);
+    ctx.lineTo(111,116);
+    ctx.lineTo(106.333,111.333);
+    ctx.lineTo(101.666,116);
+    ctx.lineTo(97,111.333);
+    ctx.lineTo(92.333,116);
+    ctx.lineTo(87.666,111.333);
+    ctx.lineTo(83,116);
+    ctx.fill();
+
+    ctx.fillStyle = "white";
+    ctx.beginPath();
+    ctx.moveTo(91,96);
+    ctx.bezierCurveTo(88,96,87,99,87,101);
+    ctx.bezierCurveTo(87,103,88,106,91,106);
+    ctx.bezierCurveTo(94,106,95,103,95,101);
+    ctx.bezierCurveTo(95,99,94,96,91,96);
+    ctx.moveTo(103,96);
+    ctx.bezierCurveTo(100,96,99,99,99,101);
+    ctx.bezierCurveTo(99,103,100,106,103,106);
+    ctx.bezierCurveTo(106,106,107,103,107,101);
+    ctx.bezierCurveTo(107,99,106,96,103,96);
+    ctx.fill();
+
+    ctx.fillStyle = "black";
+    ctx.beginPath();
+    ctx.arc(101,102,2,0,Math.PI*2,true);
+    ctx.fill();
+
+    ctx.beginPath();
+    ctx.arc(89,102,2,0,Math.PI*2,true);
+    ctx.fill();
+  }
+}
+
+// A utility function to draw a rectangle with rounded corners.
+
+function roundedRect(ctx,x,y,width,height,radius){
+  ctx.beginPath();
+  ctx.moveTo(x,y+radius);
+  ctx.lineTo(x,y+height-radius);
+  ctx.quadraticCurveTo(x,y+height,x+radius,y+height);
+  ctx.lineTo(x+width-radius,y+height);
+  ctx.quadraticCurveTo(x+width,y+height,x+width,y+height-radius);
+  ctx.lineTo(x+width,y+radius);
+  ctx.quadraticCurveTo(x+width,y,x+width-radius,y);
+  ctx.lineTo(x+radius,y);
+  ctx.quadraticCurveTo(x,y,x,y+radius);
+  ctx.stroke();
+}
+
+ +
+

The resulting image looks like this:

+ +

{{EmbedLiveSample("Making_combinations", 160, 160)}}

+ +

We won't go over this in detail, since it's actually surprisingly simple. The most important things to note are the use of the fillStyle property on the drawing context, and the use of a utility function (in this case roundedRect()). Using utility functions for bits of drawing you do often can be very helpful and reduce the amount of code you need, as well as its complexity.

+ +

We'll take another look at fillStyle, in more detail, later in this tutorial. Here, all we're doing is using it to change the fill color for paths from the default color of black to white, and then back again.

+ +

{{PreviousNext("Web/Guide/HTML/Canvas_tutorial/Basic_usage", "Web/Guide/HTML/Canvas_tutorial/Using_images")}}

+
+
+ +

 

diff --git a/files/es/web/api/canvas_api/tutorial/drawing_text/index.html b/files/es/web/api/canvas_api/tutorial/drawing_text/index.html new file mode 100644 index 0000000000..10a5970824 --- /dev/null +++ b/files/es/web/api/canvas_api/tutorial/drawing_text/index.html @@ -0,0 +1,67 @@ +--- +title: Dibujar texto usando canvas +slug: Dibujar_texto_usando_canvas +tags: + - 'HTML:Canvas' +translation_of: Web/API/Canvas_API/Tutorial/Drawing_text +--- +

{{ Gecko_minversion_header(1.9) }} +{{ Fx_minversion_header(3) }} +El elemento <canvas> permite dibujar texto en él a través de una API experimental de Mozilla. +

+

API

+
attribute DOMString mozTextStyle;
+void mozDrawText(in DOMString textToDraw);
+float mozMeasureText(in DOMString textToMeasure);
+void mozPathText(in DOMString textToPath);
+void mozTextAlongPath(in DOMString textToDraw, in boolean stroke);
+
+

Notas

+ +

Demostraciones

+

Mira algunos ejemplos aquí, aquí, y aquí. +

+

Cambiar el tipo de letra actual

+

El atributo mozTextStyle contiene el estilo de texto actual. Usa la misma sintaxis que el especificado para las tipografías CSS. +

Ej: +

+
ctx.mozTextStyle = "20pt Arial"
+
+

Dibujar texto

+

Dibujar es muy sencillo. mozDrawText usa el estilo de texto actual, cualquiera que sea éste. Se usa el color de relleno del contexto como color del texto. +

+
ctx.translate(10, 50);
+ctx.fillStyle = "Red";
+ctx.mozDrawText("Sample String");
+
+

Medir texto

+

A veces es útil saber qué tan ancho es un trozo de texto en particular (para centrarlo en una ventana, por ejemplo). +

+
var text = "Sample String";
+var width = ctx.canvas.width;
+var len = ctx.mozMeasureText(text);
+ctx.translate(len/2, 0);
+ctx.mozDrawText(text);
+
+

Interacción texto/trazo

+

Si quieres tachar un texto, mozDrawText no te lo permite. En cambio, mozPathText agrega el tachado de texto al trazo actual. +

+
ctx.fillStyle = "green";
+ctx.strokeStyle = "black";
+ctx.mozPathText("Sample String");
+ctx.fill()
+ctx.stroke()
+
+

Supongamos ahora que quieres agregar un texto que se acomode a un trazo que dibujaste (una línea curva o algo parecido) es donde aparece mozTextAlongPath. Al contrario de otras funciones de texto, mozTextAlongPath necesita dos argumentos: el texto y qué se quiere hacer con él. mozTextAlongPath aproxima el trazo actual como una serie de segmentos de línea y ubica cada carácter encima de ese trazo. Los caracteres no son cambiados de tamaño o transformados de acuerdo a la curvatura de la base; toman la orientación del trazo a la mitad del carácter. +

Una vez que mozTextAlongPath sabe dónde está el carácter, busca el segundo parámetro para decidir qué hacer con él. Si el segundo parámetro es false, entonces dibuja el carácter como lo haría mozDrawText. Si es true, agrega el carácter al trazo actual, como lo hace mozPathText. Esto puede usarse para crear efectos únicos. +


+


+

+
+
+{{ languages( { "en": "en/Drawing_text_using_a_canvas", "fr": "fr/Dessin_de_texte_avec_canvas", "ja": "ja/Drawing_text_using_a_canvas", "pl": "pl/Rysowanie_tekstu_przy_u\u017cyciu_canvas" } ) }} diff --git a/files/es/web/api/canvas_api/tutorial/hit_regions_and_accessibility/index.html b/files/es/web/api/canvas_api/tutorial/hit_regions_and_accessibility/index.html new file mode 100644 index 0000000000..967710de49 --- /dev/null +++ b/files/es/web/api/canvas_api/tutorial/hit_regions_and_accessibility/index.html @@ -0,0 +1,99 @@ +--- +title: Hit regions and accessibility +slug: Web/Guide/HTML/Canvas_tutorial/Hit_regions_and_accessibility +translation_of: Web/API/Canvas_API/Tutorial/Hit_regions_and_accessibility +--- +
{{CanvasSidebar}} {{ PreviousNext("Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas", "Web/API/Canvas_API/Tutorial/Optimizing_canvas") }}
+ +
The {{HTMLElement("canvas")}} element on its own is just a bitmap and does not provide information about any drawn objects. Canvas content is not exposed to accessibility tools like semantic HTML is. In general, you should avoid using canvas in an accessible website or app. The following guidelines can help to make it more accessible.
+ +
El elemento {{HTMLElement ("canvas")}} por sí solo es solo un mapa de bits y no proporciona información sobre ningún objeto dibujado. El contenido del lienzo no está expuesto a herramientas de accesibilidad como el HTML semántico. En general, debe evitar usar canvas en un sitio web o aplicación accesible. Las siguientes pautas pueden ayudar a que sea más accesible.
+ +

Fallback content

+ +

The content inside the <canvas> ... </canvas> tags can be used as a fallback for browsers which don't support canvas rendering. It's also very useful for assistive technology users (like screen readers) which can read and interpret the sub DOM in it. A good example at html5accessibility.com demonstrates how this can be done:

+ +
<canvas>
+  <h2>Shapes</h2>
+  <p>A rectangle with a black border.
+   In the background is a pink circle.
+   Partially overlaying the <a href="http://en.wikipedia.org/wiki/Circle" onfocus="drawCircle();" onblur="drawPicture();">circle</a>.
+   Partially overlaying the circle is a green
+   <a href="http://en.wikipedia.org/wiki/Square" onfocus="drawSquare();" onblur="drawPicture();">square</a>
+   and a purple <a href="http://en.wikipedia.org/wiki/Triangle" onfocus="drawTriangle();" onblur="drawPicture();">triangle</a>,
+   both of which are semi-opaque, so the full circle can be seen underneath.</p>
+</canvas> 
+ +

See the video how NVDA reads this example by Steve Faulkner.

+ +

ARIA rules

+ +

Accessible Rich Internet Applications (ARIA) defines ways to make Web content and Web applications more accessible to people with disabilities. You can use ARIA attributes to describe the behavior and purpose of the canvas element. See ARIA and ARIA techniques for more information.

+ +
<canvas id="button" tabindex="0" role="button" aria-pressed="false" aria-label="Start game"></canvas>
+
+ +

Hit regions

+ +

Whether the mouse coordinates are within a particular area on the canvas, is a common problem to solve. The hit region API allows you to define an area of your canvas and provides another possibility to expose interactive content on a canvas to accessibility tools. It allows you to make hit detection easier and lets you route events to DOM elements. The API has the following three methods (which are still experimental in current web browsers; check the browser compatibility tables).

+ +
+
{{domxref("CanvasRenderingContext2D.addHitRegion()")}} {{experimental_inline}}
+
Adds a hit region to the canvas.
+
{{domxref("CanvasRenderingContext2D.removeHitRegion()")}} {{experimental_inline}}
+
Removes the hit region with the specified id from the canvas.
+
{{domxref("CanvasRenderingContext2D.clearHitRegions()")}} {{experimental_inline}}
+
Removes all hit regions from the canvas.
+
+ +

You can add a hit region to your path and check for the {{domxref("MouseEvent.region")}} property to test if your mouse is hitting your region, for example.

+ +
<canvas id="canvas"></canvas>
+<script>
+var canvas = document.getElementById('canvas');
+var ctx = canvas.getContext('2d');
+
+ctx.beginPath();
+ctx.arc(70, 80, 10, 0, 2 * Math.PI, false);
+ctx.fill();
+ctx.addHitRegion({id: 'circle'});
+
+canvas.addEventListener('mousemove', function(event) {
+  if (event.region) {
+    alert('hit region: ' + event.region);
+  }
+});
+</script>
+ +

The addHitRegion() method also takes a control option to route events to an element (that is a descendant of the canvas):

+ +
ctx.addHitRegion({control: element});
+ +

This can be useful for routing to {{HTMLElement("input")}} elements, for example. See also this codepen demo.

+ +

Focus rings

+ +

When working with the keyboard, focus rings are a handy indicator to help navigating on a page. To draw focus rings on a canvas drawing, the drawFocusIfNeeded property can be used.

+ +
+
{{domxref("CanvasRenderingContext2D.drawFocusIfNeeded()")}} {{experimental_inline}}
+
If a given element is focused, this method draws a focus ring around the current path.
+
+ +

Additionally, the scrollPathIntoView() method can be used to make an element visible on the screen if focused, for example.

+ +
+
{{domxref("CanvasRenderingContext2D.scrollPathIntoView()")}} {{experimental_inline}}
+
Scrolls the current path or a given path into the view.
+
+ +

See also

+ + + +
{{ PreviousNext("Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas", "Web/API/Canvas_API/Tutorial/Optimizing_canvas") }}
diff --git a/files/es/web/api/canvas_api/tutorial/index.html b/files/es/web/api/canvas_api/tutorial/index.html new file mode 100644 index 0000000000..da5b0b3cc9 --- /dev/null +++ b/files/es/web/api/canvas_api/tutorial/index.html @@ -0,0 +1,61 @@ +--- +title: Tutorial Canvas +slug: Web/Guide/HTML/Canvas_tutorial +tags: + - Canvas + - HTML5 + - graficos +translation_of: Web/API/Canvas_API/Tutorial +--- +

+ +

<canvas> es un elemento HTML el cual puede ser usado para dibujar gráficos usando scripts (normalmente JavaScript). Este puede, por ejemplo, ser usado para dibujar gráficos, realizar composición de fotos o simples (y no tan simples) animaciones. Las imágenes a la derecha muestran algunos ejemplos de implementaciones <canvas>  las cuales se verán en un futuro en este tutorial.

+ +

<canvas>  fue introducido primero por Apple para el Mac OS X Dashboard y después implementado en Safari y Google Chrome. Navegadores basados en Gecko 1.8, tal como Firefox 1.5, que también soportan este elemento. El <canvas> es un elemento parte de las especificaciones de la WhatWG Web applications 1.0 mejor conocida como HTML5.

+ +

En este tutorial se describe cómo usar el elemento <canvas> para dibujar gráficos en 2D, empezando con lo básico. Los ejemplos le proveerán mayor claridad a las ideas que pueda tener referentes al canvas, así como los códigos que necesita para crear su propio contenido.

+ +

Antes de Empezar

+ +

Usar el elemento <canvas> no es algo muy díficil pero necesita saber y entender los aspectos básicos del HTML y JavaScript. El elemento <canvas> no está soportado en navegadores viejos, pero están soportado en la mayoría de las versiones más recientes de los navegadores. El tamaño por defecto del canvas es 300px * 150px [ancho (width) * alto (height)]. Pero se puede personalizar el tamaño usando las propiedades height y width de CSS. Con el fin de dibujar gráficos en el lienzo <canvas> se utiliza un objeto de contexto de JavaScript que crea gráficos sobre la marcha.

+ +

En este Tutorial

+ + + +

Vea también

+ + + +

Nota a los contribuyentes

+ +

Debido a un desafortunado error técnico que ocurrió el 17 de junio del 2013, perdimos la historia de este tutorial, incluyendo atribuciones a todos los contribuyentes del pasado a su contenido. Pedimos disculpas por esto, y esperamos que perdone este desafortunado percance.

+ +
{{ Next("Web/Guide/HTML/Canvas_tutorial/Basic_usage") }}
diff --git a/files/es/web/api/canvas_api/tutorial/optimizing_canvas/index.html b/files/es/web/api/canvas_api/tutorial/optimizing_canvas/index.html new file mode 100644 index 0000000000..145e2734f0 --- /dev/null +++ b/files/es/web/api/canvas_api/tutorial/optimizing_canvas/index.html @@ -0,0 +1,19 @@ +--- +title: Optimizing canvas +slug: Web/Guide/HTML/Canvas_tutorial/Optimizing_canvas +translation_of: Web/API/Canvas_API/Tutorial/Optimizing_canvas +--- +

{{HTMLElement("canvas")}} es uno de los estándares más utilizados para la representación de gráficos 2D en la Web. Se utiliza ampliamente en los juegos y visualizaciones complejas. Sin embargo, as Web sites and apps push canvas to the limits, el rendimiento comienza a sufrir. This article aims to provide suggestions for optimizing your use of the canvas element, to ensure that your Web site or app performs well.

+

A continuación una lista de tips par mejorar el rendimiento:

+ +

{{PreviousNext("Web/Guide/HTML/Canvas_tutorial/Basic_animations")}}

diff --git a/files/es/web/api/canvas_api/tutorial/pixel_manipulation_with_canvas/index.html b/files/es/web/api/canvas_api/tutorial/pixel_manipulation_with_canvas/index.html new file mode 100644 index 0000000000..14ccc9c4a5 --- /dev/null +++ b/files/es/web/api/canvas_api/tutorial/pixel_manipulation_with_canvas/index.html @@ -0,0 +1,301 @@ +--- +title: Pixel manipulation with canvas +slug: Web/Guide/HTML/Canvas_tutorial/Pixel_manipulation_with_canvas +translation_of: Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas +--- +
{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Advanced_animations", "Web/API/Canvas_API/Tutorial/Hit_regions_and_accessibility")}}
+ +
+

Hasta ahora, no habíamos mirado los píxeles reales de nuestro canvas. Con el objeto ImageData, puedes leer y escribir directamente un array de datos para manipular píxeles.

+ +

También veremos cómo se puede controlar el suavizado de la imagen (antialiasing) y cómo guardar imágenes de tu canvas.

+
+ +

El objeto ImageData

+ +

El objeto {{domxref("ImageData")}} representa los datos pixelados subyacentes de un área de un objeto lienzo. Contiene los siguientes atributos de sólo lectura:

+ +
+
width
+
El ancho de la imagen en píxeles.
+
height
+
La altura de la imagen en píxeles.
+
data
+
Un objeto {{jsxref("Uint8ClampedArray")}} que representa un array unidimensional, contiene información en formato RGBA, con valores desde 0 hasta 255 (incluído).
+
+ +

La propiedad data devuelve un  {{jsxref("Uint8ClampedArray")}}, al que se puede acceder para ver los datos originales del pixel; cada pixel está representado por cuatro valores (rojo, verde, azul, y alfa, en ese orden; esto es, formato "RGBA"). Cada componente de color se representa con un valor entero entre 0 y 255. Dentro del array, cada componente ocupa un índice consecutivo, comenzando con 0 desde el punto superior izquierdo, continuando de izquierda a derecha y de arriba hacia abajo, a través del array.

+ +

El {{jsxref("Uint8ClampedArray")}} contiene alto × ancho × 4 bytes de datos, con valores de índice en el rango entre 0 y (alto×ancho×4)-1.

+ +

Por ejemplo, para leer el valor del componente azul del pixel en la columna 200, fila 50 de una imagen, deberías hacer lo siguiente:

+ +

blueComponent = imageData.data[((50 * (imageData.width * 4)) + (200 * 4)) + 2];

+ +

Si se le da un conjunto de coordenadas (X e Y), puede que termine haciendo algo así:

+ +
var xCoord = 50;
+var yCoord = 100;
+var canvasWidth = 1024;
+
+function getColorIndicesForCoord(x, y, width) {
+  var red = y * (width * 4) + x * 4;
+  return [red, red + 1, red + 2, red + 3];
+}
+
+var colorIndices = getColorIndicesForCoord(xCoord, yCoord, canvasWidth);
+
+var redIndex = colorIndices[0];
+var greenIndex = colorIndices[1];
+var blueIndex = colorIndices[2];
+var alphaIndex = colorIndices[3];
+
+var redForCoord = imageData.data[redIndex];
+var greenForCoord = imageData.data[greenIndex];
+var blueForCoord = imageData.data[blueIndex];
+var alphaForCoord = imageData.data[alphaIndex];
+
+ +

O, en ES6 sería algo así:

+ +
const xCoord = 50;
+const yCoord = 100;
+const canvasWidth = 1024;
+
+const getColorIndicesForCoord = (x, y, width) => {
+  const red = y * (width * 4) + x * 4;
+  return [red, red + 1, red + 2, red + 3];
+};
+
+const colorIndices = getColorIndicesForCoord(xCoord, yCoord, canvasWidth);
+
+const [redIndex, greenIndex, blueIndex, alphaIndex] = colorIndices;
+
+ +

You may also access the size of the pixel array in bytes by reading the Uint8ClampedArray.length attribute:

+ +
var numBytes = imageData.data.length;
+
+ +

Creando un objeto ImageData

+ +

Para crear un objeto nuevo y vacío tipo ImageData, debes usar el método  {{domxref("CanvasRenderingContext2D.createImageData", "createImageData()")}}. Hay dos versiones del método createImageData():

+ +
var myImageData = ctx.createImageData(width, height);
+ +

Esto crea un nuevo objeto ImageData con las dimensiones especificadas. Todos los pixels tienen valor correspondiente a negro - transparente (0,0,0,0).

+ +

También puedes crear un nuevo objeto ImageData con las mismas dimensiones que otro objeto, especificado por anotherImageData. Los píxels del nuevo objeto tienen valor negro - transparente. ¡Esto no es una copia de los datos de la imagen!

+ +
var myImageData = ctx.createImageData(anotherImageData);
+ +

Getting the pixel data for a context

+ +

To obtain an ImageData object containing a copy of the pixel data for a canvas context, you can use the getImageData() method:

+ +
var myImageData = ctx.getImageData(left, top, width, height);
+ +

This method returns an ImageData object representing the pixel data for the area of the canvas whose corners are represented by the points (left,top), (left+width, top), (left, top+height), and (left+width, top+height). The coordinates are specified in canvas coordinate space units.

+ +
+

Note: Any pixels outside the canvas are returned as transparent black in the resulting ImageData object.

+
+ +

This method is also demonstrated in the article Manipulating video using canvas.

+ +

A color picker

+ +

In this example we are using the getImageData() method to display the color under the mouse cursor. For this, we need the current position of the mouse with layerX and layerY, then we look up the pixel data on that position in the pixel array that getImageData() provides us. Finally, we use the array data to set a background color and a text in the <div> to display the color.

+ + + +
var img = new Image();
+img.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg';
+var canvas = document.getElementById('canvas');
+var ctx = canvas.getContext('2d');
+img.onload = function() {
+  ctx.drawImage(img, 0, 0);
+  img.style.display = 'none';
+};
+var color = document.getElementById('color');
+function pick(event) {
+  var x = event.layerX;
+  var y = event.layerY;
+  var pixel = ctx.getImageData(x, y, 1, 1);
+  var data = pixel.data;
+  var rgba = 'rgba(' + data[0] + ', ' + data[1] +
+             ', ' + data[2] + ', ' + (data[3] / 255) + ')';
+  color.style.background =  rgba;
+  color.textContent = rgba;
+}
+canvas.addEventListener('mousemove', pick);
+
+ +

{{ EmbedLiveSample('A_color_picker', 610, 240) }}

+ +

Painting pixel data into a context

+ +

You can use the putImageData() method to paint pixel data into a context:

+ +
ctx.putImageData(myImageData, dx, dy);
+
+ +

The dx and dy parameters indicate the device coordinates within the context at which to paint the top left corner of the pixel data you wish to draw.

+ +

For example, to paint the entire image represented by myImageData to the top left corner of the context, you can simply do the following:

+ +
ctx.putImageData(myImageData, 0, 0);
+
+ +

Grayscaling and inverting colors

+ +

In this example we iterate over all pixels to change their values, then we put the modified pixel array back to the canvas using putImageData(). The invert function simply subtracts each color from the max value 255. The grayscale function simply uses the average of red, green and blue. You can also use a weighted average, given by the formula x = 0.299r + 0.587g + 0.114b, for example. See Grayscale on Wikipedia for more information.

+ + + +
var img = new Image();
+img.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg';
+img.onload = function() {
+  draw(this);
+};
+
+function draw(img) {
+  var canvas = document.getElementById('canvas');
+  var ctx = canvas.getContext('2d');
+  ctx.drawImage(img, 0, 0);
+  img.style.display = 'none';
+  var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
+  var data = imageData.data;
+
+  var invert = function() {
+    for (var i = 0; i < data.length; i += 4) {
+      data[i]     = 255 - data[i];     // red
+      data[i + 1] = 255 - data[i + 1]; // green
+      data[i + 2] = 255 - data[i + 2]; // blue
+    }
+    ctx.putImageData(imageData, 0, 0);
+  };
+
+  var grayscale = function() {
+    for (var i = 0; i < data.length; i += 4) {
+      var avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
+      data[i]     = avg; // red
+      data[i + 1] = avg; // green
+      data[i + 2] = avg; // blue
+    }
+    ctx.putImageData(imageData, 0, 0);
+  };
+
+  var invertbtn = document.getElementById('invertbtn');
+  invertbtn.addEventListener('click', invert);
+  var grayscalebtn = document.getElementById('grayscalebtn');
+  grayscalebtn.addEventListener('click', grayscale);
+}
+
+ +

{{ EmbedLiveSample('Grayscaling_and_inverting_colors', 330, 270) }}

+ +

Zooming and anti-aliasing

+ +

With the help of the {{domxref("CanvasRenderingContext2D.drawImage", "drawImage()")}} method, a second canvas and the {{domxref("CanvasRenderingContext2D.imageSmoothingEnabled", "imageSmoothingEnabled")}} property, we are able to zoom into our picture and see the details.

+ +

We get the position of the mouse and crop an image of 5 pixels left and above to 5 pixels right and below. Then we copy that one over to another canvas and resize the image to the size we want it to. In the zoom canvas we resize a 10×10 pixel crop of the original canvas to 200×200.

+ +
zoomctx.drawImage(canvas,
+                  Math.abs(x - 5), Math.abs(y - 5),
+                  10, 10, 0, 0, 200, 200);
+ +

Because anti-aliasing is enabled by default, we might want to disable the smoothing to see clear pixels. You can toggle the checkbox to see the effect of the imageSmoothingEnabled property (which needs prefixes for different browsers).

+ + + + + +
var img = new Image();
+img.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg';
+img.onload = function() {
+  draw(this);
+};
+
+function draw(img) {
+  var canvas = document.getElementById('canvas');
+  var ctx = canvas.getContext('2d');
+  ctx.drawImage(img, 0, 0);
+  img.style.display = 'none';
+  var zoomctx = document.getElementById('zoom').getContext('2d');
+
+  var smoothbtn = document.getElementById('smoothbtn');
+  var toggleSmoothing = function(event) {
+    zoomctx.imageSmoothingEnabled = this.checked;
+    zoomctx.mozImageSmoothingEnabled = this.checked;
+    zoomctx.webkitImageSmoothingEnabled = this.checked;
+    zoomctx.msImageSmoothingEnabled = this.checked;
+  };
+  smoothbtn.addEventListener('change', toggleSmoothing);
+
+  var zoom = function(event) {
+    var x = event.layerX;
+    var y = event.layerY;
+    zoomctx.drawImage(canvas,
+                      Math.abs(x - 5),
+                      Math.abs(y - 5),
+                      10, 10,
+                      0, 0,
+                      200, 200);
+  };
+
+  canvas.addEventListener('mousemove', zoom);
+}
+ +

{{ EmbedLiveSample('Zoom_example', 620, 490) }}

+ +

Guardando las imágenes

+ +

The {{domxref("HTMLCanvasElement")}} provides a toDataURL() method, which is useful when saving images. It returns a data URI containing a representation of the image in the format specified by the type parameter (defaults to PNG). The returned image is in a resolution of 96 dpi.

+ +
+
{{domxref("HTMLCanvasElement.toDataURL", "canvas.toDataURL('image/png')")}}
+
Default setting. Creates a PNG image.
+
{{domxref("HTMLCanvasElement.toDataURL", "canvas.toDataURL('image/jpeg', quality)")}}
+
Creates a JPG image. Optionally, you can provide a quality in the range from 0 to 1, with one being the best quality and with 0 almost not recognizable but small in file size.
+
+ +

Once you have generated a data URI from you canvas, you are able to use it as the source of any {{HTMLElement("image")}} or put it into a hyper link with a download attribute to save it to disc, for example.

+ +

You can also create a {{domxref("Blob")}} from the canvas.

+ +
+
{{domxref("HTMLCanvasElement.toBlob", "canvas.toBlob(callback, type, encoderOptions)")}}
+
Creates a Blob object representing the image contained in the canvas.
+
+ +

See also

+ + + +

{{PreviousNext("Web/API/Canvas_API/Tutorial/Advanced_animations", "Web/API/Canvas_API/Tutorial/Hit_regions_and_accessibility")}}

diff --git a/files/es/web/api/clipboard_api/index.html b/files/es/web/api/clipboard_api/index.html new file mode 100644 index 0000000000..53e43f6bd5 --- /dev/null +++ b/files/es/web/api/clipboard_api/index.html @@ -0,0 +1,76 @@ +--- +title: API del portapapeles +slug: Web/API/API_del_portapapeles +translation_of: Web/API/Clipboard_API +--- +
{{DefaultAPISidebar("Clipboard API")}}
+ +

La API del portapapeles permite acceder los comandos del portapapeles (cortar, copiar y pegar), así como leer y escribir de manera asíncrona el portapapeles del sistema. Acceder al contenido del portapapeles está sujeta a la API de permisos: El permiso clipboard-write es concedido automáticamente a las páginas cuando están en la pestaña activa. El permiso clipboard-read debe ser solicitado, lo que se puede hacer intentando leer directamente el portapapeles.

+ +

Esta API está diseñada para reemplazar el acceso al portapapeles usando {{domxref("document.execCommand()")}}.

+ +

Accediendo al portapapeles

+ +

En vez de instanciar un objeto Clipboard, se puede acceder al portapapeles del sistema a través de la variable global {{domxref("Navigator.clipboard")}}:

+ +
navigator.clipboard.readText().then(
+  clipText => document.querySelector(".editor").innerText += clipText);
+ +

Esta pieza de código lee el texto que hay en el portapapeles y lo añade al primer elemento que tenga la clase editor. Desde que {{domxref("Clipboard.readText", "readText()")}} (y también {{domxref("Clipboard.read", "read()")}}, de hecho) devuelve una cadena de texto vacía si el contenido del portapapeles no es texto, este código es seguro.

+ +

Interfaces

+ +
+
{{domxref("Clipboard")}} {{securecontext_inline}}
+
Proporciona una interfaz para leer y escribir texto y datos. La especificación se refiere a esto como 'Async Clipboard API.'
+
{{domxref("ClipboardEvent")}} {{securecontext_inline}}
+
Representa la información del evento que se ha disparado. Los eventos que se pueden disparar son: {{domxref("Element/cut_event", "cortar")}}, {{domxref("Element/copy_event", "copiar")}}, y {{domxref("Element/paste_event", "pegar")}}. La especificación se refiere a esto como 'Clipboard Event API'.
+
{{domxref("ClipboardItem")}} {{securecontext_inline}}
+
Representa uno de los objetos del portapapeles, usado en la lectura y escritura de datos.
+
+ +

Especificaciones

+ + + + + + + + + + + + + + +
EspecificaciónEstadoObservaciones
{{SpecName('Clipboard API')}}{{Spec2('Clipboard API')}}Definición primitiva.
+ +

Compatibilidad con navegadores

+ +

Clipboard

+ +
+ + +

{{Compat("api.Clipboard")}}

+ +

ClipboardEvent

+ + + +

{{Compat("api.ClipboardEvent")}}

+ +

ClipboardItem

+ + + +

{{Compat("api.ClipboardItem")}}

+ +

Véase también

+ + +
diff --git a/files/es/web/api/console/tabla/index.html b/files/es/web/api/console/tabla/index.html deleted file mode 100644 index 53f57f5636..0000000000 --- a/files/es/web/api/console/tabla/index.html +++ /dev/null @@ -1,212 +0,0 @@ ---- -title: Console.table() -slug: Web/API/Console/tabla -tags: - - API - - Consola - - DOM - - Depuración -translation_of: Web/API/Console/table ---- -
{{APIRef("Console API")}}
- -

Muestra datos tabulares como una tabla.

- -

Esta función toma un argumento obligatorio: data, que debe ser un array o un objeto, y un parámetro adicional: columns.

- -

Muestra data como una tabla en la consola. Cada elemento en el array (o propiedad enumerable si data es un objeto) será una fila en la tabla.

- -

La primera columna de la tabla se identificará como (index). Si data es un array, sus valores serán los índices del array. Si data es un objeto, entonces sus valores serán los nombres de las propiedades. Tenga en cuenta que (en Firefox) console.table está limitado a mostrar 1000 filas (la primera columna es la llamada index).

- -

{{AvailableInWorkers}}

- -

Colecciones de tipos primitivos

- -

El argumento data puede ser un array o un objeto.

- -
// un array de strings
-
-console.table(["apples", "oranges", "bananas"]);
- -

- -
// un objeto cuyas propiedades son strings
-
-function Person(firstName, lastName) {
-  this.firstName = firstName;
-  this.lastName = lastName;
-}
-
-var me = new Person("John", "Smith");
-
-console.table(me);
- -

- -

Colecciones de tipos compuestos

- -

Si los elementos en el array, o propiedades en el objeto, son también arrays u objetos, sus elementos o propiedades serán enumeradas en la fila, una por columna:

- -
// un array de arrays
-
-var people = [["John", "Smith"], ["Jane", "Doe"], ["Emily", "Jones"]]
-console.table(people);
- -

Table displaying array of arrays

- -
// un array de objetos
-
-function Person(firstName, lastName) {
-  this.firstName = firstName;
-  this.lastName = lastName;
-}
-
-var john = new Person("John", "Smith");
-var jane = new Person("Jane", "Doe");
-var emily = new Person("Emily", "Jones");
-
-console.table([john, jane, emily]);
- -

Tenga en cuenta que si el array contiene objetos, las columnas se etiquetarán con el nombre de la propiedad.

- -

Table displaying array of objects

- -
// un objeto cuyas propiedades son objetos
-
-var family = {};
-
-family.mother = new Person("Jane", "Smith");
-family.father = new Person("John", "Smith");
-family.daughter = new Person("Emily", "Smith");
-
-console.table(family);
- -

Table displaying object of objects

- -

Restringiendo las columnas mostradas

- -

Por defecto, console.table() muestra todos los elementos de cada fila. Puedes emplear el parámetro opcional columns  para seleccionar un subconjunto de columnas que mostrar:

- -
// an array of objects, logging only firstName
-
-function Person(firstName, lastName) {
-  this.firstName = firstName;
-  this.lastName = lastName;
-}
-
-var john = new Person("John", "Smith");
-var jane = new Person("Jane", "Doe");
-var emily = new Person("Emily", "Jones");
-
-console.table([john, jane, emily], ["firstName"]);
- -

Table displaying array of objects with filtered output

- -

Ordenando columnas

- -

Se puede ordenar la tabla por una columna en particular pulsando en la etiqueta de dicha columna.

- -

Sintaxis

- -
console.table(data [, columns]);
-
- -

Parámetros

- -
-
data
-
La información a mostrar. Puede ser tanto un array como un objeto.
-
columns
-
Un array que contenga los nombres de las columnas a incluir.
-
- -

Especificaciones

- - - - - - - - - - - - - - - - -
EspecificaciónEstadoComentario
{{SpecName("Console API", "#table", "console.table()")}}{{Spec2("Console API")}}Definición inicial
- -

Compatibilidad con navegadores

- -
{{CompatibilityTable}}
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CaracterísticaChromeEdgeFirefox (Gecko)Internet ExplorerOperaSafari
Soporte básico{{CompatVersionUnknown}}{{CompatVersionUnknown}}{{CompatGeckoDesktop("34.0")}}{{CompatNo}}{{CompatVersionUnknown}}{{CompatVersionUnknown}}
Disponible en workers{{CompatVersionUnknown}}{{CompatUnknown}}{{CompatGeckoDesktop("38.0")}}{{CompatUnknown}}{{CompatVersionUnknown}}{{CompatUnknown}}
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FeatureAndroidEdgeFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Soporte básico{{CompatUnknown}}{{CompatVersionUnknown}}{{CompatGeckoMobile("34.0")}}{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}
Disponible en workers{{CompatUnknown}}{{CompatUnknown}}{{CompatGeckoMobile("38.0")}}{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}
-
diff --git a/files/es/web/api/console/table/index.html b/files/es/web/api/console/table/index.html new file mode 100644 index 0000000000..53f57f5636 --- /dev/null +++ b/files/es/web/api/console/table/index.html @@ -0,0 +1,212 @@ +--- +title: Console.table() +slug: Web/API/Console/tabla +tags: + - API + - Consola + - DOM + - Depuración +translation_of: Web/API/Console/table +--- +
{{APIRef("Console API")}}
+ +

Muestra datos tabulares como una tabla.

+ +

Esta función toma un argumento obligatorio: data, que debe ser un array o un objeto, y un parámetro adicional: columns.

+ +

Muestra data como una tabla en la consola. Cada elemento en el array (o propiedad enumerable si data es un objeto) será una fila en la tabla.

+ +

La primera columna de la tabla se identificará como (index). Si data es un array, sus valores serán los índices del array. Si data es un objeto, entonces sus valores serán los nombres de las propiedades. Tenga en cuenta que (en Firefox) console.table está limitado a mostrar 1000 filas (la primera columna es la llamada index).

+ +

{{AvailableInWorkers}}

+ +

Colecciones de tipos primitivos

+ +

El argumento data puede ser un array o un objeto.

+ +
// un array de strings
+
+console.table(["apples", "oranges", "bananas"]);
+ +

+ +
// un objeto cuyas propiedades son strings
+
+function Person(firstName, lastName) {
+  this.firstName = firstName;
+  this.lastName = lastName;
+}
+
+var me = new Person("John", "Smith");
+
+console.table(me);
+ +

+ +

Colecciones de tipos compuestos

+ +

Si los elementos en el array, o propiedades en el objeto, son también arrays u objetos, sus elementos o propiedades serán enumeradas en la fila, una por columna:

+ +
// un array de arrays
+
+var people = [["John", "Smith"], ["Jane", "Doe"], ["Emily", "Jones"]]
+console.table(people);
+ +

Table displaying array of arrays

+ +
// un array de objetos
+
+function Person(firstName, lastName) {
+  this.firstName = firstName;
+  this.lastName = lastName;
+}
+
+var john = new Person("John", "Smith");
+var jane = new Person("Jane", "Doe");
+var emily = new Person("Emily", "Jones");
+
+console.table([john, jane, emily]);
+ +

Tenga en cuenta que si el array contiene objetos, las columnas se etiquetarán con el nombre de la propiedad.

+ +

Table displaying array of objects

+ +
// un objeto cuyas propiedades son objetos
+
+var family = {};
+
+family.mother = new Person("Jane", "Smith");
+family.father = new Person("John", "Smith");
+family.daughter = new Person("Emily", "Smith");
+
+console.table(family);
+ +

Table displaying object of objects

+ +

Restringiendo las columnas mostradas

+ +

Por defecto, console.table() muestra todos los elementos de cada fila. Puedes emplear el parámetro opcional columns  para seleccionar un subconjunto de columnas que mostrar:

+ +
// an array of objects, logging only firstName
+
+function Person(firstName, lastName) {
+  this.firstName = firstName;
+  this.lastName = lastName;
+}
+
+var john = new Person("John", "Smith");
+var jane = new Person("Jane", "Doe");
+var emily = new Person("Emily", "Jones");
+
+console.table([john, jane, emily], ["firstName"]);
+ +

Table displaying array of objects with filtered output

+ +

Ordenando columnas

+ +

Se puede ordenar la tabla por una columna en particular pulsando en la etiqueta de dicha columna.

+ +

Sintaxis

+ +
console.table(data [, columns]);
+
+ +

Parámetros

+ +
+
data
+
La información a mostrar. Puede ser tanto un array como un objeto.
+
columns
+
Un array que contenga los nombres de las columnas a incluir.
+
+ +

Especificaciones

+ + + + + + + + + + + + + + + + +
EspecificaciónEstadoComentario
{{SpecName("Console API", "#table", "console.table()")}}{{Spec2("Console API")}}Definición inicial
+ +

Compatibilidad con navegadores

+ +
{{CompatibilityTable}}
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CaracterísticaChromeEdgeFirefox (Gecko)Internet ExplorerOperaSafari
Soporte básico{{CompatVersionUnknown}}{{CompatVersionUnknown}}{{CompatGeckoDesktop("34.0")}}{{CompatNo}}{{CompatVersionUnknown}}{{CompatVersionUnknown}}
Disponible en workers{{CompatVersionUnknown}}{{CompatUnknown}}{{CompatGeckoDesktop("38.0")}}{{CompatUnknown}}{{CompatVersionUnknown}}{{CompatUnknown}}
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FeatureAndroidEdgeFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Soporte básico{{CompatUnknown}}{{CompatVersionUnknown}}{{CompatGeckoMobile("34.0")}}{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}
Disponible en workers{{CompatUnknown}}{{CompatUnknown}}{{CompatGeckoMobile("38.0")}}{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}
+
diff --git a/files/es/web/api/crypto/getrandomvalues/index.html b/files/es/web/api/crypto/getrandomvalues/index.html new file mode 100644 index 0000000000..b6e09439a9 --- /dev/null +++ b/files/es/web/api/crypto/getrandomvalues/index.html @@ -0,0 +1,75 @@ +--- +title: Crypto.getRandomValues() +slug: Web/API/RandomSource/Obtenervaloresaleatorios +tags: + - API + - Criptografía + - Referencia + - metodo +translation_of: Web/API/Crypto/getRandomValues +--- +

{{APIRef("Web Crypto API")}}

+ +

El método Crypto.getRandomValues() permite obtener valores aleatorios criptográficamente fuertes. El array que se pasa como parámetro se rellena con números aleatorios (entiéndase aleatorios en el sentido criptográfico).

+ +

Con el fin de garantizar un rendimiento razonable, las distintas implementaciones no utilizan un generador de numeros aleatorios puro, sino que utilizan un generador numérico pseudo-aleatorio, sembrado con un valor con suficiente entropía. Los generadores numéricos pseudo-aleatorios utilizados difieren entre {{Glossary("user agent", "user agents")}}, pero son adecuados para usos criptográficos. Se require también que las distintas implementaciones usen una semilla con suficiente entropía, como una fuente de entropía a nivel de sistema.

+ +

Sintaxis

+ +
cryptoObj.getRandomValues(typedArray);
+ +

Parámetros

+ +
+
typedArray
+
Es un entero {{jsxref("TypedArray")}}, que puede ser un {{jsxref("Int8Array")}}, un {{jsxref("Uint8Array")}}, un {{jsxref("Int16Array")}}, un {{jsxref("Uint16Array")}}, un {{jsxref("Int32Array")}}, o un {{jsxref("Uint32Array")}}. Todos los elementos dentro del array seran sobreescritos con números aleatorios.
+
+ +

Excepciones

+ + + +

Ejemplo

+ +
/* Asumiendo que window.crypto.getRandomValues está disponible */
+
+var array = new Uint32Array(10);
+window.crypto.getRandomValues(array);
+
+console.log("Tus numeros de la suerte:");
+for (var i = 0; i < array.length; i++) {
+    console.log(array[i]);
+}
+
+ +

Especificación

+ + + + + + + + + + + + + + +
EspecificaciónEstadoComentario
{{SpecName('Web Crypto API', '#RandomSource-method-getRandomValues')}}{{Spec2('Web Crypto API')}}Definición Inicial
+ +

Compatibilidad del navegador

+ +

La tabla de compatibilidad de esta página se genera a partir de datos estructurados. Si desea contribuir a los datos, por favor, compruebe https://github.com/mdn/browser-compat-data y envianos un pull request.

+ +

{{Compat("api.Crypto.getRandomValues")}}

+ +

Ver también

+ + diff --git a/files/es/web/api/document/abrir/index.html b/files/es/web/api/document/abrir/index.html deleted file mode 100644 index 13b541561d..0000000000 --- a/files/es/web/api/document/abrir/index.html +++ /dev/null @@ -1,109 +0,0 @@ ---- -title: Document.open() -slug: Web/API/Document/abrir -translation_of: Web/API/Document/open ---- -
{{APIRef("DOM")}}
- -

El método Document.open() abre un documento para escritura (writing)

- -

Esto viene con algunos efectos secundarios. Por ejemplo:

- - - -

Sintaxis

- -
document.open();
-
- -

Parametros

- -

Ninguno.

- -

Valor devuelto

- -

Una instancia del objeto Document (Document).

- -

Ejemplos

- -

El código simple a continuación abre el documento y reemplaza su contenido con un número de diferentes fragmentos HTML antes de cerrarlo nuevamente.

- -
document.open();
-document.write("<p>Hola mundo!</p>");
-document.write("<p>Soy un pez</p>");
-document.write("<p>El numero es 42</p>");
-document.close();
- -

Notas

- -
-

Traducción pendiente para el texto que sigue

-
- -

An automatic document.open() call happens when {{domxref("document.write()")}} is called after the page has loaded.

- -

For years Firefox and Internet Explorer additionally erased all JavaScript variables, etc., in addition to removing all nodes. This is no longer the case.document non-spec'ed parameters to document.open

- -

Gecko-specific notes

- -

Starting with Gecko 1.9, this method is subject to the same same-origin policy as other properties, and does not work if doing so would change the document's origin.

- -

Starting with Gecko 1.9.2, document.open() uses the principal of the document whose URI it uses, instead of fetching the principal off the stack. As a result, you can no longer call {{domxref("document.write()")}} into an untrusted document from chrome, even using wrappedJSObject. See Security check basics for more about principals.

- -

Three-argument document.open()

- -

There is a lesser-known and little-used three-argument version of document.open() , which is an alias of {{domxref("Window.open()")}} (see its page for full details).

- -

This call, for example opens github.com in a new window, with its opener set to null:

- -
document.open('https://www.github.com','', 'noopener=true')
- -

Two-argument document.open()

- -

Browsers used to support a two-argument document.open(), with the following signature:

- -
document.open(type, replace)
- -

Where type specified the MIME type of the data you are writing (e.g. text/html) and replace if set (i.e. a string of "replace") specified that the history entry for the new document would replace the current history entry of the document being written to.

- -

This form is now obsolete; it won't throw an error, but instead just forwards to document.open() (i.e. is the equivalent of just running it with no arguments).  The history-replacement behavior now always happens.

- -

Specifications

- - - - - - - - - - - - - - - - - - - - - -
SpecificationStatusComment
{{SpecName("HTML WHATWG", "#dom-document-open", "document.open()")}}{{Spec2("HTML WHATWG")}}
{{SpecName("DOM2 HTML", "html.html#ID-72161170", "document.open()")}}{{Spec2("DOM2 HTML")}}
- -

Browser compatibility

- - - -

{{Compat("api.Document.open")}}

- -

See also

- - diff --git a/files/es/web/api/document/async/index.html b/files/es/web/api/document/async/index.html deleted file mode 100644 index 132fd106e1..0000000000 --- a/files/es/web/api/document/async/index.html +++ /dev/null @@ -1,33 +0,0 @@ ---- -title: Document.async -slug: Web/API/Document/async -translation_of: Web/API/XMLDocument/async ---- -

document.async es utilizado para indicar cuándo un llamado de  {{domxref("document.load")}} debe ser sincrónico o asincrónico. true es su valor por defecto, indicando que el documento se cargó asincrónicamente.

- -

(Desde la versión 1.4 alpha es posible cargar documentos sincrónicamente)

- -

Ejemplo

- -
function loadXMLData(e) {
-  alert(new XMLSerializer().serializeToString(e.target)); // Devuelve los contenidos de querydata.xml como un string
-}
-
-var xmlDoc = document.implementation.createDocument("", "test", null);
-
-xmlDoc.async = false;
-xmlDoc.onload = loadXMLData;
-xmlDoc.load('querydata.xml');
- -

Especificación

- - - -

Véase también

- - diff --git a/files/es/web/api/document/cookie/index.html b/files/es/web/api/document/cookie/index.html new file mode 100644 index 0000000000..791ae788f2 --- /dev/null +++ b/files/es/web/api/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/web/api/document/crearatributo/index.html b/files/es/web/api/document/crearatributo/index.html deleted file mode 100644 index 22f769d577..0000000000 --- a/files/es/web/api/document/crearatributo/index.html +++ /dev/null @@ -1,91 +0,0 @@ ---- -title: Document.createAttribute() -slug: Web/API/Document/crearAtributo -tags: - - Atributos - - Crear Atributo - - JavaScript - - Métodos -translation_of: Web/API/Document/createAttribute ---- -
{{ ApiRef("DOM") }}
- -

El método Document.createAttribute() crea un nuevo nodo de tipo atributo (attr), y lo retorna. El objeto crea un nodo implementando la interfaz {{domxref("Attr")}}. El DOM no impone que tipo de atributos pueden ser agregados a un particular elemento de esta forma.

- -
-

El texto pasado como parametro es convertido a minusculas.

-
- -

Sintaxis

- -
atributo = document.createAttribute(nombre)
-
- -

Parametros

- - - -

Valor que retorna

- -

Un nodo {{domxref("Attr")}} nodo.

- -

Excepciones

- - - -

Ejemplo

- -
var nodo = document.getElementById("div1");
-var a = document.createAttribute("miAtributo");
-a.value = "nuevoVal";
-nodo.setAttributeNode(a);
-console.log(nodo.getAttribute("miAtributo")); // "nuevoVal"
-
- -

Especificaciones

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EspecificaciónEstatusComentario
{{SpecName('DOM WHATWG','#dom-document-createattribute','Document.createAttribute()')}}{{Spec2("DOM WHATWG")}}Comportamiento preciso con caracteres en mayuscula 
{{SpecName('DOM3 Core','core.html#ID-1084891198','Document.createAttribute()')}}{{Spec2('DOM3 Core')}}Sin cambios
{{SpecName('DOM2 Core','core.html#ID-1084891198','Document.createAttribute()')}}{{Spec2('DOM2 Core')}}Sin cambios
{{SpecName('DOM1','level-one-core.html#ID-1084891198','Document.createAttribute()')}}{{Spec2('DOM1')}}Definición inicial
- -

Compatibilidad del buscador

- - - -

{{Compat("api.Document.createAttribute")}}

- -

Ver ademas

- - diff --git a/files/es/web/api/document/createattribute/index.html b/files/es/web/api/document/createattribute/index.html new file mode 100644 index 0000000000..22f769d577 --- /dev/null +++ b/files/es/web/api/document/createattribute/index.html @@ -0,0 +1,91 @@ +--- +title: Document.createAttribute() +slug: Web/API/Document/crearAtributo +tags: + - Atributos + - Crear Atributo + - JavaScript + - Métodos +translation_of: Web/API/Document/createAttribute +--- +
{{ ApiRef("DOM") }}
+ +

El método Document.createAttribute() crea un nuevo nodo de tipo atributo (attr), y lo retorna. El objeto crea un nodo implementando la interfaz {{domxref("Attr")}}. El DOM no impone que tipo de atributos pueden ser agregados a un particular elemento de esta forma.

+ +
+

El texto pasado como parametro es convertido a minusculas.

+
+ +

Sintaxis

+ +
atributo = document.createAttribute(nombre)
+
+ +

Parametros

+ + + +

Valor que retorna

+ +

Un nodo {{domxref("Attr")}} nodo.

+ +

Excepciones

+ + + +

Ejemplo

+ +
var nodo = document.getElementById("div1");
+var a = document.createAttribute("miAtributo");
+a.value = "nuevoVal";
+nodo.setAttributeNode(a);
+console.log(nodo.getAttribute("miAtributo")); // "nuevoVal"
+
+ +

Especificaciones

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
EspecificaciónEstatusComentario
{{SpecName('DOM WHATWG','#dom-document-createattribute','Document.createAttribute()')}}{{Spec2("DOM WHATWG")}}Comportamiento preciso con caracteres en mayuscula 
{{SpecName('DOM3 Core','core.html#ID-1084891198','Document.createAttribute()')}}{{Spec2('DOM3 Core')}}Sin cambios
{{SpecName('DOM2 Core','core.html#ID-1084891198','Document.createAttribute()')}}{{Spec2('DOM2 Core')}}Sin cambios
{{SpecName('DOM1','level-one-core.html#ID-1084891198','Document.createAttribute()')}}{{Spec2('DOM1')}}Definición inicial
+ +

Compatibilidad del buscador

+ + + +

{{Compat("api.Document.createAttribute")}}

+ +

Ver ademas

+ + diff --git a/files/es/web/api/document/createevent/index.html b/files/es/web/api/document/createevent/index.html new file mode 100644 index 0000000000..7b273c6e33 --- /dev/null +++ b/files/es/web/api/document/createevent/index.html @@ -0,0 +1,35 @@ +--- +title: Event.createEvent() +slug: Web/API/Event/createEvent +tags: + - API + - DOM + - Evento + - metodo +translation_of: Web/API/Document/createEvent +translation_of_original: Web/API/Event/createEvent +--- +

{{APIRef("DOM")}}

+ +

Crea un nuevo evento, que debe ser inicializado llamando a su método init().

+ +

Sintaxis

+ +
document.createEvent(tipo);
+ +
+
tipo
+
Una string indicando el tipo de evento a crear.
+
+ +

Este método devuelve un nuevo objeto {{ domxref("Event") }} del DOM del tipo indicado, que debe ser inicializado antes de su uso.

+ +

Ejemplo

+ +
var nuevoEvento = document.createEvent("UIEvents");
+ +

Especificación

+ + diff --git a/files/es/web/api/document/getselection/index.html b/files/es/web/api/document/getselection/index.html deleted file mode 100644 index 6c03b64dcf..0000000000 --- a/files/es/web/api/document/getselection/index.html +++ /dev/null @@ -1,13 +0,0 @@ ---- -title: Document.getSelection() -slug: Web/API/Document/getSelection -tags: - - Referencia - - Selección - - metodo -translation_of: Web/API/DocumentOrShadowRoot/getSelection -translation_of_original: Web/API/Document/getSelection ---- -

{{APIRef("DOM")}}

- -

Este método funciona exactamente igual que {{domxref("Window.getSelection()")}}; devuelve un objeto {{domxref("Selection")}} que representa el texto que se ha seleccionado en el documento.

diff --git a/files/es/web/api/document/open/index.html b/files/es/web/api/document/open/index.html new file mode 100644 index 0000000000..13b541561d --- /dev/null +++ b/files/es/web/api/document/open/index.html @@ -0,0 +1,109 @@ +--- +title: Document.open() +slug: Web/API/Document/abrir +translation_of: Web/API/Document/open +--- +
{{APIRef("DOM")}}
+ +

El método Document.open() abre un documento para escritura (writing)

+ +

Esto viene con algunos efectos secundarios. Por ejemplo:

+ + + +

Sintaxis

+ +
document.open();
+
+ +

Parametros

+ +

Ninguno.

+ +

Valor devuelto

+ +

Una instancia del objeto Document (Document).

+ +

Ejemplos

+ +

El código simple a continuación abre el documento y reemplaza su contenido con un número de diferentes fragmentos HTML antes de cerrarlo nuevamente.

+ +
document.open();
+document.write("<p>Hola mundo!</p>");
+document.write("<p>Soy un pez</p>");
+document.write("<p>El numero es 42</p>");
+document.close();
+ +

Notas

+ +
+

Traducción pendiente para el texto que sigue

+
+ +

An automatic document.open() call happens when {{domxref("document.write()")}} is called after the page has loaded.

+ +

For years Firefox and Internet Explorer additionally erased all JavaScript variables, etc., in addition to removing all nodes. This is no longer the case.document non-spec'ed parameters to document.open

+ +

Gecko-specific notes

+ +

Starting with Gecko 1.9, this method is subject to the same same-origin policy as other properties, and does not work if doing so would change the document's origin.

+ +

Starting with Gecko 1.9.2, document.open() uses the principal of the document whose URI it uses, instead of fetching the principal off the stack. As a result, you can no longer call {{domxref("document.write()")}} into an untrusted document from chrome, even using wrappedJSObject. See Security check basics for more about principals.

+ +

Three-argument document.open()

+ +

There is a lesser-known and little-used three-argument version of document.open() , which is an alias of {{domxref("Window.open()")}} (see its page for full details).

+ +

This call, for example opens github.com in a new window, with its opener set to null:

+ +
document.open('https://www.github.com','', 'noopener=true')
+ +

Two-argument document.open()

+ +

Browsers used to support a two-argument document.open(), with the following signature:

+ +
document.open(type, replace)
+ +

Where type specified the MIME type of the data you are writing (e.g. text/html) and replace if set (i.e. a string of "replace") specified that the history entry for the new document would replace the current history entry of the document being written to.

+ +

This form is now obsolete; it won't throw an error, but instead just forwards to document.open() (i.e. is the equivalent of just running it with no arguments).  The history-replacement behavior now always happens.

+ +

Specifications

+ + + + + + + + + + + + + + + + + + + + + +
SpecificationStatusComment
{{SpecName("HTML WHATWG", "#dom-document-open", "document.open()")}}{{Spec2("HTML WHATWG")}}
{{SpecName("DOM2 HTML", "html.html#ID-72161170", "document.open()")}}{{Spec2("DOM2 HTML")}}
+ +

Browser compatibility

+ + + +

{{Compat("api.Document.open")}}

+ +

See also

+ + diff --git a/files/es/web/api/document/pointerlockchange_event/index.html b/files/es/web/api/document/pointerlockchange_event/index.html new file mode 100644 index 0000000000..2d5af4205b --- /dev/null +++ b/files/es/web/api/document/pointerlockchange_event/index.html @@ -0,0 +1,80 @@ +--- +title: pointerlockchange +slug: Web/Events/pointerlockchange +translation_of: Web/API/Document/pointerlockchange_event +--- +

El evento pointerlockchange es disparado cuando el cursor del navegador es bloqueado o desbloqueado.

+ +

Información general

+ +
+
Specification
+
Pointer Lock
+
Interface
+
Event
+
Bubbles
+
Yes
+
Cancelable
+
No
+
Target
+
Document
+
Default Action
+
None
+
+ +

Propiedades

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyTypeDescription
target {{readonlyInline}}{{domxref("EventTarget")}}The event target (the topmost target in the DOM tree).
type {{readonlyInline}}{{domxref("DOMString")}}The type of event.
bubbles {{readonlyInline}}{{jsxref("Boolean")}}Whether the event normally bubbles or not.
cancelable {{readonlyInline}}{{jsxref("Boolean")}}Whether the event is cancellable or not.
+ +

Ejemplo

+ +
//Ten en cuenta que el nombre del evento, en este caso "pointerlockchange" puede variar según el navegador.
+document.addEventListener("pointerlockchange", function( event ) {
+    // El objetivo, parámetro "target", del objeto "event" es siempre el objeto "document".
+    // para recuperar el objeto que recibe el bloqueo/desbloqueo es document.pointerlockElement.
+    document.pointerLockElement;
+
+});
+
+ +

Eventos relacionados

+ + + +

Véase también:

+ + diff --git a/files/es/web/api/document/pointerlockelement/index.html b/files/es/web/api/document/pointerlockelement/index.html deleted file mode 100644 index cc5d490e5c..0000000000 --- a/files/es/web/api/document/pointerlockelement/index.html +++ /dev/null @@ -1,105 +0,0 @@ ---- -title: Document.pointerLockElement -slug: Web/API/Document/pointerLockElement -translation_of: Web/API/DocumentOrShadowRoot/pointerLockElement ---- -
{{APIRef("DOM")}}
- -

La propiedad pointerLockElement conserva el elemento adquirido, para el evento del mouse, mientras el bloqueo se encuentre activo .  Es null si el bloqueo se encuentra en estado pendiente para bloqueo, o si el bloqueo se ha liberado, es decir ya no se encuentra en estado bloqueado, o si el elemento bloqueado se encuentra en otro documento.

- -

Sintaxis

- -
var elemento = document.pointerLockElement;
-
- -

Valor retornado

- -

Un {{domxref("Element")}} o null.

- -

Especificaciones

- - - - - - - - - - - - - - -
EspecificaciónEstadoComentario
{{SpecName('Bloquear puntero','l#extension-to-the-document-interface','Document')}}{{Spec2('Pointer lock')}}Extiende de la interfaz Document
- -

Compatibilidad del Navegador

- -

{{ CompatibilityTable() }}

- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CaracterísticaChromeEdgeFirefox (Gecko)Internet ExplorerOperaSafari
Soporte Básico{{ CompatVersionUnknown() }} {{property_prefix("webkit")}}{{CompatVersionUnknown}}{{ CompatVersionUnknown() }} {{property_prefix("moz")}}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}
Soporte sin prefijar{{ CompatVersionUnknown() }}{{CompatUnknown}}{{CompatGeckoDesktop(50)}}   
-
- -
- - - - - - - - - - - - - - - - - - - - - -
CaracterísticaAndroidEdgeFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Soporte Básico{{ CompatUnknown() }}{{CompatVersionUnknown}}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}
-
- -

Véase también:

- - diff --git a/files/es/web/api/document/stylesheets/index.html b/files/es/web/api/document/stylesheets/index.html deleted file mode 100644 index 0458cb3fc9..0000000000 --- a/files/es/web/api/document/stylesheets/index.html +++ /dev/null @@ -1,22 +0,0 @@ ---- -title: Document.styleSheets -slug: Web/API/Document/styleSheets -translation_of: Web/API/DocumentOrShadowRoot/styleSheets -translation_of_original: Web/API/Document/styleSheets ---- -

{{ ApiRef() }}

-

Resumen

-

Devuelve una lista de objetos de tipo stylesheet para las hojas de estilo que están específicamente enlazadas o contenidas en el documento.

-

Propiedades

-

styleSheetList.length - devuelve el número de objetos de tipo stylesheet contenidos en el objeto.

-

Sintaxis

-
styleSheetList = document.styleSheets
-
-

El objeto devuelto es del tipo StyleSheetList.

-

Es una colección ordenada de objetos de tipo stylesheet. styleSheetList.item(index) o simplemente styleSheetList{{ mediawiki.external(' - - index - ') }} devuelve un único objeto de tipo stylesheet con el índice especificado (el índice es de origen 0).

-

Especificación

-

DOM Level 2 Style: styleSheets

-

{{ languages( { "ja": "ja/DOM/document.styleSheets", "pl": "pl/DOM/document.styleSheets" } ) }}

diff --git a/files/es/web/api/document_object_model/events/index.html b/files/es/web/api/document_object_model/events/index.html new file mode 100644 index 0000000000..fce2530535 --- /dev/null +++ b/files/es/web/api/document_object_model/events/index.html @@ -0,0 +1,72 @@ +--- +title: Eventos y el DOM +slug: Referencia_DOM_de_Gecko/Eventos +tags: + - DOM + - Guía +translation_of: Web/API/Document_Object_Model/Events +--- +

Introducción

+ +

Este capítulo describe el Modelo de Eventos del DOM. Se describe la interfaz Event, así como las interfaces para el registro de eventos en los nodos del DOM, y los oyentes de eventos, y varios ejemplos más largos muestran cómo se relacionan entre sí las diversas interfaces de eventos.

+ +

Hay un diagrama excelente que explica claramente las tres fases del flujo de eventos a través del DOM en el borrador DOM Level 3 Events.

+ +

Vea también el Ejemplo 5: Propagación de eventos en el capítulo de Ejemplos para un ejemplo más detallado de cómo los eventos se mueven a través del DOM.

+ +

Registrando oyentes de eventos

+ +

Hay 3 formas de registrar gestores de eventos para un elemento DOM.

+ +

EventTarget.addEventListener

+ +
// Se supone que myButton es un elemento de botón
+myButton.addEventListener('click', function(){alert('Hello world');}, false);
+
+ +

Este es el método que debe usar en las páginas web modernas. 

+ +

Nota: Internet Explorer 6-8 no admite este método, ofreciendo una API similar {{domxref("EventTarget.attachEvent")}} en su lugar. Para la compatibilidad entre navegadores utilice una de las muchas bibliotecas de JavaScript disponibles.

+ +

Se pueden encontrar más detalles en la página de referencia {{domxref("EventTarget.addEventListener")}}.

+ +

Atributo HTML

+ +
<button onclick="alert('Hello world!')">
+
+ +

El código de JavaScript en el atributo pasa el objeto Event por medio del parámetro event. El valor de retorno se trata de una manera especial, descrita en la especificación HTML.

+ +

Debe evitarse esta forma. Hace que el marcado sea más grande y menos legible. El comitido del contenido/estructura y del comportamiento no están bien separadas, por lo que es más difícil encontrar un error.

+ +

Propiedades del elemento DOM

+ +
// Se supone que myButton es un elemento de botón
+myButton.onclick = function(event){alert('Hello world');};
+
+ +

La función se puede defirnir para que tome un parámetro event. El valor de retorno se trata de una manera especial, descrita en la especificación HTML.

+ +

El problema con este método es que solo se puede establecer un gestor por elemento y por evento.

+ +

Accediendo a las Interfaces de eventos

+ +

Los controladores de eventos se pueden adjuntar a varios objetos, incluidos los elementos DOM, documentos, al objeto window, etc. Cuando se produce un evento, se crea un objeto de evento y se pasa secuencialmente a los oyentes del evento.

+ +

Se puede acceder a la interfaz {{domxref ("Evento")}} desde la función del gestor, a través del objeto de evento pasado como primer argumento. El siguiente ejemplo simple muestra cómo se pasa un objeto de evento a la función del controlador de eventos, y se puede usar desde dentro de una de esas funciones.

+ +
function foo(evt) {
+  // al parámetro evt se le asigna automáticamente el objeto event
+  alert(evt);
+}
+table_el.onclick = foo;
+
+ + + + diff --git a/files/es/web/api/document_object_model/examples/index.html b/files/es/web/api/document_object_model/examples/index.html new file mode 100644 index 0000000000..33f0e9e2b7 --- /dev/null +++ b/files/es/web/api/document_object_model/examples/index.html @@ -0,0 +1,367 @@ +--- +title: Ejemplos +slug: Referencia_DOM_de_Gecko/Ejemplos +tags: + - DOM + - Referencia_DOM_de_Gecko + - Todas_las_Categorías + - páginas_a_traducir +translation_of: Web/API/Document_Object_Model/Examples +--- +

En este capítulo se brindan ejemplos relativamente extensos que ilustran el uso del DOM para el desarrollo web y XML. Siempre que sea posible, usaremos las APIs, trucos y patrones comunes en JavaScript para la manipulación del objeto document.

+ +

Ejemplo 1: Altos y anchos

+ +

El ejemplo siguiente muestra el uso de las propiedades de alto (height) y ancho (width) junto a imágenes de dimensiones variadas:

+ +
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+"http://www.w3.org/TR/html4/strict.dtd">
+
+<html lang="es">
+
+<head>
+<title>Ejemplo de height/width</title>
+<script type="text/javascript">
+function iniciar()
+{
+  var arrImages = new Array(3);
+  arrImages[0] = document.getElementById("imagen1");
+  arrImages[1] = document.getElementById("imagen2");
+  arrImages[2] = document.getElementById("imagen3");
+  var objOutput = document.getElementById("salida");
+  var strHtml = "<ul>";
+  for (var i = 0; i < arrImages.length; i++)
+    strHtml += "<li>imagen" + (i+1) +
+            ": height=" + arrImages[i].height +
+            ", width=" + arrImages[i].width +
+            ", style.height=" + arrImages[i].style.height +
+            ", style.width=" + arrImages[i].style.width +
+            "<\/li>";
+  strHtml += "<\/ul>";
+  salida.innerHTML = strHtml;
+}
+</script>
+</head>
+<body onload="iniciar();">
+
+<p>La 1ª imagen:
+- alto (height): no
+- ancho (width): no
+- estilo (style): no
+    <img id="imagen1" src="http://www.mozilla.org/images/mozilla-banner.gif">
+</p>
+<p>La 2ª imagen:
+- height="50"
+- width="500"
+- style: no
+    <img id="imagen2" src="http://www.mozilla.org/images/mozilla-banner.gif"
+         height="50" width="500">
+</p>
+<p>La 3ª imagen:
+- height y width: no
+- style="height: 50px; width: 500px;": sí
+    <img id="imagen3" src="http://www.mozilla.org/images/mozilla-banner.gif"
+         style="height: 50px; width: 500px;">
+</p>
+
+<div id="salida"> </div>
+</body>
+</html>
+
+ +

height y width son además propiedades de los elementos OBJECT y APPLET.

+ +

Ejemplo 2: Atributos de una imagen

+ +
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+"http://www.w3.org/TR/html4/strict.dtd">
+
+<html lang="es">
+
+<head>
+<title>Modificación del borde de una imagen</title>
+<script type="text/javascript">
+function setBorderWidth(width) {
+  document.getElementById("img1").style.borderWidth = width + "px";
+}
+</script>
+</head>
+
+<body>
+<p>
+  <img id="img1" src="image1.gif" style="border: 5px solid green;" width="100"
+height="100" alt="test de borde">
+</p>
+
+<form name="Formulario">
+  <p><input type="button" value="Definir un borde de 20px"
+onclick="setBorderWidth(20);"> <input type="button" value="Definir un borde de 5px"
+onclick="setBorderWidth(5);"></p>
+</form>
+
+</body>
+</html>
+
+ +

Ejemplo 3: Manipulación de estilos

+ +

En este ejemplo sencillo, algunas propiedades de estilo básicas de un elemento párrafo HTML son accedidas utilizando el objeto estilo en el elemento y aquellas propiedades de estilo CSS del objeto, pueden ser entregadas y establecidas desde el DOM. En este caso, está manipulando los estilos directamente. En el siguiente ejemplo (ver ejemplo 4), puede utilizar las hojas de estilo y sus reglas para cambiar estilos para el documento entero.

+ +
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+"http://www.w3.org/TR/html4/strict.dtd">
+
+<html lang="en">
+
+<head>
+<title>Changing color and font-size example</title>
+<script type="text/javascript">
+function changeText() {
+	var p = document.getElementById("pid");
+	p.style.color = "blue"
+	p.style.fontSize = "18pt"
+}
+</script>
+</head>
+<body>
+<p id="pid"
+onclick="window.location.href = 'http://www.cnn.com/';">linker</p>
+<form>
+<p><input value="rec" type="button" onclick="changeText();"></p>
+</form>
+</body>
+</html>
+
+ +

Ejemplo 4: Utilización de las hojas de estilo

+ +

La propiedad de las hojas de estilo en un objeto de documento muestra una lista de las hojas de estilo que acompañan a ése documento. Usando los objetos de la hoja de estilo, del estilo y de las reglas de CSS se puede acceder individualmente a esas hojas de estilo y sus reglas, como se demuestra en este ejemplo, el cual muestra todos los selectores de reglas de estilo en la consola.

+ +
ss = document.styleSheets;
+for(i=0; i<ss.length; i++)
+ {
+ for(j=0; j<ss[0].cssRules.length; j++)
+  {
+  dump( ss[i].cssRules[j].selectorText + "\n" );
+  }
+ }
+
+ +

Para un documento con una sola hoja de estilo en la cual son definidas las tres reglas siguientes:

+ +
BODY { background-color: darkblue; }
+P { font-face: Arial; font-size: 10pt; margin-left: .125in; }
+#lumpy { display: none; }
+
+ +

El script sale así:

+ +
BODY
+P
+#LUMPY
+
+ +

Ejemplo 5: Propagación del evento

+ +

Este ejemplo muestra de una forma muy simple como los eventos se inician y son gestionados en el DOM. Cuando el cuerpo (body ) de ese documento HTML se carga,un evento listener es registrado con la columna superior de la TABLA. El evento listener maneja el evento ejecutando la función stopEvent, que cambia el valor en el final de la celda de la tabla.

+ +

Sin embargo, stopEvent también llama a un método del objeto evento, event.stopPropagation, que mantiene el evento del burbujeo a continuación dentro del DOM. Note que la tabla misma tiene un manejador de evento onclick que muestra un mensaje cuando la tabla es seleccionada. Pero el método stopEvent ha detenido la propagación, y así despues los datos en la tabla son actualizados, la fase de evento es efectivamente finalizada, y un cuadro de alerta es mostrado para confirmar esto.

+ +
<html>
+<head>
+<title>Propagación del evento</title>
+
+<style type="text/css">
+ #t-daddy { border: 1px solid red }
+ #c1 { background-color: pink; }
+</style>
+
+<script type="text/javascript">
+
+function stopEvent(ev) {
+  c2 = document.getElementById("c2");
+  c2.innerHTML = "hola";
+
+  // this ought to keep t-daddy from getting the click.
+  ev.stopPropagation();
+  alert("La propagación del evento se ha parado.");
+}
+
+function load() {
+  elem = document.getElementById("tbl1");
+  elem.addEventListener("click", stopEvent, false);
+}
+</script>
+</head>
+
+<body onload="load();">
+
+<table id="t-daddy" onclick="alert('hi');">
+ <tr id="tbl1">
+  <td id="c1">uno</td>
+ </tr>
+ <tr>
+  <td id="c2">dos</td>
+ </tr>
+</table>
+
+</body>
+</html>
+
+ +

Ejemplo 6: Conseguir el estilo computado (getComputedStyle)

+ +

Este ejemplo demuestra como el método window.getComputedStyle puedes utilizarse para obtener los estilos de un elemento que no son especificados en el atributo style o con JavaScript (por ejemplo, element.style.backgroundColor="rgb(173, 216, 230)"). Estos últimos tipos de estilos se pueden recuperar con el atributo element.style, las propiedades del cual están en la lista de propiedades de CSS del DOM.

+ +

getComputedStyle() devuelve un objeto ComputedCSSStyleDeclaration, cuyas propiedades de estilo individuales pueden ser referenciadas con este método del objeto getPropertyValue(), el siguiente documento de ejemplo lo muestra.

+ +
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+"http://www.w3.org/TR/html4/strict.dtd">
+
+<html lang="en">
+
+<head>
+ <title>Ejemplo de ''getComputedStyle''</title>
+
+ <script type="text/javascript">
+   function cStyles()
+  {
+   var RefDiv = document.getElementById("d1");
+
+   var txtHeight = document.getElementById("t1");
+   var h_style =
+document.defaultView.getComputedStyle(RefDiv, null).getPropertyValue("height");
+   txtHeight.value = h_style;
+
+   var txtWidth = document.getElementById("t2");
+   var w_style =
+document.defaultView.getComputedStyle(RefDiv, null).getPropertyValue("width");
+   txtWidth.value = w_style;
+
+   var txtBackgroundColor = document.getElementById("t3");
+   var b_style =
+document.defaultView.getComputedStyle(RefDiv,
+null).getPropertyValue("background-color");
+   txtBackgroundColor.value = b_style;
+  }
+ </script>
+
+ <style type="text/css">
+   #d1 { margin-left: 10px; background-color: rgb(173, 216, 230);
+height: 20px; max-width: 20px; }
+ </style>
+
+</head>
+
+<body>
+
+<div id="d1">&nbsp;</div>
+
+<form action="">
+<p><button type="button" onclick="cStyles();">getComputedStyle</button>
+  height<input id="t1" type="text" value="1">
+  max-width<input id="t2" type="text" value="2">
+  bg-color<input id="t3" type="text" value="3"></p>
+</form>
+
+</body>
+</html>
+
+ +

Ejemplo 7: Mostrar las propiedades del objeto de evento

+ +

Este ejemplo utiliza métodos del DOM para mostrar todas las propiedades del evento de window.onload y sus valores en una tabla. Muestra además una cómoda técnica del uso de un buclefor...in para iterar sobre las propiedades de un objeto y conseguir sus valores.

+ +

Las propiedades de los objetos de evento difieren bastante entre los navegadores, la especificación W3C de los eventos del DOM 2 enumera las propiedades estándares, sin embargo algunos navegadores han extendido estas diferencias.

+ +

El siguiente código colocado dentro de un nuevo archivo de texto y cargado en un surtido de navegadores, sorprenderá por las diferencias de los números y nombres de propiedades y/o al ponerle más elementos a la página y llamar esa función desde diferentes gestores de evento.

+ +
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+   "http://www.w3.org/TR/html4/strict.dtd">
+
+<title>Muestra las propiedades del evento</title>
+
+<style type="text/css">
+  table {border-collapse: collapse;}
+  thead {font-weight: bold;}
+  td {padding: 2px 10px 2px 10px;}
+  .odd {background-color: #efdfef;}
+  .even {background-color: #ffffff;}
+</style>
+
+<script type="text/javascript">
+
+function showEventProperties(e)
+{
+  function addCell(row, text) {
+    var cell = row.insertCell(-1);
+    cell.appendChild(document.createTextNode(text));
+  }
+
+  document.getElementById('eventType').innerHTML = e.type;
+
+  var e = e || window.event;
+  var table = document.createElement('table');
+  var thead = table.createTHead();
+  var row = thead.insertRow(-1);
+  var lableList = ['#', 'Propriété', 'Valeur'];
+  var len = lableList.length;
+
+  for (var i=0; i<len; i++) {
+    addCell(row, lableList[i]);
+  }
+
+  var tbody = document.createElement('tbody');
+  table.appendChild(tbody);
+
+  for (var p in e) {
+    row = tbody.insertRow(-1);
+    row.className = (row.rowIndex % 2)? 'odd':'even';
+    addCell(row, row.rowIndex);
+    addCell(row, p);
+    addCell(row, e[p]);
+  }
+
+  document.body.appendChild(table);
+}
+window.onload = function(event){
+  showEventProperties(event);
+}
+</script>
+
+<h1>Propiedades del objeto evento del DOM<span id="eventType"></span></h1>
+
+ +

Ejemplo 8: Utilización del interfaz de tabla del DOM

+ +

La interfaz HTMLTableElement del DOM provee algunos métodos de conveniencia para crear y manipular tablas. Dos métodos usados frecuentemente son table.insertRow y row.insertCell.

+ +

Para agregar una columna y algunas celdas a una tabla existente:

+ +
<table id="table0">
+ <tr>
+  <td>Row 0 Cell 0</td>
+  <td>Row 0 Cell 1</td>
+ </tr>
+</table>
+
+<script type="text/javascript">
+
+var table = document.getElementById('table0');
+var row = table.insertRow(-1);
+var cell, text;
+for (var i=0; i<2; i++) {
+  cell = row.insertCell(-1);
+  text = 'Row ' + row.rowIndex + ' Cell ' + i;
+  cell.appendChild(document.createTextNode(text));
+}
+
+</script>
+
+ +

Notas

+ + diff --git a/files/es/web/api/document_object_model/how_to_create_a_dom_tree/index.html b/files/es/web/api/document_object_model/how_to_create_a_dom_tree/index.html new file mode 100644 index 0000000000..25546a1704 --- /dev/null +++ b/files/es/web/api/document_object_model/how_to_create_a_dom_tree/index.html @@ -0,0 +1,130 @@ +--- +title: Cómo crear un DOM tree +slug: How_to_create_a_DOM_tree +translation_of: Web/API/Document_object_model/How_to_create_a_DOM_tree +--- +

 

+

Esta página describe cómo usar el API DOM Core en JavaScript para crear o modificar objetos DOM. Es aplicable a todas las aplicaciones basadas en Gecko (como Mozilla Firefox) cuyo código tenga privilegios (como las extensiones) y a las que no (páginas web).

+

Creación dinámica de un DOM tree

+

Considere el siguiente documento XML:

+
<?xml version="1.0"?>
+<people>
+  <person first-name="eric" middle-initial="H" last-name="jung">
+    <address street="321 south st" city="denver" state="co" country="usa"/>
+    <address street="123 main st" city="arlington" state="ma" country="usa"/>
+  </person>
+
+  <person first-name="jed" last-name="brown">
+    <address street="321 north st" city="atlanta" state="ga" country="usa"/>
+    <address street="123 west st" city="seattle" state="wa" country="usa"/>
+    <address street="321 south avenue" city="denver" state="co" country="usa"/>
+  </person>
+</people>
+
+

El W3C DOM API soportado por Mozilla, puede ser usado para crear una representación en memoria del documento anterior, como se ve a continuación:

+
var doc = document.implementation.createDocument("", "", null);
+var peopleElem = doc.createElement("people");
+
+var personElem1 = doc.createElement("person");
+personElem1.setAttribute("first-name", "eric");
+personElem1.setAttribute("middle-initial", "h");
+personElem1.setAttribute("last-name", "jung");
+
+var addressElem1 = doc.createElement("address");
+addressElem1.setAttribute("street", "321 south st");
+addressElem1.setAttribute("city", "denver");
+addressElem1.setAttribute("state", "co");
+addressElem1.setAttribute("country", "usa");
+personElem1.appendChild(addressElem1);
+
+var addressElem2 = doc.createElement("address");
+addressElem2.setAttribute("street", "123 main st");
+addressElem2.setAttribute("city", "arlington");
+addressElem2.setAttribute("state", "ma");
+addressElem2.setAttribute("country", "usa");
+personElem1.appendChild(addressElem2);
+
+var personElem2 = doc.createElement("person");
+personElem2.setAttribute("first-name", "jed");
+personElem2.setAttribute("last-name", "brown");
+
+var addressElem3 = doc.createElement("address");
+addressElem3.setAttribute("street", "321 north st");
+addressElem3.setAttribute("city", "atlanta");
+addressElem3.setAttribute("state", "ga");
+addressElem3.setAttribute("country", "usa");
+personElem2.appendChild(addressElem3);
+
+var addressElem4 = doc.createElement("address");
+addressElem4.setAttribute("street", "123 west st");
+addressElem4.setAttribute("city", "seattle");
+addressElem4.setAttribute("state", "wa");
+addressElem4.setAttribute("country", "usa");
+personElem2.appendChild(addressElem4);
+
+var addressElem5 = doc.createElement("address");
+addressElem5.setAttribute("street", "321 south avenue");
+addressElem5.setAttribute("city", "denver");
+addressElem5.setAttribute("state", "co");
+addressElem5.setAttribute("country", "usa");
+personElem2.appendChild(addressElem5);
+
+peopleElem.appendChild(personElem1);
+peopleElem.appendChild(personElem2);
+doc.appendChild(peopleElem);
+
+

Vea también el Capítulo DOM del Tutorial de XUL.

+

Se puede automatizar la creación de un DOM tree usando el JXON reverse algorithm (algoritmo inverso JXON) en asociación con la siguiente representación JSON:

+
{
+  "people": {
+    "person": [{
+      "address": [{
+        "@street": "321 south st",
+        "@city": "denver",
+        "@state": "co",
+        "@country": "usa"
+      }, {
+        "@street": "123 main st",
+        "@city": "arlington",
+        "@state": "ma",
+        "@country": "usa"
+      }],
+      "@first-name": "eric",
+      "@middle-initial": "H",
+      "@last-name": "jung"
+    }, {
+      "address": [{
+        "@street": "321 north st",
+        "@city": "atlanta",
+        "@state": "ga",
+        "@country": "usa"
+      }, {
+        "@street": "123 west st",
+        "@city": "seattle",
+        "@state": "wa",
+        "@country": "usa"
+      }, {
+        "@street": "321 south avenue",
+        "@city": "denver",
+        "@state": "co",
+        "@country": "usa"
+      }],
+      "@first-name": "jed",
+      "@last-name": "brown"
+    }]
+  }
+}
+
+

¿Ahora qué?

+

Los DOM trees pueden ser invocados usando expresiones XPath, convertidos a cadenas de texto, salvados a un archivo local o remoto usando XMLSerializer (sin tener que convertirlo primero a una cadena de texto), Enviados mediante POST a un servidor web (via XMLHttpRequest), transformados usando XSLT, XLink, convertidos a un objeto JavaScript a tráves del  algoritmo JXON, etc.

+

Adicionalmente se puede hacer uso de los DOM trees para modelar los datos que no estén bien formados para RDF (o si sólo no prefieres usar RDF). Otra aplicación que puedes darle, se relaciona a XUL (que es XML), la UI (interfaz) de tu aplicación puede ser manipulada dinámicamente, descargada, subida, guardada, cargada, convertida, o transformada fácilmente.

+

Vea también

+ +

{{ languages( { "fr": "fr/Comment_cr\u00e9er_un_arbre_DOM", "ja": "ja/How_to_create_a_DOM_tree", "zh-cn": "zh-cn/How_to_create_a_DOM_tree" } ) }}

diff --git a/files/es/web/api/document_object_model/index.html b/files/es/web/api/document_object_model/index.html new file mode 100644 index 0000000000..0f3a2a7f75 --- /dev/null +++ b/files/es/web/api/document_object_model/index.html @@ -0,0 +1,91 @@ +--- +title: Referencia DOM de Gecko +slug: Referencia_DOM_de_Gecko +tags: + - DOM + - NecesitaRevisiónTécnica + - Todas_las_Categorías +translation_of: Web/API/Document_Object_Model +--- +

+« Referencia DOM de Gecko +


+Aquí está la tabla de contenido para la referencia DOM de Gecko. +

+

Prefacio

+ +

Introducción

+ +

La referencia al elemento (element) de DOM

+ +

La referencia a la ventana (window) de DOM

+ +

La referencia al document de DOM

+ +

La referencia al event de DOM

+ +

La referencia al estilo (style) de DOM

+ +

La referencia al rango (range) de DOM

+ +

La referencia a la selección (selection) de DOM

+ +

Interfaz del elemento formulario (form) en HTML

+ +

Interfaz del elemento tabla (table) en HTML

+ +

Ejemplos DOM

+ +
+

Información sobre el documento original

+ +
+{{ languages( { "en": "en/Gecko_DOM_Reference", "fr": "fr/R\u00e9f\u00e9rence_du_DOM_Gecko", "it": "it/Reference_del_DOM_di_Gecko", "ja": "ja/Gecko_DOM_Reference", "ko": "ko/Gecko_DOM_Reference", "pl": "pl/Dokumentacja_Gecko_DOM", "zh-cn": "cn/Gecko_DOM_\u53c2\u8003" } ) }} diff --git a/files/es/web/api/document_object_model/introduction/index.html b/files/es/web/api/document_object_model/introduction/index.html new file mode 100644 index 0000000000..5c604a7a6c --- /dev/null +++ b/files/es/web/api/document_object_model/introduction/index.html @@ -0,0 +1,248 @@ +--- +title: Introducción +slug: Referencia_DOM_de_Gecko/Introducción +tags: + - DOM + - Gecko + - Manuales + - Todas_las_Categorías +translation_of: Web/API/Document_Object_Model/Introduction +--- +

 

+ +

Ésta sección da una breve introducción conceptual del DOM: qué es, cómo proporciona la estructura para los documentos HTML y XML, cómo se accede a él, y cómo esta "API" presenta la información de referencia y ejemplos.

+ +

¿Qué es el DOM?

+ +

El modelo de objeto de documento (DOM) es una interfaz de programación para los documentos HTML y XML. Facilita una representación estructurada del documento y define de qué manera los programas pueden acceder, al fin de modificar, tanto su estructura, estilo y contenido. El DOM da una representación del documento como un grupo de nodos y objetos estructurados que tienen propiedades y métodos. Esencialmente, conecta las páginas web a scripts o lenguajes de programación.

+ +

Una página web es un documento. Éste documento puede exhibirse en la ventana de un navegador o también como código fuente HTML. Pero, en los dos casos, es el mismo documento. El modelo de objeto de documento (DOM) proporciona otras formas de presentar, guardar y manipular este mismo documento. El DOM es una representación completamente orientada al objeto de la página web y puede ser modificado con un lenguaje de script como JavaScript.

+ +

El W3C DOM estándar forma la base del funcionamiento del DOM en muchos navegadores modernos. Varios navegadores ofrecen extensiones más allá del estándar W3C, hay que ir con extremo cuidado al utilizarlas en la web, ya que los documentos pueden ser consultados por navegadores que tienen DOMs diferentes.

+ +

Por ejemplo, el DOM de W3C especifica que el método getElementsByTagName en el código de abajo debe devolver una lista de todos los elementos <p> del documento:

+ +
paragraphs = document.getElementsByTagName ("p");
+// paragraphs[0] es el primer elemento <p>
+// paragraphs[1] es el segundo elemento <p>, etc.
+alert (paragraphs [0].nodeName);
+
+ +

Todas las propiedades, métodos y eventos disponibles para la manipulación y la creación de páginas web está organizado dentro de objetos. Un ejemplo: el objeto document representa al documento mismo, el objeto table hace funcionar la interfaz especial HTMLTableElement del DOM para acceder a tablas HTML, y así sucesivamente. Ésta documentación procura una relación objeto-por-objeto del DOM que funciona con los navegadores basados en Gecko.

+ +

DOM y JavaScript

+ +

El ejemplo corto de abajo, como casi todos los ejemplos de esta referencia, es JavaScript. Es decir, es escrito en JavaScript pero utiliza el DOM para acceder al documento y a sus elementos. El DOM no es un lenguaje de programación pero sin él, el lenguaje JavaScript no tiene ningún modelo o noción de las páginas web, de la páginas XML ni de los elementos con los cuales es usualmente relacionado. Cada elemento -"el documento íntegro, el título, las tablas dentro del documento, los títulos de las tablas, el texto dentro de las celdas de las tablas"- es parte del modelo de objeto del documento para cada documento, así se puede acceder y manipularlos utilizando el DOM y un lenguaje de escritura, como JavaScript.

+ +

En el comienzo, JavaScript y el DOM estaban herméticamente enlazados, pero después se desarrollaron como entidades separadas. El contenido de la página es almacenado en DOM y el acceso y la manipulación se hace vía JavaScript, podría representarse aproximadamente así:

+ +

API(web o página XML) = DOM + JS(lenguaje de script)

+ +

El DOM fue diseñado para ser independiente de cualquier lenguaje de programación particular, hace que la presentación estructural del documento sea disponible desde un simple y consistente API. Aunque en este manual nos centramos exclusivamente en JavaScript, la directrices del DOM pueden construirse para cualquier lenguaje, así lo demuestra el siguiente ejemplo de Python:

+ +
# Ejemplo DOM de Python
+import xml.dom.minidom as m
+doc = m.parse("C:\\Projects\\Py\\chap1.xml");
+doc.nodeName # Propiedad DOM del objeto document;
+p_list = doc.getElementsByTagName("para");
+ +

¿Cómo se accede al DOM?

+ +

No se tiene que hacer nada especial para empezar a utilizar el DOM. Los diferentes navegadores tienen directrices DOM distintas, y éstas directrices tienen diversos grados de conformidad al actual estándar DOM (un tema que se intenta evitar en este manual), pero todos los navegadores web usan el modelo de objeto de documento para hacer accesibles las páginas web al script.

+ +

Cuando se crea un script –esté en un elemento <SCRIPT> o incluido en una página web por la instrucción de cargar un script– inmediatamente está disponible para usarlo con el API, accediendo así a los elementos document o window, para manipular el documento mismo o sus diferentes partes, las cuales son los varios elementos de una página web. La programación DOM hace algo tan simple como lo siguiente, lo cual abre un mensaje de alerta usando la función alert() desde el objeto window, o permite métodos DOM más sofisticados para crear realmente un nuevo contenido, como en el largo ejemplo de más abajo.

+ +
<body onload="window.alert('Bienvenido a mi página!');">
+
+ +

Aparte del elemento <script> en el cual JavaScript es definido, el ejemplo siguiente muestra la función a ejecutar cuando el documento se está cargando (y que el DOM completo es disponible para su uso). Esta función crea un nuevo elemento H1, le pone texto y después lo agrega al árbol del documento:

+ +
<html>
+  <head>
+    <script>
+       // ejecuta esta función cuando se cargue el documento
+       window.onload = function() {
+
+         // crea dinámicamente un par de elementos HTML en una página vacia
+         var heading = document.createElement("h1");
+         var heading_text = document.createTextNode("el texto que desee");
+         heading.appendChild(heading_text);
+         document.body.appendChild(heading);
+      }
+    </script>
+  </head>
+  <body>
+  </body>
+</html>
+ +

Tipos de datos importantes

+ +

Esta parte intenta describir, de la manera más simple posible, los diferentes objetos y tipos. Pero hay que conocer una cantidad de tipos de datos diferentes que son utilizados por el API. Para simplificarlo, los ejemplos de sintaxis en esta API se refieren a nodos como elements, a una lista de nodos como nodeLists (o simples elementos) y a nodos de atributo como attributes.

+ +

La siguiente tabla describe brevemente estos tipos de datos.

+ + + + + + + + + + + + + + + + + + + + + + + + +
documentCuando un miembro devuelve un objeto del tipo document (por ejemplo, la propiedad ownerDocument de un elemento devuelve el documento "document" al cual pertenece), este objeto es la raíz del objeto documento en sí mismo. El capítulo La referencia al documento (document) de DOM lo explica con más detalles.
elementelement se refiere a un elemento o a un nodo de tipo de elemento "element" devuelto por un miembro del API de DOM. Dicho de otra manera, por ejemplo, el método document.createElement() devuelve un objeto referido a un nodo, lo que significa que este método devuelve el elemento que acaba de ser creado en el DOM. Los objetos element ponen en funcionamiento a la interfaz Element del DOM y también a la interfaz de nodo "Node" más básica, las cuales son incluidas en esta referencia.
nodeListUna "nodeList" es una serie de elementos, parecido a lo que devuelve el método document.getElementsByTagName(). Se accede a los items de la nodeList de cualquiera de las siguientes dos formas: +
    +
  • list.item (1)
  • +
  • lista {{mediawiki.external (1)}}
  • +
+ +

Ambas maneras son equivalentes. En la primera, item() es el método del objeto nodeList. En la última se utiliza la típica sintaxis de acceso a listas para llegar al segundo ítem de la lista.

+
attributeCuando un atributo ("attribute") es devuelto por un miembro (por ej., por el método createAttribute()), es una referencia a un objeto que expone una interfaz particular (aunque limitada) a los atributos. Los atributos son nodos en el DOM igual que los elementos, pero no suelen usarse así.
NamedNodeMapUn namedNodeMap es una serie, pero los ítems son accesibles tanto por el nombre o por un índice, este último caso es meramente una conveniencia para enumerar ya que no están en ningún orden en particular en la lista. Un NamedNodeMap es un método de ítem() por esa razón, y permite poner o quitar ítems en un NamedNodeMap
+ +

Interfaces del DOM

+ +

Uno de los propósitos de esta guía es minimizar el hablar de interfaces abstractas, heredadas y otros detalles de funcionamiento. Más bien, concentrarse sobre los objetos en el DOM y sobre las actuales cosas que se pueden usar para manipular la jerarquía de DOM. Desde el punto de vista del programador web, es bastante indiferente saber que la representación del objeto del elemento HTML form toma la propidedad name desde la interfaz HTMLFormElement pero que las propiedades className se toman desde la propia interfaz HTMLElement. En ambos casos, la propiedad está sólo en el objeto form.

+ +

Pero puede resultar confuso el funcionamiento de la fuerte relación entre objetos e interfaces en el DOM, por eso esta sección intentará hablar un poquito sobre las interfaces actuales en la especificación del DOM y de como se dispone de ellas.

+ +

Interfaces y objetos

+ +

En algunos casos un objeto pone en ejecución a una sola interfaz. Pero a menudo un objeto toma prestada una tabla HTML (table) desde muchas interfaces diversas. El objeto table, por ejemplo, pone en funcionamiento una Interfaz especial del elemento table HTML, la cual incluye métodos como createCaption y insertRow. Pero como también es un elemento HTML, table pone en marcha a la interfaz del Element descrita en el capítulo La referencia al elemento del DOM. Y finalmente, puesto que un elemento HTML es también, por lo que concierna al DOM, un nodo en el árbol de nodos que hace el modelo de objeto para una página web o XML, el elemento de table hace funcionar la interfaz más básica de Node, desde el cual deriva Element.

+ +

La referencia a un objeto table, como en el ejemplo siguiente, utiliza estas interfaces intercambiables sobre el objeto.

+ +
var table = document.getElementById("table");
+var tableAttrs = table.attributes; // Node/interfaz Element
+for (var i = 0; i < tableAttrs.length; i++) {
+  // interfaz HTMLTableElement: atributo border
+  if(tableAttrs[i].nodeName.toLowerCase() == "border")
+    table.border = "1";
+}
+// interfaz HTMLTableElement: atributo summary
+table.summary = "nota: borde aumentado";
+ +

Interfaces esenciales en el DOM

+ +

Esta sección lista las interfaces más comúnmente utilizadas en el DOM. La idea no es describir qué hacen estas APIs pero sí dar una idea de las clases de métodos y propiedades que se encuentran con el uso del DOM. Muchos ejemplos de uso común de esta API se encuentran en el capítulo Ejemplos DOM al final de este manual.

+ +

document y window son objetos cuya interfaces son generalmente muy usadas en la programación de DOM. En término simple, el objeto window representa algo como podría ser el navegador, y el objeto document es la raíz del documento en sí. Element hereda de la interfaz genérica Node, y juntos, estas dos interfaces proporcionan muchos métodos y propiedades utilizables sobre los elementos individuales. Éstos elementos pueden igualmente tener interfaces específicas según el tipo de datos representados, como en el ejemplo anterior del objeto table. Lo siguiente es una breve lista de los APIS comunes en la web y en las páginas escritas en XML utilizando el DOM.

+ + + +

Probando el API del DOM

+ +

Ésta parte procura ejemplos para todas las interfaces usadas en el desarrollo web. En algunos casos, los ejemplos son páginas HTML enteras, con el acceso del DOM a un elemento de <script>, la interfaz necesaria (por ejemplo, botones) para la ejecución del script en un formulario, y también que los elementos HTML sobre los cuales opera el DOM se listen. Según el caso, los ejemplos se pueden copiar y pegar en un documento web para probarlos.

+ +

No es el caso donde los ejemplos son muchos más concisos. Para la ejecución de estos ejemplos que sólo demuestran la relación básica entre la interfaz y los elementos HTML, resulta útil tener una página de prueba en la cual las interfaces serán fácilmente accesibles por los scripts. La muy simple página siguiente proporciona en las cabeceras un elemento de script en el cual se pondrán las funciones para testar la interfaz elegida, algunos elementos HTML con atributos que se puedan leer, editar y también manipular, así como la interfaz web necesaria para llamar esas funciones desde el navegador.

+ +

Para probar y ver como trabajan en la plataforma del navegador las interfaces del DOM, esta página de prueba o una nueva similar son factibles. El contenido de la función test() se puede actualizar según la necesidad, para crear más botones o poner más elementos.

+ +
<html>
+  <head>
+    <title>Pruebas DOM</title>
+    <script type="application/javascript">
+    function setBodyAttr(attr, value){
+      if (document.body) eval('document.body.'+attr+'="'+value+'"');
+      else notSupported();
+    }
+    </script>
+  </head>
+  <body>
+    <div style="margin: .5in; height: 400;">
+      <p><b><tt>texto</tt></b></p>
+      <form>
+        <select onChange="setBodyAttr('text',
+        this.options[this.selectedIndex].value);">
+          <option value="black">negro
+          <option value="darkblue">azul oscuro
+        </select>
+        <p><b><tt>bgColor</tt></b></p>
+        <select onChange="setBodyAttr('bgColor',
+        this.options[this.selectedIndex].value);">
+          <option value="white">blanco
+          <option value="lightgrey">gris
+        </select>
+        <p><b><tt>link</tt></b></p>
+        <select onChange="setBodyAttr('link',
+        this.options[this.selectedIndex].value);">
+          <option value="blue">azul
+          <option value="green">verde
+        </select>  <small>
+        <a href="http://www.brownhen.com/dom_api_top.html" id="sample">
+        (sample link)</a></small><br>
+      </form>
+      <form>
+        <input type="button" value="version" onclick="ver()" />
+      </form>
+    </div>
+  </body>
+</html>
+ +

La creación de una página de prueba con una paleta de botones, campos de texto u otros elementos HTML, permitirá testar una gran cantidad de interfaces en un mismo documento, por ejemplo una serie de propiedades que afectan a los colores de una página web. Lo siguiente permite hacerse una idea de como pueden agruparse las interfaces para probarlas.

+ +

Figura 0.1 Muestra DOM página de prueba

+ +

+ +

En este ejemplo, los menúes desplegables actualizan dinámicamente los aspectos de la página web accesibles al DOM como el color de fondo (bgColor), de los hiper-enlaces (aLink), y el del texto (text). El hecho de diseñar páginas, testar las interfaces que se encuentren a lo largo de la lectura son una parte importante del aprendizaje para una utilización eficaz del DOM.

+ +

Otros enlaces

+ + + +
+ + +
 
+ +
 
+
+ +
+ + +
 
+ +
 
+
+ +
+ + +
 
+ +
 
+
diff --git a/files/es/web/api/document_object_model/locating_dom_elements_using_selectors/index.html b/files/es/web/api/document_object_model/locating_dom_elements_using_selectors/index.html new file mode 100644 index 0000000000..cf8a94cd4a --- /dev/null +++ b/files/es/web/api/document_object_model/locating_dom_elements_using_selectors/index.html @@ -0,0 +1,50 @@ +--- +title: Localizando elementos DOM usando selectores +slug: Referencia_DOM_de_Gecko/Localizando_elementos_DOM_usando_selectores +tags: + - DOM + - Necesita actualizacion para principiantes + - Principiante +translation_of: Web/API/Document_object_model/Locating_DOM_elements_using_selectors +--- +

Los selectores api proveen metodos que hacen mas facil y rapido devolver elementos del nodo {{domxref("Element")}} del DOM mediante emparejamiento de un conjunto de selectores. Esto es mucho mas rapido que las tecnicas anteriores, donde fuera necesario, por ejemplo usar un loop en un codigo JavaScript para localizar el item especifico que quisieras encontrar.

+ +

Interfaz de NodeSelector

+ +

Esta especificación añade dos nuevos metodos a cualquier objeto implementando el {{domxref("Document")}}, {{domxref("DocumentFragment")}}, o {{domxref("Element")}} interfaces:

+ +
+
{{domxref("Element.querySelector", "querySelector()")}}
+
Devuelve la primera coincidencia del (elemento) {{domxref("Element")}} nodo dentro de las subramas del nodo. Sino se encuentra un nodo coincidente, se devuelve null .
+
{{domxref("Element.querySelectorAll", "querySelectorAll()")}}
+
devuelve un listado de nodos {{domxref("NodeList")}} conteniendo todos los elementos del nodo coincidentes( Element) dentro de las subramas del nodo, o Devuelve un Listado de Nodos vacio NodeList sino se encuentran coincidencias.
+
+ +
Note: El {{domxref("NodeList")}} devuelto por {{domxref("Element.querySelectorAll()", "querySelectorAll()")}} no es dinamico, Es decir que cualquier cambio realizado en el DOM no se vera reflejado en la coleccion. Esto es diferente de otros metodos de querying del dom que si devuelven listados de nodos dinamicos.
+ +

Encontraras ejemplos y detalles leyendo el documento de metodos {{domxref("Element.querySelector()")}} y {{domxref("Element.querySelectorAll()")}}, Tambien en el articulo Code snippets for querySelector.

+ +

Selectors

+ +

El metodo de selectores acepta uno o mas selectores seperados por comas entre cada selector  para determinar que elemento o elementos deben ser devueltos. por ejemplo para seleccionar todos los elementos (p) del parrafo en un documento donde la clase CSS sea tanto warning or note, podes hacer lo siguiente:

+ +
var special = document.querySelectorAll( "p.warning, p.note" );
+ +

tambien por usar query para etiquetas id. Por ejemplo:

+ +
var el = document.querySelector( "#main, #basic, #exclamation" );
+ +

luego de ejecutar el codigo de arriba, la variable el contiene el primer elemento del documento, su ID puede ser uno de los siguentes  main, basic, or exclamation.

+ +

Podes usar cualquier selector CSS con los metodos querySelector() y querySelectorAll().

+ +

Ver tambien.

+ + diff --git a/files/es/web/api/document_object_model/traversing_an_html_table_with_javascript_and_dom_interfaces/index.html b/files/es/web/api/document_object_model/traversing_an_html_table_with_javascript_and_dom_interfaces/index.html new file mode 100644 index 0000000000..b8bc2e4cf9 --- /dev/null +++ b/files/es/web/api/document_object_model/traversing_an_html_table_with_javascript_and_dom_interfaces/index.html @@ -0,0 +1,337 @@ +--- +title: Trazado de una tabla HTML mediante JavaScript y la Interface DOM +slug: Trazado_de_una_tabla_HTML_mediante_JavaScript_y_la_Interface_DOM +translation_of: >- + Web/API/Document_Object_Model/Traversing_an_HTML_table_with_JavaScript_and_DOM_Interfaces +--- +

Introducción

+ +

Este artículo es un resumen de algunos métodos DOM nivel 1 poderosos y fundamentales así como una descripción de cómo utilizarlos utilizando Javascript.  Aprenderás a crear, accesar, controlar, y remover elementos HTML dinámicamente.  Los métodos DOM presentados aquí no son específicos de HTML; también aplican para XML. Las demostraciones aquí proporcionadas funcionarán en cualquier navegador moderno, incluyendo todas las versiones de Firefox e IE 5+.

+ +
Los métodos DOM presentados aquí forman parte del Modelo de Documento basado en Objetos (DOM: Document Object Model por sus siglas en inglés) de especificación nivel 1.  DOM nivel 1 incluye métodos tanto para acceso genérico del documento (DOM 1 Core) así como métodos específicos para documentos HTML (DOM 1 HTML).
+ +

Ejemplo: Crear una tabla HTML dinámicamente (Ejemplo1.html)

+ +

Contenido HTML

+ +
<input type="button" value="Genera una tabla" onclick="genera_tabla()">
+
+ +

JavaScript Content

+ +
function genera_tabla() {
+  // Obtener la referencia del elemento body
+  var body = document.getElementsByTagName("body")[0];
+
+  // Crea un elemento <table> y un elemento <tbody>
+  var tabla   = document.createElement("table");
+  var tblBody = document.createElement("tbody");
+
+  // Crea las celdas
+  for (var i = 0; i < 2; i++) {
+    // Crea las hileras de la tabla
+    var hilera = document.createElement("tr");
+
+    for (var j = 0; j < 2; j++) {
+      // Crea un elemento <td> y un nodo de texto, haz que el nodo de
+      // texto sea el contenido de <td>, ubica el elemento <td> al final
+      // de la hilera de la tabla
+      var celda = document.createElement("td");
+      var textoCelda = document.createTextNode("celda en la hilera "+i+", columna "+j);
+      celda.appendChild(textoCelda);
+      hilera.appendChild(celda);
+    }
+
+    // agrega la hilera al final de la tabla (al final del elemento tblbody)
+    tblBody.appendChild(hilera);
+  }
+
+  // posiciona el <tbody> debajo del elemento <table>
+  tabla.appendChild(tblBody);
+  // appends <table> into <body>
+  body.appendChild(tabla);
+  // modifica el atributo "border" de la tabla y lo fija a "2";
+  tabla.setAttribute("border", "2");
+}
+ +

{{ EmbedLiveSample('Overview_of_Sample1.html') }}

+ +

Observa cuidadosamente el orden en el que se crearon los elementos en el nodo de texto:

+ +
    +
  1. Primero se crea el elemento <table>.
  2. +
  3. Posteriormente, creamos el elemento <tbody> , que es el hijo del elemento <table> .
  4. +
  5. Después, utilizamos ciclos para crear los elementos <tr>, que son hijos del elemento <tbody>.
  6. +
  7. Para cada elemento <tr>, utilizamos nuevamente ciclos para generar los elementos <td> que son hijos de los elementos <tr>.
  8. +
  9. Para cada elemento <td>, creamos nodos de texto con el contenido de cada celda.
  10. +
+ +

Una vez creados los elementos <table>, <tbody>, <tr>, y <td> así como los nodos de texto, adicionamos a cada hijo bajo su padre en el órden opuesto:

+ +
    +
  1. Primero, anexamos cada nodo de texto a su elemento padre <td> : +
    celda.appendChild(textoCelda);
    +
  2. +
  3. Posteriormente, anexamos cada elemento <td> a su elemento padre <tr> : +
    hilera.appendChild(celda);
    +
  4. +
  5. Posteriomente, anexamos cada elemento <tr> a su elemento padre <tbody>: +
    tblBody.appendChild(hilera);
    +
  6. +
  7. Después, anexamos el elemento <tbody> a su elemento padre <table>: +
    tabla.appendChild(tblBody);
    +
  8. +
  9. Finalmente, anexamos el elemento <table> a su elemento padre <body>: +
    body.appendChild(tabla);
    +
  10. +
+ +

Recuérda esta técnica. Te será muy útil en la programación bajo el estándar W3C DOM. Primero, creas los elementos de arriba a abajo; posteriormente adicionas los hijos a los padres de abajo a arriba.

+ +

A continuación aparece el código HTML generado por el código JavaScript:

+ +
...
+<table border="2">
+    <tbody>
+        <tr><td>celda en la hilera 0, columna 0</td><td>celda en la hilera 0, columna 1</td></tr>
+        <tr><td>celda en la hilera 1, columna 0</td><td>celda en la hilera 1, columna 1</td></tr>
+    </tbody>
+</table>
+...
+
+ +

Aquí está el árbol de objetos DOM generado por el código del elemento <TABLE> :

+ +

Image:sample1-tabledom.jpg

+ +

Tú puedes construir esta tabla y sus elementos internos utilizando sólo algunos de los varios métodos del DOM. Recuerda tener en mente el modelo de la estructura que planeas crear; esto hará mucho más fácil la escritura del código necesario.

+ +

En el árbol del elemento <table> de la Figura 1, el elemento <table> tiene solamente un hijo mientras que <tbody> tiene dos.  A su vez, cada hijo de <tbody> tiene dos hijos. Finalmente, cada elemento <td> tiene sólo uno, el nodo de texto.

+ +

Ejemplo: Configuración del color de fondo de un párrafo

+ +

getElementsByTagName(tagNameValue) es un método disponible en cualquier elemento DOM o el elemento raíz del documento. Cuando se le llama, devolverá una matriz con todos los descendientes de elementos que coincidan con el nombre de la etiqueta. El primer elemento de la lista se encuentra en la posición [0] de la matriz.

+ +

HTML Content

+ +
<body>
+  <input type="button" value="Set paragraph background color" onclick="set_background()">
+  <p>hi</p>
+  <p>hello</p>
+</body>
+ +

JavaScript Content

+ +
function set_background() {
+  // get a list of all the body elements (there will only be one),
+  // and then select the zeroth (or first) such element
+  myBody = document.getElementsByTagName("body")[0];
+
+  // now, get all the p elements that are descendants of the body
+  myBodyElements = myBody.getElementsByTagName("p");
+
+  // get the second item of the list of p elements
+  myP = myBodyElements[1];
+  myP.style.background = "rgb(255,0,0)";
+}
+ +

{{ EmbedLiveSample('Setting_background_of_a_paragraph') }}

+ +

En este ejemplo, establecemos la variable myP en el objeto DOM para el segundo elementop dentro del body:

+ +
    +
  1. Primero, obtendremos una lista de todos los elementos body mediante +
    myBody = document.getElementsByTagName("body")[0]
    + Como en cualquier documento HTML sólo hay un elemento body válido, esta lista tendrá sólo un elemento, que recuperamos seleccionando el primer elemento de esa lista usando {{mediawiki.external(0)}}.
  2. +
  3. Luego, obtenemos todos los elementos p que son descendientes del body mediante +
    myBodyElements = myBody.getElementsByTagName("p");
    +
  4. +
  5. Finalmente, obtenemos el segundo item de la lista de elementos p mediante +
    myP = myBodyElements[1];
    +
  6. +
+ +

Image:sample2a2.jpg

+ +

Una vez que haya obtenido el objeto DOM para un elemento HTML, puede establecer sus propiedades. Por ejemplo, si desea establecer la propiedad estilo de color de fondo, agregue:

+ +
myP.style.background = "rgb(255,0,0)";
+// setting inline STYLE attribute
+
+ +

Creating TextNodes with document.createTextNode("..")

+ +

Use the document object to invoke the createTextNode method and create your text node. You just need to pass the text content. The return value is an object that represents the text node.

+ +
myTextNode = document.createTextNode("world");
+
+ +

This means that you have created a node of the type TEXT_NODE (a piece of text) whose text data is "world", and myTextNode is your reference to this node object. To insert this text into your HTML page, you need to make this text node a child of some other node element.

+ +

Inserting Elements with appendChild(..)

+ +

So, by calling myP.appendChild({{mediawiki.external('node_element')}}), you are making the element a new child of the second <p> element.

+ +
myP.appendChild(myTextNode);
+
+ +

After testing this sample, note that the words hello and world are together: helloworld. So visually, when you see the HTML page it seems like the two text nodes hello and world are a single node, but remember that in the document model, there are two nodes. The second node is a new node of type TEXT_NODE, and it is the second child of the second <p> tag. The following figure shows the recently created Text Node object inside the document tree.

+ +

Image:sample2b2.jpg

+ +
createTextNode and appendChild is a simple way to include white space between the words hello and world. Another important note is that the appendChild method will append the child after the last child, just like the word world has been added after the word hello. So if you want to append a Text Node between hello and world you will need to use insertBefore instead of appendChild.
+ +

Creating New Elements with the document object and the createElement(..) method

+ +

You can create new HTML elements or any other element you want with createElement. For example, if you want to create a new <p> element as a child of the <body> element, you can use the myBody in the previous example and append a new element node. To create a node simply call document.createElement("tagname"). For example:

+ +
myNewPTAGnode = document.createElement("p");
+myBody.appendChild(myNewPTAGnode);
+
+ +

Image:sample2c.jpg

+ +

Removing nodes with the removeChild(..) method

+ +

Nodes can be removed. The following code removes text node myTextNode (containing the word "world") from the second <p> element, myP.

+ +
myP.removeChild(myTextNode);
+
+ +

Text node myTextNode (containing the word "world") still exists. The following code attaches myTextNode to the recently created <p> element, myNewPTAGnode.

+ +
myNewPTAGnode.appendChild(myTextNode);
+
+ +

The final state for the modified object tree looks like this:

+ +

Image:sample2d.jpg

+ +

Creating a table dynamically (back to Sample1.html)

+ +

For the rest of this article we will continue working with sample1.html. The following figure shows the table object tree structure for the table created in the sample.

+ +

Reviewing the HTML Table structure

+ +

Image:sample1-tabledom.jpg

+ +

Creating element nodes and inserting them into the document tree

+ +

The basic steps to create the table in sample1.html are:

+ + + +
At the end of the start function there is a new line of code. The table's border property was set using another DOM method, setAttribute. setAttribute has two arguments: the attribute name and the attribute value. You can set any attribute of any element using the setAttribute method.
+ +
<head>
+<title>Sample code - Traversing an HTML Table with JavaScript and DOM Interfaces</title>
+<script>
+    function start() {
+        // get the reference for the body
+        var mybody = document.getElementsByTagName("body")[0];
+
+        // creates <table> and <tbody> elements
+        mytable     = document.createElement("table");
+        mytablebody = document.createElement("tbody");
+
+        // creating all cells
+        for(var j = 0; j < 2; j++) {
+            // creates a <tr> element
+            mycurrent_row = document.createElement("tr");
+
+            for(var i = 0; i < 2; i++) {
+                // creates a <td> element
+                mycurrent_cell = document.createElement("td");
+                // creates a Text Node
+                currenttext = document.createTextNode("cell is row " + j + ", column " + i);
+                // appends the Text Node we created into the cell <td>
+                mycurrent_cell.appendChild(currenttext);
+                // appends the cell <td> into the row <tr>
+                mycurrent_row.appendChild(mycurrent_cell);
+            }
+            // appends the row <tr> into <tbody>
+            mytablebody.appendChild(mycurrent_row);
+        }
+
+        // appends <tbody> into <table>
+        mytable.appendChild(mytablebody);
+        // appends <table> into <body>
+        mybody.appendChild(mytable);
+        // sets the border attribute of mytable to 2;
+        mytable.setAttribute("border","2");
+    }
+</script>
+</head>
+<body onload="start()">
+</body>
+</html>
+ +

Manipulating the table with DOM and CSS

+ +

Getting a text node from the table

+ +

This example introduces two new DOM attributes. First it uses the childNodes attribute to get the list of child nodes of mycel. The childNodes list includes all child nodes, regardless of what their name or type is. Like getElementsByTagName(), it returns a list of nodes. The differences are that (a) getElementsByTagName() only returns elements of the specified tag name; and (b) getElementsByTagName() returns descendants at any level, not just immediate children. Once you have the returned list, use {{mediawiki.external('x')}} method to retrieve the desired child item. This example stores in myceltext the text node of the second cell in the second row of the table. Then, to display the results in this example, it creates a new text node whose content is the data of myceltext and appends it as a child of the <body> element.

+ +
If your object is a text node, you can use the data attribute and retrieve the text content of the node.
+ +
mybody      = document.getElementsByTagName("body")[0];
+mytable     = mybody.getElementsByTagName("table")[0];
+mytablebody = mytable.getElementsByTagName("tbody")[0];
+myrow       = mytablebody.getElementsByTagName("tr")[1];
+mycel       = myrow.getElementsByTagName("td")[1];
+
+// first item element of the childNodes list of mycel
+myceltext=mycel.childNodes[0];
+
+// content of currenttext is the data content of myceltext
+currenttext=document.createTextNode(myceltext.data);
+mybody.appendChild(currenttext);
+
+ +

Getting an attribute value

+ +

At the end of sample1 there is a call to setAttribute on the mytable object. This call was used to set the border property of the table. To retrieve the value of the attribute, use the getAttribute method:

+ +
mytable.getAttribute("border");
+
+ +

Hiding a column by changing style properties

+ +

Once you have the object in your JavaScript variable, you can set style properties directly. The following code is a modified version of sample1.html in which each cell of the second column is hidden and each cell of the first column is changed to have a red background. Note that the style property was set directly.

+ +
<html>
+<body onload="start()">
+</body>
+<script>
+    function start() {
+       var mybody =document.getElementsByTagName("body")[0];
+       mytable     = document.createElement("table");
+       mytablebody = document.createElement("tbody");
+
+       for(var j = 0; j < 2; j++) {
+           mycurrent_row=document.createElement("tr");
+           for(var i = 0; i < 2; i++) {
+               mycurrent_cell = document.createElement("td");
+               currenttext = document.createTextNode("cell is:" + i + j);
+               mycurrent_cell.appendChild(currenttext);
+               mycurrent_row.appendChild(mycurrent_cell);
+               // set the cell background color
+               // if the column is 0. If the column is 1 hide the cel
+               if (i == 0) {
+                   mycurrent_cell.style.background = "rgb(255,0,0)";
+               } else {
+                   mycurrent_cell.style.display = "none";
+               }
+           }
+           mytablebody.appendChild(mycurrent_row);
+       }
+       mytable.appendChild(mytablebody);
+       mybody.appendChild(mytable);
+    }
+</script>
+</html>
+
diff --git a/files/es/web/api/document_object_model/using_the_w3c_dom_level_1_core/index.html b/files/es/web/api/document_object_model/using_the_w3c_dom_level_1_core/index.html new file mode 100644 index 0000000000..6c5f14c025 --- /dev/null +++ b/files/es/web/api/document_object_model/using_the_w3c_dom_level_1_core/index.html @@ -0,0 +1,93 @@ +--- +title: Using the W3C DOM Level 1 Core +slug: Using_the_W3C_DOM_Level_1_Core +tags: + - DOM + - NeedsTranslation + - NeedsUpdate + - TopicStub +translation_of: Web/API/Document_object_model/Using_the_W3C_DOM_Level_1_Core +--- +

The W3C's DOM Level 1 Core is a powerful object model for changing the content tree of documents. It is supported in all major browsers including Mozilla Firefox and Microsoft Internet Explorer. It is a powerful base for scripting on the web.

+ +

What is a content tree?

+ +

Many HTML authors may think of HTML as something flat -- a bunch of text with tags in the middle. However, it is something much more than that. Any HTML document (or for that matter any SGML document or XML document) is a tree structure. For example, the following document and tree structure are similar (although not identical -- see the notes on whitespace in the DOM):

+ +
<html>
+<head>
+  <title>My Document</title>
+</head>
+<body>
+  <h1>Header</h1>
+  <p>Paragraph</p>
+</body>
+</html>
+
+ +

image:Using_the_W3C_DOM_Level_1_Core-doctree.jpg

+ +

When Mozilla parses a document, it builds a content tree and then uses it to display the document.

+ +

The terms used to describe trees show up often in the DOM Level 1 Core. Each of the boxes I drew in the tree above is a node in the tree. The line above a node expresses a parent-child relationship: the node on top is the parent, and the node on the bottom is the child. Two children of the same parent are therefore siblings. Similarly, one can refer to ancestors and descendants. (Cousins are too messy, though.)

+ +

What does the DOM Level 1 Core let me do?

+ +

The W3C DOM Level 1 allows you to change the content tree any way you want. It is powerful enough to build any HTML document from scratch. It allows authors to change anything in the document from script, at any time. The easiest way for web page authors to change the DOM dynamically is using JavaScript. In JavaScript, the document is accessible the same way it has been in older browsers: from the document property of the global object. This document object implements the Document interface from the W3C's DOM Level 1 spec.

+ +

A simple example

+ +

Suppose the author wants to take the above document and change the contents of the header, and write two paragraphs instead of one. The following script would do the job:

+ +

HTML Content

+ +
<body>
+<input type="button" value="Change this document." onclick="change()">
+<h2>Header</h2>
+<p>Paragraph</p>
+</body>
+
+ +

JavaScript Content

+ +
  function change() {
+    // document.getElementsByTagName("H2") returns a NodeList of the <h2>
+    // elements in the document, and the first is number 0:
+
+    var header = document.getElementsByTagName("H2").item(0);
+    // the firstChild of the header is a Text node:
+    header.firstChild.data = "A dynamic document";
+    // now the header is "A dynamic document".
+
+    var para = document.getElementsByTagName("P").item(0);
+    para.firstChild.data = "This is the first paragraph.";
+
+    // create a new Text node for the second paragraph
+    var newText = document.createTextNode("This is the second paragraph.");
+    // create a new Element to be the second paragraph
+    var newElement = document.createElement("P");
+    // put the text in the paragraph
+    newElement.appendChild(newText);
+    // and put the paragraph on the end of the document by appending it to
+    // the BODY (which is the parent of para)
+    para.parentNode.appendChild(newElement);
+  }
+ +

{{ EmbedLiveSample('A_simple_example', 800, 300) }}

+ +

You can see this script as a complete example.

+ +

How can I learn more?

+ +

Now that you are familiar with the basic concepts of the DOM, there is a document explaining the DOM Level 1 fundamental methods. It is the follow-up to this document.

+ +

See also the DOM Level 1 Core specification from the W3C. It's a reasonably clear spec, although it is formal. The main thing that's useful to authors is the description of the different DOM objects and all their properties and methods. Also see our other DOM documentation.

+ +
+

Original Document Information

+ + +
diff --git a/files/es/web/api/document_object_model/whitespace/index.html b/files/es/web/api/document_object_model/whitespace/index.html new file mode 100644 index 0000000000..a943896180 --- /dev/null +++ b/files/es/web/api/document_object_model/whitespace/index.html @@ -0,0 +1,476 @@ +--- +title: 'Cómo manejan el espacio en blanco HTML, CSS y el DOM' +slug: Referencia_DOM_de_Gecko/Cómo_espacioenblanco +tags: + - CSS + - DOM + - HTML + - JavaScript + - espacioenblanco + - whitespace +translation_of: Web/API/Document_Object_Model/Whitespace +--- +
{{APIRef("DOM")}}
+ +

La presencia de espacios en blanco en el DOM puede causar problemas de diseño y dificultar la manipulación del árbol de contenido de formas inesperadas, dependiendo de dónde se encuentra. Este artículo explora cuándo pueden surgir dificultades y analiza qué se puede hacer para mitigar los problemas resultantes.

+ +

¿Qué es el espacio en blanco?

+ +

El espacio en blanco es cualquier cadena de texto compuesta solo por espacios, tabulaciones o saltos de línea (para ser precisos, secuencias CRLF, retornos de carro o avances de línea). Estos caracteres te permiten formatear tu código de una manera que lo hará fácilmente legible por ti y otras personas. De hecho, gran parte de nuestro código fuente está lleno de estos caracteres de espacio en blanco, y solo tendemos a deshacernos de ellos en un paso de compilación de producción para reducir el tamaño de descarga del código.

+ +

¿HTML ignora en gran medida los espacios en blanco?

+ +

En el caso de HTML, los espacios en blanco se ignoran en gran medida: los espacios en blanco entre palabras se tratan como un solo carácter y los espacios en blanco al principio y al final de los elementos y los elementos externos se ignoran. Tomemos el siguiente ejemplo minimalista:

+ + + +
<!DOCTYPE html>
+
+    <h1>       ¡Hola      mundo!     </h1>
+ +

{{EmbedLiveSample('HTML_largely_ignores_whitespace')}}

+ +

Este código fuente contiene un par de avances de línea después del DOCTYPE y un montón de caracteres de espacio antes, después y dentro del elemento <h1>, pero al navegador no parece importarle en absoluto y solo muestra las palabras "¡Hola mundo!" como si estos caracteres no existieran en absoluto:

+ +

Esto es para que los espacios en blanco no afecten el diseño de tu página. Crear espacio alrededor y dentro de los elementos es el trabajo de CSS.

+ +

¿Qué sucede con los espacios en blanco?

+ +

Sin embargo, no solo desaparecen.

+ +

Cualquier carácter de espacio en blanco que esté fuera de los elementos HTML del documento original se representan en el DOM. Esto es necesario internamente para que el editor pueda preservar el formato de los documentos. Esto significa que:

+ + + +

Tomemos el siguiente documento, por ejemplo:

+ +
<!DOCTYPE html>
+<html>
+<head>
+  <title>Mi Documento</title>
+</head>
+<body>
+  <h1>Encabezado</h1>
+  <p>
+    Párrafo
+  </p>
+</body>
+</html>
+
+ +

El árbol del DOM para esto se ve así:

+ +

árbol de dom equivalente al ejemplo de HTML anterior

+ +

Conservar caracteres de espacio en blanco en el DOM es útil de muchas maneras, pero hay ciertos lugares donde esto hace que ciertos diseños sean más difíciles de implementar y causa problemas a los desarrolladores que quieren iterar a través de los nodos del DOM. Veremos estas y algunas soluciones más adelante.

+ +

¿CSS cómo procesa los espacios en blanco?

+ +

La mayoría de los espacios en blanco se ignoran, no todos. En el ejemplo anterior, uno de los espacios entre "!Hola" y "mundo!" todavía existe cuando la página se representa en un navegador. Hay reglas en el motor del navegador que deciden qué caracteres de espacio en blanco son útiles y cuáles no; estos se especifican al menos en parte en el Módulo de texto CSS Nivel 3, y especialmente las partes sobre la propiedad white-space en CSS y detalles de procesamiento del espacio en blanco, pero también ofrecemos una explicación más sencilla a continuación.

+ +

Tomemos otro ejemplo realmente simple. Para hacerlo más fácil, ilustramos todos los espacios con ◦, todas las tabulaciones con ⇥ y todos los saltos de línea con ⏎:

+ +

Este ejemplo:

+ +
<h1>◦◦◦¡Hola◦⏎
+⇥⇥⇥⇥<span>◦mundo!</span>⇥◦◦</h1>
+ +

se representa en el navegador así:

+ + + +

{{EmbedLiveSample('Hidden_example')}}

+ +

El elemento <h1> contiene solo elementos en línea. De hecho contiene:

+ + + +

Debido a esto, establece lo que se llama un {{cssxref("Inline_formatting_context", "contexto de formato en línea")}}. Este es uno de los posibles contextos de representación de diseño con los que funcionan los motores del navegador.

+ +

Dentro de este contexto, el procesamiento de caracteres de espacio en blanco se puede resumir de la siguiente manera:

+ +
    +
  1. +

    Primero, todos los espacios y tabulaciones inmediatamente antes y después de un salto de línea se ignoran, por lo que, si tomamos nuestro marcado de ejemplo anterior y aplicamos esta primera regla, obtenemos:

    + +
    <h1>◦◦◦¡Hola⏎
    +<span>◦mundo!</span>⇥◦◦</h1>
    +
  2. +
  3. +

    A continuación, todos los caracteres de tabulación se tratan como caracteres de espacio, por lo que el ejemplo se convierte en:

    + +
    <h1>◦◦◦¡Hola⏎
    +<span>◦mundo!</span>◦◦◦</h1>
    +
  4. +
  5. +

    A continuación, los saltos de línea se convierten en espacios:

    + +
    <h1>◦◦◦¡Hola◦<span>◦mundo!</span>◦◦◦</h1>
    +
  6. +
  7. +

    Después de eso, cualquier espacio inmediatamente después de otro espacio (incluso a través de dos elementos en línea separados) se ignora, por lo que terminamos con:

    + +
    <h1>◦¡Hola◦<span>mundo!</span>◦</h1>
    +
  8. +
  9. +

    Y finalmente, las secuencias de espacios al principio y al final de una línea se eliminan, por lo que eventualmente obtenemos esto:

    + +
    <h1>¡Hola◦<span>mundo!</span></h1>
    +
  10. +
+ +

Es por eso que las personas que visitan la página web simplemente verán la frase "¡Hola mundo!" muy bien escrita en la parte superior de la página, en lugar de un "!Hola" con una sangría extraña, seguido de un "mundo!" en la línea debajo de esa.

+ +
+

Nota: Firefox DevTools ha admitido el resaltado de nodos de texto desde la versión 52, lo que facilita ver exactamente qué contenido hay dentro de los nodos de espacios en blanco. Los nodos de espacios en blanco puros están marcados con una etiqueta "whitespace".

+
+ +

Espacio en blanco en contextos de formato de bloque

+ +

Anteriormente, solo miramos elementos que contienen elementos en línea y contextos de formato en línea. Si un elemento contiene al menos un elemento de bloque, entonces establece lo que se llama un {{cssxref("Block_formatting_context", "contexto de formato de bloque")}}.

+ +

En este contexto, los espacios en blanco se tratan de manera muy diferente. Veamos un ejemplo para explicar cómo. Hemos marcado los espacios en blanco como antes.

+ +
<body>⏎
+⇥<div>◦◦¡Hola◦◦</div>⏎
+⏎
+◦◦◦<div>◦◦mundo!◦◦</div>◦◦⏎
+</body>
+ +

Tenemos 3 nodos de texto que contienen solo espacios en blanco, uno antes del primer <div>, uno entre los 2 <div>s y uno después del segundo <div>.

+ +

Esto se renderiza así:

+ + + +

{{EmbedLiveSample('Hidden_example_2')}}

+ +

Podemos resumir cómo se maneja el espacio en blanco aquí de la siguiente manera (puede haber algunas pequeñas diferencias en el comportamiento exacto entre los navegadores, pero básicamente, esto funciona):

+ +
    +
  1. +

    Debido a que estamos dentro de un contexto de formato de bloque, todo debe ser un bloque, por lo que nuestros 3 nodos de texto también se convierten en bloques, al igual que los 2 <div>s. Los bloques ocupan todo el ancho disponible y se apilan unos encima de los otros, lo cual significa que terminamos con un diseño compuesto por esta lista de bloques:

    + +
    <block>⏎⇥</block>
    +<block>◦◦¡Hola◦◦</block>
    +<block>⏎◦◦◦</block>
    +<block>◦◦mundo!◦◦</block>
    +<block>◦◦⏎</block>
    +
  2. +
  3. +

    Esto luego se simplifica aún más aplicando las reglas de procesamiento para espacios en blanco en contextos de formato en línea a estos bloques:

    + +
    <block></block>
    +<block>¡Hola</block>
    +<block></block>
    +<block>mundo!</block>
    +<block></block>
    +
  4. +
  5. +

    Los 3 bloques vacíos que tenemos ahora no van a ocupar ningún espacio en el diseño final, porque no contienen nada, así que terminaremos con solo 2 bloques ocupando espacio en la página. Las personas que visitan la página web ven las palabras "!Hola" y "mundo!" en 2 líneas separadas, ya que esperarías que se distribuyeran 2 <div>s. El motor del navegador esencialmente ha ignorado todos los espacios en blanco que se agregaron en el código fuente.

    +
  6. +
+ +

Espacios entre elementos en línea y bloques en línea

+ +

Ahora analicemos algunos problemas que pueden surgir debido a los espacios en blanco y qué se puede hacer al respecto. En primer lugar, veremos qué sucede con los espacios entre los elementos en línea y de bloque en línea. De hecho, ya vimos esto en nuestro primer ejemplo, cuando describimos cómo se procesan los espacios en blanco dentro de los contextos de formato en línea.

+ +

Dijimos que había reglas para ignorar la mayoría de los caracteres, pero que los caracteres que separan palabras permanecen. Cuando solo se trata de elementos a nivel de bloque como <p> que solo contienen elementos en línea como <em>, <strong>, <span>, etc., normalmente no te importa esto porque el espacio en blanco adicional que llega al diseño es útil para separar las palabras en la oración.

+ +

Sin embargo, se vuelve más interesante cuando comienzas a usar elementos inline-block. Estos elementos se comportan como elementos en línea en el exterior y como bloques en el interior, y a menudo se utilizan para mostrar piezas de la IU más complejas que solo texto, una al lado de la otra en la misma línea, por ejemplo, elementos del menú de navegación.

+ +

Debido a que son bloques, muchas personas esperan que se comporten como tales, pero en realidad no es así. Si hay espacios en blanco de formato entre elementos en línea adyacentes, esto dará como resultado un espacio en el diseño, al igual que los espacios entre palabras en el texto.

+ +

Considera este ejemplo (nuevamente, los espacios en blanco en el HTML están marcados para que sean visibles):

+ +
.people-list {
+  list-style-type: none;
+  margin: 0;
+  padding: 0;
+}
+
+.people-list li {
+  display: inline-block;
+  width: 2em;
+  height: 2em;
+  background: #f06;
+  border: 1px solid;
+}
+
+ +
<ul class="people-list">⏎
+
+◦◦<li></li>⏎
+
+◦◦<li></li>⏎
+
+◦◦<li></li>⏎
+
+◦◦<li></li>⏎
+
+◦◦<li></li>⏎
+
+</ul>
+ +

Esto se traduce de la siguiente manera:

+ + + +

{{EmbedLiveSample('Hidden_example_3')}}

+ +

Probablemente no desees los espacios entre los bloques — dependiendo del caso de uso (¿esta es una lista de avatares o botones de navegación horizontales?), Probablemente desees que los lados del elemento estén alineados entre sí y poder controlar cualquier espacio tú mismo.

+ +

El Inspector HTML de Firefox DevTools resaltará los nodos de texto y también te mostrará exactamente qué áreas están ocupando los elementos, lo que es útil si te preguntas qué está causando el problema y tal vez estés pensando que tienes un margen adicional allí o algo así.

+ +

Espacio en blanco en Devtools

+ +

Hay algunas formas de solucionar este problema:

+ +

Utiliza Flexbox para crear la lista horizontal de elementos en lugar de probar una solución de inline-block. Esto se encarga de todo por ti y definitivamente es la solución preferida:

+ +
ul {
+  list-style-type: none;
+  margin: 0;
+  padding: 0;
+  display: flex;
+}
+ +

Si necesitas confiar en inline-block, puedes establecer el {{cssxref("font-size")}} de la lista a 0. Esto solo trabaja si tus bloques no tienen el tamaño ems (según el font-size, por lo que el tamaño del bloque también terminaría siendo 0). rems sería una buena opción aquí:

+ +
ul {
+  font-size: 0;
+  ...
+}
+
+li {
+  display: inline-block;
+  width: 2rem;
+  height: 2rem;
+  ...
+}
+
+ +

O puedes establecer un margen negativo en los elementos de la lista:

+ +
li {
+  display: inline-block;
+  width: 2rem;
+  height: 2rem;
+  margin-right: -0.25rem;
+}
+ +

También puedes resolver este problema colocando los elementos de tu lista en la misma línea en la fuente, lo cual hace que los nodos de espacios en blanco no se creen en primer lugar:

+ +
<li></li><li></li><li></li><li></li><li></li>
+ +

Recorrido del DOM y el espacio en blanco

+ +

Al intentar realizar una manipulación del DOM en JavaScript, también puedes encontrar problemas debido a los nodos de espacios en blanco. Por ejemplo, si tienes una referencia a un nodo padre y deseas afectar su primer elemento hijo usando Node.firstChild, si hay un nodo de espacio en blanco deshonesto justo después de la etiqueta de apertura principal, no obtendrás el resultado que esperabas. Se seleccionaría el nodo de texto en lugar del elemento al que deseas afectar.

+ +

Veamos otro ejemplo, si tienes un determinado subconjunto de elementos en los que deseas hacer algo en función de si están vacíos (no tienen nodos secundarios) o, no puedes verificar si cada elemento está vacío usando algo como Node.hasChildNodes(), pero nuevamente, si algún elemento destino contiene nodos de texto, podrías terminar con resultados falsos.

+ +

Funciones auxiliares de espacios en blanco

+ +

El siguiente código JavaScript define varias funciones que facilitan el manejo de espacios en blanco en el DOM:

+ +
/**
+ * En todo, el espacio en blanco se define como uno de los caracteres
+ *  "\t" TAB \u0009
+ *  "\n" LF  \u000A
+ *  "\r" CR  \u000D
+ *  " "  SPC \u0020
+ *
+ * Esto no usa la "\s" de Javascript porque eso incluye
+ * espacios irrompibles (y también algunos otros caracteres).
+ */
+
+
+/**
+ * Determina si el contenido de texto de un nodo es completamente de espacios en blanco.
+ *
+ * @param nod  Un nodo que implementa la interfaz | CharacterData | (es decir,
+ *             un nodo |Text|, |Comment| o |CDATASection|
+ * @return     True si todo el contenido de texto de |nod| es espacio en blanco,
+ *             de lo contrario false.
+ */
+function is_all_ws( nod )
+{
+  // Usa las características de String y RegExp de ECMA-262 Edición 3
+  return !(/[^\t\n\r ]/.test(nod.textContent));
+}
+
+
+/**
+ * Determina si un nodo debe ser ignorado por las funciones del iterador.
+ *
+ * @param nod  Un objeto implementando la interfaz |Node| de DOM1.
+ * @return     true si el nodo es:
+ *                1) Un nodo |Text| en que todo es espacio en blanco
+ *                2) Un nodo |Comment|
+ *             y de lo contrario false.
+ */
+
+function is_ignorable( nod )
+{
+  return ( nod.nodeType == 8) || // Un nodo comment
+         ( (nod.nodeType == 3) && is_all_ws(nod) ); // un nodo text, todo es eeb
+}
+
+/**
+ * Versión de |previousSibling| que omite los nodos que son completamente
+ * espacio en blanco o comentarios.  (Normalmente |previousSibling| es una propiedad
+ * de todos los nodos DOM que devuelve el nodo hermano, el nodo que es
+ * un hijo del mismo padre, que ocurre inmediatamente antes del
+ * nodo de referencia).
+ *
+ * @param sib  El nodo de referencia.
+ * @return     O bien:
+ *               1) El hermano anterior más cercano a |sib| eso no es
+ *                  ignorable según |is_ignorable|, o
+ *               2) null si no existe tal nodo.
+ */
+function node_before( sib )
+{
+  while ((sib = sib.previousSibling)) {
+    if (!is_ignorable(sib)) return sib;
+  }
+  return null;
+}
+
+/**
+ * Versión de |nextSibling| que omite los nodos que son completamente
+ * espacio en blanco o comentarios.
+ *
+ * @param sib  El nodo de referencia.
+ * @return     O bien:
+ *               1) El hermano más cercano a |sib| eso no es
+ *                  ignorable según |is_ignorable|, o
+ *               2) null si no existe tal nodo.
+ */
+function node_after( sib )
+{
+  while ((sib = sib.nextSibling)) {
+    if (!is_ignorable(sib)) return sib;
+  }
+  return null;
+}
+
+/**
+ * Versión de |lastChild| que omite los nodos que son completamente
+ * espacio en blanco o comentarios.  (Normalmente |lastChild| es una propiedad
+ * de todos los nodos del DOM que da el último de los nodos contenidos
+ * directamente en el nodo de referencia).
+ *
+ * @param sib  El nodo de referencia.
+ * @return     O bien:
+ *               1) El último hijo de |sib| eso no es
+ *                  ignorable según |is_ignorable|, o
+ *               2) null si no existe tal nodo.
+ */
+function last_child( par )
+{
+  var res=par.lastChild;
+  while (res) {
+    if (!is_ignorable(res)) return res;
+    res = res.previousSibling;
+  }
+  return null;
+}
+
+/**
+ * Versión de |firstChild| que omite los nodos que son completamente
+ * espacios en blanco y comentarios.
+ *
+ * @param sib  El nodo de referencia.
+ * @return     O bien:
+ *               1) El primer hijo de |sib| eso no es
+ *                  ignorable según |is_ignorable|, o
+ *               2) null si no existe tal nodo.
+ */
+function first_child( par )
+{
+  var res=par.firstChild;
+  while (res) {
+    if (!is_ignorable(res)) return res;
+    res = res.nextSibling;
+  }
+  return null;
+}
+
+/**
+ * Versión de |data| que no incluye espacios en blanco al principio
+ * y finaliza y normaliza todos los espacios en blanco a un solo espacio.  (Normalmente
+ * |data| es una propiedad de los nodos de texto que proporciona el texto del nodo).
+ *
+ * @param txt  El nodo de texto cuyos datos se deben devolver
+ * @return     Una cadena que proporciona el contenido del nodo de texto con
+ *             espacios en blanco colapsados.
+ */
+function data_of( txt )
+{
+  var data = txt.textContent;
+  // Usa las características de String y RegExp de ECMA-262 Edición 3
+  data = data.replace(/[\t\n\r ]+/g, " ");
+  if (data.charAt(0) == " ")
+    data = data.substring(1, data.length);
+  if (data.charAt(data.length - 1) == " ")
+    data = data.substring(0, data.length - 1);
+  return data;
+}
+
+ +

Ejemplo

+ +

El siguiente código demuestra el uso de las funciones anteriores. Itera sobre los hijos de un elemento (cuyos hijos son todos elementos) para encontrar aquel cuyo texto es "Este es el tercer párrafo", y luego cambia el atributo de clase y el contenido de ese párrafo.

+ +
var cur = first_child(document.getElementById("test"));
+while (cur)
+{
+  if (data_of(cur.firstChild) == "Este es el tercer párrafo.")
+  {
+    cur.className = "magic";
+    cur.firstChild.textContent = "Este es el párrafo mágico.";
+  }
+  cur = node_after(cur);
+}
+
diff --git a/files/es/web/api/documentorshadowroot/getselection/index.html b/files/es/web/api/documentorshadowroot/getselection/index.html new file mode 100644 index 0000000000..6c03b64dcf --- /dev/null +++ b/files/es/web/api/documentorshadowroot/getselection/index.html @@ -0,0 +1,13 @@ +--- +title: Document.getSelection() +slug: Web/API/Document/getSelection +tags: + - Referencia + - Selección + - metodo +translation_of: Web/API/DocumentOrShadowRoot/getSelection +translation_of_original: Web/API/Document/getSelection +--- +

{{APIRef("DOM")}}

+ +

Este método funciona exactamente igual que {{domxref("Window.getSelection()")}}; devuelve un objeto {{domxref("Selection")}} que representa el texto que se ha seleccionado en el documento.

diff --git a/files/es/web/api/documentorshadowroot/pointerlockelement/index.html b/files/es/web/api/documentorshadowroot/pointerlockelement/index.html new file mode 100644 index 0000000000..cc5d490e5c --- /dev/null +++ b/files/es/web/api/documentorshadowroot/pointerlockelement/index.html @@ -0,0 +1,105 @@ +--- +title: Document.pointerLockElement +slug: Web/API/Document/pointerLockElement +translation_of: Web/API/DocumentOrShadowRoot/pointerLockElement +--- +
{{APIRef("DOM")}}
+ +

La propiedad pointerLockElement conserva el elemento adquirido, para el evento del mouse, mientras el bloqueo se encuentre activo .  Es null si el bloqueo se encuentra en estado pendiente para bloqueo, o si el bloqueo se ha liberado, es decir ya no se encuentra en estado bloqueado, o si el elemento bloqueado se encuentra en otro documento.

+ +

Sintaxis

+ +
var elemento = document.pointerLockElement;
+
+ +

Valor retornado

+ +

Un {{domxref("Element")}} o null.

+ +

Especificaciones

+ + + + + + + + + + + + + + +
EspecificaciónEstadoComentario
{{SpecName('Bloquear puntero','l#extension-to-the-document-interface','Document')}}{{Spec2('Pointer lock')}}Extiende de la interfaz Document
+ +

Compatibilidad del Navegador

+ +

{{ CompatibilityTable() }}

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CaracterísticaChromeEdgeFirefox (Gecko)Internet ExplorerOperaSafari
Soporte Básico{{ CompatVersionUnknown() }} {{property_prefix("webkit")}}{{CompatVersionUnknown}}{{ CompatVersionUnknown() }} {{property_prefix("moz")}}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}
Soporte sin prefijar{{ CompatVersionUnknown() }}{{CompatUnknown}}{{CompatGeckoDesktop(50)}}   
+
+ +
+ + + + + + + + + + + + + + + + + + + + + +
CaracterísticaAndroidEdgeFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Soporte Básico{{ CompatUnknown() }}{{CompatVersionUnknown}}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}{{ CompatUnknown() }}
+
+ +

Véase también:

+ + diff --git a/files/es/web/api/documentorshadowroot/stylesheets/index.html b/files/es/web/api/documentorshadowroot/stylesheets/index.html new file mode 100644 index 0000000000..0458cb3fc9 --- /dev/null +++ b/files/es/web/api/documentorshadowroot/stylesheets/index.html @@ -0,0 +1,22 @@ +--- +title: Document.styleSheets +slug: Web/API/Document/styleSheets +translation_of: Web/API/DocumentOrShadowRoot/styleSheets +translation_of_original: Web/API/Document/styleSheets +--- +

{{ ApiRef() }}

+

Resumen

+

Devuelve una lista de objetos de tipo stylesheet para las hojas de estilo que están específicamente enlazadas o contenidas en el documento.

+

Propiedades

+

styleSheetList.length - devuelve el número de objetos de tipo stylesheet contenidos en el objeto.

+

Sintaxis

+
styleSheetList = document.styleSheets
+
+

El objeto devuelto es del tipo StyleSheetList.

+

Es una colección ordenada de objetos de tipo stylesheet. styleSheetList.item(index) o simplemente styleSheetList{{ mediawiki.external(' + + index + ') }} devuelve un único objeto de tipo stylesheet con el índice especificado (el índice es de origen 0).

+

Especificación

+

DOM Level 2 Style: styleSheets

+

{{ languages( { "ja": "ja/DOM/document.styleSheets", "pl": "pl/DOM/document.styleSheets" } ) }}

diff --git a/files/es/web/api/domstring/binary/index.html b/files/es/web/api/domstring/binary/index.html new file mode 100644 index 0000000000..a52358f62c --- /dev/null +++ b/files/es/web/api/domstring/binary/index.html @@ -0,0 +1,31 @@ +--- +title: Cadenas binarias +slug: Web/API/DOMString/Cadenas_binarias +tags: + - Arreglos tipados JavaScript + - Cadena + - Cadena de caracteres + - DOM + - JavaScript + - Referencia + - String +translation_of: Web/API/DOMString/Binary +--- +

{{jsxref("String", "Cadenas JavaScript")}} son cadenas codificadas en UTF-16. Esto significa que cada unidad de código requiere dos bytes de memoria y puede representar 65535 puntos de código diferentes. Un subconjunto de estas cadenas está representado por cadenas UTF-16 que contienen solo caracteres ASCII (es decir, caracteres cuyo punto de código no excede 127). Por ejemplo, la cadena "¡Hola mundo!" pertenece al subconjunto ASCII, mientras que la cadena "ÀÈÌÒÙ" no. Una cadena binaria es un concepto similar al subconjunto ASCII, pero en lugar de limitar el rango a 127, permite hasta 255 puntos de código. Sin embargo, su propósito no es representar caracteres, sino datos binarios. El tamaño de los datos así representados es el doble de lo que sería en formato binario normal, sin embargo, esto no será visible para el usuario final, ya que la longitud de las cadenas de JavaScript se calcula usando dos bytes como unidad.

+ +

Las cadenas binarias no forman parte del diseño del lenguaje JavaScript. Sin embargo, al menos una función nativa requiere cadenas binarias como entrada, {{domxref("WindowBase64.btoa", "btoa()")}}: invocarla en una cadena que contiene puntos de código mayores de 255 causará un error Caracter fuera de rango.

+ +

La razón que llevó al uso de unidades de código UTF-16 como marcadores de posición para los números uint8 es que a medida que las aplicaciones web se vuelven cada vez más poderosas (agregando funciones como manipulación de audio y video, acceso a datos sin procesar usando WebSockets, y así sucesivamente) ha quedado claro que hay ocasiones en las que sería útil que el código JavaScript pudiera manipular rápida y fácilmente datos binarios sin procesar.

+ +

En el pasado, esto se tenía que simular tratando los datos sin procesar como string y utilizar el método charCodeAt() para leer los bytes del búfer de datos (es decir, usando cadenas binarias). Sin embargo, esto es lento y propenso a errores, debido a la necesidad de múltiples conversiones (especialmente si los datos binarios en realidad no son datos en formato de bytes, sino, por ejemplo, enteros de 32 bits o flotantes).

+ +

Los arreglos tipados en JavaScript proporcionan un mecanismo para acceder a datos binarios sin procesar mucho más eficientemente. La API de StringView cuyo constructor no es nativo está un nivel por encima de los arreglos tipados y proporciona una interfaz para cadenas similar a la de C.

+ +

Ve también

+ + diff --git a/files/es/web/api/domstring/cadenas_binarias/index.html b/files/es/web/api/domstring/cadenas_binarias/index.html deleted file mode 100644 index a52358f62c..0000000000 --- a/files/es/web/api/domstring/cadenas_binarias/index.html +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Cadenas binarias -slug: Web/API/DOMString/Cadenas_binarias -tags: - - Arreglos tipados JavaScript - - Cadena - - Cadena de caracteres - - DOM - - JavaScript - - Referencia - - String -translation_of: Web/API/DOMString/Binary ---- -

{{jsxref("String", "Cadenas JavaScript")}} son cadenas codificadas en UTF-16. Esto significa que cada unidad de código requiere dos bytes de memoria y puede representar 65535 puntos de código diferentes. Un subconjunto de estas cadenas está representado por cadenas UTF-16 que contienen solo caracteres ASCII (es decir, caracteres cuyo punto de código no excede 127). Por ejemplo, la cadena "¡Hola mundo!" pertenece al subconjunto ASCII, mientras que la cadena "ÀÈÌÒÙ" no. Una cadena binaria es un concepto similar al subconjunto ASCII, pero en lugar de limitar el rango a 127, permite hasta 255 puntos de código. Sin embargo, su propósito no es representar caracteres, sino datos binarios. El tamaño de los datos así representados es el doble de lo que sería en formato binario normal, sin embargo, esto no será visible para el usuario final, ya que la longitud de las cadenas de JavaScript se calcula usando dos bytes como unidad.

- -

Las cadenas binarias no forman parte del diseño del lenguaje JavaScript. Sin embargo, al menos una función nativa requiere cadenas binarias como entrada, {{domxref("WindowBase64.btoa", "btoa()")}}: invocarla en una cadena que contiene puntos de código mayores de 255 causará un error Caracter fuera de rango.

- -

La razón que llevó al uso de unidades de código UTF-16 como marcadores de posición para los números uint8 es que a medida que las aplicaciones web se vuelven cada vez más poderosas (agregando funciones como manipulación de audio y video, acceso a datos sin procesar usando WebSockets, y así sucesivamente) ha quedado claro que hay ocasiones en las que sería útil que el código JavaScript pudiera manipular rápida y fácilmente datos binarios sin procesar.

- -

En el pasado, esto se tenía que simular tratando los datos sin procesar como string y utilizar el método charCodeAt() para leer los bytes del búfer de datos (es decir, usando cadenas binarias). Sin embargo, esto es lento y propenso a errores, debido a la necesidad de múltiples conversiones (especialmente si los datos binarios en realidad no son datos en formato de bytes, sino, por ejemplo, enteros de 32 bits o flotantes).

- -

Los arreglos tipados en JavaScript proporcionan un mecanismo para acceder a datos binarios sin procesar mucho más eficientemente. La API de StringView cuyo constructor no es nativo está un nivel por encima de los arreglos tipados y proporciona una interfaz para cadenas similar a la de C.

- -

Ve también

- - diff --git a/files/es/web/api/element/accesskey/index.html b/files/es/web/api/element/accesskey/index.html deleted file mode 100644 index 7d73c395fc..0000000000 --- a/files/es/web/api/element/accesskey/index.html +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: Element.accessKey -slug: Web/API/Element/accessKey -tags: - - API - - Propiedad - - necesidades de contenido -translation_of: Web/API/HTMLElement/accessKey -translation_of_original: Web/API/Element/accessKey ---- -
{{APIRef("DOM")}}
- -
 
- -

La propiedad Element.accessKey establece la pulsación de teclado mediante el cual un usuario puede presionar para saltar a este elemento.

- -
-

Nota: la propiedad Element.accessKey se usa raramente debido a sus múltiples conflictos con las asociaciones de teclas que ya están presentes  en los navegadores. Para evitar esto, los navegadores implementan el comportamiento tecla de acceso si se pulsan las claves con otras teclas "cualificadas" (como Alt + tecla de acceso).

-
- -

 

- -

 

diff --git a/files/es/web/api/element/blur_event/index.html b/files/es/web/api/element/blur_event/index.html new file mode 100644 index 0000000000..b54ad3e6a6 --- /dev/null +++ b/files/es/web/api/element/blur_event/index.html @@ -0,0 +1,156 @@ +--- +title: blur (evento) +slug: Web/Events/blur +tags: + - DOM +translation_of: Web/API/Element/blur_event +--- +

El evento blur es disparado cuando un elemento ha perdido su foco. La diferencia principal entre este evento y focusout es que sólo el último se propaga (bubbles).

+ +

Información General

+ +
+
Especificación
+
DOM L3
+
Interfaz
+
{{domxref("FocusEvent")}}
+
Burbujas
+
No
+
Cancelable
+
No
+
Objetivo
+
Element
+
Acción por defecto
+
Ninguna.
+
+ +

{{NoteStart}}El valor de  {{domxref("Document.activeElement")}} varía a traves de navegadores mientras este evento está siendo manejado ({{bug(452307)}}): IE10 lo agrega al elemento al cual el foco se movera, mientras Firefox y Chrome muy seguido lo agregan al cuerpo del documento.{{NoteEnd}}

+ +

Propiedades

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropiedadTipoDescripción
target {{readonlyInline}}{{domxref("EventTarget")}}Objetivo del evento (elemento DOM)
type {{readonlyInline}}{{domxref("DOMString")}}El tipo de evento.
bubbles {{readonlyInline}}{{jsxref("Boolean")}}Si el elemento normalmente se propaga o no.
cancelable {{readonlyInline}}{{jsxref("Boolean")}}Si el evento es cancelable o no.
relatedTarget {{readonlyInline}}{{domxref("EventTarget")}} (DOM element)null
+ +

Delegación de eventos

+ +

Hay dos maneras de implementar la delegación de eventos para este evento: usando el evento focusout en exploradores que lo soporten, o cambiando el parámetro "useCapture" de addEventListener a true:

+ +

Contenido HTML

+ +
<form id="form">
+  <input type="text" placeholder="text input">
+  <input type="password" placeholder="password">
+</form>
+ +

Contenido JavaScript

+ +
var form = document.getElementById("form");
+form.addEventListener("focus", function( event ) {
+  event.target.style.background = "pink";
+}, true);
+form.addEventListener("blur", function( event ) {
+  event.target.style.background = "";
+}, true);
+ +

{{EmbedLiveSample('Delegación_de_eventos')}}

+ +

Compatibilidad en navegadores

+ +
{{CompatibilityTable}}
+ +
+ + + + + + + + + + + + + + + + + + + + + +
CaracterísticaChromeEdgeFirefox (Gecko)Internet ExplorerOperaSafari
Soporte básico5{{CompatVersionUnknown}}{{CompatVersionUnknown}}[1]612.15.1
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + +
CaracterísticaAndroidChrome para AndroidEdgeFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Soporte básico4.053{{CompatVersionUnknown}}{{CompatUnknown}}10.012.15.1
+
+ +

[1] Antes de Gecko 24 {{geckoRelease(24)}} la interfaz para este evento era {{domxref("Event")}}, no {{domxref("FocusEvent")}}. Vea ({{bug(855741)}}).

+ +

Eventos relacionados

+ + diff --git a/files/es/web/api/element/name/index.html b/files/es/web/api/element/name/index.html deleted file mode 100644 index c970ea8947..0000000000 --- a/files/es/web/api/element/name/index.html +++ /dev/null @@ -1,80 +0,0 @@ ---- -title: Element.name -slug: Web/API/Element/name -tags: - - API - - Compatibilidad de los navegadores - - DOM - - Elemento - - Propiedad - - Referencia - - Web - - actualizacion -translation_of: Web/API -translation_of_original: Web/API/Element/name ---- -

{{ APIRef("DOM") }}

- -

Sumario

- -

name obtiene o establece la propiedad del nombre de un objeto DOM; sólo se aplica a los siguientes elementos: {{ HTMLelement("a") }}, {{ HTMLelement("applet") }}, {{ HTMLelement("button") }}, {{ HTMLelement("form") }}, {{ HTMLelement("frame") }}, {{ HTMLelement("iframe") }}, {{ HTMLelement("img") }}, {{ HTMLelement("input") }}, {{ HTMLelement("map") }}, {{ HTMLelement("meta") }}, {{ HTMLelement("object") }}, {{ HTMLelement("param") }}, {{ HTMLelement("select") }}, and {{ HTMLelement("textarea") }}.

- -
-

Nota: La propiedad name no esixte para otros elementos; a diferencia de tagName y nodeName, no es una propiedad de los modos de comunicación (interfaces) {{domxref("Node")}}, {{domxref("Element")}} or {{domxref("HTMLElement")}}.

-
- -

name puede ser utilizado en el método{{ domxref("document.getElementsByName()") }} , en una configuración y con la colección de elementos de la configuración. cuando utilizamos una configuración o  elementos de una colección, puede devolver un solo elemento o una colección.

- -

Síntasix

- -
HTMLElement.name = string;
-var elName = HTMLElement.name;
-
-var fControl = HTMLFormElement.elementName;
-var controlCollection = HTMLFormElement.elements.elementName;
-
- -

Ejemplo

- -
<form action="" name="formA">
-  <input type="text" value="foo">
-</form>
-
-<script type="text/javascript">
-
-  // Get a reference to the first element in the form
-  var formElement = document.forms['formA'].elements[0];
-
-  // Give it a name
-  formElement.name = 'inputA';
-
-  // Show the value of the input
-  alert(document.forms['formA'].elements['inputA'].value);
-
-</script>
-
- -

Notas

- -

En Internet Explorer (IE), la propiedad name de los objetos DOM , creada utilizando{{ domxref("document.createElement()") }} no puede ser establecida o modificada

- -

Especificaciones

- -

W3C DOM 2 HTML Specification:

- - diff --git a/files/es/web/api/element/ongotpointercapture/index.html b/files/es/web/api/element/ongotpointercapture/index.html deleted file mode 100644 index 3023c3758e..0000000000 --- a/files/es/web/api/element/ongotpointercapture/index.html +++ /dev/null @@ -1,134 +0,0 @@ ---- -title: Element.ongotpointercapture -slug: Web/API/Element/ongotpointercapture -tags: - - API - - Controlador - - DOM - - Elemento - - Eventos Puntero - - Propiedad - - Referencia -translation_of: Web/API/GlobalEventHandlers/ongotpointercapture -translation_of_original: Web/API/Element/ongotpointercapture ---- -

{{ ApiRef("DOM") }}

- -

ongotpointercapture es una propiedad {{domxref("EventHandler")}} de la interfaz {{domxref("Element")}}  que devuelve el controlador de eventos (función) para el evento tipo {{event("gotpointercapture")}}.

- -

Síntasix

- -
var gotCaptureHandler = target.ongotpointercpature;
-
- -

Valor de Retorno

- -
-
gotCaptureHandler
-
El controlador de eventos  gotpointercapture para el elemento target.
-
- -

Ejemplo

- -
<html>
-<script>
-function overHandler(ev) {
- // Determine the target event's gotpointercapture handler
- var gotCaptureHandler = ev.target.ongotpointercapture;
-}
-function init() {
- var el=document.getElementById("target");
- el.onpointerover = overHandler;
-}
-</script>
-<body onload="init();">
-<div id="target"> Touch me ... </div>
-</body>
-</html>
-
- -

Especificaciones

- - - - - - - - - - - - - - - - - - - -
EspecificaciónEstadoComentario
{{SpecName('Pointer Events 2','#widl-Element-ongotpointercapture', 'ongotpointercapture')}}{{Spec2('Pointer Events 2')}}Versión no estable.
{{SpecName('Pointer Events', '#widl-Element-ongotpointercapture', 'ongotpointercapture')}}{{Spec2('Pointer Events')}}Definición inicial.
- -

Compatibilidad en los Navegadores

- -

{{CompatibilityTable}}

- -
- - - - - - - - - - - - - - - - - - - -
FunciónChromeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
Soporte Básico{{CompatNo}}{{CompatVersionUnknown}} [1]{{CompatIE("10")}}{{CompatNo}}{{CompatNo}}
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - -
FunciónAndroidAndroid WebviewChrome for AndroidFirefox Mobile (Gecko)Firefox OSIE MobileOpera MobileSafari Mobile
Soporte Básico{{CompatNo}}{{CompatNo}}{{CompatNo}}{{CompatNo}}{{CompatNo}}{{CompatIE("10")}}{{CompatNo}}{{CompatNo}}
-
- -

[1] Implementación retirada. Ver {{Bug("1166347")}}.

- -

Ver también

- - diff --git a/files/es/web/api/element/onlostpointercapture/index.html b/files/es/web/api/element/onlostpointercapture/index.html deleted file mode 100644 index 9a07506d45..0000000000 --- a/files/es/web/api/element/onlostpointercapture/index.html +++ /dev/null @@ -1,133 +0,0 @@ ---- -title: Element.onlostpointercapture -slug: Web/API/Element/onlostpointercapture -tags: - - API - - Controlador de Eventos - - DOM - - Eventos Puntero - - Propiedad - - Referencia -translation_of: Web/API/GlobalEventHandlers/onlostpointercapture -translation_of_original: Web/API/Element/onlostpointercapture ---- -

{{ ApiRef("DOM") }}

- -

onlostpointercapture es una propiedad {{domxref("EventHandler")}} de la interfaz {{domxref("Element")}}  que devuelve el controlador de eventos (función) para el evento tipo {{event("lostpointercapture")}} .

- -

Síntasix

- -
var lostCaptureHandler = target.onlostpointercpature;
-
- -

Valor de Retorno

- -
-
lostCaptureHandler
-
El controlador de eventos  lostpointercapture para el elemento target.
-
- -

Ejemplo

- -
<html>
-<script>
-function overHandler(ev) {
- // Determine the target event's lostpointercapture handler
- var lostCaptureHandler = ev.target.onlostpointercapture;
-}
-function init() {
- var el=document.getElementById("target");
- el.onpointerover = overHandler;
-}
-</script>
-<body onload="init();">
-<div id="target"> Touch me ... </div>
-</body>
-</html>
-
- -

Especificaciones

- - - - - - - - - - - - - - - - - - - -
EspecificaciónEstadoComentario
{{SpecName('Pointer Events 2','#widl-Element-onlostpointercapture', 'onlostpointercapture')}}{{Spec2('Pointer Events 2')}}Versión no estable.
{{SpecName('Pointer Events', '#widl-Element-onlostpointercapture', 'onlostpointercapture')}}{{Spec2('Pointer Events')}}Definición inicial.
- -

Compatibilidad en los Navegadores

- -

{{CompatibilityTable}}

- -
- - - - - - - - - - - - - - - - - - - -
FunciónChromeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
Soporte Básico{{CompatNo}}{{CompatVersionUnknown}} [1]{{CompatIE("10")}}{{CompatNo}}{{CompatNo}}
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - -
FunciónAndroidAndroid WebviewChrome for AndroidFirefox Mobile (Gecko)Firefox OSIE MobileOpera MobileSafari Mobile
Soporte Básico{{CompatNo}}{{CompatNo}}{{CompatNo}}{{CompatNo}}{{CompatNo}}{{CompatIE("10")}}{{CompatNo}}{{CompatNo}}
-
- -

[1] Implementación retirada. Ver {{Bug("1166347")}}.

- -

Ver también

- - diff --git a/files/es/web/api/element/onwheel/index.html b/files/es/web/api/element/onwheel/index.html deleted file mode 100644 index b8f829969b..0000000000 --- a/files/es/web/api/element/onwheel/index.html +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Element.onwheel -slug: Web/API/Element/onwheel -tags: - - API - - DOM - - Gecko - - Propiedad - - Referencia -translation_of: Web/API/GlobalEventHandlers/onwheel ---- -

{{ ApiRef("DOM") }}

- -

{{ non-standard_header() }}

- -

Sumario

- -

La propiedad  onwheel devuelve el código del controlador de eventos onwheel en el elemento actual.

- -

Syntax

- -
element.onwheel = event handling code
-
- -

Notas

- -

El evento wheel se genera cuando el usuario desplaza el contenido de un elemento.

- -

Ver también

- -

Bug 18542 – el atributo onmousewheel  debe ser reemplazado con onwheel

diff --git a/files/es/web/api/elementcssinlinestyle/style/index.html b/files/es/web/api/elementcssinlinestyle/style/index.html new file mode 100644 index 0000000000..62c8903b72 --- /dev/null +++ b/files/es/web/api/elementcssinlinestyle/style/index.html @@ -0,0 +1,52 @@ +--- +title: Element.style +slug: Web/API/HTMLElement/style +translation_of: Web/API/ElementCSSInlineStyle/style +--- +

{{ ApiRef("HTML DOM") }}

+ +

Resumen

+ +

Devuelve un objeto que representa el atributo style del elemento.

+ +

Ejemplo

+ +
var div = document.getElementById("div1");
+div.style.marginTop = ".25in";
+
+ +

Notas

+ +

Ya que la propiedad style tiene la misma (y más alta) prioridad en la cascada CSS que las declaraciones in-line hechas mediante el atributo style, resulta muy útil para establecer el estilo en un elemento específico. 

+ +

Sin embargo, no resulta útil para aprender acerca del estilo original de un elemento, ya que representa sólo la declaración CSS en el atributo style in-line y no aquellos atributos que vienen de alguna otra parte, como las declaraciones en la sección <head> o en hojas de estilo.

+ +

Para recoger los valores de todas las propiedades CSS de un elemento, deberías usar window.getComputedStyle en su lugar.

+ +

Mira la lista de Propiedades CSS del DOM (DOM CSS Properties List) para tener una lista completa de las propiedades CSS que están disponibles en el Gecko DOM.

+ +

Generalmente es mejor usarla propiedad style que usar elt.setAttribute('style', '...'), ya que el uso de la propiedad style no reemplazará otras propiedades CSS que puedan especificarse en el atributo style.

+ +

Los estilos no pueden establecerse asignando una cadena a la propiedad (solo lectura) style, como en elt.style = "color: blue;". Esto es porque el atributo style devuelve un objeto del tipo CSSStyleDeclaration. En su lugar, pueds establecer las propiedades como:

+ +
elt.style.color = "blue";  // Asignación directa
+
+var st = elt.style;
+st.color = "blue";  // Asignación Indirecta
+
+ +

El siguiente código presenta los nombres de todas las propiedades style, los valores se establecen de forma explícita para los elementos elt y sus valores heredados: 

+ +
var elt = document.getElementById("elementIdHere");
+var out = "";
+var st = elt.style;
+var cs = window.getComputedStyle(elt, null);
+for (x in st)
+  out += "  " + x + " = '" + st[x] + "' > '" + cs[x] + "'\n";
+
+ +

 

+ +

Especificación

+ +

DOM Level 2 Style: ElementCSSInlineStyle.style

diff --git a/files/es/web/api/elementoshtmlparavideo/index.html b/files/es/web/api/elementoshtmlparavideo/index.html deleted file mode 100644 index 4b36d9cba2..0000000000 --- a/files/es/web/api/elementoshtmlparavideo/index.html +++ /dev/null @@ -1,202 +0,0 @@ ---- -title: Elementos HTML para Video -slug: Web/API/ElementosHTMLparaVideo -translation_of: Web/API/HTMLVideoElement ---- -
-
{{APIRef("HTML DOM")}}
-
- -

El interfaz de HTMLVideoElement provee propiedades especiales y metodos para manipular objectos de videos. Tambien, este interfaz hereda propiedades y métodos de {{domxref("HTMLMediaElement")}} y {{domxref("HTMLElement")}}.

- -

La lista de supported media formats (formatos de medios compatibles) varía de un navegador a otro. Debe proveer su video en un formato sencillo que sea compatible con todos los navegadores o proveer varias fuentes de videos in varios formatos, para que así todo navegador que necesite este cubierto.

- -

{{InheritanceDiagram(600, 140)}}

- -

Propiedades

- -

Hereda las propiedades de los interfaces anteriores, {{domxref("HTMLMediaElement")}}, y {{domxref("HTMLElement")}}.

- -
-
{{domxref("HTMLVideoElement.height")}}
-
Es un {{domxref("DOMString")}} que refleja el atributo HTML {{htmlattrxref("height", "video")}}, el cual especifica la altura del area mostrada, en pixeles CSS.
-
{{domxref("HTMLVideoElement.poster")}}
-
Es un {{domxref("DOMString")}} que refleja el atributo HTML {{htmlattrxref("poster", "video")}}, el cual especifica que imagen sera mostrada en la ausencia de data de video.
-
{{domxref("HTMLVideoElement.videoHeight")}} {{readonlyInline}}
-
Devuelve un unsigned long que contiene la altura intrinsica del recurso en pixeles CSS, tomando en consideracion las dimensiones, aspecto proporcional, apertura limpia, resolucion, etc., ya definidas por el formato usado por el recurso.  Si el estado disponible del elemento es HAVE_NOTHING, su valor es 0.
-
{{domxref("HTMLVideoElement.videoWidth")}} {{readonlyInline}}
-
Devuelve un unsigned long que contiene la anchura intrinsica del recurso en pixeles CSS, tomando en consideracion las dimensiones, aspecto proporcional, apertura limpia, resolucion, etc., ya definidas por el formato usado por el recurso.  Si el estado disponible del elemento es HAVE_NOTHING, su valor es 0.
-
{{domxref("HTMLVideoElement.width")}}
-
Es un {{domxref("DOMString")}} que refleja el atributo HTML {{htmlattrxref("width", "video")}}, el cual especifica la anchura del area mostrada, en pixeles CSS.
-
- -

Propiedades especificas para Gecko

- -
-
{{domxref("HTMLVideoElement.mozParsedFrames")}} {{readonlyInline}}{{non-standard_inline}}
-
Devuelve un unsigned long con el conteo de marcos de video que han sido analizados del recurso de multimedia.
-
{{domxref("HTMLVideoElement.mozDecodedFrames")}} {{readonlyInline}}{{non-standard_inline}}
-
Devuelve un unsigned long con el conteo de marcos de video que han sido decifrados como imágines.
-
{{domxref("HTMLVideoElement.mozPresentedFrames")}} {{readonlyInline}}{{non-standard_inline}}
-
Devuelve un unsigned long con el conteo de marcos decodificados que han sido  presentados a la canalización de render para pintar.
-
{{domxref("HTMLVideoElement.mozPaintedFrames")}} {{readonlyInline}}{{non-standard_inline}}
-
Devuelve un unsigned long con el conteo de marcos presentados que han sido pintados en la pantalla.
-
{{domxref("HTMLVideoElement.mozFrameDelay")}} {{readonlyInline}}{{non-standard_inline}}
-
Devuelve un double con el tiempo, en segundos, que el último marco de video fue pintado por retrazo.
-
{{domxref("HTMLVideoElement.mozHasAudio")}} {{readonlyInline}}{{non-standard_inline}}
-
Devuelve un {{domxref("Boolean")}} indicando si existe algún audio asociado con el video.
-
- -

Métodos

- -

Hereda los métodos anteriores de {{domxref("HTMLMediaElement")}} y {{domxref("HTMLElement")}}.

- -
-
{{domxref("HTMLVideoElement.getVideoPlaybackQuality()")}} {{experimental_inline}}
-
Devuelve un {{domxref("VideoPlaybackQuality")}} para objetos que contienen las medidas de reproducciones actuales.
-
- -

Especificaciones

- - - - - - - - - - - - - - - - - - - - - - - - -
EspecificacionEstadoComentario
{{SpecName('Media Source Extensions', '#idl-def-HTMLVideoElement', 'Extensions to HTMLVideoElement')}}{{Spec2("Media Source Extensions")}}Anadio el metodo getVideoPlaybackQuality() .
{{SpecName('HTML WHATWG', "the-video-element.html#the-video-element", "HTMLAreaElement")}}{{Spec2('HTML WHATWG')}}Sin cambios del {{SpecName('HTML5 W3C')}}.
{{SpecName('HTML5 W3C', "embedded-content-0.html#the-video-element", "HTMLAreaElement")}}{{Spec2('HTML5 W3C')}}Definicion incial.
- -

Compatibilidad con Navegador

- -

{{CompatibilityTable}}

- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CaracteristicasChromeEdgeFirefox (Gecko)Internet ExplorerOperaSafari
Apoyo basico{{CompatVersionUnknown}}{{CompatVersionUnknown}}{{CompatGeckoDesktop("2.0")}}9.010.50{{CompatVersionUnknown}}
mozParsedFrames mozDecodedFrames mozPresentedFrames mozPaintedFrames mozFrameDelay {{non-standard_inline}}{{CompatNo}}{{CompatNo}}{{CompatGeckoDesktop("5.0")}}{{CompatNo}}{{CompatNo}}{{CompatNo}}
mozHasAudio {{non-standard_inline}}{{CompatNo}}{{CompatNo}}{{ CompatGeckoDesktop("15.0")}}{{CompatNo}}{{CompatNo}}{{CompatNo}}
getVideoPlaybackQuality(){{experimental_inline}}{{CompatUnknown}}{{CompatVersionUnknown}}{{ CompatGeckoDesktop("25.0")}}[1]{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CaracterísticasAndroidEdgeFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Apoyo básico{{CompatVersionUnknown}}{{CompatVersionUnknown}}{{CompatGeckoMobile("2.0")}}9.0{{CompatVersionUnknown}}{{CompatVersionUnknown}}
mozParsedFrames mozDecodedFrames mozPresentedFrames mozPaintedFrames mozFrameDelay {{non-standard_inline}}{{CompatNo}}{{CompatNo}}{{CompatGeckoMobile("5.0")}}{{CompatNo}}{{CompatNo}}{{CompatNo}}
mozHasAudio {{non-standard_inline}}{{CompatNo}}{{CompatNo}}{{ CompatGeckoMobile("15.0")}}{{CompatNo}}{{CompatNo}}{{CompatNo}}
getVideoPlaybackQuality(){{experimental_inline}}{{CompatUnknown}}{{CompatVersionUnknown}}{{ CompatGeckoMobile("25.0")}}[1]{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}
-
- -

[1] Gecko implementa esto detras de la preferencia media.mediasource.enabled, predispuesto a false.

- -

Lea Tambien

- - diff --git a/files/es/web/api/event/createevent/index.html b/files/es/web/api/event/createevent/index.html deleted file mode 100644 index 7b273c6e33..0000000000 --- a/files/es/web/api/event/createevent/index.html +++ /dev/null @@ -1,35 +0,0 @@ ---- -title: Event.createEvent() -slug: Web/API/Event/createEvent -tags: - - API - - DOM - - Evento - - metodo -translation_of: Web/API/Document/createEvent -translation_of_original: Web/API/Event/createEvent ---- -

{{APIRef("DOM")}}

- -

Crea un nuevo evento, que debe ser inicializado llamando a su método init().

- -

Sintaxis

- -
document.createEvent(tipo);
- -
-
tipo
-
Una string indicando el tipo de evento a crear.
-
- -

Este método devuelve un nuevo objeto {{ domxref("Event") }} del DOM del tipo indicado, que debe ser inicializado antes de su uso.

- -

Ejemplo

- -
var nuevoEvento = document.createEvent("UIEvents");
- -

Especificación

- - diff --git a/files/es/web/api/fetch_api/basic_concepts/index.html b/files/es/web/api/fetch_api/basic_concepts/index.html new file mode 100644 index 0000000000..8c02021de6 --- /dev/null +++ b/files/es/web/api/fetch_api/basic_concepts/index.html @@ -0,0 +1,73 @@ +--- +title: Conceptos básicos de Fetch +slug: Web/API/Fetch_API/Conceptos_basicos +tags: + - API + - API Fetch + - Fetch + - conceptos + - guard + - request +translation_of: Web/API/Fetch_API/Basic_concepts +--- +

{{DefaultAPISidebar("Fetch API")}}{{draft}}

+ +
+

La API Fetch proporciona una interfaz para obtener recursos (incluso a traves de la red). Parecera familiar a quien sea que haya usado {{domxref("XMLHttpRequest")}}, pero proporciona un conjunto de características más potentes y flexibles. Este artículo explica algunos de los conceptos básicos de la API Fetch.

+
+ +
+

Este artículo será añadido en un futuro. Si encuenta un concepto de Fetch que siente necesita una mejor explicación, hagalo saber a alguien en el foro de discusión de MDN, o Mozilla IRC (#mdn room.)

+
+ +

En pocas palabras

+ +

En el corazón de Fetch estan las abstracciones de interfaz de cargas de HTTP {{domxref("Request")}}s, {{domxref("Response")}}s, {{domxref("Headers")}}, y {{domxref("Body")}}, junto a un método {{domxref("GlobalFetch.fetch","global fetch")}} para inicializar peticiones de recurso asíncronos. Porque los prinipales componentes de HTTP son abstraídos como objetos Javascript, es sencillo para otras APIs el hacer uso de dicha funcionalidad.

+ +

Service Workers es un ejemplo de una API que hace un fuerte uso de Fetch.

+ +

Fetch toma la naturaleza asíncrona de dichas peticiones un paso adelante. La API esta completamente basada en {{jsxref("Promise")}}.

+ +

Guard

+ +

Guard es una característica de objetos {{domxref("Headers")}}, con los valores posibles immutable, request, request-no-cors, response, o none, dependiendo de donde el encabezado es usado.

+ +

Cuando un nuevo objeto {{domxref("Headers")}} es creado usando el {{domxref("Headers.Headers","Headers()")}} {{glossary("constructor")}}, su guarda (guard) se establece a none (por defecto). Cuando un objeto {{domxref("Request")}} o {{domxref("Response")}} es creado, tiene un objeto  {{domxref("Headers")}} asociado cuyo guarda (guard) se establece como se resume a continuación:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
nuevo tipo de objetocreando el constructorconfiguraciones de guarda (guard) del objeto {{domxref("Headers")}} asociado
{{domxref("Request")}}{{domxref("Request.Request","Request()")}}request
{{domxref("Request.Request","Request()")}} con {{domxref("Request.mode","mode")}} de no-corsrequest-no-cors
{{domxref("Response")}}{{domxref("Response.Response","Response()")}}response
Métodos {{domxref("Response.error","error()")}} o {{domxref("Response.redirect","redirect()")}}immutable
+ +

Un guarda (guard) de encabezado afecta los métodos {{domxref("Headers.set","set()")}}, {{domxref("Headers.delete","delete()")}}, y {{domxref("Headers.append","append()")}} que cambían los contenidos del encabezado. Un TypeError es arrojado si se trata modificar un objeto {{domxref("Headers")}} cuyo guarda (guard) es immutable. Sin embargo, la operación funcionará si

+ + diff --git a/files/es/web/api/fetch_api/conceptos_basicos/index.html b/files/es/web/api/fetch_api/conceptos_basicos/index.html deleted file mode 100644 index 8c02021de6..0000000000 --- a/files/es/web/api/fetch_api/conceptos_basicos/index.html +++ /dev/null @@ -1,73 +0,0 @@ ---- -title: Conceptos básicos de Fetch -slug: Web/API/Fetch_API/Conceptos_basicos -tags: - - API - - API Fetch - - Fetch - - conceptos - - guard - - request -translation_of: Web/API/Fetch_API/Basic_concepts ---- -

{{DefaultAPISidebar("Fetch API")}}{{draft}}

- -
-

La API Fetch proporciona una interfaz para obtener recursos (incluso a traves de la red). Parecera familiar a quien sea que haya usado {{domxref("XMLHttpRequest")}}, pero proporciona un conjunto de características más potentes y flexibles. Este artículo explica algunos de los conceptos básicos de la API Fetch.

-
- -
-

Este artículo será añadido en un futuro. Si encuenta un concepto de Fetch que siente necesita una mejor explicación, hagalo saber a alguien en el foro de discusión de MDN, o Mozilla IRC (#mdn room.)

-
- -

En pocas palabras

- -

En el corazón de Fetch estan las abstracciones de interfaz de cargas de HTTP {{domxref("Request")}}s, {{domxref("Response")}}s, {{domxref("Headers")}}, y {{domxref("Body")}}, junto a un método {{domxref("GlobalFetch.fetch","global fetch")}} para inicializar peticiones de recurso asíncronos. Porque los prinipales componentes de HTTP son abstraídos como objetos Javascript, es sencillo para otras APIs el hacer uso de dicha funcionalidad.

- -

Service Workers es un ejemplo de una API que hace un fuerte uso de Fetch.

- -

Fetch toma la naturaleza asíncrona de dichas peticiones un paso adelante. La API esta completamente basada en {{jsxref("Promise")}}.

- -

Guard

- -

Guard es una característica de objetos {{domxref("Headers")}}, con los valores posibles immutable, request, request-no-cors, response, o none, dependiendo de donde el encabezado es usado.

- -

Cuando un nuevo objeto {{domxref("Headers")}} es creado usando el {{domxref("Headers.Headers","Headers()")}} {{glossary("constructor")}}, su guarda (guard) se establece a none (por defecto). Cuando un objeto {{domxref("Request")}} o {{domxref("Response")}} es creado, tiene un objeto  {{domxref("Headers")}} asociado cuyo guarda (guard) se establece como se resume a continuación:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
nuevo tipo de objetocreando el constructorconfiguraciones de guarda (guard) del objeto {{domxref("Headers")}} asociado
{{domxref("Request")}}{{domxref("Request.Request","Request()")}}request
{{domxref("Request.Request","Request()")}} con {{domxref("Request.mode","mode")}} de no-corsrequest-no-cors
{{domxref("Response")}}{{domxref("Response.Response","Response()")}}response
Métodos {{domxref("Response.error","error()")}} o {{domxref("Response.redirect","redirect()")}}immutable
- -

Un guarda (guard) de encabezado afecta los métodos {{domxref("Headers.set","set()")}}, {{domxref("Headers.delete","delete()")}}, y {{domxref("Headers.append","append()")}} que cambían los contenidos del encabezado. Un TypeError es arrojado si se trata modificar un objeto {{domxref("Headers")}} cuyo guarda (guard) es immutable. Sin embargo, la operación funcionará si

- - diff --git a/files/es/web/api/fetch_api/using_fetch/index.html b/files/es/web/api/fetch_api/using_fetch/index.html new file mode 100644 index 0000000000..51617ad047 --- /dev/null +++ b/files/es/web/api/fetch_api/using_fetch/index.html @@ -0,0 +1,377 @@ +--- +title: Uso de Fetch +slug: Web/API/Fetch_API/Utilizando_Fetch +tags: + - API + - BODY + - Experimental + - Fetch + - Guía + - HTTP + - Petición + - Promesa + - Promise + - Respuesta +translation_of: Web/API/Fetch_API/Using_Fetch +--- +

{{DefaultAPISidebar("Fetch API")}}{{ SeeCompatTable }}

+ +
+

La API Fetch proporciona una interfaz JavaScript para acceder y manipular partes del canal HTTP, tales como peticiones y respuestas. También provee un método global {{domxref("GlobalFetch.fetch","fetch()")}} que proporciona una forma fácil y lógica de obtener recursos de forma asíncrona por la red.

+
+ +

Este tipo de funcionalidad se conseguía previamente haciendo uso de {{domxref("XMLHttpRequest")}}. Fetch proporciona una alternativa mejor que puede ser empleada fácilmente por otras tecnologías como {{domxref("ServiceWorker_API", "Service Workers")}}. Fetch también aporta un único lugar lógico en el que definir otros conceptos relacionados con HTTP como CORS y extensiones para HTTP.

+ +

La especificación fetch difiere de JQuery.ajax() en dos formas principales:

+ + + +

Una petición básica de fetch es realmente simple de realizar. Eche un vistazo al siguente código:

+ +
fetch('http://example.com/movies.json')
+  .then(response => response.json())
+  .then(data => console.log(data));
+ +

Aquí estamos recuperando un archivo JSON a través de red e imprimiendo en la consola. El uso de fetch() más simple toma un argumento (la ruta del recurso que quieres obtener) y devuelve un objeto Promise conteniendo la respuesta, un objeto {{domxref("Response")}}.

+ +

Esto es, por supuesto, una respuesta HTTP no el archivo JSON. Para extraer el contenido en el cuerpo del JSON desde la respuesta, usamos el método {{domxref("Body.json","json()")}} (definido en el mixin de {{domxref("Body")}}, el cual está implementado por los objetos {{domxref("Request")}} y {{domxref("Response")}}).

+ +
+

Nota: El mixin de Body tambien tiene metodos parecidos para extraer otros tipos de contenido del cuerpo. Vease {{anch("Body")}} para más información.

+
+ +

Las peticiones de Fetch son controladas por la directiva de connect-src de Content Security Policy en vez de la directiva de los recursos que se han devuelto.

+ +

Suministrando opciones de petición

+ +

El método fetch() puede aceptar opcionalmente un segundo parámetro, un objeto init que permite controlar un numero de diferentes ajustes:

+ +

Vea {{domxref("GlobalFetch.fetch","fetch()")}}, para ver todas las opciones disponibles y más detalles.

+ +
// Ejemplo implementando el metodo POST:
+async function postData(url = '', data = {}) {
+  // Opciones por defecto estan marcadas con un *
+  const response = await fetch(url, {
+    method: 'POST', // *GET, POST, PUT, DELETE, etc.
+    mode: 'cors', // no-cors, *cors, same-origin
+    cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
+    credentials: 'same-origin', // include, *same-origin, omit
+    headers: {
+      'Content-Type': 'application/json'
+      // 'Content-Type': 'application/x-www-form-urlencoded',
+    },
+    redirect: 'follow', // manual, *follow, error
+    referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
+    body: JSON.stringify(data) // body data type must match "Content-Type" header
+  });
+  return response.json(); // parses JSON response into native JavaScript objects
+}
+
+postData('https://example.com/answer', { answer: 42 })
+  .then(data => {
+    console.log(data); // JSON data parsed by `data.json()` call
+  });
+ +

Tenga en cuenta que  mode: "no-cors" solo permite un conjunto limitado de encabezados en la solicitud:

+ + + +

Comprobando que la petición es satisfactoria

+ +

Una petición promise {{domxref("GlobalFetch.fetch","fetch()")}} será rechazada con {{jsxref("TypeError")}} cuando se encuentre un error de red, aunque esto normalmente significa problemas de permisos o similares — por ejemplo, un 404 no constituye un error de red. Una forma precisa de comprobar que la petición fetch() es satisfactoria pasa por comprobar si la promesa ha sido resuelta, además de comprobar que la propiedad {{domxref("Response.ok")}} tiene el valor true que indica que el estado de la petición HTTP es OK (código 200-299). El código sería algo así:

+ +
fetch('flores.jpg').then(function(response) {
+  if(response.ok) {
+    response.blob().then(function(miBlob) {
+      var objectURL = URL.createObjectURL(miBlob);
+      miImagen.src = objectURL;
+    });
+  } else {
+    console.log('Respuesta de red OK pero respuesta HTTP no OK');
+  }
+})
+.catch(function(error) {
+  console.log('Hubo un problema con la petición Fetch:' + error.message);
+});
+ +

Proporcionando tu propio objeto Request

+ +

En lugar de pasar la ruta al recurso que deseas solicitar a la llamada del método fetch(), puedes crear un objeto de petición utilizando el constructor {{domxref("Request.Request","Request()")}}, y pasarlo como un argumento del método fetch():

+ +
var myHeaders = new Headers();
+
+var myInit = { method: 'GET',
+               headers: myHeaders,
+               mode: 'cors',
+               cache: 'default' };
+
+var myRequest = new Request('flowers.jpg', myInit);
+
+fetch(myRequest)
+.then(function(response) {
+  return response.blob();
+})
+.then(function(myBlob) {
+  var objectURL = URL.createObjectURL(myBlob);
+  myImage.src = objectURL;
+});
+ +

Request() acepta exactamente los mismos parámetros que el método fetch(). Puedes incluso pasar un objeto de petición existente para crear una copia del mismo:

+ +
var anotherRequest = new Request(myRequest, myInit);
+ +

Esto es muy útil ya que el cuerpo de las solicitudes y respuestas son de un sólo uso. Haciendo una copia como esta te permite utilizar la petición/respuesta de nuevo, y al mismo tiempo, si lo deseas, modificar las opciones de init. La copia debe estar hecha antes de la lectura del <body>, y leyendo el <body> en la copia, se marcará como leido en la petición original.

+ +
+

Nota: Existe también un método {{domxref("Request.clone","clone()")}} que crea una copia. Este tiene una semántica ligeramente distinta al otro método de copia — el primero fallará si el cuerpo de la petición anterior ya ha sido leído (lo mismo para copiar una respuesta), mientras que clone() no.

+
+ +

Enviar una petición con credenciales incluido

+ +

Para producir que los navegadores envien una petición con las credenciales incluidas, incluso para una llamada de origen cruzado, añadimos credentials: 'include' en el el objeto init que se pasa al método fetch().

+ +
fetch('https://example.com', {
+  credentials: 'include'
+})
+ +

Si solo quieres enviar la credenciales si la URL de la petición está en el mismo origen desde donde se llamada el script, añade credentials: 'same-origin'.

+ +
// El script fué llamado desde el origen 'https://example.com'
+
+fetch('https://example.com', {
+  credentials: 'same-origin'
+})
+ +

Sin embargo para asegurarte que el navegador no incluye las credenciales en la petición, usa credentials: 'omit'.

+ +
fetch('https://example.com', {
+  credentials: 'omit'
+})
+ +

Enviando datos JSON

+ +

Usa {{domxref("GlobalFetch.fetch","fetch()")}} para enviar una petición POST con datos codificados en JSON .

+ +
var url = 'https://example.com/profile';
+var data = {username: 'example'};
+
+fetch(url, {
+  method: 'POST', // or 'PUT'
+  body: JSON.stringify(data), // data can be `string` or {object}!
+  headers:{
+    'Content-Type': 'application/json'
+  }
+}).then(res => res.json())
+.catch(error => console.error('Error:', error))
+.then(response => console.log('Success:', response));
+ +

Enviando un archivo

+ +

Los archivos pueden ser subido mediante el HTML de un elemento input <input type="file" />, {{domxref("FormData.FormData","FormData()")}} y {{domxref("GlobalFetch.fetch","fetch()")}}.

+ +
var formData = new FormData();
+var fileField = document.querySelector("input[type='file']");
+
+formData.append('username', 'abc123');
+formData.append('avatar', fileField.files[0]);
+
+fetch('https://example.com/profile/avatar', {
+  method: 'PUT',
+  body: formData
+})
+.then(response => response.json())
+.catch(error => console.error('Error:', error))
+.then(response => console.log('Success:', response));
+ +

Cabeceras

+ +

La interfaz {{domxref("Headers")}} te permite crear tus propios objetos de headers mediante el constructor {{domxref("Headers.Headers","Headers()")}}. Un objeto headers es un simple multi-mapa de nombres y valores:

+ +
var content = "Hello World";
+var myHeaders = new Headers();
+myHeaders.append("Content-Type", "text/plain");
+myHeaders.append("Content-Length", content.length.toString());
+myHeaders.append("X-Custom-Header", "ProcessThisImmediately");
+ +

Lo mismo se puede lograr pasando un "array de arrays" o un objeto literal al constructor:

+ +
myHeaders = new Headers({
+  "Content-Type": "text/plain",
+  "Content-Length": content.length.toString(),
+  "X-Custom-Header": "ProcessThisImmediately",
+});
+ +

Los contenidos pueden ser consultados o recuperados:

+ +
console.log(myHeaders.has("Content-Type")); // true
+console.log(myHeaders.has("Set-Cookie")); // false
+myHeaders.set("Content-Type", "text/html");
+myHeaders.append("X-Custom-Header", "AnotherValue");
+
+console.log(myHeaders.get("Content-Length")); // 11
+console.log(myHeaders.getAll("X-Custom-Header")); // ["ProcessThisImmediately", "AnotherValue"]
+
+myHeaders.delete("X-Custom-Header");
+console.log(myHeaders.getAll("X-Custom-Header")); // [ ]
+ +

Algunas de estas operaciones solo serán utiles en  {{domxref("ServiceWorker_API","ServiceWorkers")}}, pero estas disponen de una mejor API  para manipular headers.

+ +

Todos los métodosde de headers lanzan un TypeError si un nombre de cabecera no es un nombre de cabecera HTTP válido. Las operaciones de mutación lanzarán un TypeError si hay un guarda inmutable (ver más abajo). Si no, fallan silenciosamente. Por ejemplo:

+ +
var myResponse = Response.error();
+try {
+  myResponse.headers.set("Origin", "http://mybank.com");
+} catch(e) {
+  console.log("Cannot pretend to be a bank!");
+}
+ +

Un buen caso de uso para headers es comprobar cuando el tipo de contenido es correcto antes de que se procese:

+ +
fetch(myRequest).then(function(response) {
+  var contentType = response.headers.get("content-type");
+  if(contentType && contentType.indexOf("application/json") !== -1) {
+    return response.json().then(function(json) {
+      // process your JSON further
+    });
+  } else {
+    console.log("Oops, we haven't got JSON!");
+  }
+});
+ +

Guarda (Guard)

+ +

Desde que las cabeceras pueden ser enviadas  en peticiones y recibidas en respuestas, y tienen limitaciones sobre que información puede y debería ser mutable, los objeto headers tienen una propierdad de guarda. Este no está expuesto a la Web, pero puede afectar a que operaciones de mutación son permitidas sobre el objeto headers.

+ +

Los valores posibles de guarda (guard) son:

+ + + +
+

Nota:  No se debería añadir o establecer una petición a un objeto headers guardado con la cabecera Content-Length. De igual manera, insertar Set-Cookie en la respuesta de la cabecera no esta permitido: ServiceWorkers no estan autorizados a establecer cookies a través de respuestas sintéticas.

+
+ +

Objetos Response

+ +

Cómo has visto anteriormente, las instancias de {{domxref("Response")}} son devueltas cuando fetch() es resuelto.

+ +

Las propiedades de response que usarás son:

+ + + +

Estos pueden también creados programáticamente a través de JavaScript, pero esto solamente es realmete útil en {{domxref("ServiceWorker_API", "ServiceWorkers")}},  cuando pones un objeto response personalizado a una respuesta recibida usando un método {{domxref("FetchEvent.respondWith","respondWith()")}}:

+ +
var myBody = new Blob();
+
+addEventListener('fetch', function(event) {
+  event.respondWith(
+    new Response(myBody, {
+      headers: { "Content-Type" : "text/plain" }
+    })
+  );
+});
+ +

El constructor {{domxref("Response.Response","Response()")}} toma dos argurmentos opcionales, un cuerpo para la respuesta y un objeto init (similar al que acepta {{domxref("Request.Request","Request()")}}).

+ +
+

Nota: El método estático {{domxref("Response.error","error()")}} simplemente devuelve un error en la respuesta. De igual manera que {{domxref("Response.redirect","redirect()")}} devuelve una respuesta que resulta en un redirección a una URL especificada. Estos son solo relevantes tambien a ServiceWorkers.

+
+ +

Body

+ +

Tanto las peticiones como las respuestas pueden contener datos body. Body es una instancia de cualquiera de los siguientes tipos:

+ + + +

El mixin de {{domxref("Body")}} define los siguientes metodos para extraer un body (implementado por {{domxref("Request")}} and {{domxref("Response")}}). Todas ellas devuelven una promesa que es eventualmente resuelta con el contenido actual.

+ + + +

Este hace uso de los datos no texttuales mucho mas facil que si fuera con XHR.

+ +

Las peticiones body pueden ser establecidas pasando el parametro body:

+ +
var form = new FormData(document.getElementById('login-form'));
+fetch("/login", {
+  method: "POST",
+  body: form
+});
+ +

Tanto peticiones y respuestas (y por extensión la function fetch()), intentaran inteligentemente determinar el tipo de contenido. Una petición tambien establecerá automáticamente la propiedad Context-Type de la cabecera si no es ha establecido una.

+ +

Detectar característica

+ +

Puedes comprobar si el navegador soporta  la API de Fetch comprobando la existencia de {{domxref("Headers")}}, {{domxref("Request")}}, {{domxref("Response")}} o {{domxref("GlobalFetch.fetch","fetch()")}} sobre el ámbito de {{domxref("Window")}} o {{domxref("Worker")}}. Por ejemplo:

+ +
if (self.fetch) {
+    // run my fetch request here
+} else {
+    // do something with XMLHttpRequest?
+}
+ +

Polyfill

+ +

Para utilizar fetch() en un explorador no soportado, hay disponible un Fetch Polyfill que recrea la funcionalidad para navegadores no soportados.

+ +

Especificaciones

+ + + + + + + + + + + + + + +
EspecificaciónEstadoComentario
{{SpecName('Fetch')}}{{Spec2('Fetch')}}Definición inicial
+ +

Compatibilidad en navegadores

+ +

{{Compat("api.WindowOrWorkerGlobalScope.fetch")}}

+ +

Vea también

+ + diff --git a/files/es/web/api/fetch_api/utilizando_fetch/index.html b/files/es/web/api/fetch_api/utilizando_fetch/index.html deleted file mode 100644 index 51617ad047..0000000000 --- a/files/es/web/api/fetch_api/utilizando_fetch/index.html +++ /dev/null @@ -1,377 +0,0 @@ ---- -title: Uso de Fetch -slug: Web/API/Fetch_API/Utilizando_Fetch -tags: - - API - - BODY - - Experimental - - Fetch - - Guía - - HTTP - - Petición - - Promesa - - Promise - - Respuesta -translation_of: Web/API/Fetch_API/Using_Fetch ---- -

{{DefaultAPISidebar("Fetch API")}}{{ SeeCompatTable }}

- -
-

La API Fetch proporciona una interfaz JavaScript para acceder y manipular partes del canal HTTP, tales como peticiones y respuestas. También provee un método global {{domxref("GlobalFetch.fetch","fetch()")}} que proporciona una forma fácil y lógica de obtener recursos de forma asíncrona por la red.

-
- -

Este tipo de funcionalidad se conseguía previamente haciendo uso de {{domxref("XMLHttpRequest")}}. Fetch proporciona una alternativa mejor que puede ser empleada fácilmente por otras tecnologías como {{domxref("ServiceWorker_API", "Service Workers")}}. Fetch también aporta un único lugar lógico en el que definir otros conceptos relacionados con HTTP como CORS y extensiones para HTTP.

- -

La especificación fetch difiere de JQuery.ajax() en dos formas principales:

- - - -

Una petición básica de fetch es realmente simple de realizar. Eche un vistazo al siguente código:

- -
fetch('http://example.com/movies.json')
-  .then(response => response.json())
-  .then(data => console.log(data));
- -

Aquí estamos recuperando un archivo JSON a través de red e imprimiendo en la consola. El uso de fetch() más simple toma un argumento (la ruta del recurso que quieres obtener) y devuelve un objeto Promise conteniendo la respuesta, un objeto {{domxref("Response")}}.

- -

Esto es, por supuesto, una respuesta HTTP no el archivo JSON. Para extraer el contenido en el cuerpo del JSON desde la respuesta, usamos el método {{domxref("Body.json","json()")}} (definido en el mixin de {{domxref("Body")}}, el cual está implementado por los objetos {{domxref("Request")}} y {{domxref("Response")}}).

- -
-

Nota: El mixin de Body tambien tiene metodos parecidos para extraer otros tipos de contenido del cuerpo. Vease {{anch("Body")}} para más información.

-
- -

Las peticiones de Fetch son controladas por la directiva de connect-src de Content Security Policy en vez de la directiva de los recursos que se han devuelto.

- -

Suministrando opciones de petición

- -

El método fetch() puede aceptar opcionalmente un segundo parámetro, un objeto init que permite controlar un numero de diferentes ajustes:

- -

Vea {{domxref("GlobalFetch.fetch","fetch()")}}, para ver todas las opciones disponibles y más detalles.

- -
// Ejemplo implementando el metodo POST:
-async function postData(url = '', data = {}) {
-  // Opciones por defecto estan marcadas con un *
-  const response = await fetch(url, {
-    method: 'POST', // *GET, POST, PUT, DELETE, etc.
-    mode: 'cors', // no-cors, *cors, same-origin
-    cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
-    credentials: 'same-origin', // include, *same-origin, omit
-    headers: {
-      'Content-Type': 'application/json'
-      // 'Content-Type': 'application/x-www-form-urlencoded',
-    },
-    redirect: 'follow', // manual, *follow, error
-    referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
-    body: JSON.stringify(data) // body data type must match "Content-Type" header
-  });
-  return response.json(); // parses JSON response into native JavaScript objects
-}
-
-postData('https://example.com/answer', { answer: 42 })
-  .then(data => {
-    console.log(data); // JSON data parsed by `data.json()` call
-  });
- -

Tenga en cuenta que  mode: "no-cors" solo permite un conjunto limitado de encabezados en la solicitud:

- - - -

Comprobando que la petición es satisfactoria

- -

Una petición promise {{domxref("GlobalFetch.fetch","fetch()")}} será rechazada con {{jsxref("TypeError")}} cuando se encuentre un error de red, aunque esto normalmente significa problemas de permisos o similares — por ejemplo, un 404 no constituye un error de red. Una forma precisa de comprobar que la petición fetch() es satisfactoria pasa por comprobar si la promesa ha sido resuelta, además de comprobar que la propiedad {{domxref("Response.ok")}} tiene el valor true que indica que el estado de la petición HTTP es OK (código 200-299). El código sería algo así:

- -
fetch('flores.jpg').then(function(response) {
-  if(response.ok) {
-    response.blob().then(function(miBlob) {
-      var objectURL = URL.createObjectURL(miBlob);
-      miImagen.src = objectURL;
-    });
-  } else {
-    console.log('Respuesta de red OK pero respuesta HTTP no OK');
-  }
-})
-.catch(function(error) {
-  console.log('Hubo un problema con la petición Fetch:' + error.message);
-});
- -

Proporcionando tu propio objeto Request

- -

En lugar de pasar la ruta al recurso que deseas solicitar a la llamada del método fetch(), puedes crear un objeto de petición utilizando el constructor {{domxref("Request.Request","Request()")}}, y pasarlo como un argumento del método fetch():

- -
var myHeaders = new Headers();
-
-var myInit = { method: 'GET',
-               headers: myHeaders,
-               mode: 'cors',
-               cache: 'default' };
-
-var myRequest = new Request('flowers.jpg', myInit);
-
-fetch(myRequest)
-.then(function(response) {
-  return response.blob();
-})
-.then(function(myBlob) {
-  var objectURL = URL.createObjectURL(myBlob);
-  myImage.src = objectURL;
-});
- -

Request() acepta exactamente los mismos parámetros que el método fetch(). Puedes incluso pasar un objeto de petición existente para crear una copia del mismo:

- -
var anotherRequest = new Request(myRequest, myInit);
- -

Esto es muy útil ya que el cuerpo de las solicitudes y respuestas son de un sólo uso. Haciendo una copia como esta te permite utilizar la petición/respuesta de nuevo, y al mismo tiempo, si lo deseas, modificar las opciones de init. La copia debe estar hecha antes de la lectura del <body>, y leyendo el <body> en la copia, se marcará como leido en la petición original.

- -
-

Nota: Existe también un método {{domxref("Request.clone","clone()")}} que crea una copia. Este tiene una semántica ligeramente distinta al otro método de copia — el primero fallará si el cuerpo de la petición anterior ya ha sido leído (lo mismo para copiar una respuesta), mientras que clone() no.

-
- -

Enviar una petición con credenciales incluido

- -

Para producir que los navegadores envien una petición con las credenciales incluidas, incluso para una llamada de origen cruzado, añadimos credentials: 'include' en el el objeto init que se pasa al método fetch().

- -
fetch('https://example.com', {
-  credentials: 'include'
-})
- -

Si solo quieres enviar la credenciales si la URL de la petición está en el mismo origen desde donde se llamada el script, añade credentials: 'same-origin'.

- -
// El script fué llamado desde el origen 'https://example.com'
-
-fetch('https://example.com', {
-  credentials: 'same-origin'
-})
- -

Sin embargo para asegurarte que el navegador no incluye las credenciales en la petición, usa credentials: 'omit'.

- -
fetch('https://example.com', {
-  credentials: 'omit'
-})
- -

Enviando datos JSON

- -

Usa {{domxref("GlobalFetch.fetch","fetch()")}} para enviar una petición POST con datos codificados en JSON .

- -
var url = 'https://example.com/profile';
-var data = {username: 'example'};
-
-fetch(url, {
-  method: 'POST', // or 'PUT'
-  body: JSON.stringify(data), // data can be `string` or {object}!
-  headers:{
-    'Content-Type': 'application/json'
-  }
-}).then(res => res.json())
-.catch(error => console.error('Error:', error))
-.then(response => console.log('Success:', response));
- -

Enviando un archivo

- -

Los archivos pueden ser subido mediante el HTML de un elemento input <input type="file" />, {{domxref("FormData.FormData","FormData()")}} y {{domxref("GlobalFetch.fetch","fetch()")}}.

- -
var formData = new FormData();
-var fileField = document.querySelector("input[type='file']");
-
-formData.append('username', 'abc123');
-formData.append('avatar', fileField.files[0]);
-
-fetch('https://example.com/profile/avatar', {
-  method: 'PUT',
-  body: formData
-})
-.then(response => response.json())
-.catch(error => console.error('Error:', error))
-.then(response => console.log('Success:', response));
- -

Cabeceras

- -

La interfaz {{domxref("Headers")}} te permite crear tus propios objetos de headers mediante el constructor {{domxref("Headers.Headers","Headers()")}}. Un objeto headers es un simple multi-mapa de nombres y valores:

- -
var content = "Hello World";
-var myHeaders = new Headers();
-myHeaders.append("Content-Type", "text/plain");
-myHeaders.append("Content-Length", content.length.toString());
-myHeaders.append("X-Custom-Header", "ProcessThisImmediately");
- -

Lo mismo se puede lograr pasando un "array de arrays" o un objeto literal al constructor:

- -
myHeaders = new Headers({
-  "Content-Type": "text/plain",
-  "Content-Length": content.length.toString(),
-  "X-Custom-Header": "ProcessThisImmediately",
-});
- -

Los contenidos pueden ser consultados o recuperados:

- -
console.log(myHeaders.has("Content-Type")); // true
-console.log(myHeaders.has("Set-Cookie")); // false
-myHeaders.set("Content-Type", "text/html");
-myHeaders.append("X-Custom-Header", "AnotherValue");
-
-console.log(myHeaders.get("Content-Length")); // 11
-console.log(myHeaders.getAll("X-Custom-Header")); // ["ProcessThisImmediately", "AnotherValue"]
-
-myHeaders.delete("X-Custom-Header");
-console.log(myHeaders.getAll("X-Custom-Header")); // [ ]
- -

Algunas de estas operaciones solo serán utiles en  {{domxref("ServiceWorker_API","ServiceWorkers")}}, pero estas disponen de una mejor API  para manipular headers.

- -

Todos los métodosde de headers lanzan un TypeError si un nombre de cabecera no es un nombre de cabecera HTTP válido. Las operaciones de mutación lanzarán un TypeError si hay un guarda inmutable (ver más abajo). Si no, fallan silenciosamente. Por ejemplo:

- -
var myResponse = Response.error();
-try {
-  myResponse.headers.set("Origin", "http://mybank.com");
-} catch(e) {
-  console.log("Cannot pretend to be a bank!");
-}
- -

Un buen caso de uso para headers es comprobar cuando el tipo de contenido es correcto antes de que se procese:

- -
fetch(myRequest).then(function(response) {
-  var contentType = response.headers.get("content-type");
-  if(contentType && contentType.indexOf("application/json") !== -1) {
-    return response.json().then(function(json) {
-      // process your JSON further
-    });
-  } else {
-    console.log("Oops, we haven't got JSON!");
-  }
-});
- -

Guarda (Guard)

- -

Desde que las cabeceras pueden ser enviadas  en peticiones y recibidas en respuestas, y tienen limitaciones sobre que información puede y debería ser mutable, los objeto headers tienen una propierdad de guarda. Este no está expuesto a la Web, pero puede afectar a que operaciones de mutación son permitidas sobre el objeto headers.

- -

Los valores posibles de guarda (guard) son:

- - - -
-

Nota:  No se debería añadir o establecer una petición a un objeto headers guardado con la cabecera Content-Length. De igual manera, insertar Set-Cookie en la respuesta de la cabecera no esta permitido: ServiceWorkers no estan autorizados a establecer cookies a través de respuestas sintéticas.

-
- -

Objetos Response

- -

Cómo has visto anteriormente, las instancias de {{domxref("Response")}} son devueltas cuando fetch() es resuelto.

- -

Las propiedades de response que usarás son:

- - - -

Estos pueden también creados programáticamente a través de JavaScript, pero esto solamente es realmete útil en {{domxref("ServiceWorker_API", "ServiceWorkers")}},  cuando pones un objeto response personalizado a una respuesta recibida usando un método {{domxref("FetchEvent.respondWith","respondWith()")}}:

- -
var myBody = new Blob();
-
-addEventListener('fetch', function(event) {
-  event.respondWith(
-    new Response(myBody, {
-      headers: { "Content-Type" : "text/plain" }
-    })
-  );
-});
- -

El constructor {{domxref("Response.Response","Response()")}} toma dos argurmentos opcionales, un cuerpo para la respuesta y un objeto init (similar al que acepta {{domxref("Request.Request","Request()")}}).

- -
-

Nota: El método estático {{domxref("Response.error","error()")}} simplemente devuelve un error en la respuesta. De igual manera que {{domxref("Response.redirect","redirect()")}} devuelve una respuesta que resulta en un redirección a una URL especificada. Estos son solo relevantes tambien a ServiceWorkers.

-
- -

Body

- -

Tanto las peticiones como las respuestas pueden contener datos body. Body es una instancia de cualquiera de los siguientes tipos:

- - - -

El mixin de {{domxref("Body")}} define los siguientes metodos para extraer un body (implementado por {{domxref("Request")}} and {{domxref("Response")}}). Todas ellas devuelven una promesa que es eventualmente resuelta con el contenido actual.

- - - -

Este hace uso de los datos no texttuales mucho mas facil que si fuera con XHR.

- -

Las peticiones body pueden ser establecidas pasando el parametro body:

- -
var form = new FormData(document.getElementById('login-form'));
-fetch("/login", {
-  method: "POST",
-  body: form
-});
- -

Tanto peticiones y respuestas (y por extensión la function fetch()), intentaran inteligentemente determinar el tipo de contenido. Una petición tambien establecerá automáticamente la propiedad Context-Type de la cabecera si no es ha establecido una.

- -

Detectar característica

- -

Puedes comprobar si el navegador soporta  la API de Fetch comprobando la existencia de {{domxref("Headers")}}, {{domxref("Request")}}, {{domxref("Response")}} o {{domxref("GlobalFetch.fetch","fetch()")}} sobre el ámbito de {{domxref("Window")}} o {{domxref("Worker")}}. Por ejemplo:

- -
if (self.fetch) {
-    // run my fetch request here
-} else {
-    // do something with XMLHttpRequest?
-}
- -

Polyfill

- -

Para utilizar fetch() en un explorador no soportado, hay disponible un Fetch Polyfill que recrea la funcionalidad para navegadores no soportados.

- -

Especificaciones

- - - - - - - - - - - - - - -
EspecificaciónEstadoComentario
{{SpecName('Fetch')}}{{Spec2('Fetch')}}Definición inicial
- -

Compatibilidad en navegadores

- -

{{Compat("api.WindowOrWorkerGlobalScope.fetch")}}

- -

Vea también

- - diff --git a/files/es/web/api/formdata/index.html b/files/es/web/api/formdata/index.html new file mode 100644 index 0000000000..2ca830daf7 --- /dev/null +++ b/files/es/web/api/formdata/index.html @@ -0,0 +1,84 @@ +--- +title: FormData +slug: Web/API/XMLHttpRequest/FormData +tags: + - API + - FormData + - Interfaz + - Referencia + - XMLHttpRequest +translation_of: Web/API/FormData +--- +

{{APIRef("XMLHttpRequest")}}

+ +

La interfaz FormData proporciona una manera sencilla de construir un conjunto de parejas clave/valor que representan los campos de un formulario y sus valores, que pueden ser enviados fácilmente con el método {{domxref("XMLHttpRequest.send()")}}. Utiliza el mismo formato que usaría un formulario si el tipo de codificación fuera "multipart/form-data".

+ +

También puede pasarse directamente al constructor de {{domxref("URLSearchParams")}} si se quieren generar parámetros de consulta de la misma forma en que lo haría un {{HTMLElement("form")}} si usara un envío GET simple.

+ +

Un objeto que implementa FormData puede usarse directamente en una estructura {{jsxref("Statements/for...of", "for...of")}}, en lugar de {{domxref('FormData.entries()', 'entries()')}}: for (var p of myFormData) es equivalente a for (var p of myFormData.entries()).

+ +
+

Nota: Esta característica está disponible en Web Workers.

+
+ +

Constructor

+ +
+
{{domxref("FormData.FormData","FormData()")}}
+
Crea un nuevo objeto FormData.
+
+ +

Métodos

+ +
+
{{domxref("FormData.append()")}}
+
Agrega un nuevo valor a una clave existente dentro de un objeto FormData, o añade la clave si aún no existe.
+
{{domxref("FormData.delete()")}}
+
Elimina una pareja clave/valor de un objeto FormData.
+
{{domxref("FormData.entries()")}}
+
Devuelve un {{jsxref("Iteration_protocols","iterator")}} que permite recorrer todas las parejas clave/valor contenidas en este objeto.
+
{{domxref("FormData.get()")}}
+
Devuelve el primer valor asociado con una clave dada en un objeto FormData.
+
{{domxref("FormData.getAll()")}}
+
Devuelve un array con todos los valores asociados con una clave dada en un objeto FormData.
+
{{domxref("FormData.has()")}}
+
Devuelve un booleano que indica si un objeto FormData contiene una clave determinada.
+
{{domxref("FormData.keys()")}}
+
Devuelve un {{jsxref("Iteration_protocols", "iterator")}} que permite recorrer todas las claves de las parejas clave/valor contenidas en este objeto.
+
{{domxref("FormData.set()")}}
+
Establece un nuevo valor para una clave existente dentro de un objeto FormData, o agrega la clave/valor si aún no existe.
+
{{domxref("FormData.values()")}}
+
Devuelve un {{jsxref("Iteration_protocols", "iterator")}} que permite recorrer todos los valores contenidos en este objeto.
+
+ +

Especificaciones

+ + + + + + + + + + + + + + +
EspecificaciónEstadoComentario
{{SpecName('XMLHttpRequest','#interface-formdata','FormData')}}{{Spec2('XMLHttpRequest')}}FormData definido en XHR spec
+ +

Compatibilidad con navegadores

+ + + +

{{Compat("api.FormData")}}

+ +

Ver también

+ + diff --git a/files/es/web/api/formdata/using_formdata_objects/index.html b/files/es/web/api/formdata/using_formdata_objects/index.html new file mode 100644 index 0000000000..13f4c9635a --- /dev/null +++ b/files/es/web/api/formdata/using_formdata_objects/index.html @@ -0,0 +1,137 @@ +--- +title: Usando Objetos FormData +slug: Web/Guide/Usando_Objetos_FormData +translation_of: Web/API/FormData/Using_FormData_Objects +translation_of_original: Web/Guide/Using_FormData_Objects +--- +

Los objetos FormData le permiten compilar un conjunto de pares clave/valor para enviar mediante XMLHttpRequest. Están destinados principalmente para el envío de los datos del formulario, pero se pueden utilizar de forma independiente con el fin de transmitir los datos tecleados. Los datos transmitidos estarán en el mismo formato que usa el método submit() del formulario para enviar los datos si el tipo de codificación del formulario se establece en "multipart/form-data".

+ +

Creación de un objeto FormData desde cero

+ +

Usted mismo puede construir un objeto FormData instanciándolo y después añadiendo campos a la instancia usando su método  append() , tal y como se muestra:

+ +
var formData = new FormData();
+
+formData.append("username", "Groucho");
+formData.append("accountnum", 123456); // number 123456 is immediately converted to string "123456"
+
+// HTML file input user's choice...
+formData.append("userfile", fileInputElement.files[0]);
+
+// JavaScript file-like object...
+var content = '<a id="a"><b id="b">hey!</b></a>'; // the body of the new file...
+var blob = new Blob([content], { type: "text/xml"});
+
+formData.append("webmasterfile", blob);
+
+var request = new XMLHttpRequest();
+request.open("POST", "http://foo.com/submitform.php");
+request.send(formData);
+
+ +
Nota: Los campos "userfile" y "webmasterfile" contienen ambos un archivo. El número asignado al campo "accountnum" es inmediatamente convertido a string por el método FormData.append() (el valor del campo puede ser un {{ domxref("Blob") }}, {{ domxref("File") }}, o una cadena de texto; si el valor no es ni un Blob, ni un File, será convertido a un string).
+ +

Este ejemplo construye una instancia de FormData que almacenará los valores de los campos "username", "accountnum", "userfile" y "webmasterfile", entonces usará el método send() de XMLHttpRequest para enviar los datos del formulario. El campo "webmasterfile" es un Blob. Un objeto Blob representa un objeto de tipo similar a un fichero que es inalterable y que almacenará datos en formato raw. Los Blobs representan datos que no necesariamente tendrán un formato Javascript nativo. La interfaz {{ domxref("File") }} está basada en Blob, y hereda su funcionalidad y la amplía para dar soporte a archivos que estén en el sistema del usuario. Para construir un Blob, puede invocar al constructor del objeto Blob.

+ +

Recuperando un objeto FormData de un formulario HTML 

+ +

Para construir un objeto FormData que contenga los datos de un {{ HTMLElement("form") }} existente, especifique ese elemento form cuando cree el objeto FormData:

+ +
var formData = new FormData(someFormElement);
+
+ +

Por ejemplo:

+ +
var formElement = document.getElementById("myFormElement");
+var request = new XMLHttpRequest();
+request.open("POST", "submitform.php");
+request.send(new FormData(formElement));
+
+ +

También puede añadir datos adicionales al objeto FormData antes de enviarlo. Así:

+ +
var formElement = document.getElementById("myFormElement");
+formData = new FormData(formElement);
+formData.append("serialnumber", serialNumber++);
+request.send(formData);
+ +

Esto le permite aumentar los datos del formulario antes de enviarlos para incluir información adicional que no necesariamente debiera ser editable por el usuario en el formulario.

+ +

Enviando archivos usando objetos FormData

+ +

También puede enviar archivos usando FormData. Simplemente incluye un elemento {{ HTMLElement("input") }} de tipo {{ domxref("File") }}:

+ +
<form enctype="multipart/form-data" method="post" name="fileinfo">
+  <label>Your email address:</label>
+  <input type="email" autocomplete="on" autofocus name="userid" placeholder="email" required size="32" maxlength="64" /><br />
+  <label>Custom file label:</label>
+  <input type="text" name="filelabel" size="12" maxlength="32" /><br />
+  <label>File to stash:</label>
+  <input type="file" name="file" required />
+  <input type="submit" value="Stash the file!" />
+</form>
+<div id="output"></div>
+
+ +

Luego puede enviarlo usando código como el siguiente:

+ +
var form = document.forms.namedItem("fileinfo");
+form.addEventListener('submit', function(ev) {
+
+  var
+    oOutput = document.getElementById("output"),
+    oData = new FormData(document.forms.namedItem("fileinfo"));
+
+  oData.append("CustomField", "This is some extra data");
+
+  var oReq = new XMLHttpRequest();
+  oReq.open("POST", "stash.php", true);
+  oReq.onload = function(oEvent) {
+    if (oReq.status == 200) {
+      oOutput.innerHTML = "Uploaded!";
+    } else {
+      oOutput.innerHTML = "Error " + oReq.status + " occurred uploading your file.<br \/>";
+    }
+  };
+
+  oReq.send(oData);
+  ev.preventDefault();
+}, false);
+
+ +
+

Nota: el método especificado en el formulario será usado por encima del método utilizado en en la llamada a open().

+
+ +

También puede añadir un {{ domxref("File") }} o un {{ domxref("Blob") }} directamente al objeto {{ domxref("XMLHttpRequest/FormData", "FormData") }} de la siguiente manera:

+ +
data.append("myfile", myBlob, "filename.txt");
+
+ +

Cuando se usa el método append es posible usar, de manera opcional, un tercer parámetro para pasarle un nombre de fichero dentro de la cabecera Content-Disposition que será enviada al servidor. Cuando no se especifica (o el parámetro no es soportado), el nombre "blob" es el que será utilizado.

+ +

Además, puede usar FormData con jQuery si asigna las opciones correctas:

+ +
var fd = new FormData(document.getElementById("fileinfo"));
+fd.append("CustomField", "This is some extra data");
+$.ajax({
+  url: "stash.php",
+  type: "POST",
+  data: fd,
+  processData: false,  // tell jQuery not to process the data
+  contentType: false   // tell jQuery not to set contentType
+});
+
+ +

Envío de formularios y carga de archivos vía AJAX sin  objetos FormData

+ +

Si quiere saber cómo serializar y enviar vía AJAX un formulario sin utilizar objetos FormData, por favor leer este párrafo .

+ +

Vea también

+ + diff --git a/files/es/web/api/geolocation_api/index.html b/files/es/web/api/geolocation_api/index.html new file mode 100644 index 0000000000..6d50972f8b --- /dev/null +++ b/files/es/web/api/geolocation_api/index.html @@ -0,0 +1,232 @@ +--- +title: Uso de geolocalización +slug: WebAPI/Using_geolocation +tags: + - API + - Geolocalización + - Guía + - clearWatch + - watchPosition +translation_of: Web/API/Geolocation_API +--- +

{{securecontext_header}}{{APIRef("Geolocation API")}}

+ +

La API de geolocalización permite al usuario compartir su ubicación a las aplicaciones web si así lo desea. Por razones de privacidad, al usuario se le pide que confirme el permiso para proporcionar información de ubicación.

+ +

El objeto geolocation

+ +

La API de geolocalización se publica a través del objeto {{domxref("window.navigator.geolocation","navigator.geolocation")}}.

+ +

Si el objeto existe, los servicios de geolocalización están disponibles. Se puede comprobar la presencia de la geolocalización de esta manera:

+ +
if ("geolocation" in navigator) {
+  /* la geolocalización está disponible */
+} else {
+  /* la geolocalización NO está disponible */
+}
+
+ +
+

Nota: En Firefox 24 y versiones anteriores, "geolocation" in navigator siempre retornaba true incluso si la API se encontraba deshabilitada. Esto ha sido corregido en Firefox 25 para cumplir con la especificación.  ({{bug(884921)}}).

+
+ +

Obtención de la ubicación actual

+ +

Para obtener la ubicación actual del usuario, puede llamar al método {{domxref("window.navigator.geolocation.getCurrentPosition()","getCurrentPosition()")}}.

+ +

Esto inicia una solicitud asíncrona para detectar la posición del usuario, y consulta el hardware de posicionamiento para obtener información actualizada. Cuando se determina la posición, se ejecuta la función de callback. Si lo desea, puede proporcionar otra función de callback que se ejecuta si se produce un error. Un tercer parámetro opcional, es un objeto de opciones donde se puede establecer la edad máxima de la posición devuelta, el tiempo de espera para una solicitud y si se requiere una alta precisión para la posición.

+ +
+

Nota: Por defecto, {{domxref("window.navigator.geolocation.getCurrentPosition()","getCurrentPosition()")}} intenta responder tan rápido como sea posible con un resultado de baja precisión. Es útil cuando se necesita una respuesta rápida sin importar su exactitud. A los dispositivos con GPS, por ejemplo, les puede tomar más de un minuto obtener una posición, por lo que datos menos precisos (localización por IP o wifi) pueden ser devueltos por {{domxref("window.navigator.geolocation.getCurrentPosition()","getCurrentPosition()")}}.

+
+ +
navigator.geolocation.getCurrentPosition(function(position) {
+  haz_algo(position.coords.latitude, position.coords.longitude);
+});
+ +

En el ejemplo anterior la función do_something() será ejecutada una vez que se obtiene la posición.

+ +

Rastreando la posición actual

+ +

Si los datos de ubicación cambian (si el dispositivo se mueve o información geográfica más precisa es recibida), puede definir una función de callback que se ejecuta al cambiar la posición. Esto se logra a través de la función {{domxref("window.navigator.geolocation.watchPosition()","watchPosition()")}}, que recibe los mismos parámetros que {{domxref("window.navigator.geolocation.getCurrentPosition()","getCurrentPosition()")}}. La función de callback es ejecutada varias veces, permitiendo al navegador actualizar la ubicación cada vez que cambia, o proporcionar una posición con mayor exactitud utilizando distintas técnicas de geolocalización. La función de callback de error, la cual es opcional como en {{domxref("window.navigator.geolocation.getCurrentPosition()","getCurrentPosition()")}}, es llamada solo una vez, cuando nunca serán devueltos resultados correctos.

+ +
+

Nota: Es posible usar la función {{domxref("window.navigator.geolocation.watchPosition()","watchPosition()")}} sin haber ejecutado antes {{domxref("window.navigator.geolocation.getCurrentPosition()","getCurrentPosition()")}}.

+
+ +
var watchID = navigator.geolocation.watchPosition(function(position) {
+  do_something(position.coords.latitude, position.coords.longitude);
+});
+ +

El método {{domxref("window.navigator.geolocation.watchPosition()","watchPosition()")}} devuelve un número que se utiliza para identificar el rastreador de posición solicitado; este valor se utiliza junto con el método {{domxref("window.navigator.geolocation.clearWatch()","clearWatch()")}} para dejar de rastrear la posición del usuario.

+ +
navigator.geolocation.clearWatch(watchID);
+
+ +

Afinando la respuesta

+ +

Ambos métodos,  {{domxref("window.navigator.geolocation.getCurrentPosition()","getCurrentPosition()")}} y {{domxref("window.navigator.geolocation.watchPosition()","watchPosition()")}} aceptan una función de callback en caso de éxito, una función callback opcional si ocurre algún error, y un objeto PositionOptions también opcional.

+ +

{{page("/en-US/docs/DOM/window.navigator.geolocation.getCurrentPosition","PositionOptions")}}

+ +

Una llamada a {{domxref("window.navigator.geolocation.watchPosition()","watchPosition")}} luce como el siguiente ejemplo:

+ +
function geo_success(position) {
+  do_something(position.coords.latitude, position.coords.longitude);
+}
+
+function geo_error() {
+  alert("Sorry, no position available.");
+}
+
+var geo_options = {
+  enableHighAccuracy: true,
+  maximumAge        : 30000,
+  timeout           : 27000
+};
+
+var wpid = navigator.geolocation.watchPosition(geo_success, geo_error, geo_options);
+ +

Demo de watchPosition: http://www.thedotproduct.org/experiments/geo/
+ 

+ +

Describiendo una posición

+ +

La ubicación del usuario es descrita con un objeto Position referenciando a un objeto Coordinates.

+ +

{{page("/en-US/docs/DOM/window.navigator.geolocation.getCurrentPosition","Position")}}

+ +

{{page("/en-US/docs/DOM/window.navigator.geolocation.getCurrentPosition","Coordinates")}}

+ +

Manejo de errores

+ +

La función de callback de error, si existe cuando se llama a getCurrentPosition() o watchPosition(), recibe un objeto  PositionError como su primer parámetro.

+ +
function errorCallback(error) {
+  alert('ERROR(' + error.code + '): ' + error.message);
+};
+
+ +

{{page("/en-US/docs/DOM/window.navigator.geolocation.getCurrentPosition","PositionError")}}

+ +

Ejemplo de geolocalización

+ + + +

Contenido HTML

+ +
<p><button onclick="geoFindMe()">Show my location</button></p>
+<div id="out"></div>
+
+ +

Contenido JavaScript

+ +
function geoFindMe() {
+  var output = document.getElementById("out");
+
+  if (!navigator.geolocation){
+    output.innerHTML = "<p>Geolocation is not supported by your browser</p>";
+    return;
+  }
+
+  function success(position) {
+    var latitude  = position.coords.latitude;
+    var longitude = position.coords.longitude;
+
+    output.innerHTML = '<p>Latitude is ' + latitude + '° <br>Longitude is ' + longitude + '°</p>';
+
+    var img = new Image();
+    img.src = "http://maps.googleapis.com/maps/api/staticmap?center=" + latitude + "," + longitude + "&zoom=13&size=300x300&sensor=false";
+
+    output.appendChild(img);
+  };
+
+  function error() {
+    output.innerHTML = "Unable to retrieve your location";
+  };
+
+  output.innerHTML = "<p>Locating…</p>";
+
+  navigator.geolocation.getCurrentPosition(success, error);
+}
+
+ +

Resultado

+ +

{{ EmbedLiveSample('Geolocation_Live_Example',350,410) }}

+ +

Compatibilidad entre navegadores

+ +

{{ CompatibilityTable() }}

+ +
+ + + + + + + + + + + + + + + + + + + +
CaracterísticaChromeFirefox (Gecko)Internet ExplorerOperaSafari
Soporte básico5{{CompatGeckoDesktop("1.9.1")}}910.605
+
+ +
+ + + + + + + + + + + + + + + + + + + + + +
FeatureAndroidChrome for AndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Soporte básico{{CompatUnknown()}}{{CompatUnknown()}}{{CompatGeckoMobile("4")}}{{CompatUnknown()}}10.60{{CompatUnknown()}}
+
+ +

Notas sobre Gecko

+ +

Firefox incluye soporte para localizar basándose en información de redes inalámbricas, usando Google Location Services. En la transacción entre Firefox y Google, los datos son compartidos incluyendo información del punto de acceso inalámbrico, un token de acceso (similar a una cookie con duración de dos semanas), y la dirección IP del usuario. Para más información, por favor consulte la Política de Privacidad de Mozilla y la Política de Privacidad de Google, dichos documentos cubren como estos datos pueden ser utilizados.

+ +

En Firefox 3.6 (Gecko 1.9.2) fue añadido soporte para utilizar el servicio GPSD para geolocalización en sistemas Linux.

+ +

Consultar también

+ + + +

 

diff --git a/files/es/web/api/globaleventhandlers/ongotpointercapture/index.html b/files/es/web/api/globaleventhandlers/ongotpointercapture/index.html new file mode 100644 index 0000000000..3023c3758e --- /dev/null +++ b/files/es/web/api/globaleventhandlers/ongotpointercapture/index.html @@ -0,0 +1,134 @@ +--- +title: Element.ongotpointercapture +slug: Web/API/Element/ongotpointercapture +tags: + - API + - Controlador + - DOM + - Elemento + - Eventos Puntero + - Propiedad + - Referencia +translation_of: Web/API/GlobalEventHandlers/ongotpointercapture +translation_of_original: Web/API/Element/ongotpointercapture +--- +

{{ ApiRef("DOM") }}

+ +

ongotpointercapture es una propiedad {{domxref("EventHandler")}} de la interfaz {{domxref("Element")}}  que devuelve el controlador de eventos (función) para el evento tipo {{event("gotpointercapture")}}.

+ +

Síntasix

+ +
var gotCaptureHandler = target.ongotpointercpature;
+
+ +

Valor de Retorno

+ +
+
gotCaptureHandler
+
El controlador de eventos  gotpointercapture para el elemento target.
+
+ +

Ejemplo

+ +
<html>
+<script>
+function overHandler(ev) {
+ // Determine the target event's gotpointercapture handler
+ var gotCaptureHandler = ev.target.ongotpointercapture;
+}
+function init() {
+ var el=document.getElementById("target");
+ el.onpointerover = overHandler;
+}
+</script>
+<body onload="init();">
+<div id="target"> Touch me ... </div>
+</body>
+</html>
+
+ +

Especificaciones

+ + + + + + + + + + + + + + + + + + + +
EspecificaciónEstadoComentario
{{SpecName('Pointer Events 2','#widl-Element-ongotpointercapture', 'ongotpointercapture')}}{{Spec2('Pointer Events 2')}}Versión no estable.
{{SpecName('Pointer Events', '#widl-Element-ongotpointercapture', 'ongotpointercapture')}}{{Spec2('Pointer Events')}}Definición inicial.
+ +

Compatibilidad en los Navegadores

+ +

{{CompatibilityTable}}

+ +
+ + + + + + + + + + + + + + + + + + + +
FunciónChromeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
Soporte Básico{{CompatNo}}{{CompatVersionUnknown}} [1]{{CompatIE("10")}}{{CompatNo}}{{CompatNo}}
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
FunciónAndroidAndroid WebviewChrome for AndroidFirefox Mobile (Gecko)Firefox OSIE MobileOpera MobileSafari Mobile
Soporte Básico{{CompatNo}}{{CompatNo}}{{CompatNo}}{{CompatNo}}{{CompatNo}}{{CompatIE("10")}}{{CompatNo}}{{CompatNo}}
+
+ +

[1] Implementación retirada. Ver {{Bug("1166347")}}.

+ +

Ver también

+ + diff --git a/files/es/web/api/globaleventhandlers/onlostpointercapture/index.html b/files/es/web/api/globaleventhandlers/onlostpointercapture/index.html new file mode 100644 index 0000000000..9a07506d45 --- /dev/null +++ b/files/es/web/api/globaleventhandlers/onlostpointercapture/index.html @@ -0,0 +1,133 @@ +--- +title: Element.onlostpointercapture +slug: Web/API/Element/onlostpointercapture +tags: + - API + - Controlador de Eventos + - DOM + - Eventos Puntero + - Propiedad + - Referencia +translation_of: Web/API/GlobalEventHandlers/onlostpointercapture +translation_of_original: Web/API/Element/onlostpointercapture +--- +

{{ ApiRef("DOM") }}

+ +

onlostpointercapture es una propiedad {{domxref("EventHandler")}} de la interfaz {{domxref("Element")}}  que devuelve el controlador de eventos (función) para el evento tipo {{event("lostpointercapture")}} .

+ +

Síntasix

+ +
var lostCaptureHandler = target.onlostpointercpature;
+
+ +

Valor de Retorno

+ +
+
lostCaptureHandler
+
El controlador de eventos  lostpointercapture para el elemento target.
+
+ +

Ejemplo

+ +
<html>
+<script>
+function overHandler(ev) {
+ // Determine the target event's lostpointercapture handler
+ var lostCaptureHandler = ev.target.onlostpointercapture;
+}
+function init() {
+ var el=document.getElementById("target");
+ el.onpointerover = overHandler;
+}
+</script>
+<body onload="init();">
+<div id="target"> Touch me ... </div>
+</body>
+</html>
+
+ +

Especificaciones

+ + + + + + + + + + + + + + + + + + + +
EspecificaciónEstadoComentario
{{SpecName('Pointer Events 2','#widl-Element-onlostpointercapture', 'onlostpointercapture')}}{{Spec2('Pointer Events 2')}}Versión no estable.
{{SpecName('Pointer Events', '#widl-Element-onlostpointercapture', 'onlostpointercapture')}}{{Spec2('Pointer Events')}}Definición inicial.
+ +

Compatibilidad en los Navegadores

+ +

{{CompatibilityTable}}

+ +
+ + + + + + + + + + + + + + + + + + + +
FunciónChromeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
Soporte Básico{{CompatNo}}{{CompatVersionUnknown}} [1]{{CompatIE("10")}}{{CompatNo}}{{CompatNo}}
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
FunciónAndroidAndroid WebviewChrome for AndroidFirefox Mobile (Gecko)Firefox OSIE MobileOpera MobileSafari Mobile
Soporte Básico{{CompatNo}}{{CompatNo}}{{CompatNo}}{{CompatNo}}{{CompatNo}}{{CompatIE("10")}}{{CompatNo}}{{CompatNo}}
+
+ +

[1] Implementación retirada. Ver {{Bug("1166347")}}.

+ +

Ver también

+ + diff --git a/files/es/web/api/globaleventhandlers/onunload/index.html b/files/es/web/api/globaleventhandlers/onunload/index.html deleted file mode 100644 index a665fc2027..0000000000 --- a/files/es/web/api/globaleventhandlers/onunload/index.html +++ /dev/null @@ -1,45 +0,0 @@ ---- -title: window.onunload -slug: Web/API/GlobalEventHandlers/onunload -translation_of: Web/API/WindowEventHandlers/onunload ---- -

{{ ApiRef() }}

-

Test Summary

-

The unload event is raised when the document is unloaded.

-

Syntax

-
window.onunload = funcRef;
-
- -

Example

-
<html>
-<head>
-
-<title>onunload test</title>
-
-<script type="text/javascript">
-
-window.onunload = unloadPage;
-
-function unloadPage()
-{
- alert("unload event detected!");
-}
-</script>
-</head>
-
-<body>
-<p>Reload a new page into the browser<br />
- to fire the unload event for this page.</p>
-<p>You can also use the back or forward buttons<br />
- to load a new page and fire this event.</p>
-</body>
-</html>
-
-

Notes

-

Note that using this event handler in your page prevents Firefox 1.5 from caching the page in the in-memory bfcache. See Using Firefox 1.5 caching for details.

-

Browsers equipped with pop-up window blockers will ignore all window.open() method calls in onunload event handler functions.

-

Specification

-

{{ DOM0() }}

-

{{ languages( {"zh-cn": "zh-cn/DOM/window.onunload" } ) }}

diff --git a/files/es/web/api/globaleventhandlers/onwheel/index.html b/files/es/web/api/globaleventhandlers/onwheel/index.html new file mode 100644 index 0000000000..b8f829969b --- /dev/null +++ b/files/es/web/api/globaleventhandlers/onwheel/index.html @@ -0,0 +1,31 @@ +--- +title: Element.onwheel +slug: Web/API/Element/onwheel +tags: + - API + - DOM + - Gecko + - Propiedad + - Referencia +translation_of: Web/API/GlobalEventHandlers/onwheel +--- +

{{ ApiRef("DOM") }}

+ +

{{ non-standard_header() }}

+ +

Sumario

+ +

La propiedad  onwheel devuelve el código del controlador de eventos onwheel en el elemento actual.

+ +

Syntax

+ +
element.onwheel = event handling code
+
+ +

Notas

+ +

El evento wheel se genera cuando el usuario desplaza el contenido de un elemento.

+ +

Ver también

+ +

Bug 18542 – el atributo onmousewheel  debe ser reemplazado con onwheel

diff --git a/files/es/web/api/history_api/example/index.html b/files/es/web/api/history_api/example/index.html new file mode 100644 index 0000000000..1971f1348f --- /dev/null +++ b/files/es/web/api/history_api/example/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/web/api/history_api/index.html b/files/es/web/api/history_api/index.html new file mode 100644 index 0000000000..eb2c0b3fdd --- /dev/null +++ b/files/es/web/api/history_api/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/web/api/html_drag_and_drop_api/file_drag_and_drop/index.html b/files/es/web/api/html_drag_and_drop_api/file_drag_and_drop/index.html new file mode 100644 index 0000000000..1225072b01 --- /dev/null +++ b/files/es/web/api/html_drag_and_drop_api/file_drag_and_drop/index.html @@ -0,0 +1,117 @@ +--- +title: Drag & Drop archivo +slug: DragDrop/Drag_and_Drop/drag_and_drop_archivo +tags: + - Guía + - arrastra y suelta + - drag and drop + - drop zone + - zona de arrastre +translation_of: Web/API/HTML_Drag_and_Drop_API/File_drag_and_drop +--- +

{{DefaultAPISidebar("HTML Drag and Drop API")}}

+ +

Las interfaces Drag-and-Drop posibilitan arrastrar y soltar archivos en una página web. En este documento se describe cómo una aplicación puede aceptar uno, o más, archivos que son arrastrados desde el explorador de archivos de la plataforma y soltados en una página web.

+ +

Los pasos principales para configurar Drag-and-drop son: 1) definir una "zona drop (drop zone), es decir, definir un elemento donde se podrá soltar el archivo; y 2) definir funciones para la gestión de los eventos {{event("drop")}} y {{event("dragover")}}. Estos pasos se describen a continuación, tambien se incluyen ejemplos snippets de código. El código fuente completo está disponible en el repositorio drag-and-drop de MDN (cualquier sugerencia o  tema que revisar es bienvenido).

+ +

Nota: {{domxref("HTML_Drag_and_Drop_API","HTML drag and drop")}} define 2 diferentes APIs para soportar drag and drop de archivos. Una API es la interfaz {{domxref("DataTransfer")}} y la segunda API son las interfaces {{domxref("DataTransferItem")}} y {{domxref("DataTransferItemList")}}.  Este ejemplo ilustra el uso de ambas APIs (y no usa ninguna interfaz específica de Gecko).

+ +

Define la zona "drop" [drop zone]

+ +

Es necesario configurar un evento {{event("drop")}} en el objeto sobre el cual se soltará el objeto arrastrado. Este evento llamará una función global {{domxref("GlobalEventHandlers.ondrop","ondrop")}} que recibe los datos del objeto arrastrado. El siguiente código muestra cómo se hace con un elemento {{HTMLelement("div")}}:

+ +
<div id="drop_zone" ondrop="dropHandler(event);">
+  <p>Arrastra y suelta uno o más archivos a esta zona ...</p>
+</div>
+ +

Normalmente, una aplicación incluirá una función de gestión de eventos {{event("dragover")}} en el elemento objetivo del arrastre y esa función desactivará el comportamiento de arrastre por defecto del browser. Para añadir esta función necesita incluir una función global {{domxref("GlobalEventHandlers.ondragover","ondragover")}}:

+ +
<div id="drop_zone" ondrop="dropHandler(event);" ondragover="dragOverHandler(event);">
+  <p>Arrastra y suelta uno o más archivos a esta zona ...</p>
+</div>
+
+ +

Por último, puede que una aplicación quiera personalizar el estilo del elemento objetivo del arrastre para indicar visualmente que es una zona drag and drop. En este ejemplo, el elemento objetivo usa el siguiente estilo:

+ +
#drop_zone {
+  border: 5px solid blue;
+  width:  200px;
+  height: 100px;
+}
+
+ +
+

Fíjese que los eventos dragstart y dragend no son activados cuando se arrastra un archivo al browser desde el SO.

+
+ +

Procesar la acción de soltar [drop]

+ +

El evento {{event("drop")}} se ejecuta cuando el usuario suelta el o los archivos. En el siguiente manejador, si el navegador sorporta la interfaz {{domxref("DataTransferItemList")}} , el método {{domxref("DataTransferItem.getAsFile","getAsFile()")}} se utiliza para acceder cada fichero; de lo contrario la propiedad {{domxref("DataTransfer")}} de la interfaz {{domxref("DataTransfer.files","files")}} es usada para acceder cada archivo.

+ +

El ejemplo siguiente muestra como escribir el nombre de cada fichero arrastrado en la consola. En una aplicación real, se querrá procesar un archivo usando {{domxref("File","File API")}}.

+ +

Nótese que en este ejemplo, cualquier item arrastrado que no sea un archivo es ignorado.

+ +
function dropHandler(ev) {
+  console.log('Fichero(s) arrastrados');
+
+  // Evitar el comportamiendo por defecto (Evitar que el fichero se abra/ejecute)
+  ev.preventDefault();
+
+  if (ev.dataTransfer.items) {
+    // Usar la interfaz DataTransferItemList para acceder a el/los archivos)
+    for (var i = 0; i < ev.dataTransfer.items.length; i++) {
+      // Si los elementos arrastrados no son ficheros, rechazarlos
+      if (ev.dataTransfer.items[i].kind === 'file') {
+        var file = ev.dataTransfer.items[i].getAsFile();
+        console.log('... file[' + i + '].name = ' + file.name);
+      }
+    }
+  } else {
+    // Usar la interfaz DataTransfer para acceder a el/los archivos
+    for (var i = 0; i < ev.dataTransfer.files.length; i++) {
+      console.log('... file[' + i + '].name = ' + ev.dataTransfer.files[i].name);
+    }
+  }
+
+  // Pasar el evento a removeDragData para limpiar
+  removeDragData(ev)
+}
+ +

Prevenir el comportamiento default de arrastrado en el browser 

+ +

El siguiente evento {{event("dragover")}} llama a  {{domxref("Event.preventDefault","preventDefault()")}} para deshabilitar (turn off) la respuesta estandar drag-and-drop del browser.

+ +
function dragOverHandler(ev) {
+  console.log('File(s) in drop zone');
+
+  // Prevent default behavior (Prevent file from being opened)
+  ev.preventDefault();
+}
+
+ +

Limpieza (Cleanup)

+ +

Typically, an application may want to perform some cleanup by deleting the file drag data. In this example, the drop event is passed along from drop handler to a custom function called removeDragData. If the browser supports the {{domxref("DataTransferItemList")}} interface, the list's {{domxref("DataTransferItemList.clear","clear()")}} method is used to delete the file drag data; otherwise the {{domxref("DataTransfer")}} object's {{domxref("DataTransfer.clearData","clearData()")}} method is used to delete the data.

+ +
function removeDragData(ev) {
+  console.log('Removing drag data')
+
+  if (ev.dataTransfer.items) {
+    // Use DataTransferItemList interface to remove the drag data
+    ev.dataTransfer.items.clear();
+  } else {
+    // Use DataTransfer interface to remove the drag data
+    ev.dataTransfer.clearData();
+  }
+}
+
+ +

See also

+ + diff --git a/files/es/web/api/html_drag_and_drop_api/index.html b/files/es/web/api/html_drag_and_drop_api/index.html new file mode 100644 index 0000000000..82e069ed48 --- /dev/null +++ b/files/es/web/api/html_drag_and_drop_api/index.html @@ -0,0 +1,57 @@ +--- +title: Arrastrar y soltar +slug: DragDrop/Drag_and_Drop +tags: + - HTML5 + - XUL +translation_of: Web/API/HTML_Drag_and_Drop_API +--- +

Firefox y otras aplicaciones de Mozilla admiten una serie de características para gestionar la funcionalidad de arrastrar y soltar. Esto le permite al usuario hacer clic y mantener presionado el botón del ratón/mouse sobre un elemento, arrastrarlo a otra ubicación y soltarlo para colocar el elemento allí. Al puntero le seguirá una representación transparente de lo que se está arrastrando durante la operación. La ubicación de destino puede ser una aplicación diferente. Sitios web, extensiones y aplicaciones XUL pueden hacer uso de esta funcionalidad para personalizar los elementos que pueden ser arrastrados, evaluar la operación, así como especificar el lugar donde los elementos se pueden soltar.

+
Esta sección trata sobre la funcionalidad de arrastrar y soltar en Firefox 3.5 (Gecko 1.9.1) y versiones posteriores. Consulta la documentación de la API anterior para Firefox 3.0 y versiones anteriores.
+ + +

Elementos básicos de arrastrar y soltar

+

Cuando comienza una operación de arrastre, se puede proporcionar una serie de datos:

+ +

Mozilla y Firefox admiten una serie de características que no se encuentran en el modelo estándar de arrastrar y soltar. Estas te permiten arrastrar elementos múltiples y arrastrar datos que no son cadenas. Para obtener más información, consulta Arrastrar y soltar múltiples elementos .

+

Para obtener una lista de tipos de datos comunes utilizados para arrastrar y soltar, consulta Tipos de operaciones de arrastre recomendados.

+

Está disponible una referencia rápida para los procedimientos recomendados en la operación de arrastre de los siguientes tipos de elementos:

+ +

Consulta DataTransfer para tener una referencia al objeto DataTransfer.

+ + +

Eventos de arrastre

+

Se utilizan una serie de eventos que se ejecutan durante las diversas etapas de la operación de arrastre y colocación. Ten en cuenta que se ejecutan sólo los eventos de arrastre, los eventos del ratón/mouse como mousemove no se ejecutan durante una operación de arrastre.

+

La propiedad dataTransfer de todos los eventos de arrastre contiene datos sobre la operación de arrastre y colocación.

+ + +
+
dragstart
+
Se ejecuta sobre un elemento cuando se inicia una operación de arrastre. El usuario está solicitando arrastrar el elemento al que dispara el evento dragstart. Durante este evento, un proceso de escucha ajustará cierto tipo de información como los datos de la operación de arrastre y la imagen que se asocia con ella. Para obtener más información al respecto, consulta Inicio de una operación de arrastre .
+
dragenter
+
Se dispara cuando el ratón/mouse se mueve primero sobre un elemento, mientras está teniendo lugar una operación de arrastre. Un proceso de escucha de este evento debe indicar si se permite una operación de arrastre sobre esta ubicación. Si no hay procesos de escucha o éstos no realizan ninguna operación, entonces no se permite, de manera predeterminada, una operación de arrastre. Este es también el evento al que escuchar si deseas proporcionar información acerca de que se permite una operación de arrastre, como, por ejemplo, mostrar un resaltado o un marcador de inserción. Para obtener más información al respecto, consulta Especificación de destinos de colocación .
+
dragover
+
Este evento se activa cuando el ratón/mouse se mueve sobre un elemento cuando está teniendo lugar una operación de arrastre. Gran parte del tiempo, la operación que tiene lugar durante un proceso de escucha será la misma que el evento dragenter. Para obtener más información al respecto, consulta Especificación de destinos de colocación.
+
dragleave
+
Este evento se activa cuando el ratón/mouse sale de un elemento mientras que está teniendo lugar una operación de arrastre. Los procesos de escucha deben eliminar cualquier resaltado o marcador de inserción que usan para la información sobre el proceso de arrastre.
+
drag
+
Este evento se activa en el origen del arrastre, es decir, el elemento donde dragstart fue disparado, durante la operación de arrastre.
+
drop
+
El evento se dispara sobre el elemento en el que se produjo la colocación al finalizar la operación de arrastre. Un proceso de escucha se encargará de recuperar los datos que se arrastran e insertarlos en la ubicación de la colocación. Este evento sólo se activará si se desea disponer de la funcionalidad de soltar. No se activará si el usuario cancela la operación de arrastre, por ejemplo, pulsando la tecla Escape, o si se liberó el botón del ratón/mouse mientras que éste no estaba sobre un destino de colocación válido. Para más información sobre esto, consulta Realizar una operación de colocación.
+
dragend
+
El origen del arrastre recibirá un evento dragend cuando la operación se haya completado, tanto si tuvo éxito como si no. Consulta Finalizar una operación de arrastre si deseas más información.
+
+ + +
{{ HTML5ArticleTOC () }}
diff --git a/files/es/web/api/html_drag_and_drop_api/recommended_drag_types/index.html b/files/es/web/api/html_drag_and_drop_api/recommended_drag_types/index.html new file mode 100644 index 0000000000..daad516a44 --- /dev/null +++ b/files/es/web/api/html_drag_and_drop_api/recommended_drag_types/index.html @@ -0,0 +1,144 @@ +--- +title: Tipos de Drag recomendados +slug: DragDrop/Recommended_Drag_Types +translation_of: Web/API/HTML_Drag_and_Drop_API/Recommended_drag_types +--- +

A continuación se describe la mejor practica para utilizar los datos a ser arrastrado.

+

Arrastramdo Texto

+

Al arrastrar el texto, utilice el texto / texto normal. Los datos deben ser la cadena de arrastre. Por ejemplo:

+
event.dataTransfer.setData("text/plain", "This is text to drag")
+
+

Arrastrar texto en cuadros de texto y las selecciones de las páginas web se realiza de forma automática, por lo que no es necesario para manejar dragging.

+

Se recomienda que siempre se agrega datos del tipo  text/plain  como un mensaje para las aplicaciones o los destinos que no soportan otros tipos, a menos que no hay alternativa de texto lógico. Siempre agregue el tipo de texto sin formato pasado, ya que es el menos específico.

+

En códigos más viejo, encontrara text/unicode o el tipo Text.Estos equivalen text/plain,que guardara y recibia los datos del texto plano en ese lugar.

+ +

Los enlaces deben incluir los datos de los dos tipos, el primero debe ser  URL utilizando el tipo text/uri-list,y el segundo es URL utilizando el tipo text/plain. Ambos tipos deben utilizar los mismos datos, la URL del enlace. Por ejemplo:

+
var dt = event.dataTransfer;
+dt.setData("text/uri-list", "http://www.mozilla.org");
+dt.setData("text/plain", "http://www.mozilla.org");
+
+

Es constumbre, establecer el tipo text/plain de ultimo, , ya que es menos específico que el tipo de URI.

+

Note que el tipo de URL uri-list es con una "i", no una "L"

+

Note that the URL type is uri-list with an 'I', not with an 'L'.

+

To drag multiple links, you can also separate each link with a linebreak. A line that begins with a number sign (#) is a comment and should not be considered a valid URL. You can use a comment to indicate the purpose of a link, or to hold the title associated with a link. The text/plain version of the drag data should include all links but should not include the comments.

+

For example:

+
http://www.mozilla.org
+#A second link
+http://www.xulplanet.com
+
+

This sample text/uri-list data contains two links and a comment.

+

When retrieving a dropped link, you should ensure you handle the case where multiple links may have been dragged, and any comments that appear in the data. For convenience, the special type URL may be used to refer to the first valid link within the data for the text/uri-list type. You should not add data using the URL type; attempting to do so will just set the value of the text/uri-list type instead.

+
var url = event.dataTransfer.getData("URL");
+
+

You may also see data using the text/x-moz-url type which is a Mozilla specific type. If it appears, it should be used before the text/uri-list type. It holds the URL of the link followed by the title of the link, separated by a linebreak. For example:

+
http://www.mozilla.org
+Mozilla
+http://www.xulplanet.com
+XUL Planet
+
+

Dragging HTML and XML

+

HTML content may use the text/html type. The data for this type should be the serialized HTML to drag. For instance, it would be suitable to set the data value for this type to the value of the innerHTML property of an element.

+

XML content may use the text/xml type, but you should ensure that the data value is well-formed XML.

+

You may also include a plain text representation of the HTML or XML data using the text/plain type. The data should be just the text and should not include any of the source tags or attributes. For instance:

+
var dt = event.dataTransfer;
+dt.setData("text/html", "Hello there, <strong>stranger</strong>");
+dt.setData("text/plain", "Hello there, stranger");
+
+

Dragging Files

+

A local file is dragged using the application/x-moz-file type with a data value that is an nsIFile object. Non-privileged web pages are not able to retrieve or modify data of this type. Because a file is not a string, you must use the mozSetDataAt method to assign the data. Similarly, when retrieving the data, you must use the mozGetDataAt method.

+
event.dataTransfer.mozSetDataAt("application/x-moz-file", file, 0);
+
+

If possible, you may also include the file URL of the file using both the text/uri-list and/or text/plain types. These types should be added last so that the more specific application/x-moz-file type has higher priority.

+

Multiple files will be received during a drop as mutliple items in the data transfer. See Dragging and Dropping Multiple Items for more details about this.

+

The following example shows how to create an area for receiving dropped files:

+
<listbox ondragenter="return checkDrag(event)"
+         ondragover="return checkDrag(event)"
+         ondrop="doDrop(event)"/>
+
+<script>
+function checkDrag(event)
+{
+  return event.dataTransfer.types.contains("application/x-moz-file");
+}
+
+function doDrop(event)
+{
+  var file = event.dataTransfer.mozGetDataAt("application/x-moz-file", 0);
+  if (file instanceof Components.interfaces.nsIFile)
+    event.currentTarget.appendItem(file.leafName);
+}
+</script>
+
+

In this example, the event returns false only if the data transfer contains the application/x-moz-file type. During the drop event, the data associated with the file type is retrieved, and the filename of the file is added to the listbox. Note that the instanceof operator is used here as the mozGetDataAt method will return an nsISupports that needs to be checked and converted into an nsIFile. This is also a good extra check in case someone made a mistake and added a non-file for this type.

+

Dragging Images

+

Direct image dragging is not commonly done. In fact, Mozilla does not support direct image dragging on Mac or Linux platforms. Instead, images are usually dragged only by their URLs. To do this, use the text/uri-list type as with other URL links. The data should be the URL of the image or a data URL if the image is not stored on a web site or disk. For more information about data URLs, see the data URL scheme.

+

As with other links, the data for the text/plain type should also contain the URL. However, a data URL is not usually as useful in a text context, so you may wish to exclude the text/plain data in this situation.

+

In chrome or other privileged code, you may also use the image/jpeg, image/png or image/gif types, depending on the type of image. The data should be an object which implements the nsIInputStream interface. When this stream is read, it should provide the data bits for the image, as if the image was a file of that type.

+

You should also include the application/x-moz-file type if the image is located on disk. In fact, this a common way in which image files are dragged.

+

It is important to set the data in the right order, from most specific to least specific. The image type such as image/jpeg should come first, followed by the application/x-moz-file type. Next, you should set the text/uri-list data and finally the text/plain data. For example:

+
var dt = event.dataTransfer;
+dt.mozSetDataAt("image/png", stream, 0);
+dt.mozSetDataAt("application/x-moz-file", file, 0);
+dt.setData("text/uri-list", imageurl);
+dt.setData("text/plain", imageurl);
+
+

Note that the mozGetDataAt method is used for non-text data. As some contexts may only include some of these types, it is important to check which type is made available when receiving dropped images.

+

Dragging Nodes

+

Nodes and elements in a document may be dragged using the application/x-moz-node type. This data for the type should be a DOM node. This allows the drop target to receive the actual node where the drag was started from. Note that callers from a different domain will not be able to access the node even when it has been dropped.

+

You should always include a plain text alternative for the node.

+

Dragging Custom Data

+

You can also use other types that you make up for custom purposes. You should strive to always include a plain text alternative unless that object being dragged is specific to a particular site or application. In this case, the custom type ensures that the data cannot be dropped elsewhere.

+

Dragging files to an operating system folder

+

There are cases in which you may want to add a file to an existing drag event session, and you may also want to write the file to disk when the drop operation happens over a folder in the operating system when your code receives notification of the target folder's location. This only works in extensions (or other privileged code) and the data flavor "application/moz-file-promise" should be used. The following sample offers an overview of this advanced case:

+
// currentEvent is a given existing drag operation event
+
+currentEvent.dataTransfer.setData("text/x-moz-url", URL);
+currentEvent.dataTransfer.setData("application/x-moz-file-promise-url", URL);
+currentEvent.dataTransfer.setData("application/x-moz-file-promise-filename", leafName);
+currentEvent.dataTransfer.mozSetDataAt('application/x-moz-file-promise',
+                  new dataProvider(success,error),
+                  0, Components.interfaces.nsISupports);
+
+function dataProvider(){}
+
+dataProvider.prototype = {
+  QueryInterface : function(iid) {
+    if (iid.equals(Components.interfaces.nsIFlavorDataProvider)
+                  || iid.equals(Components.interfaces.nsISupports))
+      return this;
+    throw Components.results.NS_NOINTERFACE;
+  },
+  getFlavorData : function(aTransferable, aFlavor, aData, aDataLen) {
+    if (aFlavor == 'application/x-moz-file-promise') {
+
+       var urlPrimitive = {};
+       var dataSize = {};
+
+       aTransferable.getTransferData('application/x-moz-file-promise-url', urlPrimitive, dataSize);
+       var url = new String(urlPrimitive.value.QueryInterface(Components.interfaces.nsISupportsString));
+       console.log("URL file orignal is = " + url);
+
+       var namePrimitive = {};
+       aTransferable.getTransferData('application/x-moz-file-promise-filename', namePrimitive, dataSize);
+       var name = new String(namePrimitive.value.QueryInterface(Components.interfaces.nsISupportsString));
+
+       console.log("target filename is = " + name);
+
+       var dirPrimitive = {};
+       aTransferable.getTransferData('application/x-moz-file-promise-dir', dirPrimitive, dataSize);
+       var dir = dirPrimitive.value.QueryInterface(Components.interfaces.nsILocalFile);
+
+       console.log("target folder is = " + dir.path);
+
+       var file = Cc['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
+       file.initWithPath(dir.path);
+       file.appendRelativePath(name);
+
+       console.log("output final path is =" + file.path);
+
+       // now you can write or copy the file yourself...
+    }
+  }
+}
+
+

{{ languages( { "ja": "Ja/DragDrop/Recommended_Drag_Types" } ) }}

diff --git a/files/es/web/api/htmlelement/accesskey/index.html b/files/es/web/api/htmlelement/accesskey/index.html new file mode 100644 index 0000000000..7d73c395fc --- /dev/null +++ b/files/es/web/api/htmlelement/accesskey/index.html @@ -0,0 +1,23 @@ +--- +title: Element.accessKey +slug: Web/API/Element/accessKey +tags: + - API + - Propiedad + - necesidades de contenido +translation_of: Web/API/HTMLElement/accessKey +translation_of_original: Web/API/Element/accessKey +--- +
{{APIRef("DOM")}}
+ +
 
+ +

La propiedad Element.accessKey establece la pulsación de teclado mediante el cual un usuario puede presionar para saltar a este elemento.

+ +
+

Nota: la propiedad Element.accessKey se usa raramente debido a sus múltiples conflictos con las asociaciones de teclas que ya están presentes  en los navegadores. Para evitar esto, los navegadores implementan el comportamiento tecla de acceso si se pulsan las claves con otras teclas "cualificadas" (como Alt + tecla de acceso).

+
+ +

 

+ +

 

diff --git a/files/es/web/api/htmlelement/animationend_event/index.html b/files/es/web/api/htmlelement/animationend_event/index.html new file mode 100644 index 0000000000..8bca8b046f --- /dev/null +++ b/files/es/web/api/htmlelement/animationend_event/index.html @@ -0,0 +1,81 @@ +--- +title: animationend +slug: Web/Events/animationend +translation_of: Web/API/HTMLElement/animationend_event +--- +

El evento animationend es lanzado cuando una animación CSS se ha completado.

+ +

Información General

+ +
+
Especificación
+
CSS Animations
+
Interface
+
AnimationEvent
+
Bubbles
+
Si
+
Cancelable
+
No
+
Target
+
Document, Element
+
Acción por defecto
+
None
+
+ +

Propiedades

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropiedadTipoDescripción
target {{ReadOnlyInline}}{{domxref("EventTarget")}}The event target (the topmost target in the DOM tree).
type {{ReadOnlyInline}}{{domxref("DOMString")}}The type of event.
bubbles {{ReadOnlyInline}}booleanDoes the event normally bubble?
cancelable {{ReadOnlyInline}}booleanIs it possible to cancel the event?
animationName {{ReadOnlyInline}}{{domxref("DOMString")}}The name of the CSS property associated with the transition.
elapsedTime {{ReadOnlyInline}}FloatThe amount of time the animation has been running, in seconds, when this event fired, excluding any time the animation was paused. For an "animationstart" event, the elapsedTime is zero unless there was a negative value for animation-delay, in which case the event will be fired with an elapsedTime of (-1 * delay).
+ +

Eventos relacionados

+ + + +

Ver también

+ + diff --git a/files/es/web/api/htmlelement/dataset/index.html b/files/es/web/api/htmlelement/dataset/index.html deleted file mode 100644 index 10c6f555f9..0000000000 --- a/files/es/web/api/htmlelement/dataset/index.html +++ /dev/null @@ -1,132 +0,0 @@ ---- -title: HTMLElement.dataset -slug: Web/API/HTMLElement/dataset -translation_of: Web/API/HTMLOrForeignElement/dataset ---- -
{{ APIRef("HTML DOM") }}
- -

La propiedad dataset en {{domxref("HTMLElement")}} proporciona una interfaz lectura/escritura para obtener todos los atributos de datos personalizados (data-*) de cada uno de los elementos. Está disponible el acceso en HTML y en el DOM.  Dentro del map of DOMString, aparece una entrada por cada atributo de datos. Hay que tener en cuenta que la propiedad dataset puede leerse, pero no modificarse directamente.  En vez de eso, las escrituras deben ser realizadas a través de cada propiedad individual del dataset, que representan a cada atributo correspondiente. Además un HTML data-attribute y su correspondiente DOM dataset.property no comparten el mismo nombre, pero son siempre similares:

- - - -

Adicionalmente, encontrarás una guía de como usar los atributos data de HTML en nuestro articulo Using data attributes.

- -

Conversion de nombres

- -

dash-style a camelCase: Un atributo de datos personalizado se transforma en una clave para la entrada {{ domxref("DOMStringMap") }} con las siguientes reglas

- - - -

camelCasedash-style: La conversión opuesta, mapea una clave en un nombre de atributo, usa las siguientes reglas:

- - - -

La restricción en las reglas anteriores aseguran que las dos conversiones sean inversas una a la otra.

- -

Por ejemplo, el atributo nombrado data-abc-def corresponde a la clave abcDef.

- - - -

Accediendo valores

- - - -

Definiendo valores

- - - -

Sintaxis

- - - -

Ejemplos

- -
<div id="user" data-id="1234567890" data-user="johndoe" data-date-of-birth>John Doe</div>
- -
const el = document.querySelector('#user');
-
-// el.id == 'user'
-// el.dataset.id === '1234567890'
-// el.dataset.user === 'johndoe'
-// el.dataset.dateOfBirth === ''
-
-// set the data attribute
-el.dataset.dateOfBirth = '1960-10-03';
-// Result: el.dataset.dateOfBirth === 1960-10-03
-
-delete el.dataset.dateOfBirth;
-// Result: el.dataset.dateOfBirth === undefined
-
-// 'someDataAttr' in el.dataset === false
-el.dataset.someDataAttr = 'mydata';
-// Result: 'someDataAttr' in el.dataset === true
-
- -

Especificaciones

- - - - - - - - - - - - - - - - - - - - - - - - -
EspecificaciónEstadoComentario
{{SpecName('HTML WHATWG', "dom.html#dom-dataset", "HTMLElement.dataset")}}{{Spec2('HTML WHATWG')}}No change from latest snapshot, {{SpecName('HTML5.1')}}
{{SpecName('HTML5.1', "dom.html#dom-dataset", "HTMLElement.dataset")}}{{Spec2('HTML5.1')}}Snapshot of {{SpecName('HTML WHATWG')}}, no change from {{SpecName('HTML5 W3C')}}
{{SpecName('HTML5 W3C', "dom.html#dom-dataset", "HTMLElement.dataset")}}{{Spec2('HTML5 W3C')}}Snapshot of  {{SpecName('HTML WHATWG')}}, initial definition.
- -

Compatibilidad en navegadores

- - - -

{{Compat("api.HTMLElement.dataset")}}

- -

Ver también

- - diff --git a/files/es/web/api/htmlelement/focus/index.html b/files/es/web/api/htmlelement/focus/index.html deleted file mode 100644 index d615cbf12e..0000000000 --- a/files/es/web/api/htmlelement/focus/index.html +++ /dev/null @@ -1,164 +0,0 @@ ---- -title: HTMLElement.focus() -slug: Web/API/HTMLElement/focus -tags: - - API - - HTML DOM - - HTMLElement - - Referencia - - metodo -translation_of: Web/API/HTMLOrForeignElement/focus ---- -
{{ APIRef("HTML DOM") }}
- -

El método HTMLElement.focus() fija el foco del cursor en el elemento indicado, si éste puede ser enfocado.

- -

Sintaxis

- -
element.focus();
-element.focus(focusOption); // Object parameter
- -

Parámetros

- -
-
focusOptions {{optional_inline}} {{experimental_inline}}
-
Es un objeto con la siguiente propiedad:
-
-
-
preventScroll {{optional_inline}}
-
Es un valor Boolean: -
    -
  • Si es false, el método hará scroll hasta que el elemento esté visible en la ventana del navegador
  • -
  • Si es true,  el método NO hará scroll hasta que el elemento esté visible en la ventana del navegador.
  • -
-
-
-
-
- -

Ejemplos

- -

Enfocar un campo de texto

- -

JavaScript

- -
focusMethod = function getFocus() {
-  document.getElementById("myTextField").focus();
-}
- -

HTML

- -
<input type="text" id="myTextField" value="Campo de texto.">
-<p></p>
-<button type="button" onclick="focusMethod()">¡Púlsame para enfocar el campo de texto!</button>
-
- -

Resultado

- -

{{ EmbedLiveSample('Focus_on_a_text_field') }}

- -

Enfocar un botón

- -

JavaScript

- -
focusMethod = function getFocus() {
-  document.getElementById("myButton").focus();
-}
-
- -

HTML

- -
<button type="button" id="myButton">Púlsame!</button>
-<p></p>
-<button type="button" onclick="focusMethod()">¡Púlsame para enfocar el botón!</button>
-
- -

Resultado

- -

{{ EmbedLiveSample('Focus_on_a_button') }}

- - - -

Enfocar con focusOption

- -

JavaScript

- -
focusScrollMethod = function getFocus() {
-  document.getElementById("myButton").focus({preventScroll:false});
-}
-focusNoScrollMethod = function getFocusWithoutScrolling() {
-  document.getElementById("myButton").focus({preventScroll:true});
-}
-
-
- -

HTML

- -
<button type="button" onclick="focusScrollMethod()">¡Púlsame para enfocar el botón!</button>
-<button type="button" onclick="focusNoScrollMethod()">¡Púlsame para enfocar el botón sin hacer scroll!</button>
-
-<div id="container" style="height: 1000px; width: 1000px;">
-<button type="button" id="myButton" style="margin-top: 500px;">¡Púlsame!</button>
-</div>
-
-
- -

Resultado

- -

{{ EmbedLiveSample('Focus_prevent_scroll') }}

- -

Especificación

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EspecificaciónEstadoComentarios
{{SpecName('HTML WHATWG', 'editing.html#dom-focus', 'focus')}}{{Spec2('HTML WHATWG')}}
{{SpecName('HTML5.1', 'editing.html#focus()-0', 'focus')}}{{Spec2('HTML5.1')}}
{{SpecName('HTML5 W3C', 'editing.html#dom-focus', 'focus')}}{{Spec2('HTML5 W3C')}}
{{SpecName('DOM2 HTML', 'html.html#ID-32130014', 'focus')}}{{Spec2('DOM2 HTML')}}
{{SpecName('DOM1', 'level-one-html.html#method-focus', 'focus')}}{{Spec2('DOM1')}}
- -

Notas

- -

Si se llama a HTMLElement.focus() desde un gestor de eventos "mousedown" (ratón presionado), se debe también llamar al método event.preventDefault() para evitar que el foco abandone HTMLElement.

- -

Compatibilidad en navegadores

- - - -

{{Compat("api.HTMLElement.focus")}}

- -

Ver también

- - diff --git a/files/es/web/api/htmlelement/style/index.html b/files/es/web/api/htmlelement/style/index.html deleted file mode 100644 index 62c8903b72..0000000000 --- a/files/es/web/api/htmlelement/style/index.html +++ /dev/null @@ -1,52 +0,0 @@ ---- -title: Element.style -slug: Web/API/HTMLElement/style -translation_of: Web/API/ElementCSSInlineStyle/style ---- -

{{ ApiRef("HTML DOM") }}

- -

Resumen

- -

Devuelve un objeto que representa el atributo style del elemento.

- -

Ejemplo

- -
var div = document.getElementById("div1");
-div.style.marginTop = ".25in";
-
- -

Notas

- -

Ya que la propiedad style tiene la misma (y más alta) prioridad en la cascada CSS que las declaraciones in-line hechas mediante el atributo style, resulta muy útil para establecer el estilo en un elemento específico. 

- -

Sin embargo, no resulta útil para aprender acerca del estilo original de un elemento, ya que representa sólo la declaración CSS en el atributo style in-line y no aquellos atributos que vienen de alguna otra parte, como las declaraciones en la sección <head> o en hojas de estilo.

- -

Para recoger los valores de todas las propiedades CSS de un elemento, deberías usar window.getComputedStyle en su lugar.

- -

Mira la lista de Propiedades CSS del DOM (DOM CSS Properties List) para tener una lista completa de las propiedades CSS que están disponibles en el Gecko DOM.

- -

Generalmente es mejor usarla propiedad style que usar elt.setAttribute('style', '...'), ya que el uso de la propiedad style no reemplazará otras propiedades CSS que puedan especificarse en el atributo style.

- -

Los estilos no pueden establecerse asignando una cadena a la propiedad (solo lectura) style, como en elt.style = "color: blue;". Esto es porque el atributo style devuelve un objeto del tipo CSSStyleDeclaration. En su lugar, pueds establecer las propiedades como:

- -
elt.style.color = "blue";  // Asignación directa
-
-var st = elt.style;
-st.color = "blue";  // Asignación Indirecta
-
- -

El siguiente código presenta los nombres de todas las propiedades style, los valores se establecen de forma explícita para los elementos elt y sus valores heredados: 

- -
var elt = document.getElementById("elementIdHere");
-var out = "";
-var st = elt.style;
-var cs = window.getComputedStyle(elt, null);
-for (x in st)
-  out += "  " + x + " = '" + st[x] + "' > '" + cs[x] + "'\n";
-
- -

 

- -

Especificación

- -

DOM Level 2 Style: ElementCSSInlineStyle.style

diff --git a/files/es/web/api/htmlelement/transitioncancel_event/index.html b/files/es/web/api/htmlelement/transitioncancel_event/index.html new file mode 100644 index 0000000000..3f9c622bd2 --- /dev/null +++ b/files/es/web/api/htmlelement/transitioncancel_event/index.html @@ -0,0 +1,163 @@ +--- +title: transitioncancel +slug: Web/Events/transitioncancel +tags: + - DOM + - Evento + - Referencia + - eventos +translation_of: Web/API/HTMLElement/transitioncancel_event +--- +

{{SeeCompatTable}}

+ +

El evento transitioncancel es lanzado cuando una transición CSS es cancelada.

+ +

Véase {{domxref("GlobalEventHandlers.ontransitioncancel")}} para mas información y ejemplos.

+ +

Información general

+ +
+
Interfaz
+
{{domxref("TransitionEvent")}}
+
Burbuja
+
+
Cancelable
+
No
+
Objetivo
+
{{domxref("document")}}, {{domxref("element")}}
+
Acción por defecto
+
Ninguna
+
+ +

Propiedades

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropiedadTipoDescripción
target {{readonlyinline}}{{domxref("EventTarget")}}El objetivo del evento (the topmost target in the DOM tree).
type {{readonlyinline}}{{domxref("DOMString")}}El tipo de evento.
bubbles {{readonlyinline}}{{domxref("Boolean")}}Si el evento normalmente se propaga o no
cancelable {{readonlyinline}}{{domxref("Boolean")}}Si el evento es cancelable o no
propertyName{{readonlyinline}}{{domxref("DOMString")}}El nombre de la propiedad CSS asociada con la transición.
elapsedTime{{readonlyinline}}{{domxref("Float")}} +

La cantidad de tiempo que ha durado la transición, en segundos, desde el momento en que el evento fué generado. Este valor no se ve afectado por el valor de transition-delay.

+
pseudoElement{{readonlyinline}}{{domxref("DOMString")}} +

El nombre (empezando con dos "dos puntos") del pseudo-elemento CSS en el que ha ocurrido la transición (en caso de que el objetivo del evento sea dicho pseudo-elemento correspondiente al elemento), o una cadena vacía si la transición ha ocurrido en un elemento (lo que quiere decir que el objetivo del evento es dicho elemento).

+
+ +

Especificaciones

+ + + + + + + + + + + + + + + + +
EspecificaciónEstadoComentario
{{SpecName('CSS3 Transitions', '#transitioncancel', 'transitioncancel')}}{{Spec2('CSS3 Transitions')}}Definición inicial.
+ +

Compatibilidad con navegadores

+ +

{{CompatibilityTable}}

+ +
+ + + + + + + + + + + + + + + + + + + +
CaracterísticaChromeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
Soporte básico{{CompatNo}}{{CompatGeckoDesktop(53)}}{{CompatNo}}{{CompatNo}}{{CompatNo}}
+
+ +

 

+ +
+ + + + + + + + + + + + + + + + + + + +
CaracterísticaAndroidFirefox Mobile (Gecko)IE PhoneOpera MobileSafari Mobile
Soporte básico{{CompatNo}}{{CompatGeckoMobile(53)}}{{CompatNo}}{{CompatNo}}{{CompatNo}}
+
+ +

Véase también

+ + diff --git a/files/es/web/api/htmlelement/transitionend_event/index.html b/files/es/web/api/htmlelement/transitionend_event/index.html new file mode 100644 index 0000000000..8370f0e39e --- /dev/null +++ b/files/es/web/api/htmlelement/transitionend_event/index.html @@ -0,0 +1,183 @@ +--- +title: transitionend +slug: Web/Events/transitionend +tags: + - DOM + - Event + - Referencia + - Transiciones CSS + - Transiciones CSS3 + - TransitionEvent + - transitionend +translation_of: Web/API/HTMLElement/transitionend_event +--- +

El evento transitionend es lanzado cuando una transición CSS se ha completado. Si la transición es eliminada antes de haberse completado, como cuando {{cssxref("transition-property")}} es eliminado o {{cssxref("display")}} se establece a "none", entonces el evento no será generado.

+ +

Información general

+ +
+
Especificación
+
{{SpecName("CSS3 Transitions")}}
+
Interfaz
+
{{domxref("TransitionEvent")}}
+
Burbuja
+
+
Cancelable
+
+
Objetivo
+
{{domxref("Element")}}, {{domxref("Document")}}, {{domxref("Window")}}
+
Acción
+
undefined
+
+ +

Propiedades

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyTypeDescription
target {{readonlyInline}}{{domxref("EventTarget")}}The event target (the topmost target in the DOM tree).
type {{readonlyInline}}{{domxref("DOMString")}}The type of event.
bubbles {{readonlyInline}}{{jsxref("Boolean")}}Whether the event normally bubbles or not.
cancelable {{readonlyInline}}{{jsxref("Boolean")}}Whether the event is cancellable or not.
propertyName {{readonlyInline}}{{domxref("DOMString")}}The name of the CSS property associated with the transition.
elapsedTime {{readonlyInline}}FloatThe amount of time the transition has been running, in seconds, as of the time the event was generated. This value is not affected by the value of transition-delay.
pseudoElement {{readonlyInline}}{{domxref("DOMString")}}The name (beginning with two colons) of the CSS pseudo-element on which the transition occured (in which case the target of the event is that pseudo-element's corresponding element), or the empty string if the transition occurred on an element (which means the target of the event is that element).
+ +

Ejemplo

+ +

Este ejemplo establece un manejador de evento para detectar el evento transitionend, y así cambiar el texto que se muestra dentro del elemento cuando la transición se completa.

+ +
let element = document.getElementById("slidingMenu");
+element.addEventListener("transitionend", function(event) {
+  element.innerHTML = "Done!";
+}, false);
+
+ +

Especificaciones

+ + + + + + + + + + + + + + + + +
EspecificaciónEstadoComentario
{{SpecName("CSS3 Transitions", "#transitionend", "transitionend")}}{{Spec2('CSS3 Transitions')}}Definición inicial.
+ +

Compatibilidad con navegadores

+ +

{{CompatibilityTable}}

+ +
+ + + + + + + + + + + + + + + + + + + + + +
CaracterísticaChromeEdgeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
Soporte básico1.0[1]
+ 36
{{CompatVersionUnknown}}{{CompatGeckoDesktop("2.0")}}1010.5[2]
+ 12
+ 12.10
+ 23
3.2[1]
+ 7.0.6
+
+ +
+ + + + + + + + + + + + + + + + + + + + + +
CaracterísticaAndroidEdgeFirefox Mobile (Gecko)IE PhoneOpera MobileSafari Mobile
Soporte básico2.1{{CompatVersionUnknown}}{{CompatGeckoMobile("2.0")}}{{CompatUnknown}}10[2]
+ 12
+ 12.10
3.2[1]
+
+ +

[1] Implementado en Chrome 1.0, Android 2.1 y WebKit 3.2 como webkitTransitionEnd. Chrome 36 y WebKit 7.0.6 usan el estándar transitionend.

+ +

[2] Implementado como oTransitionEnd desde Opera 10.5, como otransitionend desde la versión 12 y como el estándar transitionend desde la versión 12.10.

+ +

Véase también

+ +

La interfaz {{domxref("TransitionEvent")}}

+ + diff --git a/files/es/web/api/htmlmediaelement/abort_event/index.html b/files/es/web/api/htmlmediaelement/abort_event/index.html new file mode 100644 index 0000000000..641d144c77 --- /dev/null +++ b/files/es/web/api/htmlmediaelement/abort_event/index.html @@ -0,0 +1,68 @@ +--- +title: abort +slug: Web/Events/abort +translation_of: Web/API/HTMLMediaElement/abort_event +translation_of_original: Web/Events/abort +--- +

The abort event is fired when the loading of a resource has been aborted.

+ +

Información general

+ +
+
Specification
+
DOM L3
+
Interface
+
UIEvent if generated from a user interface, Event otherwise.
+
Bubbles
+
No
+
Cancelable
+
No
+
Target
+
Element
+
Default Action
+
None
+
+ +

Propiedades

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyTypeDescription
target {{readonlyInline}}EventTargetThe event target (the topmost target in the DOM tree).
type {{readonlyInline}}DOMStringThe type of event.
bubbles {{readonlyInline}}BooleanWhether the event normally bubbles or not.
cancelable {{readonlyInline}}BooleanWhether the event is cancellable or not.
view {{readonlyInline}}WindowProxydocument.defaultView (window of the document)
detail {{readonlyInline}}long (float)0.
diff --git a/files/es/web/api/htmlorforeignelement/dataset/index.html b/files/es/web/api/htmlorforeignelement/dataset/index.html new file mode 100644 index 0000000000..10c6f555f9 --- /dev/null +++ b/files/es/web/api/htmlorforeignelement/dataset/index.html @@ -0,0 +1,132 @@ +--- +title: HTMLElement.dataset +slug: Web/API/HTMLElement/dataset +translation_of: Web/API/HTMLOrForeignElement/dataset +--- +
{{ APIRef("HTML DOM") }}
+ +

La propiedad dataset en {{domxref("HTMLElement")}} proporciona una interfaz lectura/escritura para obtener todos los atributos de datos personalizados (data-*) de cada uno de los elementos. Está disponible el acceso en HTML y en el DOM.  Dentro del map of DOMString, aparece una entrada por cada atributo de datos. Hay que tener en cuenta que la propiedad dataset puede leerse, pero no modificarse directamente.  En vez de eso, las escrituras deben ser realizadas a través de cada propiedad individual del dataset, que representan a cada atributo correspondiente. Además un HTML data-attribute y su correspondiente DOM dataset.property no comparten el mismo nombre, pero son siempre similares:

+ + + +

Adicionalmente, encontrarás una guía de como usar los atributos data de HTML en nuestro articulo Using data attributes.

+ +

Conversion de nombres

+ +

dash-style a camelCase: Un atributo de datos personalizado se transforma en una clave para la entrada {{ domxref("DOMStringMap") }} con las siguientes reglas

+ + + +

camelCasedash-style: La conversión opuesta, mapea una clave en un nombre de atributo, usa las siguientes reglas:

+ + + +

La restricción en las reglas anteriores aseguran que las dos conversiones sean inversas una a la otra.

+ +

Por ejemplo, el atributo nombrado data-abc-def corresponde a la clave abcDef.

+ + + +

Accediendo valores

+ + + +

Definiendo valores

+ + + +

Sintaxis

+ + + +

Ejemplos

+ +
<div id="user" data-id="1234567890" data-user="johndoe" data-date-of-birth>John Doe</div>
+ +
const el = document.querySelector('#user');
+
+// el.id == 'user'
+// el.dataset.id === '1234567890'
+// el.dataset.user === 'johndoe'
+// el.dataset.dateOfBirth === ''
+
+// set the data attribute
+el.dataset.dateOfBirth = '1960-10-03';
+// Result: el.dataset.dateOfBirth === 1960-10-03
+
+delete el.dataset.dateOfBirth;
+// Result: el.dataset.dateOfBirth === undefined
+
+// 'someDataAttr' in el.dataset === false
+el.dataset.someDataAttr = 'mydata';
+// Result: 'someDataAttr' in el.dataset === true
+
+ +

Especificaciones

+ + + + + + + + + + + + + + + + + + + + + + + + +
EspecificaciónEstadoComentario
{{SpecName('HTML WHATWG', "dom.html#dom-dataset", "HTMLElement.dataset")}}{{Spec2('HTML WHATWG')}}No change from latest snapshot, {{SpecName('HTML5.1')}}
{{SpecName('HTML5.1', "dom.html#dom-dataset", "HTMLElement.dataset")}}{{Spec2('HTML5.1')}}Snapshot of {{SpecName('HTML WHATWG')}}, no change from {{SpecName('HTML5 W3C')}}
{{SpecName('HTML5 W3C', "dom.html#dom-dataset", "HTMLElement.dataset")}}{{Spec2('HTML5 W3C')}}Snapshot of  {{SpecName('HTML WHATWG')}}, initial definition.
+ +

Compatibilidad en navegadores

+ + + +

{{Compat("api.HTMLElement.dataset")}}

+ +

Ver también

+ + diff --git a/files/es/web/api/htmlorforeignelement/focus/index.html b/files/es/web/api/htmlorforeignelement/focus/index.html new file mode 100644 index 0000000000..d615cbf12e --- /dev/null +++ b/files/es/web/api/htmlorforeignelement/focus/index.html @@ -0,0 +1,164 @@ +--- +title: HTMLElement.focus() +slug: Web/API/HTMLElement/focus +tags: + - API + - HTML DOM + - HTMLElement + - Referencia + - metodo +translation_of: Web/API/HTMLOrForeignElement/focus +--- +
{{ APIRef("HTML DOM") }}
+ +

El método HTMLElement.focus() fija el foco del cursor en el elemento indicado, si éste puede ser enfocado.

+ +

Sintaxis

+ +
element.focus();
+element.focus(focusOption); // Object parameter
+ +

Parámetros

+ +
+
focusOptions {{optional_inline}} {{experimental_inline}}
+
Es un objeto con la siguiente propiedad:
+
+
+
preventScroll {{optional_inline}}
+
Es un valor Boolean: +
    +
  • Si es false, el método hará scroll hasta que el elemento esté visible en la ventana del navegador
  • +
  • Si es true,  el método NO hará scroll hasta que el elemento esté visible en la ventana del navegador.
  • +
+
+
+
+
+ +

Ejemplos

+ +

Enfocar un campo de texto

+ +

JavaScript

+ +
focusMethod = function getFocus() {
+  document.getElementById("myTextField").focus();
+}
+ +

HTML

+ +
<input type="text" id="myTextField" value="Campo de texto.">
+<p></p>
+<button type="button" onclick="focusMethod()">¡Púlsame para enfocar el campo de texto!</button>
+
+ +

Resultado

+ +

{{ EmbedLiveSample('Focus_on_a_text_field') }}

+ +

Enfocar un botón

+ +

JavaScript

+ +
focusMethod = function getFocus() {
+  document.getElementById("myButton").focus();
+}
+
+ +

HTML

+ +
<button type="button" id="myButton">Púlsame!</button>
+<p></p>
+<button type="button" onclick="focusMethod()">¡Púlsame para enfocar el botón!</button>
+
+ +

Resultado

+ +

{{ EmbedLiveSample('Focus_on_a_button') }}

+ + + +

Enfocar con focusOption

+ +

JavaScript

+ +
focusScrollMethod = function getFocus() {
+  document.getElementById("myButton").focus({preventScroll:false});
+}
+focusNoScrollMethod = function getFocusWithoutScrolling() {
+  document.getElementById("myButton").focus({preventScroll:true});
+}
+
+
+ +

HTML

+ +
<button type="button" onclick="focusScrollMethod()">¡Púlsame para enfocar el botón!</button>
+<button type="button" onclick="focusNoScrollMethod()">¡Púlsame para enfocar el botón sin hacer scroll!</button>
+
+<div id="container" style="height: 1000px; width: 1000px;">
+<button type="button" id="myButton" style="margin-top: 500px;">¡Púlsame!</button>
+</div>
+
+
+ +

Resultado

+ +

{{ EmbedLiveSample('Focus_prevent_scroll') }}

+ +

Especificación

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
EspecificaciónEstadoComentarios
{{SpecName('HTML WHATWG', 'editing.html#dom-focus', 'focus')}}{{Spec2('HTML WHATWG')}}
{{SpecName('HTML5.1', 'editing.html#focus()-0', 'focus')}}{{Spec2('HTML5.1')}}
{{SpecName('HTML5 W3C', 'editing.html#dom-focus', 'focus')}}{{Spec2('HTML5 W3C')}}
{{SpecName('DOM2 HTML', 'html.html#ID-32130014', 'focus')}}{{Spec2('DOM2 HTML')}}
{{SpecName('DOM1', 'level-one-html.html#method-focus', 'focus')}}{{Spec2('DOM1')}}
+ +

Notas

+ +

Si se llama a HTMLElement.focus() desde un gestor de eventos "mousedown" (ratón presionado), se debe también llamar al método event.preventDefault() para evitar que el foco abandone HTMLElement.

+ +

Compatibilidad en navegadores

+ + + +

{{Compat("api.HTMLElement.focus")}}

+ +

Ver también

+ + diff --git a/files/es/web/api/htmlvideoelement/index.html b/files/es/web/api/htmlvideoelement/index.html new file mode 100644 index 0000000000..4b36d9cba2 --- /dev/null +++ b/files/es/web/api/htmlvideoelement/index.html @@ -0,0 +1,202 @@ +--- +title: Elementos HTML para Video +slug: Web/API/ElementosHTMLparaVideo +translation_of: Web/API/HTMLVideoElement +--- +
+
{{APIRef("HTML DOM")}}
+
+ +

El interfaz de HTMLVideoElement provee propiedades especiales y metodos para manipular objectos de videos. Tambien, este interfaz hereda propiedades y métodos de {{domxref("HTMLMediaElement")}} y {{domxref("HTMLElement")}}.

+ +

La lista de supported media formats (formatos de medios compatibles) varía de un navegador a otro. Debe proveer su video en un formato sencillo que sea compatible con todos los navegadores o proveer varias fuentes de videos in varios formatos, para que así todo navegador que necesite este cubierto.

+ +

{{InheritanceDiagram(600, 140)}}

+ +

Propiedades

+ +

Hereda las propiedades de los interfaces anteriores, {{domxref("HTMLMediaElement")}}, y {{domxref("HTMLElement")}}.

+ +
+
{{domxref("HTMLVideoElement.height")}}
+
Es un {{domxref("DOMString")}} que refleja el atributo HTML {{htmlattrxref("height", "video")}}, el cual especifica la altura del area mostrada, en pixeles CSS.
+
{{domxref("HTMLVideoElement.poster")}}
+
Es un {{domxref("DOMString")}} que refleja el atributo HTML {{htmlattrxref("poster", "video")}}, el cual especifica que imagen sera mostrada en la ausencia de data de video.
+
{{domxref("HTMLVideoElement.videoHeight")}} {{readonlyInline}}
+
Devuelve un unsigned long que contiene la altura intrinsica del recurso en pixeles CSS, tomando en consideracion las dimensiones, aspecto proporcional, apertura limpia, resolucion, etc., ya definidas por el formato usado por el recurso.  Si el estado disponible del elemento es HAVE_NOTHING, su valor es 0.
+
{{domxref("HTMLVideoElement.videoWidth")}} {{readonlyInline}}
+
Devuelve un unsigned long que contiene la anchura intrinsica del recurso en pixeles CSS, tomando en consideracion las dimensiones, aspecto proporcional, apertura limpia, resolucion, etc., ya definidas por el formato usado por el recurso.  Si el estado disponible del elemento es HAVE_NOTHING, su valor es 0.
+
{{domxref("HTMLVideoElement.width")}}
+
Es un {{domxref("DOMString")}} que refleja el atributo HTML {{htmlattrxref("width", "video")}}, el cual especifica la anchura del area mostrada, en pixeles CSS.
+
+ +

Propiedades especificas para Gecko

+ +
+
{{domxref("HTMLVideoElement.mozParsedFrames")}} {{readonlyInline}}{{non-standard_inline}}
+
Devuelve un unsigned long con el conteo de marcos de video que han sido analizados del recurso de multimedia.
+
{{domxref("HTMLVideoElement.mozDecodedFrames")}} {{readonlyInline}}{{non-standard_inline}}
+
Devuelve un unsigned long con el conteo de marcos de video que han sido decifrados como imágines.
+
{{domxref("HTMLVideoElement.mozPresentedFrames")}} {{readonlyInline}}{{non-standard_inline}}
+
Devuelve un unsigned long con el conteo de marcos decodificados que han sido  presentados a la canalización de render para pintar.
+
{{domxref("HTMLVideoElement.mozPaintedFrames")}} {{readonlyInline}}{{non-standard_inline}}
+
Devuelve un unsigned long con el conteo de marcos presentados que han sido pintados en la pantalla.
+
{{domxref("HTMLVideoElement.mozFrameDelay")}} {{readonlyInline}}{{non-standard_inline}}
+
Devuelve un double con el tiempo, en segundos, que el último marco de video fue pintado por retrazo.
+
{{domxref("HTMLVideoElement.mozHasAudio")}} {{readonlyInline}}{{non-standard_inline}}
+
Devuelve un {{domxref("Boolean")}} indicando si existe algún audio asociado con el video.
+
+ +

Métodos

+ +

Hereda los métodos anteriores de {{domxref("HTMLMediaElement")}} y {{domxref("HTMLElement")}}.

+ +
+
{{domxref("HTMLVideoElement.getVideoPlaybackQuality()")}} {{experimental_inline}}
+
Devuelve un {{domxref("VideoPlaybackQuality")}} para objetos que contienen las medidas de reproducciones actuales.
+
+ +

Especificaciones

+ + + + + + + + + + + + + + + + + + + + + + + + +
EspecificacionEstadoComentario
{{SpecName('Media Source Extensions', '#idl-def-HTMLVideoElement', 'Extensions to HTMLVideoElement')}}{{Spec2("Media Source Extensions")}}Anadio el metodo getVideoPlaybackQuality() .
{{SpecName('HTML WHATWG', "the-video-element.html#the-video-element", "HTMLAreaElement")}}{{Spec2('HTML WHATWG')}}Sin cambios del {{SpecName('HTML5 W3C')}}.
{{SpecName('HTML5 W3C', "embedded-content-0.html#the-video-element", "HTMLAreaElement")}}{{Spec2('HTML5 W3C')}}Definicion incial.
+ +

Compatibilidad con Navegador

+ +

{{CompatibilityTable}}

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CaracteristicasChromeEdgeFirefox (Gecko)Internet ExplorerOperaSafari
Apoyo basico{{CompatVersionUnknown}}{{CompatVersionUnknown}}{{CompatGeckoDesktop("2.0")}}9.010.50{{CompatVersionUnknown}}
mozParsedFrames mozDecodedFrames mozPresentedFrames mozPaintedFrames mozFrameDelay {{non-standard_inline}}{{CompatNo}}{{CompatNo}}{{CompatGeckoDesktop("5.0")}}{{CompatNo}}{{CompatNo}}{{CompatNo}}
mozHasAudio {{non-standard_inline}}{{CompatNo}}{{CompatNo}}{{ CompatGeckoDesktop("15.0")}}{{CompatNo}}{{CompatNo}}{{CompatNo}}
getVideoPlaybackQuality(){{experimental_inline}}{{CompatUnknown}}{{CompatVersionUnknown}}{{ CompatGeckoDesktop("25.0")}}[1]{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CaracterísticasAndroidEdgeFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Apoyo básico{{CompatVersionUnknown}}{{CompatVersionUnknown}}{{CompatGeckoMobile("2.0")}}9.0{{CompatVersionUnknown}}{{CompatVersionUnknown}}
mozParsedFrames mozDecodedFrames mozPresentedFrames mozPaintedFrames mozFrameDelay {{non-standard_inline}}{{CompatNo}}{{CompatNo}}{{CompatGeckoMobile("5.0")}}{{CompatNo}}{{CompatNo}}{{CompatNo}}
mozHasAudio {{non-standard_inline}}{{CompatNo}}{{CompatNo}}{{ CompatGeckoMobile("15.0")}}{{CompatNo}}{{CompatNo}}{{CompatNo}}
getVideoPlaybackQuality(){{experimental_inline}}{{CompatUnknown}}{{CompatVersionUnknown}}{{ CompatGeckoMobile("25.0")}}[1]{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}
+
+ +

[1] Gecko implementa esto detras de la preferencia media.mediasource.enabled, predispuesto a false.

+ +

Lea Tambien

+ + diff --git a/files/es/web/api/indexeddb_api/basic_concepts_behind_indexeddb/index.html b/files/es/web/api/indexeddb_api/basic_concepts_behind_indexeddb/index.html new file mode 100644 index 0000000000..34d4fdd438 --- /dev/null +++ b/files/es/web/api/indexeddb_api/basic_concepts_behind_indexeddb/index.html @@ -0,0 +1,216 @@ +--- +title: Conceptos Básicos +slug: Web/API/IndexedDB_API/Conceptos_Basicos_Detras_De_IndexedDB +translation_of: Web/API/IndexedDB_API/Basic_Concepts_Behind_IndexedDB +--- +

IndexedDB es una manera de almacenar datos de manera persistente en el navegador. Dado que permite la creación de aplicaciones web con capacidades de consulta mejoradas, éstas pueden funcionar tanto en línea como fuera de línea. IndexedDB es útil para aplicaciones que almacenan una gran cantidad de datos (catálogos de DVDs en una biblioteca, por ejemplo) y para aplicaciones que no necesitan de una conexión permanente a internet para funcionar (clientes de correo, listas de tareas y blocs de notas, por ejemplo).

+ +

Sobre este documento

+ +

Esta introducción discute conceptos y terminologías fundamentales en IndexedDB. Provee una visión general y orienta sobre los conceptos clave. Para aprender más sobre los términos relacionados con IndexedDB, vea la sección de Definiciones.

+ +

Para un tutorial sobre cómo usar la API, vea Usando IndexedDB. Para ver la documentación de referencia sobre la API de IndexedDB, vea el artículo IndexedDB y sus sub-páginas, que documentan los tipos de objetos utilizados por IndexedDB, así como los métodos de las API síncrona y asíncrona.

+ +

Visión general de IndexedDB

+ +

IndexedDB le permite almacenar y obtener objetos indizados a partir de una "llave". Todos los cambios realizados a la base de datos ocurren dentro de transacciones. Como la mayoría de las soluciones de almacenamiento web, IndexedDB sigue una política de mismo origen, por lo que aún cuando se puede acceder a datos almacenados en un dominio, no se pueden acceder a datos a través de distintos dominios.

+ +

La API incluye una variante asíncrona y una síncrona. La variante asíncrona puede ser utilizada en la mayoría de los casos, incluyendo WebWorkers, mientras la variante síncrona está diseñada para ser utilizada solo con WebWorkers. Actualmente, ninguno de los navegadores principales soportan la API síncrona. Sin embargo, aún cuando la API síncrona sea soportada, en la mayoría de los casos de uso de IndexedDB lo más probable es que se utilice la API asíncrona.

+ +

IndexedDB es una alternativa a WebSQL, que fue marcada como obsoleta por W3C el 18 de Noviembre de 2010. Aún cuando IndexedDB y WebSQL son soluciones para el almacenamiento, éstas no ofrecen las mismas funcionalidades. WebSQL es un sistema relacional de acceso a datos, mientras que IndexedDB es un sistema de tablas indizadas.

+ +

Conceptos principales

+ +

Si usted tiene alguna idea de cómo trabajan otros tipos de bases de datos, podría tener algún conflicto de conceptos al trabajar con IndexedDB. Por esta razón mantenga los siguientes conceptos importantes en mente:

+ + + +

Definiciones

+ +

Esta sección define y explica los términos utilizados en la API de IndexedDB.

+ +

Base de Datos

+ +
+
base de datos
+
Un repositorio de información, típicamente compuesto de uno o más  almacenes de objetos. Cada base de datos debe tener: +
    +
  • Nombre. Identifica la base de datos dentro de un mismo origen y no cambia a lo largo de la existencia de ésta. El nombre puede ser cualquier cadena de caracteres (incluyendo una vacía).
  • +
  • +

    Versión actual. Cuando una base de datos se crea por primera vez, la versión es el número entero 1 si no se indica otra cosa. Cada base de datos puede tener una y sólo una versión en cualquier momento.

    +
  • +
+
+
almacén de objetos
+
+

El mecanismo a través del cual los datos son almacenados en la base de datos. El almacén de objetos mantiene registros de manera persistente, que son pares de llave-valor. Los registros dentro de un almacén de objetos son ordenados de acuerdo con las llaves en orden ascendente.

+ +

Todo almacén de objetos debe tener un nombre que es único a lo largo de su base de datos. Opcionalmente puede tener un generador de llaves y una ruta de llaves. Si el almacén tiene una ruta de llaves, éste utiliza llaves en línea; si no, utiliza llaves fuera de línea.

+ +

Para documentación de referencia sobre los almacenes de objetos, vew IDBObjectStore o IDBObjectStoreSync.

+
+
versión
+
Cuando una base de datos es creada por primera vez, su versión es 1. Cada base de datos tiene una versión en cualquier momento y no puede tener varias versiones al mismo tiempo. La única manera de cambiar la versión es abrir la base de datos con una versión mayor a la actual. Esto arranca una transacción versionchange y dispara el evento upgradeneeded. El único momento cuando se puede actualizar el esquema de la base de datos es dentro del manejador de este evento.
+
Nota: Esta definición describe la especificación más actual, que solo está implementada por las versiones más nuevas de los navegadores. Los navegadores más antiguos implementaron el método IDBDatabase.setVersion(), que ya ha sido marcado obsoleto y removido.
+
conexión a la base de datos
+
Una operación creada al abrir una base de datos. Una base de datos puede tener múltiples conexiónes al mismo tiempo.
+
transacción
+
+

Un conjunto atómico y durable de operaciónes de acceso y modificación de datos sobre una base de datos particular. Esta es la manera cómo se interactúa con los datos de una base de datos. De hecho, cualquier lectura o modificación de datos en la base de datos debe ocurrir dentro de una transacción.

+ +

Una conexión a la base de datos puede tener varias transacciones activas, siempre que las operaciones de escrituras no tengan ámbitos que se solapen. El ámbito de las transacciones, que es definido al momento en que éstas son creadas, determina con qué almacenes de datos puede interactuar ésta y permanece constante a lo largo de su existencia. Así, por ejemplo, si una conexión tiene una transacción escribiendo con un ámbito que abarca solo el almacén flyingMonkey, se puede iniciar una segunda que tenga como ámbito los almacenes unicornCentaur y unicornPegasus. En el caso de las transacciones de lectura, se pueden tener varias aún cuando se solapen.

+ +

Se espera que las transacciones tengan una existencia corta, de manera que el navegador puede cancelar una transacción que tome demasiado tiempo para liberar recursos que la misma tenga bloqueados. Usted puede abortar la transacción, lo que deshace los cambios hechos a la base de datos durante la misma. Ni siquiera es necesario esperar a que la transacción inicie para abortarla.

+ +

Los tres modos de transacción son: readwrite, readonly, y versionchange. La única manera de crear y borrar almacenes es usar una transacción versionchange. Para aprender más sobre los tipos de transacción, vea al artículo de referencia de IndexedDB.

+ +

Debido a que todo sucede dentro de una transacción, este es un concepto muy importante. Para aprender más sobre las transacciones, especialmente sobre como se relacionan con el versionado, vea IDBTransaction, que también cuenta con documentación de referencia. Para la documentación sobre la API asíncrona, vea IDBTransactionSync.

+
+
solicitud
+
La operación por medio de la cual se lee o se escribe en una base de datos. Toda solicitud representa una y solo una operación de lectura o escritura.
+
índice
+
+

Un almacén especializado para buscar registros en otro almacén, llamado almacén de objetos referenciado. El índice es un almacenamiento persistente llave-valor donde el valor es una llave del almacén referenciado. Los registros en un índice son rellenados automáticamente cada vez que se modifican los registros del almacén referenciado. Cada registro en un índice puede apuntar solo a un registro de su almacén referenciado, pero varios índices pueden apuntar al mismo almacén.

+ +

Alternativamente, se pueden realizar búsquedas en un almacén de objetos usando la llave.

+ +

Para aprender más sobre el uso de los índices, vea Usando IndexedDB. Para documentación de referencia, vea IDBKeyRange.

+
+
+ +

Llave y valor

+ +
+
llave
+
+

Es uno de los atributos del objeto a partir del cual los demás objetos son organizados y obtenidos desde el almacén de objetos. El almacén puede derivar una llave desde uno de tres orígenes: un generador de llaves, una ruta de llave, y un valor indicado de forma explícita. La llave debe ser de un tipo de datos que tenga un número creciente en relación con los objetos almacenados previamente. Cada registro en un almacén de datos debe tener una llave única en el almacén, de manera que no se pueden tener varios objetos con la misma llave en un almacén de objetos dado.

+ + +

Una llave puede ser de uno de los siguientes tipos: String, Date, float, y Array. Para los arreglos, la llave puede tener un rango desde un valor vacío hasta infinito, y puede incluirse un arreglo dentro de otro. No se requiere usar solo cadenas o enteros para las llaves {{ fx_minversion_inline("11") }}.

+ +

Como alternativa, se pueden realizar búsquedas de objetos usando un índice.

+
+
generador de llaves
+
Es un mecanismo para producir nuevas llaves en una secuencia ordenada. Si un almacén de objetos no tiene un generador de llaves, entonces la aplicación debe proveer llaves para los registros que se almacenen. Los generadores no son compartidos entre almacenes. Esto es un detalle de implementación de los navegadores, porque en desarrollo web, realmente no se crea o se accede a un generador de llaves.
+
llaves en línea
+
Es una llave que se almacena como parte del valor almacenado. La manera como se determina cuál es la llave es utilizando una  ruta de llave. Una llave en línea puede obtenerse con un generador. Después de que la llave ha sido generada, esta puede almacenarse en el valor usando la ruta del atributo o puede ser usada directamente como llave.
+
llave fuera de línea
+
Una llave que se almacena separada del valor.
+
ruta de llave
+
Define de dónde se debe extraer la llave desde un valor en el almacén o en un índice. Una ruta de llave válida puede incluir alguno de los siguientes: una cadena vacía, un identificador de JavaScript, o múltiples identificadores de JavaScript separados con puntos. No puede incluir espacios.
+
valor
+
+

Cada registro tiene un valor, el cual puede ser cualquier cosa que pueda ser expresada en JavaScript, incluyendo: booleanos, números, cadenas, fechas, objetos, arreglos, expresiones regulares, undefined, y null.

+ +

Cuando un objeto o un arreglo es almacenado, las propiedades y valores en ese objeto o arreglo pueden ser cualquier cosa que sea un valor válido.

+ +

Se pueden almacenar Blobs y archivos. cf. especificación {{ fx_minversion_inline("11") }}.

+
+
+ +

Rango y ámbito

+ +
+
ámbito
+
El conjunto de almacenes de objetos e índices en los que una transacción aplica. Los ámbitos de varias transacciones de solo lectura pueden solaparse y ejecutarse al mismo tiempo. En cambio, los ámbitos de transacciones de escritura no pueden solaparse. Aún así se pueden crear varias transacciones con el mismo ámbito al mismo tiempo, pero éstas serán manejadas por una cola y ejecutadas una detrás de otra.
+
cursor
+
Un mecanismo para iterar a través de múltiples registros con un rango de llaves. El cursor tiene un orígen que indica que índice o almacén de datos está iterando. El cursor tiene una posición dentro del rango, y se mueve en dirección creciente o decreciente en el orden de las llaves de cada registro. Para obtener documentación de referencia sobre cursores, vea IDBCursor o IDBCursorSync.
+
rango de llaves
+
+

Un intervalo continuo sobre algún tipo de datos utilizado para las llaves. Los registros pueden obtenerse desde los almacenes e índices usando llaves o un rango de llaves. Los rangos pueden limitarse o filtrarse con umbrales inferiores y superiores. Por ejemplo, se puede iterar sobre todos los valores de una llave desde x hasta y.

+ +

Para documentación de referencia sobre los rangos de llaves, vea IDBKeyRange.

+
+
+ +

Limitaciones

+ +

IndexedDB está diseñado para cubrir la mayoría de los casos en los que se necesita almacenamiento del lado del cliente. Sin embargo, no están contemplados en su diseño unos pocos casos como los siguientes:

+ + + +

Adicionalmente, tenga en cuenta que los navegadores podrían eliminar la base de datos, como en las siguientes condiciones:

+ + + +

Las circunstancias exactas y capacidades de los navegadores cambian con el tiempo, pero la filosofía de los navegadores es, en general, hacer lo posible por conservar los datos.

+ +
+

Advertencia: Al momento, debido a errores o a propósito, es imposible abrir una base de datos IndexedDB desde un Web App. Esto requiere mayor investigación para documentarlo.

+
+ +

Próximo paso

+ +

Ok, ahora con estos conceptos generales bajo nuestros cinturones, podemos seguir a cosas más concretas. Para un tutorial sobre como utilizar la API, vea Usando IndexedDB.

+ +

Vea también

+ +

Especificación

+ + + +

Referencia

+ + + +

Tutoriales

+ + + +

Artículo relacionado

+ + diff --git a/files/es/web/api/indexeddb_api/conceptos_basicos_detras_de_indexeddb/index.html b/files/es/web/api/indexeddb_api/conceptos_basicos_detras_de_indexeddb/index.html deleted file mode 100644 index 34d4fdd438..0000000000 --- a/files/es/web/api/indexeddb_api/conceptos_basicos_detras_de_indexeddb/index.html +++ /dev/null @@ -1,216 +0,0 @@ ---- -title: Conceptos Básicos -slug: Web/API/IndexedDB_API/Conceptos_Basicos_Detras_De_IndexedDB -translation_of: Web/API/IndexedDB_API/Basic_Concepts_Behind_IndexedDB ---- -

IndexedDB es una manera de almacenar datos de manera persistente en el navegador. Dado que permite la creación de aplicaciones web con capacidades de consulta mejoradas, éstas pueden funcionar tanto en línea como fuera de línea. IndexedDB es útil para aplicaciones que almacenan una gran cantidad de datos (catálogos de DVDs en una biblioteca, por ejemplo) y para aplicaciones que no necesitan de una conexión permanente a internet para funcionar (clientes de correo, listas de tareas y blocs de notas, por ejemplo).

- -

Sobre este documento

- -

Esta introducción discute conceptos y terminologías fundamentales en IndexedDB. Provee una visión general y orienta sobre los conceptos clave. Para aprender más sobre los términos relacionados con IndexedDB, vea la sección de Definiciones.

- -

Para un tutorial sobre cómo usar la API, vea Usando IndexedDB. Para ver la documentación de referencia sobre la API de IndexedDB, vea el artículo IndexedDB y sus sub-páginas, que documentan los tipos de objetos utilizados por IndexedDB, así como los métodos de las API síncrona y asíncrona.

- -

Visión general de IndexedDB

- -

IndexedDB le permite almacenar y obtener objetos indizados a partir de una "llave". Todos los cambios realizados a la base de datos ocurren dentro de transacciones. Como la mayoría de las soluciones de almacenamiento web, IndexedDB sigue una política de mismo origen, por lo que aún cuando se puede acceder a datos almacenados en un dominio, no se pueden acceder a datos a través de distintos dominios.

- -

La API incluye una variante asíncrona y una síncrona. La variante asíncrona puede ser utilizada en la mayoría de los casos, incluyendo WebWorkers, mientras la variante síncrona está diseñada para ser utilizada solo con WebWorkers. Actualmente, ninguno de los navegadores principales soportan la API síncrona. Sin embargo, aún cuando la API síncrona sea soportada, en la mayoría de los casos de uso de IndexedDB lo más probable es que se utilice la API asíncrona.

- -

IndexedDB es una alternativa a WebSQL, que fue marcada como obsoleta por W3C el 18 de Noviembre de 2010. Aún cuando IndexedDB y WebSQL son soluciones para el almacenamiento, éstas no ofrecen las mismas funcionalidades. WebSQL es un sistema relacional de acceso a datos, mientras que IndexedDB es un sistema de tablas indizadas.

- -

Conceptos principales

- -

Si usted tiene alguna idea de cómo trabajan otros tipos de bases de datos, podría tener algún conflicto de conceptos al trabajar con IndexedDB. Por esta razón mantenga los siguientes conceptos importantes en mente:

- - - -

Definiciones

- -

Esta sección define y explica los términos utilizados en la API de IndexedDB.

- -

Base de Datos

- -
-
base de datos
-
Un repositorio de información, típicamente compuesto de uno o más  almacenes de objetos. Cada base de datos debe tener: -
    -
  • Nombre. Identifica la base de datos dentro de un mismo origen y no cambia a lo largo de la existencia de ésta. El nombre puede ser cualquier cadena de caracteres (incluyendo una vacía).
  • -
  • -

    Versión actual. Cuando una base de datos se crea por primera vez, la versión es el número entero 1 si no se indica otra cosa. Cada base de datos puede tener una y sólo una versión en cualquier momento.

    -
  • -
-
-
almacén de objetos
-
-

El mecanismo a través del cual los datos son almacenados en la base de datos. El almacén de objetos mantiene registros de manera persistente, que son pares de llave-valor. Los registros dentro de un almacén de objetos son ordenados de acuerdo con las llaves en orden ascendente.

- -

Todo almacén de objetos debe tener un nombre que es único a lo largo de su base de datos. Opcionalmente puede tener un generador de llaves y una ruta de llaves. Si el almacén tiene una ruta de llaves, éste utiliza llaves en línea; si no, utiliza llaves fuera de línea.

- -

Para documentación de referencia sobre los almacenes de objetos, vew IDBObjectStore o IDBObjectStoreSync.

-
-
versión
-
Cuando una base de datos es creada por primera vez, su versión es 1. Cada base de datos tiene una versión en cualquier momento y no puede tener varias versiones al mismo tiempo. La única manera de cambiar la versión es abrir la base de datos con una versión mayor a la actual. Esto arranca una transacción versionchange y dispara el evento upgradeneeded. El único momento cuando se puede actualizar el esquema de la base de datos es dentro del manejador de este evento.
-
Nota: Esta definición describe la especificación más actual, que solo está implementada por las versiones más nuevas de los navegadores. Los navegadores más antiguos implementaron el método IDBDatabase.setVersion(), que ya ha sido marcado obsoleto y removido.
-
conexión a la base de datos
-
Una operación creada al abrir una base de datos. Una base de datos puede tener múltiples conexiónes al mismo tiempo.
-
transacción
-
-

Un conjunto atómico y durable de operaciónes de acceso y modificación de datos sobre una base de datos particular. Esta es la manera cómo se interactúa con los datos de una base de datos. De hecho, cualquier lectura o modificación de datos en la base de datos debe ocurrir dentro de una transacción.

- -

Una conexión a la base de datos puede tener varias transacciones activas, siempre que las operaciones de escrituras no tengan ámbitos que se solapen. El ámbito de las transacciones, que es definido al momento en que éstas son creadas, determina con qué almacenes de datos puede interactuar ésta y permanece constante a lo largo de su existencia. Así, por ejemplo, si una conexión tiene una transacción escribiendo con un ámbito que abarca solo el almacén flyingMonkey, se puede iniciar una segunda que tenga como ámbito los almacenes unicornCentaur y unicornPegasus. En el caso de las transacciones de lectura, se pueden tener varias aún cuando se solapen.

- -

Se espera que las transacciones tengan una existencia corta, de manera que el navegador puede cancelar una transacción que tome demasiado tiempo para liberar recursos que la misma tenga bloqueados. Usted puede abortar la transacción, lo que deshace los cambios hechos a la base de datos durante la misma. Ni siquiera es necesario esperar a que la transacción inicie para abortarla.

- -

Los tres modos de transacción son: readwrite, readonly, y versionchange. La única manera de crear y borrar almacenes es usar una transacción versionchange. Para aprender más sobre los tipos de transacción, vea al artículo de referencia de IndexedDB.

- -

Debido a que todo sucede dentro de una transacción, este es un concepto muy importante. Para aprender más sobre las transacciones, especialmente sobre como se relacionan con el versionado, vea IDBTransaction, que también cuenta con documentación de referencia. Para la documentación sobre la API asíncrona, vea IDBTransactionSync.

-
-
solicitud
-
La operación por medio de la cual se lee o se escribe en una base de datos. Toda solicitud representa una y solo una operación de lectura o escritura.
-
índice
-
-

Un almacén especializado para buscar registros en otro almacén, llamado almacén de objetos referenciado. El índice es un almacenamiento persistente llave-valor donde el valor es una llave del almacén referenciado. Los registros en un índice son rellenados automáticamente cada vez que se modifican los registros del almacén referenciado. Cada registro en un índice puede apuntar solo a un registro de su almacén referenciado, pero varios índices pueden apuntar al mismo almacén.

- -

Alternativamente, se pueden realizar búsquedas en un almacén de objetos usando la llave.

- -

Para aprender más sobre el uso de los índices, vea Usando IndexedDB. Para documentación de referencia, vea IDBKeyRange.

-
-
- -

Llave y valor

- -
-
llave
-
-

Es uno de los atributos del objeto a partir del cual los demás objetos son organizados y obtenidos desde el almacén de objetos. El almacén puede derivar una llave desde uno de tres orígenes: un generador de llaves, una ruta de llave, y un valor indicado de forma explícita. La llave debe ser de un tipo de datos que tenga un número creciente en relación con los objetos almacenados previamente. Cada registro en un almacén de datos debe tener una llave única en el almacén, de manera que no se pueden tener varios objetos con la misma llave en un almacén de objetos dado.

- - -

Una llave puede ser de uno de los siguientes tipos: String, Date, float, y Array. Para los arreglos, la llave puede tener un rango desde un valor vacío hasta infinito, y puede incluirse un arreglo dentro de otro. No se requiere usar solo cadenas o enteros para las llaves {{ fx_minversion_inline("11") }}.

- -

Como alternativa, se pueden realizar búsquedas de objetos usando un índice.

-
-
generador de llaves
-
Es un mecanismo para producir nuevas llaves en una secuencia ordenada. Si un almacén de objetos no tiene un generador de llaves, entonces la aplicación debe proveer llaves para los registros que se almacenen. Los generadores no son compartidos entre almacenes. Esto es un detalle de implementación de los navegadores, porque en desarrollo web, realmente no se crea o se accede a un generador de llaves.
-
llaves en línea
-
Es una llave que se almacena como parte del valor almacenado. La manera como se determina cuál es la llave es utilizando una  ruta de llave. Una llave en línea puede obtenerse con un generador. Después de que la llave ha sido generada, esta puede almacenarse en el valor usando la ruta del atributo o puede ser usada directamente como llave.
-
llave fuera de línea
-
Una llave que se almacena separada del valor.
-
ruta de llave
-
Define de dónde se debe extraer la llave desde un valor en el almacén o en un índice. Una ruta de llave válida puede incluir alguno de los siguientes: una cadena vacía, un identificador de JavaScript, o múltiples identificadores de JavaScript separados con puntos. No puede incluir espacios.
-
valor
-
-

Cada registro tiene un valor, el cual puede ser cualquier cosa que pueda ser expresada en JavaScript, incluyendo: booleanos, números, cadenas, fechas, objetos, arreglos, expresiones regulares, undefined, y null.

- -

Cuando un objeto o un arreglo es almacenado, las propiedades y valores en ese objeto o arreglo pueden ser cualquier cosa que sea un valor válido.

- -

Se pueden almacenar Blobs y archivos. cf. especificación {{ fx_minversion_inline("11") }}.

-
-
- -

Rango y ámbito

- -
-
ámbito
-
El conjunto de almacenes de objetos e índices en los que una transacción aplica. Los ámbitos de varias transacciones de solo lectura pueden solaparse y ejecutarse al mismo tiempo. En cambio, los ámbitos de transacciones de escritura no pueden solaparse. Aún así se pueden crear varias transacciones con el mismo ámbito al mismo tiempo, pero éstas serán manejadas por una cola y ejecutadas una detrás de otra.
-
cursor
-
Un mecanismo para iterar a través de múltiples registros con un rango de llaves. El cursor tiene un orígen que indica que índice o almacén de datos está iterando. El cursor tiene una posición dentro del rango, y se mueve en dirección creciente o decreciente en el orden de las llaves de cada registro. Para obtener documentación de referencia sobre cursores, vea IDBCursor o IDBCursorSync.
-
rango de llaves
-
-

Un intervalo continuo sobre algún tipo de datos utilizado para las llaves. Los registros pueden obtenerse desde los almacenes e índices usando llaves o un rango de llaves. Los rangos pueden limitarse o filtrarse con umbrales inferiores y superiores. Por ejemplo, se puede iterar sobre todos los valores de una llave desde x hasta y.

- -

Para documentación de referencia sobre los rangos de llaves, vea IDBKeyRange.

-
-
- -

Limitaciones

- -

IndexedDB está diseñado para cubrir la mayoría de los casos en los que se necesita almacenamiento del lado del cliente. Sin embargo, no están contemplados en su diseño unos pocos casos como los siguientes:

- - - -

Adicionalmente, tenga en cuenta que los navegadores podrían eliminar la base de datos, como en las siguientes condiciones:

- - - -

Las circunstancias exactas y capacidades de los navegadores cambian con el tiempo, pero la filosofía de los navegadores es, en general, hacer lo posible por conservar los datos.

- -
-

Advertencia: Al momento, debido a errores o a propósito, es imposible abrir una base de datos IndexedDB desde un Web App. Esto requiere mayor investigación para documentarlo.

-
- -

Próximo paso

- -

Ok, ahora con estos conceptos generales bajo nuestros cinturones, podemos seguir a cosas más concretas. Para un tutorial sobre como utilizar la API, vea Usando IndexedDB.

- -

Vea también

- -

Especificación

- - - -

Referencia

- - - -

Tutoriales

- - - -

Artículo relacionado

- - diff --git a/files/es/web/api/indexeddb_api/usando_indexeddb/index.html b/files/es/web/api/indexeddb_api/usando_indexeddb/index.html deleted file mode 100644 index ea9d2d879c..0000000000 --- a/files/es/web/api/indexeddb_api/usando_indexeddb/index.html +++ /dev/null @@ -1,1308 +0,0 @@ ---- -title: Usando IndexedDB -slug: Web/API/IndexedDB_API/Usando_IndexedDB -tags: - - API - - Almacenamiento - - Avanzado - - Base de datos - - Guía - - IndexedDB - - Tutorial - - jsstore -translation_of: Web/API/IndexedDB_API/Using_IndexedDB ---- -
-

IndexedDB es una manera de almacenar datos dentro del navegador del usuario. Debido a que permite la creación de aplicaciones con habilidades de consulta enriquecidas, con independencia de la disponibilidad de la red, sus aplicaciones pueden trabajar tanto en línea como fuera de línea.

-
- -

Acerca de este documento

- -

Este tutorial es una guía sobre el uso de la API asíncrona de IndexedDB. Si no está familiarizado con IndexedDB, por favor consulte primero Conceptos Básicos Acerca de IndexedDB.

- -

Para la documentación de referencia sobre la API de IndexedDB, vea el artículo IndexedDB y sus subpaginas, que documentan los tipos de objetos usados por IndexedDB, así como los métodos síncronos y asíncronos. 

- -

Patrones Básicos

- -

El patrón básico que indexedDB propone es:

- -
    -
  1. Abrir una base de datos.
  2. -
  3. Crear un objeto de almacenamiento en la base de datos.
  4. -
  5. Iniciar una transacción y hacer una petición para hacer alguna operación de la base de datos, tal como añadir o recuperar datos.
  6. -
  7. -
    Espere a que se complete la operación por la escucha de la clase correcta de eventos DOM .
    -
  8. -
  9. -
    Hacer algo con el resultado (El cual puede ser encontrado en el objeto de la petición).
    -
  10. -
- -

Con esos grandes rasgos en mente, seremos más concretos.

- -

Creando y estructurando el almacenamiento

- -

Como las especificaciones están todavía elaborandose, las implementaciones actuales de indexedDB dependen de los navegadores. Hasta que la especificación se haya consolidado, los proveedores de navegadores pueden tener diferentes implementaciones de los estandares de indexedDB. Una vez se alcance el consenso en el estandar, los proveedores implementarán la API sin prefijos. En algunas implementaciones ya fueron removidos los prefijos: Internet Explorer 10, Firefox 16, Chrome 24. Cuando utilizan un prefijo, los navegadores basados en gecko usan el prefijo moz , mientras que los navegadores basados en WebKit usan el prefijo webkit.

- -

Usando una versión experimental de IndexedDB

- -

En caso que usted quiera probar su código en navegadores que todavía usen un prefijo, puede usar el siguiente codigo:  

- -
// En la siguiente línea, puede incluir prefijos de implementación que quiera probar.
-window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
-// No use "var indexedDB = ..." Si no está en una función.
-// Por otra parte, puedes necesitar referencias a algun objeto window.IDB*:
-window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
-window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
-// (Mozilla nunca ha prefijado estos objetos, por lo tanto no necesitamos window.mozIDB*)
- -

Hay que tener cuidado con las implementaciones que usan un prefijo ya que puede ser inestables, incompletas, o usen una versión antigua de la especificación. En producción se recomienda usar el código sin prefijos. Es preferible no tener soporte para un navegador a decir que lo tiene y fallar en ello :

- -
if (!window.indexedDB) {
-    window.alert("Su navegador no soporta una versión estable de indexedDB. Tal y como las características no serán validas");
-}
-
- -

Abriendo una base de datos

- -

Iniciamos todo el proceso así:

- -
// dejamos abierta nuestra base de datos
-var request = window.indexedDB.open("MyTestDatabase", 3);
-
- -

¿Lo has visto? Abrir una base de datos es igual que cualquier otra operación — solo tienes que "solicitarla" (request).

- -

La solicitud de apertura no abre la base de datos o inicia la transacción de inmediato. La llamada a la función open() retornan unos objetos IDBOpenDBRequest, cuyo resultado, correcto o erróneo, se maneja en un evento.  Alguna otra función asincrónica en indexedDB hace lo mismo - Devolver un objeto IDBRequest que disparará un evento con el resultado o el error. El resultado para la función de abrir es una instancia de un IDBDatabase.

- -

El segundo parámetro para el método open es la versión de la base de datos. La versión de la base de datos determina el esquema - El almacen de objectos en la base de datos y su estructura. Si la base de datos no existe, es creada y se dispara un evento onupgradeneeded de inmediato, permitiéndote proveer una actualización de la estructura e índices en la función que capture dicho evento. Se verá más adelante en  Actualizando la versión de la base de datos

- -
-

Importante: El número de versión es un unsigned long. Por lo tanto significa que puede ser un entero muy grande. También significa que si usas un flotante será convertido en un entero más cercano y la transacción puede no ser iniciada, el evento upgradeneeded no se desencadenará. Por ejemplo no use 2.4 como un número de versión ya que será igual que la 2:

- -
var request = indexedDB.open("MyTestDatabase", 2.4); // Esto no se hace, la versión será redondeada a 2
-
- -

Generando manipuladores

- -

La primera cosa que usted querrá hacer con la totalidad de las peticiones que usted genera es agregar controladores de éxito y de error:

- -
request.onerror = function(event) {
-  // Hacer algo con request.errorCode!
-};
-request.onsuccess = function(event) {
-  // Hacer algo con request.result!
-};
- -

¿Cuál de las dos funciones, onSuccess () o onerror (), se vuelve a llamar? Si todo tiene éxito, un evento de éxito (es decir, un evento DOM cuya propiedad tipo se establece en el "éxito") se dispara con la solicitud como su objetivo. Una vez que se dispara, la función onSuccess () a petición se activa con el evento de éxito como argumento. De lo contrario, si había algún problema, un evento de error (es decir, un evento DOM cuyo tipo de propiedad se establece en "error") se dispara a petición. Esto desencadena la función onerror () con el evento de error como argumento.

- -

La API IndexedDB está diseñada para minimizar la necesidad de control de errores, por lo que no es probable que veamos muchos eventos de error (al menos, no una vez que estás acostumbrado a la API). En el caso de la apertura de una base de datos, sin embargo, hay algunas condiciones comunes que generan eventos de error. El problema más común se produce cuando el usuario ha decidido no dar, a su aplicación web, el permiso para crear una base de datos. Uno de los principales objetivos de diseño de IndexedDB es permitir que grandes cantidades de datos se almacenen para su uso sin conexión a internet. (Para obtener más información sobre la cantidad de almacenamiento que puede tener para cada navegador, consulte Límites de almacenamiento.)  

- -

Obviamente, los navegadores no permitirán que alguna red de publicidad o sitio web malicioso pueda contaminar su ordenador, por ello los navegadores utilizan un diálogo para indicar al usuario la primera vez que cualquier aplicación web determinada intente abrir una IndexedDB para el almacenamiento. El usuario puede optar por permitir o denegar el acceso. Además, el almacenamiento IndexedDB en los modos de privacidad navegadores sólo dura en memoria hasta que la sesión de incógnito haya sido cerrada (modo de navegación privada para el modo de Firefox e Incognito para Chrome, pero en Firefox no está implementado a partir de noviembre 2015 por lo que no puede utilizar IndexedDB en Firefox navegación privada en absoluto).

- -

Ahora, asumiendo que el usuario acepta su solicitud para crear una base de datos, y que ha recibido un evento de éxito para activar la devolución de llamada de éxito; ¿Que sigue? La solicitud aquí se generó con una llamada a indexedDB.open (), por lo request.result es una instancia de IDBDatabase, y que sin duda quieren ahorrar para más adelante. Su código podría ser algo como esto:

- -
var db;
-var request = indexedDB.open("MyTestDatabase");
-request.onerror = function(event) {
-  alert("Why didn't you allow my web app to use IndexedDB?!");
-};
-request.onsuccess = function(event) {
-  db = request.result;
-};
-
- -

Manejando errores

- -

Como se mencionó anteriormente, los eventos de error de burbujas. Eventos de error se dirigen a la solicitud que generó el error, entonces el evento se propaga a la operación, y finalmente con el objeto de base de datos. Si desea evitar la adición de controladores de errores a cada solicitud, en su lugar puede añadir un solo controlador de errores en el objeto de base de datos, así:

- -
db.onerror = function(event) {
-  // Generic error handler for all errors targeted at this database's
-  // requests!
-  alert("Database error: " + event.target.errorCode);
-};
-
- -

Uno de los errores más comunes posibles al abrir una base de datos es VER_ERR. Indica que la versión de la base de datos almacenada en el disco es mayor que la versión que está intentando abrir. Este es un caso de error que siempre debe ser manejado por el gestor de errores.

- -

Creación o actualización de la versión de la base de datos

- -

Cuando se crea una nueva base de datos o se aumenta el número de versión de una base de datos existente (mediante la especificación de un número de versión más alto de lo que hizo antes, en {{anch ("Cómo abrir una base de datos")}}), el evento onupgradeneeded se activará y un objeto IDBVersionChangeEvent será pasado a cualquier controlador de eventos onversionchange establecido en request.result (es decir, db en el ejemplo). En el controlador para el evento upgradeneeded, se debe crear los almacenes de objetos necesarios para esta versión de la base de datos:

- -
// Este evento solamente está implementado en navegadores recientes
-request.onupgradeneeded = function(event) {
-  var db = event.target.result;
-
-  // Crea un almacén de objetos (objectStore) para esta base de datos
-  var objectStore = db.createObjectStore("name", { keyPath: "myKey" });
-};
- -

En este caso, la base de datos ya tendrá los almacenes de objetos de la versión anterior de la base de datos, por lo que no tiene que crear estos almacenes de objetos de nuevo. Sólo es necesario crear nuevos almacenes de objetos, o eliminar las tiendas de objetos de la versión anterior que ya no son necesarios. Si necesita cambiar un almacén de objetos existentes (por ejemplo, para cambiar la ruta de acceso clave keyPath), entonces se debe eliminar el antiguo almacén de objetos y crear de nuevo con las nuevas opciones. (Tenga en cuenta que esto borrará la información en el almacén de objetos Si usted necesita guardar esa información, usted debe leerlo y guardarlo en otro lugar antes de actualizar la base de datos.)

- -

Tratar de crear un almacén de objetos con un nombre que ya existe (o tratando de eliminar un almacén de objetos con un nombre que no existe) lanzará un error.

- -

Si el evento onupgradeneeded retorna éxito, entonces se activará el manejador onsuccess de la solicitud de base de datos abierta.

- -

Blink / Webkit soportan la versión actual de la especificación, tal como fue liberado en Chrome 23+ y Opera 17+ ; IE10+ también lo soporta. Implementaciones mas viejas o distintas no implementan la versión actual de la especificación, y por lo tanto no son compatibles todavía con el indexedDB.open (nombre, versión).onupgradeneeded . Para obtener más información sobre cómo actualizar la versión de la base de datos en Webkit/Blink mas viejos, consulte el artículo de referencia IDBDatabase.

- -

Estructuración de la base de datos

- -

Ahora para estructurar la base de datos. IndexedDB usa almacenes de datos (object stores) en lugar de tablas, y una base de datos puede contener cualquier número de almacenes. Cuando un valor es almacenado, se le asocia con una clave. Existen diversas maneras en que una clave pude ser indicada dependiendo de si el almacén usa una ruta de clave o generador.

- -

La siguiente table muetra las distintas formas en que las claves pueden ser indicadas:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Ruta de clave(keyPath)Generador de clave (autoIncrement)Descripción
NoNoEste almacén puede contener cualquier tipo de valor, incluso valores primitivos como números y cadenas. Se debe indicar un argumento de clave distinto cada vez que se agregue un nuevo valor.
SiNoEste almacén de objetos solo puede contener objetos de JavaScript. Los objetos deben tener una propiedad con el mismo nombre que la ruta de clave.
NoSiEste objeto puede contener cualquier tipo de valor. La clave es generada automáticamente, o se puede indicar un argumento de clave distinto si se quiere usar una clave específica.
SiSi -

Este almacén de objetos solo puede contener objetos de JavaScript. Usualmente una clave es generada y dicho valor es almacenado en el objeto en una propiedad con el mismo nombre que la ruta de clave. Sin embargo, si dicha propiedad ya existe en el objeto, el valor de esa propuiedad es usado como clave en lugar de generar una nueva.

-
- -

También se puede crear índices en cualquer almacén de objetos, siempre y cuando el almacén contenga objets, y no primitivos. Un índice permite buscar valores contenidos en el almacén usando el valor de una propiedad del objeto almacenado, en lugar de la clave del mismo.

- -

Adicionalmente, los índices tienen la habilidad para hacer cumplir restricciones simples en los datos almacendos. Al indicar la bandera unique al crear el índice, el índice asegurará que no se puedan almacenar dos objetos que tengan el mismo valor para la clave del índice. Así, por ejemplo si se tiene un almacén de objetos que contiene un set de personas, y se desea asegurar que no existan dos personas con el mismo email, se puede usar un índice con la bandera unique activada para forzar esto.

- -

Esto puede sonar confuso, pero un ejemplo simple debe ilustrar el concepto. Primero, definiremos alguna información de clientes para usar en nuestro ejemplo:

- -
// Así se ve nuestra información de clientes.
-const customerData = [
-  { ssn: "444-44-4444", name: "Bill", age: 35, email: "bill@company.com" },
-  { ssn: "555-55-5555", name: "Donna", age: 32, email: "donna@home.org" }
-];
-
- -

Ahora, creemos una IndexedDB para almacenar los datos:

- -
const dbName = "the_name";
-
-var request = indexedDB.open(dbName, 2);
-
-request.onerror = function(event) {
-  // Manejar errores.
-};
-request.onupgradeneeded = function(event) {
-  var db = event.target.result;
-
-  // Se crea un almacén para contener la información de nuestros cliente
-  // Se usará "ssn" como clave ya que es garantizado que es única
-  var objectStore = db.createObjectStore("customers", { keyPath: "ssn" });
-
-  // Se crea un índice para buscar clientes por nombre. Se podrían tener duplicados
-  // por lo que no se puede usar un índice único.
-  objectStore.createIndex("name", "name", { unique: false });
-
-  // Se crea un índice para buscar clientespor email. Se quiere asegurar que
-  // no puedan haberdos clientes con el mismo email, asi que se usa un índice único.
-  objectStore.createIndex("email", "email", { unique: true });
-
-  // Se usa transaction.oncomplete para asegurarse que la creación del almacén
-  // haya finalizado antes de añadir los datos en el.
-  objectStore.transaction.oncomplete = function(event) {
-    // Guarda los datos en el almacén recién creado.
-    var customerObjectStore = db.transaction("customers", "readwrite").objectStore("customers");
-    for (var i in customerData) {
-      customerObjectStore.add(customerData[i]);
-    }
-  }
-};
-
- -

Como se indicó previamente, onupgradeneeded es el único lugar donde se puede alterar la estructura de la base de datos. En el, se puede crear y borrar almacenes de objetos y construir y remover índices.

- -
Los almacenes de datos son creados con una llamada a  createObjectStore(). El método toma como parámetros el nombre del almacén y un objeto.  A pesar de que el segundo parámetro es opcional, es muy importante, porque permite definir propiedades opcionales importantes y refinar el tipo de almacén que se desea crear. En este caso, se pregunta por un almacén llamado "customers" y se define la clave, que es la propiedad que indica que un objeto en el almacén es único. La propiedad en este ejemplo es "ssn" (Social Security Number) ya que los números de seguridad social está garantizado que sea único. "ssn" debe estar presente en cada objeto que se guarda al almacén.
- -

También se solicitó crear un índice llamado "name" que se fija en la propiedad name de los objetos almacenados. Así como con createObjectStore(), createIndex() toma un objeto opcional options que refina el tipo de índice que se desea crear. Agregar objetos que no tengan una propiedad name funcionará, pero los objetos no aparecerán en el índice "name"

- -
Ahora se pueden obtener los clientes almacenados usando su ssn directamente del almacen, o usando su nombre a través del índice. Para aprender como hacer esto, vea la sección El uso de un índice
- -

El uso de un generador de claves

- -

Indicar la bandera autoIncrement  cuando se crea el almacén habilitará el generador de claves para dicho almacén. Por defecto esta bandera no está marcada.

- -

Con el generador de claves, la clave será generada automáticamente a medida que se agreguen valores al almacén. El número actual de un generador de claves siempre se establece en 1 cuando se creal el almacén por primera vez. Básicamente la nueva clave autogenerada es incrementada en 1 basada en la llave anterior. El numero actual para un generador de claves nunca disminuye, salvo como resultado de operaciones de base de datos  que sean revertidos, por ejemplo, cuando la transacción de base de datos es abortada. Por lo tanto borrar un registro o incluso borrar todos los registros de un almacén nunca afecta al generador de claves

- -

Se puede crear otro almacén de objetos con generador de claves como se muestra abajo:

- -
// Abrir la indexedDB.
-var request = indexedDB.open(dbName, 3);
-
-request.onupgradeneeded = function (event) {
-
-    var db = event.target.result;
-
-    // Create another object store called "names" with the autoIncrement flag set as true.
-    var objStore = db.createObjectStore("names", { autoIncrement : true });
-
-    // Because the "names" object store has the key generator, the key for the name value is generated automatically.
-    // The added records would be like:
-    // key : 1 => value : "Bill"
-    // key : 2 => value : "Donna"
-    for (var i in customerData) {
-        objStore.add(customerData[i].name);
-    }
-}
- -

Para más detalles acerca del generador de claves, por favor ver "W3C Key Generators".

- -

Añadir, recuperación y eliminación de datos

- -

Antes que haga algo con su nueva base de datos , necesita comenzar una transacción. Transactions come from the database object, and you have to specify which object stores you want the transaction to span. Once you are inside the transaction, you can access the object stores that hold your data and make your requests. Next, you need to decide if you're going to make changes to the database or if you just need to read from it. Transactions have three available modes: readonly, readwrite, and versionchange.

- -

To change the "schema" or structure of the database—which involves creating or deleting object stores or indexes—the transaction must be in versionchange mode. This transaction is opened by calling the {{domxref("IDBFactory.open")}} method with a version specified. (In WebKit browsers, which have not implemented the latest specifcation, the {{domxref("IDBFactory.open")}} method takes only one parameter, the name of the database; then you must call {{domxref("IDBVersionChangeRequest.setVersion")}} to establish the versionchange transaction.)

- -

To read the records of an existing object store, the transaction can either be in readonly or readwrite mode. To make changes to an existing object store, the transaction must be in readwrite mode. You open such transactions with {{domxref("IDBDatabase.transaction")}}. The method accepts two parameters: the storeNames (the scope, defined as an array of object stores that you want to access) and the mode (readonly or readwrite) for the transaction. The method returns a transaction object containing the {{domxref("IDBIndex.objectStore")}} method, which you can use to access your object store. By default, where no mode is specified, transactions open in readonly mode.

- -

You can speed up data access by using the right scope and mode in the transaction. Here are a couple of tips:

- - - -

Agregar datos a la base de datos

- -

If you've just created a database, then you probably want to write to it. Here's what that looks like:

- -
var transaction = db.transaction(["customers"], "readwrite");
-// Note: Older experimental implementations use the deprecated constant IDBTransaction.READ_WRITE instead of "readwrite".
-// In case you want to support such an implementation, you can write:
-// var transaction = db.transaction(["customers"], IDBTransaction.READ_WRITE);
- -

The transaction() function takes two arguments (though one is optional) and returns a transaction object. The first argument is a list of object stores that the transaction will span. You can pass an empty array if you want the transaction to span all object stores, but don't do it because the spec says an empty array should generate an InvalidAccessError. If you don't specify anything for the second argument, you get a read-only transaction. Since you want to write to it here you need to pass the "readwrite" flag.

- -

Now that you have a transaction you need to understand its lifetime. Transactions are tied very closely to the event loop. If you make a transaction and return to the event loop without using it then the transaction will become inactive. The only way to keep the transaction active is to make a request on it. When the request is finished you'll get a DOM event and, assuming that the request succeeded, you'll have another opportunity to extend the transaction during that callback. If you return to the event loop without extending the transaction then it will become inactive, and so on. As long as there are pending requests the transaction remains active. Transaction lifetimes are really very simple but it might take a little time to get used to. A few more examples will help, too. If you start seeing TRANSACTION_INACTIVE_ERR error codes then you've messed something up.

- -

Transactions can receive DOM events of three different types: error, abort, and complete. We've talked about the way that error events bubble, so a transaction receives error events from any requests that are generated from it. A more subtle point here is that the default behavior of an error is to abort the transaction in which it occurred. Unless you handle the error by first calling preventDefault() on the error event then doing something else, the entire transaction is rolled back. This design forces you to think about and handle errors, but you can always add a catchall error handler to the database if fine-grained error handling is too cumbersome. If you don't handle an error event or if you call abort() on the transaction, then the transaction is rolled back and an abort event is fired on the transaction. Otherwise, after all pending requests have completed, you'll get a complete event. If you're doing lots of database operations, then tracking the transaction rather than individual requests can certainly aid your sanity.

- -

Now that you have a transaction, you'll need to get the object store from it. Transactions only let you have an object store that you specified when creating the transaction. Then you can add all the data you need.

- -
// Do something when all the data is added to the database.
-transaction.oncomplete = function(event) {
-  alert("All done!");
-};
-
-transaction.onerror = function(event) {
-  // Don't forget to handle errors!
-};
-
-var objectStore = transaction.objectStore("customers");
-for (var i in customerData) {
-  var request = objectStore.add(customerData[i]);
-  request.onsuccess = function(event) {
-    // event.target.result == customerData[i].ssn;
-  };
-}
- -

The result of a request generated from a call to add() is the key of the value that was added. So in this case, it should equal the ssn property of the object that was added, since the object store uses the ssn property for the key path. Note that the add() function requires that no object already be in the database with the same key. If you're trying to modify an existing entry, or you don't care if one exists already, you can use the put() function, as shown below in the {{ anch("Updating an entry in the database") }} section.

- -

Extracción de datos de la base de datos

- -

Removing data is very similar:

- -
var request = db.transaction(["customers"], "readwrite")
-                .objectStore("customers")
-                .delete("444-44-4444");
-request.onsuccess = function(event) {
-  // It's gone!
-};
- -

Obtener datos de la base de datos

- -

Now that the database has some info in it, you can retrieve it in several ways. First, the simple get(). You need to provide the key to retrieve the value, like so:

- -
var transaction = db.transaction(["customers"]);
-var objectStore = transaction.objectStore("customers");
-var request = objectStore.get("444-44-4444");
-request.onerror = function(event) {
-  // Handle errors!
-};
-request.onsuccess = function(event) {
-  // Do something with the request.result!
-  alert("Name for SSN 444-44-4444 is " + request.result.name);
-};
- -

That's a lot of code for a "simple" retrieval. Here's how you can shorten it up a bit, assuming that you handle errors at the database level:

- -
db.transaction("customers").objectStore("customers").get("444-44-4444").onsuccess = function(event) {
-  alert("Name for SSN 444-44-4444 is " + event.target.result.name);
-};
- -

See how this works? Since there's only one object store, you can avoid passing a list of object stores you need in your transaction and just pass the name as a string. Also, you're only reading from the database, so you don't need a "readwrite" transaction. Calling transaction() with no mode specified gives you a "readonly" transaction. Another subtlety here is that you don't actually save the request object to a variable. Since the DOM event has the request as its target you can use the event to get to the result property.

- -
-

Note: You can speed up data access by limiting the scope and mode in the transaction. Here are a couple of tips:

- - -
- -

Actualización de una entrada en la base de datos

- -

Now we've retrieved some data, updating it and inserting it back into the IndexedDB is pretty simple. Let's update the previous example somewhat:

- -
var objectStore = db.transaction(["customers"], "readwrite").objectStore("customers");
-var request = objectStore.get("444-44-4444");
-request.onerror = function(event) {
-  // Handle errors!
-};
-request.onsuccess = function(event) {
-  // Get the old value that we want to update
-  var data = request.result;
-
-  // update the value(s) in the object that you want to change
-  data.age = 42;
-
-  // Put this updated object back into the database.
-  var requestUpdate = objectStore.put(data);
-   requestUpdate.onerror = function(event) {
-     // Do something with the error
-   };
-   requestUpdate.onsuccess = function(event) {
-     // Success - the data is updated!
-   };
-};
- -

So here we're creating an objectStore and requesting a customer record out of it, identified by its ssn value (444-44-4444). We then put the result of that request in a variable (data), update the age property of this object, then create a second request (requestUpdate) to put the customer record back into the objectStore, overwriting the previous value.

- -
-

Note that in this case we've had to specify a readwrite transaction because we want to write to the database, not just read out of it.

-
- -

El uso de un cursor

- -

Using get() requires that you know which key you want to retrieve. If you want to step through all the values in your object store, then you can use a cursor. Here's what it looks like:

- -
var objectStore = db.transaction("customers").objectStore("customers");
-
-objectStore.openCursor().onsuccess = function(event) {
-  var cursor = event.target.result;
-  if (cursor) {
-    alert("Name for SSN " + cursor.key + " is " + cursor.value.name);
-    cursor.continue();
-  }
-  else {
-    alert("No more entries!");
-  }
-};
- -

The openCursor() function takes several arguments. First, you can limit the range of items that are retrieved by using a key range object that we'll get to in a minute. Second, you can specify the direction that you want to iterate. In the above example, we're iterating over all objects in ascending order. The success callback for cursors is a little special. The cursor object itself is the result of the request (above we're using the shorthand, so it's event.target.result). Then the actual key and value can be found on the key and value properties of the cursor object. If you want to keep going, then you have to call continue() on the cursor. When you've reached the end of the data (or if there were no entries that matched your openCursor() request) you still get a success callback, but the result property is undefined.

- -

One common pattern with cursors is to retrieve all objects in an object store and add them to an array, like this:

- -
var customers = [];
-
-objectStore.openCursor().onsuccess = function(event) {
-  var cursor = event.target.result;
-  if (cursor) {
-    customers.push(cursor.value);
-    cursor.continue();
-  }
-  else {
-    alert("Got all customers: " + customers);
-  }
-};
- -
-

Note: Mozilla has also implemented getAll() to handle this case (and getAllKeys(), which is currently hidden behind the dom.indexedDB.experimental preference in about:config). these aren't part of the IndexedDB standard, so may disappear in the future. We've included them because we think they're useful. The following code does precisely the same thing as above:

- -
objectStore.getAll().onsuccess = function(event) {
-  alert("Got all customers: " + event.target.result);
-};
- -

There is a performance cost associated with looking at the value property of a cursor, because the object is created lazily. When you use getAll() for example, Gecko must create all the objects at once. If you're just interested in looking at each of the keys, for instance, it is much more efficient to use a cursor than to use getAll(). If you're trying to get an array of all the objects in an object store, though, use getAll().

-
- -

El uso de un índice

- -

Storing customer data using the SSN as a key is logical since the SSN uniquely identifies an individual. (Whether this is a good idea for privacy is a different question, and outside the scope of this article.) If you need to look up a customer by name, however, you'll need to iterate over every SSN in the database until you find the right one. Searching in this fashion would be very slow, so instead you can use an index.

- -
var index = objectStore.index("name");
-index.get("Donna").onsuccess = function(event) {
-  alert("Donna's SSN is " + event.target.result.ssn);
-};
- -

The "name" cursor isn't unique, so there could be more than one entry with the name set to "Donna". In that case you always get the one with the lowest key value.

- -

If you need to access all the entries with a given name you can use a cursor. You can open two different types of cursors on indexes. A normal cursor maps the index property to the object in the object store. A key cursor maps the index property to the key used to store the object in the object store. The differences are illustrated here:

- -
// Using a normal cursor to grab whole customer record objects
-index.openCursor().onsuccess = function(event) {
-  var cursor = event.target.result;
-  if (cursor) {
-    // cursor.key is a name, like "Bill", and cursor.value is the whole object.
-    alert("Name: " + cursor.key + ", SSN: " + cursor.value.ssn + ", email: " + cursor.value.email);
-    cursor.continue();
-  }
-};
-
-// Using a key cursor to grab customer record object keys
-index.openKeyCursor().onsuccess = function(event) {
-  var cursor = event.target.result;
-  if (cursor) {
-    // cursor.key is a name, like "Bill", and cursor.value is the SSN.
-    // No way to directly get the rest of the stored object.
-    alert("Name: " + cursor.key + ", SSN: " + cursor.value);
-    cursor.continue();
-  }
-};
- -

Especificación de la gama y la dirección de los cursores

- -

If you would like to limit the range of values you see in a cursor, you can use an IDBKeyRange object and pass it as the first argument to openCursor() or openKeyCursor(). You can make a key range that only allows a single key, or one that has a lower or upper bound, or one that has both a lower and upper bound. The bound may be "closed" (i.e., the key range includes the given value(s)) or "open" (i.e., the key range does not include the given value(s)). Here's how it works:

- -
// Only match "Donna"
-var singleKeyRange = IDBKeyRange.only("Donna");
-
-// Match anything past "Bill", including "Bill"
-var lowerBoundKeyRange = IDBKeyRange.lowerBound("Bill");
-
-// Match anything past "Bill", but don't include "Bill"
-var lowerBoundOpenKeyRange = IDBKeyRange.lowerBound("Bill", true);
-
-// Match anything up to, but not including, "Donna"
-var upperBoundOpenKeyRange = IDBKeyRange.upperBound("Donna", true);
-
-// Match anything between "Bill" and "Donna", but not including "Donna"
-var boundKeyRange = IDBKeyRange.bound("Bill", "Donna", false, true);
-
-// To use one of the key ranges, pass it in as the first argument of openCursor()/openKeyCursor()
-index.openCursor(boundKeyRange).onsuccess = function(event) {
-  var cursor = event.target.result;
-  if (cursor) {
-    // Do something with the matches.
-    cursor.continue();
-  }
-};
- -

Sometimes you may want to iterate in descending order rather than in ascending order (the default direction for all cursors). Switching direction is accomplished by passing prev to the openCursor() function as the second argument:

- -
objectStore.openCursor(boundKeyRange, "prev").onsuccess = function(event) {
-  var cursor = event.target.result;
-  if (cursor) {
-    // Do something with the entries.
-    cursor.continue();
-  }
-};
- -

If you just want to specify a change of direction but not constrain the results shown, you can just pass in null as the first argument:

- -
objectStore.openCursor(null, "prev").onsuccess = function(event) {
-  var cursor = event.target.result;
-  if (cursor) {
-    // Do something with the entries.
-    cursor.continue();
-  }
-};
- -

Since the "name" index isn't unique, there might be multiple entries where name is the same. Note that such a situation cannot occur with object stores since the key must always be unique. If you wish to filter out duplicates during cursor iteration over indexes, you can pass nextunique (or prevunique if you're going backwards) as the direction parameter. When nextunique or prevunique is used, the entry with the lowest key is always the one returned.

- -
index.openKeyCursor(null, "nextunique").onsuccess = function(event) {
-  var cursor = event.target.result;
-  if (cursor) {
-    // Do something with the entries.
-    cursor.continue();
-  }
-};
- -

Please see "IDBCursor Constants" for the valid direction arguments.

- -

Cambios Versión mientras que una aplicación web está abierto en otra pestaña

- -

When your web app changes in such a way that a version change is required for your database, you need to consider what happens if the user has the old version of your app open in one tab and then loads the new version of your app in another. When you call open() with a greater version than the actual version of the database, all other open databases must explicitly acknowledge the request before you can start making changes to the database (an onblocked event is fired until tey are closed or reloaded). Here's how it works:

- -
var openReq = mozIndexedDB.open("MyTestDatabase", 2);
-
-openReq.onblocked = function(event) {
-  // If some other tab is loaded with the database, then it needs to be closed
-  // before we can proceed.
-  alert("Please close all other tabs with this site open!");
-};
-
-openReq.onupgradeneeded = function(event) {
-  // All other databases have been closed. Set everything up.
-  db.createObjectStore(/* ... */);
-  useDatabase(db);
-}
-
-openReq.onsuccess = function(event) {
-  var db = event.target.result;
-  useDatabase(db);
-  return;
-}
-
-function useDatabase(db) {
-  // Make sure to add a handler to be notified if another page requests a version
-  // change. We must close the database. This allows the other page to upgrade the database.
-  // If you don't do this then the upgrade won't happen until the user closes the tab.
-  db.onversionchange = function(event) {
-    db.close();
-    alert("A new version of this page is ready. Please reload!");
-  };
-
-  // Do stuff with the database.
-}
-
- -

Seguridad

- -

IndexedDB uses the same-origin principle, which means that it ties the store to the origin of the site that creates it (typically, this is the site domain or subdomain), so it cannot be accessed by any other origin.

- -

It's important to note that IndexedDB doesn't work for content loaded into a frame from another site (either {{ HTMLElement("frame") }} or {{ HTMLElement("iframe") }}. This is a security and privacy measure and can be considered analogous the blocking of third-party cookies. For more details, see {{ bug(595307) }}.

- -

Warning About Browser Shutdown

- -

When the browser shuts down (e.g., when the user selects Exit or clicks the Close button), any pending IndexedDB transactions are (silently) aborted — they will not complete, and they will not trigger the error handler. Since the user can exit the browser at any time, this means that you cannot rely upon any particular transaction to complete or to know that it did not complete. There are several implications of this behavior.

- -

First, you should take care to always leave your database in a consistent state at the end of every transaction. For example, suppose that you are using IndexedDB to store a list of items that you allow the user to edit. You save the list after the edit by clearing the object store and then writing out the new list. If you clear the object store in one transaction and write the new list in another transaction, there is a danger that the browser will close after the clear but before the write, leaving you with an empty database. To avoid this, you should combine the clear and the write into a single transaction. 

- -

Second, you should never tie database transactions to unload events. If the unload event is triggered by the browser closing, any transactions created in the unload event handler will never complete. An intuitive approach to maintaining some information across browser sessions is to read it from the database when the browser (or a particular page) is opened, update it as the user interacts with the browser, and then save it to the database when the browser (or page) closes. However, this will not work. The database transactions will be created in the unload event handler, but because they are asynchronous they will be aborted before they can execute.

- -

In fact, there is no way to guarantee that IndexedDB transactions will complete, even with normal browser shutdown. See {{ bug(870645) }}.

- -

Full IndexedDB example

- -

HTML Content

- -
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
-
-    <h1>IndexedDB Demo: storing blobs, e-publication example</h1>
-    <div class="note">
-      <p>
-        Works and tested with:
-      </p>
-      <div id="compat">
-      </div>
-    </div>
-
-    <div id="msg">
-    </div>
-
-    <form id="register-form">
-      <table>
-        <tbody>
-          <tr>
-            <td>
-              <label for="pub-title" class="required">
-                Title:
-              </label>
-            </td>
-            <td>
-              <input type="text" id="pub-title" name="pub-title" />
-            </td>
-          </tr>
-          <tr>
-            <td>
-              <label for="pub-biblioid" class="required">
-                Bibliographic ID:<br/>
-                <span class="note">(ISBN, ISSN, etc.)</span>
-              </label>
-            </td>
-            <td>
-              <input type="text" id="pub-biblioid" name="pub-biblioid"/>
-            </td>
-          </tr>
-          <tr>
-            <td>
-              <label for="pub-year">
-                Year:
-              </label>
-            </td>
-            <td>
-              <input type="number" id="pub-year" name="pub-year" />
-            </td>
-          </tr>
-        </tbody>
-        <tbody>
-          <tr>
-            <td>
-              <label for="pub-file">
-                File image:
-              </label>
-            </td>
-            <td>
-              <input type="file" id="pub-file"/>
-            </td>
-          </tr>
-          <tr>
-            <td>
-              <label for="pub-file-url">
-                Online-file image URL:<br/>
-                <span class="note">(same origin URL)</span>
-              </label>
-            </td>
-            <td>
-              <input type="text" id="pub-file-url" name="pub-file-url"/>
-            </td>
-          </tr>
-        </tbody>
-      </table>
-
-      <div class="button-pane">
-        <input type="button" id="add-button" value="Add Publication" />
-        <input type="reset" id="register-form-reset"/>
-      </div>
-    </form>
-
-    <form id="delete-form">
-      <table>
-        <tbody>
-          <tr>
-            <td>
-              <label for="pub-biblioid-to-delete">
-                Bibliographic ID:<br/>
-                <span class="note">(ISBN, ISSN, etc.)</span>
-              </label>
-            </td>
-            <td>
-              <input type="text" id="pub-biblioid-to-delete"
-                     name="pub-biblioid-to-delete" />
-            </td>
-          </tr>
-          <tr>
-            <td>
-              <label for="key-to-delete">
-                Key:<br/>
-                <span class="note">(for example 1, 2, 3, etc.)</span>
-              </label>
-            </td>
-            <td>
-              <input type="text" id="key-to-delete"
-                     name="key-to-delete" />
-            </td>
-          </tr>
-        </tbody>
-      </table>
-      <div class="button-pane">
-        <input type="button" id="delete-button" value="Delete Publication" />
-        <input type="button" id="clear-store-button"
-               value="Clear the whole store" class="destructive" />
-      </div>
-    </form>
-
-    <form id="search-form">
-      <div class="button-pane">
-        <input type="button" id="search-list-button"
-               value="List database content" />
-      </div>
-    </form>
-
-    <div>
-      <div id="pub-msg">
-      </div>
-      <div id="pub-viewer">
-      </div>
-      <ul id="pub-list">
-      </ul>
-    </div>
-
- -

CSS Content

- -
body {
-  font-size: 0.8em;
-  font-family: Sans-Serif;
-}
-
-form {
-  background-color: #cccccc;
-  border-radius: 0.3em;
-  display: inline-block;
-  margin-bottom: 0.5em;
-  padding: 1em;
-}
-
-table {
-  border-collapse: collapse;
-}
-
-input {
-  padding: 0.3em;
-  border-color: #cccccc;
-  border-radius: 0.3em;
-}
-
-.required:after {
-  content: "*";
-  color: red;
-}
-
-.button-pane {
-  margin-top: 1em;
-}
-
-#pub-viewer {
-  float: right;
-  width: 48%;
-  height: 20em;
-  border: solid #d092ff 0.1em;
-}
-#pub-viewer iframe {
-  width: 100%;
-  height: 100%;
-}
-
-#pub-list {
-  width: 46%;
-  background-color: #eeeeee;
-  border-radius: 0.3em;
-}
-#pub-list li {
-  padding-top: 0.5em;
-  padding-bottom: 0.5em;
-  padding-right: 0.5em;
-}
-
-#msg {
-  margin-bottom: 1em;
-}
-
-.action-success {
-  padding: 0.5em;
-  color: #00d21e;
-  background-color: #eeeeee;
-  border-radius: 0.2em;
-}
-
-.action-failure {
-  padding: 0.5em;
-  color: #ff1408;
-  background-color: #eeeeee;
-  border-radius: 0.2em;
-}
-
-.note {
-  font-size: smaller;
-}
-
-.destructive {
-  background-color: orange;
-}
-.destructive:hover {
-  background-color: #ff8000;
-}
-.destructive:active {
-  background-color: red;
-}
-
- -

 

- -

JavaScript Content

- -
(function () {
-  var COMPAT_ENVS = [
-    ['Firefox', ">= 16.0"],
-    ['Google Chrome',
-     ">= 24.0 (you may need to get Google Chrome Canary), NO Blob storage support"]
-  ];
-  var compat = $('#compat');
-  compat.empty();
-  compat.append('<ul id="compat-list"></ul>');
-  COMPAT_ENVS.forEach(function(val, idx, array) {
-    $('#compat-list').append('<li>' + val[0] + ': ' + val[1] + '</li>');
-  });
-
-  const DB_NAME = 'mdn-demo-indexeddb-epublications';
-  const DB_VERSION = 1; // Use a long long for this value (don't use a float)
-  const DB_STORE_NAME = 'publications';
-
-  var db;
-
-  // Used to keep track of which view is displayed to avoid uselessly reloading it
-  var current_view_pub_key;
-
-  function openDb() {
-    console.log("openDb ...");
-    var req = indexedDB.open(DB_NAME, DB_VERSION);
-    req.onsuccess = function (evt) {
-      // Better use "this" than "req" to get the result to avoid problems with
-      // garbage collection.
-      // db = req.result;
-      db = this.result;
-      console.log("openDb DONE");
-    };
-    req.onerror = function (evt) {
-      console.error("openDb:", evt.target.errorCode);
-    };
-
-    req.onupgradeneeded = function (evt) {
-      console.log("openDb.onupgradeneeded");
-      var store = evt.currentTarget.result.createObjectStore(
-        DB_STORE_NAME, { keyPath: 'id', autoIncrement: true });
-
-      store.createIndex('biblioid', 'biblioid', { unique: true });
-      store.createIndex('title', 'title', { unique: false });
-      store.createIndex('year', 'year', { unique: false });
-    };
-  }
-
-  /**
-   * @param {string} store_name
-   * @param {string} mode either "readonly" or "readwrite"
-   */
-  function getObjectStore(store_name, mode) {
-    var tx = db.transaction(store_name, mode);
-    return tx.objectStore(store_name);
-  }
-
-  function clearObjectStore(store_name) {
-    var store = getObjectStore(DB_STORE_NAME, 'readwrite');
-    var req = store.clear();
-    req.onsuccess = function(evt) {
-      displayActionSuccess("Store cleared");
-      displayPubList(store);
-    };
-    req.onerror = function (evt) {
-      console.error("clearObjectStore:", evt.target.errorCode);
-      displayActionFailure(this.error);
-    };
-  }
-
-  function getBlob(key, store, success_callback) {
-    var req = store.get(key);
-    req.onsuccess = function(evt) {
-      var value = evt.target.result;
-      if (value)
-        success_callback(value.blob);
-    };
-  }
-
-  /**
-   * @param {IDBObjectStore=} store
-   */
-  function displayPubList(store) {
-    console.log("displayPubList");
-
-    if (typeof store == 'undefined')
-      store = getObjectStore(DB_STORE_NAME, 'readonly');
-
-    var pub_msg = $('#pub-msg');
-    pub_msg.empty();
-    var pub_list = $('#pub-list');
-    pub_list.empty();
-    // Resetting the iframe so that it doesn't display previous content
-    newViewerFrame();
-
-    var req;
-    req = store.count();
-    // Requests are executed in the order in which they were made against the
-    // transaction, and their results are returned in the same order.
-    // Thus the count text below will be displayed before the actual pub list
-    // (not that it is algorithmically important in this case).
-    req.onsuccess = function(evt) {
-      pub_msg.append('<p>There are <strong>' + evt.target.result +
-                     '</strong> record(s) in the object store.</p>');
-    };
-    req.onerror = function(evt) {
-      console.error("add error", this.error);
-      displayActionFailure(this.error);
-    };
-
-    var i = 0;
-    req = store.openCursor();
-    req.onsuccess = function(evt) {
-      var cursor = evt.target.result;
-
-      // If the cursor is pointing at something, ask for the data
-      if (cursor) {
-        console.log("displayPubList cursor:", cursor);
-        req = store.get(cursor.key);
-        req.onsuccess = function (evt) {
-          var value = evt.target.result;
-          var list_item = $('<li>' +
-                            '[' + cursor.key + '] ' +
-                            '(biblioid: ' + value.biblioid + ') ' +
-                            value.title +
-                            '</li>');
-          if (value.year != null)
-            list_item.append(' - ' + value.year);
-
-          if (value.hasOwnProperty('blob') &&
-              typeof value.blob != 'undefined') {
-            var link = $('<a href="' + cursor.key + '">File</a>');
-            link.on('click', function() { return false; });
-            link.on('mouseenter', function(evt) {
-                      setInViewer(evt.target.getAttribute('href')); });
-            list_item.append(' / ');
-            list_item.append(link);
-          } else {
-            list_item.append(" / No attached file");
-          }
-          pub_list.append(list_item);
-        };
-
-        // Move on to the next object in store
-        cursor.continue();
-
-        // This counter serves only to create distinct ids
-        i++;
-      } else {
-        console.log("No more entries");
-      }
-    };
-  }
-
-  function newViewerFrame() {
-    var viewer = $('#pub-viewer');
-    viewer.empty();
-    var iframe = $('<iframe />');
-    viewer.append(iframe);
-    return iframe;
-  }
-
-  function setInViewer(key) {
-    console.log("setInViewer:", arguments);
-    key = Number(key);
-    if (key == current_view_pub_key)
-      return;
-
-    current_view_pub_key = key;
-
-    var store = getObjectStore(DB_STORE_NAME, 'readonly');
-    getBlob(key, store, function(blob) {
-      console.log("setInViewer blob:", blob);
-      var iframe = newViewerFrame();
-
-      // It is not possible to set a direct link to the
-      // blob to provide a mean to directly download it.
-      if (blob.type == 'text/html') {
-        var reader = new FileReader();
-        reader.onload = (function(evt) {
-          var html = evt.target.result;
-          iframe.load(function() {
-            $(this).contents().find('html').html(html);
-          });
-        });
-        reader.readAsText(blob);
-      } else if (blob.type.indexOf('image/') == 0) {
-        iframe.load(function() {
-          var img_id = 'image-' + key;
-          var img = $('<img id="' + img_id + '"/>');
-          $(this).contents().find('body').html(img);
-          var obj_url = window.URL.createObjectURL(blob);
-          $(this).contents().find('#' + img_id).attr('src', obj_url);
-          window.URL.revokeObjectURL(obj_url);
-        });
-      } else if (blob.type == 'application/pdf') {
-        $('*').css('cursor', 'wait');
-        var obj_url = window.URL.createObjectURL(blob);
-        iframe.load(function() {
-          $('*').css('cursor', 'auto');
-        });
-        iframe.attr('src', obj_url);
-        window.URL.revokeObjectURL(obj_url);
-      } else {
-        iframe.load(function() {
-          $(this).contents().find('body').html("No view available");
-        });
-      }
-
-    });
-  }
-
-  /**
-   * @param {string} biblioid
-   * @param {string} title
-   * @param {number} year
-   * @param {string} url the URL of the image to download and store in the local
-   *   IndexedDB database. The resource behind this URL is subjected to the
-   *   "Same origin policy", thus for this method to work, the URL must come from
-   *   the same origin as the web site/app this code is deployed on.
-   */
-  function addPublicationFromUrl(biblioid, title, year, url) {
-    console.log("addPublicationFromUrl:", arguments);
-
-    var xhr = new XMLHttpRequest();
-    xhr.open('GET', url, true);
-    // Setting the wanted responseType to "blob"
-    // http://www.w3.org/TR/XMLHttpRequest2/#the-response-attribute
-    xhr.responseType = 'blob';
-    xhr.onload = function (evt) {
-                           if (xhr.status == 200) {
-                             console.log("Blob retrieved");
-                             var blob = xhr.response;
-                             console.log("Blob:", blob);
-                             addPublication(biblioid, title, year, blob);
-                           } else {
-                             console.error("addPublicationFromUrl error:",
-                                           xhr.responseText, xhr.status);
-                           }
-                         };
-    xhr.send();
-
-    // We can't use jQuery here because as of jQuery 1.8.3 the new "blob"
-    // responseType is not handled.
-    // http://bugs.jquery.com/ticket/11461
-    // http://bugs.jquery.com/ticket/7248
-    // $.ajax({
-    //   url: url,
-    //   type: 'GET',
-    //   xhrFields: { responseType: 'blob' },
-    //   success: function(data, textStatus, jqXHR) {
-    //     console.log("Blob retrieved");
-    //     console.log("Blob:", data);
-    //     // addPublication(biblioid, title, year, data);
-    //   },
-    //   error: function(jqXHR, textStatus, errorThrown) {
-    //     console.error(errorThrown);
-    //     displayActionFailure("Error during blob retrieval");
-    //   }
-    // });
-  }
-
-  /**
-   * @param {string} biblioid
-   * @param {string} title
-   * @param {number} year
-   * @param {Blob=} blob
-   */
-  function addPublication(biblioid, title, year, blob) {
-    console.log("addPublication arguments:", arguments);
-    var obj = { biblioid: biblioid, title: title, year: year };
-    if (typeof blob != 'undefined')
-      obj.blob = blob;
-
-    var store = getObjectStore(DB_STORE_NAME, 'readwrite');
-    var req;
-    try {
-      req = store.add(obj);
-    } catch (e) {
-      if (e.name == 'DataCloneError')
-        displayActionFailure("This engine doesn't know how to clone a Blob, " +
-                             "use Firefox");
-      throw e;
-    }
-    req.onsuccess = function (evt) {
-      console.log("Insertion in DB successful");
-      displayActionSuccess();
-      displayPubList(store);
-    };
-    req.onerror = function() {
-      console.error("addPublication error", this.error);
-      displayActionFailure(this.error);
-    };
-  }
-
-  /**
-   * @param {string} biblioid
-   */
-  function deletePublicationFromBib(biblioid) {
-    console.log("deletePublication:", arguments);
-    var store = getObjectStore(DB_STORE_NAME, 'readwrite');
-    var req = store.index('biblioid');
-    req.get(biblioid).onsuccess = function(evt) {
-      if (typeof evt.target.result == 'undefined') {
-        displayActionFailure("No matching record found");
-        return;
-      }
-      deletePublication(evt.target.result.id, store);
-    };
-    req.onerror = function (evt) {
-      console.error("deletePublicationFromBib:", evt.target.errorCode);
-    };
-  }
-
-  /**
-   * @param {number} key
-   * @param {IDBObjectStore=} store
-   */
-  function deletePublication(key, store) {
-    console.log("deletePublication:", arguments);
-
-    if (typeof store == 'undefined')
-      store = getObjectStore(DB_STORE_NAME, 'readwrite');
-
-    // As per spec http://www.w3.org/TR/IndexedDB/#object-store-deletion-operation
-    // the result of the Object Store Deletion Operation algorithm is
-    // undefined, so it's not possible to know if some records were actually
-    // deleted by looking at the request result.
-    var req = store.get(key);
-    req.onsuccess = function(evt) {
-      var record = evt.target.result;
-      console.log("record:", record);
-      if (typeof record == 'undefined') {
-        displayActionFailure("No matching record found");
-        return;
-      }
-      // Warning: The exact same key used for creation needs to be passed for
-      // the deletion. If the key was a Number for creation, then it needs to
-      // be a Number for deletion.
-      req = store.delete(key);
-      req.onsuccess = function(evt) {
-        console.log("evt:", evt);
-        console.log("evt.target:", evt.target);
-        console.log("evt.target.result:", evt.target.result);
-        console.log("delete successful");
-        displayActionSuccess("Deletion successful");
-        displayPubList(store);
-      };
-      req.onerror = function (evt) {
-        console.error("deletePublication:", evt.target.errorCode);
-      };
-    };
-    req.onerror = function (evt) {
-      console.error("deletePublication:", evt.target.errorCode);
-      };
-  }
-
-  function displayActionSuccess(msg) {
-    msg = typeof msg != 'undefined' ? "Success: " + msg : "Success";
-    $('#msg').html('<span class="action-success">' + msg + '</span>');
-  }
-  function displayActionFailure(msg) {
-    msg = typeof msg != 'undefined' ? "Failure: " + msg : "Failure";
-    $('#msg').html('<span class="action-failure">' + msg + '</span>');
-  }
-  function resetActionStatus() {
-    console.log("resetActionStatus ...");
-    $('#msg').empty();
-    console.log("resetActionStatus DONE");
-  }
-
-  function addEventListeners() {
-    console.log("addEventListeners");
-
-    $('#register-form-reset').click(function(evt) {
-      resetActionStatus();
-    });
-
-    $('#add-button').click(function(evt) {
-      console.log("add ...");
-      var title = $('#pub-title').val();
-      var biblioid = $('#pub-biblioid').val();
-      if (!title || !biblioid) {
-        displayActionFailure("Required field(s) missing");
-        return;
-      }
-      var year = $('#pub-year').val();
-      if (year != '') {
-        // Better use Number.isInteger if the engine has EcmaScript 6
-        if (isNaN(year))  {
-          displayActionFailure("Invalid year");
-          return;
-        }
-        year = Number(year);
-      } else {
-        year = null;
-      }
-
-      var file_input = $('#pub-file');
-      var selected_file = file_input.get(0).files[0];
-      console.log("selected_file:", selected_file);
-      // Keeping a reference on how to reset the file input in the UI once we
-      // have its value, but instead of doing that we rather use a "reset" type
-      // input in the HTML form.
-      //file_input.val(null);
-      var file_url = $('#pub-file-url').val();
-      if (selected_file) {
-        addPublication(biblioid, title, year, selected_file);
-      } else if (file_url) {
-        addPublicationFromUrl(biblioid, title, year, file_url);
-      } else {
-        addPublication(biblioid, title, year);
-      }
-
-    });
-
-    $('#delete-button').click(function(evt) {
-      console.log("delete ...");
-      var biblioid = $('#pub-biblioid-to-delete').val();
-      var key = $('#key-to-delete').val();
-
-      if (biblioid != '') {
-        deletePublicationFromBib(biblioid);
-      } else if (key != '') {
-        // Better use Number.isInteger if the engine has EcmaScript 6
-        if (key == '' || isNaN(key))  {
-          displayActionFailure("Invalid key");
-          return;
-        }
-        key = Number(key);
-        deletePublication(key);
-      }
-    });
-
-    $('#clear-store-button').click(function(evt) {
-      clearObjectStore();
-    });
-
-    var search_button = $('#search-list-button');
-    search_button.click(function(evt) {
-      displayPubList();
-    });
-
-  }
-
-  openDb();
-  addEventListeners();
-
-})(); // Immediately-Invoked Function Expression (IIFE)
-
- -

{{ LiveSampleLink('Full_IndexedDB_example', "Test the online live demo") }}

- -

Next step

- -

If you want to start tinkering with the API, jump in to the reference documentation and check out the different methods.

- -

See also

- -

Reference

- - - -

Tutorials

- - - -

Related articles

- - - -

Firefox

- - diff --git a/files/es/web/api/indexeddb_api/using_indexeddb/index.html b/files/es/web/api/indexeddb_api/using_indexeddb/index.html new file mode 100644 index 0000000000..ea9d2d879c --- /dev/null +++ b/files/es/web/api/indexeddb_api/using_indexeddb/index.html @@ -0,0 +1,1308 @@ +--- +title: Usando IndexedDB +slug: Web/API/IndexedDB_API/Usando_IndexedDB +tags: + - API + - Almacenamiento + - Avanzado + - Base de datos + - Guía + - IndexedDB + - Tutorial + - jsstore +translation_of: Web/API/IndexedDB_API/Using_IndexedDB +--- +
+

IndexedDB es una manera de almacenar datos dentro del navegador del usuario. Debido a que permite la creación de aplicaciones con habilidades de consulta enriquecidas, con independencia de la disponibilidad de la red, sus aplicaciones pueden trabajar tanto en línea como fuera de línea.

+
+ +

Acerca de este documento

+ +

Este tutorial es una guía sobre el uso de la API asíncrona de IndexedDB. Si no está familiarizado con IndexedDB, por favor consulte primero Conceptos Básicos Acerca de IndexedDB.

+ +

Para la documentación de referencia sobre la API de IndexedDB, vea el artículo IndexedDB y sus subpaginas, que documentan los tipos de objetos usados por IndexedDB, así como los métodos síncronos y asíncronos. 

+ +

Patrones Básicos

+ +

El patrón básico que indexedDB propone es:

+ +
    +
  1. Abrir una base de datos.
  2. +
  3. Crear un objeto de almacenamiento en la base de datos.
  4. +
  5. Iniciar una transacción y hacer una petición para hacer alguna operación de la base de datos, tal como añadir o recuperar datos.
  6. +
  7. +
    Espere a que se complete la operación por la escucha de la clase correcta de eventos DOM .
    +
  8. +
  9. +
    Hacer algo con el resultado (El cual puede ser encontrado en el objeto de la petición).
    +
  10. +
+ +

Con esos grandes rasgos en mente, seremos más concretos.

+ +

Creando y estructurando el almacenamiento

+ +

Como las especificaciones están todavía elaborandose, las implementaciones actuales de indexedDB dependen de los navegadores. Hasta que la especificación se haya consolidado, los proveedores de navegadores pueden tener diferentes implementaciones de los estandares de indexedDB. Una vez se alcance el consenso en el estandar, los proveedores implementarán la API sin prefijos. En algunas implementaciones ya fueron removidos los prefijos: Internet Explorer 10, Firefox 16, Chrome 24. Cuando utilizan un prefijo, los navegadores basados en gecko usan el prefijo moz , mientras que los navegadores basados en WebKit usan el prefijo webkit.

+ +

Usando una versión experimental de IndexedDB

+ +

En caso que usted quiera probar su código en navegadores que todavía usen un prefijo, puede usar el siguiente codigo:  

+ +
// En la siguiente línea, puede incluir prefijos de implementación que quiera probar.
+window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
+// No use "var indexedDB = ..." Si no está en una función.
+// Por otra parte, puedes necesitar referencias a algun objeto window.IDB*:
+window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
+window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
+// (Mozilla nunca ha prefijado estos objetos, por lo tanto no necesitamos window.mozIDB*)
+ +

Hay que tener cuidado con las implementaciones que usan un prefijo ya que puede ser inestables, incompletas, o usen una versión antigua de la especificación. En producción se recomienda usar el código sin prefijos. Es preferible no tener soporte para un navegador a decir que lo tiene y fallar en ello :

+ +
if (!window.indexedDB) {
+    window.alert("Su navegador no soporta una versión estable de indexedDB. Tal y como las características no serán validas");
+}
+
+ +

Abriendo una base de datos

+ +

Iniciamos todo el proceso así:

+ +
// dejamos abierta nuestra base de datos
+var request = window.indexedDB.open("MyTestDatabase", 3);
+
+ +

¿Lo has visto? Abrir una base de datos es igual que cualquier otra operación — solo tienes que "solicitarla" (request).

+ +

La solicitud de apertura no abre la base de datos o inicia la transacción de inmediato. La llamada a la función open() retornan unos objetos IDBOpenDBRequest, cuyo resultado, correcto o erróneo, se maneja en un evento.  Alguna otra función asincrónica en indexedDB hace lo mismo - Devolver un objeto IDBRequest que disparará un evento con el resultado o el error. El resultado para la función de abrir es una instancia de un IDBDatabase.

+ +

El segundo parámetro para el método open es la versión de la base de datos. La versión de la base de datos determina el esquema - El almacen de objectos en la base de datos y su estructura. Si la base de datos no existe, es creada y se dispara un evento onupgradeneeded de inmediato, permitiéndote proveer una actualización de la estructura e índices en la función que capture dicho evento. Se verá más adelante en  Actualizando la versión de la base de datos

+ +
+

Importante: El número de versión es un unsigned long. Por lo tanto significa que puede ser un entero muy grande. También significa que si usas un flotante será convertido en un entero más cercano y la transacción puede no ser iniciada, el evento upgradeneeded no se desencadenará. Por ejemplo no use 2.4 como un número de versión ya que será igual que la 2:

+ +
var request = indexedDB.open("MyTestDatabase", 2.4); // Esto no se hace, la versión será redondeada a 2
+
+ +

Generando manipuladores

+ +

La primera cosa que usted querrá hacer con la totalidad de las peticiones que usted genera es agregar controladores de éxito y de error:

+ +
request.onerror = function(event) {
+  // Hacer algo con request.errorCode!
+};
+request.onsuccess = function(event) {
+  // Hacer algo con request.result!
+};
+ +

¿Cuál de las dos funciones, onSuccess () o onerror (), se vuelve a llamar? Si todo tiene éxito, un evento de éxito (es decir, un evento DOM cuya propiedad tipo se establece en el "éxito") se dispara con la solicitud como su objetivo. Una vez que se dispara, la función onSuccess () a petición se activa con el evento de éxito como argumento. De lo contrario, si había algún problema, un evento de error (es decir, un evento DOM cuyo tipo de propiedad se establece en "error") se dispara a petición. Esto desencadena la función onerror () con el evento de error como argumento.

+ +

La API IndexedDB está diseñada para minimizar la necesidad de control de errores, por lo que no es probable que veamos muchos eventos de error (al menos, no una vez que estás acostumbrado a la API). En el caso de la apertura de una base de datos, sin embargo, hay algunas condiciones comunes que generan eventos de error. El problema más común se produce cuando el usuario ha decidido no dar, a su aplicación web, el permiso para crear una base de datos. Uno de los principales objetivos de diseño de IndexedDB es permitir que grandes cantidades de datos se almacenen para su uso sin conexión a internet. (Para obtener más información sobre la cantidad de almacenamiento que puede tener para cada navegador, consulte Límites de almacenamiento.)  

+ +

Obviamente, los navegadores no permitirán que alguna red de publicidad o sitio web malicioso pueda contaminar su ordenador, por ello los navegadores utilizan un diálogo para indicar al usuario la primera vez que cualquier aplicación web determinada intente abrir una IndexedDB para el almacenamiento. El usuario puede optar por permitir o denegar el acceso. Además, el almacenamiento IndexedDB en los modos de privacidad navegadores sólo dura en memoria hasta que la sesión de incógnito haya sido cerrada (modo de navegación privada para el modo de Firefox e Incognito para Chrome, pero en Firefox no está implementado a partir de noviembre 2015 por lo que no puede utilizar IndexedDB en Firefox navegación privada en absoluto).

+ +

Ahora, asumiendo que el usuario acepta su solicitud para crear una base de datos, y que ha recibido un evento de éxito para activar la devolución de llamada de éxito; ¿Que sigue? La solicitud aquí se generó con una llamada a indexedDB.open (), por lo request.result es una instancia de IDBDatabase, y que sin duda quieren ahorrar para más adelante. Su código podría ser algo como esto:

+ +
var db;
+var request = indexedDB.open("MyTestDatabase");
+request.onerror = function(event) {
+  alert("Why didn't you allow my web app to use IndexedDB?!");
+};
+request.onsuccess = function(event) {
+  db = request.result;
+};
+
+ +

Manejando errores

+ +

Como se mencionó anteriormente, los eventos de error de burbujas. Eventos de error se dirigen a la solicitud que generó el error, entonces el evento se propaga a la operación, y finalmente con el objeto de base de datos. Si desea evitar la adición de controladores de errores a cada solicitud, en su lugar puede añadir un solo controlador de errores en el objeto de base de datos, así:

+ +
db.onerror = function(event) {
+  // Generic error handler for all errors targeted at this database's
+  // requests!
+  alert("Database error: " + event.target.errorCode);
+};
+
+ +

Uno de los errores más comunes posibles al abrir una base de datos es VER_ERR. Indica que la versión de la base de datos almacenada en el disco es mayor que la versión que está intentando abrir. Este es un caso de error que siempre debe ser manejado por el gestor de errores.

+ +

Creación o actualización de la versión de la base de datos

+ +

Cuando se crea una nueva base de datos o se aumenta el número de versión de una base de datos existente (mediante la especificación de un número de versión más alto de lo que hizo antes, en {{anch ("Cómo abrir una base de datos")}}), el evento onupgradeneeded se activará y un objeto IDBVersionChangeEvent será pasado a cualquier controlador de eventos onversionchange establecido en request.result (es decir, db en el ejemplo). En el controlador para el evento upgradeneeded, se debe crear los almacenes de objetos necesarios para esta versión de la base de datos:

+ +
// Este evento solamente está implementado en navegadores recientes
+request.onupgradeneeded = function(event) {
+  var db = event.target.result;
+
+  // Crea un almacén de objetos (objectStore) para esta base de datos
+  var objectStore = db.createObjectStore("name", { keyPath: "myKey" });
+};
+ +

En este caso, la base de datos ya tendrá los almacenes de objetos de la versión anterior de la base de datos, por lo que no tiene que crear estos almacenes de objetos de nuevo. Sólo es necesario crear nuevos almacenes de objetos, o eliminar las tiendas de objetos de la versión anterior que ya no son necesarios. Si necesita cambiar un almacén de objetos existentes (por ejemplo, para cambiar la ruta de acceso clave keyPath), entonces se debe eliminar el antiguo almacén de objetos y crear de nuevo con las nuevas opciones. (Tenga en cuenta que esto borrará la información en el almacén de objetos Si usted necesita guardar esa información, usted debe leerlo y guardarlo en otro lugar antes de actualizar la base de datos.)

+ +

Tratar de crear un almacén de objetos con un nombre que ya existe (o tratando de eliminar un almacén de objetos con un nombre que no existe) lanzará un error.

+ +

Si el evento onupgradeneeded retorna éxito, entonces se activará el manejador onsuccess de la solicitud de base de datos abierta.

+ +

Blink / Webkit soportan la versión actual de la especificación, tal como fue liberado en Chrome 23+ y Opera 17+ ; IE10+ también lo soporta. Implementaciones mas viejas o distintas no implementan la versión actual de la especificación, y por lo tanto no son compatibles todavía con el indexedDB.open (nombre, versión).onupgradeneeded . Para obtener más información sobre cómo actualizar la versión de la base de datos en Webkit/Blink mas viejos, consulte el artículo de referencia IDBDatabase.

+ +

Estructuración de la base de datos

+ +

Ahora para estructurar la base de datos. IndexedDB usa almacenes de datos (object stores) en lugar de tablas, y una base de datos puede contener cualquier número de almacenes. Cuando un valor es almacenado, se le asocia con una clave. Existen diversas maneras en que una clave pude ser indicada dependiendo de si el almacén usa una ruta de clave o generador.

+ +

La siguiente table muetra las distintas formas en que las claves pueden ser indicadas:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Ruta de clave(keyPath)Generador de clave (autoIncrement)Descripción
NoNoEste almacén puede contener cualquier tipo de valor, incluso valores primitivos como números y cadenas. Se debe indicar un argumento de clave distinto cada vez que se agregue un nuevo valor.
SiNoEste almacén de objetos solo puede contener objetos de JavaScript. Los objetos deben tener una propiedad con el mismo nombre que la ruta de clave.
NoSiEste objeto puede contener cualquier tipo de valor. La clave es generada automáticamente, o se puede indicar un argumento de clave distinto si se quiere usar una clave específica.
SiSi +

Este almacén de objetos solo puede contener objetos de JavaScript. Usualmente una clave es generada y dicho valor es almacenado en el objeto en una propiedad con el mismo nombre que la ruta de clave. Sin embargo, si dicha propiedad ya existe en el objeto, el valor de esa propuiedad es usado como clave en lugar de generar una nueva.

+
+ +

También se puede crear índices en cualquer almacén de objetos, siempre y cuando el almacén contenga objets, y no primitivos. Un índice permite buscar valores contenidos en el almacén usando el valor de una propiedad del objeto almacenado, en lugar de la clave del mismo.

+ +

Adicionalmente, los índices tienen la habilidad para hacer cumplir restricciones simples en los datos almacendos. Al indicar la bandera unique al crear el índice, el índice asegurará que no se puedan almacenar dos objetos que tengan el mismo valor para la clave del índice. Así, por ejemplo si se tiene un almacén de objetos que contiene un set de personas, y se desea asegurar que no existan dos personas con el mismo email, se puede usar un índice con la bandera unique activada para forzar esto.

+ +

Esto puede sonar confuso, pero un ejemplo simple debe ilustrar el concepto. Primero, definiremos alguna información de clientes para usar en nuestro ejemplo:

+ +
// Así se ve nuestra información de clientes.
+const customerData = [
+  { ssn: "444-44-4444", name: "Bill", age: 35, email: "bill@company.com" },
+  { ssn: "555-55-5555", name: "Donna", age: 32, email: "donna@home.org" }
+];
+
+ +

Ahora, creemos una IndexedDB para almacenar los datos:

+ +
const dbName = "the_name";
+
+var request = indexedDB.open(dbName, 2);
+
+request.onerror = function(event) {
+  // Manejar errores.
+};
+request.onupgradeneeded = function(event) {
+  var db = event.target.result;
+
+  // Se crea un almacén para contener la información de nuestros cliente
+  // Se usará "ssn" como clave ya que es garantizado que es única
+  var objectStore = db.createObjectStore("customers", { keyPath: "ssn" });
+
+  // Se crea un índice para buscar clientes por nombre. Se podrían tener duplicados
+  // por lo que no se puede usar un índice único.
+  objectStore.createIndex("name", "name", { unique: false });
+
+  // Se crea un índice para buscar clientespor email. Se quiere asegurar que
+  // no puedan haberdos clientes con el mismo email, asi que se usa un índice único.
+  objectStore.createIndex("email", "email", { unique: true });
+
+  // Se usa transaction.oncomplete para asegurarse que la creación del almacén
+  // haya finalizado antes de añadir los datos en el.
+  objectStore.transaction.oncomplete = function(event) {
+    // Guarda los datos en el almacén recién creado.
+    var customerObjectStore = db.transaction("customers", "readwrite").objectStore("customers");
+    for (var i in customerData) {
+      customerObjectStore.add(customerData[i]);
+    }
+  }
+};
+
+ +

Como se indicó previamente, onupgradeneeded es el único lugar donde se puede alterar la estructura de la base de datos. En el, se puede crear y borrar almacenes de objetos y construir y remover índices.

+ +
Los almacenes de datos son creados con una llamada a  createObjectStore(). El método toma como parámetros el nombre del almacén y un objeto.  A pesar de que el segundo parámetro es opcional, es muy importante, porque permite definir propiedades opcionales importantes y refinar el tipo de almacén que se desea crear. En este caso, se pregunta por un almacén llamado "customers" y se define la clave, que es la propiedad que indica que un objeto en el almacén es único. La propiedad en este ejemplo es "ssn" (Social Security Number) ya que los números de seguridad social está garantizado que sea único. "ssn" debe estar presente en cada objeto que se guarda al almacén.
+ +

También se solicitó crear un índice llamado "name" que se fija en la propiedad name de los objetos almacenados. Así como con createObjectStore(), createIndex() toma un objeto opcional options que refina el tipo de índice que se desea crear. Agregar objetos que no tengan una propiedad name funcionará, pero los objetos no aparecerán en el índice "name"

+ +
Ahora se pueden obtener los clientes almacenados usando su ssn directamente del almacen, o usando su nombre a través del índice. Para aprender como hacer esto, vea la sección El uso de un índice
+ +

El uso de un generador de claves

+ +

Indicar la bandera autoIncrement  cuando se crea el almacén habilitará el generador de claves para dicho almacén. Por defecto esta bandera no está marcada.

+ +

Con el generador de claves, la clave será generada automáticamente a medida que se agreguen valores al almacén. El número actual de un generador de claves siempre se establece en 1 cuando se creal el almacén por primera vez. Básicamente la nueva clave autogenerada es incrementada en 1 basada en la llave anterior. El numero actual para un generador de claves nunca disminuye, salvo como resultado de operaciones de base de datos  que sean revertidos, por ejemplo, cuando la transacción de base de datos es abortada. Por lo tanto borrar un registro o incluso borrar todos los registros de un almacén nunca afecta al generador de claves

+ +

Se puede crear otro almacén de objetos con generador de claves como se muestra abajo:

+ +
// Abrir la indexedDB.
+var request = indexedDB.open(dbName, 3);
+
+request.onupgradeneeded = function (event) {
+
+    var db = event.target.result;
+
+    // Create another object store called "names" with the autoIncrement flag set as true.
+    var objStore = db.createObjectStore("names", { autoIncrement : true });
+
+    // Because the "names" object store has the key generator, the key for the name value is generated automatically.
+    // The added records would be like:
+    // key : 1 => value : "Bill"
+    // key : 2 => value : "Donna"
+    for (var i in customerData) {
+        objStore.add(customerData[i].name);
+    }
+}
+ +

Para más detalles acerca del generador de claves, por favor ver "W3C Key Generators".

+ +

Añadir, recuperación y eliminación de datos

+ +

Antes que haga algo con su nueva base de datos , necesita comenzar una transacción. Transactions come from the database object, and you have to specify which object stores you want the transaction to span. Once you are inside the transaction, you can access the object stores that hold your data and make your requests. Next, you need to decide if you're going to make changes to the database or if you just need to read from it. Transactions have three available modes: readonly, readwrite, and versionchange.

+ +

To change the "schema" or structure of the database—which involves creating or deleting object stores or indexes—the transaction must be in versionchange mode. This transaction is opened by calling the {{domxref("IDBFactory.open")}} method with a version specified. (In WebKit browsers, which have not implemented the latest specifcation, the {{domxref("IDBFactory.open")}} method takes only one parameter, the name of the database; then you must call {{domxref("IDBVersionChangeRequest.setVersion")}} to establish the versionchange transaction.)

+ +

To read the records of an existing object store, the transaction can either be in readonly or readwrite mode. To make changes to an existing object store, the transaction must be in readwrite mode. You open such transactions with {{domxref("IDBDatabase.transaction")}}. The method accepts two parameters: the storeNames (the scope, defined as an array of object stores that you want to access) and the mode (readonly or readwrite) for the transaction. The method returns a transaction object containing the {{domxref("IDBIndex.objectStore")}} method, which you can use to access your object store. By default, where no mode is specified, transactions open in readonly mode.

+ +

You can speed up data access by using the right scope and mode in the transaction. Here are a couple of tips:

+ + + +

Agregar datos a la base de datos

+ +

If you've just created a database, then you probably want to write to it. Here's what that looks like:

+ +
var transaction = db.transaction(["customers"], "readwrite");
+// Note: Older experimental implementations use the deprecated constant IDBTransaction.READ_WRITE instead of "readwrite".
+// In case you want to support such an implementation, you can write:
+// var transaction = db.transaction(["customers"], IDBTransaction.READ_WRITE);
+ +

The transaction() function takes two arguments (though one is optional) and returns a transaction object. The first argument is a list of object stores that the transaction will span. You can pass an empty array if you want the transaction to span all object stores, but don't do it because the spec says an empty array should generate an InvalidAccessError. If you don't specify anything for the second argument, you get a read-only transaction. Since you want to write to it here you need to pass the "readwrite" flag.

+ +

Now that you have a transaction you need to understand its lifetime. Transactions are tied very closely to the event loop. If you make a transaction and return to the event loop without using it then the transaction will become inactive. The only way to keep the transaction active is to make a request on it. When the request is finished you'll get a DOM event and, assuming that the request succeeded, you'll have another opportunity to extend the transaction during that callback. If you return to the event loop without extending the transaction then it will become inactive, and so on. As long as there are pending requests the transaction remains active. Transaction lifetimes are really very simple but it might take a little time to get used to. A few more examples will help, too. If you start seeing TRANSACTION_INACTIVE_ERR error codes then you've messed something up.

+ +

Transactions can receive DOM events of three different types: error, abort, and complete. We've talked about the way that error events bubble, so a transaction receives error events from any requests that are generated from it. A more subtle point here is that the default behavior of an error is to abort the transaction in which it occurred. Unless you handle the error by first calling preventDefault() on the error event then doing something else, the entire transaction is rolled back. This design forces you to think about and handle errors, but you can always add a catchall error handler to the database if fine-grained error handling is too cumbersome. If you don't handle an error event or if you call abort() on the transaction, then the transaction is rolled back and an abort event is fired on the transaction. Otherwise, after all pending requests have completed, you'll get a complete event. If you're doing lots of database operations, then tracking the transaction rather than individual requests can certainly aid your sanity.

+ +

Now that you have a transaction, you'll need to get the object store from it. Transactions only let you have an object store that you specified when creating the transaction. Then you can add all the data you need.

+ +
// Do something when all the data is added to the database.
+transaction.oncomplete = function(event) {
+  alert("All done!");
+};
+
+transaction.onerror = function(event) {
+  // Don't forget to handle errors!
+};
+
+var objectStore = transaction.objectStore("customers");
+for (var i in customerData) {
+  var request = objectStore.add(customerData[i]);
+  request.onsuccess = function(event) {
+    // event.target.result == customerData[i].ssn;
+  };
+}
+ +

The result of a request generated from a call to add() is the key of the value that was added. So in this case, it should equal the ssn property of the object that was added, since the object store uses the ssn property for the key path. Note that the add() function requires that no object already be in the database with the same key. If you're trying to modify an existing entry, or you don't care if one exists already, you can use the put() function, as shown below in the {{ anch("Updating an entry in the database") }} section.

+ +

Extracción de datos de la base de datos

+ +

Removing data is very similar:

+ +
var request = db.transaction(["customers"], "readwrite")
+                .objectStore("customers")
+                .delete("444-44-4444");
+request.onsuccess = function(event) {
+  // It's gone!
+};
+ +

Obtener datos de la base de datos

+ +

Now that the database has some info in it, you can retrieve it in several ways. First, the simple get(). You need to provide the key to retrieve the value, like so:

+ +
var transaction = db.transaction(["customers"]);
+var objectStore = transaction.objectStore("customers");
+var request = objectStore.get("444-44-4444");
+request.onerror = function(event) {
+  // Handle errors!
+};
+request.onsuccess = function(event) {
+  // Do something with the request.result!
+  alert("Name for SSN 444-44-4444 is " + request.result.name);
+};
+ +

That's a lot of code for a "simple" retrieval. Here's how you can shorten it up a bit, assuming that you handle errors at the database level:

+ +
db.transaction("customers").objectStore("customers").get("444-44-4444").onsuccess = function(event) {
+  alert("Name for SSN 444-44-4444 is " + event.target.result.name);
+};
+ +

See how this works? Since there's only one object store, you can avoid passing a list of object stores you need in your transaction and just pass the name as a string. Also, you're only reading from the database, so you don't need a "readwrite" transaction. Calling transaction() with no mode specified gives you a "readonly" transaction. Another subtlety here is that you don't actually save the request object to a variable. Since the DOM event has the request as its target you can use the event to get to the result property.

+ +
+

Note: You can speed up data access by limiting the scope and mode in the transaction. Here are a couple of tips:

+ + +
+ +

Actualización de una entrada en la base de datos

+ +

Now we've retrieved some data, updating it and inserting it back into the IndexedDB is pretty simple. Let's update the previous example somewhat:

+ +
var objectStore = db.transaction(["customers"], "readwrite").objectStore("customers");
+var request = objectStore.get("444-44-4444");
+request.onerror = function(event) {
+  // Handle errors!
+};
+request.onsuccess = function(event) {
+  // Get the old value that we want to update
+  var data = request.result;
+
+  // update the value(s) in the object that you want to change
+  data.age = 42;
+
+  // Put this updated object back into the database.
+  var requestUpdate = objectStore.put(data);
+   requestUpdate.onerror = function(event) {
+     // Do something with the error
+   };
+   requestUpdate.onsuccess = function(event) {
+     // Success - the data is updated!
+   };
+};
+ +

So here we're creating an objectStore and requesting a customer record out of it, identified by its ssn value (444-44-4444). We then put the result of that request in a variable (data), update the age property of this object, then create a second request (requestUpdate) to put the customer record back into the objectStore, overwriting the previous value.

+ +
+

Note that in this case we've had to specify a readwrite transaction because we want to write to the database, not just read out of it.

+
+ +

El uso de un cursor

+ +

Using get() requires that you know which key you want to retrieve. If you want to step through all the values in your object store, then you can use a cursor. Here's what it looks like:

+ +
var objectStore = db.transaction("customers").objectStore("customers");
+
+objectStore.openCursor().onsuccess = function(event) {
+  var cursor = event.target.result;
+  if (cursor) {
+    alert("Name for SSN " + cursor.key + " is " + cursor.value.name);
+    cursor.continue();
+  }
+  else {
+    alert("No more entries!");
+  }
+};
+ +

The openCursor() function takes several arguments. First, you can limit the range of items that are retrieved by using a key range object that we'll get to in a minute. Second, you can specify the direction that you want to iterate. In the above example, we're iterating over all objects in ascending order. The success callback for cursors is a little special. The cursor object itself is the result of the request (above we're using the shorthand, so it's event.target.result). Then the actual key and value can be found on the key and value properties of the cursor object. If you want to keep going, then you have to call continue() on the cursor. When you've reached the end of the data (or if there were no entries that matched your openCursor() request) you still get a success callback, but the result property is undefined.

+ +

One common pattern with cursors is to retrieve all objects in an object store and add them to an array, like this:

+ +
var customers = [];
+
+objectStore.openCursor().onsuccess = function(event) {
+  var cursor = event.target.result;
+  if (cursor) {
+    customers.push(cursor.value);
+    cursor.continue();
+  }
+  else {
+    alert("Got all customers: " + customers);
+  }
+};
+ +
+

Note: Mozilla has also implemented getAll() to handle this case (and getAllKeys(), which is currently hidden behind the dom.indexedDB.experimental preference in about:config). these aren't part of the IndexedDB standard, so may disappear in the future. We've included them because we think they're useful. The following code does precisely the same thing as above:

+ +
objectStore.getAll().onsuccess = function(event) {
+  alert("Got all customers: " + event.target.result);
+};
+ +

There is a performance cost associated with looking at the value property of a cursor, because the object is created lazily. When you use getAll() for example, Gecko must create all the objects at once. If you're just interested in looking at each of the keys, for instance, it is much more efficient to use a cursor than to use getAll(). If you're trying to get an array of all the objects in an object store, though, use getAll().

+
+ +

El uso de un índice

+ +

Storing customer data using the SSN as a key is logical since the SSN uniquely identifies an individual. (Whether this is a good idea for privacy is a different question, and outside the scope of this article.) If you need to look up a customer by name, however, you'll need to iterate over every SSN in the database until you find the right one. Searching in this fashion would be very slow, so instead you can use an index.

+ +
var index = objectStore.index("name");
+index.get("Donna").onsuccess = function(event) {
+  alert("Donna's SSN is " + event.target.result.ssn);
+};
+ +

The "name" cursor isn't unique, so there could be more than one entry with the name set to "Donna". In that case you always get the one with the lowest key value.

+ +

If you need to access all the entries with a given name you can use a cursor. You can open two different types of cursors on indexes. A normal cursor maps the index property to the object in the object store. A key cursor maps the index property to the key used to store the object in the object store. The differences are illustrated here:

+ +
// Using a normal cursor to grab whole customer record objects
+index.openCursor().onsuccess = function(event) {
+  var cursor = event.target.result;
+  if (cursor) {
+    // cursor.key is a name, like "Bill", and cursor.value is the whole object.
+    alert("Name: " + cursor.key + ", SSN: " + cursor.value.ssn + ", email: " + cursor.value.email);
+    cursor.continue();
+  }
+};
+
+// Using a key cursor to grab customer record object keys
+index.openKeyCursor().onsuccess = function(event) {
+  var cursor = event.target.result;
+  if (cursor) {
+    // cursor.key is a name, like "Bill", and cursor.value is the SSN.
+    // No way to directly get the rest of the stored object.
+    alert("Name: " + cursor.key + ", SSN: " + cursor.value);
+    cursor.continue();
+  }
+};
+ +

Especificación de la gama y la dirección de los cursores

+ +

If you would like to limit the range of values you see in a cursor, you can use an IDBKeyRange object and pass it as the first argument to openCursor() or openKeyCursor(). You can make a key range that only allows a single key, or one that has a lower or upper bound, or one that has both a lower and upper bound. The bound may be "closed" (i.e., the key range includes the given value(s)) or "open" (i.e., the key range does not include the given value(s)). Here's how it works:

+ +
// Only match "Donna"
+var singleKeyRange = IDBKeyRange.only("Donna");
+
+// Match anything past "Bill", including "Bill"
+var lowerBoundKeyRange = IDBKeyRange.lowerBound("Bill");
+
+// Match anything past "Bill", but don't include "Bill"
+var lowerBoundOpenKeyRange = IDBKeyRange.lowerBound("Bill", true);
+
+// Match anything up to, but not including, "Donna"
+var upperBoundOpenKeyRange = IDBKeyRange.upperBound("Donna", true);
+
+// Match anything between "Bill" and "Donna", but not including "Donna"
+var boundKeyRange = IDBKeyRange.bound("Bill", "Donna", false, true);
+
+// To use one of the key ranges, pass it in as the first argument of openCursor()/openKeyCursor()
+index.openCursor(boundKeyRange).onsuccess = function(event) {
+  var cursor = event.target.result;
+  if (cursor) {
+    // Do something with the matches.
+    cursor.continue();
+  }
+};
+ +

Sometimes you may want to iterate in descending order rather than in ascending order (the default direction for all cursors). Switching direction is accomplished by passing prev to the openCursor() function as the second argument:

+ +
objectStore.openCursor(boundKeyRange, "prev").onsuccess = function(event) {
+  var cursor = event.target.result;
+  if (cursor) {
+    // Do something with the entries.
+    cursor.continue();
+  }
+};
+ +

If you just want to specify a change of direction but not constrain the results shown, you can just pass in null as the first argument:

+ +
objectStore.openCursor(null, "prev").onsuccess = function(event) {
+  var cursor = event.target.result;
+  if (cursor) {
+    // Do something with the entries.
+    cursor.continue();
+  }
+};
+ +

Since the "name" index isn't unique, there might be multiple entries where name is the same. Note that such a situation cannot occur with object stores since the key must always be unique. If you wish to filter out duplicates during cursor iteration over indexes, you can pass nextunique (or prevunique if you're going backwards) as the direction parameter. When nextunique or prevunique is used, the entry with the lowest key is always the one returned.

+ +
index.openKeyCursor(null, "nextunique").onsuccess = function(event) {
+  var cursor = event.target.result;
+  if (cursor) {
+    // Do something with the entries.
+    cursor.continue();
+  }
+};
+ +

Please see "IDBCursor Constants" for the valid direction arguments.

+ +

Cambios Versión mientras que una aplicación web está abierto en otra pestaña

+ +

When your web app changes in such a way that a version change is required for your database, you need to consider what happens if the user has the old version of your app open in one tab and then loads the new version of your app in another. When you call open() with a greater version than the actual version of the database, all other open databases must explicitly acknowledge the request before you can start making changes to the database (an onblocked event is fired until tey are closed or reloaded). Here's how it works:

+ +
var openReq = mozIndexedDB.open("MyTestDatabase", 2);
+
+openReq.onblocked = function(event) {
+  // If some other tab is loaded with the database, then it needs to be closed
+  // before we can proceed.
+  alert("Please close all other tabs with this site open!");
+};
+
+openReq.onupgradeneeded = function(event) {
+  // All other databases have been closed. Set everything up.
+  db.createObjectStore(/* ... */);
+  useDatabase(db);
+}
+
+openReq.onsuccess = function(event) {
+  var db = event.target.result;
+  useDatabase(db);
+  return;
+}
+
+function useDatabase(db) {
+  // Make sure to add a handler to be notified if another page requests a version
+  // change. We must close the database. This allows the other page to upgrade the database.
+  // If you don't do this then the upgrade won't happen until the user closes the tab.
+  db.onversionchange = function(event) {
+    db.close();
+    alert("A new version of this page is ready. Please reload!");
+  };
+
+  // Do stuff with the database.
+}
+
+ +

Seguridad

+ +

IndexedDB uses the same-origin principle, which means that it ties the store to the origin of the site that creates it (typically, this is the site domain or subdomain), so it cannot be accessed by any other origin.

+ +

It's important to note that IndexedDB doesn't work for content loaded into a frame from another site (either {{ HTMLElement("frame") }} or {{ HTMLElement("iframe") }}. This is a security and privacy measure and can be considered analogous the blocking of third-party cookies. For more details, see {{ bug(595307) }}.

+ +

Warning About Browser Shutdown

+ +

When the browser shuts down (e.g., when the user selects Exit or clicks the Close button), any pending IndexedDB transactions are (silently) aborted — they will not complete, and they will not trigger the error handler. Since the user can exit the browser at any time, this means that you cannot rely upon any particular transaction to complete or to know that it did not complete. There are several implications of this behavior.

+ +

First, you should take care to always leave your database in a consistent state at the end of every transaction. For example, suppose that you are using IndexedDB to store a list of items that you allow the user to edit. You save the list after the edit by clearing the object store and then writing out the new list. If you clear the object store in one transaction and write the new list in another transaction, there is a danger that the browser will close after the clear but before the write, leaving you with an empty database. To avoid this, you should combine the clear and the write into a single transaction. 

+ +

Second, you should never tie database transactions to unload events. If the unload event is triggered by the browser closing, any transactions created in the unload event handler will never complete. An intuitive approach to maintaining some information across browser sessions is to read it from the database when the browser (or a particular page) is opened, update it as the user interacts with the browser, and then save it to the database when the browser (or page) closes. However, this will not work. The database transactions will be created in the unload event handler, but because they are asynchronous they will be aborted before they can execute.

+ +

In fact, there is no way to guarantee that IndexedDB transactions will complete, even with normal browser shutdown. See {{ bug(870645) }}.

+ +

Full IndexedDB example

+ +

HTML Content

+ +
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
+
+    <h1>IndexedDB Demo: storing blobs, e-publication example</h1>
+    <div class="note">
+      <p>
+        Works and tested with:
+      </p>
+      <div id="compat">
+      </div>
+    </div>
+
+    <div id="msg">
+    </div>
+
+    <form id="register-form">
+      <table>
+        <tbody>
+          <tr>
+            <td>
+              <label for="pub-title" class="required">
+                Title:
+              </label>
+            </td>
+            <td>
+              <input type="text" id="pub-title" name="pub-title" />
+            </td>
+          </tr>
+          <tr>
+            <td>
+              <label for="pub-biblioid" class="required">
+                Bibliographic ID:<br/>
+                <span class="note">(ISBN, ISSN, etc.)</span>
+              </label>
+            </td>
+            <td>
+              <input type="text" id="pub-biblioid" name="pub-biblioid"/>
+            </td>
+          </tr>
+          <tr>
+            <td>
+              <label for="pub-year">
+                Year:
+              </label>
+            </td>
+            <td>
+              <input type="number" id="pub-year" name="pub-year" />
+            </td>
+          </tr>
+        </tbody>
+        <tbody>
+          <tr>
+            <td>
+              <label for="pub-file">
+                File image:
+              </label>
+            </td>
+            <td>
+              <input type="file" id="pub-file"/>
+            </td>
+          </tr>
+          <tr>
+            <td>
+              <label for="pub-file-url">
+                Online-file image URL:<br/>
+                <span class="note">(same origin URL)</span>
+              </label>
+            </td>
+            <td>
+              <input type="text" id="pub-file-url" name="pub-file-url"/>
+            </td>
+          </tr>
+        </tbody>
+      </table>
+
+      <div class="button-pane">
+        <input type="button" id="add-button" value="Add Publication" />
+        <input type="reset" id="register-form-reset"/>
+      </div>
+    </form>
+
+    <form id="delete-form">
+      <table>
+        <tbody>
+          <tr>
+            <td>
+              <label for="pub-biblioid-to-delete">
+                Bibliographic ID:<br/>
+                <span class="note">(ISBN, ISSN, etc.)</span>
+              </label>
+            </td>
+            <td>
+              <input type="text" id="pub-biblioid-to-delete"
+                     name="pub-biblioid-to-delete" />
+            </td>
+          </tr>
+          <tr>
+            <td>
+              <label for="key-to-delete">
+                Key:<br/>
+                <span class="note">(for example 1, 2, 3, etc.)</span>
+              </label>
+            </td>
+            <td>
+              <input type="text" id="key-to-delete"
+                     name="key-to-delete" />
+            </td>
+          </tr>
+        </tbody>
+      </table>
+      <div class="button-pane">
+        <input type="button" id="delete-button" value="Delete Publication" />
+        <input type="button" id="clear-store-button"
+               value="Clear the whole store" class="destructive" />
+      </div>
+    </form>
+
+    <form id="search-form">
+      <div class="button-pane">
+        <input type="button" id="search-list-button"
+               value="List database content" />
+      </div>
+    </form>
+
+    <div>
+      <div id="pub-msg">
+      </div>
+      <div id="pub-viewer">
+      </div>
+      <ul id="pub-list">
+      </ul>
+    </div>
+
+ +

CSS Content

+ +
body {
+  font-size: 0.8em;
+  font-family: Sans-Serif;
+}
+
+form {
+  background-color: #cccccc;
+  border-radius: 0.3em;
+  display: inline-block;
+  margin-bottom: 0.5em;
+  padding: 1em;
+}
+
+table {
+  border-collapse: collapse;
+}
+
+input {
+  padding: 0.3em;
+  border-color: #cccccc;
+  border-radius: 0.3em;
+}
+
+.required:after {
+  content: "*";
+  color: red;
+}
+
+.button-pane {
+  margin-top: 1em;
+}
+
+#pub-viewer {
+  float: right;
+  width: 48%;
+  height: 20em;
+  border: solid #d092ff 0.1em;
+}
+#pub-viewer iframe {
+  width: 100%;
+  height: 100%;
+}
+
+#pub-list {
+  width: 46%;
+  background-color: #eeeeee;
+  border-radius: 0.3em;
+}
+#pub-list li {
+  padding-top: 0.5em;
+  padding-bottom: 0.5em;
+  padding-right: 0.5em;
+}
+
+#msg {
+  margin-bottom: 1em;
+}
+
+.action-success {
+  padding: 0.5em;
+  color: #00d21e;
+  background-color: #eeeeee;
+  border-radius: 0.2em;
+}
+
+.action-failure {
+  padding: 0.5em;
+  color: #ff1408;
+  background-color: #eeeeee;
+  border-radius: 0.2em;
+}
+
+.note {
+  font-size: smaller;
+}
+
+.destructive {
+  background-color: orange;
+}
+.destructive:hover {
+  background-color: #ff8000;
+}
+.destructive:active {
+  background-color: red;
+}
+
+ +

 

+ +

JavaScript Content

+ +
(function () {
+  var COMPAT_ENVS = [
+    ['Firefox', ">= 16.0"],
+    ['Google Chrome',
+     ">= 24.0 (you may need to get Google Chrome Canary), NO Blob storage support"]
+  ];
+  var compat = $('#compat');
+  compat.empty();
+  compat.append('<ul id="compat-list"></ul>');
+  COMPAT_ENVS.forEach(function(val, idx, array) {
+    $('#compat-list').append('<li>' + val[0] + ': ' + val[1] + '</li>');
+  });
+
+  const DB_NAME = 'mdn-demo-indexeddb-epublications';
+  const DB_VERSION = 1; // Use a long long for this value (don't use a float)
+  const DB_STORE_NAME = 'publications';
+
+  var db;
+
+  // Used to keep track of which view is displayed to avoid uselessly reloading it
+  var current_view_pub_key;
+
+  function openDb() {
+    console.log("openDb ...");
+    var req = indexedDB.open(DB_NAME, DB_VERSION);
+    req.onsuccess = function (evt) {
+      // Better use "this" than "req" to get the result to avoid problems with
+      // garbage collection.
+      // db = req.result;
+      db = this.result;
+      console.log("openDb DONE");
+    };
+    req.onerror = function (evt) {
+      console.error("openDb:", evt.target.errorCode);
+    };
+
+    req.onupgradeneeded = function (evt) {
+      console.log("openDb.onupgradeneeded");
+      var store = evt.currentTarget.result.createObjectStore(
+        DB_STORE_NAME, { keyPath: 'id', autoIncrement: true });
+
+      store.createIndex('biblioid', 'biblioid', { unique: true });
+      store.createIndex('title', 'title', { unique: false });
+      store.createIndex('year', 'year', { unique: false });
+    };
+  }
+
+  /**
+   * @param {string} store_name
+   * @param {string} mode either "readonly" or "readwrite"
+   */
+  function getObjectStore(store_name, mode) {
+    var tx = db.transaction(store_name, mode);
+    return tx.objectStore(store_name);
+  }
+
+  function clearObjectStore(store_name) {
+    var store = getObjectStore(DB_STORE_NAME, 'readwrite');
+    var req = store.clear();
+    req.onsuccess = function(evt) {
+      displayActionSuccess("Store cleared");
+      displayPubList(store);
+    };
+    req.onerror = function (evt) {
+      console.error("clearObjectStore:", evt.target.errorCode);
+      displayActionFailure(this.error);
+    };
+  }
+
+  function getBlob(key, store, success_callback) {
+    var req = store.get(key);
+    req.onsuccess = function(evt) {
+      var value = evt.target.result;
+      if (value)
+        success_callback(value.blob);
+    };
+  }
+
+  /**
+   * @param {IDBObjectStore=} store
+   */
+  function displayPubList(store) {
+    console.log("displayPubList");
+
+    if (typeof store == 'undefined')
+      store = getObjectStore(DB_STORE_NAME, 'readonly');
+
+    var pub_msg = $('#pub-msg');
+    pub_msg.empty();
+    var pub_list = $('#pub-list');
+    pub_list.empty();
+    // Resetting the iframe so that it doesn't display previous content
+    newViewerFrame();
+
+    var req;
+    req = store.count();
+    // Requests are executed in the order in which they were made against the
+    // transaction, and their results are returned in the same order.
+    // Thus the count text below will be displayed before the actual pub list
+    // (not that it is algorithmically important in this case).
+    req.onsuccess = function(evt) {
+      pub_msg.append('<p>There are <strong>' + evt.target.result +
+                     '</strong> record(s) in the object store.</p>');
+    };
+    req.onerror = function(evt) {
+      console.error("add error", this.error);
+      displayActionFailure(this.error);
+    };
+
+    var i = 0;
+    req = store.openCursor();
+    req.onsuccess = function(evt) {
+      var cursor = evt.target.result;
+
+      // If the cursor is pointing at something, ask for the data
+      if (cursor) {
+        console.log("displayPubList cursor:", cursor);
+        req = store.get(cursor.key);
+        req.onsuccess = function (evt) {
+          var value = evt.target.result;
+          var list_item = $('<li>' +
+                            '[' + cursor.key + '] ' +
+                            '(biblioid: ' + value.biblioid + ') ' +
+                            value.title +
+                            '</li>');
+          if (value.year != null)
+            list_item.append(' - ' + value.year);
+
+          if (value.hasOwnProperty('blob') &&
+              typeof value.blob != 'undefined') {
+            var link = $('<a href="' + cursor.key + '">File</a>');
+            link.on('click', function() { return false; });
+            link.on('mouseenter', function(evt) {
+                      setInViewer(evt.target.getAttribute('href')); });
+            list_item.append(' / ');
+            list_item.append(link);
+          } else {
+            list_item.append(" / No attached file");
+          }
+          pub_list.append(list_item);
+        };
+
+        // Move on to the next object in store
+        cursor.continue();
+
+        // This counter serves only to create distinct ids
+        i++;
+      } else {
+        console.log("No more entries");
+      }
+    };
+  }
+
+  function newViewerFrame() {
+    var viewer = $('#pub-viewer');
+    viewer.empty();
+    var iframe = $('<iframe />');
+    viewer.append(iframe);
+    return iframe;
+  }
+
+  function setInViewer(key) {
+    console.log("setInViewer:", arguments);
+    key = Number(key);
+    if (key == current_view_pub_key)
+      return;
+
+    current_view_pub_key = key;
+
+    var store = getObjectStore(DB_STORE_NAME, 'readonly');
+    getBlob(key, store, function(blob) {
+      console.log("setInViewer blob:", blob);
+      var iframe = newViewerFrame();
+
+      // It is not possible to set a direct link to the
+      // blob to provide a mean to directly download it.
+      if (blob.type == 'text/html') {
+        var reader = new FileReader();
+        reader.onload = (function(evt) {
+          var html = evt.target.result;
+          iframe.load(function() {
+            $(this).contents().find('html').html(html);
+          });
+        });
+        reader.readAsText(blob);
+      } else if (blob.type.indexOf('image/') == 0) {
+        iframe.load(function() {
+          var img_id = 'image-' + key;
+          var img = $('<img id="' + img_id + '"/>');
+          $(this).contents().find('body').html(img);
+          var obj_url = window.URL.createObjectURL(blob);
+          $(this).contents().find('#' + img_id).attr('src', obj_url);
+          window.URL.revokeObjectURL(obj_url);
+        });
+      } else if (blob.type == 'application/pdf') {
+        $('*').css('cursor', 'wait');
+        var obj_url = window.URL.createObjectURL(blob);
+        iframe.load(function() {
+          $('*').css('cursor', 'auto');
+        });
+        iframe.attr('src', obj_url);
+        window.URL.revokeObjectURL(obj_url);
+      } else {
+        iframe.load(function() {
+          $(this).contents().find('body').html("No view available");
+        });
+      }
+
+    });
+  }
+
+  /**
+   * @param {string} biblioid
+   * @param {string} title
+   * @param {number} year
+   * @param {string} url the URL of the image to download and store in the local
+   *   IndexedDB database. The resource behind this URL is subjected to the
+   *   "Same origin policy", thus for this method to work, the URL must come from
+   *   the same origin as the web site/app this code is deployed on.
+   */
+  function addPublicationFromUrl(biblioid, title, year, url) {
+    console.log("addPublicationFromUrl:", arguments);
+
+    var xhr = new XMLHttpRequest();
+    xhr.open('GET', url, true);
+    // Setting the wanted responseType to "blob"
+    // http://www.w3.org/TR/XMLHttpRequest2/#the-response-attribute
+    xhr.responseType = 'blob';
+    xhr.onload = function (evt) {
+                           if (xhr.status == 200) {
+                             console.log("Blob retrieved");
+                             var blob = xhr.response;
+                             console.log("Blob:", blob);
+                             addPublication(biblioid, title, year, blob);
+                           } else {
+                             console.error("addPublicationFromUrl error:",
+                                           xhr.responseText, xhr.status);
+                           }
+                         };
+    xhr.send();
+
+    // We can't use jQuery here because as of jQuery 1.8.3 the new "blob"
+    // responseType is not handled.
+    // http://bugs.jquery.com/ticket/11461
+    // http://bugs.jquery.com/ticket/7248
+    // $.ajax({
+    //   url: url,
+    //   type: 'GET',
+    //   xhrFields: { responseType: 'blob' },
+    //   success: function(data, textStatus, jqXHR) {
+    //     console.log("Blob retrieved");
+    //     console.log("Blob:", data);
+    //     // addPublication(biblioid, title, year, data);
+    //   },
+    //   error: function(jqXHR, textStatus, errorThrown) {
+    //     console.error(errorThrown);
+    //     displayActionFailure("Error during blob retrieval");
+    //   }
+    // });
+  }
+
+  /**
+   * @param {string} biblioid
+   * @param {string} title
+   * @param {number} year
+   * @param {Blob=} blob
+   */
+  function addPublication(biblioid, title, year, blob) {
+    console.log("addPublication arguments:", arguments);
+    var obj = { biblioid: biblioid, title: title, year: year };
+    if (typeof blob != 'undefined')
+      obj.blob = blob;
+
+    var store = getObjectStore(DB_STORE_NAME, 'readwrite');
+    var req;
+    try {
+      req = store.add(obj);
+    } catch (e) {
+      if (e.name == 'DataCloneError')
+        displayActionFailure("This engine doesn't know how to clone a Blob, " +
+                             "use Firefox");
+      throw e;
+    }
+    req.onsuccess = function (evt) {
+      console.log("Insertion in DB successful");
+      displayActionSuccess();
+      displayPubList(store);
+    };
+    req.onerror = function() {
+      console.error("addPublication error", this.error);
+      displayActionFailure(this.error);
+    };
+  }
+
+  /**
+   * @param {string} biblioid
+   */
+  function deletePublicationFromBib(biblioid) {
+    console.log("deletePublication:", arguments);
+    var store = getObjectStore(DB_STORE_NAME, 'readwrite');
+    var req = store.index('biblioid');
+    req.get(biblioid).onsuccess = function(evt) {
+      if (typeof evt.target.result == 'undefined') {
+        displayActionFailure("No matching record found");
+        return;
+      }
+      deletePublication(evt.target.result.id, store);
+    };
+    req.onerror = function (evt) {
+      console.error("deletePublicationFromBib:", evt.target.errorCode);
+    };
+  }
+
+  /**
+   * @param {number} key
+   * @param {IDBObjectStore=} store
+   */
+  function deletePublication(key, store) {
+    console.log("deletePublication:", arguments);
+
+    if (typeof store == 'undefined')
+      store = getObjectStore(DB_STORE_NAME, 'readwrite');
+
+    // As per spec http://www.w3.org/TR/IndexedDB/#object-store-deletion-operation
+    // the result of the Object Store Deletion Operation algorithm is
+    // undefined, so it's not possible to know if some records were actually
+    // deleted by looking at the request result.
+    var req = store.get(key);
+    req.onsuccess = function(evt) {
+      var record = evt.target.result;
+      console.log("record:", record);
+      if (typeof record == 'undefined') {
+        displayActionFailure("No matching record found");
+        return;
+      }
+      // Warning: The exact same key used for creation needs to be passed for
+      // the deletion. If the key was a Number for creation, then it needs to
+      // be a Number for deletion.
+      req = store.delete(key);
+      req.onsuccess = function(evt) {
+        console.log("evt:", evt);
+        console.log("evt.target:", evt.target);
+        console.log("evt.target.result:", evt.target.result);
+        console.log("delete successful");
+        displayActionSuccess("Deletion successful");
+        displayPubList(store);
+      };
+      req.onerror = function (evt) {
+        console.error("deletePublication:", evt.target.errorCode);
+      };
+    };
+    req.onerror = function (evt) {
+      console.error("deletePublication:", evt.target.errorCode);
+      };
+  }
+
+  function displayActionSuccess(msg) {
+    msg = typeof msg != 'undefined' ? "Success: " + msg : "Success";
+    $('#msg').html('<span class="action-success">' + msg + '</span>');
+  }
+  function displayActionFailure(msg) {
+    msg = typeof msg != 'undefined' ? "Failure: " + msg : "Failure";
+    $('#msg').html('<span class="action-failure">' + msg + '</span>');
+  }
+  function resetActionStatus() {
+    console.log("resetActionStatus ...");
+    $('#msg').empty();
+    console.log("resetActionStatus DONE");
+  }
+
+  function addEventListeners() {
+    console.log("addEventListeners");
+
+    $('#register-form-reset').click(function(evt) {
+      resetActionStatus();
+    });
+
+    $('#add-button').click(function(evt) {
+      console.log("add ...");
+      var title = $('#pub-title').val();
+      var biblioid = $('#pub-biblioid').val();
+      if (!title || !biblioid) {
+        displayActionFailure("Required field(s) missing");
+        return;
+      }
+      var year = $('#pub-year').val();
+      if (year != '') {
+        // Better use Number.isInteger if the engine has EcmaScript 6
+        if (isNaN(year))  {
+          displayActionFailure("Invalid year");
+          return;
+        }
+        year = Number(year);
+      } else {
+        year = null;
+      }
+
+      var file_input = $('#pub-file');
+      var selected_file = file_input.get(0).files[0];
+      console.log("selected_file:", selected_file);
+      // Keeping a reference on how to reset the file input in the UI once we
+      // have its value, but instead of doing that we rather use a "reset" type
+      // input in the HTML form.
+      //file_input.val(null);
+      var file_url = $('#pub-file-url').val();
+      if (selected_file) {
+        addPublication(biblioid, title, year, selected_file);
+      } else if (file_url) {
+        addPublicationFromUrl(biblioid, title, year, file_url);
+      } else {
+        addPublication(biblioid, title, year);
+      }
+
+    });
+
+    $('#delete-button').click(function(evt) {
+      console.log("delete ...");
+      var biblioid = $('#pub-biblioid-to-delete').val();
+      var key = $('#key-to-delete').val();
+
+      if (biblioid != '') {
+        deletePublicationFromBib(biblioid);
+      } else if (key != '') {
+        // Better use Number.isInteger if the engine has EcmaScript 6
+        if (key == '' || isNaN(key))  {
+          displayActionFailure("Invalid key");
+          return;
+        }
+        key = Number(key);
+        deletePublication(key);
+      }
+    });
+
+    $('#clear-store-button').click(function(evt) {
+      clearObjectStore();
+    });
+
+    var search_button = $('#search-list-button');
+    search_button.click(function(evt) {
+      displayPubList();
+    });
+
+  }
+
+  openDb();
+  addEventListeners();
+
+})(); // Immediately-Invoked Function Expression (IIFE)
+
+ +

{{ LiveSampleLink('Full_IndexedDB_example', "Test the online live demo") }}

+ +

Next step

+ +

If you want to start tinkering with the API, jump in to the reference documentation and check out the different methods.

+ +

See also

+ +

Reference

+ + + +

Tutorials

+ + + +

Related articles

+ + + +

Firefox

+ + diff --git a/files/es/web/api/media_streams_api/index.html b/files/es/web/api/media_streams_api/index.html new file mode 100644 index 0000000000..f8ab5d5c05 --- /dev/null +++ b/files/es/web/api/media_streams_api/index.html @@ -0,0 +1,445 @@ +--- +title: API de MediaStream +slug: WebRTC/MediaStream_API +translation_of: Web/API/Media_Streams_API +--- +

{{SeeCompatTable}}

+

La API de proceso [i]MediaStream, a veces llamadaMedia Stream API o Stream API, es parte de la norma WebRTC [en] y describe un flujo de datos de audio o video, los métodos para trabajar con ellos, las limitaciones asociadas con este tipo de datos, las respuestas de error y éxito al usar los datos asincrónicamente y los eventos que se disparan durante el proceso.

+
+  
+

Conceptos Básicos

+

La API está basada sobre la manipulación de  un objeto {{domxref("MediaStream")}} que representa un flujo de datos de audio o video. Generalmente, un objeto MediaStream es una simple cadena URL que puede ser usada para referirse a datos almacenados en un {{domxref("Archivo")}} DOM  o un objeto {{domxref("Blob")}} creado con {{domxref("window.URL.createObjectURL()")}}, como se lo describe en Obtener el video.

+

Un MediaStream está compuesto por más objetos [i]{{domxref("MediaStreamTrack")}} que representan varias pistas de audio o video. Cada MediaStreamTrack puede tener uno o más canales. El canal representa la unidad menor de un flujo de medio, como una señal de audio asociada a un parlante específico, como el izquierdo o el derecho en una pista de audio estéreo.

+

Los objetos MediaStream poseen una sola entrada y salida[ii]. Un objeto MediaStream creado con getUserMedia() se denomina local y tiene como origen de entrada una de las cámaras o micrófonos del usuario. Un MediaStream no local puede estar representando un elemento de medio como {{HTMLElement("video")}} o {{HTMLElement("audio")}}, un flujo originado en la red y obtenido a través de la [iii]PeerConnection API o un flujo creado con la API de Audio Web [iv]{{domxref("MediaStreamAudioSourceNode")}}. La salida de un objeto MediaStream está enlazada a un consumidor. El mismo puede ser un elemento de medio  como <audio> o <video>, la PeerConnection API de WebRTC o una API de Audio Web [v]{{domxref("MediaStreamAudioDestinationNode")}}.

+
+  
+

Referencia

+
+ +
+

Pista de MediaStream

+

Una MediaStreamTrack puede ser de dos tipos, de audio o video, y representa el origen del medio, como una cámara.

+

Atributos

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AtributoTipoDescripción
enabledBoolean True si la pista sigue asociada a su fuente.
idDOMString, read-onlyUn globally unique identifier (GUID) que describe la pista de medios.
kindDOMString, read-onlyEl audio o video para la pista de origen.
labelDOMString, read-onlyUna cadena de tipo usuario-asignada que identifica la pista de origen, como en "internal microphone." 
onendedEventHandlerManeja el evento finalizado cuando se lo activa en el objeto MediaStreamTrack.
onmute EventHandlerManeja el evento mudo del objeto MediaStreamTrack. 
onoverconstrainedEventHandlerManeja el evento superrestricto cuando se lo activa en el objeto MediaStreamTrack.
onstartedEventHandlerManeja el evento iniciado cuando se lo activa en el objeto MediaStreamTrack.
onunmute EventHandlerManjea el evento sin enmudecer cuando se lo activa en el objeto MediaStreamTrack.
readyStateunsigned short, read-only +

Valores para la pista lista:

+
    +
  • live - la pista está activa; la salida se puede activar on y off con el atributo habilitado.
  • +
  • muted - el origen del medio subyacente de la pista no puede proveer temporalmente datos en tiempo real.
  • +
+
sourceIdDOMString, read-onlyLa identidad de este origen que es único para esta aplicación y persistente. Se recomienda una GUID pero no es obligatoria.
sourceTypeSourceTypeEnum, read-onlyContiene el tipo de información del origen, si es que existe.
+

Eventos

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
EventoInterfazDescripción 
startedEventEl objeto MediaStreamTrack no es más "new" en el readyState.
mutedEventEl origen del objeto MediaStreamTrack no puede proveer datos temporalmente.
unmuted EventEl origen del objeto MediaStreamTrack a recomenzado a proveer datos
overconstrainedEventEl origen del objeto MediaStreamTrack no puede ser confugurado para que encaje en las restricciones impuestas por la pista. Esto podría ocurrir con la altura en el caso de un video, entre otras posibilidades.
endedEvent +

El origen del objeto MediaStreamTrack no proveerá datos; puede ocurrir por lo siguiente:

+
    +
  • el usuario a deshabilitado los permisos de la aplicación
  • +
  • el dispositivo de origen está desconectado
  • +
  • el peer remoto no transmite datos
  • +
  • se llamó el método stop()
  • +
+
+

appendConstraint()

+

Agrega la restricción al final de la lista. Esto sólo es un método para añadir restricciones optativas.

+
+
+ Parámetros
+
+ constraintName DOMString, required.
+
+ constraintValue Primitive (DOMString, float, etc.) or MinMaxConstraint, required.
+
+ Devuelve
+
+ void
+
+ Excepciones
+
+ None.
+
+

applyConstraints()

+

Aplica una lista de opciones optativas a la pista. Sobrescribe cualquier otra restricción optativa ya existente en la pista.

+
+
+ Parámetros
+
+ constraints MediaTrackConstraints, required.
+
+ Devuelve
+
+ void
+
+ Excepciones
+
+ None.
+
+

constraints()

+

Devuelte todas las [i]restricciones en la pista, obligatorias y optativas. Si tanto mandatory u optional no tienen restricciones, ese campo será undefined. Si ninguno tiene restricciones, entonces constraints() devolverá null.

+
+
+ Parámetros
+
+ None.
+
+ Devuelve
+
+ MediaTrackConstraints or null
+
+ Excepciones
+
+ None.
+
+

getConstraint()

+

Trae una restricción específica, por nombre, de la pista. Este método puede devolver una de varias posibilidades:

+ +
+
+ Parámetros
+
+ constraintName DOMString, required.
+
+ mandatory boolean, optional, default false.
+
+ Devuelve
+
+ Any of the possibilities mentioned above.
+
+ Excepciones
+
+ None.
+
+

--

+

stop()

+

Detiene permanentemente la creación de datos para las pistas y remueve las referencias a los orígines.

+
+
+ Parámetros
+
+ None.
+
+ Devuelve
+
+ Void.
+
+ Excepciones
+
+ None.
+
+

Lista de pistas de MediaStream

+

Un MediaStream tiene dos objetos MediaStreamTrackList, uno para la pista de video y otro para la de audio.

+ + + + + + + + + + + + + + + + + + + + + + + + + +
Atributo TipoDescripción
lengthunsigned long, read-only El número de pistas en la lista.
onaddtrack EventHandlerManeja el evento addtrack.
onremovetrack EventHandlerManeja el evento removetrack.
+

Eventos

+ + + + + + + + + + + + + + + + + + + + +
EventoInterfazDescripción 
addtrack MediaStreamTrackEvent Se agregó una MediaStreamTrack a la lista.
removetrack MediaStreamTrackEventSe removió una MediaStreamTrack de la lista.
+

add()

+

Agrega una MediaStreamTrack a la lista de pistas.

+
+
+ Parámetros
+
+ MediaStreamTrack track, required.
+
+ Devuelve
+
+ Void.
+
+ Excepciones
+
+ INVALID_STATE_ERR if the stream is finished (all tracks have ended).
+
+

item()

+

Devuelve la MediaStreamTrack al valor de índice (index) especificado.

+
+
+ Parámetros
+
+ unsigned long index, required.
+
+ Devuelve
+
+ MediaStreamTrack
+
+ Excepciones
+
+ None.
+
+

Remove()

+

Remueve una MediaStreamTrack de la lista de pistas.

+
+
+ Parámetros
+
+ MediaStreamTrack track, required.
+
+ Devuelve
+
+ Void.
+
+ Excepciones
+
+ INVALID_STATE_ERR if the stream is finished (all tracks have ended).
+
+

Compatibilidad con navegadores

+

{{ CompatibilityTable() }}

+
+ + + + + + + + + + + + + + + + + + + +
FeatureChromeFirefox (Gecko)Internet ExplorerOpera Safari (WebKit)
Stream API 21{{ property_prefix("webkit") }} nightly 18{{ property_prefix("moz") }} {{ CompatUnknown() }} 12{{ CompatUnknown() }} 
+
+
+ + + + + + + + + + + + + + + + + + + +
FeatureAndroidFirefox Mobile (Gecko)IE PhoneOpera MobileSafari Mobile
Stream API {{ CompatNo() }} {{ CompatUnknown() }}{{ CompatUnknown() }} {{ CompatNo() }} {{ CompatNo() }} 
+
+

Actualmente, el uso de WebRTC para acceder a la cámara está soportado en Chrome, Opera y Firefox Nightly 18. Habilitar WebRTC en Firefox Nightly requiere que usted establezca una advertencia (flag) en la configuración:

+ +
+

 

+
+
+
+
+

[i] en castellano,Flujo o transmisión de [multi] Medios

+
+
+
+
+

[i] en castellano, Pista de Flujo de Media

+
+
+

[ii] del inglés, input youtput

+
+
+

[iii] en castellano, API de Conexión de Pares

+
+
+

[iv] en castellano, Nodo de origen del Audio MediaStream

+
+
+

[v] en castellano, Nodo de destino del Audio MediaStream

+
+
+
+
+

[i] del inglés,constraints

+
+
+
+
+

 

+

Ver también

+ diff --git a/files/es/web/api/navigator/geolocation/index.html b/files/es/web/api/navigator/geolocation/index.html new file mode 100644 index 0000000000..94c22ef6b0 --- /dev/null +++ b/files/es/web/api/navigator/geolocation/index.html @@ -0,0 +1,106 @@ +--- +title: window.navigator.geolocation +slug: Web/API/NavigatorGeolocation/geolocation +translation_of: Web/API/Navigator/geolocation +--- +

{{APIRef("Geolocation API")}}
+ La propiedad de sólo lectura Navigator.geolocation devuelve un objeto Geolocation que proporciona acceso web a la ubicación de un dispositivo. Esto permite ofrecer al sitio web o aplicación resultados personalizados basados en la ubicación del usuario.

+ +
+

Nota: Por motivos de seguridad, cuando una página web trata de acceder a la información de ubicación, se solicita permiso al usuario. Cada navegador tiene sus propias reglas sobre cómo y cuándo obtener dicho permiso.

+
+ +

Sintaxis

+ +
geo = navigator.geolocation
+
+ +

Métodos

+ +
+
{{domxref("geolocation.getCurrentPosition", "geolocation.getCurrentPosition()")}}
+
Utilizado para obtener la posición actual.
+
{{domxref("geolocation.watchPosition", "geolocation.watchPosition()")}}
+
Utilizado para asignar un manejador para dar seguimiento a cualquier cambio de ubicación.
+
{{domxref("geolocation.clearWatch", "geolocation.clearWatch()")}}
+
Utilizado para eliminar un manejador asignado a los cambios de ubicación.
+
+ +

Especificaciones

+ + + + + + + + + + + + + + + + +
EspecificaciónEstadoComentario
{{SpecName('Geolocation')}}{{Spec2('Geolocation')}}Especificación inicial
+ +

Compatibilidad en navegadores

+ +

{{ CompatibilityTable() }}

+ +
+ + + + + + + + + + + + + + + + + + + +
CaracterísticaChromeFirefox (Gecko)Internet ExplorerOperaSafari
Soporte básico5{{CompatGeckoDesktop("1.9.1")}}910.60
+ {{CompatNo}} 15.0
+ 16.0
5
+
+ +
+ + + + + + + + + + + + + + + + + + + + + +
CaracterísticaAndroidChrome para AndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Soporte básico2.150{{CompatGeckoMobile("4")}}1010.603.2
+
+ +

Ver también

+ + diff --git a/files/es/web/api/navigatorgeolocation/geolocation/index.html b/files/es/web/api/navigatorgeolocation/geolocation/index.html deleted file mode 100644 index 94c22ef6b0..0000000000 --- a/files/es/web/api/navigatorgeolocation/geolocation/index.html +++ /dev/null @@ -1,106 +0,0 @@ ---- -title: window.navigator.geolocation -slug: Web/API/NavigatorGeolocation/geolocation -translation_of: Web/API/Navigator/geolocation ---- -

{{APIRef("Geolocation API")}}
- La propiedad de sólo lectura Navigator.geolocation devuelve un objeto Geolocation que proporciona acceso web a la ubicación de un dispositivo. Esto permite ofrecer al sitio web o aplicación resultados personalizados basados en la ubicación del usuario.

- -
-

Nota: Por motivos de seguridad, cuando una página web trata de acceder a la información de ubicación, se solicita permiso al usuario. Cada navegador tiene sus propias reglas sobre cómo y cuándo obtener dicho permiso.

-
- -

Sintaxis

- -
geo = navigator.geolocation
-
- -

Métodos

- -
-
{{domxref("geolocation.getCurrentPosition", "geolocation.getCurrentPosition()")}}
-
Utilizado para obtener la posición actual.
-
{{domxref("geolocation.watchPosition", "geolocation.watchPosition()")}}
-
Utilizado para asignar un manejador para dar seguimiento a cualquier cambio de ubicación.
-
{{domxref("geolocation.clearWatch", "geolocation.clearWatch()")}}
-
Utilizado para eliminar un manejador asignado a los cambios de ubicación.
-
- -

Especificaciones

- - - - - - - - - - - - - - - - -
EspecificaciónEstadoComentario
{{SpecName('Geolocation')}}{{Spec2('Geolocation')}}Especificación inicial
- -

Compatibilidad en navegadores

- -

{{ CompatibilityTable() }}

- -
- - - - - - - - - - - - - - - - - - - -
CaracterísticaChromeFirefox (Gecko)Internet ExplorerOperaSafari
Soporte básico5{{CompatGeckoDesktop("1.9.1")}}910.60
- {{CompatNo}} 15.0
- 16.0
5
-
- -
- - - - - - - - - - - - - - - - - - - - - -
CaracterísticaAndroidChrome para AndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Soporte básico2.150{{CompatGeckoMobile("4")}}1010.603.2
-
- -

Ver también

- - diff --git a/files/es/web/api/navigatorgeolocation/index.html b/files/es/web/api/navigatorgeolocation/index.html deleted file mode 100644 index 0f8895b7b8..0000000000 --- a/files/es/web/api/navigatorgeolocation/index.html +++ /dev/null @@ -1,107 +0,0 @@ ---- -title: NavigatorGeolocation -slug: Web/API/NavigatorGeolocation -tags: - - API -translation_of: Web/API/Geolocation -translation_of_original: Web/API/NavigatorGeolocation ---- -

{{APIRef("Geolocation API")}}

- -

NavigatorGeolocation  contains a creation method allowing objects implementing it to obtain a {{domxref("Geolocation")}} instance.

- -

There is no object of type NavigatorGeolocation, but some interfaces, like {{domxref("Navigator")}} implements it.

- -

Properties

- -

The NavigatorGeolocation interface doesn't inherit any property.

- -
-
{{domxref("NavigatorGeolocation.geolocation")}} {{readonlyInline}}
-
Returns a {{domxref("Geolocation")}} object allowing accessing the location of the device.
-
- -

Methods

- -

The NavigatorGeolocation interface neither implements, nor inherit any method.

- -

Specifications

- - - - - - - - - - - - - - - - -
SpecificationStatusComment
{{SpecName('Geolocation', '#navi-geo', 'NavigatorGeolocation')}}{{Spec2('Geolocation')}}Initial specification.
- -

Browser compatibility

- -

{{ CompatibilityTable() }}

- -
- - - - - - - - - - - - - - - - - - - -
FeatureChromeFirefox (Gecko)Internet ExplorerOperaSafari
Basic support5{{CompatGeckoDesktop("1.9.1")}}910.60
- Removed in 15.0
- Reintroduced in 16.0
5
-
- -
- - - - - - - - - - - - - - - - - - - - - -
FeatureAndroidChrome for AndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Basic support{{CompatUnknown()}}{{CompatUnknown()}}{{CompatGeckoMobile("4")}}{{CompatUnknown()}}10.60{{CompatUnknown()}}
-
- -

See also

- - - -

 

diff --git a/files/es/web/api/navigatoronline/eventos_online_y_offline/index.html b/files/es/web/api/navigatoronline/eventos_online_y_offline/index.html deleted file mode 100644 index d4f8a77824..0000000000 --- a/files/es/web/api/navigatoronline/eventos_online_y_offline/index.html +++ /dev/null @@ -1,91 +0,0 @@ ---- -title: Eventos online y offline -slug: Web/API/NavigatorOnLine/Eventos_online_y_offline -tags: - - AJAX - - DOM - - Desarrollo_Web - - Todas_las_Categorías -translation_of: Web/API/NavigatorOnLine/Online_and_offline_events ---- -

{{ Fx_minversion_header(3) }} -Firefox 3 implementa eventos Online/Offline de la especificacióbn WHATWG Web Applications 1.0. -

-

Descripción

-

Para poder construir una aplicación web offline-funcional, necesitas conocer cuándo está tu aplicación realmente offline. Además, también necesitas conocer cuándo vuelve la aplicación al estado 'online' nuevamente. A efectos prácticos, los requisitos son los siguientes: -

-
  1. Necesitas conocer cuándo el usuario vuelve a estar online, para que puedas re-sincronizar con el servidor. -
  2. Necesitas conocer cuándo el usuario está offline, para así estar seguro de poner en cola las peticiones al servidor para más tarde. -
-

Es este proceso el que los eventos online/offline ayudan a trivializar. -

Tu aplicación web quizás necesite establecer que ciertos documentos se mantengan en la caché de recursos offline. Para hacerlo, incluye elementos LINK en tu sección HEAD de la siguiente manera: -

-
<link rel="offline-resource" href="mi_recurso>
-
-

Cuando Firefox 3 o superiores procesan el HTML, esto causará que el recurso referenciado se mantenga en caché para su uso offline en un recurso especial de la caché. -

-

API

- -

navigator.onLine es una propiedad que mantiene un valor true/false (true para online, false para offline). Esta propiedad se actualiza siempre que el usuario cambia hacia "Modo sin conexión" seleccionando el menú correspondiente (Archivo -> Trabajar sin conexión en Firefox). -

Además, esta propiedad se debería actualizar siempre que el navegador no sea capaz de conectarse a la red. De acuerdo con la especificación: -

-
-El atributo navigator.onLine debe devolver false si el agente de usuario no contactará con la red cuando el usuario siga los enlaces o cuando un script solicite una página remota (o sepa que intentarlo fallaría)... -
-

Firefox 2 actualiza esta propiedad cuando cambia desde/hacia el modo offline del navegador, y cuando pierde/recupera la conectividad de la red en Windows y Linux. -

Esta propiedad existía en versiones más antiguas de Firefox e Internet Explorer (la especificación surgió a partir estas implementaciones previas), así que puedes comenzar a usarla inmediatamente. La detección automática del estado de la red se implementó en Firefox 2. -

-

eventos "online" y "offline"

-

Firefox 3 introduce dos nuevos eventos: "online" y "offline". Estos dos eventos son iniciados en el <body> de cada página cuando el usuario cambia de modo online a offline. Además, los eventos emergen desde document.body a document, terminando en window. Ambos eventos son no-cancelables (no se puede evitar que el usuario pase a modo online u offline). -

Puedes registrar listeners para estos eventos de varias maneras habituales: -

- -

Ejemplo:

-

Hay una prueba sencilla que puedes ejecutar para verificar que los eventos están funcionando. -

-

-
 <!doctype html>
- <html>
- <head>
-   <script>
-     function updateOnlineStatus(msg) {
-       var status = document.getElementById("status");
-       var condition = navigator.onLine ? "ONLINE" : "OFFLINE";
-       status.setAttribute("class", condition);
-       var state = document.getElementById("state");
-       state.innerHTML = condition;
-       var log = document.getElementById("log");
-       log.appendChild(document.createTextNode("Event: " + msg + "; status=" + condition + "\n"));
-     }
-     function loaded() {
-       updateOnlineStatus("load");
-       document.body.addEventListener("offline", function () {
-         updateOnlineStatus("offline")
-       }, false);
-       document.body.addEventListener("online", function () {
-         updateOnlineStatus("online")
-       }, false);
-     }
-   </script>
-   <style>...</style>
- </head>
- <body onload="loaded()">
-   <div id="status"><p id="state"></p></div>
-   <div id="log"></div>
- </body>
- </html>
-
-

Referencias

- -


-

-
-
-{{ languages( { "en": "en/Online_and_offline_events", "fr": "fr/\u00c9v\u00e8nements_online_et_offline", "ja": "ja/Online_and_offline_events", "pl": "pl/Zdarzenia_online_i_offline", "pt": "pt/Eventos_online_e_offline" } ) }} diff --git a/files/es/web/api/navigatoronline/online_and_offline_events/index.html b/files/es/web/api/navigatoronline/online_and_offline_events/index.html new file mode 100644 index 0000000000..d4f8a77824 --- /dev/null +++ b/files/es/web/api/navigatoronline/online_and_offline_events/index.html @@ -0,0 +1,91 @@ +--- +title: Eventos online y offline +slug: Web/API/NavigatorOnLine/Eventos_online_y_offline +tags: + - AJAX + - DOM + - Desarrollo_Web + - Todas_las_Categorías +translation_of: Web/API/NavigatorOnLine/Online_and_offline_events +--- +

{{ Fx_minversion_header(3) }} +Firefox 3 implementa eventos Online/Offline de la especificacióbn WHATWG Web Applications 1.0. +

+

Descripción

+

Para poder construir una aplicación web offline-funcional, necesitas conocer cuándo está tu aplicación realmente offline. Además, también necesitas conocer cuándo vuelve la aplicación al estado 'online' nuevamente. A efectos prácticos, los requisitos son los siguientes: +

+
  1. Necesitas conocer cuándo el usuario vuelve a estar online, para que puedas re-sincronizar con el servidor. +
  2. Necesitas conocer cuándo el usuario está offline, para así estar seguro de poner en cola las peticiones al servidor para más tarde. +
+

Es este proceso el que los eventos online/offline ayudan a trivializar. +

Tu aplicación web quizás necesite establecer que ciertos documentos se mantengan en la caché de recursos offline. Para hacerlo, incluye elementos LINK en tu sección HEAD de la siguiente manera: +

+
<link rel="offline-resource" href="mi_recurso>
+
+

Cuando Firefox 3 o superiores procesan el HTML, esto causará que el recurso referenciado se mantenga en caché para su uso offline en un recurso especial de la caché. +

+

API

+ +

navigator.onLine es una propiedad que mantiene un valor true/false (true para online, false para offline). Esta propiedad se actualiza siempre que el usuario cambia hacia "Modo sin conexión" seleccionando el menú correspondiente (Archivo -> Trabajar sin conexión en Firefox). +

Además, esta propiedad se debería actualizar siempre que el navegador no sea capaz de conectarse a la red. De acuerdo con la especificación: +

+
+El atributo navigator.onLine debe devolver false si el agente de usuario no contactará con la red cuando el usuario siga los enlaces o cuando un script solicite una página remota (o sepa que intentarlo fallaría)... +
+

Firefox 2 actualiza esta propiedad cuando cambia desde/hacia el modo offline del navegador, y cuando pierde/recupera la conectividad de la red en Windows y Linux. +

Esta propiedad existía en versiones más antiguas de Firefox e Internet Explorer (la especificación surgió a partir estas implementaciones previas), así que puedes comenzar a usarla inmediatamente. La detección automática del estado de la red se implementó en Firefox 2. +

+

eventos "online" y "offline"

+

Firefox 3 introduce dos nuevos eventos: "online" y "offline". Estos dos eventos son iniciados en el <body> de cada página cuando el usuario cambia de modo online a offline. Además, los eventos emergen desde document.body a document, terminando en window. Ambos eventos son no-cancelables (no se puede evitar que el usuario pase a modo online u offline). +

Puedes registrar listeners para estos eventos de varias maneras habituales: +

+ +

Ejemplo:

+

Hay una prueba sencilla que puedes ejecutar para verificar que los eventos están funcionando. +

+

+
 <!doctype html>
+ <html>
+ <head>
+   <script>
+     function updateOnlineStatus(msg) {
+       var status = document.getElementById("status");
+       var condition = navigator.onLine ? "ONLINE" : "OFFLINE";
+       status.setAttribute("class", condition);
+       var state = document.getElementById("state");
+       state.innerHTML = condition;
+       var log = document.getElementById("log");
+       log.appendChild(document.createTextNode("Event: " + msg + "; status=" + condition + "\n"));
+     }
+     function loaded() {
+       updateOnlineStatus("load");
+       document.body.addEventListener("offline", function () {
+         updateOnlineStatus("offline")
+       }, false);
+       document.body.addEventListener("online", function () {
+         updateOnlineStatus("online")
+       }, false);
+     }
+   </script>
+   <style>...</style>
+ </head>
+ <body onload="loaded()">
+   <div id="status"><p id="state"></p></div>
+   <div id="log"></div>
+ </body>
+ </html>
+
+

Referencias

+ +


+

+
+
+{{ languages( { "en": "en/Online_and_offline_events", "fr": "fr/\u00c9v\u00e8nements_online_et_offline", "ja": "ja/Online_and_offline_events", "pl": "pl/Zdarzenia_online_i_offline", "pt": "pt/Eventos_online_e_offline" } ) }} diff --git a/files/es/web/api/node/elementopadre/index.html b/files/es/web/api/node/elementopadre/index.html deleted file mode 100644 index 2a2e947a8d..0000000000 --- a/files/es/web/api/node/elementopadre/index.html +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: Node.parentElement -slug: Web/API/Node/elementoPadre -tags: - - API - - DOM - - NecesitaCompatiblidadNavegador - - Nodo - - Propiedad -translation_of: Web/API/Node/parentElement ---- -
-
{{APIRef("DOM")}}
-
- -

La propiedad de sólo lectura de Nodo.parentElement devuelve el nodo padre del DOM {{domxref("Element")}}, o null, si el nodo no tiene padre o si el padre no es un {{domxref("Element")}} DOM .

- -

Sintaxis

- -
elementoPadre = node.parentElement
- -

El elementoPadre es el padre del nodo actual. Esto es siempre un objecto {{domxref("Element")}} DOM, o null.

- -

Ejemplo

- -
if (node.parentElement) {
-    node.parentElement.style.color = "red";
-}
- -

Compatibilidad en navegadores

- -

En algunos navegadores, la propiedad elementoPadre es solo definida en nodos que ellos mismos son {{domxref("Element")}}. En particular, esto no está definido en nodos de texto.

- -

{{Compat("api.Node.parentElement")}}

- -

Especificación

- - - -

Ver también

- - diff --git a/files/es/web/api/node/insertarantes/index.html b/files/es/web/api/node/insertarantes/index.html deleted file mode 100644 index 102d4dfbdd..0000000000 --- a/files/es/web/api/node/insertarantes/index.html +++ /dev/null @@ -1,172 +0,0 @@ ---- -title: Node.insertBefore() -slug: Web/API/Node/insertarAntes -tags: - - API - - DOM - - Nodo - - Referencia - - metodo -translation_of: Web/API/Node/insertBefore ---- -
{{APIRef("DOM")}}
- -

El método Node.insertBefore() inserta un nodo antes del nodo de referencia como hijo de un nodo padre indicado. Si el nodo hijo es una referencia a un nodo ya existente en el documento, insertBefore() lo mueve de la posición actual a la nueva posición (no hay necesidad de eliminar el nodo de su nodo padre antes de agregarlo al algún nodo nuevo).

- -

Esto significa que el nodo no puede estar en dos puntos del documento al simultáneamente. Por lo que si el nodo ya tiene un padre, primero se elimina el nodo, y luego se inserta en la nueva posición. {{domxref("Node.cloneNode()")}} puede utilizarse para hacer una copia de un nodo antes de insertarlo en un nuevo padre. Ten en cuenta que las copias hechas con cloneNode() no se mantendrán sincronizadas automáticamente.

- -

Si el nodo de referencia es null, el nodo indicado se añadirá al final de la lista de hijos del nodo padre especificado.

- -

Si el hijo proporcionado es un {{domxref("DocumentFragment")}}, el contenido completo del DocumentFragment se moverá a la lista de hijos del nodo padre indicado.

- -

Sintaxis

- -
var insertedNode = parentNode.insertBefore(newNode, referenceNode);
-
- - - -

Si referenceNode es null, el newNode se insertará al final de la lista de nodos hijos.

- -
-

referenceNode no es un parámetro opcional -- debes pasar explícitamente un Node o null. No proporcionándolo o pasando valores no válidos podría provocar un comportamiento distinto en diferentes versiones de navegadores.

-
- -

Valor devuelto

- -

El valor devuelto es el hijo añadido excepto cuando newNode es un {{domxref("DocumentFragment")}}, en cuyo caso se devuelve un {{domxref("DocumentFragment")}}.

- -

Ejemplo

- -

Ejemplo 1

- -
<div id="parentElement">
-   <span id="childElement">foo bar</span>
-</div>
-
-<script>
-// Crear el nodo a insertar
-var newNode = document.createElement("span");
-
-// Obtener una referencia al nodo padre
-var parentDiv = document.getElementById("childElement").parentNode;
-
-// Comienzo del test [ 1 ] : Existe un childElement --> Todo funciona correctamente
-var sp2 = document.getElementById("childElement");
-parentDiv.insertBefore(newNode, sp2);
-// Fin del test [ 1 ]
-
-// Comienzo del test [ 2 ] : childElement no es del tipo undefined
-var sp2 = undefined; // No existe un nodo con id "childElement"
-parentDiv.insertBefore(newNode, sp2); // Implicit dynamic cast to type Node
-// Fin del test [ 2 ]
-
-// Comienzo del test [ 3 ] : childElement es de Tipo "undefined" ( string )
-var sp2 = "undefined"; // No existe un nodo con id "childElement"
-parentDiv.insertBefore(newNode, sp2); // Genera "Type Error: Invalid Argument"
-// Fin del test [ 3 ]
-</script>
-
- -

Ejemplo 2

- -
<div id="parentElement">
-  <span id="childElement">foo bar</span>
-</div>
-
-<script>
-// Crea un nuevo, elemento <span>
-var sp1 = document.createElement("span");
-
-// Obtener una referencia al elemento, antes de donde queremos insertar el elemento
-var sp2 = document.getElementById("childElement");
-// Obtener una referencia al nodo padre
-var parentDiv = sp2.parentNode;
-
-// Inserta un nuevo elemento en el DOM antes de sp2
-parentDiv.insertBefore(sp1, sp2);
-</script>
-
- -

No existe el método insertAfter().  Puede ser emulado mediante la combinación del método con {{domxref("Node.nextSibling()")}}.

- -

En el ejemplo anterior, sp1 podría insertarse después de sp2 usando:

- -
parentDiv.insertBefore(sp1, sp2.nextSibling);
- -

Si sp2 no tiene ningún hermano depués de él, entonces debe ser el último hijo — sp2.nextSibling devuelve null, y sp1 se inserta al final de la lista de nodos hijos (inmediatamente después de sp2).

- -

Ejemplo 3

- -

Inserta un elemento antes del primer elemento hijo, utilizando la propiedad firstChild.

- -
// Obtener una referencia al elemento en el que se quiere insertar un nuevo nodo
-var parentElement = document.getElementById('parentElement');
-// Obtener una referencia al primer hijo
-var theFirstChild = parentElement.firstChild;
-
-// Crear un nuevo elemento
-var newElement = document.createElement("div");
-
-// Insertar el nuevo elemento antes que el primer hijo
-parentElement.insertBefore(newElement, theFirstChild);
-
- -

Cuando el elemento no tiene ub primer hijo, entonces firstChild es null. Aun así, el elemento se añade al padre después del último hijo. Puesto que el elemento padre no tenía primer hijo, tampoco tiene último hijo. Por tanto, el nuevo elemento es el único elemento después de ser insertado.

- -

Compatibilidad en navegadores

- -

{{Compat("api.Node.insertBefore")}}

- -

Especificaciones

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EspecificaciónEstadoComentario
{{SpecName('DOM WHATWG','#dom-node-insertbefore','Node.insertBefore')}}{{Spec2('DOM WHATWG')}}Corrige errores en el algoritmo de inserción
{{SpecName('DOM4','#dom-node-insertbefore','Node.insertBefore')}}{{Spec2('DOM4')}}Describe el algoritmo con mayor detalle
{{SpecName('DOM3 Core','core.html#ID-952280727','Node.insertBefore')}}{{Spec2('DOM3 Core')}}Sin cambios notables
{{SpecName('DOM2 Core','core.html#ID-952280727','Node.insertBefore')}}{{Spec2('DOM2 Core')}}Sin cambios notables
{{SpecName('DOM1','level-one-core.html#method-insertBefore','Node.insertBefore')}}{{Spec2('DOM1')}}Introducido
- -

Ver también

- - diff --git a/files/es/web/api/node/insertbefore/index.html b/files/es/web/api/node/insertbefore/index.html new file mode 100644 index 0000000000..102d4dfbdd --- /dev/null +++ b/files/es/web/api/node/insertbefore/index.html @@ -0,0 +1,172 @@ +--- +title: Node.insertBefore() +slug: Web/API/Node/insertarAntes +tags: + - API + - DOM + - Nodo + - Referencia + - metodo +translation_of: Web/API/Node/insertBefore +--- +
{{APIRef("DOM")}}
+ +

El método Node.insertBefore() inserta un nodo antes del nodo de referencia como hijo de un nodo padre indicado. Si el nodo hijo es una referencia a un nodo ya existente en el documento, insertBefore() lo mueve de la posición actual a la nueva posición (no hay necesidad de eliminar el nodo de su nodo padre antes de agregarlo al algún nodo nuevo).

+ +

Esto significa que el nodo no puede estar en dos puntos del documento al simultáneamente. Por lo que si el nodo ya tiene un padre, primero se elimina el nodo, y luego se inserta en la nueva posición. {{domxref("Node.cloneNode()")}} puede utilizarse para hacer una copia de un nodo antes de insertarlo en un nuevo padre. Ten en cuenta que las copias hechas con cloneNode() no se mantendrán sincronizadas automáticamente.

+ +

Si el nodo de referencia es null, el nodo indicado se añadirá al final de la lista de hijos del nodo padre especificado.

+ +

Si el hijo proporcionado es un {{domxref("DocumentFragment")}}, el contenido completo del DocumentFragment se moverá a la lista de hijos del nodo padre indicado.

+ +

Sintaxis

+ +
var insertedNode = parentNode.insertBefore(newNode, referenceNode);
+
+ + + +

Si referenceNode es null, el newNode se insertará al final de la lista de nodos hijos.

+ +
+

referenceNode no es un parámetro opcional -- debes pasar explícitamente un Node o null. No proporcionándolo o pasando valores no válidos podría provocar un comportamiento distinto en diferentes versiones de navegadores.

+
+ +

Valor devuelto

+ +

El valor devuelto es el hijo añadido excepto cuando newNode es un {{domxref("DocumentFragment")}}, en cuyo caso se devuelve un {{domxref("DocumentFragment")}}.

+ +

Ejemplo

+ +

Ejemplo 1

+ +
<div id="parentElement">
+   <span id="childElement">foo bar</span>
+</div>
+
+<script>
+// Crear el nodo a insertar
+var newNode = document.createElement("span");
+
+// Obtener una referencia al nodo padre
+var parentDiv = document.getElementById("childElement").parentNode;
+
+// Comienzo del test [ 1 ] : Existe un childElement --> Todo funciona correctamente
+var sp2 = document.getElementById("childElement");
+parentDiv.insertBefore(newNode, sp2);
+// Fin del test [ 1 ]
+
+// Comienzo del test [ 2 ] : childElement no es del tipo undefined
+var sp2 = undefined; // No existe un nodo con id "childElement"
+parentDiv.insertBefore(newNode, sp2); // Implicit dynamic cast to type Node
+// Fin del test [ 2 ]
+
+// Comienzo del test [ 3 ] : childElement es de Tipo "undefined" ( string )
+var sp2 = "undefined"; // No existe un nodo con id "childElement"
+parentDiv.insertBefore(newNode, sp2); // Genera "Type Error: Invalid Argument"
+// Fin del test [ 3 ]
+</script>
+
+ +

Ejemplo 2

+ +
<div id="parentElement">
+  <span id="childElement">foo bar</span>
+</div>
+
+<script>
+// Crea un nuevo, elemento <span>
+var sp1 = document.createElement("span");
+
+// Obtener una referencia al elemento, antes de donde queremos insertar el elemento
+var sp2 = document.getElementById("childElement");
+// Obtener una referencia al nodo padre
+var parentDiv = sp2.parentNode;
+
+// Inserta un nuevo elemento en el DOM antes de sp2
+parentDiv.insertBefore(sp1, sp2);
+</script>
+
+ +

No existe el método insertAfter().  Puede ser emulado mediante la combinación del método con {{domxref("Node.nextSibling()")}}.

+ +

En el ejemplo anterior, sp1 podría insertarse después de sp2 usando:

+ +
parentDiv.insertBefore(sp1, sp2.nextSibling);
+ +

Si sp2 no tiene ningún hermano depués de él, entonces debe ser el último hijo — sp2.nextSibling devuelve null, y sp1 se inserta al final de la lista de nodos hijos (inmediatamente después de sp2).

+ +

Ejemplo 3

+ +

Inserta un elemento antes del primer elemento hijo, utilizando la propiedad firstChild.

+ +
// Obtener una referencia al elemento en el que se quiere insertar un nuevo nodo
+var parentElement = document.getElementById('parentElement');
+// Obtener una referencia al primer hijo
+var theFirstChild = parentElement.firstChild;
+
+// Crear un nuevo elemento
+var newElement = document.createElement("div");
+
+// Insertar el nuevo elemento antes que el primer hijo
+parentElement.insertBefore(newElement, theFirstChild);
+
+ +

Cuando el elemento no tiene ub primer hijo, entonces firstChild es null. Aun así, el elemento se añade al padre después del último hijo. Puesto que el elemento padre no tenía primer hijo, tampoco tiene último hijo. Por tanto, el nuevo elemento es el único elemento después de ser insertado.

+ +

Compatibilidad en navegadores

+ +

{{Compat("api.Node.insertBefore")}}

+ +

Especificaciones

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
EspecificaciónEstadoComentario
{{SpecName('DOM WHATWG','#dom-node-insertbefore','Node.insertBefore')}}{{Spec2('DOM WHATWG')}}Corrige errores en el algoritmo de inserción
{{SpecName('DOM4','#dom-node-insertbefore','Node.insertBefore')}}{{Spec2('DOM4')}}Describe el algoritmo con mayor detalle
{{SpecName('DOM3 Core','core.html#ID-952280727','Node.insertBefore')}}{{Spec2('DOM3 Core')}}Sin cambios notables
{{SpecName('DOM2 Core','core.html#ID-952280727','Node.insertBefore')}}{{Spec2('DOM2 Core')}}Sin cambios notables
{{SpecName('DOM1','level-one-core.html#method-insertBefore','Node.insertBefore')}}{{Spec2('DOM1')}}Introducido
+ +

Ver también

+ + diff --git a/files/es/web/api/node/nodoprincipal/index.html b/files/es/web/api/node/nodoprincipal/index.html deleted file mode 100644 index f6faf58631..0000000000 --- a/files/es/web/api/node/nodoprincipal/index.html +++ /dev/null @@ -1,34 +0,0 @@ ---- -title: Nodo.nodoPrincipal -slug: Web/API/Node/nodoPrincipal -tags: - - API - - DOM - - Gecko - - Propiedad -translation_of: Web/API/Node -translation_of_original: Web/API/Node/nodePrincipal ---- -
-
{{APIRef("DOM")}}
-{{Non-standard_header}} - -

La propiedad de solo loctura de Nodo.nodePrincipal devuelve el objeto {{Interface("nsIPrincipal")}} representando el contexto de seguridad del nodo actual.

- -

{{Note("This property exists on all nodes (HTML, XUL, SVG, MathML, etc.), but only if the script trying to use it has chrome privileges.")}}

- -

Sintaxis

- -
principalObj = element.nodePrincipal
-
- -

Notas

- -

Esta propiedad es de solo lectura; Si intentamos editarla nos lanzará una excepción. Además, esta propiedad tan solo debería ser accesible desde código privilegiado

- -

Especificación

- -

No hay especificaciones.

-
- -

 

diff --git a/files/es/web/api/node/parentelement/index.html b/files/es/web/api/node/parentelement/index.html new file mode 100644 index 0000000000..2a2e947a8d --- /dev/null +++ b/files/es/web/api/node/parentelement/index.html @@ -0,0 +1,46 @@ +--- +title: Node.parentElement +slug: Web/API/Node/elementoPadre +tags: + - API + - DOM + - NecesitaCompatiblidadNavegador + - Nodo + - Propiedad +translation_of: Web/API/Node/parentElement +--- +
+
{{APIRef("DOM")}}
+
+ +

La propiedad de sólo lectura de Nodo.parentElement devuelve el nodo padre del DOM {{domxref("Element")}}, o null, si el nodo no tiene padre o si el padre no es un {{domxref("Element")}} DOM .

+ +

Sintaxis

+ +
elementoPadre = node.parentElement
+ +

El elementoPadre es el padre del nodo actual. Esto es siempre un objecto {{domxref("Element")}} DOM, o null.

+ +

Ejemplo

+ +
if (node.parentElement) {
+    node.parentElement.style.color = "red";
+}
+ +

Compatibilidad en navegadores

+ +

En algunos navegadores, la propiedad elementoPadre es solo definida en nodos que ellos mismos son {{domxref("Element")}}. En particular, esto no está definido en nodos de texto.

+ +

{{Compat("api.Node.parentElement")}}

+ +

Especificación

+ + + +

Ver también

+ + diff --git a/files/es/web/api/notifications_api/usando_la_api_de_notificaciones/index.html b/files/es/web/api/notifications_api/usando_la_api_de_notificaciones/index.html deleted file mode 100644 index 8220534f63..0000000000 --- a/files/es/web/api/notifications_api/usando_la_api_de_notificaciones/index.html +++ /dev/null @@ -1,294 +0,0 @@ ---- -title: Usando la API de Notificaciones -slug: Web/API/Notifications_API/Usando_la_API_de_Notificaciones -tags: - - API - - Notificaciones - - Tutorial -translation_of: Web/API/Notifications_API/Using_the_Notifications_API ---- -

{{APIRef("Web Notifications")}}

- -

La API de Notificaciones permite a una página web enviar notificaciones que se mostrarán fuera de la web al nivel del sistema. Esto permite a las aplicaciones web enviar información al usuario aunque estas estén de fondo.

- -

{{AvailableInWorkers}}

- -

Para comenzar, tenemos que saber qué son las notificaciones y cómo se muestran. En la imagen de abajo se puede ver un ejemplo de notificaciones en android.

- -

- -

El sistema de notificaciones variará según el navegador y la plataforma en la que estemos, pero esto no va a suponer ningún problema. La API de notificaciones está escrita de manera que sea compatible con la gran mayoría de sistemas.

- -

Ejemplos

- -
-
-

Un ejemplo claro de uso de notificaciones web puede ser un mail basado en web o una aplicación IRC que nos notificará cada vez que nos llegue un nuevo mensaje, aunque estemos fuera de la aplicación. Un ejemplo de esto lo podemos ver en Slack.

- -

Hemos escrito un par de ejemplos del mundo real para dar una idea más clara de cómo podemos usar las notificaciones web:

- -
    -
  • Lista de pendientes: Esto es una app sencilla que almacena las tareas pendientes localmente usando IndexedDB y avisa al usuario cuándo hay que realizar las tareas mediante notificaciones. Descarga el código, o echa un vistazo al ejemplo en tiempo real.
  • -
  • Emogotchi: Una parodia absurda de Tamagotchi, en la que tienes que mantener a tu Emo miserable o perderás el juego. Esto usa las notificaciones del sistema para indicarte cómo lo estás haciendo y para quejarse de ti, TODO EL RATO. Descarga el código de Emogotchi, o echa un vistazo a la versión en tiempo real.
  • -
-
- -
-

.

-
-
- -

Pidiendo permiso

- -

Antes de que una app pueda lanzar una notificación, el usuario tiene que darle permiso para ello. Esto es un requisito común cuando una API intenta interactuar con algo fuera de una página web — al menos una vez, el usuario tendrá que permitir a la aplicación mostrar notificaciones, de esta forma, el usuario decide qué aplicaciones le pueden mostrar notificaciones y cuáles no.

- -

Comprobando el permiso actual

- -

Puedes comprobar si ya tienes permiso comprobando la propiedad {{domxref("Notification.permission")}} de solo lectura. Esta puede tener uno de los siguientes valores:

- -
-
default
-
No se le ha pedido permiso al usuario aún, por lo que la app no tiene permisos.
-
granted
-
El usuario ha permitido las notificaciones de la app.
-
denied
-
El usuario ha denegado las notificaciones de la app.
-
- -

Obteniendo permiso

- -

Si la aplicación aún no tiene permiso para mostrar notificaciones, tendremos que hacer uso de {{domxref("Notification.requestPermission()")}} para pedir permiso al usuario. En su manera más simple, tal y como se usa en la Demo de Emogotchi (código fuente), solo tenemos que incluir lo siguiente:

- -
Notification.requestPermission().then(function(result) {
-  console.log(result);
-});
- -

Esto usa la versión promise del método, que está soportada en las versiones recientes (p.ej. Firefox 47). Si quieres soportar versiones más antiguas tendrás que usar la versión de callback, que es la siguiente:

- -
Notification.requestPermission();
- -

La versión de callback acepta de forma opcional una función callback que será ejecutada cuando el usuario responda a si quiere notificaciones o no (tal y como se ve en el segundo else ... if abajo). Por lo general, pedirás permiso para mostrar notificaciones una vez que hayas inicializado la app, y antes de lanzar una notificación. Si quieres ser muy cuidadoso puedes usar lo siguiente (ver To-do List Notifications):

- -
function notifyMe() {
-  // Comprobamos si el navegador soporta las notificaciones
-  if (!("Notification" in window)) {
-    alert("Este navegador no soporta las notificaciones del sistema");
-  }
-
-  // Comprobamos si ya nos habían dado permiso
-  else if (Notification.permission === "granted") {
-    // Si esta correcto lanzamos la notificación
-    var notification = new Notification("Holiwis :D");
-  }
-
-  // Si no, tendremos que pedir permiso al usuario
-  else if (Notification.permission !== 'denied') {
-    Notification.requestPermission(function (permission) {
-      // Si el usuario acepta, lanzamos la notificación
-      if (permission === "granted") {
-        var notification = new Notification("Gracias majo!");
-      }
-    });
-  }
-
-  // Finalmente, si el usuario te ha denegado el permiso y
-  // quieres ser respetuoso no hay necesidad molestar más.
-}
- -
-

Nota: Antes de la versión 37, Chrome no te deja llamar a {{domxref("Notification.requestPermission()")}} en manejador de eventos load (ver problema 274284).

-
- -

Permisos en Firefox OS manifest

- -

Ten en cuenta que mientras la API de Notificaciones no esté {{Glossary("privileged")}} o {{Glossary("certified")}}, deberías seguir inculyendo una entrada en tu archivo manifest.webapp cuando vayas a usarlo en una app en Firefox OS:

- -
"permissions": {
-  "desktop-notification": {
-    "description": "Needed for creating system notifications."
-  }
-},
-"messages": [{"notification": "path/to/your/index.html"}]
-
-
- -
-

Nota: Cuándo una aplicación es instalada, no deberías de necesitar {{anch("Getting permission","explicitly request permission")}}, pero vas a seguir necesitando los permisos y las entradas de texto de arriba para poder lanzar las notificaciones.

-
- -

Creando una notificación

- -

Crear una notificación es fácil, simplemente usa el constructor {{domxref("Notification")}}. Este constructor espera un título que mostrar en la notificación y otras opciones para mejorar la notificación, como un {{domxref("Notification.icon","icon")}} o un texto {{domxref("Notification.body","body")}}.

- -

Por ejemplo, en el Ejemplo de Emogotchi tenemos dos funciones que pueden ser llamadas cuando hay que lanzar una notificación; el uso de una u otra depende de si queremos establecer el contenido de la notificación, o si queremos una notificación con contenido aleatorio:

- -
function spawnNotification(theBody,theIcon,theTitle) {
-  var options = {
-      body: theBody,
-      icon: theIcon
-  }
-  var n = new Notification(theTitle,options);
-  setTimeout(n.close.bind(n), 5000);
-}
-
-function randomNotification() {
-  var randomQuote = quoteChooser();
-  var options = {
-      body: randomQuote,
-      icon: 'img/sad_head.png',
-  }
-
-  var n = new Notification('Emogotchi says',options);
-  setTimeout(n.close.bind(n), 5000);
-}
- -

Cerrando las notificaciones

- -

Firefox y Safari cierran las notificaciones automáticamente tras cierto tiempo (unos 4 segundos). Esto también puede suceder a nivel de sistema operativo (en Windows duran 7 segundos por defecto). En cambio, en algunos navegadores no se cierran automáticamente, como en Chrome, por ejemplo. Para asegurarnos de que las notificaciones se cierran en todos los navegadores, al final de las funciones de arriba, llamamos a la función {domxref("Notification.close")}}  dentro de {{domxref("WindowTimers.setTimeout","setTimeout()")}} para cerrar la notificación tras 5 segundos. Date cuenta también del uso que hacemos de bind() para asegurarnos de que la función close() está asociada a la notificación.

- -
setTimeout(n.close.bind(n), 5000);
-
- -
-

Nota: Cuándo recibes un evento "close", no hay ninguna garantía de que haya sido el usuario quién ha cerrado la notificación. Esto coincide con la especificación que dice: "Cuando una notificación es cerrada, sea por la misma plataforma o por el usuario, se deben lanzar los pasos de cierre para la misma".

-
- -

Eventos de Notificación

- -

Las especificaciones de la API de notificaciones listan cuatro eventos que pueden ser lanzados en la instancia {{domxref("Notification")}}:

- -
-
{{event("click")}}
-
Lanzado cuando el usuario hace click en la notificación.
-
{{event("error")}}
-
Lanzado cuando algo falla en la notificación; habitualmente es porque la notificación no se ha podido mostrar por algún motivo.
-
- -

Estos eventos se pueden monitorizar usando los manejadores {{domxref("Notification.onclick","onclick")}} y {{domxref("Notification.onerror","onerror")}}. Como {{domxref("Notification")}} también hereda de {{domxref("EventTarget")}}, es posible usar el método {{domxref("EventTarget.addEventListener","addEventListener()")}} en ella.

- -

También hay otros dos eventos que estaban listados en la especificación, pero que han sido eliminados recientemente. Estos puede que sigan funcionando en los navegadores por ahora, pero deberían tratarse como obsoletos y evitar su uso:

- -
-
{{event("close")}}
-
Lanzado cuándo la notificación se cierra.
-
{{event("show")}}
-
Lanzado cuándo la notificación se muestra al usuario.
-
- -

Reemplazando notificaciones existentes

- -

Normalmente los usuario no quieren recibir muchas notificaciones en poco tiempo — por ejemplo, una aplicación de mensajería que te notifica cada mensaje que te llegue, y te llegan un montón. Para evitar el spam de notificaciones, se puede modificar la cola de notificaciones, reemplazando una o varias notificaciones pendientes, por una nueva notificación.

- -

Para hacer esto, se puede añadir una etiqueta a cualquier nueva notificación. Si ya hay una notificación con la misma etiqueta y aún no se ha mostrado, la nueva reemplazará a la anterior. Si la notificación con la misma etiqueta ya ha sido mostrada, se cerrará la anterior y se mostrará la nueva.

- -

Ejemplo de etiquta

- -

Teniendo el siguiente código HTML:

- -
<button>Notifícame!</button>
- -

Es posible controlar múltiples notificaciones de la siguiente forma:

- -
window.addEventListener('load', function () {
-  // Primero, comprobamos si tenemos permiso para lanzar notificaciones
-  // Si no lo tenemos, lo pedimos
-  if (window.Notification && Notification.permission !== "granted") {
-    Notification.requestPermission(function (status) {
-      if (Notification.permission !== status) {
-        Notification.permission = status;
-      }
-    });
-  }
-
-  var button = document.getElementsByTagName('button')[0];
-
-  button.addEventListener('click', function () {
-    // Si el usuario ha dado permiso
-    // le intentamos enviar 10 notificaciones
-    if (window.Notification && Notification.permission === "granted") {
-      var i = 0;
-      // Usamos un inteval porque algunos navegadores (Firefox incluído) bloquean las notificaciones si se envían demasiadas en cierto tiempo.
-      var interval = window.setInterval(function () {
-        // Gracias a la etiqueta, deberíamos de ver sólo la notificación "Holiws! 9"
-        var n = new Notification("Holiwis! " + i, {tag: 'soManyNotification'});
-        if (i++ == 9) {
-          window.clearInterval(interval);
-        }
-      }, 200);
-    }
-
-    // Si el usuario no ha dicho si quiere notificaciones
-    // Nota: en Chrome no estamos seguros de si la propiedad permission
-    // esta asignada, por lo que es inseguro comprobar el valor "default".
-    else if (window.Notification && Notification.permission !== "denied") {
-      Notification.requestPermission(function (status) {
-        // Si el usuario acepta
-        if (status === "granted") {
-          var i = 0;
-          // Usamos un inteval porque algunos navegadores (Firefox incluído) bloquean las notificaciones si se envían demasiadas en cierto tiempo.
-          var interval = window.setInterval(function () {
-          // Gracias a la etiqueta, deberíamos de ver sólo la notificación "Holiws! 9" var n = new Notification("Holiwis! " + i, {tag: 'soManyNotification'});
-            if (i++ == 9) {
-              window.clearInterval(interval);
-            }
-          }, 200);
-        }
-
-        // Otherwise, we can fallback to a regular modal alert
-        else {
-          alert("Hi!");
-        }
-      });
-    }
-
-    // If the user refuses to get notified
-    else {
-      // We can fallback to a regular modal alert
-      alert("Hi!");
-    }
-  });
-});
- -

Comprueba el ejemplo en directo abajo:

- -

{{ EmbedLiveSample('Tag_example', '100%', 30) }}

- -

Receiving notification of clicks on app notifications

- -

When a user clicks on a notification generated by an app, you will be notified of this event in two different ways, depending on the circumstance:

- -
    -
  1. A click event if your app has not closed or been put in the background between the time you create the notification and the time the user clicks on it.
  2. -
  3. A system message otherwise.
  4. -
- -

See this code snippet for an example of how to deal with this.

- -

Especificaciones

- - - - - - - - - - - - - - -
EspecificaciónEstadoComentario
{{SpecName('Web Notifications')}}{{Spec2('Web Notifications')}}Living standard
- -

Compatibilidad de navegadores

- -

{{page("/en-US/Web/API/Notification","Browser compatibility")}}

- -

Ver también

- - diff --git a/files/es/web/api/notifications_api/using_the_notifications_api/index.html b/files/es/web/api/notifications_api/using_the_notifications_api/index.html new file mode 100644 index 0000000000..8220534f63 --- /dev/null +++ b/files/es/web/api/notifications_api/using_the_notifications_api/index.html @@ -0,0 +1,294 @@ +--- +title: Usando la API de Notificaciones +slug: Web/API/Notifications_API/Usando_la_API_de_Notificaciones +tags: + - API + - Notificaciones + - Tutorial +translation_of: Web/API/Notifications_API/Using_the_Notifications_API +--- +

{{APIRef("Web Notifications")}}

+ +

La API de Notificaciones permite a una página web enviar notificaciones que se mostrarán fuera de la web al nivel del sistema. Esto permite a las aplicaciones web enviar información al usuario aunque estas estén de fondo.

+ +

{{AvailableInWorkers}}

+ +

Para comenzar, tenemos que saber qué son las notificaciones y cómo se muestran. En la imagen de abajo se puede ver un ejemplo de notificaciones en android.

+ +

+ +

El sistema de notificaciones variará según el navegador y la plataforma en la que estemos, pero esto no va a suponer ningún problema. La API de notificaciones está escrita de manera que sea compatible con la gran mayoría de sistemas.

+ +

Ejemplos

+ +
+
+

Un ejemplo claro de uso de notificaciones web puede ser un mail basado en web o una aplicación IRC que nos notificará cada vez que nos llegue un nuevo mensaje, aunque estemos fuera de la aplicación. Un ejemplo de esto lo podemos ver en Slack.

+ +

Hemos escrito un par de ejemplos del mundo real para dar una idea más clara de cómo podemos usar las notificaciones web:

+ +
    +
  • Lista de pendientes: Esto es una app sencilla que almacena las tareas pendientes localmente usando IndexedDB y avisa al usuario cuándo hay que realizar las tareas mediante notificaciones. Descarga el código, o echa un vistazo al ejemplo en tiempo real.
  • +
  • Emogotchi: Una parodia absurda de Tamagotchi, en la que tienes que mantener a tu Emo miserable o perderás el juego. Esto usa las notificaciones del sistema para indicarte cómo lo estás haciendo y para quejarse de ti, TODO EL RATO. Descarga el código de Emogotchi, o echa un vistazo a la versión en tiempo real.
  • +
+
+ +
+

.

+
+
+ +

Pidiendo permiso

+ +

Antes de que una app pueda lanzar una notificación, el usuario tiene que darle permiso para ello. Esto es un requisito común cuando una API intenta interactuar con algo fuera de una página web — al menos una vez, el usuario tendrá que permitir a la aplicación mostrar notificaciones, de esta forma, el usuario decide qué aplicaciones le pueden mostrar notificaciones y cuáles no.

+ +

Comprobando el permiso actual

+ +

Puedes comprobar si ya tienes permiso comprobando la propiedad {{domxref("Notification.permission")}} de solo lectura. Esta puede tener uno de los siguientes valores:

+ +
+
default
+
No se le ha pedido permiso al usuario aún, por lo que la app no tiene permisos.
+
granted
+
El usuario ha permitido las notificaciones de la app.
+
denied
+
El usuario ha denegado las notificaciones de la app.
+
+ +

Obteniendo permiso

+ +

Si la aplicación aún no tiene permiso para mostrar notificaciones, tendremos que hacer uso de {{domxref("Notification.requestPermission()")}} para pedir permiso al usuario. En su manera más simple, tal y como se usa en la Demo de Emogotchi (código fuente), solo tenemos que incluir lo siguiente:

+ +
Notification.requestPermission().then(function(result) {
+  console.log(result);
+});
+ +

Esto usa la versión promise del método, que está soportada en las versiones recientes (p.ej. Firefox 47). Si quieres soportar versiones más antiguas tendrás que usar la versión de callback, que es la siguiente:

+ +
Notification.requestPermission();
+ +

La versión de callback acepta de forma opcional una función callback que será ejecutada cuando el usuario responda a si quiere notificaciones o no (tal y como se ve en el segundo else ... if abajo). Por lo general, pedirás permiso para mostrar notificaciones una vez que hayas inicializado la app, y antes de lanzar una notificación. Si quieres ser muy cuidadoso puedes usar lo siguiente (ver To-do List Notifications):

+ +
function notifyMe() {
+  // Comprobamos si el navegador soporta las notificaciones
+  if (!("Notification" in window)) {
+    alert("Este navegador no soporta las notificaciones del sistema");
+  }
+
+  // Comprobamos si ya nos habían dado permiso
+  else if (Notification.permission === "granted") {
+    // Si esta correcto lanzamos la notificación
+    var notification = new Notification("Holiwis :D");
+  }
+
+  // Si no, tendremos que pedir permiso al usuario
+  else if (Notification.permission !== 'denied') {
+    Notification.requestPermission(function (permission) {
+      // Si el usuario acepta, lanzamos la notificación
+      if (permission === "granted") {
+        var notification = new Notification("Gracias majo!");
+      }
+    });
+  }
+
+  // Finalmente, si el usuario te ha denegado el permiso y
+  // quieres ser respetuoso no hay necesidad molestar más.
+}
+ +
+

Nota: Antes de la versión 37, Chrome no te deja llamar a {{domxref("Notification.requestPermission()")}} en manejador de eventos load (ver problema 274284).

+
+ +

Permisos en Firefox OS manifest

+ +

Ten en cuenta que mientras la API de Notificaciones no esté {{Glossary("privileged")}} o {{Glossary("certified")}}, deberías seguir inculyendo una entrada en tu archivo manifest.webapp cuando vayas a usarlo en una app en Firefox OS:

+ +
"permissions": {
+  "desktop-notification": {
+    "description": "Needed for creating system notifications."
+  }
+},
+"messages": [{"notification": "path/to/your/index.html"}]
+
+
+ +
+

Nota: Cuándo una aplicación es instalada, no deberías de necesitar {{anch("Getting permission","explicitly request permission")}}, pero vas a seguir necesitando los permisos y las entradas de texto de arriba para poder lanzar las notificaciones.

+
+ +

Creando una notificación

+ +

Crear una notificación es fácil, simplemente usa el constructor {{domxref("Notification")}}. Este constructor espera un título que mostrar en la notificación y otras opciones para mejorar la notificación, como un {{domxref("Notification.icon","icon")}} o un texto {{domxref("Notification.body","body")}}.

+ +

Por ejemplo, en el Ejemplo de Emogotchi tenemos dos funciones que pueden ser llamadas cuando hay que lanzar una notificación; el uso de una u otra depende de si queremos establecer el contenido de la notificación, o si queremos una notificación con contenido aleatorio:

+ +
function spawnNotification(theBody,theIcon,theTitle) {
+  var options = {
+      body: theBody,
+      icon: theIcon
+  }
+  var n = new Notification(theTitle,options);
+  setTimeout(n.close.bind(n), 5000);
+}
+
+function randomNotification() {
+  var randomQuote = quoteChooser();
+  var options = {
+      body: randomQuote,
+      icon: 'img/sad_head.png',
+  }
+
+  var n = new Notification('Emogotchi says',options);
+  setTimeout(n.close.bind(n), 5000);
+}
+ +

Cerrando las notificaciones

+ +

Firefox y Safari cierran las notificaciones automáticamente tras cierto tiempo (unos 4 segundos). Esto también puede suceder a nivel de sistema operativo (en Windows duran 7 segundos por defecto). En cambio, en algunos navegadores no se cierran automáticamente, como en Chrome, por ejemplo. Para asegurarnos de que las notificaciones se cierran en todos los navegadores, al final de las funciones de arriba, llamamos a la función {domxref("Notification.close")}}  dentro de {{domxref("WindowTimers.setTimeout","setTimeout()")}} para cerrar la notificación tras 5 segundos. Date cuenta también del uso que hacemos de bind() para asegurarnos de que la función close() está asociada a la notificación.

+ +
setTimeout(n.close.bind(n), 5000);
+
+ +
+

Nota: Cuándo recibes un evento "close", no hay ninguna garantía de que haya sido el usuario quién ha cerrado la notificación. Esto coincide con la especificación que dice: "Cuando una notificación es cerrada, sea por la misma plataforma o por el usuario, se deben lanzar los pasos de cierre para la misma".

+
+ +

Eventos de Notificación

+ +

Las especificaciones de la API de notificaciones listan cuatro eventos que pueden ser lanzados en la instancia {{domxref("Notification")}}:

+ +
+
{{event("click")}}
+
Lanzado cuando el usuario hace click en la notificación.
+
{{event("error")}}
+
Lanzado cuando algo falla en la notificación; habitualmente es porque la notificación no se ha podido mostrar por algún motivo.
+
+ +

Estos eventos se pueden monitorizar usando los manejadores {{domxref("Notification.onclick","onclick")}} y {{domxref("Notification.onerror","onerror")}}. Como {{domxref("Notification")}} también hereda de {{domxref("EventTarget")}}, es posible usar el método {{domxref("EventTarget.addEventListener","addEventListener()")}} en ella.

+ +

También hay otros dos eventos que estaban listados en la especificación, pero que han sido eliminados recientemente. Estos puede que sigan funcionando en los navegadores por ahora, pero deberían tratarse como obsoletos y evitar su uso:

+ +
+
{{event("close")}}
+
Lanzado cuándo la notificación se cierra.
+
{{event("show")}}
+
Lanzado cuándo la notificación se muestra al usuario.
+
+ +

Reemplazando notificaciones existentes

+ +

Normalmente los usuario no quieren recibir muchas notificaciones en poco tiempo — por ejemplo, una aplicación de mensajería que te notifica cada mensaje que te llegue, y te llegan un montón. Para evitar el spam de notificaciones, se puede modificar la cola de notificaciones, reemplazando una o varias notificaciones pendientes, por una nueva notificación.

+ +

Para hacer esto, se puede añadir una etiqueta a cualquier nueva notificación. Si ya hay una notificación con la misma etiqueta y aún no se ha mostrado, la nueva reemplazará a la anterior. Si la notificación con la misma etiqueta ya ha sido mostrada, se cerrará la anterior y se mostrará la nueva.

+ +

Ejemplo de etiquta

+ +

Teniendo el siguiente código HTML:

+ +
<button>Notifícame!</button>
+ +

Es posible controlar múltiples notificaciones de la siguiente forma:

+ +
window.addEventListener('load', function () {
+  // Primero, comprobamos si tenemos permiso para lanzar notificaciones
+  // Si no lo tenemos, lo pedimos
+  if (window.Notification && Notification.permission !== "granted") {
+    Notification.requestPermission(function (status) {
+      if (Notification.permission !== status) {
+        Notification.permission = status;
+      }
+    });
+  }
+
+  var button = document.getElementsByTagName('button')[0];
+
+  button.addEventListener('click', function () {
+    // Si el usuario ha dado permiso
+    // le intentamos enviar 10 notificaciones
+    if (window.Notification && Notification.permission === "granted") {
+      var i = 0;
+      // Usamos un inteval porque algunos navegadores (Firefox incluído) bloquean las notificaciones si se envían demasiadas en cierto tiempo.
+      var interval = window.setInterval(function () {
+        // Gracias a la etiqueta, deberíamos de ver sólo la notificación "Holiws! 9"
+        var n = new Notification("Holiwis! " + i, {tag: 'soManyNotification'});
+        if (i++ == 9) {
+          window.clearInterval(interval);
+        }
+      }, 200);
+    }
+
+    // Si el usuario no ha dicho si quiere notificaciones
+    // Nota: en Chrome no estamos seguros de si la propiedad permission
+    // esta asignada, por lo que es inseguro comprobar el valor "default".
+    else if (window.Notification && Notification.permission !== "denied") {
+      Notification.requestPermission(function (status) {
+        // Si el usuario acepta
+        if (status === "granted") {
+          var i = 0;
+          // Usamos un inteval porque algunos navegadores (Firefox incluído) bloquean las notificaciones si se envían demasiadas en cierto tiempo.
+          var interval = window.setInterval(function () {
+          // Gracias a la etiqueta, deberíamos de ver sólo la notificación "Holiws! 9" var n = new Notification("Holiwis! " + i, {tag: 'soManyNotification'});
+            if (i++ == 9) {
+              window.clearInterval(interval);
+            }
+          }, 200);
+        }
+
+        // Otherwise, we can fallback to a regular modal alert
+        else {
+          alert("Hi!");
+        }
+      });
+    }
+
+    // If the user refuses to get notified
+    else {
+      // We can fallback to a regular modal alert
+      alert("Hi!");
+    }
+  });
+});
+ +

Comprueba el ejemplo en directo abajo:

+ +

{{ EmbedLiveSample('Tag_example', '100%', 30) }}

+ +

Receiving notification of clicks on app notifications

+ +

When a user clicks on a notification generated by an app, you will be notified of this event in two different ways, depending on the circumstance:

+ +
    +
  1. A click event if your app has not closed or been put in the background between the time you create the notification and the time the user clicks on it.
  2. +
  3. A system message otherwise.
  4. +
+ +

See this code snippet for an example of how to deal with this.

+ +

Especificaciones

+ + + + + + + + + + + + + + +
EspecificaciónEstadoComentario
{{SpecName('Web Notifications')}}{{Spec2('Web Notifications')}}Living standard
+ +

Compatibilidad de navegadores

+ +

{{page("/en-US/Web/API/Notification","Browser compatibility")}}

+ +

Ver también

+ + diff --git a/files/es/web/api/pointer_lock_api/index.html b/files/es/web/api/pointer_lock_api/index.html new file mode 100644 index 0000000000..13a27d6d48 --- /dev/null +++ b/files/es/web/api/pointer_lock_api/index.html @@ -0,0 +1,284 @@ +--- +title: API Pointer Lock +slug: WebAPI/Pointer_Lock +translation_of: Web/API/Pointer_Lock_API +--- +

 

+ +

Pointer Lock (antes llamado Bloqueo del Mouse)  proporciona métodos de entrada basados ​​en el movimiento del ratón a traves del tiempo (es decir, deltas), no sólo la posición absoluta del cursor del mouse. Te da acceso al movimiento puro del mouse, bloquea el objetivo de los eventos del mouse a un solo elemento, elimina límites en cuanto a  que tan lejos puedes mover el mouse en una sola dirección, y quita el cursor de la vista.

+ +

Esta API es útil para aplicaciones que requieren bastantes acciones para controlar los movimientos del mouse, rotar objetos y cambiar las entradas. Es especialmente importante para aplicaciones altamente visuales, tales como los que utilizan la perspectiva en primera persona, así como vistas en 3D y modelado.

+ +

Por ejemplo, puedes crear aplicaciones que permiten a los usuarios controlar el ángulo de visión con sólo mover el mouse sin ningún clic, permitiendo liberar los clics para otras acciones. Este tipo de entrada del mouse es muy útil para ver mapas, imágenes de satélite, o escenas en primera persona (como en un juego o un vídeo embebido).

+ +

Pointer Lock te permite acceder a los eventos del mouse incluso cuando el cursor pasa por el límite de la pantalla del navegador. Por ejemplo, los usuarios pueden seguir girando o manipular un modelo 3D moviendo el mouse sin fin. Sin Pointer Lock, la rotación o la manipulación se detiene en el momento en que el cursor alcanza el borde de la pantalla del navegador. Los jugadores se verán particularmente encantados con esta característica, ya que anciosamente pueden hacer clic en los botones y arrastrar el cursor del mouse de un lado a otro sin tener que preocuparse por salir de la zona de juego ni de cliquear accidentalmente otra aplicación que podría llevar al mouse fuera del juego. Una tragedia!

+ +

Conceptos Básicos

+ +

Pointer Lock está relacionado con la mouse capturemouse capture proporciona la entrega continua de eventos a un elemento de destino, mientras que el mouse se arrastra, pero se detiene cuando se suelta el clic. Pointer Lock es diferente de mouse capture en las siguientes maneras:

+ + + +

Ejemplo

+ +

El siguiente es un ejemplo de cómo se puede configurar Pointer Lock en su página web.

+ +
<button onclick="lockPointer();">Lock it!</button>
+<div id="pointer-lock-element"></div>
+<script>
+// Nota: Al momento de escribir esto, sólo Mozilla y WebKit apoyan Pointer Lock.
+
+// El elemento que servirá para pantalla completa y pointer lock.
+var elem;
+
+document.addEventListener("mousemove", function(e) {
+  var movementX = e.movementX       ||
+                  e.mozMovementX    ||
+                  e.webkitMovementX ||
+                  0,
+      movementY = e.movementY       ||
+                  e.mozMovementY    ||
+                  e.webkitMovementY ||
+                  0;
+
+  // Imprime los valores delta del movimiento del mouse
+  console.log("movementX=" + movementX, "movementY=" + movementY);
+}, false);
+
+function fullscreenChange() {
+  if (document.webkitFullscreenElement === elem ||
+      document.mozFullscreenElement === elem ||
+      document.mozFullScreenElement === elem) { // Older API upper case 'S'.
+    // El elemento esta en pantalla completa, ahora podemos hacer el request de pointer lock
+    elem.requestPointerLock = elem.requestPointerLock    ||
+                              elem.mozRequestPointerLock ||
+                              elem.webkitRequestPointerLock;
+    elem.requestPointerLock();
+  }
+}
+
+document.addEventListener('fullscreenchange', fullscreenChange, false);
+document.addEventListener('mozfullscreenchange', fullscreenChange, false);
+document.addEventListener('webkitfullscreenchange', fullscreenChange, false);
+
+function pointerLockChange() {
+  if (document.mozPointerLockElement === elem ||
+      document.webkitPointerLockElement === elem) {
+    console.log("Pointer Lock was successful.");
+  } else {
+    console.log("Pointer Lock was lost.");
+  }
+}
+
+document.addEventListener('pointerlockchange', pointerLockChange, false);
+document.addEventListener('mozpointerlockchange', pointerLockChange, false);
+document.addEventListener('webkitpointerlockchange', pointerLockChange, false);
+
+function pointerLockError() {
+  console.log("Error while locking pointer.");
+}
+
+document.addEventListener('pointerlockerror', pointerLockError, false);
+document.addEventListener('mozpointerlockerror', pointerLockError, false);
+document.addEventListener('webkitpointerlockerror', pointerLockError, false);
+
+function lockPointer() {
+  elem = document.getElementById("pointer-lock-element");
+  // Start by going fullscreen with the element.  Current implementations
+  // require the element to be in fullscreen before requesting pointer
+  // lock--something that will likely change in the future.
+  elem.requestFullscreen = elem.requestFullscreen    ||
+                           elem.mozRequestFullscreen ||
+                           elem.mozRequestFullScreen || // Older API upper case 'S'.
+                           elem.webkitRequestFullscreen;
+  elem.requestFullscreen();
+}
+</script>
+
+ +

Propiedades/Métodos

+ +

La API de bloque de puntero ,similar a la API de Fullscreen,extiende del elemento DOM agregando un nuevo método, requestPointerLock, la cual es dependiente del vendedor(del navegador). Puede escribirse como:

+ +
element.webkitRequestPointerLock(); // para Chrome
+
+element.mozRequestPointerLock(); // para Firefox
+
+ +

Current implementations of requestPointerLock are tightly bound to requestFullScreen and the Fullscreen API. Before an element can be pointer locked, it must first enter the fullscreen state. As demonstrated above, the process of locking the pointer is asynchronous, with events (pointerlockchange, pointerlockerror) indicating the success or failure of the request. This matches how the Fullscreen API works, with its requestFullScreen method and fullscreenchange and fullscreenerror events.

+ +

The Pointer lock API also extends the document interface, adding both a new property and a new method. The new property is used for accessing the currently locked element (if any), and is named pointerLockElement, which is vendor-prefixed for now. The new method on document is exitPointerLock and, as the name implies, it is used to exit Pointer lock.

+ +

The pointerLockElement property is useful for determining if any element is currently pointer locked (e.g., for doing a boolean check) and also for obtaining a reference to the locked element, if any. Here is an example of both uses:

+ +
document.pointerLockElement = document.pointerLockElement    ||
+                              document.mozPointerLockElement ||
+                              document.webkitPointerLockElement;
+
+// 1) Used as a boolean check--are we pointer locked?
+if (!!document.pointerLockElement) {
+  // pointer is locked
+} else {
+  // pointer is not locked
+}
+
+// 2) Used to access the pointer locked element
+if (document.pointerLockElement === someElement) {
+  // someElement is currently pointer locked
+}
+
+ +

The document's exitPointerLock method is used to exit pointer lock, and like requestPointerLock, works asynchrounously using the pointerlockchange and pointerlockerror events:

+ +
document.exitPointerLock = document.exitPointerLock    ||
+                           document.mozExitPointerLock ||
+                           document.webkitExitPointerLock;
+
+function pointerLockChange() {
+  document.pointerLockElement = document.pointerLockElement    ||
+                                document.mozPointerLockElement ||
+                                document.webkitPointerLockElement;
+
+  if (!!document.pointerLockElement) {
+    console.log("Still locked.");
+  } else {
+    console.log("Exited lock.");
+  }
+}
+
+document.addEventListener('pointerlockchange', pointerLockChange, false);
+document.addEventListener('mozpointerlockchange', pointerLockChange, false);
+document.addEventListener('webkitpointerlockchange', pointerLockChange, false);
+
+// Attempt to unlock
+document.exitPointerLock();
+
+ +

pointerlockchange event

+ +

When the Pointer lock state changes—for example, when calling requestPointerLock, exitPointerLock, the user pressing the ESC key, etc.—the pointerlockchange event is dispatched to the document. This is a simple event and contains no extra data.

+ +
This event is currently prefixed as mozpointerlockchange in Firefox and webkitpointerlockchange in Chrome. 
+ +

pointerlockerror event

+ +

When there is an error caused by calling requestPointerLock or exitPointerLock, the pointerlockerror event is dispatched to the document. This is a simple event and contains no extra data.

+ +
This event is currently prefixed as mozpointerlockerror in Firefox and webkitpointerlockerror in Chrome. 
+ +

Extensions to mouse events

+ +

The Pointer lock API extends the normal MouseEvent with movement attributes.

+ +
partial interface MouseEvent {
+    readonly attribute long movementX;
+    readonly attribute long movementY;
+};
+ +
The movement attributes are currently prefixed as .mozMovementX and .mozMovementY in Firefox, and.webkitMovementX and .webkitMovementY in Chrome.
+ +

Two new parameters to mouse events—movementX and movementY—provide the change in mouse positions. The values of the parameters are the same as the difference between the values of MouseEvent properties, screenX and screenY, which are stored in two subsequent mousemove events, eNow and ePrevious. In other words, the Pointer lock parameter movementX = eNow.screenX - ePrevious.screenX.

+ +

Locked state

+ +

When Pointer lock is enabled, the standard MouseEvent properties clientX, clientY, screenX, and screenY are held constant, as if the mouse is not moving. The movementX and movementY properties continue to provide the mouse's change in position. There is no limit to movementX and movementY values if the mouse is continuously moving in a single direction. The concept of the mouse cursor does not exist and the cursor cannot move off the window or be clamped by a screen edge.

+ +

Unlocked state

+ +

The parameters movementX and movementY are valid regardless of the mouse lock state, and are available even when unlocked for convenience.

+ +

When the mouse is unlocked, the system cursor can exit and re-enter the browser window. If that happens, movementX and movementY could be set to zero.

+ +

iframe limitations

+ +

Pointer lock can only lock one iframe at a time. If you lock one iframe, you cannot try to lock another iframe and transfer the target to it; Pointer lock will error out. To avoid this limitation, first unlock the locked iframe, and then lock the other.

+ +

While iframes work by default, "sandboxed" iframes block Pointer lock. The ability to avoid this limitation, in the form of the attribute/value combination <iframe sandbox="allow-pointer-lock">, is expected to appear in Chrome soon.

+ +

Specifications

+ + + + + + + + + + + + + + + + +
SpecificationStatusComment
{{SpecName('Pointer Lock')}}{{Spec2('Pointer Lock')}}Initial specification.
+ +

Browser compatibility

+ +

{{ CompatibilityTable() }}

+ +
+ + + + + + + + + + + + + + + + + + + +
FeatureChromeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
Basic support +

Targeting 23{{ property_prefix("webkit") }}*

+ +

See CR/72574

+
+

{{ CompatGeckoDesktop("14.0") }}

+ +

{{bug("633602") }}

+
{{ CompatNo() }}{{ CompatNo() }}{{ CompatNo() }}
+
+ +
+ + + + + + + + + + + + + + + + + + + +
FeatureAndroidFirefox Mobile (Gecko)IE PhoneOpera MobileSafari Mobile
Basic support{{ CompatNo() }}{{ CompatNo() }}{{ CompatNo() }}{{ CompatNo() }}{{ CompatNo() }}
+
+ +

* Requires the feature be enabled in about:flags or Chrome started with the --enable-pointer-lock flag.

+ +

See also

+ +

MouseEvent

diff --git a/files/es/web/api/push_api/using_the_push_api/index.html b/files/es/web/api/push_api/using_the_push_api/index.html deleted file mode 100644 index 05d101e5fa..0000000000 --- a/files/es/web/api/push_api/using_the_push_api/index.html +++ /dev/null @@ -1,433 +0,0 @@ ---- -title: Usando la API Push -slug: Web/API/Push_API/Using_the_Push_API -translation_of: Web/API/Push_API -translation_of_original: Web/API/Push_API/Using_the_Push_API ---- -

La W3C Push API offers some exciting new functionality for developers to use in web applications: this article provides an introduction to getting Push notifications setup and running, with a simple demo.

- -

La habilidad de enviar mensajes o notificaciones de un servidor a un cliente en cualquier momento —si la aplicación está activa en su sitema o no—es algo que ha sido disfrutado por las plataformas nativas durante algún tiempo, y esta finalmente llega a la web! el soporte para muchos push está ahora disponible en Firefox 43+ y Chrome 42+ en escritorio, esperamos seguir pronto con las plataformas moviles. {{domxref("PushMessageData")}} actualmente solo es soportada experimentalmente en Firefox (44+), y la implementación está sujeta a cambios.

- -
-

Note: Early versions of Firefox OS used a proprietary version of this API called Simple Push. This is being rendered obsolete by the Push API standard.

-
- -

Demo: las bases de una simple app de servidor de chat

- -

La demo que hemos creado proporciona los principios de una simple app de chat. Esta presenta un formulario para que ingreses un identificador de chat y un boton que al presionar se suscriba a los mensajes push.

- -

En este punto, el nombre de los nuevos suscriptores aparecerá en la lista de suscriptores, junto con un campo de texto y un botón de envío para permitir al suscriptor enviar mensajes.

- -

At this point, the new subscriber's name will appear in the subscriber's list, along with a text field and submit button to allow the subscriber to send messages.

- -

- -

Para correr la demo, siga las instrucciones de push-api-demo README. Tenga en cuenta que el componente server-side aún nesecita un poco de trabajo para que funcione en chrome y se ejecute de una manera más rezonable. Pero los aspectos de push pueden ser explicados a fondo; Nos sumergiremos en ella después de revisar las tecnologías en juego.

- -

Visión de la tecnología

- -

Esta sección proporciona un esquema de qué tecnologías están involucradas en este ejemplo.

- -

Los mensajes web push hacen parte de la familia tecnológica service workers; en particular, se requiere que un service worker esté activo en la página para recibir mensajes push. el service worker recibe el mensaje push, y acontinuación depende de usted cómo notificar a la página. Usted puede:

- -

Web Push messages are part of the service workers technology family; in particular, a service worker is required to be active on the page for it to receive push messages. The service worker receives the push message, and then it is up to you how to then notify the page. You can:

- - - -

A menudo una combinación de los dos será necesario; La demo a continuación muestra un ejemplo de cada uno.

- -
-

Nota: Usted necesita algún tipo de código que se ejecute en el servidor para manejar el cifrado de punto final/datos y enviar las solicitudes de notificaciones push. En nuestra demostración hemos creado un servidor  quick-and-dirty usando NodeJS.

-
- -

El service worker también tiene que suscribirse al servicio de mensajería psuh. Cada sesión recibe su propio punto final único cuando se suscribe al servicio de mensajería. Este punto final es obtenido desde la propiedad  ({{domxref("PushSubscription.endpoint")}}) en el objeto de suscripción. Este punto final se puede enviar a su servidor y se utiliza para enviar un mensaje al service worker asctivo en esta sesión. Cada navegador tiene su propio servidor de mensajería push para manejar el envío del mensaje push.

- -

Encryption

- -
-

Note: For an interactive walkthrough, try JR Conlin's Web Push Data Encryption Test Page.

-
- -

To send data via a push message, it needs to be encrypted. This requires a public key created using the {{domxref("PushSubscription.getKey()")}} method, which relies upon some complex encryption mechanisms that are run server-side; read Message Encryption for Web Push for more details. As time goes on, libraries will appear to handle key generation and encryption/decryption of push messages; for this demo we used Marco Castelluccio's NodeJS web-push library.

- -
-

Note: There is also another library to handle the encryption with a Node and Python version available, see encrypted-content-encoding.

-
- -

Push workflow summary

- -

To summarize, here is what is needed to implement push messaging. You can find more details about specific parts of the demo code in subsequent sections.

- -
    -
  1. Request permission for web notifications, or anything else you are using that requires permissions.
  2. -
  3. Register a service worker to control the page by calling {{domxref("ServiceWorkerContainer.register()")}}.
  4. -
  5. Subscribe to the push messaging service using {{domxref("PushManager.subscribe()")}}.
  6. -
  7. Retrieve the endpoint associated with the subscription and generate a client public key ({{domxref("PushSubscription.endpoint")}} and {{domxref("PushSubscription.getKey()")}}. Note that getKey() is currently experimental and Firefox only.)
  8. -
  9. Send these details to the server so it can send push message when required. This demo uses {{domxref("XMLHttpRequest")}}, but you could use Fetch.
  10. -
  11. If you are using the Channel Messaging API to comunicate with the service worker, set up a new message channel ({{domxref("MessageChannel.MessageChannel()")}}) and send port2 over to the service worker by calling {{domxref("Worker.postMessage()")}} on the service worker, in order to open up the communication channel. You should also set up a listener to respond to messages sent back from the service worker.
  12. -
  13. On the server side, store the endpoint and any other required details so they are available when a push message needs to be sent to a push subscriber (we are using a simple text file, but you could use a database or whatever you like). In a production app, make sure you keep these details hidden, so malicious parties can't steal endpoints and spam subscribers with push messages.
  14. -
  15. To send a push message, you need to send an HTTP POST to the endpoint URL. The request must include a TTL header that limits how long the message should be queued if the user is not online. To include payload data in your request, you must encrypt it (which involves the client public key). In our demo, we are using the web-push module, which handles all the hard work for you.
  16. -
  17. Over in your service worker, set up a push event handler to respond to push messages being received. -
      -
    1. If you want to respond by sending a channel message back to the main context (see Step 6) you need to first get a reference to the port2 we sent over to the service worker context ({{domxref("MessagePort")}}). This is available on the {{domxref("MessageEvent")}} object passed to the onmessage handler ({{domxref("ServiceWorkerGlobalScope.onmessage")}}). Specifically, this is found in the ports property, index 0. Once this is done, you can send a message back to port1, using {{domxref("MessagePort.postMessage()")}}.
    2. -
    3. If you want to respond by firing a system notification, you can do this by calling {{domxref("ServiceWorkerRegistration.showNotification()")}}. Note that in our code we have run this inside an {{domxref("ExtendableEvent.waitUntil()")}} method — this extends the lifetime of the event until after the notification has been fired, so we can make sure everything has happened that we want to happen.
    4. -
    -
  18. -
- -

Construyendo la demo

- -

Vamos a ensayar el código para esta demo, podemos empezar a entender como trabaja todo esto.

- - - -

No hay nada destacalbe sobre el HTML y el CSS para la demo; el HTML inicialmente contiene un simple formulario que permite introducir un udentificador para la sala de chat, un boton que al hacer click se suscribe a las notificaciones push, y dos listas con los suscriptores y los mensajes. Una vez suscrito, aparecerán controles adicionales para permitir al usuario actual escribir mensajes en el chat.

- -

El CSS ha sido muy minimo para no desvirtuar la explicación de la funcionalidad Push Api.

- - - -

El JavaScript es obviamente más sustancial. Echemos un vistazo al archivo JavaScript principal.

- -

Variables y configuración inicial

- -

Para iniciar, nosotros declaramos algunas variables a usar en nuestra app:

- -
var isPushEnabled = false;
-var useNotifications = false;
-
-var subBtn = document.querySelector('.subscribe');
-var sendBtn;
-var sendInput;
-
-var controlsBlock = document.querySelector('.controls');
-var subscribersList = document.querySelector('.subscribers ul');
-var messagesList = document.querySelector('.messages ul');
-
-var nameForm = document.querySelector('#form');
-var nameInput = document.querySelector('#name-input');
-nameForm.onsubmit = function(e) {
-  e.preventDefault()
-};
-nameInput.value = 'Bob';
- -

Primero, tenemos dos boleanos para hacer un seguimiento si se a suscrito a push, y si ha permitido las notificaciones.

- -

A continuación, tomamos una referencia al suscrito/no-suscrito {{htmlelement("button")}}, y se declaran variables para almacenar referencias a nuestro mensaje enviado boton/entrada (sólo se crean cuando la suscripsión es correcta.)

- -

Las siguientes variables toman referencia a los trés elementos principales {{htmlelement("div")}} en el diseño, por lo que podemos insertar elementos en ellos (por ejemplo cuando aparezca el botón envíar el mensaje de chat o el mensaje de chat aparezca en la lista de mensajes.)

- -

Finalmente tomamos referencia a nuestro formulario de selección de nombre y el elemento {{htmlelement("input")}}, damos a la entrada un valor por defecto, y usamos preventDefault() para detener el envío del formulario cuando este es enviado pulsando return.

- -

A continuación, pedimos permiso para enviar las notificaciones web, usando {{domxref("Notification.requestPermission","requestPermission()")}}:

- -
Notification.requestPermission();
- -

Ahora ejecutamos una sección de código cuando se dispara el onload, para empezar el proceso de inicialización de la app cuando se carga pro primera vez. En primer lugar añadimos un detector de eventos de clik al botón  Sucribirse/unsubscribe que ejecuta nuestra funcion unsubscribe() si actualmente estamos suscritos (isPushEnabled is true), y subscribe() de la otra manera:

- -
window.addEventListener('load', function() {
-  subBtn.addEventListener('click', function() {
-    if (isPushEnabled) {
-      unsubscribe();
-    } else {
-      subscribe();
-    }
-  });
- -

A continuación verificamos el service worked es soportado. Si es así, registramos un service worker usando {{domxref("ServiceWorkerContainer.register()")}}, y ejecutando nuestra función initialiseState(). Si no es así, entregamos un mensaje de error a la consola.

- -
  // Check that service workers are supported, if so, progressively
-  // enhance and add push messaging support, otherwise continue without it.
-  if ('serviceWorker' in navigator) {
-    navigator.serviceWorker.register('sw.js').then(function(reg) {
-      if(reg.installing) {
-        console.log('Service worker installing');
-      } else if(reg.waiting) {
-        console.log('Service worker installed');
-      } else if(reg.active) {
-        console.log('Service worker active');
-      }
-
-      initialiseState(reg);
-    });
-  } else {
-    console.log('Service workers aren\'t supported in this browser.');
-  }
-});
-
- -

La siguiente cosa en el código es la función initialiseState() — para el codigo completo comentado, mira en initialiseState() source on Github (no lo estamos repitiendo aquí por brevedad.)

- -

initialiseState() primero comprueba si las notificaciones son soportadas en los service workers, entonces establece la variable  useNotifications a verdadero. A continuación comprueba si dichas notificaciones están permitidas por el usuario y si los mensajes push están soportados, y reacciona deacuerdo a cada uno.

- -

Finalmente, se usa {{domxref("ServiceWorkerContainer.ready()")}} para esperar a que el service worker esté activo y listo para hacer las cosas. Una vez se revuelva el promise, recuperamos nuestra suscripsión para enviar los mensajes push usando la propiedad {{domxref("ServiceWorkerRegistration.pushManager")}}, que devuelve un objeto {{domxref("PushManager")}} cuando llamamos a {{domxref("PushManager.getSubscription()")}}. Una vez la segunda promesa interna se resuelva, habilitamos el botón subscribe/unsubscribe (subBtn.disabled = false;), y verificamos que tenemos un objeto suscripsión para trabajar.

- -

Si lo hacemos, entonces ya estamos suscritos. Esto es posible cuando la app no está abierta en el navegador; el service worker aun puede ser activado en segundo plano. si estamos suscritos, actualizamos la UI para mostrar que estamos suscritos por la actualizacion del label en el botón, entonces establecemos isPushEnabled to true, toma el punto final de suscripsión desde {{domxref("PushSubscription.endpoint")}}, genera una public key usando {{domxref("PushSubscription.getKey()")}}, y ejecutando nuestra función updateStatus(), que como verá más adelante se comunica con el servidor.

- -

Como un bonus añadido, configuramos un nuevo {{domxref("MessageChannel")}} usando el constructor {{domxref("MessageChannel.MessageChannel()")}}, toma una referencia al service worker activo usando {{domxref("ServiceworkerRegistration.active")}}, luego configure un canal entre el contexto principal del navegador y el contexto del service worker usando {{domxref("Worker.postMessage()")}}. El contexto del navegador recive mensajes en {{domxref("MessageChannel.port1")}}; Cuando esto suceda, ejecutamos la función handleChannelMessage() para decidir que hacer con esos datos (mirar la sección {{anch("Handling channel messages sent from the service worker")}} ).

- -

Subscribing and unsubscribing

- -

Ahora regresamos la atención a las funciones subscribe()unsubscribe() usadas para subscribe/unsubscribe al servicion de notificaciones push.

- -

In the case of subscription, we again check that our service worker is active and ready by calling {{domxref("ServiceWorkerContainer.ready()")}}. When the promise resolves, we subscribe to the service using {{domxref("PushManager.subscribe()")}}. If the subscription is successful, we get a {{domxref("PushSubscription")}} object, extract the subscription endpoint from this and generate a public key (again, {{domxref("PushSubscription.endpoint")}} and {{domxref("PushSubscription.getKey()")}}), and pass them to our updateStatus() function along with the update type (subscribe) to send the necessary details to the server.

- -

We also make the necessary updates to the app state (set isPushEnabled to true) and UI (enable the subscribe/unsubscribe button and set its label text to show that the next time it is pressed it will unsubscribe.)

- -

The unsubscribe() function is pretty similar in structure, but it basically does the opposite; the most notable difference is that it gets the current subscription using {{domxref("PushManager.getSubscription()")}}, and when that promise resolves it unsubscribes using {{domxref("PushSubscription.unsubscribe()")}}.

- -

Appropriate error handling is also provided in both functions.  

- -

We only show the subscribe() code below, for brevity; see the full subscribe/unsubscribe code on Github.

- -
function subscribe() {
-  // Disable the button so it can't be changed while
-  // we process the permission request
-
-  subBtn.disabled = true;
-
-  navigator.serviceWorker.ready.then(function(reg) {
-    reg.pushManager.subscribe({userVisibleOnly: true})
-      .then(function(subscription) {
-        // The subscription was successful
-        isPushEnabled = true;
-        subBtn.textContent = 'Unsubscribe from Push Messaging';
-        subBtn.disabled = false;
-
-        // Update status to subscribe current user on server, and to let
-        // other users know this user has subscribed
-        var endpoint = subscription.endpoint;
-        var key = subscription.getKey('p256dh');
-        updateStatus(endpoint,key,'subscribe');
-      })
-      .catch(function(e) {
-        if (Notification.permission === 'denied') {
-          // The user denied the notification permission which
-          // means we failed to subscribe and the user will need
-          // to manually change the notification permission to
-          // subscribe to push messages
-          console.log('Permission for Notifications was denied');
-
-        } else {
-          // A problem occurred with the subscription, this can
-          // often be down to an issue or lack of the gcm_sender_id
-          // and / or gcm_user_visible_only
-          console.log('Unable to subscribe to push.', e);
-          subBtn.disabled = false;
-          subBtn.textContent = 'Subscribe to Push Messaging';
-        }
-      });
-  });
-}
- -

Updating the status in the app and server

- -

The next function in our main JavaScript is updateStatus(), which updates the UI for sending chat messages when subscribing/unsubscribing and sends a request to update this information on the server.

- -

The function does one of three different things, depending on the value of the statusType parameter passed into it:

- - - -

Again, we have not included the entire function listing for brevity. Examine the full updateStatus() code on Github.

- -

Handling channel messages sent from the service worker

- -

As mentioned earlier, when a channel message is received from the service worker, our handleChannelMessage() function is called to handle it. This is done by our handler for the {{event("message")}} event, {{domxref("channel.port1.onmessage")}}:

- -
channel.port1.onmessage = function(e) {
-  handleChannelMessage(e.data);
-}
- -

This occurs when the service worker sends a channel message over.

- -

The handleChannelMessage() function looks like this:

- -
function handleChannelMessage(data) {
-  if(data.action === 'subscribe' || data.action === 'init') {
-    var listItem = document.createElement('li');
-    listItem.textContent = data.name;
-    subscribersList.appendChild(listItem);
-  } else if(data.action === 'unsubscribe') {
-    for(i = 0; i < subscribersList.children.length; i++) {
-      if(subscribersList.children[i].textContent === data.name) {
-        subscribersList.children[i].parentNode.removeChild(subscribersList.children[i]);
-      }
-    }
-    nameInput.disabled = false;
-  } else if(data.action === 'chatMsg') {
-    var listItem = document.createElement('li');
-    listItem.textContent = data.name + ": " + data.msg;
-    messagesList.appendChild(listItem);
-    sendInput.value = '';
-  }
-}
- -

What happens here depends on what the action property on the data object is set to:

- - - -
-

Note: We have to pass the data back to the main context before we do DOM updates because service workers don't have access to the DOM. You should be aware of the limitations of service workers before attemping to ue them. Read Using Service Workers for more details.

-
- -

Sending chat messages

- -

When the Send Chat Message button is clicked, the content of the associated text field is sent as a chat message. This is handled by the sendChatMessage() function (again, not shown in full for brevity). This works in a similar way to the different parts of the updateStatus() function (see {{anch("Updating the status in the app and server")}}) — we retrieve an endpoint and public key via a {{domxref("PushSubscription")}} object, which is itself retrieved via {{domxref("ServiceWorkerContainer.ready()")}} and {{domxref("PushManager.subscribe()")}}. These are sent to the server via {{domxref("XMLHttpRequest")}} in a message object, along with the name of the subscribed user, the chat message to send, and a statusType of chatMsg.

- -

The server

- -

As mentioned above, we need a server-side component in our app, to handle storing subscription details, and send out push messages when updates occur. We've hacked together a quick-and-dirty server using NodeJS (server.js), which handles the XHR requests sent by our client-side JavaScript code.

- -

It uses a text file (endpoint.txt) to store subscription details; this file starts out empty. There are four different types of request, marked by the statusType property of the object sent over in the request; these are the same as those understood client-side, and perform the required server actions for that same situation. Here's what each means in the context of the server:

- - - -

A couple more things to note:

- - - -

The service worker

- -

Now let's have a look at the service worker code (sw.js), which responds to the push messages, represented by {{Event("push")}} events. These are handled on the service worker's scope by the ({{domxref("ServiceWorkerGlobalScope.onpush")}}) event handler; its job is to work out what to do in response to each received message. We first convert the received message back into an object by calling {{domxref("PushMessageData.json()")}}. Next, we check what type of push message it is, by looking at the object's action property:

- - - -
self.addEventListener('push', function(event) {
-  var obj = event.data.json();
-
-  if(obj.action === 'subscribe' || obj.action === 'unsubscribe') {
-    fireNotification(obj, event);
-    port.postMessage(obj);
-  } else if(obj.action === 'init' || obj.action === 'chatMsg') {
-    port.postMessage(obj);
-  }
-});
- -

Next, let's look at the fireNotification() function (which is blissfully pretty simple).

- -
function fireNotification(obj, event) {
-  var title = 'Subscription change';
-  var body = obj.name + ' has ' + obj.action + 'd.';
-  var icon = 'push-icon.png';
-  var tag = 'push';
-
-  event.waitUntil(self.registration.showNotification(title, {
-    body: body,
-    icon: icon,
-    tag: tag
-  }));
-}
- -

Here we assemble the assets needed by the notification box: the title, body, and icon. Then we send a notification via the {{domxref("ServiceWorkerRegistration.showNotification()")}} method, providing that information as well as the tag "push", which we can use to identify this notification among any other notifications we might be using. When the notification is successfully sent, it manifests as a system notification dialog on the users computers/devices in whatever style system notifications look like on those systems (the following image shows a Mac OSX system notification.)

- -

- -

Note that we do this from inside an {{domxref("ExtendableEvent.waitUntil()")}} method; this is to make sure the service worker remains active until the notification has been sent. waitUntil() will extend the life cycle of the service worker until everything inside this method has completed.

- -
-

Note: Web notifications from service workers were introduced around Firefox version 42, but are likely to be removed again while the surrounding functionality (such as Clients.openWindow()) is properly implemented (see {{bug(1203324)}} for more details.)

-
- -

Handling premature subscription expiration

- -

Sometimes push subscriptions expire prematurely, without {{domxref("PushSubscription.unsubscribe()")}} being called. This can happen when the server gets overloaded, or if you are offline for a long time, for example.  This is highly server-dependent, so the exact behavior is difficult to predict. In any case, you can handle this problem by watching for the {{Event("pushsubscriptionchange")}} event, which you can listen for by providing a {{domxref("ServiceWorkerGlobalScope.onpushsubscriptionchange")}} event handler; this event is fired only in this specific case.

- -
self.addEventListener('pushsubscriptionchange', function() {
-  // do something, usually resubscribe to push and
-  // send the new subscription details back to the
-  // server via XHR or Fetch
-});
- -

Note that we don't cover this case in our demo, as a subscription ending is not a big deal for a simple chat server. But for a more complex example you'd probably want to resubscribe the user.

- -

Extra steps for Chrome support

- -

To get the app working on Chrome, we need a few extra steps, as Chrome currently relies on Google's Cloud Messaging service to work.

- -

Setting up Google Cloud Messaging

- -

To get this set up, follow these steps:

- -
    -
  1. Navigate to the Google Developers Console  and set up a new project.
  2. -
  3. Go to your project's homepage (ours is at https://console.developers.google.com/project/push-project-978, for example), then -
      -
    1. Select the Enable Google APIs for use in your apps option.
    2. -
    3. In the next screen, click Cloud Messaging for Android under the Mobile APIs section.
    4. -
    5. Click the Enable API button.
    6. -
    -
  4. -
  5. Now you need to make a note of your project number and API key because you'll need them later. To find them: -
      -
    1. Project number: click Home on the left; the project number is clearly marked at the top of your project's home page.
    2. -
    3. API key: click Credentials on the left hand menu; the API key can be found on that screen.
    4. -
    -
  6. -
- -

manifest.json

- -

You need to include a Google app-style manifest.json file in your app, which references the project number you made a note of earlier in the gcm_sender_id parameter. Here is our simple example manifest.json:

- -
{
-  "name": "Push Demo",
-  "short_name": "Push Demo",
-  "icons": [{
-        "src": "push-icon.png",
-        "sizes": "111x111",
-        "type": "image/png"
-      }],
-  "start_url": "/index.html",
-  "display": "standalone",
-  "gcm_sender_id": "224273183921"
-}
- -

You also need to reference your manifest using a {{HTMLElement("link")}} element in your HTML:

- -
<link rel="manifest" href="manifest.json">
- -

userVisibleOnly

- -

Chrome requires you to set the userVisibleOnly parameter to true when subscribing to the push service, which indicates that we are promising to show a notification whenever a push is received. This can be seen in action in our subscribe() function.

- -

See also

- - - -
-

Note: Some of the client-side code in our Push demo is heavily influenced by Matt Gaunt's excellent examples in Push Notifications on the Open Web. Thanks for the awesome work, Matt!

-
diff --git a/files/es/web/api/randomsource/index.html b/files/es/web/api/randomsource/index.html deleted file mode 100644 index 76e8d7fdc2..0000000000 --- a/files/es/web/api/randomsource/index.html +++ /dev/null @@ -1,111 +0,0 @@ ---- -title: RandomSource -slug: Web/API/RandomSource -tags: - - API - - Interface - - NeedsTranslation - - RandomSource - - Reference - - TopicStub - - Web Crypto API -translation_of: Web/API/Crypto/getRandomValues -translation_of_original: Web/API/RandomSource ---- -

{{APIRef("Web Crypto API")}}

- -

RandomSource represents a source of cryptographically secure random numbers. It is available via the {{domxref("Crypto")}} object of the global object: {{domxref("Window.crypto")}} on Web pages, {{domxref("WorkerGlobalScope.crypto")}} in workers.

- -

RandomSource is a not an interface and no object of this type can be created.

- -

Properties

- -

RandomSource neither defines nor inherits any property.

- -
-
- -

Methods

- -
-
{{ domxref("RandomSource.getRandomValues()") }}
-
Fills the passed {{ domxref("ArrayBufferView") }} with cryptographically sound random values.
-
- -

Specification

- - - - - - - - - - - - - - -
SpecificationStatusComment
{{SpecName('Web Crypto API', '#dfn-RandomSource')}}{{Spec2('Web Crypto API')}}Initial definition
- -

Browser Compatibility

- -

{{ CompatibilityTable() }}

- -
- - - - - - - - - - - - - - - - - - - -
FeatureChromeFirefox (Gecko)Internet ExplorerOperaSafari
Basic support11.0 {{ webkitbug("22049") }}{{CompatGeckoDesktop(21)}} [1]11.015.03.1
-
- -
- - - - - - - - - - - - - - - - - - - - - -
FeatureAndroidChrome for AndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Basic support{{ CompatNo() }}23{{CompatGeckoMobile(21)}}{{ CompatNo() }}{{ CompatNo() }}6
-
- -

[1] Although the transparent RandomSource is only available since Firefox 26, the feature was available in Firefox 21.

- -

See also

- - diff --git a/files/es/web/api/randomsource/obtenervaloresaleatorios/index.html b/files/es/web/api/randomsource/obtenervaloresaleatorios/index.html deleted file mode 100644 index b6e09439a9..0000000000 --- a/files/es/web/api/randomsource/obtenervaloresaleatorios/index.html +++ /dev/null @@ -1,75 +0,0 @@ ---- -title: Crypto.getRandomValues() -slug: Web/API/RandomSource/Obtenervaloresaleatorios -tags: - - API - - Criptografía - - Referencia - - metodo -translation_of: Web/API/Crypto/getRandomValues ---- -

{{APIRef("Web Crypto API")}}

- -

El método Crypto.getRandomValues() permite obtener valores aleatorios criptográficamente fuertes. El array que se pasa como parámetro se rellena con números aleatorios (entiéndase aleatorios en el sentido criptográfico).

- -

Con el fin de garantizar un rendimiento razonable, las distintas implementaciones no utilizan un generador de numeros aleatorios puro, sino que utilizan un generador numérico pseudo-aleatorio, sembrado con un valor con suficiente entropía. Los generadores numéricos pseudo-aleatorios utilizados difieren entre {{Glossary("user agent", "user agents")}}, pero son adecuados para usos criptográficos. Se require también que las distintas implementaciones usen una semilla con suficiente entropía, como una fuente de entropía a nivel de sistema.

- -

Sintaxis

- -
cryptoObj.getRandomValues(typedArray);
- -

Parámetros

- -
-
typedArray
-
Es un entero {{jsxref("TypedArray")}}, que puede ser un {{jsxref("Int8Array")}}, un {{jsxref("Uint8Array")}}, un {{jsxref("Int16Array")}}, un {{jsxref("Uint16Array")}}, un {{jsxref("Int32Array")}}, o un {{jsxref("Uint32Array")}}. Todos los elementos dentro del array seran sobreescritos con números aleatorios.
-
- -

Excepciones

- - - -

Ejemplo

- -
/* Asumiendo que window.crypto.getRandomValues está disponible */
-
-var array = new Uint32Array(10);
-window.crypto.getRandomValues(array);
-
-console.log("Tus numeros de la suerte:");
-for (var i = 0; i < array.length; i++) {
-    console.log(array[i]);
-}
-
- -

Especificación

- - - - - - - - - - - - - - -
EspecificaciónEstadoComentario
{{SpecName('Web Crypto API', '#RandomSource-method-getRandomValues')}}{{Spec2('Web Crypto API')}}Definición Inicial
- -

Compatibilidad del navegador

- -

La tabla de compatibilidad de esta página se genera a partir de datos estructurados. Si desea contribuir a los datos, por favor, compruebe https://github.com/mdn/browser-compat-data y envianos un pull request.

- -

{{Compat("api.Crypto.getRandomValues")}}

- -

Ver también

- - diff --git a/files/es/web/api/server-sent_events/index.html b/files/es/web/api/server-sent_events/index.html new file mode 100644 index 0000000000..9b22801732 --- /dev/null +++ b/files/es/web/api/server-sent_events/index.html @@ -0,0 +1,99 @@ +--- +title: Server-sent events +slug: Server-sent_events +tags: + - NeedsTranslation + - Server-sent events + - TopicStub +translation_of: Web/API/Server-sent_events +--- +

Traditionally, a web page has to send a request to the server to receive new data; that is, the page requests data from the server. With server-sent events, it's possible for a server to send new data to a web page at any time, by pushing messages to the web page. These incoming messages can be treated as Events + data inside the web page.

+ + + + + + + +
+

Documentation

+
+
+ Using server-sent events
+
+ A tutorial guide to writing both server and client side part of a server-sent events app.
+
+ EventSource reference
+
+ A reference to the client-side EventSource API.
+
+

View All...

+
+

Tools

+ + + +
+

See also

+ +

Specification

+ +

Browser compatibility

+
+ {{CompatibilityTable}}
+
+ + + + + + + + + + + + + + + + + + + +
FeatureChromeFirefox (Gecko)Internet ExplorerOperaSafari
EventSource support9{{CompatGeckoDesktop("6.0")}}{{CompatUnknown}}115
+
+
+ + + + + + + + + + + + + + + + + + + +
FeatureAndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
EventSource support{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}
+
diff --git a/files/es/web/api/server-sent_events/using_server-sent_events/index.html b/files/es/web/api/server-sent_events/using_server-sent_events/index.html new file mode 100644 index 0000000000..81a2f5dacc --- /dev/null +++ b/files/es/web/api/server-sent_events/using_server-sent_events/index.html @@ -0,0 +1,231 @@ +--- +title: Utilizando eventos enviados por el servidor (server-sent event) +slug: Server-sent_events/utilizando_server_sent_events_sse +translation_of: Web/API/Server-sent_events/Using_server-sent_events +--- +

Desarrollar una aplicación web que utilice server-sent events es muy fácil. Solo necesitas un pequeño código del lado del servidor para transmitir los eventos a la aplicación web, pero del lado de la aplicacion web se trabaja prácticamente igual que con cualquier otro tipo de eventos.

+ +

Puedes ver un ejemplo aqui (actualmente no funciona).

+ +

ejemplo2

+ +

Recibiendo eventos desde el servidor

+ +

El server-sent event API está contenido en la interfaz EventSource; para abrir una conexión al servidor para recibir eventos de él. Se crea un nuevo objeto new EventSource, especificando el URI de un script que genera los eventos, Por ejemplo:

+ +
var evtSource = new EventSource("ssedemo.php");
+
+ +
Nota: Aunque todavía no es parte de la norma, EventSource es soportado por Firefox 11 y posteriores. Se espera que pronto forme parte del estándar.
+ +

Una vez que ha instanciado el origen del evento, puede comenzar a escuchar los mensajes:

+ +
evtSource.onmessage = function(e) {
+  var newElement = document.createElement("li");
+
+  newElement.innerHTML = "message: " + e.data;
+  eventList.appendChild(newElement);
+}
+
+ +

Este codigo escucha todos los mensajes entrantes (Es decir, todos los avisos del servidor, que no tienen un campo de eventos en ellos) y anexa texto del mensaje a la lista en el documento HTML.

+ +

También puedes escuchar eventos, usando addEventListener():

+ +
evtSource.addEventListener("ping", function(e) {
+  var newElement = document.createElement("li");
+
+  var obj = JSON.parse(e.data);
+  newElement.innerHTML = "ping at " + obj.time;
+  eventList.appendChild(newElement);
+}, false);
+
+ +

Este código es similar,  excepto que este se activa cada vez que el servidor envia un mensaje con el campo de evento "ping"; entonces se analiza el JSON en el campo de datos y retorna esa informacion.

+ +

Enviando eventos desde el servidor

+ +

El script del servidor que envia los datos tiene que responder con el tipo MIME text/event-stream. Cada notificación se envia con un bloque de texto terminado en un par de saltos de línea, para mas detalles sobre el formato sobre la secuencia de evetos, ver {{ anch("Event stream format") }},

+ +

El codigo PHP para este ejemplo que estamos utilizando:

+ +
date_default_timezone_set("America/New_York");
+header("Content-Type: text/event-stream\n\n");
+
+$counter = rand(1, 10);
+while (1) {
+  // Every second, sent a "ping" event.
+
+  echo "event: ping\n";
+  $curDate = date(DATE_ISO8601);
+  echo 'data: {"time": "' . $curDate . '"}';
+  echo "\n\n";
+
+  // Send a simple message at random intervals.
+
+  $counter--;
+
+  if (!$counter) {
+    echo 'data: This is a message at time ' . $curDate . "\n\n";
+    $counter = rand(1, 10);
+  }
+
+  ob_flush();
+  flush();
+  sleep(1);
+}
+
+ +

Se genera un evento cada segundo, con el evento "ping". Los datos de cada evento es un objeto JSON que contiene, en este caso, solo la fecha en formato ISO 8601 correspondiente a la hora en que se generó el evento. A intervalos aleatorios, se envia un mensaje simple (sin ningún tipo de evento)

+ +

Gestion de errores

+ +

Cuando se producen problemas (como un tiempo de espera o problemas relacionados con el control de acceso), se genera un evento de error. Puedes tomar acción sobre esto al implementar una devolución de llamada al objeto EventSource:

+ +
evtSource.onerror = function(e) {
+  alert("EventSource failed.");
+};
+
+ +

En Firefox 22, no parece que haya manera de distinguir entre los diferentes de eventos de error.

+ +

Cerrando flujo de eventos

+ +

Por defecto, si la conexión entre el cliente y el servidor se cierra, la conexión es reiniciada. Podemos terminar la conexión con el método .close()

+ +
evtSource.close();
+ +
 
+ +

Formato de flujo de eventos (formato stream)

+ +

El flujo de eventos es una corriente sencilla de datos de texto, que deben ser codificados usando UTF-8. Los mensajes en el flujo de eventos están separados por un par de caracteres de salto de línea. Si hay un símbolo de dos puntos como primer caracter de una línea, se entiende que es un comentario y es ignorado. 

+ +
Nota: La línea de comentario se puede usar para prevenir que la conexión se agote por tiempo (timeout); un sevidor puede enviar periódicamente un comentario para mantener viva la conexión.
+ +

Cada mensaje consiste en una o más líneas de texto que enumeran los campos para ese mensaje. Cada campo está representado por el nombre del campo, seguido por los datos de texto para el valor de ese campo.

+ +

Campos

+ +

Los siguientes nombres de campo son definidos por la especificación:

+ +

event

+ +

El tipo de evento. Si se especifica, un evento se enviará al navegador a la escucha para el nombre del evento especificado, el sitio web usaria addEventLister() para escuchar eventos nombrados. El controlador onmessage se llama si no se especifica el nombre del evento para un mensaje.

+ +

data

+ +

El campo de datos  para el mensaje. Cuando el EventSource recibe múltiples lineas con "data:", se concatenara, insertando un caracter de nueva de linea entre cada uno. Se eliminan los saltos de línea al final [VERIFICAR].

+ +

id

+ +

  El ID del evento que establecerá el último ID del objeto EventSource.

+ +

Retry

+ +

El tiempo de reconexión para usar al intentar enviar el evento. [Qué código maneja esto?] Este debe ser un número entero, que especifica el tiempo de reconexion en milisegundos. Si se especifica un valor no entero, el campo se ignora.  

+ +

Se omiten todos los demas nombres de campo.

+ +
Nota: Si una línea no contiene dos puntos, la línea entera se tratara como un nombre de campo, con una cadena de valor vacio.
+ +

Ejemplos

+ +

Mensajes con datos únicamente

+ +

En el siguiente ejemplo, hay tres mensajes enviados. El primero es solo un comentario, debido a que empieza con dos puntos. Como se mencionó anteriormente, esto puede ser útil para mantener la conexión viva si los mensajes no son enviados regularmente.

+ +

The second message contains a data field with the value "some text". The third message contains a data field with the value "another message\nwith two lines". Note the newline in the value.

+ +

El segundo mensaje contiene un campo de datos con el valor "some text". El tercer mensaje contiene un campo de datos con el valor "another message\nwith two lines". Nota la nueva línea en el valor.

+ +
: this is a test stream
+
+data: some text
+
+data: another message
+data: with two lines
+
+ +

Eventos nombrados

+ +

Este ejemplo envia algunos eventos nombrados. Cada uno tiene un nombre de evento especificado por el campo event, y un campo data cuyo valor es una cadena JSON apropiada con los datos necesarios para que el cliente actue sobre el evento. El campo data, podria, por supuesto, tener cualquier cadena; no tiene que ser un JSON.

+ +
event: userconnect
+data: {"username": "bobby", "time": "02:33:48"}
+
+event: usermessage
+data: {"username": "bobby", "time": "02:34:11", "text": "Hi everyone."}
+
+event: userdisconnect
+data: {"username": "bobby", "time": "02:34:23"}
+
+event: usermessage
+data: {"username": "sean", "time": "02:34:36", "text": "Bye, bobby."}
+
+ +

Mezclando y emparejando

+ +

No tienes que usar solamente mensajes sin nombrar o eventos tipados; puedes mezclarlo juntos en un solo flujo de evento.

+ +
event: userconnect
+data: {"username": "bobby", "time": "02:33:48"}
+
+data: Here's a system message of some kind that will get used
+data: to accomplish some task.
+
+event: usermessage
+data: {"username": "bobby", "time": "02:34:11", "text": "Hi everyone."}
+
+ +

Compatibilidad con navegadores

+ +

{{ CompatibilityTable() }}

+ +
+ + + + + + + + + + + + + + + + + + + +
FeatureChromeFirefox (Gecko)Internet ExplorerOperaSafari
EventSource support9{{ CompatGeckoDesktop("6.0") }}{{ CompatNo() }}115
+
+ +
+ + + + + + + + + + + + + + + + + + + +
FeatureAndroid-and-MeegoFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
EventSource support6 a 24 b1.0{{ CompatUnknown() }}11.14
+
+ +

 

diff --git a/files/es/web/api/storage/localstorage/index.html b/files/es/web/api/storage/localstorage/index.html deleted file mode 100644 index 5c46cb9559..0000000000 --- a/files/es/web/api/storage/localstorage/index.html +++ /dev/null @@ -1,136 +0,0 @@ ---- -title: LocalStorage -slug: Web/API/Storage/LocalStorage -tags: - - Almacenamiento en Navegador - - Almacenamiento local -translation_of: Web/API/Window/localStorage -translation_of_original: Web/API/Web_Storage_API/Local_storage ---- -

localStorage (almacenamiento local) es lo mismo que sessionStorage (almacenamiento de sesión), con las mismas reglas de mismo-origen aplicadas, pero es persistente a través de diferentes sesiones. localStorage se introdujo en la version Firefox 3.5.

- -
Nota: Cuando el navegador está en modo de navegación privado, una nueva base de datos temporal se crea para guardar datos de almacenamiento local. Esta base de datos se vacía y descarta cuando salimos del modo de navegación privado.
- -
// Guardar datos al almacenamiento local actual
-localStorage.setItem("nombredeusuario", "John");
-
-// Acceder a datos almacenados
-alert( "nombredeusuario = " + localStorage.getItem("nombredeusuario"));
- -

La persistencia de localStorage lo hace útil para una variedad de cosas, incluyendo contadores de páginas, como se demuestra en este tutorial en Codepen.

- -

Compatibilidad

- -

Los objetos de Storage (almacenamiento) son una adición reciente al estándar, por lo que pueden no estar presentes en todos los navegadores. Esto se puede solucionar si introduce uno de los dos códigos al principio de sus scripts, permitiendo el uso de el objeto localStorage en implementaciones que no lo soportan de forma nativa.

- -

Este algoritmo es una imitación exacta del objeto localStorage, pero hace 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) + "; expires=Tue, 19 Jan 2038 03:14:07 GMT; 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; }
-        document.cookie = escape(sKey) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; 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 aCouple, iKey, nIdx = 0, aCouples = document.cookie.split(/\s*;\s*/); nIdx < aCouples.length; nIdx++) {
-        aCouple = aCouples[nIdx].split(/\s*=\s*/);
-        if (aCouple.length > 1) {
-          oStorage[iKey = unescape(aCouple[0])] = unescape(aCouple[1]);
-          aKeys.push(iKey);
-        }
-      }
-      return oStorage;
-    };
-    this.configurable = false;
-    this.enumerable = true;
-  })());
-}
-
- -
Nota: El tamaño máximo de datos que se puede guardar está muy restringido por el uso de cookies. Con este algoritmo, utilize las funciones localStorage.getItem()localStorage.setItem(), y localStorage.removeItem() para agregar, cambiar, o quitar una clave. El uso del método localStorage.suClave para obtener, establecer, o borrar una clave no está permitido con este código. También se puede cambiar el nombre y usarse sólo para gestionar las cookies de el documento sin importar el objeto localStorage.
- -
Nota: Al cambiar la cadena "; expires=Tue, 19 Jan 2038 03:14:07 GMT; path=/" a: "; path=/" (y al cambiar el nombre del objeto), esto se pasará a ser un sessionStorage polyfill en vez de un localStorage polyfill. Sin embargo, esta implementación compartirá valores almacenados a través de pestañas y ventanas del navegador (y sólo se borrará cuando todas las ventanas del navegador hayan sido cerradas), mientras que una implementación  sessionStorage completamente compatible sólo restringirá los valores guardados al contexto actual del navegador.
- -

Esta es otra imitación menos exacta de el objeto localStorage, es más simple que la anterior, pero es compatible con navegadores antiguos, como Internet Explorer < 8 (probado y funcional incluso en Internet Explorer 6). También hace uso de 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) + "; expires=Tue, 19 Jan 2038 03:14:07 GMT; path=/";
-      this.length = document.cookie.match(/\=/g).length;
-    },
-    length: 0,
-    removeItem: function (sKey) {
-      if (!sKey || !this.hasOwnProperty(sKey)) { return; }
-      document.cookie = escape(sKey) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; 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 se puede guardar está muy restringido por el uso de cookies. Con este algoritmo, utilize las funciones localStorage.getItem()localStorage.setItem(), y localStorage.removeItem() para agregar, cambiar, o quitar una clave. El uso del método localStorage.suClave para obtener, establecer, o borrar una clave no está permitido con este código. También se puede cambiar el nombre y usarse sólo para gestionar las cookies de el documento sin importar el objeto localStorage.
- -
Nota: Al cambiar la cadena "; expires=Tue, 19 Jan 2038 03:14:07 GMT; path=/" a: "; path=/" (y al cambiar el nombre del objeto), esto se volverá un sessionStorage polyfill en vez de un localStorage polyfill. Sin embargo, esta implementación compartirá valores almacenados a través de pestañas y ventanas del navegador (y sólo se borrará cuando todas las ventanas del navegador hayan sido cerradas), mientras que una implementación  sessionStorage completamente compatible sólo restringirá los valores guardados al contexto actual del navegador.
- -

Compatibilidad y relación con globalStorage

- -

localStorage es lo mismo que globalStorage[location.hostname], con la excepción de que tiene un ámbito de origen HTML5 (esquema + nombre del host + puerto no estandar), y localStorage es una instancia de Storage, al contrario que globalStorage[location.hostname], que es una instancia de StorageObsolete, como se explica más adelante. Por ejemplo, http://ejemplo.com no puede acceder al mismo objeto localStorage que https://ejemplo.com, pero los dos pueden acceder al mismo elemento de globalStorage. --localStorage es una interfaz estándar mientras que globalStorage no lo es, así que no se debe depender de ella.

- -

Nótese que al establecer una propiedad en globalStorage[location.hostname] no la establece en localStorage, y al extender Storage.prototype no afecta a los elementos de globalStorage; sólo al extender StorageObsolete.prototype los afecta.

- -

El formato de Storage

- -

Las claves y valores de Storage se guardan en el formato UTF-16 DOMString, que usa 2 bytes por carácter.

diff --git a/files/es/web/api/subtlecrypto/encrypt/index.html b/files/es/web/api/subtlecrypto/encrypt/index.html deleted file mode 100644 index 8f35030d35..0000000000 --- a/files/es/web/api/subtlecrypto/encrypt/index.html +++ /dev/null @@ -1,142 +0,0 @@ ---- -title: SubtleCrypto.digest() -slug: Web/API/SubtleCrypto/encrypt -tags: - - API - - Encriptación - - Referencia - - SubtleCrypto - - Web Crypto API - - encrypt -translation_of: Web/HTTP/Headers/Digest ---- -
{{APIRef("Web Crypto API")}}
- -

El método digest() de la interfaz {{domxref("SubtleCrypto")}} genera un digest de los datos proveidos. Un {{domxref("digest")}} es un valor corto de longitud fija derivado de alguna entrada de longitud variable. Los digest criptográficos deben mostrar resistencia a colisiones, lo que significa que es difícil encontrar dos entradas diferentes que tengan el mismo valor de digest.

- -

Toma como argumento un identificador para el algoritmo digest a utilizar y los datos a codificar. Devuelve un Promise que se completará con el digest.

- -

Sintaxis

- -
const digest = crypto.subtle.digest(algorithm, data);
-
- -

Parámetros

- - - -

Valor de retorno

- - - -

Algoritmos soportados

- -

Los argoritmos digest, también conocidos como funciones criptográficas hash, transforman un bloque de datos arbitrariamente grande en una salida de tamaño fijo, normalmente mucho más corta que la entrada. Tienen una variedad de aplicaciones en criptografía.

- -

SHA-1

- -

Este algoritmo se especifica en FIPS 180-4, sección 6.1, y produce una salida de 160 bits de largo.

- -
-

Advertencia: Este algoritmo se considera ahora vulnerable y no debe utilizarse para aplicaciones criptográficas.

-
- -

SHA-256

- -

Este algoritmo se especifica en FIPS 180-4, sección 6.2, y produce una salida de 256 bits de largo.

- -

SHA-384

- -

Este algoritmo se especifica en FIPS 180-4, sección 6.5, y produce una salida de 384 bits de largo.

- -

SHA-512

- -

Este algoritmo se especifica en FIPS 180-4, sección 6.4, y produce una salida de 512 bits de largo.

- -
-

Sugerencia: Si estás buscando aquí cómo crear un código de autenticación de mensajes "keyed-hash" (HMAC), necesitas usar SubtleCrypto.sign() en su lugar.

-
- -

Ejemplos

- -

Ejemplo básico

- -

Este ejemplo codifica un mensaje, luego calcula su digest SHA-256 y muestra la longitud del mismo:

- -
const text = 'An obscure body in the S-K System, your majesty. The inhabitants refer to it as the planet Earth.';
-
-async function digestMessage(message) {
-  const encoder = new TextEncoder();
-  const data = encoder.encode(message);
-  const hash = await crypto.subtle.digest('SHA-256', data);
-  return hash;
-}
-
-const digestBuffer = await digestMessage(text);
-console.log(digestBuffer.byteLength);
-
- -

Convirtiendo un digest a una cadena hexadecimal

- -

El resumen se devuelve como un ArrayBuffer, pero para la comparación y visualización los digests se representan a menudo como cadenas hexadecimales. Este ejemplo calcula un digest, y luego convierte el ArrayBuffer a un string hexadecimal:

- -
const text = 'An obscure body in the S-K System, your majesty. The inhabitants refer to it as the planet Earth.';
-
-async function digestMessage(message) {
-  const msgUint8 = new TextEncoder().encode(message);                           // encode as (utf-8) Uint8Array
-  const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8);           // hash the message
-  const hashArray = Array.from(new Uint8Array(hashBuffer));                     // convert buffer to byte array
-  const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); // convert bytes to hex string
-  return hashHex;
-}
-
-const digestHex = await digestMessage(text);
-console.log(digestHex);
-
- -

Especificaciones

- - - - - - - - - - - - - - -
EspecificaciónEstadoComentario
{{SpecName('Web Crypto API', '#dfn-SubtleCrypto-method-digest', 'SubtleCrypto.digest()')}}{{Spec2('Web Crypto API')}}Definición inicial.
- -

Compatibilidad del navegador

- - - -

{{Compat("api.SubtleCrypto.digest")}}

- -
-

En Chrome 60, se añadió una característica que deshabilita crypto.subtle para conexiones no TLS.

-
- -

Ver también

- - diff --git a/files/es/web/api/touch_events/index.html b/files/es/web/api/touch_events/index.html new file mode 100644 index 0000000000..02a4a4eacb --- /dev/null +++ b/files/es/web/api/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."
diff --git a/files/es/web/api/vibration_api/index.html b/files/es/web/api/vibration_api/index.html new file mode 100644 index 0000000000..8c9c7b5f06 --- /dev/null +++ b/files/es/web/api/vibration_api/index.html @@ -0,0 +1,155 @@ +--- +title: Vibración API +slug: Web/Guide/API/Vibration +tags: + - API + - Firefox OS + - Mobile + - Principiante + - Vibración + - Vibrado + - Vibrar + - WebAPI +translation_of: Web/API/Vibration_API +--- +

La mayoría de los dispositivos modernos pueden vibrar a través del hardware, esto permite que a través del código de software se pueda emitir estas vibraciones. La Vibration API  ofrece a las aplicaciones web la capacidad de acceder a este hardware en caso este lo soporte, caso contrario el dispositivo no hace nada.

+ +

Describiendo vibraciones

+ +

Vibración se puede describir como un patrón de prender y apagar pulsos, los cuales pueden variar en longitud. El patrón puede consistir de un sólo número que indica cuantos milisegundos vibrará, o un arreglo de enteros describiendo un patrón de vibraciones y pausas. La vibración es controlada por un solo método:

+ +

{{domxref("window.navigator.vibrate()")}}.

+ +

Vibración simple

+ +

Puedes iniciar una sola vibración del hardware pasando como argumento un sólo número, o un arreglo de un sólo número:

+ +
window.navigator.vibrate(200);
+window.navigator.vibrate([200]);
+
+ +

Ambos ejemplos hacen vibrar el dispositivo por 200 ms.

+ +

Patrones de vibración

+ +

Un arreglo de valores describen que las vibraciones serán por períodos alternados, es decir, el dispositivo vibrará luego no lo hará, así según la secuencia definida. Cada valor en el arreglo es convertido a entero para luego ser interpretado alternadamente como el tiempo que el dispositivo debe vibrar y el tiempo que no debe vibrar. Ejemplo:

+ +
window.navigator.vibrate([200, 100, 200]);
+
+ +

Según este ejemplo el dispositivo vibrará por 200ms, luego se detiene por 100ms y luego vibra 200ms.

+ +

Puedes especificar cuantas vibraciones/pausas desees, y el arreglo puede tener un tamaño par o impar. No importa que agregues una pausa como el último valor del arreglo, ya que el celular dejará de vibrar de todas formas al final de cada vibración.

+ +

Cancelar vibraciones existentes

+ +

Llamar {{domxref("window.navigator.vibrate()")}} con un valor de 0, arreglo vació, o arreglo que contenga 0's detendrá cualquier vibración en curso.

+ +

Vibraciones continuas

+ +

Algunas básicas acciones son setInterval y clearInterval que nos permitirán crear vibraciones persistentes:

+ +
var intervaloDeVibrado;
+
+// Iniciar la vibración
+function iniciarVibrado(duracion) {
+	navigator.vibrate(duracion);
+}
+
+// Detiene la vibración
+function detenerVibrado() {
+	// Limpiar el intervalo y detener las vibraciones existentes
+	if(intervaloDeVibrado) clearInterval(intervaloDeVibrado);
+	navigator.vibrate(0);
+}
+
+// Iniciar las vibraciones con una determinado tiempo e intervalo
+// Asumir que el valor recibido es un entero
+function iniciarVibradoPersistente(duracion, intervalo) {
+	intervaloDeVibrado = setInterval(function() {
+		iniciarVibrado(duracion);
+	}, intervalo);
+}
+ +

Claro que el código de arriba no toma en cuenta el método de utilizar un arreglo de vibración, utilizar un arreglo para vibración persistente necesitaría recalcular la suma de los elementos del arregloo y crear un intervalo basado en esos números (agregando adicionalmente las pausas)

+ +

¿Por qué utilizar Vibration API?

+ +

Esta API es claramente accesible a través de dispositivos móbiles. Vibration API puede servir para alertas en las aplicaciones web del celular, y sería es asombrosa cuando se utiliza en juegos o en aplicaciones pesadas. Imagínate mirando un video en tu celular y durante la escena de explosión,tu teléfono vibra un poco. O la sensación que tendría tu usuario al sentir el estallido de una bomba en el juego Bomberman.

+ +

Especificaciones

+ + + + + + + + + + + + + + + + +
EspecificaciónEstadoComentario
{{SpecName('Vibration API')}}{{Spec2('Vibration API')}}Especificación inicial.
+ +

Compatibilidad entre navegadores

+ +
{{CompatibilityTable}}
+ +
+ + + + + + + + + + + + + + + + + + + +
CaracteŕisticaChromeFirefox (Gecko)Internet ExplorerOperaSafari
Soporte básico{{CompatVersionUnknown}} {{property_prefix("webkit")}}11.0 {{property_prefix("moz")}}
+ 16 (no prefix)
{{CompatNo}}{{CompatNo}}{{CompatNo}}
+
+ +
+ + + + + + + + + + + + + + + + + + + +
CaracterísticaAndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Soporte básico{{CompatVersionUnknown}} {{property_prefix("webkit")}}11.0 {{property_prefix("moz")}}{{CompatNo}}{{CompatNo}}{{CompatNo}}
+
+ +

Ver También

+ + diff --git a/files/es/web/api/web_audio_api/index.html b/files/es/web/api/web_audio_api/index.html new file mode 100644 index 0000000000..e7cb54d803 --- /dev/null +++ b/files/es/web/api/web_audio_api/index.html @@ -0,0 +1,510 @@ +--- +title: Web Audio API +slug: Web_Audio_API +translation_of: Web/API/Web_Audio_API +--- +
{{apiref("Web Audio API")}}
+ +

La API de Audio Web provee un sistema poderoso y versatil para controlar audio en la Web, permitiendo a los desarrolladores escoger fuentes de audio, agregar efectos al audio, crear visualizaciones de audios, aplicar efectos espaciales (como paneo) y mucho más.

+ +

Conceptos y uso de audio Web

+ +

La API de Audio Web involucra manejar operaciones de audio dentro de un contexto de audio, y ha sido diseñada para permitir enrutamiento modular. Las operaciones de audio básicas son realizadas con nodos de audio, que están enlazados juntos para formar un gráfico de enrutamiento de audio. Muchas fuentes — con diferentes tipos de diseño de canales — están soportadas incluso dentro de un único contexto. Este diseño modular provee flexibilidad para crear funciones de audio complejas con efectos dinámicos.

+ +

Los nodos de audio están enlazados en cadenas y redes simples por sus entradas y salidas. Éstos típicamente empiezan con una o más fuentes. Las fuentes provee matrices de intensidades de sonidos (muestras) en segmentos de tiempo muy pequeños, a menudo decenas de miles de éstos por segundo.  Éstos podrían ser calculados matemáticamente (como  {{domxref("OscillatorNode")}}), o pueden ser grabaciones de archivos de audio o video (como {{domxref("AudioBufferSourceNode")}} y {{domxref("MediaElementAudioSourceNode")}}) y transmisiones de audio ({{domxref("MediaStreamAudioSourceNode")}}). De hecho, los archivos de sonido son sólo grabaciones de intensidades de sonido, que vienen desde micrófonos o instrumentos eléctricos, y mezclados en una onda única y complicada.

+ +

Los resultados de éstos nodos podrían ser enlazados a las entradas de otros, que mezclan o modifican estas transmisiones de muestras de audio en diferentes transmisiones. Una modificación común es multiplicar las muestras por un valor para hacerlas más fuertes o silenciosas (como es el caso con {{domxref("GainNode")}}). Una vez que el sonido ha sido lo suficientemente procesado por el efecto necesario, puede ser enlazados a la entrada de un destino({{domxref("AudioContext.destination")}}), que enviá el sonido a los altavoces o auriculares. Esta última conexión sólo es necesaria si el usuario debe escuchar el audio.

+ +

Un diagrama de flujo simple y típico para el audio web se vería algo como esto:

+ +
    +
  1. Crear contexto de audio
  2. +
  3. Dentro del contexto, crear fuentes — como <audio>, oscillator, stream
  4. +
  5. Crear nodos de efectos, tales como reverberación, filtro biquad, panner, compresor
  6. +
  7. Ecoge el destino final del audio, por ejemplo tu sistema de altavoces
  8. +
  9. Conecta las fuentes a los efectos, y los efectos al destino.
  10. +
+ +

A simple box diagram with an outer box labeled Audio context, and three inner boxes labeled Sources, Effects and Destination. The three inner boxes have arrow between them pointing from left to right, indicating the flow of audio information.

+ +

El tiempo es controlado con alta precisión baja latencia, permitiendo a los desarrolladores escribir código que responda con precisión a los eventos y sea capaz de apuntar a muestras específicas, incluso en una alta frecuencia de muestreo. Por lo tanto, las aplicaciones como las cajas de ritmos y los secuenciadores están a su alcance.

+ +

El API de Audio Web también nos permite controlar cómo el audio es espacializado. Usando un sistema basado en un modelo fuente-oyente, esto permite controlar el modeo de paneo y que se ocupa de la atenuación inducida por distancia o desplazamiento doppler inducido por una fuente en movimiento (o un oyente en movimiento).

+ +
+

Puedes leear sobre la teoría del API de Audio Web con más detalle en nuestro artículo Conceptos Básicos detrás del API de Audio Web.

+
+ +

Interfaces del API de Audio Web

+ +

La API de Audio Web tiene un número de interfaces y eventos asociados, que han sido divididos en nueve categorias de funcionalidad.

+ +

Definición general del gráfico de audio

+ +

Contenedores y definiciones generales que dan forma a los gráficos de audio en el uso de Web Audio API.

+ +
+
{{domxref("AudioContext")}}
+
La interfaz AudioContext representa un gráfico de procesamiento de audio construido de módulos de audio enlazados juntos, cada uno representado por un {{domxref("AudioNode")}}. Un contexto de audio controla la creación de los nodos que contiene y la ejecución del procesamiento del audio, or decoding. Necesitas crear un AudioContext antes de hacer cualquier cosa,  ya que todo pasa dentro de un contexto de audio.
+
{{domxref("AudioNode")}}
+
La interfaz AudioNode representa un módulo de procesamiento de audio como una fuente de audio (por ejemplo un ejemplo HTML {{HTMLElement("audio")}} or {{HTMLElement("video")}}), destino de audio, módulo de procesamiento intermedio (por ejemplo un filtro como {{domxref("BiquadFilterNode")}}, o control de volumen como {{domxref("GainNode")}}).
+
{{domxref("AudioParam")}}
+
La interfaz AudioParam representa un parámetro relacionado al audio, como uno de un {{domxref("AudioNode")}}. Esto puede ser establecido a un valor específico o un cambio de valor, y puede ser agendado para que ocurra en un momento específico y siguiendo un patrón específico.
+
{{domxref("AudioParamMap")}}
+
Provee una interfaz como para mapear a un grupo de interfaces {{domxref("AudioParam")}}, lo que significa que proporciona los métodos forEach(), get(), has(), keys(), y values(), como también una propiedad size.
+
{{domxref("BaseAudioContext")}}
+
La interfaz BaseAudioContext actúa como una definición base para procesamiento de gráficos de audio en y fuera de línea, como lo representa {{domxref("AudioContext")}} y {{domxref("OfflineAudioContext")}} resepectivamente. No tendrás que usar BaseAudioContext directamente — tendrás que usar sus características a través de una de éstas dos interfaces heredadas.
+
El evento {{event("ended")}}
+
El evento ended es lanzado cuando la reproducción se detiene porque se alcanzó el fin del archivo de medio.
+
+ +

Definiendo fuentes de audio

+ +

Las interfaces que definen fuentes de audio para usar en la API de Web.

+ +
+
{{domxref("AudioScheduledSourceNode")}}
+
La interfaz AudioScheduledSourceNode es una interfaz padre para muchos tipos de interfaces de nodos de fuentes de audio. Es un {{domxref("AudioNode")}}.
+
+ +
+
{{domxref("OscillatorNode")}}
+
La interfaz OscillatorNode representa una forma de onda periódica, como una onda sinusoidal o triangular. Es un módulo de procesamiento de audio {{domxref("AudioNode")}} que causa que se cree una frecuencia de onda determinada.
+
{{domxref("AudioBuffer")}}
+
La interfaz AudioBuffer representa un recurso de audio corto que reside en la memoria, creado desde un archivo de audio usando el método {{ domxref("AudioContext.decodeAudioData()") }}, o creado con datos sin procesar usando {{ domxref("AudioContext.createBuffer()") }}. Una vez decodificado en esta forma, el audio puede ser colocado en un {{ domxref("AudioBufferSourceNode") }}.
+
{{domxref("AudioBufferSourceNode")}}
+
La interfaz AudioBufferSourceNode representa una fuente de audio que consiste en una datos de audio en la memoria, almacenada en un {{domxref("AudioBuffer")}}. Es un {{domxref("AudioNode")}} que actúa como una fuente de audio.
+
{{domxref("MediaElementAudioSourceNode")}}
+
La interfaz MediaElementAudioSourceNode representa una fuente de audio que consiste en un elemento {{ htmlelement("audio") }} o {{ htmlelement("video") }} de HTML5. Es un {{domxref("AudioNode")}} que actúa como una fuente de audio.
+
{{domxref("MediaStreamAudioSourceNode")}}
+
La interfaz MediaStreamAudioSourceNode representa una fuente de audio que consiste en un {{domxref("MediaStream")}} de WebRTC (como una cámara web, micrófono, o una transmisión siendo enviada a una computadora remota). Es un {{domxref("AudioNode")}} que actúa como una fuente de audio.
+
+ +

Definiendo filtros de efectos de audio

+ +

Interfaces para definir efectos que quieras aplicar a tus fuentes de audio.

+ +
+
{{domxref("BiquadFilterNode")}}
+
La interfaz BiquadFilterNode representa una filtro de bajo orden sencillo. Es un {{domxref("AudioNode")}} que puede representar diferentes tipos de filtros, dispositivos de control de tono, o ecualizadores gráficos. Un BiquadFilterNode siempre tiene exactamente una entrada y una salida.
+
{{domxref("ConvolverNode")}}
+
La interfaz ConvolverNode es un {{domxref("AudioNode")}} que realiza una Convolución Lineal en un {{domxref("AudioBuffer")}} determinado, y es usado a menudo para lograr un efecto de reverberación.
+
{{domxref("DelayNode")}}
+
La interfaz DelayNode representa una línea de detardo; un módulo de procesamiento de audio de {{domxref("AudioNode")}} que causa un retardo entre la llegada de una entrada de datos y su propagación a la salida.
+
{{domxref("DynamicsCompressorNode")}}
+
La intefaz DynamicsCompressorNode proporciona un efecto de compresión, que reduce el volumen de las partes más ruidosas de la señal para ayudar a evitar el recorte y la distorsión que pueden ocurrir cuando se reproducen y multiplexan múltiples sonidos a la vez.
+
{{domxref("GainNode")}}
+
La intefaz GainNode representa un cambio de volumen. Es un módulo de procesamiento de audio de {{domxref("AudioNode")}} que causa que una ganancia determinada para ser aplicada a la entrada de datos antes de su propacación a la salida.
+
{{domxref("WaveShaperNode")}}
+
La interfaz WaveShaperNode representa un la interfaz representa un distorsionador no lineal. Es un {{domxref("AudioNode")}} que usa una curva para aplicar una distorsión en forma de onda a la señal. Además de los obvios efectos de distorsión, a menudo se usa para agregar una sensación cálida a la señal.
+
{{domxref("PeriodicWave")}}
+
Describe una forma de onda periódica que puede ser usada para dar forma a la salida de un {{ domxref("OscillatorNode") }}.
+
{{domxref("IIRFilterNode")}}
+
Implementa un filtro de respuesta de pulso infinito (IIR) general; este tipo de filtro se puede usar para implementar dispositivos de control de tono y ecualizadores gráficos también.
+
+ +

Definición de destinos de audio

+ +

Una vez que haya terminado de procesar su audio, estas interfaces definen dónde emitirlo.

+ +
+
{{domxref("AudioDestinationNode")}}
+
La interfaz AudioDestinationNode representa el destino final de una fuente de audio en contexto determinado — usualmente los altavoces de tu dispositivo.
+
{{domxref("MediaStreamAudioDestinationNode")}}
+
La interfaz MediaStreamAudioDestinationNode representa un destino de audio que consiste en un {{domxref("MediaStream")}} de WebRTC con un AudioMediaStreamTrack sencillo, que puede ser usado de una manera similiar a un {{domxref("MediaStream")}} obtenido desde {{ domxref("MediaDevices.getUserMedia", "getUserMedia()") }}. Es un {{domxref("AudioNode")}} que actúa como un destino de audio.
+
+ +

Análisis y visualización de datos

+ +

Si quieres extraer el tiempo, frecuencia, y otros datos de tu audio, AnalyserNode es lo que necesitas.

+ +
+
{{domxref("AnalyserNode")}}
+
La interfaz AnalyserNode representa un nodo capáz de proveer la frecuencia en tiempo real y la información del análisis del dominio de tiempo, para propósitos de análisis y visualización de datos.
+
+ +

División y fusión de canales de audio

+ +

Para dividir y fusionar canales de audio, deberás usar estas interfaces.

+ +
+
{{domxref("ChannelSplitterNode")}}
+
La interfaz ChannelSplitterNode separa los diferentes canales de una fuente de audio enn un conjunto de salidas mono.
+
{{domxref("ChannelMergerNode")}}
+
La interfaz ChannelMergerNode reune las diferentes salidas mono en una sola salida. Cada entrada deberá ser usada para llenar un canal de la salida.
+
+ +

Espacialización de audio

+ +

Estas interfaces te permiten agregar efectos de paneo de especialización de audio a tus fuentes de audio.

+ +
+
{{domxref("AudioListener")}}
+
La interfaz AudioListener representa la posición y orientación de la única persona escuchando la escena de audio usada en la espacialización de audio.
+
{{domxref("PannerNode")}}
+
La interfaz PannerNode representa la posición y comportamiento de una señal de fuente de audio en un espacio 3D, permitiéndote crear efectos de paneo complejos.
+
{{domxref("StereoPannerNode")}}
+
La interfaz StereoPannerNode representa un nodo de panner estéreo simple que se puede usar para panoramizar un flujo de audio hacia la izquierda o hacia la derecha.
+
+ +

Proccesamiento de audio en JavaScript

+ +

Usando worklets de audio (pequeñas tareas), puedes definir nodos personalizados de audio escritos en JavaScript o WebAssembly. Los worklets de audios implementan la interfaz {{domxref("Worklet")}}, una versión ligera de la interfaz {{domxref("Worker")}}. A partir del enero de 2018, los worklets de audio están disponibles en Chrome 64 detrás de un identificador.

+ +
+
{{domxref("AudioWorklet")}} {{experimental_inline}}
+
La interfaz AudioWorklet está disponible a través de {{domxref("BaseAudioContext.audioWorklet")}} y te permite agregar nuevos móduloss al worklet de audio.
+
{{domxref("AudioWorkletNode")}} {{experimental_inline}}
+
La intefaz AudioWorkletNode representa un {{domxref("AudioNode")}} que está insertada en un gráfico de audio y puede pasar mensajes a la AudioWorkletProcessor.
+
{{domxref("AudioWorkletProcessor")}} {{experimental_inline}}
+
La interfaz AudioWorkletProcessor representa código de procesamiento de audio que se ejecuta en un AudioWorkletGlobalScope que genera, procesa, o analiza audio directamente, y puede pasar mensajes al AudioWorkletNode.
+
{{domxref("AudioWorkletGlobalScope")}} {{experimental_inline}}
+
La interfaz AudioWorkletGlobalScope es un objeto derivado de WorkletGlobalScope que representa un contexto del worker en el que se ejecuta un script de procesamiento de audio; está diseñado para permitir la generación, procesamiento, y análisis de datos de audio directamente usando JavaScript en un hilo worklet.
+
+ +

Antes de que se definieran los worklets de audio, la API de Web Audio usó  ScriptProcessorNode {{deprecated_inline}} para procesamiento de audio basado en JavaScript. Como el código se ejecuta en el hilo principal, tuvo un mal rendimiento. ScriptProcessorNode se mantiene por razones históricas pero está marcada como obsoleta y será removida en una versión futura de la especificación.

+ +
+
{{domxref("ScriptProcessorNode")}} {{deprecated_inline}}
+
La interfaz ScriptProcessorNode permite la generación, procesamiento, o análisis de audio usando JavaScript. Es un módulo de procesamiento de audio {{domxref("AudioNode")}} que está enlazado a dos buffers, uno conteniendo la actual entrada, uno conteniendo la salida. Un evento, implementando la interfaz {{domxref("AudioProcessingEvent")}}, es enviado al objeto cada vez que el buffer de entrada contiene nuevos datos, y el manejador del evento termina cuando ha llenado el buffer de salida con datos.
+
{{event("audioprocess")}} (event) {{deprecated_inline}}
+
El evento audioprocess es lanzado cuando un buffer de entrada de un {{domxref("ScriptProcessorNode")}} del API del Audio Web está listo para ser procesado.
+
{{domxref("AudioProcessingEvent")}} {{deprecated_inline}}
+
El evento AudioProcessingEvent del API de Audio Web representa los eventos que ocurren cuando un buffer de entrada {{domxref("ScriptProcessorNode")}} está listo para ser procesado.
+
+ +

Procesamiento de audio offline/en segundo plano

+ +

Es posible procesar/renderizar un gráfico de muy rápidamente en segundo plano — renderizándolo en un {{domxref("AudioBuffer")}} en lugar de hacerlo a los altavoces del equipo — con lo siguiente.

+ +
+
{{domxref("OfflineAudioContext")}}
+
La interfaz OfflineAudioContext es una interfaz {{domxref("AudioContext")}} representando un gráfico de procesamiento de audio construido a partir de varios {{domxref("AudioNode")}} enlazados juntos. En contraste con un AudioContext estándar, un OfflineAudioContext realmente no procesa el audio sino que lo genera, lo más rápido que puede, en un buffer.
+
{{event("complete")}} (event)
+
El evento complete es lanzado cuando el renderizado de un {{domxref("OfflineAudioContext")}} está terminado.
+
{{domxref("OfflineAudioCompletionEvent")}}
+
La interfaz OfflineAudioCompletionEvent representa los eventos que ocurren cuando procesamiento de un {{domxref("OfflineAudioContext")}} is terminado. El evento {{event("complete")}} implementa esta interfaz.
+
+ +

Interfaces obsoletas

+ +

Las siguientes interfaces fueron definidas en versiones antiguas de la especificación del API de Audio Web, pero ahora están obsoletas y han sido reemplazadas por otras interfaces.

+ +
+
{{domxref("JavaScriptNode")}}
+
Usada para dirigir procesamiento de audio a través de JavaScript. Esta interfaz está obsoleta, y ha sido reemplazada por {{domxref("ScriptProcessorNode")}}.
+
{{domxref("WaveTableNode")}}
+
Usada para definir una forma de onda periórica. Esta interfaz está obsoleta, y ha sido reemplazada por {{domxref("PeriodicWave")}}.
+
+ +

Ejemplo

+ +

Este ejemplo muestra una amplia variedad de funciones del API de Audio Web siendo usadas. Puedes ver este código en acción en la demostración de Voice-change-o-matic (también revisa el código completo en Github) — este es un demo experimental de juguete cambiador de voz; manten tus parlantes en bajo volumen cuando lo uses ¡Al menos al comenzar!

+ +

Las líneas del API de Audio Web están resaltadas; si quieres saber más sobre lo que hacen los diferentes métodos, etc., busca en las páginas de referencia.

+ +
var audioCtx = new (window.AudioContext || window.webkitAudioContext)(); // definir contexto de audio
+// los navegadores Webkit/blink necesitan prefijo, Safari no funcionará sin window.
+
+var voiceSelect = document.getElementById("voice"); // caja de selección para la selección de opciones de efectos de voz
+var visualSelect = document.getElementById("visual"); // caja de selección para la selección de opciones de visualización de audio
+var mute = document.querySelector('.mute'); // botón de silencio
+var drawVisual; // requestAnimationFrame
+
+var analyser = audioCtx.createAnalyser();
+var distortion = audioCtx.createWaveShaper();
+var gainNode = audioCtx.createGain();
+var biquadFilter = audioCtx.createBiquadFilter();
+
+function makeDistortionCurve(amount) { // función para hacer que la forma de curva para distorsión / nodo modificador de onda para usar
+  var k = typeof amount === 'number' ? amount : 50,
+    n_samples = 44100,
+    curve = new Float32Array(n_samples),
+    deg = Math.PI / 180,
+    i = 0,
+    x;
+  for ( ; i < n_samples; ++i ) {
+    x = i * 2 / n_samples - 1;
+    curve[i] = ( 3 + k ) * x * 20 * deg / ( Math.PI + k * Math.abs(x) );
+  }
+  return curve;
+};
+
+navigator.getUserMedia (
+  // restricciones - solo el audio es necesario para esta aplicación
+  {
+    audio: true
+  },
+
+  // Retrollamada de éxito
+  function(stream) {
+    source = audioCtx.createMediaStreamSource(stream);
+    source.connect(analyser);
+    analyser.connect(distortion);
+    distortion.connect(biquadFilter);
+    biquadFilter.connect(gainNode);
+    gainNode.connect(audioCtx.destination); // conectando los diferentes nodos de grafo de audio juntos
+
+    visualize(stream);
+    voiceChange();
+
+  },
+
+  // Retrollamada de error
+  function(err) {
+    console.log('Se produjo el siguiente error gUM: ' + err);
+  }
+);
+
+function visualize(stream) {
+  WIDTH = canvas.width;
+  HEIGHT = canvas.height;
+
+  var visualSetting = visualSelect.value;
+  console.log(visualSetting);
+
+  if(visualSetting == "sinewave") {
+    analyser.fftSize = 2048;
+    var bufferLength = analyser.frequencyBinCount; // la mitad del valor de FFT
+    var dataArray = new Uint8Array(bufferLength); // crear una matriz para almacenar los datos
+
+    canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);
+
+    function draw() {
+
+      drawVisual = requestAnimationFrame(draw);
+
+      analyser.getByteTimeDomainData(dataArray); // obtener datos de forma de onda y ponerlo en la matriz creada arriba
+
+      canvasCtx.fillStyle = 'rgb(200, 200, 200)'; // dibujar onda con canvas
+      canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);
+
+      canvasCtx.lineWidth = 2;
+      canvasCtx.strokeStyle = 'rgb(0, 0, 0)';
+
+      canvasCtx.beginPath();
+
+      var sliceWidth = WIDTH * 1.0 / bufferLength;
+      var x = 0;
+
+      for(var i = 0; i < bufferLength; i++) {
+
+        var v = dataArray[i] / 128.0;
+        var y = v * HEIGHT/2;
+
+        if(i === 0) {
+          canvasCtx.moveTo(x, y);
+        } else {
+          canvasCtx.lineTo(x, y);
+        }
+
+        x += sliceWidth;
+      }
+
+      canvasCtx.lineTo(canvas.width, canvas.height/2);
+      canvasCtx.stroke();
+    };
+
+    draw();
+
+  } else if(visualSetting == "off") {
+    canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);
+    canvasCtx.fillStyle = "red";
+    canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);
+  }
+
+}
+
+function voiceChange() {
+  distortion.curve = new Float32Array;
+  biquadFilter.gain.value = 0; // restablecer los efectos cada vez que se ejecuta la función VoiceChange
+
+  var voiceSetting = voiceSelect.value;
+  console.log(voiceSetting);
+
+  if(voiceSetting == "distortion") {
+    distortion.curve = makeDistortionCurve(400); // aplicar distorsión al sonido usando el nodo waveshaper
+  } else if(voiceSetting == "biquad") {
+    biquadFilter.type = "lowshelf";
+    biquadFilter.frequency.value = 1000;
+    biquadFilter.gain.value = 25; // aplicar el filtro lowshelf a los sonidos usando biquad
+  } else if(voiceSetting == "off") {
+    console.log("Voice settings turned off"); // no hacer nada, ya que se eligió la opción de apagado
+  }
+
+}
+
+// oyentes de eventos para cambiar la visualización y la configuración de voz
+
+visualSelect.onchange = function() {
+  window.cancelAnimationFrame(drawVisual);
+  visualize(stream);
+}
+
+voiceSelect.onchange = function() {
+  voiceChange();
+}
+
+mute.onclick = voiceMute;
+
+function voiceMute() { // alternar para silenciar y activar el sonido
+  if(mute.id == "") {
+    gainNode.gain.value = 0; // ganancia establecida en 0 para silenciar el sonido
+    mute.id = "activated";
+    mute.innerHTML = "Unmute";
+  } else {
+    gainNode.gain.value = 1; // ganancia establecida en 1 para activar el sonido
+    mute.id = "";
+    mute.innerHTML = "Mute";
+  }
+}
+
+ +

Especificaciones

+ + + + + + + + + + + + + + +
EspecificaciónEstadoComentario
{{SpecName('Web Audio API')}}{{Spec2('Web Audio API')}} 
+ +

Compatibilidad en navegadores

+ +
{{CompatibilityTable}}
+ +
+ + + + + + + + + + + + + + + + + + + + + +
FeatureChromeEdgeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
Soporte Básico14 {{property_prefix("webkit")}}{{CompatVersionUnknown}}23{{CompatNo}}15 {{property_prefix("webkit")}}
+ 22 (unprefixed)
6 {{property_prefix("webkit")}}
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
FeatureAndroidChromeEdgeFirefox Mobile (Gecko)Firefox OSIE PhoneOpera MobileSafari Mobile
Soporte Básico{{CompatNo}}28 {{property_prefix("webkit")}}{{CompatVersionUnknown}}251.2{{CompatNo}}{{CompatNo}}6 {{property_prefix("webkit")}}
+
+ +

También ver

+ + + +

Enlaces rápidos

+ +
    +
  1. Guíass + +
      +
    1. Conceptos básicos detrás del API de Audio Web
    2. +
    3. Usando el API de Audio Web
    4. +
    5. Visualizaciones con el API de Audio Web
    6. +
    7. Bases de la espacialización de audio Web
    8. +
    9. Portando código de webkitAudioContext a estándares basados en AudioContext
    10. +
    +
  2. +
  3. Ejemplos +
      +
    1. Teclado sintetizador sencillo
    2. +
    3. Voice-change-O-matic
    4. +
    5. Violent Theremin
    6. +
    +
  4. +
  5. Interfaces +
      +
    1. {{domxref("AnalyserNode")}}
    2. +
    3. {{domxref("AudioBuffer")}}
    4. +
    5. {{domxref("AudioScheduledSourceNode")}}
    6. +
    7. {{domxref("AudioBufferSourceNode")}}
    8. +
    9. {{domxref("AudioContext")}}
    10. +
    11. {{domxref("AudioDestinationNode")}}
    12. +
    13. {{domxref("AudioListener")}}
    14. +
    15. {{domxref("AudioNode")}}
    16. +
    17. {{domxref("AudioParam")}}
    18. +
    19. {{event("audioprocess")}} (event)
    20. +
    21. {{domxref("AudioProcessingEvent")}}
    22. +
    23. {{domxref("BiquadFilterNode")}}
    24. +
    25. {{domxref("ChannelMergerNode")}}
    26. +
    27. {{domxref("ChannelSplitterNode")}}
    28. +
    29. {{event("complete")}} (event)
    30. +
    31. {{domxref("ConvolverNode")}}
    32. +
    33. {{domxref("DelayNode")}}
    34. +
    35. {{domxref("DynamicsCompressorNode")}}
    36. +
    37. {{event("ended_(Web_Audio)", "ended")}} (event)
    38. +
    39. {{domxref("GainNode")}}
    40. +
    41. {{domxref("MediaElementAudioSourceNode")}}
    42. +
    43. {{domxref("MediaStreamAudioDestinationNode")}}
    44. +
    45. {{domxref("MediaStreamAudioSourceNode")}}
    46. +
    47. {{domxref("OfflineAudioCompletionEvent")}}
    48. +
    49. {{domxref("OfflineAudioContext")}}
    50. +
    51. {{domxref("OscillatorNode")}}
    52. +
    53. {{domxref("PannerNode")}}
    54. +
    55. {{domxref("PeriodicWave")}}
    56. +
    57. {{domxref("ScriptProcessorNode")}}
    58. +
    59. {{domxref("WaveShaperNode")}}
    60. +
    +
  6. +
diff --git a/files/es/web/api/web_crypto_api/checking_authenticity_with_password/index.html b/files/es/web/api/web_crypto_api/checking_authenticity_with_password/index.html deleted file mode 100644 index f2e2ef65fd..0000000000 --- a/files/es/web/api/web_crypto_api/checking_authenticity_with_password/index.html +++ /dev/null @@ -1,30 +0,0 @@ ---- -title: Verificando la autenticidad usando contraseña -slug: Web/API/Web_Crypto_API/Checking_authenticity_with_password -translation_of: Web/API/Web_Crypto_API/Checking_authenticity_with_password ---- -

{{APIRef("Web Crypto API")}}{{draft}}

- -

La autenticidad del mensaje puede ser verificada usando el API Crypto Web. En este artículo mostrarémos como crear y controlar una firma digital utilizando una contraseña y una función hash.

- -

Un algoritmo HMAC toma una llave y genera un código digerido de la llave y los datos a firmar. Posteriormente, cualquiera con la llave puede volver a generar el mismo código digerido para verificar si los datos han sido alterados.  Además, mediante el conocimiento de una clave secreta, permite almacenar juntos el código digerido y los datos: un atacante no podrá generar un código digerido sobre datos manipulados si no conoce la clave secreta.

- -

Note that this algorithm doesn't carry any information about the person owning the data, nor its unicity: the mere knowledge of the key is enough to alter the data.

- -

Let's assume that the data is stored on the computer. To access it, both for writing and reading, we will use localforage.js a small library wrapping the different storages capabilities of a browser in the same interface. This library is not an essential component of this use case and is used here for convenience, to keep focused on what really matter, the cryptographic part.

- -

The data we want to access is of the form:

- -

 

- -

where data is the information to guarantee the integrity and signature the information used to verify it.

- -

Cryptographic keys can't be remembered by heart by human, and passwords, or passphrases, make bad, that is unsecure, cryptographic key. To solve this problem, cryptographers have designed algorithms generating cryptographically-sound keys from password. Knowing the password allowed to regenerate the same key and to use it.

- -

We ask the user for a password, and we use it to generate the key:

- -
 
- -

With that key, we will be able to compute the mac of the data.

- -
 
diff --git a/files/es/web/api/web_speech_api/using_the_web_speech_api/index.html b/files/es/web/api/web_speech_api/using_the_web_speech_api/index.html new file mode 100644 index 0000000000..407de0e10d --- /dev/null +++ b/files/es/web/api/web_speech_api/using_the_web_speech_api/index.html @@ -0,0 +1,301 @@ +--- +title: Uso de la Web Speech API +slug: Web/API/Web_Speech_API/Uso_de_la_Web_Speech_API +translation_of: Web/API/Web_Speech_API/Using_the_Web_Speech_API +--- +

La API Web Speech API proporciona dos funcionalidades distintas — reconocimiento de voz, y síntesis de voz (también conocido como texto a voz o tts) — lo que nos ofrece nuevas e interesantes posibilidades para accesibilidad y otros mecanismos. Este artículo ofrece una breve introducción en las dos áreas, así como unas demos.

+ +

Reconocimiento de voz

+ +

El reconocimiento de voz implica recibir voz a través del micrófono de un dispositivo, luego es verificado por un servicio de reconocimiento de voz contra una lista de palabras o frases 'grammar' (esta lista básicamente es el vocabulario a reconocer en una app particular). Cuando se reconoce con éxito una palabra o frase, esta se devuelve como una cadena de texto y así poder iniciar otras acciones.

+ +

La API Web Speech tiene una interfaz principal de control para el - {{domxref ("SpeechRecognition")}} -, además de una serie de interfaces estrechamente relacionadas para representar la gramática, los resultados, etc. Normalmente, el sistema de reconocimiento de voz predeterminado que dispone el dispositivo se utilizará para el reconocimiento de voz: la mayoría de los sistemas operativos modernos tienen un sistema de reconocimiento de voz para emitir comandos de voz. Piense en Dictado en macOS, Siri en iOS, Cortana en Windows 10, Android Speech, etc.

+ +
+

Note: En algunos navegadores como Chrome, el uso del reconocimiento de voz implica el uso de un motor de reconocimiento basado en un servidor. Tu audio se envía a un servicio de reconocimiento web para ser procesado, por lo que no funcionará offline.

+
+ +

Demo

+ +

Para mostrar un uso sencillo del reconocimiento de voz Web, hemos escrito una demo llamada Speech color changer. Cuando se toca o hace click en la pantalla, se puede decir un color HTML, y el color de fondo de la aplicación cambiará a ese color.

+ +

The UI of an app titled Speech Color changer. It invites the user to tap the screen and say a color, and then it turns the background of the app that colour. In this case it has turned the background red.

+ +

Para ejecutar la demo, se puede clonar (o directamente descargar) el repositorio Github donde se encuentra, abrir el fichero index HTML en un navegador de escritorio compatible, o navegar a través del enlace live demo URL en un navegador de móvil compatible como Chrome.

+ +

Compatibilidad de navegadores

+ +

Actualmente, la compatibilidad de la Web Speech API para el reconocimiento de voz se limita a Chrome para escritorio y Android — Chrome tiene soporte desde la versión 33 pero con interfaces 'prefixed', por lo que se deben incluir ese tipo de interfaces 'prefixed', por ejemplo webkitSpeechRecognition.

+ +

HTML y CSS

+ +

El HTML y el CSS para esta app no son importantes. Solo tenemos un título, instrucciones en un párrafo, y un div dentro del cual visualizaremos los mensajes de diagnósticos.

+ +
<h1>Speech color changer</h1>
+<p>Tap/click then say a color to change the background color of the app.</p>
+<div>
+  <p class="output"><em>...diagnostic messages</em></p>
+</div>
+ +

El CSS proporciona un estilo responsive muy simple para que se visualice bien en todos los dispositivos.

+ +

JavaScript

+ +

Miremos el JavaScript con un poco más de detalle.

+ +

Compatibilidad con Chrome 

+ +

Como se ha mencionado anteriormente, Chrome actualmente admite el reconocimiento de voz con propiedades 'prefixed', por lo tanto, al principio de nuestro código incluiremos las siguientes líneas para usar los objetos que correspondan a Chrome, y así cualquier implementación en el futuro pueda admitir estas características sin ningún 'prefixed':

+ +
var SpeechRecognition = SpeechRecognition || webkitSpeechRecognition
+var SpeechGrammarList = SpeechGrammarList || webkitSpeechGrammarList
+var SpeechRecognitionEvent = SpeechRecognitionEvent || webkitSpeechRecognitionEvent
+ +

La gramática -grammar-

+ +

Las siguientes líneas de código definen la gramática que queremos que reconozca nuestra app. Se define una variable para contener nuestra gramática:

+ +
var colors = [ 'aqua' , 'azure' , 'beige', 'bisque', 'black', 'blue', 'brown', 'chocolate', 'coral' ... ];
+var grammar = '#JSGF V1.0; grammar colors; public <color> = ' + colors.join(' | ') + ' ;'
+ +

El formato usado para 'grammar' es JSpeech Grammar Format (JSGF) — Se puede encontrar más sobre las especificaciones de este formato en el anterior enlace. Sin embargo y por ahora vamos a echarle un vistazo rápidamente:

+ + + +

Conectando la gramática a nuestro reconocimiento de voz

+ +

Lo siguiente que tenemos que hacer es definir una instancia de reconocimiento de voz para el control en nuestra aplicación. Esto se hace usando el constructor {{domxref("SpeechRecognition.SpeechRecognition()", "SpeechRecognition()")}}. También creamos una lista de gramática de voz para contener nuestra gramática usando el constructor {{domxref("SpeechGrammarList.SpeechGrammarList()","SpeechGrammarList()")}}.

+ +
var recognition = new SpeechRecognition();
+var speechRecognitionList = new SpeechGrammarList();
+ +

Añadimos nuestra  grammar  a la lista anterior usando el método {{domxref("SpeechGrammarList.addFromString()")}}. Este método acepta como parámetro la cadena que queremos añadir, así como opcionalmente, un valor que especifique la importancia de esta gramática en relación a otras gramáticas disponibles en la lista (el rango del valor va desde 0 hasta 1 inclusive). La gramática agregada está disponible en la lista como una instancia del objeto {{domxref("SpeechGrammar")}}.

+ +
speechRecognitionList.addFromString(grammar, 1);
+ +

Después añadimos la lista {{domxref("SpeechGrammarList")}} a la instancia del reconocimiento de voz estableciéndola en la propiedad {{domxref("SpeechRecognition.grammars")}}. También establecemos algunas otras propiedades a la instancia de reconocimiento antes de continuar:

+ + + +
recognition.grammars = speechRecognitionList;
+recognition.continuous = false;
+recognition.lang = 'en-US';
+recognition.interimResults = false;
+recognition.maxAlternatives = 1;
+ +

Comenzando el reconocimiento de voz

+ +

Tomamos las referencias de la salida {{htmlelement("div")}} y del elemento HTML (para que podamos enviar mensajes de diagnostico y actualizar el color de fondo de la aplicación más adelante), e implementamos un manejador onclick para que cuando se haga click o se toque la pantalla, se inicie el reconocimiento de voz. Esto se logra llamando al método {{domxref("SpeechRecognition.start()")}}. El método forEach() se usa para visualizar indicadores de colores que muestran qué colores intenta decir.

+ +
var diagnostic = document.querySelector('.output');
+var bg = document.querySelector('html');
+var hints = document.querySelector('.hints');
+
+var colorHTML= '';
+colors.forEach(function(v, i, a){
+  console.log(v, i);
+  colorHTML += '<span style="background-color:' + v + ';"> ' + v + ' </span>';
+});
+hints.innerHTML = 'Tap/click then say a color to change the background color of the app. Try ' + colorHTML + '.';
+
+document.body.onclick = function() {
+  recognition.start();
+  console.log('Ready to receive a color command.');
+}
+ +

Recepción y manejo de resultados

+ +

Una vez que comienza el reconocimiento de voz, hay muchos manejadores de eventos que se pueden usar para recuperar los resultados, así como otros elementos de información adicional (ver SpeechRecognition event handlers list.) El más común que probablemente usarás es {{domxref("SpeechRecognition.onresult")}}, el cual es lanzado cuando se recibe el resultado con éxito:

+ +
recognition.onresult = function(event) {
+  var color = event.results[0][0].transcript;
+  diagnostic.textContent = 'Result received: ' + color + '.';
+  bg.style.backgroundColor = color;
+  console.log('Confidence: ' + event.results[0][0].confidence);
+}
+ +

La tercera línea es un poco más compleja, así que vamos a explicarla paso a paso. La propiedad {{domxref("SpeechRecognitionEvent.results")}} devuelve un objeto {{domxref("SpeechRecognitionResultList")}} que contiene los objetos {{domxref("SpeechRecognitionResult")}}. Tiene un getter para que pueda ser accesible como si fuera un array — así el primer [0] devuelve el SpeechRecognitionResult en la posición 0. Cada objeto SpeechRecognitionResult contiene objetos {{domxref("SpeechRecognitionAlternative")}} que contienen palabras individuales reconocidas. Estos también tienen getters para que se puedan acceder a ellos como arrays — por lo tanto el segundo [0] devuelve SpeechRecognitionAlternative en la posición 0. Luego devolvemos su propiedad transcript para obtener una cadena que contenga  el resultado individual reconocido como un string, estableciendo el color de fondo a ese color, e informando del color reconocido como un mensaje de diagnóstico en el IU.

+ +

También usamos el manejador {{domxref("SpeechRecognition.onspeechend")}} para parar el servicio de reconocimiento de voz (usando {{domxref("SpeechRecognition.stop()")}}) cuando se ha reconocido una sola palabra y se ha finalizado de hablar:

+ +
recognition.onspeechend = function() {
+  recognition.stop();
+}
+ +

Manejo de errores y voz no reconocida

+ +

Los dos últimos manejadores son para controlar los casos donde no se reconoce ninguna de las palabras definidas en la gramática, o cuando ocurre un error. {{domxref("SpeechRecognition.onnomatch")}} maneja el primer caso mencionado, pero tenga en cuenta que de momento no parece que se dispare correctamente; solo devuelve lo que ha reconocido:

+ +
recognition.onnomatch = function(event) {
+  diagnostic.textContent = 'I didnt recognise that color.';
+}
+ +

{{domxref("SpeechRecognition.onerror")}} maneja los casos donde se ha producido en error en el reconocimiento  — la propiedad {{domxref("SpeechRecognitionError.error")}} contiene el error devuelto:

+ +
recognition.onerror = function(event) {
+  diagnostic.textContent = 'Error occurred in recognition: ' + event.error;
+}
+ +

Síntesis de voz

+ +

La síntesis de voz (también  conocida como texto a voz o tts) implica recibir  contenido en forma de texto dentro de una aplicación y convertirla en voz a través del altavoz del dispositivo o de la conexión de salida del audio.

+ +

La Web Speech API tiene una interface principal controladora — {{domxref("SpeechSynthesis")}} — además de una serie de interfaces estrechamente relacionadas para representar  el texto que se va a sintetizar (conocido como dictados 'utterances'), voces que se usarán para los dictados, etc. De nuevo, la mayoría de sistemas operativos disponen de algún sistema de síntesis de voz que pueden serán usados por la API si están disponibles.

+ +

Demo

+ +

Para mostrar un uso sencillo de la síntesis de voz Web, tenemos la demo llamada Speak easy synthesis. Esta incluye un juego de controles de formulario para introducir texto a sintetizar, configurar el tono, velocidad de reproducción y la voz a usar cuando el texto sea pronunciado.  Después de introducir el texto se puede presionar Enter/Return para escucharlo.

+ +

UI of an app called speak easy synthesis. It has an input field in which to input text to be synthesised, slider controls to change the rate and pitch of the speech, and a drop down menu to choose between different voices.

+ +

Para ejecutar la demo, se puede clonar (o directamente descargar) el repositorio Github donde se encuentra, abrir el fichero index HTML en un navegador de escritorio compatible, o navegar a través del enlace live demo URL en un navegador de móvil compatible como Chrome.

+ +

Compatibilidad de navegadores

+ +

El soporte para la síntesis de voz Web Speech API solo ha llegado a los navegadores más importantes  y actualmente se limita a los siguientes:

+ + + +

HTML y CSS

+ +

El HTML y CSS de nuevo no son fundamentales, simplemente contiene un titulo, algunas instrucciones de uso y un formulario con algunos controles sencillos. El elemento {{htmlelement("select")}} inicialmente está vacío pero se rellena con {{htmlelement("option")}} mediante JavaScript (ver más adelante.)

+ +
<h1>Speech synthesiser</h1>
+
+<p>Enter some text in the input below and press return to hear it. change voices using the dropdown menu.</p>
+
+<form>
+  <input type="text" class="txt">
+  <div>
+    <label for="rate">Rate</label><input type="range" min="0.5" max="2" value="1" step="0.1" id="rate">
+    <div class="rate-value">1</div>
+    <div class="clearfix"></div>
+  </div>
+  <div>
+    <label for="pitch">Pitch</label><input type="range" min="0" max="2" value="1" step="0.1" id="pitch">
+    <div class="pitch-value">1</div>
+    <div class="clearfix"></div>
+  </div>
+  <select>
+
+  </select>
+</form>
+ +

JavaScript

+ +

Analicemos el JavaScript usado en esta app.

+ +

Configurar variables

+ +

En primer lugar, capturamos las referencias de todos los elementos DOM involucrados en la IU, pero lo más importante es capturar una referencia a  {{domxref("Window.speechSynthesis")}}. Este es el punto de entrada a la API — devuelve una instancia a {{domxref("SpeechSynthesis")}}, la interface controladora para la síntesis de voz en la web.

+ +
var synth = window.speechSynthesis;
+
+var inputForm = document.querySelector('form');
+var inputTxt = document.querySelector('.txt');
+var voiceSelect = document.querySelector('select');
+
+var pitch = document.querySelector('#pitch');
+var pitchValue = document.querySelector('.pitch-value');
+var rate = document.querySelector('#rate');
+var rateValue = document.querySelector('.rate-value');
+
+var voices = [];
+
+ +

Rellenar el elemento select 

+ +

Para rellenar el elemento {{htmlelement("select")}} con las diferentes opciones de voz del que dispone el dispositivo, hemos escrito la función populateVoiceList(). Primero invocamos {{domxref("SpeechSynthesis.getVoices()")}}, que devuelve una lista de todas las voces disponibles representadas por objetos {{domxref("SpeechSynthesisVoice")}}. Después recorremos esa lista — para cada voz creamos un elemento {{htmlelement("option")}}, establecemos su contenido con el nombre de la voz (desde {{domxref("SpeechSynthesisVoice.name")}}), y el lenguaje de la misma (desde {{domxref("SpeechSynthesisVoice.lang")}}), y -- DEFAULT si la voz es la predeterminada para el motor de síntesis (verificando si {{domxref("SpeechSynthesisVoice.default")}} devuelve true.)

+ +

Para cada opción también creamos atributos data-, conteniendo el nombre y lenguaje de la voz asociada, para que más tarde podamos usarlos fácilmente, y después añadimos las opciones 'option' como hijos del select.

+ +
function populateVoiceList() {
+  voices = synth.getVoices();
+
+  for(i = 0; i < voices.length ; i++) {
+    var option = document.createElement('option');
+    option.textContent = voices[i].name + ' (' + voices[i].lang + ')';
+
+    if(voices[i].default) {
+      option.textContent += ' -- DEFAULT';
+    }
+
+    option.setAttribute('data-lang', voices[i].lang);
+    option.setAttribute('data-name', voices[i].name);
+    voiceSelect.appendChild(option);
+  }
+}
+ +

Cuando vamos a ejecutar la función hacemos lo siguiente debido a que Firefox no soporta {{domxref("SpeechSynthesis.onvoiceschanged")}}, y sólo devolverá una lista de voces cuando se ejecute {{domxref("SpeechSynthesis.getVoices()")}}. Sin embargo con Chrome solo hay que esperar a que se active el evento antes de rellenar la lista, de ahí la siguiente parte de código.

+ +
populateVoiceList();
+if (speechSynthesis.onvoiceschanged !== undefined) {
+  speechSynthesis.onvoiceschanged = populateVoiceList;
+}
+ +

Reproduciendo el texto introducido

+ +

A continuación, creamos un manejador de eventos para comenzar a reproducir el texto introducido en el campo de texto. Usamos un manejador onsubmit en el formulario para que la acción ocurra cuando se presione Enter/Return. Primero creamos una nueva instancia de  {{domxref("SpeechSynthesisUtterance.SpeechSynthesisUtterance()", "SpeechSynthesisUtterance()")}} usando su constructor — a este se le pasa el valor de la entrada de texto como parámetro.

+ +

A continuación, debemos obtener la voz que queremos utilizar. Usamos la propiedad {{domxref("HTMLSelectElement")}} selectedOptions para devolver el elemento seleccionado {{htmlelement("option")}}. Entonces usamos el atributo de este elemento data-name, encontrando el objeto {{domxref("SpeechSynthesisVoice")}} cuyo nombre coincida con el valor del atributo. Y configuramos la propiedad de {{domxref("SpeechSynthesisUtterance.voice")}} con el valor que coincida con la opción seleccionada.

+ +

Por último, configuramos {{domxref("SpeechSynthesisUtterance.pitch")}} y {{domxref("SpeechSynthesisUtterance.rate")}} con los valores de los elementos del formulario correspondientes. Entonces y ya configurado todo lo necesario, comenzamos la reproducción invocando  {{domxref("SpeechSynthesis.speak()")}}, y pasándolo a la instancia {{domxref("SpeechSynthesisUtterance")}} como parámetro.

+ +
inputForm.onsubmit = function(event) {
+  event.preventDefault();
+
+  var utterThis = new SpeechSynthesisUtterance(inputTxt.value);
+  var selectedOption = voiceSelect.selectedOptions[0].getAttribute('data-name');
+  for(i = 0; i < voices.length ; i++) {
+    if(voices[i].name === selectedOption) {
+      utterThis.voice = voices[i];
+    }
+  }
+  utterThis.pitch = pitch.value;
+  utterThis.rate = rate.value;
+  synth.speak(utterThis);
+ +

Al final del manejador incluimos un manejador {{domxref("SpeechSynthesisUtterance.onpause")}} para mostrar cómo usar {{domxref("SpeechSynthesisEvent")}}. Cuando se invoca a  {{domxref("SpeechSynthesis.pause()")}},este devuelve un mensaje informando del número y nombre del carácter en el que se detuvo la reproducción.

+ +
   utterThis.onpause = function(event) {
+    var char = event.utterance.text.charAt(event.charIndex);
+    console.log('Speech paused at character ' + event.charIndex + ' of "' +
+    event.utterance.text + '", which is "' + char + '".');
+  }
+ +

Por último, llamamos a blur() en la entrada de texto. Esto permite ocultar la entrada de texto en Firefox OS.

+ +
  inputTxt.blur();
+}
+ +

Actualizando los valores de tono y velocidad mostrados

+ +

La última parte del código simplemente actualiza los valores pitch/rate mostrados en la IU, cada vez que el slider cambia de posición.

+ +
pitch.onchange = function() {
+  pitchValue.textContent = pitch.value;
+}
+
+rate.onchange = function() {
+  rateValue.textContent = rate.value;
+}
diff --git a/files/es/web/api/web_speech_api/uso_de_la_web_speech_api/index.html b/files/es/web/api/web_speech_api/uso_de_la_web_speech_api/index.html deleted file mode 100644 index 407de0e10d..0000000000 --- a/files/es/web/api/web_speech_api/uso_de_la_web_speech_api/index.html +++ /dev/null @@ -1,301 +0,0 @@ ---- -title: Uso de la Web Speech API -slug: Web/API/Web_Speech_API/Uso_de_la_Web_Speech_API -translation_of: Web/API/Web_Speech_API/Using_the_Web_Speech_API ---- -

La API Web Speech API proporciona dos funcionalidades distintas — reconocimiento de voz, y síntesis de voz (también conocido como texto a voz o tts) — lo que nos ofrece nuevas e interesantes posibilidades para accesibilidad y otros mecanismos. Este artículo ofrece una breve introducción en las dos áreas, así como unas demos.

- -

Reconocimiento de voz

- -

El reconocimiento de voz implica recibir voz a través del micrófono de un dispositivo, luego es verificado por un servicio de reconocimiento de voz contra una lista de palabras o frases 'grammar' (esta lista básicamente es el vocabulario a reconocer en una app particular). Cuando se reconoce con éxito una palabra o frase, esta se devuelve como una cadena de texto y así poder iniciar otras acciones.

- -

La API Web Speech tiene una interfaz principal de control para el - {{domxref ("SpeechRecognition")}} -, además de una serie de interfaces estrechamente relacionadas para representar la gramática, los resultados, etc. Normalmente, el sistema de reconocimiento de voz predeterminado que dispone el dispositivo se utilizará para el reconocimiento de voz: la mayoría de los sistemas operativos modernos tienen un sistema de reconocimiento de voz para emitir comandos de voz. Piense en Dictado en macOS, Siri en iOS, Cortana en Windows 10, Android Speech, etc.

- -
-

Note: En algunos navegadores como Chrome, el uso del reconocimiento de voz implica el uso de un motor de reconocimiento basado en un servidor. Tu audio se envía a un servicio de reconocimiento web para ser procesado, por lo que no funcionará offline.

-
- -

Demo

- -

Para mostrar un uso sencillo del reconocimiento de voz Web, hemos escrito una demo llamada Speech color changer. Cuando se toca o hace click en la pantalla, se puede decir un color HTML, y el color de fondo de la aplicación cambiará a ese color.

- -

The UI of an app titled Speech Color changer. It invites the user to tap the screen and say a color, and then it turns the background of the app that colour. In this case it has turned the background red.

- -

Para ejecutar la demo, se puede clonar (o directamente descargar) el repositorio Github donde se encuentra, abrir el fichero index HTML en un navegador de escritorio compatible, o navegar a través del enlace live demo URL en un navegador de móvil compatible como Chrome.

- -

Compatibilidad de navegadores

- -

Actualmente, la compatibilidad de la Web Speech API para el reconocimiento de voz se limita a Chrome para escritorio y Android — Chrome tiene soporte desde la versión 33 pero con interfaces 'prefixed', por lo que se deben incluir ese tipo de interfaces 'prefixed', por ejemplo webkitSpeechRecognition.

- -

HTML y CSS

- -

El HTML y el CSS para esta app no son importantes. Solo tenemos un título, instrucciones en un párrafo, y un div dentro del cual visualizaremos los mensajes de diagnósticos.

- -
<h1>Speech color changer</h1>
-<p>Tap/click then say a color to change the background color of the app.</p>
-<div>
-  <p class="output"><em>...diagnostic messages</em></p>
-</div>
- -

El CSS proporciona un estilo responsive muy simple para que se visualice bien en todos los dispositivos.

- -

JavaScript

- -

Miremos el JavaScript con un poco más de detalle.

- -

Compatibilidad con Chrome 

- -

Como se ha mencionado anteriormente, Chrome actualmente admite el reconocimiento de voz con propiedades 'prefixed', por lo tanto, al principio de nuestro código incluiremos las siguientes líneas para usar los objetos que correspondan a Chrome, y así cualquier implementación en el futuro pueda admitir estas características sin ningún 'prefixed':

- -
var SpeechRecognition = SpeechRecognition || webkitSpeechRecognition
-var SpeechGrammarList = SpeechGrammarList || webkitSpeechGrammarList
-var SpeechRecognitionEvent = SpeechRecognitionEvent || webkitSpeechRecognitionEvent
- -

La gramática -grammar-

- -

Las siguientes líneas de código definen la gramática que queremos que reconozca nuestra app. Se define una variable para contener nuestra gramática:

- -
var colors = [ 'aqua' , 'azure' , 'beige', 'bisque', 'black', 'blue', 'brown', 'chocolate', 'coral' ... ];
-var grammar = '#JSGF V1.0; grammar colors; public <color> = ' + colors.join(' | ') + ' ;'
- -

El formato usado para 'grammar' es JSpeech Grammar Format (JSGF) — Se puede encontrar más sobre las especificaciones de este formato en el anterior enlace. Sin embargo y por ahora vamos a echarle un vistazo rápidamente:

- - - -

Conectando la gramática a nuestro reconocimiento de voz

- -

Lo siguiente que tenemos que hacer es definir una instancia de reconocimiento de voz para el control en nuestra aplicación. Esto se hace usando el constructor {{domxref("SpeechRecognition.SpeechRecognition()", "SpeechRecognition()")}}. También creamos una lista de gramática de voz para contener nuestra gramática usando el constructor {{domxref("SpeechGrammarList.SpeechGrammarList()","SpeechGrammarList()")}}.

- -
var recognition = new SpeechRecognition();
-var speechRecognitionList = new SpeechGrammarList();
- -

Añadimos nuestra  grammar  a la lista anterior usando el método {{domxref("SpeechGrammarList.addFromString()")}}. Este método acepta como parámetro la cadena que queremos añadir, así como opcionalmente, un valor que especifique la importancia de esta gramática en relación a otras gramáticas disponibles en la lista (el rango del valor va desde 0 hasta 1 inclusive). La gramática agregada está disponible en la lista como una instancia del objeto {{domxref("SpeechGrammar")}}.

- -
speechRecognitionList.addFromString(grammar, 1);
- -

Después añadimos la lista {{domxref("SpeechGrammarList")}} a la instancia del reconocimiento de voz estableciéndola en la propiedad {{domxref("SpeechRecognition.grammars")}}. También establecemos algunas otras propiedades a la instancia de reconocimiento antes de continuar:

- - - -
recognition.grammars = speechRecognitionList;
-recognition.continuous = false;
-recognition.lang = 'en-US';
-recognition.interimResults = false;
-recognition.maxAlternatives = 1;
- -

Comenzando el reconocimiento de voz

- -

Tomamos las referencias de la salida {{htmlelement("div")}} y del elemento HTML (para que podamos enviar mensajes de diagnostico y actualizar el color de fondo de la aplicación más adelante), e implementamos un manejador onclick para que cuando se haga click o se toque la pantalla, se inicie el reconocimiento de voz. Esto se logra llamando al método {{domxref("SpeechRecognition.start()")}}. El método forEach() se usa para visualizar indicadores de colores que muestran qué colores intenta decir.

- -
var diagnostic = document.querySelector('.output');
-var bg = document.querySelector('html');
-var hints = document.querySelector('.hints');
-
-var colorHTML= '';
-colors.forEach(function(v, i, a){
-  console.log(v, i);
-  colorHTML += '<span style="background-color:' + v + ';"> ' + v + ' </span>';
-});
-hints.innerHTML = 'Tap/click then say a color to change the background color of the app. Try ' + colorHTML + '.';
-
-document.body.onclick = function() {
-  recognition.start();
-  console.log('Ready to receive a color command.');
-}
- -

Recepción y manejo de resultados

- -

Una vez que comienza el reconocimiento de voz, hay muchos manejadores de eventos que se pueden usar para recuperar los resultados, así como otros elementos de información adicional (ver SpeechRecognition event handlers list.) El más común que probablemente usarás es {{domxref("SpeechRecognition.onresult")}}, el cual es lanzado cuando se recibe el resultado con éxito:

- -
recognition.onresult = function(event) {
-  var color = event.results[0][0].transcript;
-  diagnostic.textContent = 'Result received: ' + color + '.';
-  bg.style.backgroundColor = color;
-  console.log('Confidence: ' + event.results[0][0].confidence);
-}
- -

La tercera línea es un poco más compleja, así que vamos a explicarla paso a paso. La propiedad {{domxref("SpeechRecognitionEvent.results")}} devuelve un objeto {{domxref("SpeechRecognitionResultList")}} que contiene los objetos {{domxref("SpeechRecognitionResult")}}. Tiene un getter para que pueda ser accesible como si fuera un array — así el primer [0] devuelve el SpeechRecognitionResult en la posición 0. Cada objeto SpeechRecognitionResult contiene objetos {{domxref("SpeechRecognitionAlternative")}} que contienen palabras individuales reconocidas. Estos también tienen getters para que se puedan acceder a ellos como arrays — por lo tanto el segundo [0] devuelve SpeechRecognitionAlternative en la posición 0. Luego devolvemos su propiedad transcript para obtener una cadena que contenga  el resultado individual reconocido como un string, estableciendo el color de fondo a ese color, e informando del color reconocido como un mensaje de diagnóstico en el IU.

- -

También usamos el manejador {{domxref("SpeechRecognition.onspeechend")}} para parar el servicio de reconocimiento de voz (usando {{domxref("SpeechRecognition.stop()")}}) cuando se ha reconocido una sola palabra y se ha finalizado de hablar:

- -
recognition.onspeechend = function() {
-  recognition.stop();
-}
- -

Manejo de errores y voz no reconocida

- -

Los dos últimos manejadores son para controlar los casos donde no se reconoce ninguna de las palabras definidas en la gramática, o cuando ocurre un error. {{domxref("SpeechRecognition.onnomatch")}} maneja el primer caso mencionado, pero tenga en cuenta que de momento no parece que se dispare correctamente; solo devuelve lo que ha reconocido:

- -
recognition.onnomatch = function(event) {
-  diagnostic.textContent = 'I didnt recognise that color.';
-}
- -

{{domxref("SpeechRecognition.onerror")}} maneja los casos donde se ha producido en error en el reconocimiento  — la propiedad {{domxref("SpeechRecognitionError.error")}} contiene el error devuelto:

- -
recognition.onerror = function(event) {
-  diagnostic.textContent = 'Error occurred in recognition: ' + event.error;
-}
- -

Síntesis de voz

- -

La síntesis de voz (también  conocida como texto a voz o tts) implica recibir  contenido en forma de texto dentro de una aplicación y convertirla en voz a través del altavoz del dispositivo o de la conexión de salida del audio.

- -

La Web Speech API tiene una interface principal controladora — {{domxref("SpeechSynthesis")}} — además de una serie de interfaces estrechamente relacionadas para representar  el texto que se va a sintetizar (conocido como dictados 'utterances'), voces que se usarán para los dictados, etc. De nuevo, la mayoría de sistemas operativos disponen de algún sistema de síntesis de voz que pueden serán usados por la API si están disponibles.

- -

Demo

- -

Para mostrar un uso sencillo de la síntesis de voz Web, tenemos la demo llamada Speak easy synthesis. Esta incluye un juego de controles de formulario para introducir texto a sintetizar, configurar el tono, velocidad de reproducción y la voz a usar cuando el texto sea pronunciado.  Después de introducir el texto se puede presionar Enter/Return para escucharlo.

- -

UI of an app called speak easy synthesis. It has an input field in which to input text to be synthesised, slider controls to change the rate and pitch of the speech, and a drop down menu to choose between different voices.

- -

Para ejecutar la demo, se puede clonar (o directamente descargar) el repositorio Github donde se encuentra, abrir el fichero index HTML en un navegador de escritorio compatible, o navegar a través del enlace live demo URL en un navegador de móvil compatible como Chrome.

- -

Compatibilidad de navegadores

- -

El soporte para la síntesis de voz Web Speech API solo ha llegado a los navegadores más importantes  y actualmente se limita a los siguientes:

- - - -

HTML y CSS

- -

El HTML y CSS de nuevo no son fundamentales, simplemente contiene un titulo, algunas instrucciones de uso y un formulario con algunos controles sencillos. El elemento {{htmlelement("select")}} inicialmente está vacío pero se rellena con {{htmlelement("option")}} mediante JavaScript (ver más adelante.)

- -
<h1>Speech synthesiser</h1>
-
-<p>Enter some text in the input below and press return to hear it. change voices using the dropdown menu.</p>
-
-<form>
-  <input type="text" class="txt">
-  <div>
-    <label for="rate">Rate</label><input type="range" min="0.5" max="2" value="1" step="0.1" id="rate">
-    <div class="rate-value">1</div>
-    <div class="clearfix"></div>
-  </div>
-  <div>
-    <label for="pitch">Pitch</label><input type="range" min="0" max="2" value="1" step="0.1" id="pitch">
-    <div class="pitch-value">1</div>
-    <div class="clearfix"></div>
-  </div>
-  <select>
-
-  </select>
-</form>
- -

JavaScript

- -

Analicemos el JavaScript usado en esta app.

- -

Configurar variables

- -

En primer lugar, capturamos las referencias de todos los elementos DOM involucrados en la IU, pero lo más importante es capturar una referencia a  {{domxref("Window.speechSynthesis")}}. Este es el punto de entrada a la API — devuelve una instancia a {{domxref("SpeechSynthesis")}}, la interface controladora para la síntesis de voz en la web.

- -
var synth = window.speechSynthesis;
-
-var inputForm = document.querySelector('form');
-var inputTxt = document.querySelector('.txt');
-var voiceSelect = document.querySelector('select');
-
-var pitch = document.querySelector('#pitch');
-var pitchValue = document.querySelector('.pitch-value');
-var rate = document.querySelector('#rate');
-var rateValue = document.querySelector('.rate-value');
-
-var voices = [];
-
- -

Rellenar el elemento select 

- -

Para rellenar el elemento {{htmlelement("select")}} con las diferentes opciones de voz del que dispone el dispositivo, hemos escrito la función populateVoiceList(). Primero invocamos {{domxref("SpeechSynthesis.getVoices()")}}, que devuelve una lista de todas las voces disponibles representadas por objetos {{domxref("SpeechSynthesisVoice")}}. Después recorremos esa lista — para cada voz creamos un elemento {{htmlelement("option")}}, establecemos su contenido con el nombre de la voz (desde {{domxref("SpeechSynthesisVoice.name")}}), y el lenguaje de la misma (desde {{domxref("SpeechSynthesisVoice.lang")}}), y -- DEFAULT si la voz es la predeterminada para el motor de síntesis (verificando si {{domxref("SpeechSynthesisVoice.default")}} devuelve true.)

- -

Para cada opción también creamos atributos data-, conteniendo el nombre y lenguaje de la voz asociada, para que más tarde podamos usarlos fácilmente, y después añadimos las opciones 'option' como hijos del select.

- -
function populateVoiceList() {
-  voices = synth.getVoices();
-
-  for(i = 0; i < voices.length ; i++) {
-    var option = document.createElement('option');
-    option.textContent = voices[i].name + ' (' + voices[i].lang + ')';
-
-    if(voices[i].default) {
-      option.textContent += ' -- DEFAULT';
-    }
-
-    option.setAttribute('data-lang', voices[i].lang);
-    option.setAttribute('data-name', voices[i].name);
-    voiceSelect.appendChild(option);
-  }
-}
- -

Cuando vamos a ejecutar la función hacemos lo siguiente debido a que Firefox no soporta {{domxref("SpeechSynthesis.onvoiceschanged")}}, y sólo devolverá una lista de voces cuando se ejecute {{domxref("SpeechSynthesis.getVoices()")}}. Sin embargo con Chrome solo hay que esperar a que se active el evento antes de rellenar la lista, de ahí la siguiente parte de código.

- -
populateVoiceList();
-if (speechSynthesis.onvoiceschanged !== undefined) {
-  speechSynthesis.onvoiceschanged = populateVoiceList;
-}
- -

Reproduciendo el texto introducido

- -

A continuación, creamos un manejador de eventos para comenzar a reproducir el texto introducido en el campo de texto. Usamos un manejador onsubmit en el formulario para que la acción ocurra cuando se presione Enter/Return. Primero creamos una nueva instancia de  {{domxref("SpeechSynthesisUtterance.SpeechSynthesisUtterance()", "SpeechSynthesisUtterance()")}} usando su constructor — a este se le pasa el valor de la entrada de texto como parámetro.

- -

A continuación, debemos obtener la voz que queremos utilizar. Usamos la propiedad {{domxref("HTMLSelectElement")}} selectedOptions para devolver el elemento seleccionado {{htmlelement("option")}}. Entonces usamos el atributo de este elemento data-name, encontrando el objeto {{domxref("SpeechSynthesisVoice")}} cuyo nombre coincida con el valor del atributo. Y configuramos la propiedad de {{domxref("SpeechSynthesisUtterance.voice")}} con el valor que coincida con la opción seleccionada.

- -

Por último, configuramos {{domxref("SpeechSynthesisUtterance.pitch")}} y {{domxref("SpeechSynthesisUtterance.rate")}} con los valores de los elementos del formulario correspondientes. Entonces y ya configurado todo lo necesario, comenzamos la reproducción invocando  {{domxref("SpeechSynthesis.speak()")}}, y pasándolo a la instancia {{domxref("SpeechSynthesisUtterance")}} como parámetro.

- -
inputForm.onsubmit = function(event) {
-  event.preventDefault();
-
-  var utterThis = new SpeechSynthesisUtterance(inputTxt.value);
-  var selectedOption = voiceSelect.selectedOptions[0].getAttribute('data-name');
-  for(i = 0; i < voices.length ; i++) {
-    if(voices[i].name === selectedOption) {
-      utterThis.voice = voices[i];
-    }
-  }
-  utterThis.pitch = pitch.value;
-  utterThis.rate = rate.value;
-  synth.speak(utterThis);
- -

Al final del manejador incluimos un manejador {{domxref("SpeechSynthesisUtterance.onpause")}} para mostrar cómo usar {{domxref("SpeechSynthesisEvent")}}. Cuando se invoca a  {{domxref("SpeechSynthesis.pause()")}},este devuelve un mensaje informando del número y nombre del carácter en el que se detuvo la reproducción.

- -
   utterThis.onpause = function(event) {
-    var char = event.utterance.text.charAt(event.charIndex);
-    console.log('Speech paused at character ' + event.charIndex + ' of "' +
-    event.utterance.text + '", which is "' + char + '".');
-  }
- -

Por último, llamamos a blur() en la entrada de texto. Esto permite ocultar la entrada de texto en Firefox OS.

- -
  inputTxt.blur();
-}
- -

Actualizando los valores de tono y velocidad mostrados

- -

La última parte del código simplemente actualiza los valores pitch/rate mostrados en la IU, cada vez que el slider cambia de posición.

- -
pitch.onchange = function() {
-  pitchValue.textContent = pitch.value;
-}
-
-rate.onchange = function() {
-  rateValue.textContent = rate.value;
-}
diff --git a/files/es/web/api/web_storage_api/index.html b/files/es/web/api/web_storage_api/index.html new file mode 100644 index 0000000000..26a858d254 --- /dev/null +++ b/files/es/web/api/web_storage_api/index.html @@ -0,0 +1,146 @@ +--- +title: API de almacenamiento web +slug: Web/API/API_de_almacenamiento_web +tags: + - API + - API de almacenamiento web + - Almacenamiento web + - Storage + - localStorage + - sessionStorage +translation_of: Web/API/Web_Storage_API +--- +

{{DefaultAPISidebar("Web Storage API")}}

+ +

La API de almacenamiento web proporciona los mecanismos mediante los cuales el navegador puede almacenar información de tipo clave/valor, de una forma mucho más intuitiva que utilizando cookies.

+ +

Almacenamiento web, conceptos y uso

+ +

Los dos mecanismos en el almacenamiento web son los siguientes:

+ + + +

Estos mecanismos están disponibles mediante las propiedades Window.sessionStorage y  Window.localStorage (dicho con más precisión, en navegadores con soporte, el objeto Window implementa los objetos  WindowLocalStorage y WindowSessionStorage, en los cuales se basan las propiedades localStorage y sessionStorage). Al invocar uno de éstos, se creará una instancia del objeto Storage, a través del cual los datos pueden ser creados, recuperados y eliminados. sessionStorage y localStorage utilizan un objeto de almacenamiento diferente según su origen — funcionan y son controlados por separado.

+ +
+

Nota: Acceder al Almacenamiento web desde IFrames de terceros está prohibido si el usuario tiene deshabilitadas las cookies de terceros (Firefox implementa este comportamiento a partir de la versión 43).

+
+ +
+

Nota: El almacenamiento web no es lo mismo que mozStorage (interfaces Mozilla's XPCOM para SQLite) o la Session store API (una utilidad de almacenamiento XPCOM usada por extensiones).

+
+ +

Interfaces de almacenamiento web

+ +
+
{{domxref("Storage")}}
+
Permite crear, recuperar y eliminar datos de un dominio y tipo de almacenamiento (sesión o local) específicos.
+
{{domxref("Window")}}
+
La API de almacenamiento web extiende el objeto {{domxref("Window")}} con dos nuevas propiedades — {{domxref("Window.sessionStorage")}} y {{domxref("Window.localStorage")}} — que proveen acceso a la sesión actual del dominio y a objetos {{domxref("Storage")}} locales, respectivamente. También ofrece un manejador de evento {{domxref("Window.onstorage")}} que se dispara cuando un área de la memoria cambia (por ejemplo, cuando se almacena un nuevo elemento).
+
{{domxref("StorageEvent")}}
+
El evento storage se dispara en el objeto Window de un documento cuando un área de la memoria cambia.
+
+ +

Ejemplos

+ +

Para ilustrar algunos usos típicos del almacenamiento web, hemos creado un ejemplo simple, llamado Demo de almacenamiento web. La página de inicio proporciona controles que puedes utilizar para personalizar el color, la tipografía y la imagen decorativa. Cuando seleccionas una opción diferente, la página se actualiza instantáneamente; además, tus opciones se almacenan en localStorage, de forma que si abandonas la página y la vuelves a cargar, tus opciones son recordadas.

+ +

También creamos una página de salida del evento — si cargas esta página en otra pestaña y luego haces cambios a tus opciones en la página de inicio, verás que se muestra la información almacenada actualizada puesto que se dispara un evento {{event("StorageEvent")}}.

+ +

Especificaciones

+ + + + + + + + + + + + + + +
EspecificaciónEstadoComentario
{{SpecName('Web Storage')}}{{Spec2('Web Storage')}} 
+ +

Compatibilidad de navegadores

+ +

{{ CompatibilityTable() }}

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
CaracterísticaChromeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
localStorage43.5810.504
sessionStorage52810.504
+
+ +
+ + + + + + + + + + + + + + + + + + + +
CaracteristicaAndroidFirefox Mobile (Gecko)IE PhoneOpera MobileSafari Mobile
Basic support2.1{{ CompatUnknown }}811iOS 3.2
+
+ +

Todos los navegadores tienen distintos niveles de capacidad tanto para localStorage como para sessionStorage. Aquí está una análisis detallado de todas las capacidades de almacenamiento de diferentes navegadores.

+ +
+

Nota: Desde iOS 5.1, Safari Mobile almacena los datos de localStorage en la carpeta de caché, la cual está sujeta a limpiezas ocasionales, a petición del sistema operativo, típicamente cuando el espacio es reducido.

+
+ + + +

La mayoría de los navegadores de hoy en día soportan una opción de privacidad llamada  'Modo incógnito', 'Navegación privada', o algo similar, que básicamente se asegura de que la sesión de navegación no deje rastros después de que el navegador se cierra. Esto es fundamentalmente incompatible con el almacenamiento web por obvias razones. Por ello, muchos navegadores están experimentando con diferentes escenarios para lidiar con esta incompatibilidad.

+ +

La mayoría de los navegadores han optado por una estrategia donde las API de almacenamiento siguen disponibles y aparentemente completamente funcionales, con la única gran diferencia de que todos los datos almacenados son eliminados después de cerrar el navegador. Para estos navegadores aún hay diferentes interpretaciones sobre qué debería hacerse con los datos almacenados existentes (de una sesión de navegación normal). ¿Deberían de estar disponibles para lectura cuando esté en modo privado? Entonces, hay algunos navegadores, sobre todo Safari, que han optado por una solución donde el almacenamiento está disponible, pero vacío, y tiene un cupo de 0 bytes asignado, por lo que se vuelve imposible usar esta memoria para escribir datos.

+ +

Los desarrolladores deberían de estar conscientes de estas diferentes implementaciones y tenerlas en cuenta a la hora de desarrollar aplicaciones web que depende de la API de almacenamiento web. Para más información, échale un vistazo a esta entrada de blog WHATWG que trata específicamente con este tema.

+ +

Ver también

+ + diff --git a/files/es/web/api/web_storage_api/using_the_web_storage_api/index.html b/files/es/web/api/web_storage_api/using_the_web_storage_api/index.html new file mode 100644 index 0000000000..086af18610 --- /dev/null +++ b/files/es/web/api/web_storage_api/using_the_web_storage_api/index.html @@ -0,0 +1,272 @@ +--- +title: Usando la API de almacenamiento web +slug: Web/API/API_de_almacenamiento_web/Usando_la_API_de_almacenamiento_web +tags: + - API + - API de almacenamiento web + - Guía + - localStorage + - sessionStorage +translation_of: Web/API/Web_Storage_API/Using_the_Web_Storage_API +--- +
{{DefaultAPISidebar("Web Storage API")}}
+ +
+

La API de almacenamiento web proporciona los mecanismos mediante los cuales el navegador puede almacenar información de tipo clave/valor, de una forma mucho más intuitiva que utilizando cookies.

+ +

Este artículo proporciona una guía general de cómo usar esta tecnología.

+
+ +

Conceptos básicos

+ +

Los objetos de almacenamiento son simples almacenes de clave/valor, similares a objetos, pero que permanecen intactos cuando la página se recarga. Las claves y los valores siempre son cadenas de texto (fíjate que las claves con enteros se convierten automáticamente a cadenas, tal y como lo hacen los objetos). Puedes acceder a estos valores como un objeto, o con los métodos {{domxref("Storage.getItem()")}} y {{domxref("Storage.setItem()")}}. Estas tres líneas modifican el valor de colorSetting de la misma manera:

+ +
localStorage.colorSetting = '#a4509b';
+localStorage['colorSetting'] = '#a4509b';
+localStorage.setItem('colorSetting', '#a4509b');
+ +
+

Nota: Se recomiendo usar la API de almacenamiento web (setItem, getItem, removeItem, key, length) para prevenir las dificultades asociadas al uso de simples objetos como almacenes de valores llave/valor.

+
+ +

Los dos mecanismos en el almacenamiento web son los siguientes:

+ + + +

Estos mecanismos están disponibles mediante las propiedades Window.sessionStorage y  Window.localStorage (dicho con más precisión, en navegadores con soporte, el objeto Window implementa los objetos  WindowLocalStorage y WindowSessionStorage, en los cuales se basan las propiedades localStorage y sessionStorage). Al invocar uno de éstos, se creará una instancia del objeto Storage, a través del cual los datos pueden ser creados, recuperados y eliminados. sessionStorage y localStorage utilizan un objeto de almacenamiento diferente según su origen — funcionan y son controlados por separado.

+ +

Así que, por ejemplo, si en un inicio se llama a localStorage en un documento, esto devolverá un objeto {{domxref("Storage")}}; llamar a sessionStorage en un documento devolverá un objeto {{domxref("Storage")}} diferente. Ambos objetos se pueden manipular de la misma forma, pero separados.

+ +

Detectar la característica localStorage

+ +

Para poder usar localStorage, debemos de verificar que tiene soporte y que está disponible en la sesión del buscador actual.

+ +

Probar la disponibilidad

+ +
+

Nota: esta API está disponible en las versiones actuales de todos los navegadores principales. La prueba de disponibilidad es necesaria sólo si debes soportar navegadores muy antiguos, como Internet Explorer 6 o 7, o en las circunstancias limitadas descritas más abajo.

+
+ +

Los buscadores que soportan localStorage tienen una propiedad en el objeto window que se llama localStorage. Sin embargo, por varias razones, el sólo asegurarse de que la propiedad existe puede arrojar excepciones. El que localStorage exista no es garantía suficiente de que en verdad esté disponible, puesto que varios buscadores ofrecen configuraciones que lo inhabilitan. Así que un buscador puede soportar localStorage, pero puede no hacerlo disponible para los scripts en la página. Un ejemplo de esto es Safari, que en el modo de búsqueda privada ofrece un objeto localStorage vacío con un cupo de 0, por lo que es inutilizable. Sin embargo, es posible que aún así obtengamos un QuotaExceededError legítimo, lo que significa que ya usamos todo el espacio de almacenamiento disponible, aunque el almacenamiento esté, de hecho, disponible. Nuestra detección de la característica debe de tomar en cuenta estos escenarios.

+ +

Esta función detecta si localStorage tiene soporte y está disponible:

+ +
function storageAvailable(type) {
+    try {
+        var storage = window[type],
+            x = '__storage_test__';
+        storage.setItem(x, x);
+        storage.removeItem(x);
+        return true;
+    }
+    catch(e) {
+        return e instanceof DOMException && (
+            // everything except Firefox
+            e.code === 22 ||
+            // Firefox
+            e.code === 1014 ||
+            // test name field too, because code might not be present
+            // everything except Firefox
+            e.name === 'QuotaExceededError' ||
+            // Firefox
+            e.name === 'NS_ERROR_DOM_QUOTA_REACHED') &&
+            // acknowledge QuotaExceededError only if there's something already stored
+            storage.length !== 0;
+    }
+}
+ +

Y aquí se muestra cómo usarla:

+ +
if (storageAvailable('localStorage')) {
+  // Yippee! We can use localStorage awesomeness
+}
+else {
+  // Too bad, no localStorage for us
+}
+ +

También puedes probar sessionStorage invocando storageAvailable('sessionStorage').

+ +

Aquí puedes ver una breve historia de la detección de la característica localStorage.

+ +

Ejemplo

+ +

Para ilustrar un uso típico de almacenamiento web, creamos un ejemplo simple que llamamos Demo de almacenamiento web. La página de inicio proporciona unos controles que se pueden usar para personalizar el color, la tipografía y la imagen decorativa:

+ +

Cuando seleccionas una opción diferente, la página se actualiza instantáneamente; además, tus opciones se almacenan en localStorage, de forma que si abandonas la página y la vuelves a cargar, tus opciones son recordadas.

+ +

También creamos una página de salida del evento — si cargas esta página en otra pestaña y luego haces cambios a tus opciones en la página de inicio, verás que se muestra la información almacenada actualizada puesto que se dispara un {{domxref("StorageEvent")}}.

+ +

+ +
+

Nota: Puedes ver las páginas de ejemplo usando los links de arriba y también puedes ver el código fuente.

+
+ +

Probar si la memoria tiene valores

+ +

En el inicio de main.js, probamos si el objeto ya tiene valores (es decir, si la página ya fue visitada):

+ +
if(!localStorage.getItem('bgcolor')) {
+  populateStorage();
+} else {
+  setStyles();
+}
+ +

El método {{domxref("Storage.getItem()")}} se usa para obtener un dato de la memoria; en este caso, estamos probando si el dato bgcolor existe; si no, corremos populateStorage() para añadir los valores personalizados actuales a la memoria. Si ya hay valores guardados, corremos setStyles() para actualizar el estilo de la página con los valores almacenados.

+ +

Nota: También puedes usar {{domxref("Storage.length")}} para probar si el objeto de almacenamiento está vació o no.

+ +

Obtener valores de la memoria

+ +

Como dijimos arriba, los valores se pueden recuperar de la memoria usando {{domxref("Storage.getItem()")}}. Este método usa la llave del dato como argumento y devuelve el valor. Por ejemplo:

+ +
function setStyles() {
+  var currentColor = localStorage.getItem('bgcolor');
+  var currentFont = localStorage.getItem('font');
+  var currentImage = localStorage.getItem('image');
+
+  document.getElementById('bgcolor').value = currentColor;
+  document.getElementById('font').value = currentFont;
+  document.getElementById('image').value = currentImage;
+
+  htmlElem.style.backgroundColor = '#' + currentColor;
+  pElem.style.fontFamily = currentFont;
+  imgElem.setAttribute('src', currentImage);
+}
+ +

Aquí, en las primeras tres líneas tomamos los valores del almacenamiento local. Después, fijamos los valores mostrados en los elementos del formulario a esos valores, de forma que se mantengan sincronizados cuando recargues la página. Finalmente, actualizamos los estilos y la imagen decorativa en la página para que tus opciones personalizadas vuelvan a aparecer al recargar.

+ +

Guardar valores en la memoria

+ +

{{domxref("Storage.setItem()")}} se usa tanto para crear nuevos datos como para actualizar valores existentes (si el dato ya existía). Este método recibe dos argumentos: la llave del dato que se va a crear/modificar y el valor que se va a guardar.

+ +
function populateStorage() {
+  localStorage.setItem('bgcolor', document.getElementById('bgcolor').value);
+  localStorage.setItem('font', document.getElementById('font').value);
+  localStorage.setItem('image', document.getElementById('image').value);
+
+  setStyles();
+}
+ +

La función populateStorage() define tres elementos en el almacenamiento local — el color de fondo, la tipografía y la ruta de almacenamiento de la imagen. Luego corre la función setStyles() para actualizar el estilo de la página, etc.

+ +

También incluimos un manejador onchange para cada elemento del formulario, de manera que los datos y los estilos son actualizados cada vez que un valor del formulario cambia:

+ +
bgcolorForm.onchange = populateStorage;
+fontForm.onchange = populateStorage;
+imageForm.onchange = populateStorage;
+ +

Responder a cambios en la memoria con el evento StorageEvent

+ +

El evento {{domxref("StorageEvent")}} se dispara siempre que se hace un cambio al objeto {{domxref("Storage")}} (nota que este evento no se dispara para cambios en sessionStorage). Este evento no va a trabajar en la misma página en la que se están haciendo los cambios, sino que es una manera para que las otras páginas del dominio que usan la memoria sincronicen los cambios que se están haciendo. Las páginas en otros dominios no pueden acceder a los mismos objetos de almacenamiento.

+ +

En la página de eventos (ver events.js) el único JavaScript es el siguiente:

+ +
window.addEventListener('storage', function(e) {
+  document.querySelector('.my-key').textContent = e.key;
+  document.querySelector('.my-old').textContent = e.oldValue;
+  document.querySelector('.my-new').textContent = e.newValue;
+  document.querySelector('.my-url').textContent = e.url;
+  document.querySelector('.my-storage').textContent = JSON.stringify(e.storageArea);
+});
+ +

Aquí añadimos un detector de evento al objeto window que se dispara cuando el objeto {{domxref("Storage")}} asociado con el origen actual cambia. Como puedes ver arriba, el objeto asociado a este evento tiene varias propiedades que contienen información útil — la llave del dato que cambió, el valor anterior al cambio, el nuevo valor tras el cambio, la URL del documento que cambió la memoria y el objeto de almacenamiento mismo (que volvimos una cadena para que pudieras ver su contenido).

+ +

Borrar registros

+ +

El almacenamiento web también provee un par de métodos simples para remover datos. No los usamos en nuestro demo, pero se pueden añadir de manera muy simple a tu proyecto:

+ + + +

Especificaciones

+ + + + + + + + + + + + + + +
EspecificaciónEstadoComentario
{{SpecName('HTML WHATWG', 'webstorage.html#webstorage')}}{{Spec2('HTML WHATWG')}}
+ +

Compatibilidad de navegadores

+ +

{{ CompatibilityTable() }}

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
CaracterísticaChromeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
localStorage43.5810.504
sessionStorage52810.504
+
+ +
+ + + + + + + + + + + + + + + + + + + +
CaracterísticaAndroidFirefox Mobile (Gecko)IE PhoneOpera MobileSafari Mobile
Basic support2.1{{ CompatUnknown }}811iOS 3.2
+
+ +

Todos los navegadores tienen distintos niveles de capacidad tanto para localStorage como para sessionStorage. Aquí está una análisis detallado de todas las capacidades de almacenamiento de diferentes navegadores.

+ +
+

Nota: Desde iOS 5.1, Safari Mobile almacena los datos de localStorage en la carpeta de caché, la cual está sujeta a limpiezas ocasionales, a petición del sistema operativo, típicamente cuando el espacio es reducido.

+
+ +

Ver también

+ + diff --git a/files/es/web/api/web_workers_api/using_web_workers/index.html b/files/es/web/api/web_workers_api/using_web_workers/index.html new file mode 100644 index 0000000000..db4dbc07e3 --- /dev/null +++ b/files/es/web/api/web_workers_api/using_web_workers/index.html @@ -0,0 +1,633 @@ +--- +title: Usando Web Workers +slug: Web/Guide/Performance/Usando_web_workers +translation_of: Web/API/Web_Workers_API/Using_web_workers +--- +

Los Web Workers dedicados proveen un medio sencillo para que el contenido web ejecute scripts en hilos en segundo plano. Una vez creado, un worker puede enviar mensajes a la tarea creada mediante envio de mensajes al manejador de eventos especificado por el creador. Sin embargo, los workers trabajan dentro de un contexto global diferente de la ventana actual (usar el atajo  {{ domxref("window") }} en lugar de {{ domxref("window.self","self") }} con el fin de obtener el scope actual dentro de un {{ domxref("Worker") }} retornaría, de hecho, un error).

+ +

El hilo worker puede realizar tareas sin interferir con la interfaz de usuario. Ademas, pueden realizar I/O usando XMLHttpRequest (aunque el responseXML y los atributos channel son siempre null).

+ +

Para documentacion de referencia acerca de workers busca {{ domxref("Worker") }} ; este articulo complementa ese ofreciendo ejemplos y detalles adicionales. Para una lista de las funciones disponibles sobre workers, visita  Functions and interfaces available to workers.

+ +

Acerca de seguridad de hilos

+ +

La interfaz  {{ domxref("Worker") }} crea hilos a nivel de SO reales, y la concurrencia puede causar effectos interesantes en tu código si no eres cuidadoso. Sin embargo, en el caso de los web workers, el control cuidadoso de los puntos de comunicacion con otros hilos indica que es realmente muy dificil causar problemas de concurrencia. No existe acceso a componentes no-hilo seguros o al DOM y debes pasar la informacion entrante o saliente del hilo a traves de objetos serializados. Así que debes poner esfuerzo para causar problemas en tu código.

+ +

Creando un web worker

+ +

Crear un nuevo worker es simple.  Sólo tienes que llamar el constructor {{ domxref("Worker.Worker", "Worker()") }}, especificando la URI de un script a ejecutar en el hilo del worker (worker thread), y, si deseas poder recibir notificaciones del worker, establece la propiedad  {{domxref("Worker.onmessage")}} del worker a una función de manejo de eventos apropiada.

+ +
+
var myWorker = new Worker("my_task.js");
+
+myWorker.onmessage = function (oEvent) {
+  console.log("Called back by the worker!\n");
+};
+
+ +

Alternativamente, puedes usar addEventListener() :

+ +
+
var myWorker = new Worker("my_task.js");
+
+myWorker.addEventListener("message", function (oEvent) {
+  console.log("Called back by the worker!\n");
+}, false);
+
+myWorker.postMessage(""); // start the worker.
+
+ +

La Línea 1 en  este ejemplo crea un nuevo worker (worker thread). La Línea 3 configura un manejador de eventos (listener) para encargarse de los eventos message del worker. Este manejador de eventos se llamará cuando el worker llame a su propia función {{domxref("Worker.postMessage()")}}. Finalmente, la Linea 7 inicia el worker (worker thread).

+ +
Nota : La URI pasada como parámetro del constructor de Worker debe obedecer la política same-origin policy . Actualmente hay desacuerdo entre los desarolladores de navegadores sobre qué URIs son del mismo origen; Gecko 10.0 {{ geckoRelease("10.0") }} y posteriores sí permiten data URIs e Internet Explorer 10 no permite Blob URIs como un script válido para los workers.
+ +

Pasando datos

+ +

Los datos pasan entre la página principal y los workers son copiados, no compartidos. Los objetos se serializan a medida que se entregan al worker, y posteriormente, se deserializan en el otro extremo. La página y el worker no comparten la misma instancia, por lo que el resultado final es que un duplicado es creado en cada extremo. La mayoría de los navegadores implementan esta característica como structured cloning.

+ +

Antes de continuar, vamos a crear con fines didácticos una función llamada emulateMessage() que simulará el comportamiento de un valor el cual es clonado y no compartido durante el paso desde un worker a la página principal o viceversa:

+ +
function emulateMessage (vVal) {
+    return eval("(" + JSON.stringify(vVal) + ")");
+}
+
+// Tests
+
+// test #1
+var example1 = new Number(3);
+alert(typeof example1); // object
+alert(typeof emulateMessage(example1)); // number
+
+// test #2
+var example2 = true;
+alert(typeof example2); // boolean
+alert(typeof emulateMessage(example2)); // boolean
+
+// test #3
+var example3 = new String("Hello World");
+alert(typeof example3); // object
+alert(typeof emulateMessage(example3)); // string
+
+// test #4
+var example4 = {
+    "name": "John Smith",
+    "age": 43
+};
+alert(typeof example4); // object
+alert(typeof emulateMessage(example4)); // object
+
+// test #5
+function Animal (sType, nAge) {
+    this.type = sType;
+    this.age = nAge;
+}
+var example5 = new Animal("Cat", 3);
+alert(example5.constructor); // Animal
+alert(emulateMessage(example5).constructor); // Object
+ +

A Un valor que es clonado y no compartido se denomina mensaje. De vuelta con los workers, los mensajes pueden ser enviados hacia y desde el hilo principal empleando postMessage(). Los eventos de mensaje {{domxref("MessageEvent.data", "data")}} atributo contienen datos devueltos desde el worker.

+ +

example.html: (la página principal):

+ +
var myWorker = new Worker("my_task.js");
+
+myWorker.onmessage = function (oEvent) {
+  console.log("Worker said : " + oEvent.data);
+};
+
+myWorker.postMessage("ali");
+ +

my_task.js (el worker):

+ +
postMessage("I\'m working before postMessage(\'ali\').");
+
+onmessage = function (oEvent) {
+  postMessage("Hi " + oEvent.data);
+};
+ +
Note: Como siempre, los hilos en segundo plano -incluyendo workers- no pueden manipular el DOM. Si acciones tomadas por el hilo en segundo planos resultarían en cambios en el DOM, deberian enviar mensajes a sus creadores para llevarlos a cabo.
+ +

The structured cloning algorithm can accept JSON and a few things that JSON can't like circular references.

+ +

Ejemplos pasando datos

+ +

Example #1: Crear un "eval() asíncrono" genérico

+ +

El siguiente ejemplo muestra como usar un worker para ejecutar asíncronamente cualquier tipo de código en Javascript a traves de eval dentro del worker:

+ +
// Syntax: asyncEval(code[, listener])
+
+var asyncEval = (function () {
+
+  var aListeners = [], oParser = new Worker("data:text/javascript;charset=US-ASCII,onmessage%20%3D%20function%20%28oEvent%29%20%7B%0A%09postMessage%28%7B%0A%09%09%22id%22%3A%20oEvent.data.id%2C%0A%09%09%22evaluated%22%3A%20eval%28oEvent.data.code%29%0A%09%7D%29%3B%0A%7D");
+
+  oParser.onmessage = function (oEvent) {
+    if (aListeners[oEvent.data.id]) { aListeners[oEvent.data.id](oEvent.data.evaluated); }
+    delete aListeners[oEvent.data.id];
+  };
+
+
+  return function (sCode, fListener) {
+    aListeners.push(fListener || null);
+    oParser.postMessage({
+      "id": aListeners.length - 1,
+      "code": sCode
+    });
+  };
+
+})();
+ +

Ejemplo de uso:

+ +
// asynchronous alert message...
+asyncEval("3 + 2", function (sMessage) {
+    alert("3 + 2 = " + sMessage);
+});
+
+// asynchronous print message...
+asyncEval("\"Hello World!!!\"", function (sHTML) {
+    document.body.appendChild(document.createTextNode(sHTML));
+});
+
+// asynchronous void...
+asyncEval("(function () {\n\tvar oReq = new XMLHttpRequest();\n\toReq.open(\"get\", \"http://www.mozilla.org/\", false);\n\toReq.send(null);\n\treturn oReq.responseText;\n})()");
+ +

Ejemplo #2: Paso avanzado de JSON Data y creación de un sistema de conmutación

+ +

Si tiene que pasar datos complejos y tienes que llamar a muchas funciones diferentes tanto en la página principal como en el Worker, puede crear un sistema como el siguiente.

+ +

example.html (the main page):

+ +
<!doctype html>
+<html>
+<head>
+<meta charset="UTF-8"  />
+<title>MDN Example - Queryable worker</title>
+<script type="text/javascript">
+  /*
+    QueryableWorker instances methods:
+     * sendQuery(queryable function name, argument to pass 1, argument to pass 2, etc. etc): calls a Worker's queryable function
+     * postMessage(string or JSON Data): see Worker.prototype.postMessage()
+     * terminate(): terminates the Worker
+     * addListener(name, function): adds a listener
+     * removeListener(name): removes a listener
+    QueryableWorker instances properties:
+     * defaultListener: the default listener executed only when the Worker calls the postMessage() function directly
+  */
+  function QueryableWorker (sURL, fDefListener, fOnError) {
+    var oInstance = this, oWorker = new Worker(sURL), oListeners = {};
+    this.defaultListener = fDefListener || function () {};
+    oWorker.onmessage = function (oEvent) {
+      if (oEvent.data instanceof Object && oEvent.data.hasOwnProperty("vo42t30") && oEvent.data.hasOwnProperty("rnb93qh")) {
+        oListeners[oEvent.data.vo42t30].apply(oInstance, oEvent.data.rnb93qh);
+      } else {
+        this.defaultListener.call(oInstance, oEvent.data);
+      }
+    };
+    if (fOnError) { oWorker.onerror = fOnError; }
+    this.sendQuery = function (/* queryable function name, argument to pass 1, argument to pass 2, etc. etc */) {
+      if (arguments.length < 1) { throw new TypeError("QueryableWorker.sendQuery - not enough arguments"); return; }
+      oWorker.postMessage({ "bk4e1h0": arguments[0], "ktp3fm1": Array.prototype.slice.call(arguments, 1) });
+    };
+    this.postMessage = function (vMsg) {
+      //I just think there is no need to use call() method
+      //how about just oWorker.postMessage(vMsg);
+      //the same situation with terminate
+      //well,just a little faster,no search up the prototye chain
+      Worker.prototype.postMessage.call(oWorker, vMsg);
+    };
+    this.terminate = function () {
+      Worker.prototype.terminate.call(oWorker);
+    };
+    this.addListener = function (sName, fListener) {
+      oListeners[sName] = fListener;
+    };
+    this.removeListener = function (sName) {
+      delete oListeners[sName];
+    };
+  };
+
+  // your custom "queryable" worker
+  var oMyTask = new QueryableWorker("my_task.js" /* , yourDefaultMessageListenerHere [optional], yourErrorListenerHere [optional] */);
+
+  // your custom "listeners"
+
+  oMyTask.addListener("printSomething", function (nResult) {
+    document.getElementById("firstLink").parentNode.appendChild(document.createTextNode(" The difference is " + nResult + "!"));
+  });
+
+  oMyTask.addListener("alertSomething", function (nDeltaT, sUnit) {
+    alert("Worker waited for " + nDeltaT + " " + sUnit + " :-)");
+  });
+</script>
+</head>
+<body>
+  <ul>
+    <li><a id="firstLink" href="javascript:oMyTask.sendQuery('getDifference', 5, 3);">What is the difference between 5 and 3?</a></li>
+    <li><a href="javascript:oMyTask.sendQuery('waitSomething');">Wait 3 seconds</a></li>
+    <li><a href="javascript:oMyTask.terminate();">terminate() the Worker</a></li>
+  </ul>
+</body>
+</html>
+ +

my_task.js (el worker):

+ +
// your custom PRIVATE functions
+
+function myPrivateFunc1 () {
+  // do something
+}
+
+function myPrivateFunc2 () {
+  // do something
+}
+
+// etc. etc.
+
+// your custom PUBLIC functions (i.e. queryable from the main page)
+
+var queryableFunctions = {
+  // example #1: get the difference between two numbers:
+  getDifference: function (nMinuend, nSubtrahend) {
+      reply("printSomething", nMinuend - nSubtrahend);
+  },
+  // example #2: wait three seconds
+  waitSomething: function () {
+      setTimeout(function() { reply("alertSomething", 3, "seconds"); }, 3000);
+  }
+};
+
+// system functions
+
+function defaultQuery (vMsg) {
+  // your default PUBLIC function executed only when main page calls the queryableWorker.postMessage() method directly
+  // do something
+}
+
+function reply (/* listener name, argument to pass 1, argument to pass 2, etc. etc */) {
+  if (arguments.length < 1) { throw new TypeError("reply - not enough arguments"); return; }
+  postMessage({ "vo42t30": arguments[0], "rnb93qh": Array.prototype.slice.call(arguments, 1) });
+}
+
+onmessage = function (oEvent) {
+  if (oEvent.data instanceof Object && oEvent.data.hasOwnProperty("bk4e1h0") && oEvent.data.hasOwnProperty("ktp3fm1")) {
+    queryableFunctions[oEvent.data.bk4e1h0].apply(self, oEvent.data.ktp3fm1);
+  } else {
+    defaultQuery(oEvent.data);
+  }
+};
+ +

Es un método posible para conmutar el contenido de cada mensaje de cada mainpage-worker y viceversa.

+ +

Pasando datos mediante transferencia de propiedades (objetos transferibles)

+ +

Google Chrome 17 y Firefox 18 implementan un método adicional para enviar ciertos tipos de objetos desde o hacia el worker con un mejor rendimiento. Estos objetos se denominan objetos transferibles (transferable objects), es decir, objetos que implementan la interfaz {{domxref("Transferable")}}. Los objetos transferibles se transfieren de un contexto a otro con una operación "zero-copy". Esto supone una gran mejora de rendimiento al enviar grandes cantidades de datos. Piensa en ello como un paso por referencia si vienes del mundo de C/C++.  Sin embargo, a diferecia del paso por referencia, la "versión" original no queda disponible una vez transferida. Su contenido es transferido al nuevo contexto. Por ejemplo, cuando se transfiere un {{domxref("ArrayBuffer")}} de tu aplicacion al Worker, el contenido del {{domxref("ArrayBuffer")}} original se vacía y no se puede utilizar posteriormente. Su contenido es (literalmente) transferido al contexto del Worker.

+ +
// Create a 32MB "file" and fill it.
+var uInt8Array = new Uint8Array(1024*1024*32); // 32MB
+for (var i = 0; i < uInt8Array.length; ++i) {
+  uInt8Array[i] = i;
+}
+
+worker.postMessage(uInt8Array.buffer, [uInt8Array.buffer]);
+
+ +

Para más información sobre los objetos transferibles, visita HTML5Rocks .

+ +

Spawning subworkers

+ +

Workers may spawn more workers if they wish.  So-called subworkers must be hosted within the same origin as the parent page.  Also, the URIs for subworkers are resolved relative to the parent worker's location rather than that of the owning page.  This makes it easier for workers to keep track of where their dependencies are.

+ +

Subworkers are currently not supported in Chrome. See crbug.com/31666 .

+ +

Embedded workers

+ +

There is not an "official" way to embed the code of a worker within a web page as for the {{ HTMLElement("script") }} elements. But a {{ HTMLElement("script") }} element which does not have a src attribute and has a type attribute that does not identify an executable mime-type will be considered a data block element, that JavaScript could use.  "Data blocks" is a more general feature of HTML5 that can carry almost any textual data. So, a worker could be embedded in this way:

+ +
<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8" />
+<title>MDN Example - Embedded worker</title>
+<script type="text/js-worker">
+  // This script WON'T be parsed by JS engines because its mime-type is text/js-worker.
+  var myVar = "Hello World!";
+  // Rest of your worker code goes here.
+</script>
+<script type="text/javascript">
+  // This script WILL be parsed by JS engines because its mime-type is text/javascript.
+  function pageLog (sMsg) {
+    // Use a fragment: browser will only render/reflow once.
+    var oFragm = document.createDocumentFragment();
+    oFragm.appendChild(document.createTextNode(sMsg));
+    oFragm.appendChild(document.createElement("br"));
+    document.querySelector("#logDisplay").appendChild(oFragm);
+  }
+</script>
+<script type="text/js-worker">
+  // This script WON'T be parsed by JS engines because its mime-type is text/js-worker.
+  onmessage = function (oEvent) {
+    postMessage(myVar);
+  };
+  // Rest of your worker code goes here.
+</script>
+<script type="text/javascript">
+  // This script WILL be parsed by JS engines because its mime-type is text/javascript.
+
+  // In the past...:
+  // blob builder existed
+  // ...but now we use Blob...:
+  var blob = new Blob(Array.prototype.map.call(document.querySelectorAll("script[type=\"text\/js-worker\"]"), function (oScript) { return oScript.textContent; }),{type: "text/javascript"});
+
+  // Creating a new document.worker property containing all our "text/js-worker" scripts.
+  document.worker = new Worker(window.URL.createObjectURL(blob));
+
+  document.worker.onmessage = function (oEvent) {
+    pageLog("Received: " + oEvent.data);
+  };
+
+  // Start the worker.
+  window.onload = function() { document.worker.postMessage(""); };
+</script>
+</head>
+<body><div id="logDisplay"></div></body>
+</html>
+ +

The embedded worker is now nested into a new custom document.worker property.

+ +

Tiempos fuera e intervalos

+ +

Los trabajadores pueden usar tiempos fuera e intervalos de la misma forma que el "hilo principal".  Esto puede ser útil, por ejemplo, si quieres tener a tu hilo trabajador corriendo codigo periodicamente en lugar de sin parar.

+ +

Ver setTimeout() , clearTimeout() , setInterval() , y clearInterval() para más detalles. Ver también: JavaScript Timers.

+ +

Terminating a worker

+ +

If you need to immediately terminate a running worker, you can do so by calling the worker's terminate() method:

+ +
myWorker.terminate();
+ +

The worker thread is killed immediately without an opportunity to complete its operations or clean up after itself.

+ +

Workers may close themselves by calling their own {{ ifmethod("nsIWorkerScope", "close") }} method:

+ +
self.close();
+ +

Manejo de errores

+ +

When a runtime error occurs in worker, its onerror event handler is called.  It receives an event named error which implements the ErrorEvent interface.  The event doesn't bubble and is cancelable; to prevent the default action from taking place, the worker can call the error event's preventDefault() method.

+ +

The error event has the following three fields that are of interest:

+ +
+
message
+
A human-readable error message.
+
filename
+
The name of the script file in which the error occurred.
+
lineno
+
The line number of the script file on which the error occurred.
+
+ +

Accediendo al objeto navigator

+ +

Los workers pueden acceder al objeto navigator, el cuál está disponible dentro de su scope actual. Este contiene los siguientes strings que pueden ser usados para identificar el navegador, al igual que puede realizarse usando scripts normales:

+ + + +

Importing scripts and libraries

+ +

Worker threads have access to a global function, importScripts() , which lets them import scripts or libraries into their scope.  It accepts as parameters zero or more URIs to resources to import; all of the following examples are valid:

+ +
importScripts();                        /* imports nothing */
+importScripts('foo.js');                /* imports just "foo.js" */
+importScripts('foo.js', 'bar.js');      /* imports two scripts */
+
+ +

The browser loads each listed script and executes it. Any global objects from each script may then be used by the worker. If the script can't be loaded, NETWORK_ERROR is thrown, and subsequent code will not be executed. Previously executed code (including code deferred using {{ domxref("window.setTimeout()") }}) will still be functional though. Function declarations after the importScripts() method are also kept, since these are always evaluated before the rest of the code.

+ +
Note: Scripts may be downloaded in any order, but will be executed in the order in which you pass the filenames into importScripts() .  This is done synchronously; importScripts() does not return until all the scripts have been loaded and executed.
+ +

Examples

+ +

This section provides several examples of how to use DOM workers.

+ +

Performing computations in the background

+ +

One way workers are useful is to allow your code to perform processor-intensive calculations without blocking the user interface thread.  In this example, a worker is used to calculate Fibonacci numbers.

+ +

The JavaScript code

+ +

The following JavaScript code is stored in the "fibonacci.js" file referenced by the HTML in the next section.

+ +
var results = [];
+
+function resultReceiver(event) {
+  results.push(parseInt(event.data));
+  if (results.length == 2) {
+    postMessage(results[0] + results[1]);
+  }
+}
+
+function errorReceiver(event) {
+  throw event.data;
+}
+
+onmessage = function(event) {
+  var n = parseInt(event.data);
+
+  if (n == 0 || n == 1) {
+    postMessage(n);
+    return;
+  }
+
+  for (var i = 1; i <= 2; i++) {
+    var worker = new Worker("fibonacci.js");
+    worker.onmessage = resultReceiver;
+    worker.onerror = errorReceiver;
+    worker.postMessage(n - i);
+  }
+ };
+ +

The worker sets the property onmessage  to a function which will receive messages sent when the worker object's  postMessage() is called.  (Note that this differs from defining a global variable of that name, or defining a function with that name.   var onmessage and function onmessage will define global properties with those names, but they will not register the function to receive messages sent by the  web page that created the worker.)  This starts the recursion, spawning new copies of itself to handle each iteration of the calculation.

+ +

The HTML code

+ +
<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="UTF-8"  />
+    <title>Test threads fibonacci</title>
+  </head>
+  <body>
+
+  <div id="result"></div>
+
+  <script language="javascript">
+
+    var worker = new Worker("fibonacci.js");
+
+    worker.onmessage = function(event) {
+      document.getElementById("result").textContent = event.data;
+      dump("Got: " + event.data + "\n");
+    };
+
+    worker.onerror = function(error) {
+      dump("Worker error: " + error.message + "\n");
+      throw error;
+    };
+
+    worker.postMessage("5");
+
+  </script>
+  </body>
+</html>
+
+ +

The web page creates a div element with the ID  result , which gets used to display the result, then spawns the worker.  After spawning the worker, the onmessage handler is configured to display the results by setting the contents of the div element, and the onerror handler is set to dump the error message.

+ +

Finally, a message is sent to the worker to start it.

+ +

Try this example .

+ +

Performing web I/O in the background

+ +

You can find an example of this in the article Using workers in extensions .

+ +

Dividing tasks among multiple workers

+ +

As multi-core computers become increasingly common, it's often useful to divide computationally complex tasks among multiple workers, which may then perform those tasks on multiple-processor cores.

+ +

example coming soon

+ +

Creating workers from within workers

+ +

The Fibonacci example shown previously demonstrates that workers can in fact spawn additional workers.  This makes it easy to create recursive routines.

+ +

Browser compatibility

+ +

{{ CompatibilityTable() }}

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FeatureChromeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
Dedicated workers{{CompatChrome(3)}}{{CompatGeckoDesktop(1.9.1)}}{{CompatIE(10)}}{{CompatOpera(10.60)}}{{CompatSafari(4)}}
Shared workers{{CompatChrome(3)}}{{CompatNo}}{{CompatNo}}{{CompatOpera(10.60)}}{{CompatSafari(5)}}
Passing data using structured cloning{{CompatChrome(13)}}{{CompatGeckoDesktop(8)}}{{CompatIE(10)}}{{CompatOpera(11.50)}}{{CompatSafari(5.1)}}
Passing data using  transferable objects17 {{ property_prefix("webkit") }}
+ {{CompatChrome(21)}}
{{CompatGeckoDesktop(18)}}{{CompatNo}}{{CompatOpera(15)}}{{CompatSafari(6)}}
Global {{ domxref("window.URL", "URL") }}10 as webkitURL
+ {{CompatChrome(23)}}
{{CompatGeckoDesktop(21)}}{{CompatIE(11)}}{{CompatOpera(15)}}6 as webkitURL
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FeatureAndroidChrome for AndroidFirefox Mobile (Gecko)IE PhoneOpera MobileSafari Mobile
Dedicated workers---0.163.5 (1.9.1)---115
Shared workers---{{ CompatNo() }}------------
Passing data using structured cloning---0.168---------
Passing data using  transferable objects---18---------
+
+ +

See also

+ + diff --git a/files/es/web/api/webgl_api/tutorial/creating_3d_objects_using_webgl/index.html b/files/es/web/api/webgl_api/tutorial/creating_3d_objects_using_webgl/index.html new file mode 100644 index 0000000000..91e6bf3d37 --- /dev/null +++ b/files/es/web/api/webgl_api/tutorial/creating_3d_objects_using_webgl/index.html @@ -0,0 +1,133 @@ +--- +title: Creación de objetos 3D utilizando WebGL +slug: Web/API/WebGL_API/Tutorial/Objetos_3D_utilizando_WebGL +tags: + - Cubo 3D + - Objetos 3D + - Tutorial + - WebGL +translation_of: Web/API/WebGL_API/Tutorial/Creating_3D_objects_using_WebGL +--- +

{{WebGLSidebar("Tutorial")}} {{PreviousNext("Web/API/WebGL_API/Tutorial/Animating_objects_with_WebGL", "Web/API/WebGL_API/Tutorial/Using_textures_in_WebGL")}}

+ +

Vamos a llevar nuestro cuadrado hacia la tercera dimensión agregando cinco caras más para crear el cubo. Para hacer esto de manera eficiente, vamos a cambiar el dibujado por medio de vertices utilizando el método {{domxref("WebGLRenderingContext.drawArrays()", "gl.drawArrays()")}} por el uso de un arreglo de vértices como tabla, esto por medio del llamado hacia   {{domxref("WebGLRenderingContext.drawElements()", "gl.drawElements()")}}.

+ +

Considerar: cada cara cuatro vértices para su definición, pero cada vértice es compartido por 3 caras. Realizando una lista con los 24 vértices es como nosotros haremos un menor intercambio de datos, después haremos referencia hacia cada vértic por medio se su índice en la lista en lugar de pasar el juego entero de coordenadas. Si te preguntas por qué necesitamos 24 vértices, y no solo 8, es porque cada esquina pertenece a tres caras de diferente color, y un solo vértice necesita tener un solo color específico, por lo tanto crearemos 3 copias de cada vértice en tres colores diferentes, uno por cada cara.

+ +

Definir la posición de los vértices del cubo

+ +

Primero, construiremos el buffer para la posición de los vértices actualizando el código en initBuffers(). Esto es muy parecido a lo como si fuera para el cuadraro, pero más lardo debido a que ahora son 24 vértices (4 por lado):

+ +
var vertices = [
+  // Cara delantera
+  -1.0, -1.0,  1.0,
+   1.0, -1.0,  1.0,
+   1.0,  1.0,  1.0,
+  -1.0,  1.0,  1.0,
+
+  // Cara trasera
+  -1.0, -1.0, -1.0,
+  -1.0,  1.0, -1.0,
+   1.0,  1.0, -1.0,
+   1.0, -1.0, -1.0,
+
+  // Top face
+  -1.0,  1.0, -1.0,
+  -1.0,  1.0,  1.0,
+   1.0,  1.0,  1.0,
+   1.0,  1.0, -1.0,
+
+  // Bottom face
+  -1.0, -1.0, -1.0,
+   1.0, -1.0, -1.0,
+   1.0, -1.0,  1.0,
+  -1.0, -1.0,  1.0,
+
+  // Right face
+   1.0, -1.0, -1.0,
+   1.0,  1.0, -1.0,
+   1.0,  1.0,  1.0,
+   1.0, -1.0,  1.0,
+
+  // Left face
+  -1.0, -1.0, -1.0,
+  -1.0, -1.0,  1.0,
+  -1.0,  1.0,  1.0,
+  -1.0,  1.0, -1.0
+];
+
+ +

Definir los colores de los vértices

+ +

Necesitamos construir un arreglo de colores por cada uno de los 24 vertices. Este código comienza por definir un color para cada cara, después utiliza un ciclo para ensamblar el arreglo de colores por cada uno de los vértices.

+ +
var colors = [
+  [1.0,  1.0,  1.0,  1.0],    // Cara delantera: blanco
+  [1.0,  0.0,  0.0,  1.0],    // Cara trasera: rojo
+  [0.0,  1.0,  0.0,  1.0],    // Cara superior: verde
+  [0.0,  0.0,  1.0,  1.0],    // Cara inferior: azul
+  [1.0,  1.0,  0.0,  1.0],    // Cara derecha: amarillo
+  [1.0,  0.0,  1.0,  1.0]     // Cara izquierda: morado
+];
+
+var generatedColors = [];
+
+for (j=0; j<6; j++) {
+  var c = colors[j];
+
+  for (var i=0; i<4; i++) {
+    generatedColors = generatedColors.concat(c);
+  }
+}
+
+var cubeVerticesColorBuffer = gl.createBuffer();
+gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesColorBuffer);
+gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(generatedColors), gl.STATIC_DRAW);
+
+ +

Definir el elemento arreglo

+ +

Una ves que el vértice es generado, nosotros necesitamos contruir el elemento arreglo.

+ +
var cubeVerticesIndexBuffer = gl.createBuffer();
+gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVerticesIndexBuffer);
+
+// Este arrelgo define cada cara como 2 triángulos utilizando
+// los índices dentro de cada arreglo de vértices
+// para especificar cada posición en los tríangulos.
+
+var cubeVertexIndices = [
+  0,  1,  2,      0,  2,  3,    // enfrente
+  4,  5,  6,      4,  6,  7,    // atrás
+  8,  9,  10,     8,  10, 11,   // arriba
+  12, 13, 14,     12, 14, 15,   // fondo
+  16, 17, 18,     16, 18, 19,   // derecha
+  20, 21, 22,     20, 22, 23    // izquierda
+];
+
+// Ahora enviamos el elemento arreglo a  GL
+
+gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,
+    new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW);
+
+ +

El arreglo cubeVertexIndices define cada cara como un par de triángulos, especificando cada vértice del triángulo como un índice dentro del arreglo de vértices en el cubo. Así el cubo es descrito como una colección de 12 triángulos. 

+ +

Dibujando el cubo

+ +

Para continuar necesitaremos agregar el código a nuestra función drawScene() esto para poder dibujar utilizando el buffer índice del cubo, agregaremos un nuevo llamado a  {{domxref("WebGLRenderingContext.bindBuffer()", "gl.bindBuffer()")}}  y {{domxref("WebGLRenderingContext.drawElements()", "gl.drawElements()")}}como se muestra a continuación:

+ +
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVerticesIndexBuffer);
+setMatrixUniforms();
+gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
+
+ +

Desde que cada cara de nuestro cubo está compuesto de dos triángulos, tenemos 6 vértices en el cubo, se podría pensar que algunos de ellos son duplicados. Sin embargo, desde que nuestro arreglo índice se encuentra compuesto de enteros simples, esto no es una cantidad excesiva de intercambio de datos para pasar por cada cuadro de la animación.

+ +

En este punto ahora tenemos un cubo animado rebotando y rotando, cuenta con seis caras coloreadas vívidamente.

+ +

{{EmbedGHLiveSample('webgl-examples/tutorial/sample5/index.html', 670, 510) }}

+ +

Ver el Código completo | Abrir esta demostración en una página nueva

+ +

{{PreviousNext("Web/API/WebGL_API/Tutorial/Animating_objects_with_WebGL", "Web/API/WebGL_API/Tutorial/Using_textures_in_WebGL")}}

diff --git a/files/es/web/api/webgl_api/tutorial/objetos_3d_utilizando_webgl/index.html b/files/es/web/api/webgl_api/tutorial/objetos_3d_utilizando_webgl/index.html deleted file mode 100644 index 91e6bf3d37..0000000000 --- a/files/es/web/api/webgl_api/tutorial/objetos_3d_utilizando_webgl/index.html +++ /dev/null @@ -1,133 +0,0 @@ ---- -title: Creación de objetos 3D utilizando WebGL -slug: Web/API/WebGL_API/Tutorial/Objetos_3D_utilizando_WebGL -tags: - - Cubo 3D - - Objetos 3D - - Tutorial - - WebGL -translation_of: Web/API/WebGL_API/Tutorial/Creating_3D_objects_using_WebGL ---- -

{{WebGLSidebar("Tutorial")}} {{PreviousNext("Web/API/WebGL_API/Tutorial/Animating_objects_with_WebGL", "Web/API/WebGL_API/Tutorial/Using_textures_in_WebGL")}}

- -

Vamos a llevar nuestro cuadrado hacia la tercera dimensión agregando cinco caras más para crear el cubo. Para hacer esto de manera eficiente, vamos a cambiar el dibujado por medio de vertices utilizando el método {{domxref("WebGLRenderingContext.drawArrays()", "gl.drawArrays()")}} por el uso de un arreglo de vértices como tabla, esto por medio del llamado hacia   {{domxref("WebGLRenderingContext.drawElements()", "gl.drawElements()")}}.

- -

Considerar: cada cara cuatro vértices para su definición, pero cada vértice es compartido por 3 caras. Realizando una lista con los 24 vértices es como nosotros haremos un menor intercambio de datos, después haremos referencia hacia cada vértic por medio se su índice en la lista en lugar de pasar el juego entero de coordenadas. Si te preguntas por qué necesitamos 24 vértices, y no solo 8, es porque cada esquina pertenece a tres caras de diferente color, y un solo vértice necesita tener un solo color específico, por lo tanto crearemos 3 copias de cada vértice en tres colores diferentes, uno por cada cara.

- -

Definir la posición de los vértices del cubo

- -

Primero, construiremos el buffer para la posición de los vértices actualizando el código en initBuffers(). Esto es muy parecido a lo como si fuera para el cuadraro, pero más lardo debido a que ahora son 24 vértices (4 por lado):

- -
var vertices = [
-  // Cara delantera
-  -1.0, -1.0,  1.0,
-   1.0, -1.0,  1.0,
-   1.0,  1.0,  1.0,
-  -1.0,  1.0,  1.0,
-
-  // Cara trasera
-  -1.0, -1.0, -1.0,
-  -1.0,  1.0, -1.0,
-   1.0,  1.0, -1.0,
-   1.0, -1.0, -1.0,
-
-  // Top face
-  -1.0,  1.0, -1.0,
-  -1.0,  1.0,  1.0,
-   1.0,  1.0,  1.0,
-   1.0,  1.0, -1.0,
-
-  // Bottom face
-  -1.0, -1.0, -1.0,
-   1.0, -1.0, -1.0,
-   1.0, -1.0,  1.0,
-  -1.0, -1.0,  1.0,
-
-  // Right face
-   1.0, -1.0, -1.0,
-   1.0,  1.0, -1.0,
-   1.0,  1.0,  1.0,
-   1.0, -1.0,  1.0,
-
-  // Left face
-  -1.0, -1.0, -1.0,
-  -1.0, -1.0,  1.0,
-  -1.0,  1.0,  1.0,
-  -1.0,  1.0, -1.0
-];
-
- -

Definir los colores de los vértices

- -

Necesitamos construir un arreglo de colores por cada uno de los 24 vertices. Este código comienza por definir un color para cada cara, después utiliza un ciclo para ensamblar el arreglo de colores por cada uno de los vértices.

- -
var colors = [
-  [1.0,  1.0,  1.0,  1.0],    // Cara delantera: blanco
-  [1.0,  0.0,  0.0,  1.0],    // Cara trasera: rojo
-  [0.0,  1.0,  0.0,  1.0],    // Cara superior: verde
-  [0.0,  0.0,  1.0,  1.0],    // Cara inferior: azul
-  [1.0,  1.0,  0.0,  1.0],    // Cara derecha: amarillo
-  [1.0,  0.0,  1.0,  1.0]     // Cara izquierda: morado
-];
-
-var generatedColors = [];
-
-for (j=0; j<6; j++) {
-  var c = colors[j];
-
-  for (var i=0; i<4; i++) {
-    generatedColors = generatedColors.concat(c);
-  }
-}
-
-var cubeVerticesColorBuffer = gl.createBuffer();
-gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesColorBuffer);
-gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(generatedColors), gl.STATIC_DRAW);
-
- -

Definir el elemento arreglo

- -

Una ves que el vértice es generado, nosotros necesitamos contruir el elemento arreglo.

- -
var cubeVerticesIndexBuffer = gl.createBuffer();
-gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVerticesIndexBuffer);
-
-// Este arrelgo define cada cara como 2 triángulos utilizando
-// los índices dentro de cada arreglo de vértices
-// para especificar cada posición en los tríangulos.
-
-var cubeVertexIndices = [
-  0,  1,  2,      0,  2,  3,    // enfrente
-  4,  5,  6,      4,  6,  7,    // atrás
-  8,  9,  10,     8,  10, 11,   // arriba
-  12, 13, 14,     12, 14, 15,   // fondo
-  16, 17, 18,     16, 18, 19,   // derecha
-  20, 21, 22,     20, 22, 23    // izquierda
-];
-
-// Ahora enviamos el elemento arreglo a  GL
-
-gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,
-    new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW);
-
- -

El arreglo cubeVertexIndices define cada cara como un par de triángulos, especificando cada vértice del triángulo como un índice dentro del arreglo de vértices en el cubo. Así el cubo es descrito como una colección de 12 triángulos. 

- -

Dibujando el cubo

- -

Para continuar necesitaremos agregar el código a nuestra función drawScene() esto para poder dibujar utilizando el buffer índice del cubo, agregaremos un nuevo llamado a  {{domxref("WebGLRenderingContext.bindBuffer()", "gl.bindBuffer()")}}  y {{domxref("WebGLRenderingContext.drawElements()", "gl.drawElements()")}}como se muestra a continuación:

- -
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVerticesIndexBuffer);
-setMatrixUniforms();
-gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
-
- -

Desde que cada cara de nuestro cubo está compuesto de dos triángulos, tenemos 6 vértices en el cubo, se podría pensar que algunos de ellos son duplicados. Sin embargo, desde que nuestro arreglo índice se encuentra compuesto de enteros simples, esto no es una cantidad excesiva de intercambio de datos para pasar por cada cuadro de la animación.

- -

En este punto ahora tenemos un cubo animado rebotando y rotando, cuenta con seis caras coloreadas vívidamente.

- -

{{EmbedGHLiveSample('webgl-examples/tutorial/sample5/index.html', 670, 510) }}

- -

Ver el Código completo | Abrir esta demostración en una página nueva

- -

{{PreviousNext("Web/API/WebGL_API/Tutorial/Animating_objects_with_WebGL", "Web/API/WebGL_API/Tutorial/Using_textures_in_WebGL")}}

diff --git a/files/es/web/api/webgl_api/tutorial/using_textures_in_webgl/index.html b/files/es/web/api/webgl_api/tutorial/using_textures_in_webgl/index.html new file mode 100644 index 0000000000..9d2be2d61b --- /dev/null +++ b/files/es/web/api/webgl_api/tutorial/using_textures_in_webgl/index.html @@ -0,0 +1,209 @@ +--- +title: Utilizando texturas en WebGL +slug: Web/API/WebGL_API/Tutorial/Wtilizando_texturas_en_WebGL +tags: + - Texturas + - Tutorial + - WebGL +translation_of: Web/API/WebGL_API/Tutorial/Using_textures_in_WebGL +--- +

{{WebGLSidebar("Tutorial")}} {{PreviousNext("Web/API/WebGL_API/Tutorial/Creating_3D_objects_using_WebGL", "Web/API/WebGL_API/Tutorial/Lighting_in_WebGL")}}

+ +

Ahora que nuestro programa de prueba tiene un cubo, asignemos una textura en lugar de tener sus caras de un color solido.

+ +

Cargando texturas

+ +

La primera cosa que debemos hacer es añadir el codigo para cargar nuestra textura. en nuestro caso, estaremos usando una unica textura, asignada en las seis caras de nuestro cubo rotador, pero la misma tecnica puede ser utilizada para cualquier cantidad de texturas.

+ +
Note: Es importante señalar que la carga de texturas sigue reglas de dominio-cruzado; Es decir, sólo puede cargar texturas de sitios para los que su contenido tiene aprobación de CORS. Vea las texturas entre dominios a continuación para más detalles.
+ +

El codigo que carga la textura se ve como esto:

+ +
function initTextures() {
+  cubeTexture = gl.createTexture();
+  cubeImage = new Image();
+  cubeImage.onload = function() { handleTextureLoaded(cubeImage, cubeTexture); }
+  cubeImage.src = 'cubetexture.png';
+}
+
+function handleTextureLoaded(image, texture) {
+  gl.bindTexture(gl.TEXTURE_2D, texture);
+  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
+  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
+  gl.generateMipmap(gl.TEXTURE_2D);
+  gl.bindTexture(gl.TEXTURE_2D, null);
+}
+
+ +

La rutina initTextures() comienza por crear el GL texture cubeTexture llamando la rutina GL {{domxref("WebGLRenderingContext.createTexture()", "createTexture()")}}. Para cargar la textura desde un archivo de imagen, este luego crea un Objeto Imagen y carga en él el archivo de imagen que deseamos utilizar como nuestra textura. La rutina handleTextureLoaded() corre cuando la textura ha terminado de cargar.

+ +

Para realmente crear la textura, especificamos que la nueva textura es la textura actual en la que queremos operar vinculándola a gl.TEXTURE_2D. Despues de esto, la imagen cargada es pasada a  {{domxref("WebGLRenderingContext.texImage2D()", "texImage2D()")}} para escribir la informacion de la imagen en la textura.

+ +
Nota: El alto y hancho de las texturas deben, en la mayoría de las circunstancias, ser una potencia de dos píxeles (es decir, 1, 2, 4, 8, 16, etc.) en cada dimensión. Para la excepción, vea la sección: "Texturas no potencia de dos", a continuación.
+ +

Las siguientes dos líneas setean el filtrado para la textura; Esto controla cómo se filtra la imagen mientras se escala. En este caso estamos usando linear filtering cuando escala la imagen, y mipmap cuando se hace mas pequeña. Entonces el mipmap es generado llamando {{domxref("WebGLRenderingContext.generateMipMap()", "generateMipMap()")}}, Y terminamos diciéndole a WebGL que hemos terminado de manipular la textura vinculando null a gl.TEXTURE_2D.

+ +

Texturas no potencia-de-dos

+ +

Generalmente hablando, Utilizar texturas cuyos lados son una potencia de dos es ideal. Están almacenadas eficientemente en la memoria de video y no están restringidas en cómo podrían ser utilizadas. Las texturas creadas por el artista deben ser escaladas hacia arriba o hacia abajo a una potencia cercana a dos y, realmente, debería haber sido creada en potencia-de-dos para empezar. Cada lado debe ser: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 ó 2048 píxeles. Muchos dispositivos, pero no todos, pueden soportar 4096 píxeles; Algunos pueden soportar 8192 e incluso más.

+ +

Ocasionalmente, es difícil utilizar texturas en potencia-de-dos dada una circunstancia especifica. Si la fuente es alguna tercera parte, A menudo los mejores resultados vienen de modificar las imágenes usando canvas HTML5 en tamaños de potencia-de-dos antes de que se pasen a WebGL; Las coordenadas UV también pueden requerir ajuste si el estiramiento es notorio.

+ +

Pero, Si tiene que tener una textura no-potencia-de-dos (NPOT = no-power-of-two), WebGL incluye un limitado soporte nativo. Las texturas NPOT son en su mayoría útiles si las dimensiones de la textura debe ser la misma resolución que otra cosa, como la resolución de tu monitor, o si no vale la pena molestarse por las sugerencias anteriores. Resumiendo: estas texturas no se pueden usar con mipmapping y no deben repetirse (tile o wrap).

+ +

Un ejemplo de una textura es tilear una imagen de unos ladrillos para cubrir una pared de ladrillos.

+ +

Mipmapping y "UV tiling" pueden ser desactivados utilizando {{domxref("WebGLRenderingContext.texParameter()", "texParameteri()")}} y cuando creas tu textura utilizando {{domxref("WebGLRenderingContext.bindTexture()", "bindTexture()")}}. Ésto permitirá las texturas NPOT a expensas de mipmapping, UV wrapping, UV tiling, y tu control sobre cómo el dispositivo procederá a manejar tu textura.

+ +
// gl.NEAREST is also allowed, instead of gl.LINEAR, as neither mipmap.
+gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+// Prevents s-coordinate wrapping (repeating).
+gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+// Prevents t-coordinate wrapping (repeating).
+gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+ +

Una vez más, con estos parámetros, los dispositivos compatibles con WebGL aceptarán automáticamente cualquier resolución para esa textura (hasta sus dimensiones máximas). Sin realizar la configuración anterior, WebGL requiere que todas las muestras de texturas NPOT fallen al devolver el color "negro sólido": rgba (0,0,0,1).

+ +

Mapeando la textura en las caras

+ +

A este punto, la textura esta cargada y lista para usar. pero antes de utilizarla, Necesitamos asignar el mapeo de las coordenadas de textura a los vértices de las caras de nuestro cubo. Esto reemplaza todo el código previamente existente para configurar colores para cada una de las caras del cubo en initBuffers().

+ +
cubeVerticesTextureCoordBuffer = gl.createBuffer();
+gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesTextureCoordBuffer);
+
+var textureCoordinates = [
+  // Front
+  0.0,  0.0,
+  1.0,  0.0,
+  1.0,  1.0,
+  0.0,  1.0,
+  // Back
+  0.0,  0.0,
+  1.0,  0.0,
+  1.0,  1.0,
+  0.0,  1.0,
+  // Top
+  0.0,  0.0,
+  1.0,  0.0,
+  1.0,  1.0,
+  0.0,  1.0,
+  // Bottom
+  0.0,  0.0,
+  1.0,  0.0,
+  1.0,  1.0,
+  0.0,  1.0,
+  // Right
+  0.0,  0.0,
+  1.0,  0.0,
+  1.0,  1.0,
+  0.0,  1.0,
+  // Left
+  0.0,  0.0,
+  1.0,  0.0,
+  1.0,  1.0,
+  0.0,  1.0
+];
+
+gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoordinates),
+              gl.STATIC_DRAW);
+
+ +

Primeramente, este codigo crea un GL buffer en el cual almacenaremos las coordenadas de la textura para cada cara, luego enlazamos ese buffer como el array en el cual escribiremos.

+ +

El array textureCoordinates define las coordenadas de textura correspondientes a cada vértice de cada cara. Tenga en cuenta que las coordenadas de textura van de 0,0 a 1,0; Las dimensiones de las texturas se normalizan a un rango de 0,0 a 1,0 independientemente de su tamaño real, con el propósito de mapeo de textura.

+ +

Una vez que hemos seteado la matriz de mapeo de textura, pasamos la matriz al búfer, de modo que GL tiene esos datos listos para su uso.

+ +

Actualizando los shaders

+ +

El shader -- y el código que inicializa los shaders -- también necesita ser actualizado para utilizar la textura en vez de un color solido.

+ +

Primero, echemos un vistazo a un cambio muy sencillo que se necesita en initShaders():

+ +
textureCoordAttribute = gl.getAttribLocation(shaderProgram, 'aTextureCoord');
+gl.enableVertexAttribArray(textureCoordAttribute);
+gl.vertexAttribPointer(texCoordAttribute, 2, gl.FLOAT, false, 0, 0);
+
+ +

Esto reemplaza el código que setea el atributo "vertex color" (color del vertice) con uno que contiene la coordenada de textura para cada vértice.

+ +

El vertex shader

+ +

A continuación, necesitamos reemplazar el "vertex shader" de modo que en lugar de buscar datos de color, busque los datos de coordenadas de textura.

+ +
<script id="shader-vs" type="x-shader/x-vertex">
+  attribute vec3 aVertexPosition;
+  attribute vec2 aTextureCoord;
+
+  uniform mat4 uMVMatrix;
+  uniform mat4 uPMatrix;
+
+  varying highp vec2 vTextureCoord;
+
+  void main(void) {
+    gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
+    vTextureCoord = aTextureCoord;
+  }
+</script>
+
+ +

El cambio clave aquí es que en lugar de buscar el color del vértice (vertex color), estamos estableciendo las coordenadas de textura; Esto indicará la ubicación dentro de la textura correspondiente al vértice.

+ +

El fragment shader

+ +

El fragment shader también debe actualizarse:

+ +
<script id="shader-fs" type="x-shader/x-fragment">
+  varying highp vec2 vTextureCoord;
+
+  uniform sampler2D uSampler;
+
+  void main(void) {
+    gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
+  }
+</script>
+
+ +

En lugar de asignar un valor de color al fragment color, el fragment color se calcula recolectando el texel (es decir, el píxel dentro de la textura) que el muestreador dice que se corresponde mejor con la posición del fragment.

+ +

Dibujando el cubo texturado

+ +

El cambio a la función drawScene() es simple (excepto que por razones de claridad, he eliminado el código que hace que el cubo se traslade a través del espacio mientras se anima, en vez de eso, solo gira).

+ +

El código para mapear colores a la textura se ha ido, sustituido por esto:

+ +
gl.activeTexture(gl.TEXTURE0);
+gl.bindTexture(gl.TEXTURE_2D, cubeTexture);
+gl.uniform1i(gl.getUniformLocation(shaderProgram, 'uSampler'), 0);
+
+ +

GL proporciona 32 registros de textura; La primera de ellas es gl.TEXTURE0. Vincularemos nuestra textura previamente cargada a ese registro, a continuación, establecremos el shader sampler uSampler (especificado en el shader) para utilizar esa textura.

+ +

En este punto, el cubo giratorio debe estar listo.

+ +

{{EmbedGHLiveSample('webgl-examples/tutorial/sample6/index.html', 670, 510) }}

+ +

Ver el código completo | Abrir esta demo en una nueva pestaña

+ +

Texturas entre dominios

+ +

La carga de texturas WebGL esta sujeta a controles de acceso entre dominios. Para que su contenido cargue una textura de otro dominio, La aprobacion CORS debe ser obtenida. Ver control de acceso HTTP para mas detalles sobre CORS.

+ +

Ver este articulo hacks.mozilla.org para una explicacion de como usar imágenes CORS-approved como texturas WebGL , con un ejemplo auto-contenido.

+ +
+

Nota: El soporte CORS para texturas WebGL y el atributo crossOrigin para elementos de imagen se implementan en {{Gecko ("8.0")}}.

+
+ +

Canvas 2D contaminados (Solo lectura) no pueden ser utilizados como texturas WebGL. una 2D {{ HTMLElement("canvas") }} se convierte en contaminada, por ejemplo, cuando una imagen de dominio cruzado (cross-domain) es dibujada en el.

+ +
+

Nota: El soporte de CORS para Canvas 2D drawImage se implementa en {{Gecko ("9.0")}}. Esto significa que el uso de una imagen de dominio cruzado con aprobación de CORS ya no pinta el lienzo 2D, por lo que el lienzo 2D sigue siendo utilizable como fuente de una textura WebGL. 

+
+ +
+

Nota: El soporte de CORS para videos de dominio cruzado y el atributo de crossorigin para elementos {{HTMLElement("video")}} se implementa en {{Gecko ("12.0")}}.

+
+ +

{{PreviousNext("Web/API/WebGL_API/Tutorial/Creating_3D_objects_using_WebGL", "Web/API/WebGL_API/Tutorial/Lighting_in_WebGL")}}

diff --git a/files/es/web/api/webgl_api/tutorial/wtilizando_texturas_en_webgl/index.html b/files/es/web/api/webgl_api/tutorial/wtilizando_texturas_en_webgl/index.html deleted file mode 100644 index 9d2be2d61b..0000000000 --- a/files/es/web/api/webgl_api/tutorial/wtilizando_texturas_en_webgl/index.html +++ /dev/null @@ -1,209 +0,0 @@ ---- -title: Utilizando texturas en WebGL -slug: Web/API/WebGL_API/Tutorial/Wtilizando_texturas_en_WebGL -tags: - - Texturas - - Tutorial - - WebGL -translation_of: Web/API/WebGL_API/Tutorial/Using_textures_in_WebGL ---- -

{{WebGLSidebar("Tutorial")}} {{PreviousNext("Web/API/WebGL_API/Tutorial/Creating_3D_objects_using_WebGL", "Web/API/WebGL_API/Tutorial/Lighting_in_WebGL")}}

- -

Ahora que nuestro programa de prueba tiene un cubo, asignemos una textura en lugar de tener sus caras de un color solido.

- -

Cargando texturas

- -

La primera cosa que debemos hacer es añadir el codigo para cargar nuestra textura. en nuestro caso, estaremos usando una unica textura, asignada en las seis caras de nuestro cubo rotador, pero la misma tecnica puede ser utilizada para cualquier cantidad de texturas.

- -
Note: Es importante señalar que la carga de texturas sigue reglas de dominio-cruzado; Es decir, sólo puede cargar texturas de sitios para los que su contenido tiene aprobación de CORS. Vea las texturas entre dominios a continuación para más detalles.
- -

El codigo que carga la textura se ve como esto:

- -
function initTextures() {
-  cubeTexture = gl.createTexture();
-  cubeImage = new Image();
-  cubeImage.onload = function() { handleTextureLoaded(cubeImage, cubeTexture); }
-  cubeImage.src = 'cubetexture.png';
-}
-
-function handleTextureLoaded(image, texture) {
-  gl.bindTexture(gl.TEXTURE_2D, texture);
-  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
-  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
-  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
-  gl.generateMipmap(gl.TEXTURE_2D);
-  gl.bindTexture(gl.TEXTURE_2D, null);
-}
-
- -

La rutina initTextures() comienza por crear el GL texture cubeTexture llamando la rutina GL {{domxref("WebGLRenderingContext.createTexture()", "createTexture()")}}. Para cargar la textura desde un archivo de imagen, este luego crea un Objeto Imagen y carga en él el archivo de imagen que deseamos utilizar como nuestra textura. La rutina handleTextureLoaded() corre cuando la textura ha terminado de cargar.

- -

Para realmente crear la textura, especificamos que la nueva textura es la textura actual en la que queremos operar vinculándola a gl.TEXTURE_2D. Despues de esto, la imagen cargada es pasada a  {{domxref("WebGLRenderingContext.texImage2D()", "texImage2D()")}} para escribir la informacion de la imagen en la textura.

- -
Nota: El alto y hancho de las texturas deben, en la mayoría de las circunstancias, ser una potencia de dos píxeles (es decir, 1, 2, 4, 8, 16, etc.) en cada dimensión. Para la excepción, vea la sección: "Texturas no potencia de dos", a continuación.
- -

Las siguientes dos líneas setean el filtrado para la textura; Esto controla cómo se filtra la imagen mientras se escala. En este caso estamos usando linear filtering cuando escala la imagen, y mipmap cuando se hace mas pequeña. Entonces el mipmap es generado llamando {{domxref("WebGLRenderingContext.generateMipMap()", "generateMipMap()")}}, Y terminamos diciéndole a WebGL que hemos terminado de manipular la textura vinculando null a gl.TEXTURE_2D.

- -

Texturas no potencia-de-dos

- -

Generalmente hablando, Utilizar texturas cuyos lados son una potencia de dos es ideal. Están almacenadas eficientemente en la memoria de video y no están restringidas en cómo podrían ser utilizadas. Las texturas creadas por el artista deben ser escaladas hacia arriba o hacia abajo a una potencia cercana a dos y, realmente, debería haber sido creada en potencia-de-dos para empezar. Cada lado debe ser: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 ó 2048 píxeles. Muchos dispositivos, pero no todos, pueden soportar 4096 píxeles; Algunos pueden soportar 8192 e incluso más.

- -

Ocasionalmente, es difícil utilizar texturas en potencia-de-dos dada una circunstancia especifica. Si la fuente es alguna tercera parte, A menudo los mejores resultados vienen de modificar las imágenes usando canvas HTML5 en tamaños de potencia-de-dos antes de que se pasen a WebGL; Las coordenadas UV también pueden requerir ajuste si el estiramiento es notorio.

- -

Pero, Si tiene que tener una textura no-potencia-de-dos (NPOT = no-power-of-two), WebGL incluye un limitado soporte nativo. Las texturas NPOT son en su mayoría útiles si las dimensiones de la textura debe ser la misma resolución que otra cosa, como la resolución de tu monitor, o si no vale la pena molestarse por las sugerencias anteriores. Resumiendo: estas texturas no se pueden usar con mipmapping y no deben repetirse (tile o wrap).

- -

Un ejemplo de una textura es tilear una imagen de unos ladrillos para cubrir una pared de ladrillos.

- -

Mipmapping y "UV tiling" pueden ser desactivados utilizando {{domxref("WebGLRenderingContext.texParameter()", "texParameteri()")}} y cuando creas tu textura utilizando {{domxref("WebGLRenderingContext.bindTexture()", "bindTexture()")}}. Ésto permitirá las texturas NPOT a expensas de mipmapping, UV wrapping, UV tiling, y tu control sobre cómo el dispositivo procederá a manejar tu textura.

- -
// gl.NEAREST is also allowed, instead of gl.LINEAR, as neither mipmap.
-gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
-// Prevents s-coordinate wrapping (repeating).
-gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
-// Prevents t-coordinate wrapping (repeating).
-gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
- -

Una vez más, con estos parámetros, los dispositivos compatibles con WebGL aceptarán automáticamente cualquier resolución para esa textura (hasta sus dimensiones máximas). Sin realizar la configuración anterior, WebGL requiere que todas las muestras de texturas NPOT fallen al devolver el color "negro sólido": rgba (0,0,0,1).

- -

Mapeando la textura en las caras

- -

A este punto, la textura esta cargada y lista para usar. pero antes de utilizarla, Necesitamos asignar el mapeo de las coordenadas de textura a los vértices de las caras de nuestro cubo. Esto reemplaza todo el código previamente existente para configurar colores para cada una de las caras del cubo en initBuffers().

- -
cubeVerticesTextureCoordBuffer = gl.createBuffer();
-gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesTextureCoordBuffer);
-
-var textureCoordinates = [
-  // Front
-  0.0,  0.0,
-  1.0,  0.0,
-  1.0,  1.0,
-  0.0,  1.0,
-  // Back
-  0.0,  0.0,
-  1.0,  0.0,
-  1.0,  1.0,
-  0.0,  1.0,
-  // Top
-  0.0,  0.0,
-  1.0,  0.0,
-  1.0,  1.0,
-  0.0,  1.0,
-  // Bottom
-  0.0,  0.0,
-  1.0,  0.0,
-  1.0,  1.0,
-  0.0,  1.0,
-  // Right
-  0.0,  0.0,
-  1.0,  0.0,
-  1.0,  1.0,
-  0.0,  1.0,
-  // Left
-  0.0,  0.0,
-  1.0,  0.0,
-  1.0,  1.0,
-  0.0,  1.0
-];
-
-gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoordinates),
-              gl.STATIC_DRAW);
-
- -

Primeramente, este codigo crea un GL buffer en el cual almacenaremos las coordenadas de la textura para cada cara, luego enlazamos ese buffer como el array en el cual escribiremos.

- -

El array textureCoordinates define las coordenadas de textura correspondientes a cada vértice de cada cara. Tenga en cuenta que las coordenadas de textura van de 0,0 a 1,0; Las dimensiones de las texturas se normalizan a un rango de 0,0 a 1,0 independientemente de su tamaño real, con el propósito de mapeo de textura.

- -

Una vez que hemos seteado la matriz de mapeo de textura, pasamos la matriz al búfer, de modo que GL tiene esos datos listos para su uso.

- -

Actualizando los shaders

- -

El shader -- y el código que inicializa los shaders -- también necesita ser actualizado para utilizar la textura en vez de un color solido.

- -

Primero, echemos un vistazo a un cambio muy sencillo que se necesita en initShaders():

- -
textureCoordAttribute = gl.getAttribLocation(shaderProgram, 'aTextureCoord');
-gl.enableVertexAttribArray(textureCoordAttribute);
-gl.vertexAttribPointer(texCoordAttribute, 2, gl.FLOAT, false, 0, 0);
-
- -

Esto reemplaza el código que setea el atributo "vertex color" (color del vertice) con uno que contiene la coordenada de textura para cada vértice.

- -

El vertex shader

- -

A continuación, necesitamos reemplazar el "vertex shader" de modo que en lugar de buscar datos de color, busque los datos de coordenadas de textura.

- -
<script id="shader-vs" type="x-shader/x-vertex">
-  attribute vec3 aVertexPosition;
-  attribute vec2 aTextureCoord;
-
-  uniform mat4 uMVMatrix;
-  uniform mat4 uPMatrix;
-
-  varying highp vec2 vTextureCoord;
-
-  void main(void) {
-    gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
-    vTextureCoord = aTextureCoord;
-  }
-</script>
-
- -

El cambio clave aquí es que en lugar de buscar el color del vértice (vertex color), estamos estableciendo las coordenadas de textura; Esto indicará la ubicación dentro de la textura correspondiente al vértice.

- -

El fragment shader

- -

El fragment shader también debe actualizarse:

- -
<script id="shader-fs" type="x-shader/x-fragment">
-  varying highp vec2 vTextureCoord;
-
-  uniform sampler2D uSampler;
-
-  void main(void) {
-    gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
-  }
-</script>
-
- -

En lugar de asignar un valor de color al fragment color, el fragment color se calcula recolectando el texel (es decir, el píxel dentro de la textura) que el muestreador dice que se corresponde mejor con la posición del fragment.

- -

Dibujando el cubo texturado

- -

El cambio a la función drawScene() es simple (excepto que por razones de claridad, he eliminado el código que hace que el cubo se traslade a través del espacio mientras se anima, en vez de eso, solo gira).

- -

El código para mapear colores a la textura se ha ido, sustituido por esto:

- -
gl.activeTexture(gl.TEXTURE0);
-gl.bindTexture(gl.TEXTURE_2D, cubeTexture);
-gl.uniform1i(gl.getUniformLocation(shaderProgram, 'uSampler'), 0);
-
- -

GL proporciona 32 registros de textura; La primera de ellas es gl.TEXTURE0. Vincularemos nuestra textura previamente cargada a ese registro, a continuación, establecremos el shader sampler uSampler (especificado en el shader) para utilizar esa textura.

- -

En este punto, el cubo giratorio debe estar listo.

- -

{{EmbedGHLiveSample('webgl-examples/tutorial/sample6/index.html', 670, 510) }}

- -

Ver el código completo | Abrir esta demo en una nueva pestaña

- -

Texturas entre dominios

- -

La carga de texturas WebGL esta sujeta a controles de acceso entre dominios. Para que su contenido cargue una textura de otro dominio, La aprobacion CORS debe ser obtenida. Ver control de acceso HTTP para mas detalles sobre CORS.

- -

Ver este articulo hacks.mozilla.org para una explicacion de como usar imágenes CORS-approved como texturas WebGL , con un ejemplo auto-contenido.

- -
-

Nota: El soporte CORS para texturas WebGL y el atributo crossOrigin para elementos de imagen se implementan en {{Gecko ("8.0")}}.

-
- -

Canvas 2D contaminados (Solo lectura) no pueden ser utilizados como texturas WebGL. una 2D {{ HTMLElement("canvas") }} se convierte en contaminada, por ejemplo, cuando una imagen de dominio cruzado (cross-domain) es dibujada en el.

- -
-

Nota: El soporte de CORS para Canvas 2D drawImage se implementa en {{Gecko ("9.0")}}. Esto significa que el uso de una imagen de dominio cruzado con aprobación de CORS ya no pinta el lienzo 2D, por lo que el lienzo 2D sigue siendo utilizable como fuente de una textura WebGL. 

-
- -
-

Nota: El soporte de CORS para videos de dominio cruzado y el atributo de crossorigin para elementos {{HTMLElement("video")}} se implementa en {{Gecko ("12.0")}}.

-
- -

{{PreviousNext("Web/API/WebGL_API/Tutorial/Creating_3D_objects_using_WebGL", "Web/API/WebGL_API/Tutorial/Lighting_in_WebGL")}}

diff --git a/files/es/web/api/webrtc_api/session_lifetime/index.html b/files/es/web/api/webrtc_api/session_lifetime/index.html new file mode 100644 index 0000000000..2529f32085 --- /dev/null +++ b/files/es/web/api/webrtc_api/session_lifetime/index.html @@ -0,0 +1,21 @@ +--- +title: WebRTC Introduction +slug: WebRTC/Introduction +translation_of: Web/API/WebRTC_API/Session_lifetime +--- +
+

WebRTC te permite establecer una comunicación par-a-par en una aplicación del navegador.

+
+

Estableciendo la conexión

+

La conexión inicial entre pares debe ser establecida a través de un servidor de aplicaciones que proporcione descubrimiento de usuarios, comunicación y traducción de direcciones de red (NAT) con transmisión de datos.

+

Señalización

+

Señalización es el mecanismo por el cual los pares se envían mensajes de control entre sí con el propósito de establecer el protocolo, canal, y método de comunicación. Estos no están especifiados en el standar WebRTC. En su lugar, el desarrollador puede elegir cualquier protocolo de mensajería (como SIP o XMPP), y cualquier canal de comunicación bidirecional (como WebSocket o XMLHttpRequest) en conjunción con un servidor con un API de  conexión permanente (como  el Google Channel API) para AppEngine.

+

Transmisión

+

getUserMedia

+

LocalMediaStream object

+

Recepción

+

El soporte para WebRTC en Firefox está escondido detrás de un selector de preferencias. Ve a about:config y establce 'media.navigator.enabled' a 'true'.

+
+

Hay algunos archivos de prueba en el repositorio de código para darte una idea de cómo funciona. Ve: dom/media/tests/local_video_test.html. Por favor, prueba también lademo de servicio de llamadas, la página de su código, y el código de su servidor.

+
+

 

diff --git a/files/es/web/api/webrtc_api/taking_still_photos/index.html b/files/es/web/api/webrtc_api/taking_still_photos/index.html new file mode 100644 index 0000000000..143b2ee72f --- /dev/null +++ b/files/es/web/api/webrtc_api/taking_still_photos/index.html @@ -0,0 +1,159 @@ +--- +title: Capturar fotografías con la cámara web +slug: WebRTC/Taking_webcam_photos +tags: + - Canvas + - WebRTC + - cámara web + - getusermedia +translation_of: Web/API/WebRTC_API/Taking_still_photos +--- +

Introducción y demostración

+

Este es un tutorial rápido de cómo acceder a la cámara de tu laptop y capturar una foto con ella. Puedes observar el código final en acción en este JSFiddle. También existe una versión más avanzada en JavaScript para cargar fotos a imgur disponible como código en GitHub o como demo.

+

El formato HTML

+

Lo primero que necesitas para acceder a la cámara web utilizando WebRTC es un elemento {{HTMLElement("video")}} y un elemento {{HTMLElement("canvas")}} en la página. El elemento de video recibe la secuencia desde WebRTC y el elemento canvas es utilizado para agarrar la imagen desde el video. También añadimos una imagen de relleno que luego será reemplazada con la toma capturada por la cámara web.

+
<video id="video"></video>
+<button id="startbutton">Take photo</button>
+<canvas id="canvas"></canvas>
+<img src="http://placekitten.com/g/320/261" id="photo" alt="photo">
+
+

El script completo

+

Aquí se muestra completamente el código JavaScript. Más abajo, explicaremos gradualmente cada sección con más detalle.

+
(function() {
+
+  var streaming = false,
+      video        = document.querySelector('#video'),
+      canvas       = document.querySelector('#canvas'),
+      photo        = document.querySelector('#photo'),
+      startbutton  = document.querySelector('#startbutton'),
+      width = 320,
+      height = 0;
+
+  navigator.getMedia = ( navigator.getUserMedia ||
+                         navigator.webkitGetUserMedia ||
+                         navigator.mozGetUserMedia ||
+                         navigator.msGetUserMedia);
+
+  navigator.getMedia(
+    {
+      video: true,
+      audio: false
+    },
+    function(stream) {
+      if (navigator.mozGetUserMedia) {
+        video.mozSrcObject = stream;
+      } else {
+        var vendorURL = window.URL || window.webkitURL;
+        video.src = vendorURL.createObjectURL(stream);
+      }
+      video.play();
+    },
+    function(err) {
+      console.log("An error occured! " + err);
+    }
+  );
+
+  video.addEventListener('canplay', function(ev){
+    if (!streaming) {
+      height = video.videoHeight / (video.videoWidth/width);
+      video.setAttribute('width', width);
+      video.setAttribute('height', height);
+      canvas.setAttribute('width', width);
+      canvas.setAttribute('height', height);
+      streaming = true;
+    }
+  }, false);
+
+  function takepicture() {
+    canvas.width = width;
+    canvas.height = height;
+    canvas.getContext('2d').drawImage(video, 0, 0, width, height);
+    var data = canvas.toDataURL('image/png');
+    photo.setAttribute('src', data);
+  }
+
+  startbutton.addEventListener('click', function(ev){
+      takepicture();
+    ev.preventDefault();
+  }, false);
+
+})();
+

Explicando paso a paso

+

Entonces, ¿Qué es lo que sucede aquí? Lo analizaremos paso por paso.

+

Función Anónima

+
(function() {
+
+  var streaming = false,
+      video        = document.querySelector('#video'),
+      canvas       = document.querySelector('#canvas'),
+      photo        = document.querySelector('#photo'),
+      startbutton  = document.querySelector('#startbutton'),
+      width = 320,
+      height = 0;
+

Empezamos por encerrar el script entero en una función anónima para evitar las variables globales. Tomamos los elementos HTML que necesitamos y definimos el ancho (width) del video a 320 y la altura (height) a 0, ya que calcularemos la altura apropiada posteriormente.

+
+

En estos momentos existe una diferencia entre los tamaños de video proporcionados por getUserMedia. Firefox Nightly utiliza una resolución de 352x288 y Opera y Chrome utiliza una resolución de 640x400. Esto cambiará en el futuro, pero cambiando el tamaño con la proporción que usaremos más abajo, nos aseguraremos de no obtener sorpresas desagradables.

+
+

Obtener el video

+

Ahora necesitamos obtener el video desde la cámara web. Actualmente esto está predeterminado para los diferentes navegadores, así que necesitamos comprobar cuál es compatible:

+
  navigator.getMedia = ( navigator.getUserMedia ||
+                         navigator.webkitGetUserMedia ||
+                         navigator.mozGetUserMedia ||
+                         navigator.msGetUserMedia);
+

Le solicitamos al navegador que nos dé un video sin audio y obtenemos una secuencia (stream) en la función de retrollamada:

+
  navigator.getMedia(
+    {
+      video: true,
+      audio: false
+    },
+    function(stream) {
+      if (navigator.mozGetUserMedia) {
+        video.mozSrcObject = stream;
+      } else {
+        var vendorURL = window.URL || window.webkitURL;
+        video.src = vendorURL.createObjectURL(stream);
+      }
+      video.play();
+    },
+    function(err) {
+      console.log("An error occured! " + err);
+    }
+  );
+

En estos momentos Firefox Nightly necesita que tu configures la propiedad de mozSrcObject del elemento del video con el fin de reproducirlo; para otros navegadores, configura el atributo src. Mientras que Firefox puede utilizar la secuencia de video directamente, Webkit y Opera necesitan crear un objeto URL desde ella. Todo esto será estandarizado en un futuro cercano.

+

Redefinir el tamaño del video

+

Luego necesitamos configurar el tamaño del video a las dimensiones deseadas.

+
  video.addEventListener('canplay', function(ev){
+    if (!streaming) {
+      height = video.videoHeight / (video.videoWidth/width);
+      video.setAttribute('width', width);
+      video.setAttribute('height', height);
+      canvas.setAttribute('width', width);
+      canvas.setAttribute('height', height);
+      streaming = true;
+    }
+  }, false);
+

Nos subscribimos al evento canplay del video y leemos sus dimensiones utilizando videoHeight y videoWidth. Estas no están disponible realmente hasta que el evento sea iniciado. Establecemos streaming a verdadero (true) para que compruebe esto solo una vez, mientras que el evento canplay  siga en actividad.

+

Esto es todo lo que se necesita para que inicie el video.

+

Capturar una imagen

+

Ahora necesitamos capturar una imagen utilizando un lienzo (canvas). Asignamos un manejador de eventos al botón de inicio para llamar a la función de takepicture.

+
  startbutton.addEventListener('click', function(ev){
+      takepicture();
+    ev.preventDefault();
+  }, false);
+

En esta función, reestablecemos el tamaño del lienzo (canvas) a las dimensiones del video, el cual lo sustituye y tenemos un marco del video, el cual se copia al canvas. Luego necesitamos convertir los datos del canvas en datos tipo URL con un encabezado PNG, y establecer el src de la fotografía a este mismo url.

+
  function takepicture() {
+    canvas.width = width;
+    canvas.height = height;
+    canvas.getContext('2d').drawImage(video, 0, 0, width, height);
+    var data = canvas.toDataURL('image/png');
+    photo.setAttribute('src', data);
+  }
+
+})();
+

Eso es todo lo que se necesita para mostrar la secuencia de la cámara web y capturar una imagen fija de ella, a través de Chrome, Opera y Firefox.

+

Compatibilidad

+

Actualmente utilizar WebRTC para acceder a la cámara es compatible en Chrome, Opera y Firefox Nightly 18. Para habilitar WebRTC en Firefox Nightly requiere que establezcas un indicador en la configuración:

+ diff --git a/files/es/web/api/websockets_api/escribiendo_servidor_websocket/index.html b/files/es/web/api/websockets_api/escribiendo_servidor_websocket/index.html deleted file mode 100644 index 333e8e8830..0000000000 --- a/files/es/web/api/websockets_api/escribiendo_servidor_websocket/index.html +++ /dev/null @@ -1,244 +0,0 @@ ---- -title: Escribiendo un servidor WebSocket en C# -slug: Web/API/WebSockets_API/Escribiendo_servidor_WebSocket -tags: - - HTML5 - - Tutorial - - WebSockets -translation_of: Web/API/WebSockets_API/Writing_WebSocket_server ---- -

Introducción

- -

Si deseas utilizar la API WebSocket, es conveniente si tienes un servidor. En este artículo te mostraré como puedes escribir uno en C#. Tú puedes hacer esto en cualquier lenguaje del lado del servidor, pero para mantener las cosas simples y más comprensibles, elegí el lenguaje de Microsoft.

- -

Este servidor se ajusta a RFC 6455 por lo que solo manejará las conexiones de Chrome version 16, Firefox 11, IE 10 and superiores.

- -

Primeros pasos

- -

WebSocket se comunica a través de conexiones TCP (Transmission Control Protocol), afortunadamente C# tiene una clase TcpListener la cual hace lo que su nombre sugiere. Esta se encuentra en el namespace System.Net.Sockets.

- -
-

Es una buena idea usar la instrucción using para escribir menos. Eso significa que no tendrás que re escribir el namespace de nuevo en cada ocasión.

-
- -

TcpListener

- -

Constructor:

- -
TcpListener(System.Net.IPAddress localaddr, int port)
- -

localaddr especifica la IP a escuchar y port especifica el puerto.

- -
-

Para crear un objeto IPAddress desde un string, usa el método estático Parse de IPAddres.

-
- -

Métodos:

- - - -

Aquí está como utilizar lo que hemos aprendido:

- -
​using System.Net.Sockets;
-using System.Net;
-using System;
-
-class Server {
-    public static void Main() {
-        TcpListener server = new TcpListener(IPAddress.Parse("127.0.0.1"), 80);
-
-        server.Start();
-        Console.WriteLine("El server se ha iniciado en 127.0.0.1:80.{0}Esperando una conexión...", Environment.NewLine);
-
-        TcpClient client = server.AcceptTcpClient();
-
-        Console.WriteLine("Un cliente conectado.");
-    }
-}
-
- -

TcpClient

- -

Métodos:

- - - -

Propiedades:

- - - -

NetworkStream

- -

Métodos:

- -
Write(Byte[] buffer, int offset, int size)
- -

Escribe bytes desde el buffer; el offset y el size determinan la longitud del mensaje.

- -
Read(Byte[] buffer, int offset, int size)
- -

Lee bytes al buffer; el offset y el size determinan la longitud del mensaje.

- -

Ampliemos nuestro ejemplo anterior.

- -
TcpClient client = server.AcceptTcpClient();
-
-Console.WriteLine("Un cliente conectado.");
-
-NetworkStream stream = client.GetStream();
-
-//enter to an infinite cycle to be able to handle every change in stream
-while (true) {
-    while (!stream.DataAvailable);
-
-    Byte[] bytes = new Byte[client.Available];
-
-    stream.Read(bytes, 0, bytes.Length);
-}
- -

Handshaking

- -

Cuando un cliente se conecta al servidor, envía una solicitud GET para actualizar la conexión al WebSocket desde una simple petición HTTP. Esto es conocido como handshaking.

- -

Este código de ejemplo detecta el GET desde el cliente. Nota que esto bloqueará hasta los 3 primeros bytes del mensaje disponible. Soluciones alternativas deben ser investigadas para ambientes de producción.

- -
using System.Text;
-using System.Text.RegularExpressions;
-
-while(client.Available < 3)
-{
-   // wait for enough bytes to be available
-}
-
-Byte[] bytes = new Byte[client.Available];
-
-stream.Read(bytes, 0, bytes.Length);
-
-//translate bytes of request to string
-String data = Encoding.UTF8.GetString(bytes);
-
-if (Regex.IsMatch(data, "^GET")) {
-
-} else {
-
-}
- -

Esta respuesta es fácil de construir, pero puede ser un poco díficil de entender. La explicación completa del handshake al servidor puede encontrarse en  RFC 6455, section 4.2.2. Para nuestros propósitos, solo construiremos una respuesta simple.

- -

Debes:

- -
    -
  1. Obtener el valor de "Sec-WebSocket-Key" sin espacios iniciales ni finales de el encabezado de la solicitud
  2. -
  3. Concatenarlo con "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
  4. -
  5. Calcular el código SHA-1 y Base64
  6. -
  7. Escribe el valor Sec-WebSocket-Accept en el encabezado como parte de la respuesta HTTP.
  8. -
- -
if (new Regex("^GET").IsMatch(data)) {
-    Byte[] response = Encoding.UTF8.GetBytes("HTTP/1.1 101 Switching Protocols" + Environment.NewLine
-        + "Connection: Upgrade" + Environment.NewLine
-        + "Upgrade: websocket" + Environment.NewLine
-        + "Sec-WebSocket-Accept: " + Convert.ToBase64String (
-            SHA1.Create().ComputeHash (
-                Encoding.UTF8.GetBytes (
-                    new Regex("Sec-WebSocket-Key: (.*)").Match(data).Groups[1].Value.Trim() + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
-                )
-            )
-        ) + Environment.NewLine
-        + Environment.NewLine);
-
-    stream.Write(response, 0, response.Length);
-}
-
- -

Decoding messages

- -

Luego de un handshake exitoso el cliente puede enviar mensajes al servidor, pero estos serán codificados.

- -

Si nosotros enviamos "MDN", obtendremos estos bytes:

- - - - - - - - - - - - - - - -
129131618435611216109
- -

- 129:

- - - - - - - - - - - - - - - - - - - - -
FIN (¿Es el mensaje completo?)RSV1RSV2RSV3Opcode
10000x1=0001
- -

FIN: Puedes enviar tu mensaje en marcos, pero ahora debe mantener las cosas simples.
- Opcode 0x1 significa que es un texto. Lista completa de Opcodes

- -

- 131:

- -

Si el segundo byte menos 128 se encuentra entre 0 y 125, esta es la longitud del mensaje. Si es 126, los siguientes 2 bytes (entero sin signo de 16 bits), si es 127, los siguientes 8 bytes (entero sin signo de 64 bits) son la longitud.

- -
-

Puedo tomar 128, porque el primer bit siempre es 1.

-
- -

- 61, 84, 35 y 6 son los bytes de la clave a decodificar. Cambian en cada oportunidad.

- -

- Los bytes codificados restantes son el mensaje.

- -

Algoritmo de decodificación

- -

byte decodificado = byte codificado XOR (posición del byte codificado Mod 4) byte de la clave

- -

Ejemplo en C#:

- -
Byte[] decoded = new Byte[3];
-Byte[] encoded = new Byte[3] {112, 16, 109};
-Byte[] key = Byte[4] {61, 84, 35, 6};
-
-for (int i = 0; i < encoded.Length; i++) {
-    decoded[i] = (Byte)(encoded[i] ^ key[i % 4]);
-}
- -

Relacionado

- - - -
 
diff --git a/files/es/web/api/websockets_api/escribiendo_servidores_con_websocket/index.html b/files/es/web/api/websockets_api/escribiendo_servidores_con_websocket/index.html deleted file mode 100644 index 1261f75bec..0000000000 --- a/files/es/web/api/websockets_api/escribiendo_servidores_con_websocket/index.html +++ /dev/null @@ -1,246 +0,0 @@ ---- -title: Escribir servidores WebSocket -slug: Web/API/WebSockets_API/Escribiendo_servidores_con_WebSocket -translation_of: Web/API/WebSockets_API/Writing_WebSocket_servers ---- -

{{gecko_minversion_header("2")}}

- -

Introducción

- -

Un servidor WebSocket es simplemente una aplicación TCP que escucha en cualquier puerto de un servidor que sigue un protocolo específico. La tarea de crear un servidor propio personalizado suele asustar a los desarrolladores, sin embargo puede resultar muy fácil implementar un servidor WebSocket en la plataforma que elijas.

- -

Un servidor WebSocket puede ser escrito en cualquier lenguaje de programación Server-Side que sea soporte Berkeley Sockets, como por ejemplo C++ o Python o inclusive PHP y JavaScript para servidores. Este no va a ser un tutorial para ningún lenguaje espefícamente sino que te ayudará a escribir tu propio servidor.
-
- Necesitarás conocer como trabaja el protocolo HTTP y una experiencia intermedia en programación. Dependiendo de las capacidades de tu lenguaje puede ser necesario tener conocimientos en sockets TCP. Esta guía te dará el conocimiento necesario para escribir un servidor con WebSocket.

- -
-

Lea las últimas especificaciones oficiales de WebSocket RFC 6455. Las secciones 1 y 4-7 son especialmente interesantes para personas que deseen implementar servidores. La sección 10 abarca temas de seguridad y definitivamente deberías leerla antes de exponer tu servidor a la red.

-
- -

Un servidor WebSocket es explicado a un muy bajo nivel aquí. Los servidores WebSocket usualmente estan separados y especializados (por una cuestión de balance de cargas y otra razones prácticas), por lo tanto deberías usar un Reverse Proxy (semejante a un servidor HTTP común) casi siempre para detectar los Handshakes de WebSocket, preprocesarlos, y reenviar los datos de los clientes al servidor WebSocket real.
-  

- -

Paso 1: El Handshake del WebSocket

- -

Antes que nada, el servidor debe escuchar las conexiones entrantes usando un socket TCP estandar. Dependiendo de tu plataforma, esto puede ser manejado por tí. Por ejemplo asumamos que tu servidor esta escuchando la dirección example.com en el puerto 8000, y tu socket en el servidor responde a la petición GET con /chat.

- -
-

Advertencia: El servidor puede escuchar cualquier puerto que elijas, pero si elijes un puerto diferente al 80 o 443 podría haber problemas con los firewalls y proxies. Suele suceder con el puerto 443 tambien pero para eso se necesita un conexión segura (TLS/SSL). También se debe aclarar que la mayoría de los navegadores (como Firefox 8 o superiores) no permiten conexiones a servidores WebSocket sin seguridad que se realicen desde páginas web con seguridad (HTTPS). 

-
- -

El Handshake es el puente desde HTTP a WS. En el Handshake se negocian los detalles de la conexión y cualquier de las partes pueden abandonar el proceso antes de completar dicha conexión si los términos no son favorables. El servidor debe ser cuidadoso al analizar lo que el cliente pide, de lo contrario podrían introducirse problemas de seguridad.

- -

Petición de Handshake en el cliente

- -

A pesar de que estamos creando un servidor, un cliente es quien tiene que comenzar el proceso de Handshake de WebSocket. Entonces tú tienes que saber cómo interpretar la petición del cliente. El cliente enviará una linda petición HTTP estandar que lucirá algo asi (la versión del HTTP debe ser 1.1 o mayor y el método debe ser GET):

- -
GET /chat HTTP/1.1
-Host: example.com:8000
-Upgrade: websocket
-Connection: Upgrade
-Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
-Sec-WebSocket-Version: 13
-
-
- -

El cliente puede solicitar aquí extensiones y/o sub protocolos; vea Misceláneos para más detalles. También, cabeceras comunes como User-Agent, RefererCookie, or cabeceras de autenticación podrían ser incluidos. Haz lo que quieras con ellos; no pertencen a WebSocket. También puedes ignorarlos. En muchas configuraciones comunes, un proxy inverso ya ha tratado con ellos.

- -

Si alguna cabecera no se entiende o posee un valor incorrecto, el servidor debe responder "400 Bad Request" e inmediatamente cerrar la conexión. Normalmente, también puede dar la razón porque falló el handshake en el cuerpo de la respuesta HTTP, pero el mensaje podría no ser mostrado (el browser no lo muestra). Si el servidor no comprende que la versión del WebSockets, debería enviar una cabecera Sec-WebSocket-Version que contenga la(s) versión(es) no entendidas. (Esta guía explica v13, la más nueva). Ahora, vamos a ver la cabecera más curiosa, Sec-WebSocket-Key.

- -
-

Tip: Todos los navegadores deben enviar una cabecera Origin. Tu puedes usar esta cabecera por seguridad (revisando por el mismo origen, listas blancas/ listas negras, etc.) y enviar un 403 Forbidden si no te gusta lo que ves. Sin embargo, se advierte que los agentes no navegadores pueden enviar un falso Origin.  La mayoría de las aplicaciones rechazaran las solicitudes sin esta cabecera.

-
- -
-

Tip: The request-uri (/chat here) has no defined meaning in the spec. So many people cleverly use it to let one server handle multiple WebSocket applications. For example, example.com/chat could invoke a multiuser chat app, while /game on the same server might invoke a multiplayer game.

-
- -
-

Note: Regular HTTP status codes can only be used before the handshake. After the handshake succeeds, you have to use a different set of codes (defined in section 7.4 of the spec).

-
- -

Respuesta de Handshake del servidor

- -

Después de la petición, el servidor debería enviar una linda respuesta (aunque todavía en formato HTTP) que se verá asi (hay que recordar que la cabecera termina con \r \n y agrega un \r \n extra después del último):

- -
HTTP/1.1 101 Switching Protocols
-Upgrade: websocket
-Connection: Upgrade
-Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
-
-
- -

Adicionalmente, el servidor puede decidir respecto de las solicitudes "extension/subprotocol" en este punto (ver Miscelláneos para más detalles). La cabecera Sec-WebSocket-Accept es interesante. El servidor debe derivarla a partir de la cabecera Sec-WebSocket-Key enviada anteriormente por el cliente. Para lograr esto se deben concatenar la cabecera del cliente Sec-WebSocket-Key y el string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" (es un "magic string"), calcular el hash SHA-1 del resultado y devolver el string codificado en base64 de este hash.

- -
-

FYI: Este aparentemente complicado e innecesario proceso se realiza de manera que sea obvio para el cliente si el servidor soporta o noWebSockets. Esto es importante de realizar, ya que podrían crearse problemas de seguridad si el servidor acepta conexiones WebSockets pero interpreta los datos como solicitudes HTTP.

-
- -

Así, si la cabecera Sec-WebSocket-Key era "dGhlIHNhbXBsZSBub25jZQ==", la correspondiente respuesta Sec-WebSocket-Accept será "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=". Una vez que el servidor envía estas cabeceras, el "handshake" se considera completo y puedes comenzar a intercambiar datos.

- -
-

El servidor puede enviar otras cabeceras como Set-Cookie, o solicitar autenticación o redirigir mediante otros status codes antes de responder al handshake.

-
- -

Llevando registro de los clientes

- -

Esto no está directamente relacionado con el protocolo WebSocket, pero no está de más mencionarlo: tu servidor debe llevar el registro de los sockets de los clientes, de manera de no realizar handshakes constantemente con los clientes que ya han completado este proceso. La misma dirección IP cliente puede intentar conectarse múltiples veces (pero el servidor puede denegar la conexión si se intentan demasiadas conexiones con el objetivo de evitar ataques ataques DoS).

- -

Paso 2: Intercambiando Data Frames

- -

Tanto el cliente como el servidor puede decidir enviar un mensaje en cualquier momento — ese es el encanto de los WebSockets. Sin embargo, extraer información de esos denominados "frames" o tramas de datos no es una experiencia muy mágica. Aunque todos los frames siguen el mismo formato específico, los datos que van del cliente al servidor se enmascaran utilizando el cifrado XOR (con una clave de 32 bits). La sección 5 de la especificación describe esto en detalle.

- -

Formato

- -

Cada trama de datos (desde el cliente al servidor o viceversa) sigue este mismo formato:

- -
 0                   1                   2                   3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-+-+-+-+-+-------+-+-------------+-------------------------------+
-|F|R|R|R| opcode|M| Payload len |    Extended payload length    |
-|I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
-|N|V|V|V|       |S|             |   (if payload len==126/127)   |
-| |1|2|3|       |K|             |                               |
-+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
-|     Extended payload length continued, if payload len == 127  |
-+ - - - - - - - - - - - - - - - +-------------------------------+
-|                               |Masking-key, if MASK set to 1  |
-+-------------------------------+-------------------------------+
-| Masking-key (continued)       |          Payload Data         |
-+-------------------------------- - - - - - - - - - - - - - - - +
-:                     Payload Data continued ...                :
-+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
-|                     Payload Data continued ...                |
-+---------------------------------------------------------------+
- -

Los RSV1-3 se pueden ignorar, son para las extensiones.

- -

El bit MASK simplemente indica si el mensaje está codificado. Los mensajes del cliente deben estar enmascarados, por lo que tu servidor debe esperar que valga 1. (De hecho, la sección 5.1 de las espeficicaciones  dice que tu servidor debe desconectarse de un cliente si ese cliente envía un mensaje sin enmascarar). Cuando se envía una trama al cliente, no lo ocultes y no pongas el bit de la máscara. Te explicaremos el enmascaramiento más tarde. Nota: Tienes que enmascarar los mensajes incluso cuando uses un socket seguro.

- -

El campo opcode define cómo interpretar los datos de la carga útil:0x0 para continuar, 0x1 para texto (que siempre se codifica con UTF-8), 0x2 para datos binarios, otros llamados "códigos de control" se explican más tarde. En esta versión de WebSockets, de 0x3 a 0x7 y de 0xB a 0xF no tienen significado.

- -

El bit FIN indica si este es el último mensaje de una serie. Si es 0, el servidor seguirá escuchando más partes del mensaje; de lo contrario, el servidor debería considerar el mensaje entregado. Más sobre esto después.

- -

Decoding Payload Length

- -

To read the payload data, you must know when to stop reading. That's why the payload length is important to know. Unfortunately, this is somewhat complicated. To read it, follow these steps:

- -
    -
  1. Read bits 9-15 (inclusive) and interpret that as an unsigned integer. If it's 125 or less, then that's the length; you're done. If it's 126, go to step 2. If it's 127, go to step 3.
  2. -
  3. Read the next 16 bits and interpret those as an unsigned integer. You're done.
  4. -
  5. Read the next 64 bits and interpret those as an unsigned integer. You're done.
  6. -
- -

Reading and Unmasking the Data

- -

If the MASK bit was set (and it should be, for client-to-server messages), read the next 4 octets (32 bits); this is the masking key. Once the payload length and masking key is decoded, you can go ahead and read that number of bytes from the socket. Let's call the data ENCODED, and the key MASK. To get DECODED, loop through the octets (bytes a.k.a. characters for text data) of ENCODED and XOR the octet with the (i modulo 4)th octet of MASK. In pseudo-code (that happens to be valid JavaScript):

- -
var DECODED = "";
-for (var i = 0; i < ENCODED.length; i++) {
-    DECODED[i] = ENCODED[i] ^ MASK[i % 4];
-}
- -

Now you can figure out what DECODED means depending on your application.

- -

Message Fragmentation

- -

The FIN and opcode fields work together to send a message split up into separate frames.  This is called message fragmentation. Fragmentation is only available on opcodes 0x0 to 0x2.

- -

Recall that the opcode tells what a frame is meant to do. If it's 0x1, the payload is text. If it's 0x2, the payload is binary data. However, if it's 0x0, the frame is a continuation frame. This means the server should concatenate the frame's payload to the last frame it received from that client. Here is a rough sketch, in which a server reacts to a client sending text messages. The first message is sent in a single frame, while the second message is sent across three frames. FIN and opcode details are shown only for the client:

- -
Client: FIN=1, opcode=0x1, msg="hello"
-Server: (process complete message immediately) Hi.
-Client: FIN=0, opcode=0x1, msg="and a"
-Server: (listening, new message containing text started)
-Client: FIN=0, opcode=0x0, msg="happy new"
-Server: (listening, payload concatenated to previous message)
-Client: FIN=1, opcode=0x0, msg="year!"
-Server: (process complete message) Happy new year to you too!
- -

Notice the first frame contains an entire message (has FIN=1 and opcode!=0x0), so the server can process or respond as it sees fit. The second frame sent by the client has a text payload (opcode=0x1), but the entire message has not arrived yet (FIN=0). All remaining parts of that message are sent with continuation frames (opcode=0x0), and the final frame of the message is marked by FIN=1. Section 5.4 of the spec describes message fragmentation.

- -

Pings and Pongs: The Heartbeat of WebSockets

- -

At any point after the handshake, either the client or the server can choose to send a ping to the other party. When the ping is received, the recipient must send back a pong as soon as possible. You can use this to make sure that the client is still connected, for example.

- -

A ping or pong is just a regular frame, but it's a control frame. Pings have an opcode of 0x9, and pongs have an opcode of 0xA. When you get a ping, send back a pong with the exact same Payload Data as the ping (for pings and pongs, the max payload length is 125). You might also get a pong without ever sending a ping; ignore this if it happens.

- -
-

If you have gotten more than one ping before you get the chance to send a pong, you only send one pong.

-
- -

Step 4: Closing the connection

- -

To close a connection either the client or server can send a control frame with data containing a specified control sequence to begin the closing handshake (detailed in Section 5.5.1). Upon receiving such a frame, the other peer sends a Close frame in response. The first peer then closes the connection. Any further data received after closing of connection is then discarded. 

- -

Miscellaneous

- -
-

WebSocket codes, extensions, subprotocols, etc. are registered at the IANA WebSocket Protocol Registry.

-
- -

WebSocket extensions and subprotocols are negotiated via headers during the handshake. Sometimes extensions and subprotocols seem too similar to be different things, but there is a clear distinction. Extensions control the WebSocket frame and modify the payload, while subprotocols structure the WebSocket payload and never modify anything. Extensions are optional and generalized (like compression); subprotocols are mandatory and localized (like ones for chat and for MMORPG games).

- -

Extensions

- -
-

This section needs expansion. Please edit if you are equipped to do so.

-
- -

Think of an extension as compressing a file before e-mailing it to someone. Whatever you do, you're sending the same data in different forms. The recipient will eventually be able to get the same data as your local copy, but it is sent differently. That's what an extension does. WebSockets defines a protocol and a simple way to send data, but an extension such as compression could allow sending the same data but in a shorter format.

- -
-

Extensions are explained in sections 5.8, 9, 11.3.2, and 11.4 of the spec.

-
- -

TODO

- -

Subprotocols

- -

Think of a subprotocol as a custom XML schema or doctype declaration. You're still using XML and its syntax, but you're additionally restricted by a structure you agreed on. WebSocket subprotocols are just like that. They do not introduce anything fancy, they just establish structure. Like a doctype or schema, both parties must agree on the subprotocol; unlike a doctype or schema, the subprotocol is implemented on the server and cannot be externally refered to by the client.

- -
-

Subprotocols are explained in sections 1.9, 4.2, 11.3.4, and 11.5 of the spec.

-
- -

A client has to ask for a specific subprotocol. To do so, it will send something like this as part of the original handshake:

- -
GET /chat HTTP/1.1
-...
-Sec-WebSocket-Protocol: soap, wamp
-
-
- -

or, equivalently:

- -
...
-Sec-WebSocket-Protocol: soap
-Sec-WebSocket-Protocol: wamp
-
-
- -

Now the server must pick one of the protocols that the client suggested and it supports. If there are more than one, send the first one the client sent. Imagine our server can use both soap and wamp. Then, in the response handshake, it'll send:

- -
Sec-WebSocket-Protocol: soap
-
-
- -
-

The server can't send more than one Sec-Websocket-Protocol header.
- If the server doesn't want to use any subprotocol, it shouldn't send any Sec-WebSocket-Protocol header. Sending a blank header is incorrect.
- The client may close the connection if it doesn't get the subprotocol it wants.

-
- -

If you want your server to obey certain subprotocols, then naturally you'll need extra code on the server. Let's imagine we're using a subprotocol json. In this subprotocol, all data is passed as JSON. If the client solicits this protocol and the server wants to use it, the server will need to have a JSON parser. Practically speaking, this will be part of a library, but the server will need to pass the data around.

- -
-

Tip: To avoid name conflict, it's recommended to make your subprotocol name part of a domain string. If you are building a custom chat app that uses a proprietary format exclusive to Example Inc., then you might use this: Sec-WebSocket-Protocol: chat.example.com. For different versions, a widely-used method is to add a / followed by the version number, like chat.example.com/2.0. Note that this isn't required, it's just an optional convention, and you can use any string you wish.

-
- - - - diff --git a/files/es/web/api/websockets_api/writing_websocket_server/index.html b/files/es/web/api/websockets_api/writing_websocket_server/index.html new file mode 100644 index 0000000000..333e8e8830 --- /dev/null +++ b/files/es/web/api/websockets_api/writing_websocket_server/index.html @@ -0,0 +1,244 @@ +--- +title: Escribiendo un servidor WebSocket en C# +slug: Web/API/WebSockets_API/Escribiendo_servidor_WebSocket +tags: + - HTML5 + - Tutorial + - WebSockets +translation_of: Web/API/WebSockets_API/Writing_WebSocket_server +--- +

Introducción

+ +

Si deseas utilizar la API WebSocket, es conveniente si tienes un servidor. En este artículo te mostraré como puedes escribir uno en C#. Tú puedes hacer esto en cualquier lenguaje del lado del servidor, pero para mantener las cosas simples y más comprensibles, elegí el lenguaje de Microsoft.

+ +

Este servidor se ajusta a RFC 6455 por lo que solo manejará las conexiones de Chrome version 16, Firefox 11, IE 10 and superiores.

+ +

Primeros pasos

+ +

WebSocket se comunica a través de conexiones TCP (Transmission Control Protocol), afortunadamente C# tiene una clase TcpListener la cual hace lo que su nombre sugiere. Esta se encuentra en el namespace System.Net.Sockets.

+ +
+

Es una buena idea usar la instrucción using para escribir menos. Eso significa que no tendrás que re escribir el namespace de nuevo en cada ocasión.

+
+ +

TcpListener

+ +

Constructor:

+ +
TcpListener(System.Net.IPAddress localaddr, int port)
+ +

localaddr especifica la IP a escuchar y port especifica el puerto.

+ +
+

Para crear un objeto IPAddress desde un string, usa el método estático Parse de IPAddres.

+
+ +

Métodos:

+ + + +

Aquí está como utilizar lo que hemos aprendido:

+ +
​using System.Net.Sockets;
+using System.Net;
+using System;
+
+class Server {
+    public static void Main() {
+        TcpListener server = new TcpListener(IPAddress.Parse("127.0.0.1"), 80);
+
+        server.Start();
+        Console.WriteLine("El server se ha iniciado en 127.0.0.1:80.{0}Esperando una conexión...", Environment.NewLine);
+
+        TcpClient client = server.AcceptTcpClient();
+
+        Console.WriteLine("Un cliente conectado.");
+    }
+}
+
+ +

TcpClient

+ +

Métodos:

+ + + +

Propiedades:

+ + + +

NetworkStream

+ +

Métodos:

+ +
Write(Byte[] buffer, int offset, int size)
+ +

Escribe bytes desde el buffer; el offset y el size determinan la longitud del mensaje.

+ +
Read(Byte[] buffer, int offset, int size)
+ +

Lee bytes al buffer; el offset y el size determinan la longitud del mensaje.

+ +

Ampliemos nuestro ejemplo anterior.

+ +
TcpClient client = server.AcceptTcpClient();
+
+Console.WriteLine("Un cliente conectado.");
+
+NetworkStream stream = client.GetStream();
+
+//enter to an infinite cycle to be able to handle every change in stream
+while (true) {
+    while (!stream.DataAvailable);
+
+    Byte[] bytes = new Byte[client.Available];
+
+    stream.Read(bytes, 0, bytes.Length);
+}
+ +

Handshaking

+ +

Cuando un cliente se conecta al servidor, envía una solicitud GET para actualizar la conexión al WebSocket desde una simple petición HTTP. Esto es conocido como handshaking.

+ +

Este código de ejemplo detecta el GET desde el cliente. Nota que esto bloqueará hasta los 3 primeros bytes del mensaje disponible. Soluciones alternativas deben ser investigadas para ambientes de producción.

+ +
using System.Text;
+using System.Text.RegularExpressions;
+
+while(client.Available < 3)
+{
+   // wait for enough bytes to be available
+}
+
+Byte[] bytes = new Byte[client.Available];
+
+stream.Read(bytes, 0, bytes.Length);
+
+//translate bytes of request to string
+String data = Encoding.UTF8.GetString(bytes);
+
+if (Regex.IsMatch(data, "^GET")) {
+
+} else {
+
+}
+ +

Esta respuesta es fácil de construir, pero puede ser un poco díficil de entender. La explicación completa del handshake al servidor puede encontrarse en  RFC 6455, section 4.2.2. Para nuestros propósitos, solo construiremos una respuesta simple.

+ +

Debes:

+ +
    +
  1. Obtener el valor de "Sec-WebSocket-Key" sin espacios iniciales ni finales de el encabezado de la solicitud
  2. +
  3. Concatenarlo con "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
  4. +
  5. Calcular el código SHA-1 y Base64
  6. +
  7. Escribe el valor Sec-WebSocket-Accept en el encabezado como parte de la respuesta HTTP.
  8. +
+ +
if (new Regex("^GET").IsMatch(data)) {
+    Byte[] response = Encoding.UTF8.GetBytes("HTTP/1.1 101 Switching Protocols" + Environment.NewLine
+        + "Connection: Upgrade" + Environment.NewLine
+        + "Upgrade: websocket" + Environment.NewLine
+        + "Sec-WebSocket-Accept: " + Convert.ToBase64String (
+            SHA1.Create().ComputeHash (
+                Encoding.UTF8.GetBytes (
+                    new Regex("Sec-WebSocket-Key: (.*)").Match(data).Groups[1].Value.Trim() + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
+                )
+            )
+        ) + Environment.NewLine
+        + Environment.NewLine);
+
+    stream.Write(response, 0, response.Length);
+}
+
+ +

Decoding messages

+ +

Luego de un handshake exitoso el cliente puede enviar mensajes al servidor, pero estos serán codificados.

+ +

Si nosotros enviamos "MDN", obtendremos estos bytes:

+ + + + + + + + + + + + + + + +
129131618435611216109
+ +

- 129:

+ + + + + + + + + + + + + + + + + + + + +
FIN (¿Es el mensaje completo?)RSV1RSV2RSV3Opcode
10000x1=0001
+ +

FIN: Puedes enviar tu mensaje en marcos, pero ahora debe mantener las cosas simples.
+ Opcode 0x1 significa que es un texto. Lista completa de Opcodes

+ +

- 131:

+ +

Si el segundo byte menos 128 se encuentra entre 0 y 125, esta es la longitud del mensaje. Si es 126, los siguientes 2 bytes (entero sin signo de 16 bits), si es 127, los siguientes 8 bytes (entero sin signo de 64 bits) son la longitud.

+ +
+

Puedo tomar 128, porque el primer bit siempre es 1.

+
+ +

- 61, 84, 35 y 6 son los bytes de la clave a decodificar. Cambian en cada oportunidad.

+ +

- Los bytes codificados restantes son el mensaje.

+ +

Algoritmo de decodificación

+ +

byte decodificado = byte codificado XOR (posición del byte codificado Mod 4) byte de la clave

+ +

Ejemplo en C#:

+ +
Byte[] decoded = new Byte[3];
+Byte[] encoded = new Byte[3] {112, 16, 109};
+Byte[] key = Byte[4] {61, 84, 35, 6};
+
+for (int i = 0; i < encoded.Length; i++) {
+    decoded[i] = (Byte)(encoded[i] ^ key[i % 4]);
+}
+ +

Relacionado

+ + + +
 
diff --git a/files/es/web/api/websockets_api/writing_websocket_servers/index.html b/files/es/web/api/websockets_api/writing_websocket_servers/index.html new file mode 100644 index 0000000000..1261f75bec --- /dev/null +++ b/files/es/web/api/websockets_api/writing_websocket_servers/index.html @@ -0,0 +1,246 @@ +--- +title: Escribir servidores WebSocket +slug: Web/API/WebSockets_API/Escribiendo_servidores_con_WebSocket +translation_of: Web/API/WebSockets_API/Writing_WebSocket_servers +--- +

{{gecko_minversion_header("2")}}

+ +

Introducción

+ +

Un servidor WebSocket es simplemente una aplicación TCP que escucha en cualquier puerto de un servidor que sigue un protocolo específico. La tarea de crear un servidor propio personalizado suele asustar a los desarrolladores, sin embargo puede resultar muy fácil implementar un servidor WebSocket en la plataforma que elijas.

+ +

Un servidor WebSocket puede ser escrito en cualquier lenguaje de programación Server-Side que sea soporte Berkeley Sockets, como por ejemplo C++ o Python o inclusive PHP y JavaScript para servidores. Este no va a ser un tutorial para ningún lenguaje espefícamente sino que te ayudará a escribir tu propio servidor.
+
+ Necesitarás conocer como trabaja el protocolo HTTP y una experiencia intermedia en programación. Dependiendo de las capacidades de tu lenguaje puede ser necesario tener conocimientos en sockets TCP. Esta guía te dará el conocimiento necesario para escribir un servidor con WebSocket.

+ +
+

Lea las últimas especificaciones oficiales de WebSocket RFC 6455. Las secciones 1 y 4-7 son especialmente interesantes para personas que deseen implementar servidores. La sección 10 abarca temas de seguridad y definitivamente deberías leerla antes de exponer tu servidor a la red.

+
+ +

Un servidor WebSocket es explicado a un muy bajo nivel aquí. Los servidores WebSocket usualmente estan separados y especializados (por una cuestión de balance de cargas y otra razones prácticas), por lo tanto deberías usar un Reverse Proxy (semejante a un servidor HTTP común) casi siempre para detectar los Handshakes de WebSocket, preprocesarlos, y reenviar los datos de los clientes al servidor WebSocket real.
+  

+ +

Paso 1: El Handshake del WebSocket

+ +

Antes que nada, el servidor debe escuchar las conexiones entrantes usando un socket TCP estandar. Dependiendo de tu plataforma, esto puede ser manejado por tí. Por ejemplo asumamos que tu servidor esta escuchando la dirección example.com en el puerto 8000, y tu socket en el servidor responde a la petición GET con /chat.

+ +
+

Advertencia: El servidor puede escuchar cualquier puerto que elijas, pero si elijes un puerto diferente al 80 o 443 podría haber problemas con los firewalls y proxies. Suele suceder con el puerto 443 tambien pero para eso se necesita un conexión segura (TLS/SSL). También se debe aclarar que la mayoría de los navegadores (como Firefox 8 o superiores) no permiten conexiones a servidores WebSocket sin seguridad que se realicen desde páginas web con seguridad (HTTPS). 

+
+ +

El Handshake es el puente desde HTTP a WS. En el Handshake se negocian los detalles de la conexión y cualquier de las partes pueden abandonar el proceso antes de completar dicha conexión si los términos no son favorables. El servidor debe ser cuidadoso al analizar lo que el cliente pide, de lo contrario podrían introducirse problemas de seguridad.

+ +

Petición de Handshake en el cliente

+ +

A pesar de que estamos creando un servidor, un cliente es quien tiene que comenzar el proceso de Handshake de WebSocket. Entonces tú tienes que saber cómo interpretar la petición del cliente. El cliente enviará una linda petición HTTP estandar que lucirá algo asi (la versión del HTTP debe ser 1.1 o mayor y el método debe ser GET):

+ +
GET /chat HTTP/1.1
+Host: example.com:8000
+Upgrade: websocket
+Connection: Upgrade
+Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
+Sec-WebSocket-Version: 13
+
+
+ +

El cliente puede solicitar aquí extensiones y/o sub protocolos; vea Misceláneos para más detalles. También, cabeceras comunes como User-Agent, RefererCookie, or cabeceras de autenticación podrían ser incluidos. Haz lo que quieras con ellos; no pertencen a WebSocket. También puedes ignorarlos. En muchas configuraciones comunes, un proxy inverso ya ha tratado con ellos.

+ +

Si alguna cabecera no se entiende o posee un valor incorrecto, el servidor debe responder "400 Bad Request" e inmediatamente cerrar la conexión. Normalmente, también puede dar la razón porque falló el handshake en el cuerpo de la respuesta HTTP, pero el mensaje podría no ser mostrado (el browser no lo muestra). Si el servidor no comprende que la versión del WebSockets, debería enviar una cabecera Sec-WebSocket-Version que contenga la(s) versión(es) no entendidas. (Esta guía explica v13, la más nueva). Ahora, vamos a ver la cabecera más curiosa, Sec-WebSocket-Key.

+ +
+

Tip: Todos los navegadores deben enviar una cabecera Origin. Tu puedes usar esta cabecera por seguridad (revisando por el mismo origen, listas blancas/ listas negras, etc.) y enviar un 403 Forbidden si no te gusta lo que ves. Sin embargo, se advierte que los agentes no navegadores pueden enviar un falso Origin.  La mayoría de las aplicaciones rechazaran las solicitudes sin esta cabecera.

+
+ +
+

Tip: The request-uri (/chat here) has no defined meaning in the spec. So many people cleverly use it to let one server handle multiple WebSocket applications. For example, example.com/chat could invoke a multiuser chat app, while /game on the same server might invoke a multiplayer game.

+
+ +
+

Note: Regular HTTP status codes can only be used before the handshake. After the handshake succeeds, you have to use a different set of codes (defined in section 7.4 of the spec).

+
+ +

Respuesta de Handshake del servidor

+ +

Después de la petición, el servidor debería enviar una linda respuesta (aunque todavía en formato HTTP) que se verá asi (hay que recordar que la cabecera termina con \r \n y agrega un \r \n extra después del último):

+ +
HTTP/1.1 101 Switching Protocols
+Upgrade: websocket
+Connection: Upgrade
+Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
+
+
+ +

Adicionalmente, el servidor puede decidir respecto de las solicitudes "extension/subprotocol" en este punto (ver Miscelláneos para más detalles). La cabecera Sec-WebSocket-Accept es interesante. El servidor debe derivarla a partir de la cabecera Sec-WebSocket-Key enviada anteriormente por el cliente. Para lograr esto se deben concatenar la cabecera del cliente Sec-WebSocket-Key y el string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" (es un "magic string"), calcular el hash SHA-1 del resultado y devolver el string codificado en base64 de este hash.

+ +
+

FYI: Este aparentemente complicado e innecesario proceso se realiza de manera que sea obvio para el cliente si el servidor soporta o noWebSockets. Esto es importante de realizar, ya que podrían crearse problemas de seguridad si el servidor acepta conexiones WebSockets pero interpreta los datos como solicitudes HTTP.

+
+ +

Así, si la cabecera Sec-WebSocket-Key era "dGhlIHNhbXBsZSBub25jZQ==", la correspondiente respuesta Sec-WebSocket-Accept será "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=". Una vez que el servidor envía estas cabeceras, el "handshake" se considera completo y puedes comenzar a intercambiar datos.

+ +
+

El servidor puede enviar otras cabeceras como Set-Cookie, o solicitar autenticación o redirigir mediante otros status codes antes de responder al handshake.

+
+ +

Llevando registro de los clientes

+ +

Esto no está directamente relacionado con el protocolo WebSocket, pero no está de más mencionarlo: tu servidor debe llevar el registro de los sockets de los clientes, de manera de no realizar handshakes constantemente con los clientes que ya han completado este proceso. La misma dirección IP cliente puede intentar conectarse múltiples veces (pero el servidor puede denegar la conexión si se intentan demasiadas conexiones con el objetivo de evitar ataques ataques DoS).

+ +

Paso 2: Intercambiando Data Frames

+ +

Tanto el cliente como el servidor puede decidir enviar un mensaje en cualquier momento — ese es el encanto de los WebSockets. Sin embargo, extraer información de esos denominados "frames" o tramas de datos no es una experiencia muy mágica. Aunque todos los frames siguen el mismo formato específico, los datos que van del cliente al servidor se enmascaran utilizando el cifrado XOR (con una clave de 32 bits). La sección 5 de la especificación describe esto en detalle.

+ +

Formato

+ +

Cada trama de datos (desde el cliente al servidor o viceversa) sigue este mismo formato:

+ +
 0                   1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-------+-+-------------+-------------------------------+
+|F|R|R|R| opcode|M| Payload len |    Extended payload length    |
+|I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
+|N|V|V|V|       |S|             |   (if payload len==126/127)   |
+| |1|2|3|       |K|             |                               |
++-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
+|     Extended payload length continued, if payload len == 127  |
++ - - - - - - - - - - - - - - - +-------------------------------+
+|                               |Masking-key, if MASK set to 1  |
++-------------------------------+-------------------------------+
+| Masking-key (continued)       |          Payload Data         |
++-------------------------------- - - - - - - - - - - - - - - - +
+:                     Payload Data continued ...                :
++ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
+|                     Payload Data continued ...                |
++---------------------------------------------------------------+
+ +

Los RSV1-3 se pueden ignorar, son para las extensiones.

+ +

El bit MASK simplemente indica si el mensaje está codificado. Los mensajes del cliente deben estar enmascarados, por lo que tu servidor debe esperar que valga 1. (De hecho, la sección 5.1 de las espeficicaciones  dice que tu servidor debe desconectarse de un cliente si ese cliente envía un mensaje sin enmascarar). Cuando se envía una trama al cliente, no lo ocultes y no pongas el bit de la máscara. Te explicaremos el enmascaramiento más tarde. Nota: Tienes que enmascarar los mensajes incluso cuando uses un socket seguro.

+ +

El campo opcode define cómo interpretar los datos de la carga útil:0x0 para continuar, 0x1 para texto (que siempre se codifica con UTF-8), 0x2 para datos binarios, otros llamados "códigos de control" se explican más tarde. En esta versión de WebSockets, de 0x3 a 0x7 y de 0xB a 0xF no tienen significado.

+ +

El bit FIN indica si este es el último mensaje de una serie. Si es 0, el servidor seguirá escuchando más partes del mensaje; de lo contrario, el servidor debería considerar el mensaje entregado. Más sobre esto después.

+ +

Decoding Payload Length

+ +

To read the payload data, you must know when to stop reading. That's why the payload length is important to know. Unfortunately, this is somewhat complicated. To read it, follow these steps:

+ +
    +
  1. Read bits 9-15 (inclusive) and interpret that as an unsigned integer. If it's 125 or less, then that's the length; you're done. If it's 126, go to step 2. If it's 127, go to step 3.
  2. +
  3. Read the next 16 bits and interpret those as an unsigned integer. You're done.
  4. +
  5. Read the next 64 bits and interpret those as an unsigned integer. You're done.
  6. +
+ +

Reading and Unmasking the Data

+ +

If the MASK bit was set (and it should be, for client-to-server messages), read the next 4 octets (32 bits); this is the masking key. Once the payload length and masking key is decoded, you can go ahead and read that number of bytes from the socket. Let's call the data ENCODED, and the key MASK. To get DECODED, loop through the octets (bytes a.k.a. characters for text data) of ENCODED and XOR the octet with the (i modulo 4)th octet of MASK. In pseudo-code (that happens to be valid JavaScript):

+ +
var DECODED = "";
+for (var i = 0; i < ENCODED.length; i++) {
+    DECODED[i] = ENCODED[i] ^ MASK[i % 4];
+}
+ +

Now you can figure out what DECODED means depending on your application.

+ +

Message Fragmentation

+ +

The FIN and opcode fields work together to send a message split up into separate frames.  This is called message fragmentation. Fragmentation is only available on opcodes 0x0 to 0x2.

+ +

Recall that the opcode tells what a frame is meant to do. If it's 0x1, the payload is text. If it's 0x2, the payload is binary data. However, if it's 0x0, the frame is a continuation frame. This means the server should concatenate the frame's payload to the last frame it received from that client. Here is a rough sketch, in which a server reacts to a client sending text messages. The first message is sent in a single frame, while the second message is sent across three frames. FIN and opcode details are shown only for the client:

+ +
Client: FIN=1, opcode=0x1, msg="hello"
+Server: (process complete message immediately) Hi.
+Client: FIN=0, opcode=0x1, msg="and a"
+Server: (listening, new message containing text started)
+Client: FIN=0, opcode=0x0, msg="happy new"
+Server: (listening, payload concatenated to previous message)
+Client: FIN=1, opcode=0x0, msg="year!"
+Server: (process complete message) Happy new year to you too!
+ +

Notice the first frame contains an entire message (has FIN=1 and opcode!=0x0), so the server can process or respond as it sees fit. The second frame sent by the client has a text payload (opcode=0x1), but the entire message has not arrived yet (FIN=0). All remaining parts of that message are sent with continuation frames (opcode=0x0), and the final frame of the message is marked by FIN=1. Section 5.4 of the spec describes message fragmentation.

+ +

Pings and Pongs: The Heartbeat of WebSockets

+ +

At any point after the handshake, either the client or the server can choose to send a ping to the other party. When the ping is received, the recipient must send back a pong as soon as possible. You can use this to make sure that the client is still connected, for example.

+ +

A ping or pong is just a regular frame, but it's a control frame. Pings have an opcode of 0x9, and pongs have an opcode of 0xA. When you get a ping, send back a pong with the exact same Payload Data as the ping (for pings and pongs, the max payload length is 125). You might also get a pong without ever sending a ping; ignore this if it happens.

+ +
+

If you have gotten more than one ping before you get the chance to send a pong, you only send one pong.

+
+ +

Step 4: Closing the connection

+ +

To close a connection either the client or server can send a control frame with data containing a specified control sequence to begin the closing handshake (detailed in Section 5.5.1). Upon receiving such a frame, the other peer sends a Close frame in response. The first peer then closes the connection. Any further data received after closing of connection is then discarded. 

+ +

Miscellaneous

+ +
+

WebSocket codes, extensions, subprotocols, etc. are registered at the IANA WebSocket Protocol Registry.

+
+ +

WebSocket extensions and subprotocols are negotiated via headers during the handshake. Sometimes extensions and subprotocols seem too similar to be different things, but there is a clear distinction. Extensions control the WebSocket frame and modify the payload, while subprotocols structure the WebSocket payload and never modify anything. Extensions are optional and generalized (like compression); subprotocols are mandatory and localized (like ones for chat and for MMORPG games).

+ +

Extensions

+ +
+

This section needs expansion. Please edit if you are equipped to do so.

+
+ +

Think of an extension as compressing a file before e-mailing it to someone. Whatever you do, you're sending the same data in different forms. The recipient will eventually be able to get the same data as your local copy, but it is sent differently. That's what an extension does. WebSockets defines a protocol and a simple way to send data, but an extension such as compression could allow sending the same data but in a shorter format.

+ +
+

Extensions are explained in sections 5.8, 9, 11.3.2, and 11.4 of the spec.

+
+ +

TODO

+ +

Subprotocols

+ +

Think of a subprotocol as a custom XML schema or doctype declaration. You're still using XML and its syntax, but you're additionally restricted by a structure you agreed on. WebSocket subprotocols are just like that. They do not introduce anything fancy, they just establish structure. Like a doctype or schema, both parties must agree on the subprotocol; unlike a doctype or schema, the subprotocol is implemented on the server and cannot be externally refered to by the client.

+ +
+

Subprotocols are explained in sections 1.9, 4.2, 11.3.4, and 11.5 of the spec.

+
+ +

A client has to ask for a specific subprotocol. To do so, it will send something like this as part of the original handshake:

+ +
GET /chat HTTP/1.1
+...
+Sec-WebSocket-Protocol: soap, wamp
+
+
+ +

or, equivalently:

+ +
...
+Sec-WebSocket-Protocol: soap
+Sec-WebSocket-Protocol: wamp
+
+
+ +

Now the server must pick one of the protocols that the client suggested and it supports. If there are more than one, send the first one the client sent. Imagine our server can use both soap and wamp. Then, in the response handshake, it'll send:

+ +
Sec-WebSocket-Protocol: soap
+
+
+ +
+

The server can't send more than one Sec-Websocket-Protocol header.
+ If the server doesn't want to use any subprotocol, it shouldn't send any Sec-WebSocket-Protocol header. Sending a blank header is incorrect.
+ The client may close the connection if it doesn't get the subprotocol it wants.

+
+ +

If you want your server to obey certain subprotocols, then naturally you'll need extra code on the server. Let's imagine we're using a subprotocol json. In this subprotocol, all data is passed as JSON. If the client solicits this protocol and the server wants to use it, the server will need to have a JSON parser. Practically speaking, this will be part of a library, but the server will need to pass the data around.

+ +
+

Tip: To avoid name conflict, it's recommended to make your subprotocol name part of a domain string. If you are building a custom chat app that uses a proprietary format exclusive to Example Inc., then you might use this: Sec-WebSocket-Protocol: chat.example.com. For different versions, a widely-used method is to add a / followed by the version number, like chat.example.com/2.0. Note that this isn't required, it's just an optional convention, and you can use any string you wish.

+
+ + + + diff --git a/files/es/web/api/window/beforeunload_event/index.html b/files/es/web/api/window/beforeunload_event/index.html new file mode 100644 index 0000000000..98fb747ae9 --- /dev/null +++ b/files/es/web/api/window/beforeunload_event/index.html @@ -0,0 +1,215 @@ +--- +title: beforeunload +slug: Web/Events/beforeunload +translation_of: Web/API/Window/beforeunload_event +--- +
El evento beforeunload es disparado cuando la ventana, el documento y sus recursos estan a punto de ser descargados. El documento todavia es visible y el evento todavia es cancelable en este punto.
+ +
 
+ +

Si es asignado un string a la propiedad del objeto Evento returnValue, una caja de dialogo aparece, preguntando al usuario que confirme que dejara la pagina(mirar el ejemplo de abajo).  Algunos navegadores muestran el string devuelto en una caja de dialogos, otros muestran un mensaje fijo. Si no se provee ningun valor, el evento procede silenciosamente.

+ +
+

Nota:Para combatir pop-ups indeseados, los navegadores pueden no mostrar mensajes creados en manejadores del evento beforeunload a menos que se haya interactuado con la pagina, o incluso sin que se haya interactuado en nada con esta.

+
+ + + + + + + + + + + + + + + + + + + + +
BurbujasNo
CancelableSi
Objetos de destinodefaultView
Interfaz{{domxref("Event")}}
+ +

Propiedades

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropiedadTipoDescripcion
target {{readOnlyInline}}{{domxref("EventTarget")}}El evento objetivo(el objetivo superior en el arbol del DOM).
type {{readOnlyInline}}{{domxref("DOMString")}}El tipo de evento.
bubbles {{readOnlyInline}}{{jsxref("Boolean")}}El evento normalmente burbujea?
cancelable {{readOnlyInline}}{{jsxref("Boolean")}}Es posible cancelar el evento?
returnValue{{domxref("DOMString")}}El valor actual devuelto por el evento (el mensaje que se le mostrara al usuario).
+ +

Ejemplos

+ +
window.addEventListener("beforeunload", function (event) {
+  event.returnValue = "\o/";
+});
+
+// es equivalente a
+window.addEventListener("beforeunload", function (event) {
+  event.preventDefault();
+});
+ +

Navegadores basados en WebKit no siguen las especificaciones para la caja de dialogos. Un ejemplo que funcione con distintos navegadores seria similar al siguiente:

+ +
window.addEventListener("beforeunload", function (e) {
+  var confirmationMessage = "\o/";
+
+  e.returnValue = confirmationMessage;     // Gecko, Trident, Chrome 34+
+  return confirmationMessage;              // Gecko, WebKit, Chrome <34
+});
+ +

Notas

+ +

A partir del 25 de Mayo del 2011, la especificion HTML5 establece que llamadas a los metodos {{domxref("window.alert()")}}, {{domxref("window.confirm()")}}, y {{domxref("window.prompt()")}}pueden ser ignoradas durante este evento.Mire las especificaciones de HTML5 para mas detalles.

+ +

Varios navegadores ignoran el resultado del evento y no le preguntan al usuario por confirmacion en absoluto. El documento siempre se descargara automaticamente. Firefox tiene un switch llamado dom.disable_beforeunload en about:config  para habilitar este comportamiento.

+ +

Usando este manejador de evento tu pagina previene que Firefox cambie el cache de la pagina a uno en memoria bfcache. Mire Usando el almacenamiento en cache Firefox 1.5 para detalles.

+ +

Especificaciones

+ + + + + + + + + + + + + + + + + + + + + +
EspecificacionEstadoComentario
{{SpecName("HTML WHATWG", "indices.html#event-beforeunload", "beforeunload")}}{{Spec2("HTML WHATWG")}} 
{{SpecName("HTML5 W3C", "browsers.html#unloading-documents", "beforeunload")}}{{Spec2("HTML5 W3C")}}Definicion inicial
+ +

Compatibilidad con navegadores

+ +
{{CompatibilityTable}}
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CaracteristicaChromeEdgeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
Soporte basico{{CompatChrome(1.0)}}{{CompatVersionUnknown}}14123
Texto personalizado soporte removido{{CompatChrome(51.0)}}{{CompatNo}}{{CompatGeckoMobile("44.0")}} 389.1
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CaracteristicaAndroidAndroid WebviewEdgeFirefox Mobile (Gecko)IE PhoneOpera MobileSafari MobileChrome for Android
Soporte basico{{CompatUnknown}}{{CompatVersionUnknown}}{{CompatVersionUnknown}}{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}(no) defect{{CompatVersionUnknown}}
Texto personalizado soporte removido{{CompatUnknown}}{{CompatChrome(51.0)}}{{CompatNo}}{{CompatGeckoMobile("44.0")}}   {{CompatChrome(51.0)}}
+
+ +

Mire tambien

+ + diff --git a/files/es/web/api/window/domcontentloaded_event/index.html b/files/es/web/api/window/domcontentloaded_event/index.html new file mode 100644 index 0000000000..ffbf3accbc --- /dev/null +++ b/files/es/web/api/window/domcontentloaded_event/index.html @@ -0,0 +1,148 @@ +--- +title: DOMContentLoaded +slug: Web/Events/DOMContentLoaded +translation_of: Web/API/Window/DOMContentLoaded_event +--- +

El evento DOMContentLoaded es disparado cuando el documento HTML ha sido completamente cargado y parseado, sin esperar hojas de estilo, images y subframes para  finalizar la carga. Un evento muy diferente - load - debería ser usado solo para detectar una carga completa de la página. Es un error increíblemente popular usar load cuando DOMContentLoaded sería mucho más apropiado, así que úsalo con cuidado.

+ +

JavaScript síncrono pausa el parseo del DOM.

+ +

También hay mucho propósito general y bibliotecas autónomas que ofrecen métodos de navegador cruzado para detectar que el DOM está preparado.

+ +

Speeding up

+ +

If you want DOM to get parsed as fast as possible after the user had requested the page, some things you could do is turn your JavaScript asynchronous and to optimize loading of stylesheets which if used as usual, slow down page load due to being loaded in parallel, "stealing" traffic from the main html document.

+ +

General info

+ +
+
Specification
+
HTML5
+
Interface
+
Event
+
Bubbles
+
Yes
+
Cancelable
+
Yes (although specified as a simple event that isn't cancelable)
+
Target
+
Document
+
Default Action
+
None.
+
+ +

Properties

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyTypeDescription
target {{readonlyInline}}{{domxref("EventTarget")}}The event target (the topmost target in the DOM tree).
type {{readonlyInline}}{{domxref("DOMString")}}The type of event.
bubbles {{readonlyInline}}{{jsxref("Boolean")}}Whether the event normally bubbles or not.
cancelable {{readonlyInline}}{{jsxref("Boolean")}}Whether the event is cancellable or not.
+ +

Example

+ +
<script>
+  document.addEventListener("DOMContentLoaded", function(event) {
+    console.log("DOM fully loaded and parsed");
+  });
+</script>
+
+ +
<script>
+  document.addEventListener("DOMContentLoaded", function(event) {
+    console.log("DOM fully loaded and parsed");
+  });
+
+for(var i=0; i<1000000000; i++)
+{} // this synchronous script is going to delay parsing of the DOM. So the DOMContentLoaded event is going to launch later.
+</script>
+
+ +

Browser compatibility

+ +

{{CompatibilityTable}}

+ +
+ + + + + + + + + + + + + + + + + + + +
FeatureChromeFirefox (Gecko)Internet ExplorerOperaSafari
Basic support1.0[1]{{CompatGeckoDesktop("1")}}[1]9.0[2]9.03.1[1]
+
+ +
+ + + + + + + + + + + + + + + + + + + +
FeatureAndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Basic support{{CompatVersionUnknown}}[1]{{CompatGeckoMobile("1")}}[1]{{CompatUnknown}}[2]{{CompatVersionUnknown}}{{CompatVersionUnknown}}[1]
+
+ +

[1] Bubbling for this event is supported by at least Gecko 1.9.2, Chrome 6, and Safari 4.

+ +

[2] Internet Explorer 8 supports the readystatechange event, which can be used to detect when the DOM is ready. In earlier versions of Internet Explorer, this state can be detected by repeatedly trying to execute document.documentElement.doScroll("left");, as this snippet will throw an error until the DOM is ready.

+ + + + diff --git a/files/es/web/api/window/load_event/index.html b/files/es/web/api/window/load_event/index.html new file mode 100644 index 0000000000..f38e214839 --- /dev/null +++ b/files/es/web/api/window/load_event/index.html @@ -0,0 +1,125 @@ +--- +title: load +slug: Web/Events/load +tags: + - Evento +translation_of: Web/API/Window/load_event +--- +

El evento load se dispara cuando un recurso y sus recursos dependientes han terminado de cargar.

+ +

 

+ +

Ejemplos

+ +

Window

+ +
<script>
+  window.addEventListener("load", function(event) {
+    console.log("'Todos los recursos terminaron de cargar!");
+  });
+</script>
+ +

Elemento script

+ +
<script>
+  var script = document.createElement("script");
+  script.addEventListener("load", function(event) {
+    console.log("Script terminó de cargarse y ejecutarse");
+  });
+  script.src = "http://example.com/example.js";
+  script.async = true;
+  document.getElementsByTagName("script")[0].parentNode.appendChild(script);
+</script>
+ +

Información general

+ +
+
Especificación
+
DOM L3
+
Interfaz
+
UIEvent
+
Propagación
+
No
+
Cancelable
+
No
+
Objetivo
+
Window,Document,Element
+
Por defecto Acción
+
None.
+
+ +

Propiedades

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropiedadTipoDescripción
target {{readonlyInline}}{{domxref("EventTarget")}}El objetivo del evento (el objetivo superior en el árbol DOM).
type {{readonlyInline}}{{domxref("DOMString")}}El tipo de evento.
bubbles {{readonlyInline}}{{domxref("Boolean")}}Si el elemento normalmente se propaga (bubbles) o no.
cancelable {{readonlyInline}}{{domxref("Boolean")}}Si el evento es cancelable o no.
view {{readonlyInline}}{{domxref("WindowProxy")}}{{domxref("Document.defaultView", "document.defaultView")}} (window del documento)
detail {{readonlyInline}}long (float)0.
+ +

Especificaciones

+ + + + + + + + + + + + + + + + + + + +
EspecificaciónEstadoComentario
{{SpecName('UI Events', '#event-type-load', 'load')}}{{Spec2('UI Events')}} 
{{SpecName('HTML WHATWG', 'parsing.html#the-end:event-load', 'Load event')}}{{Spec2('HTML WHATWG')}}Esto enlaza con la sección en los pasos que se llevan a cabo al final de cargar un documento. Los eventos 'load' también se disparan a muchos elementos. Y tenga en cuenta que hay muchos lugares en la especificación que hacen referencia a cosas que pueden "retrasar el evento de carga".
+ +

Eventos relacionados

+ + diff --git a/files/es/web/api/window/url/index.html b/files/es/web/api/window/url/index.html deleted file mode 100644 index 6ca15914a4..0000000000 --- a/files/es/web/api/window/url/index.html +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: Window.URL -slug: Web/API/Window/URL -tags: - - API - - DOM - - Propiedad - - Referencia - - Referência DOM - - WebAPI -translation_of: Web/API/URL -translation_of_original: Web/API/Window/URL ---- -

{{ApiRef("Window")}}{{SeeCompatTable}}

- -

La propiedad Window.URL devuelve un objeto que proporciona métodos estáticos usados para crear y gestionar objetos de URLs. Además puede ser llamado como un constructor para construir objetos {{domxref("URL")}}.

- -

{{AvailableInWorkers}}

- -

Sintaxis

- -

Llamando a un método estático:

- -
img.src = URL.{{domxref("URL.createObjectURL", "createObjectURL")}}(blob);
- -

Construyendo un nuevo objeto:

- -
var url = new {{domxref("URL.URL", "URL")}}("../cats/", "https://www.example.com/dogs/");
- -

Especificación

- - - - - - - - - - - - - - -
EspecificaciónEstadoComentario
{{SpecName('URL', '#dom-url', 'URL')}}{{Spec2('URL')}}Definición inicial
- -

Compatibilidad con navegadores

- -

{{CompatibilityTable}}

- -
- - - - - - - - - - - - - - - - - - - -
CaracterísticaChromeFirefox (Gecko)Internet ExplorerOperaSafari
Soporte básico8.0[2]{{CompatGeckoDesktop("2.0")}}[1]
- {{CompatGeckoDesktop("19.0")}}
10.015.0[2]6.0[2]
- 7.0
-
- -
- - - - - - - - - - - - - - - - - - - -
CaracterísticaAndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Soporte básico{{CompatVersionUnknown}}[2]{{CompatGeckoMobile("14.0")}}[1]
- {{CompatGeckoMobile("19.0")}}
{{CompatVersionUnknown}}15.0[2]6.0[2]
-
- -

[1] Desde Gecko 2 (Firefox 4) hasta Gecko 18 incluidos, Gecko devuelve un objeto con el tipo interno no estandar nsIDOMMozURLProperty. En la práctica, esto no hace ninguna diferencia.

- -

[2] Implementado bajo el nombre no estandar webkitURL.

diff --git a/files/es/web/api/windowbase64/atob/index.html b/files/es/web/api/windowbase64/atob/index.html deleted file mode 100644 index aa9eeead07..0000000000 --- a/files/es/web/api/windowbase64/atob/index.html +++ /dev/null @@ -1,111 +0,0 @@ ---- -title: WindowBase64.atob() -slug: Web/API/WindowBase64/atob -translation_of: Web/API/WindowOrWorkerGlobalScope/atob ---- -

{{APIRef}}

- -

La función WindowBase64.atob() descodifica una cadena de datos que ha sido codificada utilizando la codificación en base-64. Puedes utilizar el método {{domxref("window.btoa()")}} para codificar y transmitir datos que, de otro modo podrían generar problemas de comunicación. Luego de ser transmitidos se puede usar el método window.atob() para decodificar los datos de nuevo. Por ejemplo, puedes codificar, transmitir y decodificar los caracteres de control como valores ASCII 0 a 31.

- -

For use with Unicode or UTF-8 strings, see this note at Base64 encoding and decoding and this note at window.btoa().

- -

Syntax

- -
var decodedData = window.atob(encodedData);
- -

Example

- -
var encodedData = window.btoa("Hello, world"); // encode a string
-var decodedData = window.atob(encodedData); // decode the string
- -

Specifications

- - - - - - - - - - - - - - - - - - - - - - - - - - -
SpecificationStatusComment
{{SpecName('HTML WHATWG', '#dom-windowbase64-atob', 'WindowBase64.atob()')}}{{Spec2('HTML WHATWG')}}No change since the latest snapshot, {{SpecName("HTML5.1")}}.
{{SpecName('HTML5.1', '#dom-windowbase64-atob', 'WindowBase64.atob()')}}{{Spec2('HTML5.1')}}Snapshot of {{SpecName("HTML WHATWG")}}. No change.
{{SpecName("HTML5 W3C", "#dom-windowbase64-atob", "WindowBase64.atob()")}}{{Spec2('HTML5 W3C')}}Snapshot of {{SpecName("HTML WHATWG")}}. Creation of WindowBase64 (properties where on the target before it).
- -

Browser compatibility

- -
{{CompatibilityTable}}
- -
- - - - - - - - - - - - - - - - - - - -
FeatureChromeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
Basic support{{CompatVersionUnknown}}{{CompatGeckoDesktop(1)}}[1][2]10{{CompatVersionUnknown}}{{CompatVersionUnknown}}
-
- -
- - - - - - - - - - - - - - - - - - - -
FeatureAndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Basic support{{CompatVersionUnknown}}{{CompatGeckoMobile(1)}}{{CompatNo}}{{CompatUnknown}}{{CompatVersionUnknown}}
-
- -

[1]  atob() is also available to XPCOM components implemented in JavaScript, even though window is not the global object in components.

- -

[2] Starting with Firefox 27, this atob() method ignores all space characters in the argument to comply with the latest HTML5 spec. ({{ bug(711180) }})

- -

See also

- - diff --git a/files/es/web/api/windowbase64/base64_codificando_y_decodificando/index.html b/files/es/web/api/windowbase64/base64_codificando_y_decodificando/index.html deleted file mode 100644 index c8747777cd..0000000000 --- a/files/es/web/api/windowbase64/base64_codificando_y_decodificando/index.html +++ /dev/null @@ -1,345 +0,0 @@ ---- -title: Base64 codificando y decodificando -slug: Web/API/WindowBase64/Base64_codificando_y_decodificando -translation_of: Glossary/Base64 ---- -

Base64 es un grupo de esquemas de codificación de binario a texto que representa los datos binarios mediante una cadena ASCII, traduciéndolos en una representación radix-64. El término Base64 se origina de un sistema de codificación de transmisión de contenido MIME específico.

- -

Los esquemas de codificación Base64 son comúnmente usados cuando se necesita codificar datos binarios para que sean almacenados y transferidos sobre un medio diseñado para tratar con datos textuales. Esto es para asegurar que los datos se mantienen intactos y sin modificaciones durante la transmisión. Base64 es comúnmente usado en muchas aplicaciones, incluyendo la escritura de emails vía MIME y el almacenamiento de datos complejos en XML.

- -

En JavaScript hay dos funciones para decodificar y codificar cadenas base64, respectivamente:

- - - -

La función atob() decodifica una cadena de datos que ha sido codificada usando la codificación en base 64. Por el contrario, la función btoa() crea una cadena ASCII codificada en base 64 a partir de una "cadena" de datos binarios.

- -

Ambas funciones trabajan sobre cadenas de texto. Si desea trabajar con ArrayBuffers, lea este párrafo.

- - - - - - - - - - -
-

Documentación

- -
-
data URIs
-
Los URIs de datos, definidos por RFC 2397, permiten a los creadores de contenido introducir pequeños ficheros en línea en documentos.
-
Base64
-
Artículo en Wikipedia sobre el sistema de codificación Base64.
-
{{domxref("WindowBase64.atob","atob()")}}
-
Decodifica una cadena de datos que ha sido codificada utilizando base-64.
-
{{domxref("WindowBase64.btoa","btoa()")}}
-
Crea una cadena ASCII codificada en base 64 a partir de una "cadena" de datos binarios.
-
The "Unicode Problem"
-
En la mayoría de navegadores, llamar a btoa() con una cadena Unicode causará una excepción Character Out Of Range. Este párrafo muestra algunas soluciones.
-
URIScheme
-
Lista de esquemas URI soportados por Mozilla.
-
StringView
-
En este artículo está publicada una librería hecha por nosotros con los siguientes objetivos:
-
-
    -
  • crear una interfaz al estilo de C para cadenas (es decir, arrays de códigos de caracteres — ArrayBufferView en JavaScript) basada en la interfaz  ArrayBuffer de JavaScript.
  • -
  • crear una colección de métodos para los que los objetos parecidos a cadenas (de ahora en adelante, stringViews) funcionen estrictamente en arrays de números más que en cadenas JavaScript inmutables.
  • -
  • trabajar con otras codificaciones Unicode diferentes de las DOMStrings UTF-16 por defecto de JavaScript.
  • -
-
-
- -

Ver todo...

-
-

Herramientas

- - - - - - -
- - - -

El "Problema Unicode"

- -

Como las DOMStrings son cadenas codificadas en 16 bits, en la mayoría de navegadores llamar a window.btoa sobre una cadena Unicode resultará en una excepción Character Out Of Range si un carácter excede el rango de los caracteres ASCII de 8 bits. Hay dos posibles métodos para resolver este problema:

- - - -

Aquí están los dos posibles métodos:

- -

Solución #1 – escapar la cadena antes de codificarla

- -
function utf8_to_b64( str ) {
-  return window.btoa(unescape(encodeURIComponent( str )));
-}
-
-function b64_to_utf8( str ) {
-  return decodeURIComponent(escape(window.atob( str )));
-}
-
-// Uso:
-utf8_to_b64('✓ à la mode'); // "4pyTIMOgIGxhIG1vZGU="
-b64_to_utf8('4pyTIMOgIGxhIG1vZGU='); // "✓ à la mode"
- -

Esta solución ha sido propuesta por Johan Sundström.

- -

Otra posible solución sin utilizar las funciones 'unscape' y 'escape', ya obsoletas.

- -
function b64EncodeUnicode(str) {
-    return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) {
-        return String.fromCharCode('0x' + p1);
-    }));
-}
- b64EncodeUnicode('✓ à la mode'); // "4pyTIMOgIGxhIG1vZGU="
-
- -

Solución #2 – reescribir atob() y btoa() usando TypedArrays y UTF-8

- -
Nota: El siguiente código también es útil para obtener un ArrayBuffer a partir de una cadena Base64 y/o viceversa (véase abajo). Para una librería completa de arrays tipados, vea este artículo.
- -
"use strict";
-
-/*\
-|*|
-|*|  Base64 / binary data / UTF-8 strings utilities
-|*|
-|*|  https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding
-|*|
-\*/
-
-/* Decodificación de cadena base64 en array de bytes */
-
-function b64ToUint6 (nChr) {
-
-  return nChr > 64 && nChr < 91 ?
-      nChr - 65
-    : nChr > 96 && nChr < 123 ?
-      nChr - 71
-    : nChr > 47 && nChr < 58 ?
-      nChr + 4
-    : nChr === 43 ?
-      62
-    : nChr === 47 ?
-      63
-    :
-      0;
-
-}
-
-function base64DecToArr (sBase64, nBlocksSize) {
-
-  var
-    sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, ""), nInLen = sB64Enc.length,
-    nOutLen = nBlocksSize ? Math.ceil((nInLen * 3 + 1 >> 2) / nBlocksSize) * nBlocksSize : nInLen * 3 + 1 >> 2, taBytes = new Uint8Array(nOutLen);
-
-  for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) {
-    nMod4 = nInIdx & 3;
-    nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 18 - 6 * nMod4;
-    if (nMod4 === 3 || nInLen - nInIdx === 1) {
-      for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) {
-        taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255;
-      }
-      nUint24 = 0;
-
-    }
-  }
-
-  return taBytes;
-}
-
-/* Codificación de array en una cadena Base64 */
-
-function uint6ToB64 (nUint6) {
-
-  return nUint6 < 26 ?
-      nUint6 + 65
-    : nUint6 < 52 ?
-      nUint6 + 71
-    : nUint6 < 62 ?
-      nUint6 - 4
-    : nUint6 === 62 ?
-      43
-    : nUint6 === 63 ?
-      47
-    :
-      65;
-
-}
-
-function base64EncArr (aBytes) {
-
-  var nMod3 = 2, sB64Enc = "";
-
-  for (var nLen = aBytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; nIdx++) {
-    nMod3 = nIdx % 3;
-    if (nIdx > 0 && (nIdx * 4 / 3) % 76 === 0) { sB64Enc += "\r\n"; }
-    nUint24 |= aBytes[nIdx] << (16 >>> nMod3 & 24);
-    if (nMod3 === 2 || aBytes.length - nIdx === 1) {
-      sB64Enc += String.fromCharCode(uint6ToB64(nUint24 >>> 18 & 63), uint6ToB64(nUint24 >>> 12 & 63), uint6ToB64(nUint24 >>> 6 & 63), uint6ToB64(nUint24 & 63));
-      nUint24 = 0;
-    }
-  }
-
-  return sB64Enc.substr(0, sB64Enc.length - 2 + nMod3) + (nMod3 === 2 ? '' : nMod3 === 1 ? '=' : '==');
-
-}
-
-/* De array UTF-8 a DOMString y viceversa */
-
-function UTF8ArrToStr (aBytes) {
-
-  var sView = "";
-
-  for (var nPart, nLen = aBytes.length, nIdx = 0; nIdx < nLen; nIdx++) {
-    nPart = aBytes[nIdx];
-    sView += String.fromCharCode(
-      nPart > 251 && nPart < 254 && nIdx + 5 < nLen ? /* six bytes */
-        /* (nPart - 252 << 30) may be not so safe in ECMAScript! So...: */
-        (nPart - 252) * 1073741824 + (aBytes[++nIdx] - 128 << 24) + (aBytes[++nIdx] - 128 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
-      : nPart > 247 && nPart < 252 && nIdx + 4 < nLen ? /* five bytes */
-        (nPart - 248 << 24) + (aBytes[++nIdx] - 128 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
-      : nPart > 239 && nPart < 248 && nIdx + 3 < nLen ? /* four bytes */
-        (nPart - 240 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
-      : nPart > 223 && nPart < 240 && nIdx + 2 < nLen ? /* three bytes */
-        (nPart - 224 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
-      : nPart > 191 && nPart < 224 && nIdx + 1 < nLen ? /* two bytes */
-        (nPart - 192 << 6) + aBytes[++nIdx] - 128
-      : /* nPart < 127 ? */ /* one byte */
-        nPart
-    );
-  }
-
-  return sView;
-
-}
-
-function strToUTF8Arr (sDOMStr) {
-
-  var aBytes, nChr, nStrLen = sDOMStr.length, nArrLen = 0;
-
-  /* mapeando... */
-
-  for (var nMapIdx = 0; nMapIdx < nStrLen; nMapIdx++) {
-    nChr = sDOMStr.charCodeAt(nMapIdx);
-    nArrLen += nChr < 0x80 ? 1 : nChr < 0x800 ? 2 : nChr < 0x10000 ? 3 : nChr < 0x200000 ? 4 : nChr < 0x4000000 ? 5 : 6;
-  }
-
-  aBytes = new Uint8Array(nArrLen);
-
-  /* transcripción... */
-
-  for (var nIdx = 0, nChrIdx = 0; nIdx < nArrLen; nChrIdx++) {
-    nChr = sDOMStr.charCodeAt(nChrIdx);
-    if (nChr < 128) {
-      /* un byte */
-      aBytes[nIdx++] = nChr;
-    } else if (nChr < 0x800) {
-      /* dos bytes */
-      aBytes[nIdx++] = 192 + (nChr >>> 6);
-      aBytes[nIdx++] = 128 + (nChr & 63);
-    } else if (nChr < 0x10000) {
-      /* tres bytes */
-      aBytes[nIdx++] = 224 + (nChr >>> 12);
-      aBytes[nIdx++] = 128 + (nChr >>> 6 & 63);
-      aBytes[nIdx++] = 128 + (nChr & 63);
-    } else if (nChr < 0x200000) {
-      /* cuatro bytes */
-      aBytes[nIdx++] = 240 + (nChr >>> 18);
-      aBytes[nIdx++] = 128 + (nChr >>> 12 & 63);
-      aBytes[nIdx++] = 128 + (nChr >>> 6 & 63);
-      aBytes[nIdx++] = 128 + (nChr & 63);
-    } else if (nChr < 0x4000000) {
-      /* cinco bytes */
-      aBytes[nIdx++] = 248 + (nChr >>> 24);
-      aBytes[nIdx++] = 128 + (nChr >>> 18 & 63);
-      aBytes[nIdx++] = 128 + (nChr >>> 12 & 63);
-      aBytes[nIdx++] = 128 + (nChr >>> 6 & 63);
-      aBytes[nIdx++] = 128 + (nChr & 63);
-    } else /* if (nChr <= 0x7fffffff) */ {
-      /* seis bytes */
-      aBytes[nIdx++] = 252 + (nChr >>> 30);
-      aBytes[nIdx++] = 128 + (nChr >>> 24 & 63);
-      aBytes[nIdx++] = 128 + (nChr >>> 18 & 63);
-      aBytes[nIdx++] = 128 + (nChr >>> 12 & 63);
-      aBytes[nIdx++] = 128 + (nChr >>> 6 & 63);
-      aBytes[nIdx++] = 128 + (nChr & 63);
-    }
-  }
-
-  return aBytes;
-
-}
-
- -

Tests

- -
/* Tests */
-
-var sMyInput = "Base 64 \u2014 Mozilla Developer Network";
-
-var aMyUTF8Input = strToUTF8Arr(sMyInput);
-
-var sMyBase64 = base64EncArr(aMyUTF8Input);
-
-alert(sMyBase64);
-
-var aMyUTF8Output = base64DecToArr(sMyBase64);
-
-var sMyOutput = UTF8ArrToStr(aMyUTF8Output);
-
-alert(sMyOutput);
- -

Apéndice: Decodificar una cadena Base64 en Uint8Array o ArrayBuffer

- -

Estas funciones nos permiten crear también uint8Arrays o arrayBuffers a partir de cadenas codificadas en base 64:

- -
var myArray = base64DecToArr("QmFzZSA2NCDigJQgTW96aWxsYSBEZXZlbG9wZXIgTmV0d29yaw=="); // "Base 64 \u2014 Mozilla Developer Network"
-
-var myBuffer = base64DecToArr("QmFzZSA2NCDigJQgTW96aWxsYSBEZXZlbG9wZXIgTmV0d29yaw==").buffer; // "Base 64 \u2014 Mozilla Developer Network"
-
-alert(myBuffer.byteLength);
- -
Nota: La función base64DecToArr(sBase64[, nBlocksSize]) devuelve un uint8Array de bytes. Si tu objetivo es construir un búfer de datos crudos de 16, 32 o 64 bits, usa el argumento nBlocksSize, que es el número de bytes de los que la propiedad uint8Array.buffer.bytesLength debe devolver un múltiplo (1 u omitido para ASCII, cadenas binarias o cadenas UTF-8 codificacas, 2 para cadenas UTF-16, 4 para cadenas UTF-32).
- -

Para una librería más completa, véase StringView, una representación parecida a C de cadenas basadas en arrays tipados.

- -

Véase también

- - diff --git a/files/es/web/api/windowbase64/index.html b/files/es/web/api/windowbase64/index.html deleted file mode 100644 index 2607e635fa..0000000000 --- a/files/es/web/api/windowbase64/index.html +++ /dev/null @@ -1,109 +0,0 @@ ---- -title: WindowBase64 -slug: Web/API/WindowBase64 -tags: - - API - - HTML-DOM - - Helper - - NeedsTranslation - - TopicStub - - WindowBase64 -translation_of: Web/API/WindowOrWorkerGlobalScope -translation_of_original: Web/API/WindowBase64 ---- -

{{APIRef}}

-

The WindowBase64 helper contains utility methods to convert data to and from base64, a binary-to-text encoding scheme. For example it is used in data URIs.

-

There is no object of this type, though the context object, either the {{domxref("Window")}} for regular browsing scope, or the {{domxref("WorkerGlobalScope")}}  for workers, implements it.

-

Properties

-

This helper neither defines nor inherits any properties.

-

Methods

-

This helper does not inherit any methods.

-
-
- {{domxref("WindowBase64.atob()")}}
-
- Decodes a string of data which has been encoded using base-64 encoding.
-
- {{domxref("WindowBase64.btoa()")}}
-
- Creates a base-64 encoded ASCII string from a string of binary data.
-
-

Specifications

- - - - - - - - - - - - - - - - - - - - - - - - - -
SpecificationStatusComment
{{SpecName('HTML WHATWG', '#windowbase64', 'WindowBase64')}}{{Spec2('HTML WHATWG')}}No change since the latest snapshot, {{SpecName("HTML5.1")}}.
{{SpecName('HTML5.1', '#windowbase64', 'WindowBase64')}}{{Spec2('HTML5.1')}}Snapshot of {{SpecName("HTML WHATWG")}}. No change.
{{SpecName("HTML5 W3C", "#windowbase64", "WindowBase64")}}{{Spec2('HTML5 W3C')}}Snapshot of {{SpecName("HTML WHATWG")}}. Creation of WindowBase64 (properties where on the target before it).
-

Browser compatibility

-

{{CompatibilityTable}}

-
- - - - - - - - - - - - - - - - - - - -
FeatureFirefox (Gecko)ChromeInternet ExplorerOperaSafari
Basic support{{CompatGeckoDesktop(1)}} [1]{{CompatVersionUnknown}}10.0{{CompatVersionUnknown}}{{CompatVersionUnknown}}
-
-
- - - - - - - - - - - - - - - - - - - -
FeatureFirefox Mobile (Gecko)AndroidIE MobileOpera MobileSafari Mobile
Basic support{{CompatGeckoMobile(1)}}{{CompatVersionUnknown}}{{CompatVersionUnknown}}{{CompatVersionUnknown}}{{CompatVersionUnknown}}
-
-

[1]  atob() is also available to XPCOM components implemented in JavaScript, even though {{domxref("Window")}} is not the global object in components.

-

See also

- diff --git a/files/es/web/api/windoweventhandlers/onunload/index.html b/files/es/web/api/windoweventhandlers/onunload/index.html new file mode 100644 index 0000000000..a665fc2027 --- /dev/null +++ b/files/es/web/api/windoweventhandlers/onunload/index.html @@ -0,0 +1,45 @@ +--- +title: window.onunload +slug: Web/API/GlobalEventHandlers/onunload +translation_of: Web/API/WindowEventHandlers/onunload +--- +

{{ ApiRef() }}

+

Test Summary

+

The unload event is raised when the document is unloaded.

+

Syntax

+
window.onunload = funcRef;
+
+ +

Example

+
<html>
+<head>
+
+<title>onunload test</title>
+
+<script type="text/javascript">
+
+window.onunload = unloadPage;
+
+function unloadPage()
+{
+ alert("unload event detected!");
+}
+</script>
+</head>
+
+<body>
+<p>Reload a new page into the browser<br />
+ to fire the unload event for this page.</p>
+<p>You can also use the back or forward buttons<br />
+ to load a new page and fire this event.</p>
+</body>
+</html>
+
+

Notes

+

Note that using this event handler in your page prevents Firefox 1.5 from caching the page in the in-memory bfcache. See Using Firefox 1.5 caching for details.

+

Browsers equipped with pop-up window blockers will ignore all window.open() method calls in onunload event handler functions.

+

Specification

+

{{ DOM0() }}

+

{{ languages( {"zh-cn": "zh-cn/DOM/window.onunload" } ) }}

diff --git a/files/es/web/api/windoworworkerglobalscope/atob/index.html b/files/es/web/api/windoworworkerglobalscope/atob/index.html new file mode 100644 index 0000000000..aa9eeead07 --- /dev/null +++ b/files/es/web/api/windoworworkerglobalscope/atob/index.html @@ -0,0 +1,111 @@ +--- +title: WindowBase64.atob() +slug: Web/API/WindowBase64/atob +translation_of: Web/API/WindowOrWorkerGlobalScope/atob +--- +

{{APIRef}}

+ +

La función WindowBase64.atob() descodifica una cadena de datos que ha sido codificada utilizando la codificación en base-64. Puedes utilizar el método {{domxref("window.btoa()")}} para codificar y transmitir datos que, de otro modo podrían generar problemas de comunicación. Luego de ser transmitidos se puede usar el método window.atob() para decodificar los datos de nuevo. Por ejemplo, puedes codificar, transmitir y decodificar los caracteres de control como valores ASCII 0 a 31.

+ +

For use with Unicode or UTF-8 strings, see this note at Base64 encoding and decoding and this note at window.btoa().

+ +

Syntax

+ +
var decodedData = window.atob(encodedData);
+ +

Example

+ +
var encodedData = window.btoa("Hello, world"); // encode a string
+var decodedData = window.atob(encodedData); // decode the string
+ +

Specifications

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
SpecificationStatusComment
{{SpecName('HTML WHATWG', '#dom-windowbase64-atob', 'WindowBase64.atob()')}}{{Spec2('HTML WHATWG')}}No change since the latest snapshot, {{SpecName("HTML5.1")}}.
{{SpecName('HTML5.1', '#dom-windowbase64-atob', 'WindowBase64.atob()')}}{{Spec2('HTML5.1')}}Snapshot of {{SpecName("HTML WHATWG")}}. No change.
{{SpecName("HTML5 W3C", "#dom-windowbase64-atob", "WindowBase64.atob()")}}{{Spec2('HTML5 W3C')}}Snapshot of {{SpecName("HTML WHATWG")}}. Creation of WindowBase64 (properties where on the target before it).
+ +

Browser compatibility

+ +
{{CompatibilityTable}}
+ +
+ + + + + + + + + + + + + + + + + + + +
FeatureChromeFirefox (Gecko)Internet ExplorerOperaSafari (WebKit)
Basic support{{CompatVersionUnknown}}{{CompatGeckoDesktop(1)}}[1][2]10{{CompatVersionUnknown}}{{CompatVersionUnknown}}
+
+ +
+ + + + + + + + + + + + + + + + + + + +
FeatureAndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Basic support{{CompatVersionUnknown}}{{CompatGeckoMobile(1)}}{{CompatNo}}{{CompatUnknown}}{{CompatVersionUnknown}}
+
+ +

[1]  atob() is also available to XPCOM components implemented in JavaScript, even though window is not the global object in components.

+ +

[2] Starting with Firefox 27, this atob() method ignores all space characters in the argument to comply with the latest HTML5 spec. ({{ bug(711180) }})

+ +

See also

+ + diff --git a/files/es/web/api/windoworworkerglobalscope/clearinterval/index.html b/files/es/web/api/windoworworkerglobalscope/clearinterval/index.html new file mode 100644 index 0000000000..14a1b0d1b8 --- /dev/null +++ b/files/es/web/api/windoworworkerglobalscope/clearinterval/index.html @@ -0,0 +1,43 @@ +--- +title: WindowTimers.clearInterval() +slug: Web/API/WindowTimers/clearInterval +translation_of: Web/API/WindowOrWorkerGlobalScope/clearInterval +--- +
+
{{APIRef("HTML DOM")}}
+
+ +

Cancela una acción reiterativa que se inició mediante una llamada a {{domxref("window.setInterval", "setInterval")}}.

+ +

Sintaxis

+ +
window.clearInterval(intervalID)
+
+ +

intervalID es el identificador de la acción reiterativa que se desea cancelar. Este ID se obtiene a partir de setInterval().

+ +

Ejemplo

+ +

Vea el ejemplo de setInterval().

+ +

Especificación

+ + + + + + + + +
{{SpecName('HTML WHATWG', 'timers.html#timers', 'clearInterval')}}{{Spec2('HTML WHATWG')}}
+ +

Vea también

+ + diff --git a/files/es/web/api/windoworworkerglobalscope/cleartimeout/index.html b/files/es/web/api/windoworworkerglobalscope/cleartimeout/index.html new file mode 100644 index 0000000000..0df5242672 --- /dev/null +++ b/files/es/web/api/windoworworkerglobalscope/cleartimeout/index.html @@ -0,0 +1,63 @@ +--- +title: window.clearTimeout +slug: Web/API/WindowTimers/clearTimeout +translation_of: Web/API/WindowOrWorkerGlobalScope/clearTimeout +--- +
{{ApiRef}}
+ +

Resumen

+ +

Borra el retraso asignado por {{domxref("window.setTimeout","window.setTimeout()")}}.

+ +

Sintaxis

+ +
window.clearTimeout(timeoutID)
+
+ + + +

Ejemplo

+ +

Ejecute el script de abajo en el contexto de una página web y haga clic en la página una vez. Verá un mensaje emergente en un segundo. Si permanece haciendo clic en la página cada segundo, la alerta nunca aparece.

+ +
var alarm = {
+  remind: function(aMessage) {
+    alert(aMessage);
+    delete this.timeoutID;
+  },
+
+  setup: function() {
+    this.cancel();
+    var self = this;
+    this.timeoutID = window.setTimeout(function(msg) {self.remind(msg);}, 1000, "Wake up!");
+  },
+
+  cancel: function() {
+    if(typeof this.timeoutID == "number") {
+      window.clearTimeout(this.timeoutID);
+      delete this.timeoutID;
+    }
+  }
+};
+window.onclick = function() { alarm.setup() };
+ +

Notas

+ +

Pasar un ID inválido a clearTimeout no tiene ningún efecto (y no lanza una excepción).

+ +

Especificación

+ +

DOM Nivel 0. Especificado en HTML5.

+ +

Vea también

+ + diff --git a/files/es/web/api/windoworworkerglobalscope/setinterval/index.html b/files/es/web/api/windoworworkerglobalscope/setinterval/index.html new file mode 100644 index 0000000000..fe41612dd6 --- /dev/null +++ b/files/es/web/api/windoworworkerglobalscope/setinterval/index.html @@ -0,0 +1,692 @@ +--- +title: WindowTimers.setInterval() +slug: Web/API/WindowTimers/setInterval +tags: + - API + - DOM + - Gecko + - Intervalos + - Method + - Temporizadores + - Temporizadores de JavaScript + - WindowTimers + - setInterval +translation_of: Web/API/WindowOrWorkerGlobalScope/setInterval +--- +
{{APIRef("HTML DOM")}}
+ +
Ejecuta una función o un fragmento de código de forma repetitiva cada vez que termina el periodo de tiempo determinado. Devuelve un ID de proceso.
+ +
+ +

Sintaxis

+ +
var procesoID = window.setInterval(función, intervaloDeTiempo[, parámetro1, parámetro2, ... , parámetroN]);
+var procesoID = window.setInterval(código, intervaloDeTiempo);
+
+ +

Parámetros

+ +
+
función
+
La {{jsxref("function")}} que será ejecutada cada intervaloDeTiempo milisegundos.
+
código
+
Una sintaxis opcional permite introducir una cadena en lugar de una función, la cual es evaluada y ejecutada cada intervaloDeTiempo milisegundos. Se recomienda evitar esta sintaxis por la misma razón por la que el comando {{jsxref("eval", "eval()")}} conlleva problemas de seguridad.
+
intervaloDeTiempo
+
El tiempo en milisegundos (1/1000 de segundo, ms) que se debe esperar entre cada ejecución de la función o del código. Si el valor es menor que 10, se usará 10 en su lugar. El tiempo entre cada ejecución puede ser mayor al que indicamos, para mayor información puedes revisar el siguiente artículo: {{SectionOnPage("/en-US/docs/Web/API/WindowTimers/setTimeout", "Reasons for delays longer than specified")}}.
+
+
El parámetro intervaloDeTiempo es convertido en un entero de 32 bits con signo en el IDL, por lo que el valor más grande que puede tener es 2,147,483,647 milisegundos, aproximadamente 24.8 días.
+
+
parámetro1, ..., parámetroN {{optional_inline}}
+
Parámetros adicionales que se pasan a la función a ejecutar.
+
+ +
+

En Internet Explorer 9 y anteriores no es posible pasar más parámetros mediante esta sintaxis. Si quieres activar esta funcionalidad en dichos navegadores deberás usar un polyfill (entra en la sección Callback arguments).

+
+ +

Valor de Retorno

+ +

El valor de retorno procesoID es un valor numérico distinto de cero que identifica al temporizador que fue creado al llamar setInterval(); este valor puede ser usado como parámetro en la función {{domxref("Window.clearInterval()")}} para detener el temporizador. Las funciones setInterval() y {{domxref("WindowTimers.setTimeout", "setTimeout()")}} comparten la misma pila de IDs, por lo que, técnicamente, los comandos clearInterval() y {{domxref("WindowTimers.clearTimeout", "clearTimeout()")}} pueden usarse indiscriminadamente. Sin embargo, por motivos de claridad y mantenimiento, es importante usarlos como corresponde.

+ +
+

Nota: El argumento intervaloDeTiempo se convierte aun entero con signo de 32 bits. Esto limita efectivamente al intervaloDeTiempo a valores de 2147483647 ms, ya que se especifica como entero con signo en el IDL.

+
+ +

Ejemplos

+ +

Ejemplo 1: Sintaxis básica

+ +

El siguiente ejemplo muestra la sintaxis básica.

+ +
var intervalID = window.setInterval(miFuncion, 500, 'Parametro 1', 'Parametro 2');
+
+function miFuncion(a,b) {
+  // Aquí va tu código
+  // Los parámetros son opcionales completamente
+  console.log(a);
+  console.log(b);
+}
+
+ +

Ejemplo 2: Alternando dos colores

+ +

El siguiente ejemplo se llama a la función flashtext() una vez por segundo hasta que se presiona el botón Detener.

+ +
<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="UTF-8" />
+  <title>Ejemplo de setInterval/clearInterval</title>
+  <script>
+     var nIntervId;
+
+     function cambiaDeColor() {
+        nIntervId = setInterval(flasheaTexto, 1000);
+     }
+
+     function flasheaTexto() {
+        var oElem = document.getElementById('mi_mensaje');
+        oElem.style.color = oElem.style.color == 'red' ? 'blue' : 'red';
+        //oElem.style.color ... es un operador ternario o condicional
+     }
+
+     function detenerCambioDeColor() {
+        clearInterval(nIntervId);
+     }
+  </script>
+</head>
+<body onload="cambiaDeColor();">
+  <div id="mi_mensaje">
+    <p>¡Hola mundo!</p>
+  </div>
+  <button onclick="detenerCambioDeColor();">Detener</button>
+</body>
+</html>
+
+ +

Ejemplo 3: Simulando una máquina de escribir

+ +

El siguiente ejemplo simula una máquina de escribir, primero borra el contenido de una lista de nodos (NodeList) que coinciden con un grupo de selectores y después lo escribe lentamente.

+ +
<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8" />
+<title>Máquina de Escribir con JavaScript</title>
+<script>
+  function maquinaEscribir (sSelector, nRate) {
+
+      function limpiar () {
+        clearInterval(nIntervId);
+        bTyping = false;
+        bStart = true;
+        oCurrent = null;
+        aSheets.length = nIdx = 0;
+      }
+
+      function desplazarse (oSheet, nPos, bEraseAndStop) {
+        if (!oSheet.hasOwnProperty("parts") || aMap.length < nPos) { return true; }
+
+        var oRel, bExit = false;
+
+        if (aMap.length === nPos) { aMap.push(0); }
+
+        while (aMap[nPos] < oSheet.parts.length) {
+          oRel = oSheet.parts[aMap[nPos]];
+
+          desplazarse(oRel, nPos + 1, bEraseAndStop) ? aMap[nPos]++ : bExit = true;
+
+          if (bEraseAndStop && (oRel.ref.nodeType - 1 | 1) === 3 && oRel.ref.nodeValue) {
+            bExit = true;
+            oCurrent = oRel.ref;
+            sPart = oCurrent.nodeValue;
+            oCurrent.nodeValue = "";
+          }
+
+          oSheet.ref.appendChild(oRel.ref);
+          if (bExit) { return false; }
+        }
+
+        aMap.length--;
+        return true;
+      }
+
+      function mecanografear () {
+        if (sPart.length === 0 && desplazarse(aSheets[nIdx], 0, true) && nIdx++ === aSheets.length - 1) { limpiar(); return; }
+
+        oCurrent.nodeValue += sPart.charAt(0);
+        sPart = sPart.slice(1);
+      }
+
+      function Hoja (oNode) {
+        this.ref = oNode;
+        if (!oNode.hasChildNodes()) { return; }
+        this.parts = Array.prototype.slice.call(oNode.childNodes);
+
+        for (var nChild = 0; nChild < this.parts.length; nChild++) {
+          oNode.removeChild(this.parts[nChild]);
+          this.parts[nChild] = new Hoja(this.parts[nChild]);
+        }
+      }
+
+      var
+        nIntervId, oCurrent = null, bTyping = false, bStart = true,
+        nIdx = 0, sPart = "", aSheets = [], aMap = [];
+
+      this.rate = nRate || 100;
+
+      this.ejecuta = function () {
+        if (bTyping) { return; }
+        if (bStart) {
+          var aItems = document.querySelectorAll(sSelector);
+
+          if (aItems.length === 0) { return; }
+          for (var nItem = 0; nItem < aItems.length; nItem++) {
+            aSheets.push(new Hoja(aItems[nItem]));
+            /* Uncomment the following line if you have previously hidden your elements via CSS: */
+            // aItems[nItem].style.visibility = "visible";
+          }
+
+          bStart = false;
+        }
+
+        nIntervId = setInterval(mecanografear, this.rate);
+        bTyping = true;
+      };
+
+      this.pausa = function () {
+        clearInterval(nIntervId);
+        bTyping = false;
+      };
+
+      this.finaliza = function () {
+        oCurrent.nodeValue += sPart;
+        sPart = "";
+        for (nIdx; nIdx < aSheets.length; desplazarse(aSheets[nIdx++], 0, false));
+        limpiar();
+      };
+  }
+
+    /* usage: */
+    var oTWExample1 = new maquinaEscribir(/* elements: */ "#article, h1, #info, #copyleft", /* frame rate (optional): */ 15);
+
+    /* default frame rate is 100: */
+    var oTWExample2 = new maquinaEscribir("#controls");
+
+    /* you can also change the frame rate value modifying the "rate" property; for example: */
+    // oTWExample2.rate = 150;
+
+    onload = function () {
+      oTWExample1.ejecuta();
+      oTWExample2.ejecuta();
+    };
+</script>
+<style type="text/css">
+span.intLink, a, a:visited {
+  cursor: pointer;
+  color: #000000;
+  text-decoration: underline;
+}
+
+#info {
+  width: 180px;
+  height: 150px;
+  float: right;
+  background-color: #eeeeff;
+  padding: 4px;
+  overflow: auto;
+  font-size: 12px;
+  margin: 4px;
+  border-radius: 5px;
+  /* visibility: hidden; */
+}
+</style>
+</head>
+
+<body>
+
+<p id="copyleft" style="font-style: italic; font-size: 12px; text-align: center;">CopyLeft 2012 by <a href="https://developer.mozilla.org/" target="_blank">Mozilla Developer Network</a></p>
+<p id="controls" style="text-align: center;">[&nbsp;<span class="intLink" onclick="oTWExample1.ejecuta();">Ejecutar</span> | <span class="intLink" onclick="oTWExample1.pausa();">Pausar</span> | <span class="intLink" onclick="oTWExample1.finaliza();">Terminar</span>&nbsp;]</p>
+<div id="info">
+Vivamus blandit massa ut metus mattis in fringilla lectus imperdiet. Proin ac ante a felis ornare vehicula. Fusce pellentesque lacus vitae eros convallis ut mollis magna pellentesque. Pellentesque placerat enim at lacus ultricies vitae facilisis nisi fringilla. In tincidunt tincidunt tincidunt.
+</div>
+<h1>Maquina de Escribir en JavaScript </h1>
+
+<div id="article">
+<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam ultrices dolor ac dolor imperdiet ullamcorper. Suspendisse quam libero, luctus auctor mollis sed, malesuada condimentum magna. Quisque in ante tellus, in placerat est. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec a mi magna, quis mattis dolor. Etiam sit amet ligula quis urna auctor imperdiet nec faucibus ante. Mauris vel consectetur dolor. Nunc eget elit eget velit pulvinar fringilla consectetur aliquam purus. Curabitur convallis, justo posuere porta egestas, velit erat ornare tortor, non viverra justo diam eget arcu. Phasellus adipiscing fermentum nibh ac commodo. Nam turpis nunc, suscipit a hendrerit vitae, volutpat non ipsum.</p>
+<form>
+<p>Phasellus ac nisl lorem: <input type="text" /><br />
+<textarea style="width: 400px; height: 200px;">Nullam commodo suscipit lacus non aliquet. Phasellus ac nisl lorem, sed facilisis ligula. Nam cursus lobortis placerat. Sed dui nisi, elementum eu sodales ac, placerat sit amet mauris. Pellentesque dapibus tellus ut ipsum aliquam eu auctor dui vehicula. Quisque ultrices laoreet erat, at ultrices tortor sodales non. Sed venenatis luctus magna, ultricies ultricies nunc fringilla eget. Praesent scelerisque urna vitae nibh tristique varius consequat neque luctus. Integer ornare, erat a porta tempus, velit justo fermentum elit, a fermentum metus nisi eu ipsum. Vivamus eget augue vel dui viverra adipiscing congue ut massa. Praesent vitae eros erat, pulvinar laoreet magna. Maecenas vestibulum mollis nunc in posuere. Pellentesque sit amet metus a turpis lobortis tempor eu vel tortor. Cras sodales eleifend interdum.</textarea></p>
+<p><input type="submit" value="Send" />
+</form>
+<p>Duis lobortis sapien quis nisl luctus porttitor. In tempor semper libero, eu tincidunt dolor eleifend sit amet. Ut nec velit in dolor tincidunt rhoncus non non diam. Morbi auctor ornare orci, non euismod felis gravida nec. Curabitur elementum nisi a eros rutrum nec blandit diam placerat. Aenean tincidunt risus ut nisi consectetur cursus. Ut vitae quam elit. Donec dignissim est in quam tempor consequat. Aliquam aliquam diam non felis convallis suscipit. Nulla facilisi. Donec lacus risus, dignissim et fringilla et, egestas vel eros. Duis malesuada accumsan dui, at fringilla mauris bibStartum quis. Cras adipiscing ultricies fermentum. Praesent bibStartum condimentum feugiat.</p>
+<p>Nam faucibus, ligula eu fringilla pulvinar, lectus tellus iaculis nunc, vitae scelerisque metus leo non metus. Proin mattis lobortis lobortis. Quisque accumsan faucibus erat, vel varius tortor ultricies ac. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nec libero nunc. Nullam tortor nunc, elementum a consectetur et, ultrices eu orci. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque a nisl eu sem vehicula egestas.</p>
+</div>
+</body>
+</html>
+
+ +

Observa este ejemplo en acción. Ver más: clearInterval().

+ +

Argumentos de Callback

+ +

Como se mencionó previamente Internet Explorer version 9 y anteriores no soportan el pasar argumentos a la función Callback en setTimeout() ni en setInterval(). El siguiente código específico de Internet Explorer muestra un método de superar esta limitante. Para usarlo basta añadir el código marcado al inicio de tu script.

+ +
/*\
+|*|
+|*|  IE-specific polyfill that enables the passage of arbitrary arguments to the
+|*|  callback functions of javascript timers (HTML5 standard syntax).
+|*|
+|*|  https://developer.mozilla.org/en-US/docs/Web/API/window.setInterval
+|*|  https://developer.mozilla.org/User:fusionchess
+|*|
+|*|  Syntax:
+|*|  var timeoutID = window.setTimeout(func, delay[, param1, param2, ...]);
+|*|  var timeoutID = window.setTimeout(code, delay);
+|*|  var intervalID = window.setInterval(func, delay[, param1, param2, ...]);
+|*|  var intervalID = window.setInterval(code, delay);
+|*|
+\*/
+
+if (document.all && !window.setTimeout.isPolyfill) {
+  var __nativeST__ = window.setTimeout;
+  window.setTimeout = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
+    var aArgs = Array.prototype.slice.call(arguments, 2);
+    return __nativeST__(vCallback instanceof Function ? function () {
+      vCallback.apply(null, aArgs);
+    } : vCallback, nDelay);
+  };
+  window.setTimeout.isPolyfill = true;
+}
+
+if (document.all && !window.setInterval.isPolyfill) {
+  var __nativeSI__ = window.setInterval;
+  window.setInterval = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
+    var aArgs = Array.prototype.slice.call(arguments, 2);
+    return __nativeSI__(vCallback instanceof Function ? function () {
+      vCallback.apply(null, aArgs);
+    } : vCallback, nDelay);
+  };
+  window.setInterval.isPolyfill = true;
+}
+
+ +

Otra posible solución es el usar funciones anónimas para llamar al Callback, aunque esta solución es un poco más cara. Ejemplo:

+ +
var intervalID = setInterval(function() { myFunc("one", "two", "three"); }, 1000);
+ +

También puedes hacer uso de function's bind. Ejemplo:

+ +
var intervalID = setInterval(function(arg1) {}.bind(undefined, 10), 1000);
+ +

{{h3_gecko_minversion("Inactive tabs", "5.0")}}

+ +

A partir de Gecko 5.0 {{geckoRelease("5.0")}}, los intervalos no se disparan más de una vez por segundo en las pestañas inactivas.

+ +

Problemas usando "this"

+ +

Cuando pasas el método de un objeto a la función setInterval() éste es invocado fuera de su contexto. Esto puede crear un valor de this que puede no ser el esperado. Este problema es abordado en detalle en JavaScript reference.

+ +

Explicación

+ +

Cuando setInterval() o setTimeOut() ejecuta un determinado código o función, ésta corre en un contexto de ejecución separado al de la función en la que se creó dicho temporizador. Por esta razón a la palabra clave this se le asigna el valor del objeto window (o el objeto global), no es igual que usar this dentro de una fuinción que invoque a setTimeOut(). Observa el siguiente ejemplo (que utiliza setTimeOut() en lugar de setInterval() – el problema, de hecho, es el mismo para ambos temporizadores):

+ +
miArreglo = ["cero", "uno", "dos"];
+
+miArreglo.miMetodo = function (sPropiedad) {
+    alert(arguments.length > 0 ? this[sPropiedad] : this);
+};
+
+miArreglo.miMetodo(); // imprime "cero,uno,dos"
+miArreglo.miMetodo(1); // imprime "uno"
+setTimeout(miArreglo.miMetodo, 1000); // imprime "[object Window]" despues de 1 segundo
+setTimeout(miArreglo.miMetodo, 1500, "1"); // imprime "undefined" despues de 1,5 segundos
+// tratemos de pasar el objeto 'this'
+setTimeout.call(miArreglo, miArreglo.miMetodo, 2000); // error: "NS_ERROR_XPC_BAD_OP_ON_WN_PROTO: Illegal operation on WrappedNative prototype object"
+setTimeout.call(miArreglo, miArreglo.miMetodo, 2500, 2); // same error
+ +

Como puedes ver no hay forma de pasar el objeto this a la función de Callback en la versión anterior de JavaScript.

+ +

Una posible solución

+ +

Una posible alternativa para resolver ésto es reemplazar las dos funciones globales nativas setTimeout() y setInterval() con las siguientes funciones no nativas que permiten su ejecución a través del método Function.prototype.call. El siguiente ejemplo muestra una posible sustitución:

+ +
// Permite el pase del objeto 'this' a través de temporizadores JavaScript
+
+var __nativeST__ = window.setTimeout, __nativeSI__ = window.setInterval;
+
+window.setTimeout = function (vCallback, nDelay /*, argumentoAPasar1, argumentuAPasar2, etc. */) {
+  var oThis = this, aArgs = Array.prototype.slice.call(arguments, 2);
+  return __nativeST__(vCallback instanceof Function ? function () {
+    vCallback.apply(oThis, aArgs);
+  } : vCallback, nDelay);
+};
+
+window.setInterval = function (vCallback, nDelay /*, argumentoAPasar1, argumentoAPasar2, etc. */) {
+  var oThis = this, aArgs = Array.prototype.slice.call(arguments, 2);
+  return __nativeSI__(vCallback instanceof Function ? function () {
+    vCallback.apply(oThis, aArgs);
+  } : vCallback, nDelay);
+};
+
+ +
Estos dos reemplazos también permiten el camino estándar en HTML5 de pasar argumentos arbitrarios a las funciones de Callback de los temporizadores dentro de IE. Por lo tanto, pueden utilizarse como rellenos (polyfills) no estándar. Para más información vea callback arguments paragraph.
+ +

Prueba de nueva funcionalidad:

+ +
miArreglo = ["cero", "uno", "dos"];
+
+miArreglo.miMetodo = function (sProperty) {
+    alert(arguments.length > 0 ? this[sProperty] : this);
+};
+
+setTimeout(alert, 1500, "Hola Mundo!"); // la utilizacion estandar de setTimeout y de setInterval se mantiene, pero...
+setTimeout.call(miArreglo, miArreglo.miMetodo, 2000); // imprime "cero,uno,dos" despues de 2 segundos
+setTimeout.call(miArreglo, miArreglo.miMetodo, 2500, 2); // imprime "dos" despues de 2,5 segundos
+
+ +

Otra solución más compleja está en la siguiente liga de framework.

+ +
JavaScript 1.8.5 introduce el método Function.prototype.bind(), el cual permite especificar el valor de this para todas sus llamadas en una determinada función. Esto permite sobrellevar facilmente diferentes problemas de contexto con el uso de la palabra this. También, ES2015 soporta arrow functions, dentro del lenguaje nos permite escribir cosas como setInterval( () => this.myMethod) si estamos dentro del método de miArreglo .
+ +

MiniDaemon - Un framework para administrar temporizadores

+ +

En proyectos que requieren muchos temporizadores puede volverse complicado el seguimiento de todos los eventos generados. Una forma de facilitar la administración de timers es guardando sus estados en un objeto. El siguiente ejemplo muestra este tipo de abstracción, la arquitectura del constructor evita explicitamente el uso de cerraduras. También ofrece un camino alternativo para pasar el objeto this a la función de Callback (observa la sección Problemas usando "this" para más detalles). Puedes consultar también el siguiente código en GitHub.

+ +
Para una versión más modular de este (Daemon)puedes verlo en JavaScript Daemons Management. Aquí encontrarás una versión mas complicada que se reduce a una colección escalable de métodos para el constructor Daemon. Éste constructor no es más que un clon del  MiniDaemon con soporte para las funciones init y onstart declarables durante la instanciación del mismo. Por esto el MiniDaemon framework se mantiene como el camino recomendado para realizar animaciones simples.
+ +

minidaemon.js

+ +
/*\
+|*|
+|*|  :: MiniDaemon ::
+|*|
+|*|  Revision #2 - September 26, 2014
+|*|
+|*|  https://developer.mozilla.org/en-US/docs/Web/API/window.setInterval
+|*|  https://developer.mozilla.org/User:fusionchess
+|*|  https://github.com/madmurphy/minidaemon.js
+|*|
+|*|  This framework is released under the GNU Lesser General Public License, version 3 or later.
+|*|  http://www.gnu.org/licenses/lgpl-3.0.html
+|*|
+\*/
+
+function MiniDaemon (oOwner, fTask, nRate, nLen) {
+  if (!(this && this instanceof MiniDaemon)) { return; }
+  if (arguments.length < 2) { throw new TypeError("MiniDaemon - not enough arguments"); }
+  if (oOwner) { this.owner = oOwner; }
+  this.task = fTask;
+  if (isFinite(nRate) && nRate > 0) { this.rate = Math.floor(nRate); }
+  if (nLen > 0) { this.length = Math.floor(nLen); }
+}
+
+MiniDaemon.prototype.owner = null;
+MiniDaemon.prototype.task = null;
+MiniDaemon.prototype.rate = 100;
+MiniDaemon.prototype.length = Infinity;
+
+  /* These properties should be read-only */
+
+MiniDaemon.prototype.SESSION = -1;
+MiniDaemon.prototype.INDEX = 0;
+MiniDaemon.prototype.PAUSED = true;
+MiniDaemon.prototype.BACKW = true;
+
+  /* Global methods */
+
+MiniDaemon.forceCall = function (oDmn) {
+  oDmn.INDEX += oDmn.BACKW ? -1 : 1;
+  if (oDmn.task.call(oDmn.owner, oDmn.INDEX, oDmn.length, oDmn.BACKW) === false || oDmn.isAtEnd()) { oDmn.pause(); return false; }
+  return true;
+};
+
+  /* Instances methods */
+
+MiniDaemon.prototype.isAtEnd = function () {
+  return this.BACKW ? isFinite(this.length) && this.INDEX < 1 : this.INDEX + 1 > this.length;
+};
+
+MiniDaemon.prototype.synchronize = function () {
+  if (this.PAUSED) { return; }
+  clearInterval(this.SESSION);
+  this.SESSION = setInterval(MiniDaemon.forceCall, this.rate, this);
+};
+
+MiniDaemon.prototype.pause = function () {
+  clearInterval(this.SESSION);
+  this.PAUSED = true;
+};
+
+MiniDaemon.prototype.start = function (bReverse) {
+  var bBackw = Boolean(bReverse);
+  if (this.BACKW === bBackw && (this.isAtEnd() || !this.PAUSED)) { return; }
+  this.BACKW = bBackw;
+  this.PAUSED = false;
+  this.synchronize();
+};
+
+ +
MiniDaemon pasa argumentos a la función callback. Si quieres trabajar con ellos en navegadores que no soportan nativamente esta característica, usa uno de los métodos propuestos arriba.
+ +

Sintaxis

+ +

var myDaemon = new MiniDaemon(thisObject, callback[, rate[, length]]);

+ +

Descripción

+ +

Regresa un Objecto que contiene la información necesaria para una animación (como el objeto this, la función de Callback, la duración y el frame-rate).

+ +

Parámetros

+ +
+
thisObject
+
El valor de la palabra this sobre el cual funcionará la función de Callback. Puede ser un objecto o null.
+
callback
+
La función que se invocará repetidas veces. Dicha función se invocará con tres parámetros: index que corresponde al valor iterativo de cada invocación, length que es el número total de invocaciones asignadas al daemon (puede ser un valor finito o Infinity) y backwards (valor booleano que expresa cuando el valor de index es creciente o decreciente). Es similar a callback.call(thisObject, index, length, backwards). Si la función de Callback regresa un valor false el deamon se detiene.
+
rate (optional)
+
El tiempo minimo en milisegundos que transcurre entre cada invocación. El valor por omisión es 100.
+
length (optional)
+
El número total de invocaciones. Puede ser un valor entero positivo o Infinity. El valor por omisión es Infinity.
+
+ +

Propiedades de la intancia MiniDaemon 

+ +
+
myDaemon.owner
+
El objeto this sobre el cual se ejecuta el daemon (lectura/escritura). Puede ser un objecto o null.
+
myDaemon.task
+
La función que se invocará repetidas veces. Dicha función se invocará con tres parámetros: index que corresponde al valor iterativo de cada invocación, length que es el número total de invocaciones asignadas al daemon (puede ser un valor finito o Infinity) y backwards (valor booleano que expresa cuando el valor de index es creciente o decreciente). Es similar a callback.call(thisObject, index, length, backwards). Si la función de Callback regresa un valor false el deamon se detiene.
+
myDaemon.rate
+
El tiempo minimo en milisegundos que transcurre entre cada invocación. El valor por omición es 100 (lectura/escritura).
+
myDaemon.length
+
El número total de invocaciones. Puede ser un valor entero positivo o Infinity. El valor por omición es Infinity (lectura/escritura).
+
+ +

MiniDaemon instances methods

+ +
+
myDaemon.isAtEnd()
+
Regresa un valor boleano que expresa cuando el daemon está en posición de inicio/fin o no.
+
myDaemon.synchronize()
+
Sincroniza el tiempo de un deamon iniciado con el tiempo de su invocación.
+
myDaemon.pause()
+
Pausa el deamon.
+
myDaemon.start([reverse])
+
Inicia el daemon hacia adelante "forward" (el indice de cada invocación se incrementa) o hacia atrás "backwards" (el índice de cada invocación se decrementa).
+
+ +

Métodos del objeto global del MiniDaemon

+ +
+
MiniDaemon.forceCall(minidaemon)
+
Fuerza una sola función callback a la función minidaemon.task  en lugar del hecho de que se ha alcanzado el final o no. En cualquier caso la propiedad INDEX interna crece o decrece según la dirección del proceso.
+
+ +

Ejemplo de uso

+ +

Tu página HTML:

+ +
<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="UTF-8" />
+  <title>MiniDaemin Example - MDN</title>
+  <script type="text/javascript" src="minidaemon.js"></script>
+  <style type="text/css">
+    #sample_div {
+      visibility: hidden;
+    }
+  </style>
+</head>
+
+<body>
+  <p>
+    <input type="button" onclick="fadeInOut.start(false /* optional */);" value="fade in" />
+    <input type="button" onclick="fadeInOut.start(true);" value="fade out">
+    <input type="button" onclick="fadeInOut.pause();" value="pause" />
+  </p>
+
+  <div id="sample_div">Some text here</div>
+
+  <script type="text/javascript">
+    function opacity (nIndex, nLength, bBackwards) {
+      this.style.opacity = nIndex / nLength;
+      if (bBackwards ? nIndex === 0 : nIndex === 1) {
+        this.style.visibility = bBackwards ? "hidden" : "visible";
+      }
+    }
+
+    var fadeInOut = new MiniDaemon(document.getElementById("sample_div"), opacity, 300, 8);
+  </script>
+</body>
+</html>
+ +

Prueba este ejemplo

+ +

Notas

+ +

La función setInterval() es usada frecuentemente para asignar una pausa para ejecutar funciones recurrentes, como por ejemplo pintar el siguiente cuadro de una animación.

+ +

Puedes cancelar el ciclo iniciado por un setInterval() usando el comando window.clearInterval().

+ +

Si solo deseas ejecutar el ciclo una sola vez despues de una pausa usa en su lugar la función window.setTimeout().

+ +

Asegúrate que el tiempo de ejecución sea menor que la frecuencia

+ +

Si existe la posibilidad de que tu función o el código a ejecutarse una y otra vez exeda el tiempo marcado en cada intervalo es recomendado que uses recursivamente el nombre de tu función usando window.setTimeout. Por ejemplo, si usas setInterval para hacer llamadas a un servidor remoto cada 5 segundos, la latencia en la red, un servidor que no responde, o cualquier otro tipo de contratiempo puede generar una pausa mayor a la que indicaste. De esta forma terminarás con solicitudes XHR apiladas que no se resolverán necesariamente en orden.

+ +

En estos casos llamadas con un patrón de setTimeout() recursivo es preferible:

+ +
(function loop(){
+   setTimeout(function() {
+      // Your logic here
+
+      loop();
+  }, delay);
+})();
+
+ +

En este fragmento de código, la función loop() es declarada y es ejecutada inmediatamente. La función loop() es invocada de forma recursiva dentro de setTimeout() despues de cada ejecución. Si bien este patrón no garantiza una ejecución a intervalos fijos, si garantiza que nunca se ejecutará un paso sin que se haya finalizado el anterior. 

+ +

Especificaciones

+ + + + + + + + + + + + + + +
EspecificacionesEstatusComentarios
{{SpecName("HTML WHATWG", "webappapis.html#dom-setinterval", "WindowTimers.setInterval()")}}{{Spec2("HTML WHATWG")}}Definición inicial (DOM Level 0)
+ +

Compatibilidad

+ +
{{CompatibilityTable}}
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
CaracterísticaChromeFirefox (Gecko)[2]Internet ExplorerOperaSafari
Soporte básico1.0{{CompatGeckoDesktop("1")}}4.04.01.0
Soporta parámetros para callback[1]{{CompatVersionUnknown}}{{CompatVersionUnknown}}10.0{{CompatVersionUnknown}}{{CompatUnknown}}
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CaracterísticaAndroidChrome for AndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Soporte básico1.01.0{{CompatGeckoMobile("1")}}6.06.01.0
Soporta parámetros para callback[1]{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}
+
+ +

[1] Whether it supports the optional parameters when in its first form or not.

+ +

[2] Anterior a Firefox 13, Firefox pasaba un parametro adicional al callback, indicando el "actual lateness" del timeout en milisegundos. Este parámetro no estandar dejó de usarse en versiones posteriores a Firefox 13. No es recomendable que extensiones basadas en XPCOM para Firefox usen setInterval(), ya que las actualizaciones pueden causar el que el objeto {{domxref("Window")}} se actualice perdiendo los temporizadores. Deberás usar en su lugar {{interface("nsITimer")}}.

+ +

Ver más

+ + diff --git a/files/es/web/api/windoworworkerglobalscope/settimeout/index.html b/files/es/web/api/windoworworkerglobalscope/settimeout/index.html new file mode 100644 index 0000000000..1180d9f8af --- /dev/null +++ b/files/es/web/api/windoworworkerglobalscope/settimeout/index.html @@ -0,0 +1,340 @@ +--- +title: WindowOrWorkerGlobalScope.setTimeout +slug: Web/API/WindowTimers/setTimeout +tags: + - API + - HTML DOM + - WindowOrWorkerGlobalScope + - setTimeout +translation_of: Web/API/WindowOrWorkerGlobalScope/setTimeout +--- +
{{APIRef("HTML DOM")}}
+ +

El método setTimeout() del mixin {{domxref("WindowOrWorkerGlobalScope")}} establece un temporizador que ejecuta una función o una porción de código después de que transcurre un tiempo establecido.

+ + +

Sintaxis

+ +
var idTemporizador = scope.setTimeout(funcion[, retraso, parametro1, parametro2, ...]);
+var idTimeout = scope.setTimeout(funcion[, retraso]);
+var idTimeout = scope.setTimeout(codigo[, retraso]);
+
+ +

Parámetros

+ +
+
funcion
+
Una {{jsxref("function")}} para ejecutar después de que expire el temporizador.
+
codigo
+
Una sintaxis opcional que le permite incluir una cadena en lugar de una función, la cual es compilada y ejecutada cuando el temporizador expira. Esta sintaxis no se recomienda por las mismas razones que hacen del uso de {{jsxref("Global_Objects/eval", "eval()")}} un riesgo de seguridad.
+
retraso {{optional_inline}}
+
Tiempo, en milisegundos  (milésimas de segundo), que el temporizador debe esperar antes de ejecutar la función o el código. Si se omite este parámetro se usa el valor 0. Tenga en cuenta que el retraso real puede ser más prolongado; ver más abajo {{anch("Reasons for delays longer than specified")}}.
+
param1, ..., paramN {{optional_inline}}
+
Parámetros adicionales que se pasan a la función especificada por  func una vez el temporizador expira.
+
+ +
Nota: Pasar parámetros adicionales a la función en la primera sintaxis no funciona en Internet Explorer 9 o inferior. Si quiere habilitar esta funcionalidad en ese navegador,  debe usar un código de compatibilidad (vea la sección Callback arguments).
+ +

Valor retornado

+ +

El valor retornado IDtemporizador es númerico y no es cero; identifica el temporizador creado con la llamada a setTimeout(); este valor puede pasarse a {{domxref("WindowOrWorkerGlobalScope.clearTimeout()")}} para cancelar el temporizador.

+ +

Puede ser útil advertir que  setTimeout() y {{domxref("WindowOrWorkerGlobalScope.setInterval", "setInterval()")}} comparten la misma piscina de IDs, y que tanto clearTimeout() como {{domxref("WindowOrWorkerGlobalScope.clearInterval", "clearInterval()")}} pueden intercambiarse.  Por claridad, sin embargo,  debe hacerlos coincidir para evitar confusiones cuando mantenga su código.

+ +

Ejemplo

+ +

El siguiente ejemplo establece dos botenes simples en una página web y los engancha a las rutinas setTimeout() y clearTimeout(). Presionando el primer botón establecerá un temporizador que llama un diálogo de alerta después de dos segundos y guarda el id del temporizador para usarlo con clearTimeout(). Opcionalmente puede cancelar este temporizador presionando el segundo botón.

+ +

Contenido HTML

+ +
<p>Ejemplo funcional</p>
+<button onclick="delayedAlert();">Muestra una caja de alerta después de dos segundos</button>
+<p></p>
+<button onclick="clearAlert();">Cancela la alerta antes de que ocurra</button>
+
+ +

Contenido JavaScript

+ +
var timeoutID;
+
+function delayedAlert() {
+  timeoutID = window.setTimeout(slowAlert, 2000);
+}
+
+function slowAlert() {
+  alert("That was really slow!");
+}
+
+function clearAlert() {
+  window.clearTimeout(timeoutID);
+}
+
+ +

{{ EmbedLiveSample('Example') }}

+ +

Vea también clearTimeout() example.

+ +

Callback arguments

+ +

Si necesita pasar un argumento a su función callback, pero necesita que funcione en Internet Explorer, que no soporta el envío de parámetros adicionales (ni con setTimeout()setInterval()) usted puede incluir este código de compatibilidad IE-specific que habilitará la funcionalidad estándar de HTML5 para pasar los parámetros adicionales en ese navegador para ambos temporizadores solamente insertandolo al inicio de sus scripts.

+ +
/*\
+|*|
+|*|  IE-specific polyfill which enables the passage of arbitrary arguments to the
+|*|  callback functions of JavaScript timers (HTML5 standard syntax).
+|*|
+|*|  https://developer.mozilla.org/en-US/docs/DOM/window.setInterval
+|*|
+|*|  Syntax:
+|*|  var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]);
+|*|  var timeoutID = window.setTimeout(code, delay);
+|*|  var intervalID = window.setInterval(func, delay[, param1, param2, ...]);
+|*|  var intervalID = window.setInterval(code, delay);
+|*|
+\*/
+
+if (document.all && !window.setTimeout.isPolyfill) {
+  var __nativeST__ = window.setTimeout;
+  window.setTimeout = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
+    var aArgs = Array.prototype.slice.call(arguments, 2);
+    return __nativeST__(vCallback instanceof Function ? function () {
+      vCallback.apply(null, aArgs);
+    } : vCallback, nDelay);
+  };
+  window.setTimeout.isPolyfill = true;
+}
+
+if (document.all && !window.setInterval.isPolyfill) {
+  var __nativeSI__ = window.setInterval;
+  window.setInterval = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
+    var aArgs = Array.prototype.slice.call(arguments, 2);
+    return __nativeSI__(vCallback instanceof Function ? function () {
+      vCallback.apply(null, aArgs);
+    } : vCallback, nDelay);
+  };
+  window.setInterval.isPolyfill = true;
+}
+
+ +

Arreglo solo para IE

+ +

Si quiere una solución completamente no intrusiva con otros navegadores móviles o de escritorio, incluyendo IE 9 y superior, puede usar los comentarios condicionales de JavaScript:

+ +
/*@cc_on
+  // conditional IE < 9 only fix
+  @if (@_jscript_version <= 6)
+  (function(f){
+     window.setTimeout =f(window.setTimeout);
+     window.setInterval =f(window.setInterval);
+  })(function(f){return function(c,t){var a=[].slice.call(arguments,2);return f(function(){c.apply(this,a)},t)}});
+  @end
+@*/
+
+ +

O usar un enfoque más limpio basado en el condicional para IE de HTML:

+ +
<!--[if lt IE 9]><script>
+(function(f){
+window.setTimeout =f(window.setTimeout);
+window.setInterval =f(window.setInterval);
+})(function(f){return function(c,t){
+var a=[].slice.call(arguments,2);return f(function(){c.apply(this,a)},t)}
+});
+</script><![endif]-->
+
+ +

Otra posibilidad es usar una función anónima para llamar el callback, pero esta solución es un poco más costosa. Ejemplo:

+ +
var intervalID = setTimeout(function() { myFunc("uno", "dos", "tres"); }, 1000);
+
+ +

Sin embargo, otra posibilidad es usar function's bind. Ejemplo:

+ +
setTimeout(function(arg1){}.bind(undefined, 10));
+
+ +

El problema "this"

+ +

Cuando pasa un método a setTimeout() (o cualquier otra función , por el estilo), podría ser invocada con el valor de this equivocado. Este problema es explicado en detalle en la referencia de JavaScript.

+ +

Explicación

+ +

El código ejecutado por setTimeout() corre en un contexto de ejecución diferente al de la función por la que fue llamado. Como consecuencia, la palabra clave this para la función llamada será asignado al objeto window (o global); no tendrá el mismo valor del this de la función que llamó al setTimeout. Vea el siguiente ejemplo:

+ +
myArray = ["cero", "uno", "dos"];
+myArray.myMethod = function (sProperty) {
+    alert(arguments.length > 0 ? this[sProperty] : this);
+};
+
+myArray.myMethod(); // imprime "cero,uno,dos"
+myArray.myMethod(1); // imprime "uno"
+setTimeout(myArray.myMethod, 1000); // imprime "[object Window]" después de 1 segundo
+setTimeout(myArray.myMethod, 1500, "1"); // imprime "undefined" después de 1.5 segundos
+// intentemos pasar el objeto 'this'
+setTimeout.call(myArray, myArray.myMethod, 2000); // error: "NS_ERROR_XPC_BAD_OP_ON_WN_PROTO: Illegal operation on WrappedNative prototype object"
+setTimeout.call(myArray, myArray.myMethod, 2500, 2); // mismo error
+ +

Como puedes ver no hay forma de pasar el objeto this a la función callback.

+ +

Una posible solución

+ +

Una posible forma de resolver el problema del "this" es reemplazar las dos funciones globales nativas setTimeout() or setInterval()por dos no-nativas  que permitan su invocación a través del método Function.prototype.call. El siguiente ejemplo muestra un posible reemplazo:

+ +
// Enable the passage of the 'this' object through the JavaScript timers
+
+var __nativeST__ = window.setTimeout, __nativeSI__ = window.setInterval;
+
+window.setTimeout = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
+  var oThis = this, aArgs = Array.prototype.slice.call(arguments, 2);
+  return __nativeST__(vCallback instanceof Function ? function () {
+    vCallback.apply(oThis, aArgs);
+  } : vCallback, nDelay);
+};
+
+window.setInterval = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
+  var oThis = this, aArgs = Array.prototype.slice.call(arguments, 2);
+  return __nativeSI__(vCallback instanceof Function ? function () {
+    vCallback.apply(oThis, aArgs);
+  } : vCallback, nDelay);
+};
+ +
Nota: Estos dos reemplazos habilitarán el estándar HTML5 para el paso de argumentos arbitrarios a las funciones callback de los temporizadores en IE. Pueden usarse como polyfills también. Vea el párrafo Callback arguments.
+ +

Prueba de la nueva característica:

+ +
myArray = ["zero", "one", "two"];
+myArray.myMethod = function (sProperty) {
+    alert(arguments.length > 0 ? this[sProperty] : this);
+};
+
+setTimeout(alert, 1500, "Hello world!"); // the standard use of setTimeout and setInterval is preserved, but...
+setTimeout.call(myArray, myArray.myMethod, 2000); // prints "zero,one,two" after 2 seconds
+setTimeout.call(myArray, myArray.myMethod, 2500, 2); // prints "two" after 2.5 seconds
+
+ +

No hay soluciones nativas ad hoc a este problema.

+ +
Nota: JavaScript 1.8.5 introduce el método Function.prototype.bind(, que permite especificar el valor que debería usarse como this para todas las llamadas a una función dada. Esto permite evitar fácilmente los problemas en los que no es claro que será, dependiendo del contexto desde el cual la función sea llamada.
+ +

Notas

+ +

Puede cancelar el temporizador usando window.clearTimeout(). Si desea tener una función llamada repetidamente (p.e., cada N milisegundos), considere usar window.setInterval().

+ +

Es importante notar que la función o fragmento de código no puede ser ejecutado hasta que el hilo que llamó setTimeout()haya terminado.

+ +

Pasando cadenas literales

+ +

Pasando una cadena en vez de una función a setTimeout()pasa lo mismo que al usar eval.

+ +
// Correcto
+window.setTimeout(function() {
+    alert("Hello World!");
+}, 500);
+
+// Incorrecto
+window.setTimeout("alert(\"Hello World!\");", 500);
+
+
+ +

Las cadenas literales son evaluadas en el contexto global, así que los símbolos locales en el contexto donde setTimeout() fue llamado no estarán disponibles cuando la cadena es evaluada como código.

+ +

Minimum/ maximum delay and timeout nesting

+ +

Historically browsers implement setTimeout() "clamping": successive setTimeout() calls with delay smaller than the "minimum delay" limit are forced to use at least the minimum delay. The minimum delay, DOM_MIN_TIMEOUT_VALUE, is 4 ms (stored in a preference in Firefox: dom.min_timeout_value), with a DOM_CLAMP_TIMEOUT_NESTING_LEVEL of 5ms.

+ +

In fact, 4ms is specified by the HTML5 spec and is consistent across browsers released in 2010 and onward. Prior to {{ geckoRelease("5.0") }}, the minimum timeout value for nested timeouts was 10 ms.

+ +

In addition to "clamping", the timeout can also fire later when the page (or the OS/browser itself) is busy with other tasks.

+ +

To implement a 0 ms timeout in a modern browser, you can use {{ domxref("window.postMessage()") }} as described here.

+ +

Browsers including Internet Explorer, Chrome, Safari, and Firefox store the delay as a 32-bit signed Integer internally. This causes an Integer overflow when using delays larger than 2147483647, resulting in the timeout being executed immediately.

+ +

Inactive tabs

+ +

In {{ geckoRelease("5.0") }} and Chrome 11, timeouts are clamped to firing no more often than once per second (1000ms) in inactive tabs; see {{ bug(633421) }} for more information about this in Mozilla or crbug.com/66078 for details about this in Chrome.

+ +

Compatibilidad de navegadores

+ +

{{ CompatibilityTable() }}

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
CaracterísticaChromeFirefox (Gecko)Internet ExplorerOperaSafari
Soporte básico1.0{{ CompatGeckoDesktop("1") }}4.04.01.0
Soporta parámetros para callback*1{{ CompatVersionUnknown }}{{ CompatVersionUnknown }}10.0{{ CompatVersionUnknown }}{{ CompatUnknown }}
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CaracterísticaAndroidChrome for AndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Soporte básico1.01.0{{ CompatGeckoMobile("1") }}6.06.01.0
Soporta parámetros para callback*1{{ CompatUnknown }}{{ CompatUnknown }}{{ CompatUnknown }}{{ CompatUnknown }}{{ CompatUnknown }}{{ CompatUnknown }}
+
+ +

*1 Whether it supports the optional parameters when in its first form or not.

+ +

Especificación

+ +

Parte del DOM nivel 0, como se especifica en HTML5.

+ +

Vea también

+ + diff --git a/files/es/web/api/windowtimers/clearinterval/index.html b/files/es/web/api/windowtimers/clearinterval/index.html deleted file mode 100644 index 14a1b0d1b8..0000000000 --- a/files/es/web/api/windowtimers/clearinterval/index.html +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: WindowTimers.clearInterval() -slug: Web/API/WindowTimers/clearInterval -translation_of: Web/API/WindowOrWorkerGlobalScope/clearInterval ---- -
-
{{APIRef("HTML DOM")}}
-
- -

Cancela una acción reiterativa que se inició mediante una llamada a {{domxref("window.setInterval", "setInterval")}}.

- -

Sintaxis

- -
window.clearInterval(intervalID)
-
- -

intervalID es el identificador de la acción reiterativa que se desea cancelar. Este ID se obtiene a partir de setInterval().

- -

Ejemplo

- -

Vea el ejemplo de setInterval().

- -

Especificación

- - - - - - - - -
{{SpecName('HTML WHATWG', 'timers.html#timers', 'clearInterval')}}{{Spec2('HTML WHATWG')}}
- -

Vea también

- - diff --git a/files/es/web/api/windowtimers/cleartimeout/index.html b/files/es/web/api/windowtimers/cleartimeout/index.html deleted file mode 100644 index 0df5242672..0000000000 --- a/files/es/web/api/windowtimers/cleartimeout/index.html +++ /dev/null @@ -1,63 +0,0 @@ ---- -title: window.clearTimeout -slug: Web/API/WindowTimers/clearTimeout -translation_of: Web/API/WindowOrWorkerGlobalScope/clearTimeout ---- -
{{ApiRef}}
- -

Resumen

- -

Borra el retraso asignado por {{domxref("window.setTimeout","window.setTimeout()")}}.

- -

Sintaxis

- -
window.clearTimeout(timeoutID)
-
- - - -

Ejemplo

- -

Ejecute el script de abajo en el contexto de una página web y haga clic en la página una vez. Verá un mensaje emergente en un segundo. Si permanece haciendo clic en la página cada segundo, la alerta nunca aparece.

- -
var alarm = {
-  remind: function(aMessage) {
-    alert(aMessage);
-    delete this.timeoutID;
-  },
-
-  setup: function() {
-    this.cancel();
-    var self = this;
-    this.timeoutID = window.setTimeout(function(msg) {self.remind(msg);}, 1000, "Wake up!");
-  },
-
-  cancel: function() {
-    if(typeof this.timeoutID == "number") {
-      window.clearTimeout(this.timeoutID);
-      delete this.timeoutID;
-    }
-  }
-};
-window.onclick = function() { alarm.setup() };
- -

Notas

- -

Pasar un ID inválido a clearTimeout no tiene ningún efecto (y no lanza una excepción).

- -

Especificación

- -

DOM Nivel 0. Especificado en HTML5.

- -

Vea también

- - diff --git a/files/es/web/api/windowtimers/index.html b/files/es/web/api/windowtimers/index.html deleted file mode 100644 index 549969232f..0000000000 --- a/files/es/web/api/windowtimers/index.html +++ /dev/null @@ -1,119 +0,0 @@ ---- -title: WindowTimers -slug: Web/API/WindowTimers -tags: - - API -translation_of: Web/API/WindowOrWorkerGlobalScope -translation_of_original: Web/API/WindowTimers ---- -
{{APIRef("HTML DOM")}}
- -

WindowTimers contains utility methods to set and clear timers.

- -

There is no object of this type, though the context object, either the {{domxref("Window")}} for regular browsing scope, or the {{domxref("WorkerGlobalScope")}}  for workers, implements it.

- -

Properties

- -

This interface do not define any property, nor inherit any.

- -

Methods

- -

This interface do not inherit any method.

- -
-
{{domxref("WindowTimers.clearInterval()")}}
-
Cancels the repeated execution set using {{domxref("WindowTimers.setInterval()")}}.
-
{{domxref("WindowTimers.clearTimeout()")}}
-
Cancels the repeated execution set using {{domxref("WindowTimers.setTimeout()")}}.
-
{{domxref("WindowTimers.setInterval()")}}
-
Schedules the execution of a function each X milliseconds.
-
{{domxref("WindowTimers.setTimeout()")}}
-
Sets a delay for executing a function.
-
- -

Specifications

- - - - - - - - - - - - - - - - - - - - - - - - - - -
SpecificationStatusComment
{{SpecName('HTML WHATWG', '#windowtimers', 'WindowTimers')}}{{Spec2('HTML WHATWG')}}No change since the latest snapshot, {{SpecName("HTML5.1")}}.
{{SpecName('HTML5.1', '#windowtimers', 'WindowTimers')}}{{Spec2('HTML5.1')}}Snapshot of {{SpecName("HTML WHATWG")}}. No change.
{{SpecName("HTML5 W3C", "#windowtimers", "WindowTimers")}}{{Spec2('HTML5 W3C')}}Snapshot of {{SpecName("HTML WHATWG")}}. Creation of WindowBase64 (properties where on the target before it).
- -

Browser compatibility

- -

{{CompatibilityTable}}

- -
- - - - - - - - - - - - - - - - - - - -
FeatureFirefox (Gecko)ChromeInternet ExplorerOperaSafari
Basic support{{CompatGeckoDesktop(1)}}1.04.04.01.0
-
- -
- - - - - - - - - - - - - - - - - - - -
FeatureFirefox Mobile (Gecko)AndroidIE MobileOpera MobileSafari Mobile
Basic support{{CompatGeckoMobile(1)}}{{CompatVersionUnknown}}{{CompatVersionUnknown}}{{CompatVersionUnknown}}{{CompatVersionUnknown}}
-
- -

 

- -

See also

- - diff --git a/files/es/web/api/windowtimers/setinterval/index.html b/files/es/web/api/windowtimers/setinterval/index.html deleted file mode 100644 index fe41612dd6..0000000000 --- a/files/es/web/api/windowtimers/setinterval/index.html +++ /dev/null @@ -1,692 +0,0 @@ ---- -title: WindowTimers.setInterval() -slug: Web/API/WindowTimers/setInterval -tags: - - API - - DOM - - Gecko - - Intervalos - - Method - - Temporizadores - - Temporizadores de JavaScript - - WindowTimers - - setInterval -translation_of: Web/API/WindowOrWorkerGlobalScope/setInterval ---- -
{{APIRef("HTML DOM")}}
- -
Ejecuta una función o un fragmento de código de forma repetitiva cada vez que termina el periodo de tiempo determinado. Devuelve un ID de proceso.
- -
- -

Sintaxis

- -
var procesoID = window.setInterval(función, intervaloDeTiempo[, parámetro1, parámetro2, ... , parámetroN]);
-var procesoID = window.setInterval(código, intervaloDeTiempo);
-
- -

Parámetros

- -
-
función
-
La {{jsxref("function")}} que será ejecutada cada intervaloDeTiempo milisegundos.
-
código
-
Una sintaxis opcional permite introducir una cadena en lugar de una función, la cual es evaluada y ejecutada cada intervaloDeTiempo milisegundos. Se recomienda evitar esta sintaxis por la misma razón por la que el comando {{jsxref("eval", "eval()")}} conlleva problemas de seguridad.
-
intervaloDeTiempo
-
El tiempo en milisegundos (1/1000 de segundo, ms) que se debe esperar entre cada ejecución de la función o del código. Si el valor es menor que 10, se usará 10 en su lugar. El tiempo entre cada ejecución puede ser mayor al que indicamos, para mayor información puedes revisar el siguiente artículo: {{SectionOnPage("/en-US/docs/Web/API/WindowTimers/setTimeout", "Reasons for delays longer than specified")}}.
-
-
El parámetro intervaloDeTiempo es convertido en un entero de 32 bits con signo en el IDL, por lo que el valor más grande que puede tener es 2,147,483,647 milisegundos, aproximadamente 24.8 días.
-
-
parámetro1, ..., parámetroN {{optional_inline}}
-
Parámetros adicionales que se pasan a la función a ejecutar.
-
- -
-

En Internet Explorer 9 y anteriores no es posible pasar más parámetros mediante esta sintaxis. Si quieres activar esta funcionalidad en dichos navegadores deberás usar un polyfill (entra en la sección Callback arguments).

-
- -

Valor de Retorno

- -

El valor de retorno procesoID es un valor numérico distinto de cero que identifica al temporizador que fue creado al llamar setInterval(); este valor puede ser usado como parámetro en la función {{domxref("Window.clearInterval()")}} para detener el temporizador. Las funciones setInterval() y {{domxref("WindowTimers.setTimeout", "setTimeout()")}} comparten la misma pila de IDs, por lo que, técnicamente, los comandos clearInterval() y {{domxref("WindowTimers.clearTimeout", "clearTimeout()")}} pueden usarse indiscriminadamente. Sin embargo, por motivos de claridad y mantenimiento, es importante usarlos como corresponde.

- -
-

Nota: El argumento intervaloDeTiempo se convierte aun entero con signo de 32 bits. Esto limita efectivamente al intervaloDeTiempo a valores de 2147483647 ms, ya que se especifica como entero con signo en el IDL.

-
- -

Ejemplos

- -

Ejemplo 1: Sintaxis básica

- -

El siguiente ejemplo muestra la sintaxis básica.

- -
var intervalID = window.setInterval(miFuncion, 500, 'Parametro 1', 'Parametro 2');
-
-function miFuncion(a,b) {
-  // Aquí va tu código
-  // Los parámetros son opcionales completamente
-  console.log(a);
-  console.log(b);
-}
-
- -

Ejemplo 2: Alternando dos colores

- -

El siguiente ejemplo se llama a la función flashtext() una vez por segundo hasta que se presiona el botón Detener.

- -
<!DOCTYPE html>
-<html>
-<head>
-  <meta charset="UTF-8" />
-  <title>Ejemplo de setInterval/clearInterval</title>
-  <script>
-     var nIntervId;
-
-     function cambiaDeColor() {
-        nIntervId = setInterval(flasheaTexto, 1000);
-     }
-
-     function flasheaTexto() {
-        var oElem = document.getElementById('mi_mensaje');
-        oElem.style.color = oElem.style.color == 'red' ? 'blue' : 'red';
-        //oElem.style.color ... es un operador ternario o condicional
-     }
-
-     function detenerCambioDeColor() {
-        clearInterval(nIntervId);
-     }
-  </script>
-</head>
-<body onload="cambiaDeColor();">
-  <div id="mi_mensaje">
-    <p>¡Hola mundo!</p>
-  </div>
-  <button onclick="detenerCambioDeColor();">Detener</button>
-</body>
-</html>
-
- -

Ejemplo 3: Simulando una máquina de escribir

- -

El siguiente ejemplo simula una máquina de escribir, primero borra el contenido de una lista de nodos (NodeList) que coinciden con un grupo de selectores y después lo escribe lentamente.

- -
<!DOCTYPE html>
-<html>
-<head>
-<meta charset="UTF-8" />
-<title>Máquina de Escribir con JavaScript</title>
-<script>
-  function maquinaEscribir (sSelector, nRate) {
-
-      function limpiar () {
-        clearInterval(nIntervId);
-        bTyping = false;
-        bStart = true;
-        oCurrent = null;
-        aSheets.length = nIdx = 0;
-      }
-
-      function desplazarse (oSheet, nPos, bEraseAndStop) {
-        if (!oSheet.hasOwnProperty("parts") || aMap.length < nPos) { return true; }
-
-        var oRel, bExit = false;
-
-        if (aMap.length === nPos) { aMap.push(0); }
-
-        while (aMap[nPos] < oSheet.parts.length) {
-          oRel = oSheet.parts[aMap[nPos]];
-
-          desplazarse(oRel, nPos + 1, bEraseAndStop) ? aMap[nPos]++ : bExit = true;
-
-          if (bEraseAndStop && (oRel.ref.nodeType - 1 | 1) === 3 && oRel.ref.nodeValue) {
-            bExit = true;
-            oCurrent = oRel.ref;
-            sPart = oCurrent.nodeValue;
-            oCurrent.nodeValue = "";
-          }
-
-          oSheet.ref.appendChild(oRel.ref);
-          if (bExit) { return false; }
-        }
-
-        aMap.length--;
-        return true;
-      }
-
-      function mecanografear () {
-        if (sPart.length === 0 && desplazarse(aSheets[nIdx], 0, true) && nIdx++ === aSheets.length - 1) { limpiar(); return; }
-
-        oCurrent.nodeValue += sPart.charAt(0);
-        sPart = sPart.slice(1);
-      }
-
-      function Hoja (oNode) {
-        this.ref = oNode;
-        if (!oNode.hasChildNodes()) { return; }
-        this.parts = Array.prototype.slice.call(oNode.childNodes);
-
-        for (var nChild = 0; nChild < this.parts.length; nChild++) {
-          oNode.removeChild(this.parts[nChild]);
-          this.parts[nChild] = new Hoja(this.parts[nChild]);
-        }
-      }
-
-      var
-        nIntervId, oCurrent = null, bTyping = false, bStart = true,
-        nIdx = 0, sPart = "", aSheets = [], aMap = [];
-
-      this.rate = nRate || 100;
-
-      this.ejecuta = function () {
-        if (bTyping) { return; }
-        if (bStart) {
-          var aItems = document.querySelectorAll(sSelector);
-
-          if (aItems.length === 0) { return; }
-          for (var nItem = 0; nItem < aItems.length; nItem++) {
-            aSheets.push(new Hoja(aItems[nItem]));
-            /* Uncomment the following line if you have previously hidden your elements via CSS: */
-            // aItems[nItem].style.visibility = "visible";
-          }
-
-          bStart = false;
-        }
-
-        nIntervId = setInterval(mecanografear, this.rate);
-        bTyping = true;
-      };
-
-      this.pausa = function () {
-        clearInterval(nIntervId);
-        bTyping = false;
-      };
-
-      this.finaliza = function () {
-        oCurrent.nodeValue += sPart;
-        sPart = "";
-        for (nIdx; nIdx < aSheets.length; desplazarse(aSheets[nIdx++], 0, false));
-        limpiar();
-      };
-  }
-
-    /* usage: */
-    var oTWExample1 = new maquinaEscribir(/* elements: */ "#article, h1, #info, #copyleft", /* frame rate (optional): */ 15);
-
-    /* default frame rate is 100: */
-    var oTWExample2 = new maquinaEscribir("#controls");
-
-    /* you can also change the frame rate value modifying the "rate" property; for example: */
-    // oTWExample2.rate = 150;
-
-    onload = function () {
-      oTWExample1.ejecuta();
-      oTWExample2.ejecuta();
-    };
-</script>
-<style type="text/css">
-span.intLink, a, a:visited {
-  cursor: pointer;
-  color: #000000;
-  text-decoration: underline;
-}
-
-#info {
-  width: 180px;
-  height: 150px;
-  float: right;
-  background-color: #eeeeff;
-  padding: 4px;
-  overflow: auto;
-  font-size: 12px;
-  margin: 4px;
-  border-radius: 5px;
-  /* visibility: hidden; */
-}
-</style>
-</head>
-
-<body>
-
-<p id="copyleft" style="font-style: italic; font-size: 12px; text-align: center;">CopyLeft 2012 by <a href="https://developer.mozilla.org/" target="_blank">Mozilla Developer Network</a></p>
-<p id="controls" style="text-align: center;">[&nbsp;<span class="intLink" onclick="oTWExample1.ejecuta();">Ejecutar</span> | <span class="intLink" onclick="oTWExample1.pausa();">Pausar</span> | <span class="intLink" onclick="oTWExample1.finaliza();">Terminar</span>&nbsp;]</p>
-<div id="info">
-Vivamus blandit massa ut metus mattis in fringilla lectus imperdiet. Proin ac ante a felis ornare vehicula. Fusce pellentesque lacus vitae eros convallis ut mollis magna pellentesque. Pellentesque placerat enim at lacus ultricies vitae facilisis nisi fringilla. In tincidunt tincidunt tincidunt.
-</div>
-<h1>Maquina de Escribir en JavaScript </h1>
-
-<div id="article">
-<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam ultrices dolor ac dolor imperdiet ullamcorper. Suspendisse quam libero, luctus auctor mollis sed, malesuada condimentum magna. Quisque in ante tellus, in placerat est. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec a mi magna, quis mattis dolor. Etiam sit amet ligula quis urna auctor imperdiet nec faucibus ante. Mauris vel consectetur dolor. Nunc eget elit eget velit pulvinar fringilla consectetur aliquam purus. Curabitur convallis, justo posuere porta egestas, velit erat ornare tortor, non viverra justo diam eget arcu. Phasellus adipiscing fermentum nibh ac commodo. Nam turpis nunc, suscipit a hendrerit vitae, volutpat non ipsum.</p>
-<form>
-<p>Phasellus ac nisl lorem: <input type="text" /><br />
-<textarea style="width: 400px; height: 200px;">Nullam commodo suscipit lacus non aliquet. Phasellus ac nisl lorem, sed facilisis ligula. Nam cursus lobortis placerat. Sed dui nisi, elementum eu sodales ac, placerat sit amet mauris. Pellentesque dapibus tellus ut ipsum aliquam eu auctor dui vehicula. Quisque ultrices laoreet erat, at ultrices tortor sodales non. Sed venenatis luctus magna, ultricies ultricies nunc fringilla eget. Praesent scelerisque urna vitae nibh tristique varius consequat neque luctus. Integer ornare, erat a porta tempus, velit justo fermentum elit, a fermentum metus nisi eu ipsum. Vivamus eget augue vel dui viverra adipiscing congue ut massa. Praesent vitae eros erat, pulvinar laoreet magna. Maecenas vestibulum mollis nunc in posuere. Pellentesque sit amet metus a turpis lobortis tempor eu vel tortor. Cras sodales eleifend interdum.</textarea></p>
-<p><input type="submit" value="Send" />
-</form>
-<p>Duis lobortis sapien quis nisl luctus porttitor. In tempor semper libero, eu tincidunt dolor eleifend sit amet. Ut nec velit in dolor tincidunt rhoncus non non diam. Morbi auctor ornare orci, non euismod felis gravida nec. Curabitur elementum nisi a eros rutrum nec blandit diam placerat. Aenean tincidunt risus ut nisi consectetur cursus. Ut vitae quam elit. Donec dignissim est in quam tempor consequat. Aliquam aliquam diam non felis convallis suscipit. Nulla facilisi. Donec lacus risus, dignissim et fringilla et, egestas vel eros. Duis malesuada accumsan dui, at fringilla mauris bibStartum quis. Cras adipiscing ultricies fermentum. Praesent bibStartum condimentum feugiat.</p>
-<p>Nam faucibus, ligula eu fringilla pulvinar, lectus tellus iaculis nunc, vitae scelerisque metus leo non metus. Proin mattis lobortis lobortis. Quisque accumsan faucibus erat, vel varius tortor ultricies ac. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nec libero nunc. Nullam tortor nunc, elementum a consectetur et, ultrices eu orci. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque a nisl eu sem vehicula egestas.</p>
-</div>
-</body>
-</html>
-
- -

Observa este ejemplo en acción. Ver más: clearInterval().

- -

Argumentos de Callback

- -

Como se mencionó previamente Internet Explorer version 9 y anteriores no soportan el pasar argumentos a la función Callback en setTimeout() ni en setInterval(). El siguiente código específico de Internet Explorer muestra un método de superar esta limitante. Para usarlo basta añadir el código marcado al inicio de tu script.

- -
/*\
-|*|
-|*|  IE-specific polyfill that enables the passage of arbitrary arguments to the
-|*|  callback functions of javascript timers (HTML5 standard syntax).
-|*|
-|*|  https://developer.mozilla.org/en-US/docs/Web/API/window.setInterval
-|*|  https://developer.mozilla.org/User:fusionchess
-|*|
-|*|  Syntax:
-|*|  var timeoutID = window.setTimeout(func, delay[, param1, param2, ...]);
-|*|  var timeoutID = window.setTimeout(code, delay);
-|*|  var intervalID = window.setInterval(func, delay[, param1, param2, ...]);
-|*|  var intervalID = window.setInterval(code, delay);
-|*|
-\*/
-
-if (document.all && !window.setTimeout.isPolyfill) {
-  var __nativeST__ = window.setTimeout;
-  window.setTimeout = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
-    var aArgs = Array.prototype.slice.call(arguments, 2);
-    return __nativeST__(vCallback instanceof Function ? function () {
-      vCallback.apply(null, aArgs);
-    } : vCallback, nDelay);
-  };
-  window.setTimeout.isPolyfill = true;
-}
-
-if (document.all && !window.setInterval.isPolyfill) {
-  var __nativeSI__ = window.setInterval;
-  window.setInterval = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
-    var aArgs = Array.prototype.slice.call(arguments, 2);
-    return __nativeSI__(vCallback instanceof Function ? function () {
-      vCallback.apply(null, aArgs);
-    } : vCallback, nDelay);
-  };
-  window.setInterval.isPolyfill = true;
-}
-
- -

Otra posible solución es el usar funciones anónimas para llamar al Callback, aunque esta solución es un poco más cara. Ejemplo:

- -
var intervalID = setInterval(function() { myFunc("one", "two", "three"); }, 1000);
- -

También puedes hacer uso de function's bind. Ejemplo:

- -
var intervalID = setInterval(function(arg1) {}.bind(undefined, 10), 1000);
- -

{{h3_gecko_minversion("Inactive tabs", "5.0")}}

- -

A partir de Gecko 5.0 {{geckoRelease("5.0")}}, los intervalos no se disparan más de una vez por segundo en las pestañas inactivas.

- -

Problemas usando "this"

- -

Cuando pasas el método de un objeto a la función setInterval() éste es invocado fuera de su contexto. Esto puede crear un valor de this que puede no ser el esperado. Este problema es abordado en detalle en JavaScript reference.

- -

Explicación

- -

Cuando setInterval() o setTimeOut() ejecuta un determinado código o función, ésta corre en un contexto de ejecución separado al de la función en la que se creó dicho temporizador. Por esta razón a la palabra clave this se le asigna el valor del objeto window (o el objeto global), no es igual que usar this dentro de una fuinción que invoque a setTimeOut(). Observa el siguiente ejemplo (que utiliza setTimeOut() en lugar de setInterval() – el problema, de hecho, es el mismo para ambos temporizadores):

- -
miArreglo = ["cero", "uno", "dos"];
-
-miArreglo.miMetodo = function (sPropiedad) {
-    alert(arguments.length > 0 ? this[sPropiedad] : this);
-};
-
-miArreglo.miMetodo(); // imprime "cero,uno,dos"
-miArreglo.miMetodo(1); // imprime "uno"
-setTimeout(miArreglo.miMetodo, 1000); // imprime "[object Window]" despues de 1 segundo
-setTimeout(miArreglo.miMetodo, 1500, "1"); // imprime "undefined" despues de 1,5 segundos
-// tratemos de pasar el objeto 'this'
-setTimeout.call(miArreglo, miArreglo.miMetodo, 2000); // error: "NS_ERROR_XPC_BAD_OP_ON_WN_PROTO: Illegal operation on WrappedNative prototype object"
-setTimeout.call(miArreglo, miArreglo.miMetodo, 2500, 2); // same error
- -

Como puedes ver no hay forma de pasar el objeto this a la función de Callback en la versión anterior de JavaScript.

- -

Una posible solución

- -

Una posible alternativa para resolver ésto es reemplazar las dos funciones globales nativas setTimeout() y setInterval() con las siguientes funciones no nativas que permiten su ejecución a través del método Function.prototype.call. El siguiente ejemplo muestra una posible sustitución:

- -
// Permite el pase del objeto 'this' a través de temporizadores JavaScript
-
-var __nativeST__ = window.setTimeout, __nativeSI__ = window.setInterval;
-
-window.setTimeout = function (vCallback, nDelay /*, argumentoAPasar1, argumentuAPasar2, etc. */) {
-  var oThis = this, aArgs = Array.prototype.slice.call(arguments, 2);
-  return __nativeST__(vCallback instanceof Function ? function () {
-    vCallback.apply(oThis, aArgs);
-  } : vCallback, nDelay);
-};
-
-window.setInterval = function (vCallback, nDelay /*, argumentoAPasar1, argumentoAPasar2, etc. */) {
-  var oThis = this, aArgs = Array.prototype.slice.call(arguments, 2);
-  return __nativeSI__(vCallback instanceof Function ? function () {
-    vCallback.apply(oThis, aArgs);
-  } : vCallback, nDelay);
-};
-
- -
Estos dos reemplazos también permiten el camino estándar en HTML5 de pasar argumentos arbitrarios a las funciones de Callback de los temporizadores dentro de IE. Por lo tanto, pueden utilizarse como rellenos (polyfills) no estándar. Para más información vea callback arguments paragraph.
- -

Prueba de nueva funcionalidad:

- -
miArreglo = ["cero", "uno", "dos"];
-
-miArreglo.miMetodo = function (sProperty) {
-    alert(arguments.length > 0 ? this[sProperty] : this);
-};
-
-setTimeout(alert, 1500, "Hola Mundo!"); // la utilizacion estandar de setTimeout y de setInterval se mantiene, pero...
-setTimeout.call(miArreglo, miArreglo.miMetodo, 2000); // imprime "cero,uno,dos" despues de 2 segundos
-setTimeout.call(miArreglo, miArreglo.miMetodo, 2500, 2); // imprime "dos" despues de 2,5 segundos
-
- -

Otra solución más compleja está en la siguiente liga de framework.

- -
JavaScript 1.8.5 introduce el método Function.prototype.bind(), el cual permite especificar el valor de this para todas sus llamadas en una determinada función. Esto permite sobrellevar facilmente diferentes problemas de contexto con el uso de la palabra this. También, ES2015 soporta arrow functions, dentro del lenguaje nos permite escribir cosas como setInterval( () => this.myMethod) si estamos dentro del método de miArreglo .
- -

MiniDaemon - Un framework para administrar temporizadores

- -

En proyectos que requieren muchos temporizadores puede volverse complicado el seguimiento de todos los eventos generados. Una forma de facilitar la administración de timers es guardando sus estados en un objeto. El siguiente ejemplo muestra este tipo de abstracción, la arquitectura del constructor evita explicitamente el uso de cerraduras. También ofrece un camino alternativo para pasar el objeto this a la función de Callback (observa la sección Problemas usando "this" para más detalles). Puedes consultar también el siguiente código en GitHub.

- -
Para una versión más modular de este (Daemon)puedes verlo en JavaScript Daemons Management. Aquí encontrarás una versión mas complicada que se reduce a una colección escalable de métodos para el constructor Daemon. Éste constructor no es más que un clon del  MiniDaemon con soporte para las funciones init y onstart declarables durante la instanciación del mismo. Por esto el MiniDaemon framework se mantiene como el camino recomendado para realizar animaciones simples.
- -

minidaemon.js

- -
/*\
-|*|
-|*|  :: MiniDaemon ::
-|*|
-|*|  Revision #2 - September 26, 2014
-|*|
-|*|  https://developer.mozilla.org/en-US/docs/Web/API/window.setInterval
-|*|  https://developer.mozilla.org/User:fusionchess
-|*|  https://github.com/madmurphy/minidaemon.js
-|*|
-|*|  This framework is released under the GNU Lesser General Public License, version 3 or later.
-|*|  http://www.gnu.org/licenses/lgpl-3.0.html
-|*|
-\*/
-
-function MiniDaemon (oOwner, fTask, nRate, nLen) {
-  if (!(this && this instanceof MiniDaemon)) { return; }
-  if (arguments.length < 2) { throw new TypeError("MiniDaemon - not enough arguments"); }
-  if (oOwner) { this.owner = oOwner; }
-  this.task = fTask;
-  if (isFinite(nRate) && nRate > 0) { this.rate = Math.floor(nRate); }
-  if (nLen > 0) { this.length = Math.floor(nLen); }
-}
-
-MiniDaemon.prototype.owner = null;
-MiniDaemon.prototype.task = null;
-MiniDaemon.prototype.rate = 100;
-MiniDaemon.prototype.length = Infinity;
-
-  /* These properties should be read-only */
-
-MiniDaemon.prototype.SESSION = -1;
-MiniDaemon.prototype.INDEX = 0;
-MiniDaemon.prototype.PAUSED = true;
-MiniDaemon.prototype.BACKW = true;
-
-  /* Global methods */
-
-MiniDaemon.forceCall = function (oDmn) {
-  oDmn.INDEX += oDmn.BACKW ? -1 : 1;
-  if (oDmn.task.call(oDmn.owner, oDmn.INDEX, oDmn.length, oDmn.BACKW) === false || oDmn.isAtEnd()) { oDmn.pause(); return false; }
-  return true;
-};
-
-  /* Instances methods */
-
-MiniDaemon.prototype.isAtEnd = function () {
-  return this.BACKW ? isFinite(this.length) && this.INDEX < 1 : this.INDEX + 1 > this.length;
-};
-
-MiniDaemon.prototype.synchronize = function () {
-  if (this.PAUSED) { return; }
-  clearInterval(this.SESSION);
-  this.SESSION = setInterval(MiniDaemon.forceCall, this.rate, this);
-};
-
-MiniDaemon.prototype.pause = function () {
-  clearInterval(this.SESSION);
-  this.PAUSED = true;
-};
-
-MiniDaemon.prototype.start = function (bReverse) {
-  var bBackw = Boolean(bReverse);
-  if (this.BACKW === bBackw && (this.isAtEnd() || !this.PAUSED)) { return; }
-  this.BACKW = bBackw;
-  this.PAUSED = false;
-  this.synchronize();
-};
-
- -
MiniDaemon pasa argumentos a la función callback. Si quieres trabajar con ellos en navegadores que no soportan nativamente esta característica, usa uno de los métodos propuestos arriba.
- -

Sintaxis

- -

var myDaemon = new MiniDaemon(thisObject, callback[, rate[, length]]);

- -

Descripción

- -

Regresa un Objecto que contiene la información necesaria para una animación (como el objeto this, la función de Callback, la duración y el frame-rate).

- -

Parámetros

- -
-
thisObject
-
El valor de la palabra this sobre el cual funcionará la función de Callback. Puede ser un objecto o null.
-
callback
-
La función que se invocará repetidas veces. Dicha función se invocará con tres parámetros: index que corresponde al valor iterativo de cada invocación, length que es el número total de invocaciones asignadas al daemon (puede ser un valor finito o Infinity) y backwards (valor booleano que expresa cuando el valor de index es creciente o decreciente). Es similar a callback.call(thisObject, index, length, backwards). Si la función de Callback regresa un valor false el deamon se detiene.
-
rate (optional)
-
El tiempo minimo en milisegundos que transcurre entre cada invocación. El valor por omisión es 100.
-
length (optional)
-
El número total de invocaciones. Puede ser un valor entero positivo o Infinity. El valor por omisión es Infinity.
-
- -

Propiedades de la intancia MiniDaemon 

- -
-
myDaemon.owner
-
El objeto this sobre el cual se ejecuta el daemon (lectura/escritura). Puede ser un objecto o null.
-
myDaemon.task
-
La función que se invocará repetidas veces. Dicha función se invocará con tres parámetros: index que corresponde al valor iterativo de cada invocación, length que es el número total de invocaciones asignadas al daemon (puede ser un valor finito o Infinity) y backwards (valor booleano que expresa cuando el valor de index es creciente o decreciente). Es similar a callback.call(thisObject, index, length, backwards). Si la función de Callback regresa un valor false el deamon se detiene.
-
myDaemon.rate
-
El tiempo minimo en milisegundos que transcurre entre cada invocación. El valor por omición es 100 (lectura/escritura).
-
myDaemon.length
-
El número total de invocaciones. Puede ser un valor entero positivo o Infinity. El valor por omición es Infinity (lectura/escritura).
-
- -

MiniDaemon instances methods

- -
-
myDaemon.isAtEnd()
-
Regresa un valor boleano que expresa cuando el daemon está en posición de inicio/fin o no.
-
myDaemon.synchronize()
-
Sincroniza el tiempo de un deamon iniciado con el tiempo de su invocación.
-
myDaemon.pause()
-
Pausa el deamon.
-
myDaemon.start([reverse])
-
Inicia el daemon hacia adelante "forward" (el indice de cada invocación se incrementa) o hacia atrás "backwards" (el índice de cada invocación se decrementa).
-
- -

Métodos del objeto global del MiniDaemon

- -
-
MiniDaemon.forceCall(minidaemon)
-
Fuerza una sola función callback a la función minidaemon.task  en lugar del hecho de que se ha alcanzado el final o no. En cualquier caso la propiedad INDEX interna crece o decrece según la dirección del proceso.
-
- -

Ejemplo de uso

- -

Tu página HTML:

- -
<!DOCTYPE html>
-<html>
-<head>
-  <meta charset="UTF-8" />
-  <title>MiniDaemin Example - MDN</title>
-  <script type="text/javascript" src="minidaemon.js"></script>
-  <style type="text/css">
-    #sample_div {
-      visibility: hidden;
-    }
-  </style>
-</head>
-
-<body>
-  <p>
-    <input type="button" onclick="fadeInOut.start(false /* optional */);" value="fade in" />
-    <input type="button" onclick="fadeInOut.start(true);" value="fade out">
-    <input type="button" onclick="fadeInOut.pause();" value="pause" />
-  </p>
-
-  <div id="sample_div">Some text here</div>
-
-  <script type="text/javascript">
-    function opacity (nIndex, nLength, bBackwards) {
-      this.style.opacity = nIndex / nLength;
-      if (bBackwards ? nIndex === 0 : nIndex === 1) {
-        this.style.visibility = bBackwards ? "hidden" : "visible";
-      }
-    }
-
-    var fadeInOut = new MiniDaemon(document.getElementById("sample_div"), opacity, 300, 8);
-  </script>
-</body>
-</html>
- -

Prueba este ejemplo

- -

Notas

- -

La función setInterval() es usada frecuentemente para asignar una pausa para ejecutar funciones recurrentes, como por ejemplo pintar el siguiente cuadro de una animación.

- -

Puedes cancelar el ciclo iniciado por un setInterval() usando el comando window.clearInterval().

- -

Si solo deseas ejecutar el ciclo una sola vez despues de una pausa usa en su lugar la función window.setTimeout().

- -

Asegúrate que el tiempo de ejecución sea menor que la frecuencia

- -

Si existe la posibilidad de que tu función o el código a ejecutarse una y otra vez exeda el tiempo marcado en cada intervalo es recomendado que uses recursivamente el nombre de tu función usando window.setTimeout. Por ejemplo, si usas setInterval para hacer llamadas a un servidor remoto cada 5 segundos, la latencia en la red, un servidor que no responde, o cualquier otro tipo de contratiempo puede generar una pausa mayor a la que indicaste. De esta forma terminarás con solicitudes XHR apiladas que no se resolverán necesariamente en orden.

- -

En estos casos llamadas con un patrón de setTimeout() recursivo es preferible:

- -
(function loop(){
-   setTimeout(function() {
-      // Your logic here
-
-      loop();
-  }, delay);
-})();
-
- -

En este fragmento de código, la función loop() es declarada y es ejecutada inmediatamente. La función loop() es invocada de forma recursiva dentro de setTimeout() despues de cada ejecución. Si bien este patrón no garantiza una ejecución a intervalos fijos, si garantiza que nunca se ejecutará un paso sin que se haya finalizado el anterior. 

- -

Especificaciones

- - - - - - - - - - - - - - -
EspecificacionesEstatusComentarios
{{SpecName("HTML WHATWG", "webappapis.html#dom-setinterval", "WindowTimers.setInterval()")}}{{Spec2("HTML WHATWG")}}Definición inicial (DOM Level 0)
- -

Compatibilidad

- -
{{CompatibilityTable}}
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
CaracterísticaChromeFirefox (Gecko)[2]Internet ExplorerOperaSafari
Soporte básico1.0{{CompatGeckoDesktop("1")}}4.04.01.0
Soporta parámetros para callback[1]{{CompatVersionUnknown}}{{CompatVersionUnknown}}10.0{{CompatVersionUnknown}}{{CompatUnknown}}
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CaracterísticaAndroidChrome for AndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Soporte básico1.01.0{{CompatGeckoMobile("1")}}6.06.01.0
Soporta parámetros para callback[1]{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}{{CompatUnknown}}
-
- -

[1] Whether it supports the optional parameters when in its first form or not.

- -

[2] Anterior a Firefox 13, Firefox pasaba un parametro adicional al callback, indicando el "actual lateness" del timeout en milisegundos. Este parámetro no estandar dejó de usarse en versiones posteriores a Firefox 13. No es recomendable que extensiones basadas en XPCOM para Firefox usen setInterval(), ya que las actualizaciones pueden causar el que el objeto {{domxref("Window")}} se actualice perdiendo los temporizadores. Deberás usar en su lugar {{interface("nsITimer")}}.

- -

Ver más

- - diff --git a/files/es/web/api/windowtimers/settimeout/index.html b/files/es/web/api/windowtimers/settimeout/index.html deleted file mode 100644 index 1180d9f8af..0000000000 --- a/files/es/web/api/windowtimers/settimeout/index.html +++ /dev/null @@ -1,340 +0,0 @@ ---- -title: WindowOrWorkerGlobalScope.setTimeout -slug: Web/API/WindowTimers/setTimeout -tags: - - API - - HTML DOM - - WindowOrWorkerGlobalScope - - setTimeout -translation_of: Web/API/WindowOrWorkerGlobalScope/setTimeout ---- -
{{APIRef("HTML DOM")}}
- -

El método setTimeout() del mixin {{domxref("WindowOrWorkerGlobalScope")}} establece un temporizador que ejecuta una función o una porción de código después de que transcurre un tiempo establecido.

- - -

Sintaxis

- -
var idTemporizador = scope.setTimeout(funcion[, retraso, parametro1, parametro2, ...]);
-var idTimeout = scope.setTimeout(funcion[, retraso]);
-var idTimeout = scope.setTimeout(codigo[, retraso]);
-
- -

Parámetros

- -
-
funcion
-
Una {{jsxref("function")}} para ejecutar después de que expire el temporizador.
-
codigo
-
Una sintaxis opcional que le permite incluir una cadena en lugar de una función, la cual es compilada y ejecutada cuando el temporizador expira. Esta sintaxis no se recomienda por las mismas razones que hacen del uso de {{jsxref("Global_Objects/eval", "eval()")}} un riesgo de seguridad.
-
retraso {{optional_inline}}
-
Tiempo, en milisegundos  (milésimas de segundo), que el temporizador debe esperar antes de ejecutar la función o el código. Si se omite este parámetro se usa el valor 0. Tenga en cuenta que el retraso real puede ser más prolongado; ver más abajo {{anch("Reasons for delays longer than specified")}}.
-
param1, ..., paramN {{optional_inline}}
-
Parámetros adicionales que se pasan a la función especificada por  func una vez el temporizador expira.
-
- -
Nota: Pasar parámetros adicionales a la función en la primera sintaxis no funciona en Internet Explorer 9 o inferior. Si quiere habilitar esta funcionalidad en ese navegador,  debe usar un código de compatibilidad (vea la sección Callback arguments).
- -

Valor retornado

- -

El valor retornado IDtemporizador es númerico y no es cero; identifica el temporizador creado con la llamada a setTimeout(); este valor puede pasarse a {{domxref("WindowOrWorkerGlobalScope.clearTimeout()")}} para cancelar el temporizador.

- -

Puede ser útil advertir que  setTimeout() y {{domxref("WindowOrWorkerGlobalScope.setInterval", "setInterval()")}} comparten la misma piscina de IDs, y que tanto clearTimeout() como {{domxref("WindowOrWorkerGlobalScope.clearInterval", "clearInterval()")}} pueden intercambiarse.  Por claridad, sin embargo,  debe hacerlos coincidir para evitar confusiones cuando mantenga su código.

- -

Ejemplo

- -

El siguiente ejemplo establece dos botenes simples en una página web y los engancha a las rutinas setTimeout() y clearTimeout(). Presionando el primer botón establecerá un temporizador que llama un diálogo de alerta después de dos segundos y guarda el id del temporizador para usarlo con clearTimeout(). Opcionalmente puede cancelar este temporizador presionando el segundo botón.

- -

Contenido HTML

- -
<p>Ejemplo funcional</p>
-<button onclick="delayedAlert();">Muestra una caja de alerta después de dos segundos</button>
-<p></p>
-<button onclick="clearAlert();">Cancela la alerta antes de que ocurra</button>
-
- -

Contenido JavaScript

- -
var timeoutID;
-
-function delayedAlert() {
-  timeoutID = window.setTimeout(slowAlert, 2000);
-}
-
-function slowAlert() {
-  alert("That was really slow!");
-}
-
-function clearAlert() {
-  window.clearTimeout(timeoutID);
-}
-
- -

{{ EmbedLiveSample('Example') }}

- -

Vea también clearTimeout() example.

- -

Callback arguments

- -

Si necesita pasar un argumento a su función callback, pero necesita que funcione en Internet Explorer, que no soporta el envío de parámetros adicionales (ni con setTimeout()setInterval()) usted puede incluir este código de compatibilidad IE-specific que habilitará la funcionalidad estándar de HTML5 para pasar los parámetros adicionales en ese navegador para ambos temporizadores solamente insertandolo al inicio de sus scripts.

- -
/*\
-|*|
-|*|  IE-specific polyfill which enables the passage of arbitrary arguments to the
-|*|  callback functions of JavaScript timers (HTML5 standard syntax).
-|*|
-|*|  https://developer.mozilla.org/en-US/docs/DOM/window.setInterval
-|*|
-|*|  Syntax:
-|*|  var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]);
-|*|  var timeoutID = window.setTimeout(code, delay);
-|*|  var intervalID = window.setInterval(func, delay[, param1, param2, ...]);
-|*|  var intervalID = window.setInterval(code, delay);
-|*|
-\*/
-
-if (document.all && !window.setTimeout.isPolyfill) {
-  var __nativeST__ = window.setTimeout;
-  window.setTimeout = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
-    var aArgs = Array.prototype.slice.call(arguments, 2);
-    return __nativeST__(vCallback instanceof Function ? function () {
-      vCallback.apply(null, aArgs);
-    } : vCallback, nDelay);
-  };
-  window.setTimeout.isPolyfill = true;
-}
-
-if (document.all && !window.setInterval.isPolyfill) {
-  var __nativeSI__ = window.setInterval;
-  window.setInterval = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
-    var aArgs = Array.prototype.slice.call(arguments, 2);
-    return __nativeSI__(vCallback instanceof Function ? function () {
-      vCallback.apply(null, aArgs);
-    } : vCallback, nDelay);
-  };
-  window.setInterval.isPolyfill = true;
-}
-
- -

Arreglo solo para IE

- -

Si quiere una solución completamente no intrusiva con otros navegadores móviles o de escritorio, incluyendo IE 9 y superior, puede usar los comentarios condicionales de JavaScript:

- -
/*@cc_on
-  // conditional IE < 9 only fix
-  @if (@_jscript_version <= 6)
-  (function(f){
-     window.setTimeout =f(window.setTimeout);
-     window.setInterval =f(window.setInterval);
-  })(function(f){return function(c,t){var a=[].slice.call(arguments,2);return f(function(){c.apply(this,a)},t)}});
-  @end
-@*/
-
- -

O usar un enfoque más limpio basado en el condicional para IE de HTML:

- -
<!--[if lt IE 9]><script>
-(function(f){
-window.setTimeout =f(window.setTimeout);
-window.setInterval =f(window.setInterval);
-})(function(f){return function(c,t){
-var a=[].slice.call(arguments,2);return f(function(){c.apply(this,a)},t)}
-});
-</script><![endif]-->
-
- -

Otra posibilidad es usar una función anónima para llamar el callback, pero esta solución es un poco más costosa. Ejemplo:

- -
var intervalID = setTimeout(function() { myFunc("uno", "dos", "tres"); }, 1000);
-
- -

Sin embargo, otra posibilidad es usar function's bind. Ejemplo:

- -
setTimeout(function(arg1){}.bind(undefined, 10));
-
- -

El problema "this"

- -

Cuando pasa un método a setTimeout() (o cualquier otra función , por el estilo), podría ser invocada con el valor de this equivocado. Este problema es explicado en detalle en la referencia de JavaScript.

- -

Explicación

- -

El código ejecutado por setTimeout() corre en un contexto de ejecución diferente al de la función por la que fue llamado. Como consecuencia, la palabra clave this para la función llamada será asignado al objeto window (o global); no tendrá el mismo valor del this de la función que llamó al setTimeout. Vea el siguiente ejemplo:

- -
myArray = ["cero", "uno", "dos"];
-myArray.myMethod = function (sProperty) {
-    alert(arguments.length > 0 ? this[sProperty] : this);
-};
-
-myArray.myMethod(); // imprime "cero,uno,dos"
-myArray.myMethod(1); // imprime "uno"
-setTimeout(myArray.myMethod, 1000); // imprime "[object Window]" después de 1 segundo
-setTimeout(myArray.myMethod, 1500, "1"); // imprime "undefined" después de 1.5 segundos
-// intentemos pasar el objeto 'this'
-setTimeout.call(myArray, myArray.myMethod, 2000); // error: "NS_ERROR_XPC_BAD_OP_ON_WN_PROTO: Illegal operation on WrappedNative prototype object"
-setTimeout.call(myArray, myArray.myMethod, 2500, 2); // mismo error
- -

Como puedes ver no hay forma de pasar el objeto this a la función callback.

- -

Una posible solución

- -

Una posible forma de resolver el problema del "this" es reemplazar las dos funciones globales nativas setTimeout() or setInterval()por dos no-nativas  que permitan su invocación a través del método Function.prototype.call. El siguiente ejemplo muestra un posible reemplazo:

- -
// Enable the passage of the 'this' object through the JavaScript timers
-
-var __nativeST__ = window.setTimeout, __nativeSI__ = window.setInterval;
-
-window.setTimeout = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
-  var oThis = this, aArgs = Array.prototype.slice.call(arguments, 2);
-  return __nativeST__(vCallback instanceof Function ? function () {
-    vCallback.apply(oThis, aArgs);
-  } : vCallback, nDelay);
-};
-
-window.setInterval = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
-  var oThis = this, aArgs = Array.prototype.slice.call(arguments, 2);
-  return __nativeSI__(vCallback instanceof Function ? function () {
-    vCallback.apply(oThis, aArgs);
-  } : vCallback, nDelay);
-};
- -
Nota: Estos dos reemplazos habilitarán el estándar HTML5 para el paso de argumentos arbitrarios a las funciones callback de los temporizadores en IE. Pueden usarse como polyfills también. Vea el párrafo Callback arguments.
- -

Prueba de la nueva característica:

- -
myArray = ["zero", "one", "two"];
-myArray.myMethod = function (sProperty) {
-    alert(arguments.length > 0 ? this[sProperty] : this);
-};
-
-setTimeout(alert, 1500, "Hello world!"); // the standard use of setTimeout and setInterval is preserved, but...
-setTimeout.call(myArray, myArray.myMethod, 2000); // prints "zero,one,two" after 2 seconds
-setTimeout.call(myArray, myArray.myMethod, 2500, 2); // prints "two" after 2.5 seconds
-
- -

No hay soluciones nativas ad hoc a este problema.

- -
Nota: JavaScript 1.8.5 introduce el método Function.prototype.bind(, que permite especificar el valor que debería usarse como this para todas las llamadas a una función dada. Esto permite evitar fácilmente los problemas en los que no es claro que será, dependiendo del contexto desde el cual la función sea llamada.
- -

Notas

- -

Puede cancelar el temporizador usando window.clearTimeout(). Si desea tener una función llamada repetidamente (p.e., cada N milisegundos), considere usar window.setInterval().

- -

Es importante notar que la función o fragmento de código no puede ser ejecutado hasta que el hilo que llamó setTimeout()haya terminado.

- -

Pasando cadenas literales

- -

Pasando una cadena en vez de una función a setTimeout()pasa lo mismo que al usar eval.

- -
// Correcto
-window.setTimeout(function() {
-    alert("Hello World!");
-}, 500);
-
-// Incorrecto
-window.setTimeout("alert(\"Hello World!\");", 500);
-
-
- -

Las cadenas literales son evaluadas en el contexto global, así que los símbolos locales en el contexto donde setTimeout() fue llamado no estarán disponibles cuando la cadena es evaluada como código.

- -

Minimum/ maximum delay and timeout nesting

- -

Historically browsers implement setTimeout() "clamping": successive setTimeout() calls with delay smaller than the "minimum delay" limit are forced to use at least the minimum delay. The minimum delay, DOM_MIN_TIMEOUT_VALUE, is 4 ms (stored in a preference in Firefox: dom.min_timeout_value), with a DOM_CLAMP_TIMEOUT_NESTING_LEVEL of 5ms.

- -

In fact, 4ms is specified by the HTML5 spec and is consistent across browsers released in 2010 and onward. Prior to {{ geckoRelease("5.0") }}, the minimum timeout value for nested timeouts was 10 ms.

- -

In addition to "clamping", the timeout can also fire later when the page (or the OS/browser itself) is busy with other tasks.

- -

To implement a 0 ms timeout in a modern browser, you can use {{ domxref("window.postMessage()") }} as described here.

- -

Browsers including Internet Explorer, Chrome, Safari, and Firefox store the delay as a 32-bit signed Integer internally. This causes an Integer overflow when using delays larger than 2147483647, resulting in the timeout being executed immediately.

- -

Inactive tabs

- -

In {{ geckoRelease("5.0") }} and Chrome 11, timeouts are clamped to firing no more often than once per second (1000ms) in inactive tabs; see {{ bug(633421) }} for more information about this in Mozilla or crbug.com/66078 for details about this in Chrome.

- -

Compatibilidad de navegadores

- -

{{ CompatibilityTable() }}

- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
CaracterísticaChromeFirefox (Gecko)Internet ExplorerOperaSafari
Soporte básico1.0{{ CompatGeckoDesktop("1") }}4.04.01.0
Soporta parámetros para callback*1{{ CompatVersionUnknown }}{{ CompatVersionUnknown }}10.0{{ CompatVersionUnknown }}{{ CompatUnknown }}
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CaracterísticaAndroidChrome for AndroidFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Soporte básico1.01.0{{ CompatGeckoMobile("1") }}6.06.01.0
Soporta parámetros para callback*1{{ CompatUnknown }}{{ CompatUnknown }}{{ CompatUnknown }}{{ CompatUnknown }}{{ CompatUnknown }}{{ CompatUnknown }}
-
- -

*1 Whether it supports the optional parameters when in its first form or not.

- -

Especificación

- -

Parte del DOM nivel 0, como se especifica en HTML5.

- -

Vea también

- - diff --git a/files/es/web/api/xmldocument/async/index.html b/files/es/web/api/xmldocument/async/index.html new file mode 100644 index 0000000000..132fd106e1 --- /dev/null +++ b/files/es/web/api/xmldocument/async/index.html @@ -0,0 +1,33 @@ +--- +title: Document.async +slug: Web/API/Document/async +translation_of: Web/API/XMLDocument/async +--- +

document.async es utilizado para indicar cuándo un llamado de  {{domxref("document.load")}} debe ser sincrónico o asincrónico. true es su valor por defecto, indicando que el documento se cargó asincrónicamente.

+ +

(Desde la versión 1.4 alpha es posible cargar documentos sincrónicamente)

+ +

Ejemplo

+ +
function loadXMLData(e) {
+  alert(new XMLSerializer().serializeToString(e.target)); // Devuelve los contenidos de querydata.xml como un string
+}
+
+var xmlDoc = document.implementation.createDocument("", "test", null);
+
+xmlDoc.async = false;
+xmlDoc.onload = loadXMLData;
+xmlDoc.load('querydata.xml');
+ +

Especificación

+ + + +

Véase también

+ + diff --git a/files/es/web/api/xmlhttprequest/formdata/index.html b/files/es/web/api/xmlhttprequest/formdata/index.html deleted file mode 100644 index 2ca830daf7..0000000000 --- a/files/es/web/api/xmlhttprequest/formdata/index.html +++ /dev/null @@ -1,84 +0,0 @@ ---- -title: FormData -slug: Web/API/XMLHttpRequest/FormData -tags: - - API - - FormData - - Interfaz - - Referencia - - XMLHttpRequest -translation_of: Web/API/FormData ---- -

{{APIRef("XMLHttpRequest")}}

- -

La interfaz FormData proporciona una manera sencilla de construir un conjunto de parejas clave/valor que representan los campos de un formulario y sus valores, que pueden ser enviados fácilmente con el método {{domxref("XMLHttpRequest.send()")}}. Utiliza el mismo formato que usaría un formulario si el tipo de codificación fuera "multipart/form-data".

- -

También puede pasarse directamente al constructor de {{domxref("URLSearchParams")}} si se quieren generar parámetros de consulta de la misma forma en que lo haría un {{HTMLElement("form")}} si usara un envío GET simple.

- -

Un objeto que implementa FormData puede usarse directamente en una estructura {{jsxref("Statements/for...of", "for...of")}}, en lugar de {{domxref('FormData.entries()', 'entries()')}}: for (var p of myFormData) es equivalente a for (var p of myFormData.entries()).

- -
-

Nota: Esta característica está disponible en Web Workers.

-
- -

Constructor

- -
-
{{domxref("FormData.FormData","FormData()")}}
-
Crea un nuevo objeto FormData.
-
- -

Métodos

- -
-
{{domxref("FormData.append()")}}
-
Agrega un nuevo valor a una clave existente dentro de un objeto FormData, o añade la clave si aún no existe.
-
{{domxref("FormData.delete()")}}
-
Elimina una pareja clave/valor de un objeto FormData.
-
{{domxref("FormData.entries()")}}
-
Devuelve un {{jsxref("Iteration_protocols","iterator")}} que permite recorrer todas las parejas clave/valor contenidas en este objeto.
-
{{domxref("FormData.get()")}}
-
Devuelve el primer valor asociado con una clave dada en un objeto FormData.
-
{{domxref("FormData.getAll()")}}
-
Devuelve un array con todos los valores asociados con una clave dada en un objeto FormData.
-
{{domxref("FormData.has()")}}
-
Devuelve un booleano que indica si un objeto FormData contiene una clave determinada.
-
{{domxref("FormData.keys()")}}
-
Devuelve un {{jsxref("Iteration_protocols", "iterator")}} que permite recorrer todas las claves de las parejas clave/valor contenidas en este objeto.
-
{{domxref("FormData.set()")}}
-
Establece un nuevo valor para una clave existente dentro de un objeto FormData, o agrega la clave/valor si aún no existe.
-
{{domxref("FormData.values()")}}
-
Devuelve un {{jsxref("Iteration_protocols", "iterator")}} que permite recorrer todos los valores contenidos en este objeto.
-
- -

Especificaciones

- - - - - - - - - - - - - - -
EspecificaciónEstadoComentario
{{SpecName('XMLHttpRequest','#interface-formdata','FormData')}}{{Spec2('XMLHttpRequest')}}FormData definido en XHR spec
- -

Compatibilidad con navegadores

- - - -

{{Compat("api.FormData")}}

- -

Ver también

- - diff --git a/files/es/web/api/xmlhttprequest/loadend_event/index.html b/files/es/web/api/xmlhttprequest/loadend_event/index.html new file mode 100644 index 0000000000..39e528634d --- /dev/null +++ b/files/es/web/api/xmlhttprequest/loadend_event/index.html @@ -0,0 +1,91 @@ +--- +title: loadend +slug: Web/Events/loadend +tags: + - eventos +translation_of: Web/API/XMLHttpRequest/loadend_event +--- +

El evento loadend es emitido cuando el progreso de la carga de un recurso se ha detenido (e.g. despues que "error", "abort", o "load" han sido emitidos). Por ejemplo, esto aplica a las llamadas de {{domxref("XMLHttpRequest")}}, y al contenido de un elemento {{htmlelement("img")}} o {{htmlelement("video")}}.

+ +

Información General

+ +
+
Especificación
+
Progress
+
Interfaz
+
ProgressEvent
+
Bubbles
+
No
+
Cancelable
+
No
+
Target
+
{{domxref("HTMLImageElement")}}, Por Ejemplo
+
Acción por Defecto
+
None
+
+ +

Propiedades

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyTypeDescription
target {{readonlyInline}}{{domxref("EventTarget")}}The event target (the topmost target in the DOM tree).
type {{readonlyInline}}{{domxref("DOMString")}}The type of event.
bubbles {{readonlyInline}}{{jsxref("Boolean")}}Whether the event normally bubbles or not.
cancelable {{readonlyInline}}{{jsxref("Boolean")}}Whether the event is cancellable or not.
lengthComputable {{readonlyInline}}{{jsxref("Boolean")}}Specifies whether or not the total size of the transfer is known. Read only.
loaded {{readonlyInline}}unsigned long (long)The number of bytes transferred since the beginning of the operation. This doesn't include headers and other overhead, but only the content itself. Read only.
total {{readonlyInline}}unsigned long (long)The total number of bytes of content that will be transferred during the operation. If the total size is unknown, this value is zero. Read only.
+ +

Eventos Relacionados

+ + + +

Ver También

+ + -- cgit v1.2.3-54-g00ecf