diff --git a/1-js/01-getting-started/1-intro/article.md b/1-js/01-getting-started/1-intro/article.md index a58d5d725..b316d80da 100644 --- a/1-js/01-getting-started/1-intro/article.md +++ b/1-js/01-getting-started/1-intro/article.md @@ -1,122 +1,123 @@ -# An Introduction to JavaScript +# Una introducción a JavaScript -Let's see what's so special about JavaScript, what we can achieve with it, and what other technologies play well with it. +Veamos qué tiene de especial JavaScript, qué podemos lograr con este lenguaje y qué otras tecnologías se integran bien con él. -## What is JavaScript? +## ¿Qué es JavaScript? -*JavaScript* was initially created to "make web pages alive". +_JavaScript_ fue creado para _"dar vida a las páginas web"_. -The programs in this language are called *scripts*. They can be written right in a web page's HTML and run automatically as the page loads. +Los programas en este lenguaje se llaman _scripts_. Se pueden escribir directamente en el HTML de una página web y ejecutarse automáticamente a medida que se carga la página. -Scripts are provided and executed as plain text. They don't need special preparation or compilation to run. +Los scripts se proporcionan y ejecutan como texto plano. No necesitan preparación especial o compilación para correr. -In this aspect, JavaScript is very different from another language called [Java](https://en.wikipedia.org/wiki/Java_(programming_language)). +En este aspecto, JavaScript es muy diferente a otro lenguaje llamado [Java](). -```smart header="Why is it called JavaScript?" -When JavaScript was created, it initially had another name: "LiveScript". But Java was very popular at that time, so it was decided that positioning a new language as a "younger brother" of Java would help. +```smart header="¿Por qué se llama JavaScript?" +Cuando JavaScript fue creado, inicialmente tenía otro nombre: "LiveScript". Pero Java era muy popular en ese momento, así que se decidió que el posicionamiento de un nuevo lenguaje como un "Hermano menor" de Java ayudaría. -But as it evolved, JavaScript became a fully independent language with its own specification called [ECMAScript](http://en.wikipedia.org/wiki/ECMAScript), and now it has no relation to Java at all. +Pero a medida que evolucionaba, JavaScript se convirtió en un lenguaje completamente independiente con su propia especificación llamada [ECMAScript](https://es.wikipedia.org/wiki/ECMAScript), y ahora no tiene ninguna relación con Java. ``` -Today, JavaScript can execute not only in the browser, but also on the server, or actually on any device that has a special program called [the JavaScript engine](https://en.wikipedia.org/wiki/JavaScript_engine). +Hoy, JavaScript puede ejecutarse no solo en los navegadores, sino también en servidores o incluso en cualquier dispositivo que cuente con un programa especial llamado [El motor o intérprete de JavaScript](https://es.wikipedia.org/wiki/Int%C3%A9rprete_de_JavaScript). -The browser has an embedded engine sometimes called a "JavaScript virtual machine". +El navegador tiene un motor embebido a veces llamado una "Máquina virtual de JavaScript". -Different engines have different "codenames". For example: +Diferentes motores tienen diferentes "nombres en clave". Por ejemplo: -- [V8](https://en.wikipedia.org/wiki/V8_(JavaScript_engine)) -- in Chrome, Opera and Edge. -- [SpiderMonkey](https://en.wikipedia.org/wiki/SpiderMonkey) -- in Firefox. -- ...There are other codenames like "Chakra" for IE, "JavaScriptCore", "Nitro" and "SquirrelFish" for Safari, etc. +- [V8](https://es.wikipedia.org/wiki/Chrome_V8) -- en Chrome, Opera y Edge. +- [SpiderMonkey](https://es.wikipedia.org/wiki/SpiderMonkey) -- en Firefox. +- ...Existen otros nombres en clave como "Chakra" para IE , "JavaScriptCore", "Nitro" y "SquirrelFish" para Safari, etc. -The terms above are good to remember because they are used in developer articles on the internet. We'll use them too. For instance, if "a feature X is supported by V8", then it probably works in Chrome, Opera and Edge. +Es bueno recordar estos términos porque son usados en artículos para desarrolladores en internet. También los usaremos. Por ejemplo, si "la característica X es soportada por V8", entonces probablemente funciona en Chrome, Opera y Edge. -```smart header="How do engines work?" +```smart header="¿Como trabajan los motores?" -Engines are complicated. But the basics are easy. +Los motores son complicados, pero los fundamentos son fáciles. -1. The engine (embedded if it's a browser) reads ("parses") the script. -2. Then it converts ("compiles") the script to machine code. -3. And then the machine code runs, pretty fast. +1. El motor (embebido si es un navegador) lee ("analiza") el script. +2. Luego convierte ("compila") el script a lenguaje de máquina. +3. Por último, el código máquina se ejecuta, muy rápido. -The engine applies optimizations at each step of the process. It even watches the compiled script as it runs, analyzes the data that flows through it, and further optimizes the machine code based on that knowledge. +El motor aplica optimizaciones en cada paso del proceso. Incluso observa como el script compilado se ejecuta, analiza los datos que fluyen a través de él y aplica optimizaciones al código maquina basadas en ese conocimiento. ``` -## What can in-browser JavaScript do? +## ¿Qué puede hacer JavaScript en el navegador? -Modern JavaScript is a "safe" programming language. It does not provide low-level access to memory or the CPU, because it was initially created for browsers which do not require it. +El JavaScript moderno es un lenguaje de programación "seguro". No proporciona acceso de bajo nivel a la memoria ni a la CPU (UCP); ya que se creó inicialmente para los navegadores, los cuales no lo requieren. -JavaScript's capabilities greatly depend on the environment it's running in. For instance, [Node.js](https://wikipedia.org/wiki/Node.js) supports functions that allow JavaScript to read/write arbitrary files, perform network requests, etc. +Las capacidades de JavaScript dependen en gran medida en el entorno en que se ejecuta. Por ejemplo, [Node.JS](https://es.wikipedia.org/wiki/Node.js) soporta funciones que permiten a JavaScript leer y escribir archivos arbitrariamente, realizar solicitudes de red, etc. -In-browser JavaScript can do everything related to webpage manipulation, interaction with the user, and the webserver. +En el navegador JavaScript puede realizar cualquier cosa relacionada con la manipulación de una página web, interacción con el usuario y el servidor web. -For instance, in-browser JavaScript is able to: +Por ejemplo, en el navegador JavaScript es capaz de: -- Add new HTML to the page, change the existing content, modify styles. -- React to user actions, run on mouse clicks, pointer movements, key presses. -- Send requests over the network to remote servers, download and upload files (so-called [AJAX](https://en.wikipedia.org/wiki/Ajax_(programming)) and [COMET](https://en.wikipedia.org/wiki/Comet_(programming)) technologies). -- Get and set cookies, ask questions to the visitor, show messages. -- Remember the data on the client-side ("local storage"). +- Agregar nuevo HTML a la página, cambiar el contenido existente y modificar estilos. +- Reaccionar a las acciones del usuario, ejecutarse con los clics del ratón, movimientos del puntero y al oprimir teclas. +- Enviar solicitudes de red a servidores remotos, descargar y cargar archivos (Tecnologías llamadas [AJAX](https://es.wikipedia.org/wiki/AJAX) y [COMET](https://es.wikipedia.org/wiki/Comet)). +- Obtener y configurar cookies, hacer preguntas al visitante y mostrar mensajes. +- Recordar datos en el lado del cliente con el almacenamiento local ("local storage"). -## What CAN'T in-browser JavaScript do? +## ¿Qué NO PUEDE hacer JavaScript en el navegador? -JavaScript's abilities in the browser are limited to protect the user's safety. The aim is to prevent an evil webpage from accessing private information or harming the user's data. +Las capacidades de JavaScript en el navegador están limitadas para proteger la seguridad de usuario. El objetivo es evitar que una página maliciosa acceda a información privada o dañe los datos de usuario. -Examples of such restrictions include: +Ejemplos de tales restricciones incluyen: -- JavaScript on a webpage may not read/write arbitrary files on the hard disk, copy them or execute programs. It has no direct access to OS functions. +- JavaScript en el navegador no puede leer y escribir arbitrariamente archivos en el disco duro, copiarlos o ejecutar programas. No tiene acceso directo a funciones del Sistema operativo (OS). - Modern browsers allow it to work with files, but the access is limited and only provided if the user does certain actions, like "dropping" a file into a browser window or selecting it via an `` tag. + Los navegadores más modernos le permiten trabajar con archivos, pero el acceso es limitado y solo permitido si el usuario realiza ciertas acciones, como "arrastrar" un archivo a la ventana del navegador o seleccionarlo por medio de una etiqueta `. - There are ways to interact with the camera/microphone and other devices, but they require a user's explicit permission. So a JavaScript-enabled page may not sneakily enable a web-camera, observe the surroundings and send the information to the [NSA](https://en.wikipedia.org/wiki/National_Security_Agency). -- Different tabs/windows generally do not know about each other. Sometimes they do, for example when one window uses JavaScript to open the other one. But even in this case, JavaScript from one page may not access the other page if they come from different sites (from a different domain, protocol or port). + Existen maneras de interactuar con la cámara, micrófono y otros dispositivos, pero eso requiere el permiso explícito del usuario. Por lo tanto, una página habilitada para JavaScript no puede habilitar una cámara web para observar el entorno y enviar la información a la [NSA](https://es.wikipedia.org/wiki/Agencia_de_Seguridad_Nacional). +- Diferentes pestañas y ventanas generalmente no se conocen entre sí. A veces sí lo hacen: por ejemplo, cuando una ventana usa JavaScript para abrir otra. Pero incluso en este caso, JavaScript no puede acceder a la otra si provienen de diferentes sitios (de diferente dominio, protocolo o puerto). - This is called the "Same Origin Policy". To work around that, *both pages* must agree for data exchange and must contain special JavaScript code that handles it. We'll cover that in the tutorial. + Esta restricción es conocida como "política del mismo origen" ("Same Origin Policy"). Es posible la comunicación, pero ambas páginas deben acordar el intercambio de datos y también deben contener el código especial de JavaScript que permite controlarlo. Cubriremos esto en el tutorial. - This limitation is, again, for the user's safety. A page from `http://anysite.com` which a user has opened must not be able to access another browser tab with the URL `http://gmail.com`, for example, and steal information from there. -- JavaScript can easily communicate over the net to the server where the current page came from. But its ability to receive data from other sites/domains is severely limited. Though possible, it requires explicit agreement (expressed in HTTP headers) from the remote side. Once again, that's a safety limitation. + Esta limitación, una vez más, existe para la seguridad del usuario. Una página `http://algunsitio.com` abierta en una pestaña no debe poder acceder a otra pestaña con la URL `http://gmail.com` y robar su información. + +- JavaScript puede comunicarse fácilmente a través de la red con el servidor del que proviene la página actual. Pero su capacidad para recibir información de otros sitios o dominios está restringida. Aunque es posible hacerlo, requiere un acuerdo explícito del sitio remoto mediante encabezados HTTP. Una vez más: esto es una limitación de seguridad. ![](limitations.svg) -Such limitations do not exist if JavaScript is used outside of the browser, for example on a server. Modern browsers also allow plugins/extensions which may ask for extended permissions. +Tales limitaciones no existen si JavaScript es usado fuera del navegador; por ejemplo, en un servidor. Los navegadores modernos también permiten complementos y extensiones que pueden solicitar permisos extendidos. -## What makes JavaScript unique? +## ¿Qué hace a JavaScript único? -There are at least *three* great things about JavaScript: +Existen al menos _tres_ cosas geniales sobre JavaScript: ```compare -+ Full integration with HTML/CSS. -+ Simple things are done simply. -+ Supported by all major browsers and enabled by default. ++ Completa integración con HTML y CSS. ++ Las cosas simples se hacen de manera simple. ++ Soportado por la mayoría de los navegadores y habilitado de forma predeterminada. ``` -JavaScript is the only browser technology that combines these three things. +JavaScript es la única tecnología de los navegadores que combina estas tres cosas. -That's what makes JavaScript unique. That's why it's the most widespread tool for creating browser interfaces. +Eso es lo que hace a JavaScript único. Por esto es la herramienta mas extendida para crear interfaces de navegador. -That said, JavaScript can be used to create servers, mobile applications, etc. +Dicho esto, JavaScript también permite crear servidores, aplicaciones móviles, etc. -## Languages "over" JavaScript +## Lenguajes "por arriba de" JavaScript -The syntax of JavaScript does not suit everyone's needs. Different people want different features. +La sintaxis de JavaScript no se adapta a las necesidades de todos. Personas diferentes querrán diferentes características. -That's to be expected, because projects and requirements are different for everyone. +Esto es algo obvio, porque los proyectos y requerimientos son diferentes para cada persona. -So, recently a plethora of new languages appeared, which are *transpiled* (converted) to JavaScript before they run in the browser. +Así que recientemente han aparecido una gran cantidad de nuevos lenguajes, los cuales son *transpilados* (convertidos) a JavaScript antes de ser ejecutados en el navegador. -Modern tools make the transpilation very fast and transparent, actually allowing developers to code in another language and auto-converting it "under the hood". +Las herramientas modernas hacen la conversión (Transpilación) muy rápida y transparente, permitiendo a los desarrolladores codificar en otros lenguajes y convertirlo automáticamente detrás de escena. -Examples of such languages: +Ejemplos de tales lenguajes: -- [CoffeeScript](https://coffeescript.org/) is "syntactic sugar" for JavaScript. It introduces shorter syntax, allowing us to write clearer and more precise code. Usually, Ruby devs like it. -- [TypeScript](https://www.typescriptlang.org/) is concentrated on adding "strict data typing" to simplify the development and support of complex systems. It is developed by Microsoft. -- [Flow](https://flow.org/) also adds data typing, but in a different way. Developed by Facebook. -- [Dart](https://www.dartlang.org/) is a standalone language that has its own engine that runs in non-browser environments (like mobile apps), but also can be transpiled to JavaScript. Developed by Google. -- [Brython](https://brython.info/) is a Python transpiler to JavaScript that enables the writing of applications in pure Python without JavaScript. -- [Kotlin](https://kotlinlang.org/docs/reference/js-overview.html) is a modern, concise and safe programming language that can target the browser or Node. +- [CoffeeScript](https://coffeescript.org/) Es una "sintaxis azucarada" para JavaScript. Introduce una sintaxis corta, permitiéndonos escribir un código más claro y preciso. Usualmente desarrolladores de Ruby prefieren este lenguaje. +- [TypeScript](https://www.typescriptlang.org/) se concentra en agregar "tipado estricto" ("strict data typing") para simplificar el desarrollo y soporte de sistemas complejos. Es desarrollado por Microsoft. +- [FLow](https://flow.org/) también agrega la escritura de datos, pero de una manera diferente. Desarrollado por Facebook. +- [Dart](https://www.dartlang.org/) es un lenguaje independiente, tiene su propio motor que se ejecuta en entornos que no son de navegador (como aplicaciones móviles), pero que también se puede convertir/transpilar a JavaScript. Desarrollado por Google. +- [Brython](https://brython.info/) es un transpilador de Python a JavaScript que permite escribir aplicaciones en Python puro sin JavaScript. +- [Kotlin](https://kotlinlang.org/docs/reference/js-overview.html) es un lenguaje moderno, seguro y conciso que puede apuntar al navegador o a Node. -There are more. Of course, even if we use one of these transpiled languages, we should also know JavaScript to really understand what we're doing. +Hay más. Por supuesto, incluso si nosotros usamos alguno de estos lenguajes transpilados, deberíamos conocer también JavaScript para realmente entender qué estamos haciendo. -## Summary +## Resumen -- JavaScript was initially created as a browser-only language, but it is now used in many other environments as well. -- Today, JavaScript has a unique position as the most widely-adopted browser language, fully integrated with HTML/CSS. -- There are many languages that get "transpiled" to JavaScript and provide certain features. It is recommended to take a look at them, at least briefly, after mastering JavaScript. +- JavaScript fue inicialmente creado como un lenguaje solamente para el navegador, pero ahora es usado también en muchos otros entornos. +- Hoy en día, JavaScript tiene una posición única como el lenguaje más extendido y adoptado de navegador, con una integración completa con HTML y CSS. +- Existen muchos lenguajes que se convierten o transpilan a JavaScript y aportan ciertas características. Es recomendable echarles un vistazo, al menos brevemente, después de dominar JavaScript. diff --git a/1-js/01-getting-started/2-manuals-specifications/article.md b/1-js/01-getting-started/2-manuals-specifications/article.md index 3fa243336..d1991ac7f 100644 --- a/1-js/01-getting-started/2-manuals-specifications/article.md +++ b/1-js/01-getting-started/2-manuals-specifications/article.md @@ -1,37 +1,37 @@ -# Manuals and specifications +# Manuales y especificaciones -This book is a *tutorial*. It aims to help you gradually learn the language. But once you're familiar with the basics, you'll need other resources. +Este libro es un *tutorial*. Su objetivo es ayudarte a aprender el lenguaje gradualmente. Pero una vez que te familiarices con lo básico, necesitarás otras fuentes. -## Specification +## Especificación -[The ECMA-262 specification](https://www.ecma-international.org/publications/standards/Ecma-262.htm) contains the most in-depth, detailed and formalized information about JavaScript. It defines the language. +[La especificación ECMA-262](https://www.ecma-international.org/publications/standards/Ecma-262.htm) contiene la información más exhaustiva, detallada y formal sobre JavaScript. En ella se define el lenguaje. -But being that formalized, it's difficult to understand at first. So if you need the most trustworthy source of information about the language details, the specification is the right place. But it's not for everyday use. +Pero por su estilo formal, es difícil de entender a primeras. Así que si necesitas la fuente de información más fiable sobre los detalles del lenguaje, esta especificación es el lugar correcto a consultar. Es de entender entonces que no es para el uso diario. -A new specification version is released every year. Between these releases, the latest specification draft is at . +Una nueva versión de la especificación del lenguaje es publicada anualmente. Entre publicaciones, el último borrador de la especificación se puede consultar en . -To read about new bleeding-edge features, including those that are "almost standard" (so-called "stage 3"), see proposals at . +Para leer acerca de las nuevas prestaciones de vanguardia del lenguaje, incluyendo aquellas que son "cuasi-estándar" (apodado "stage 3"), encuentra las propuestas en . -Also, if you're developing for the browser, then there are other specifications covered in the [second part](info:browser-environment) of the tutorial. +Si estás desarrollando para navegadores web, se mencionan otras especificaciones en la [segunda parte](info:browser-environment) del tutorial. -## Manuals +## Manuales -- **MDN (Mozilla) JavaScript Reference** is the main manual with examples and other information. It's great to get in-depth information about individual language functions, methods etc. +- **MDN (Mozilla) JavaScript Reference** es el manual principal, con ejemplos y otras informaciones. Es fantástico para obtener información exhaustiva sobre funciones individuales del lenguaje, métodos, etc. - You can find it at . + Se puede acceder en . -Although, it's often best to use an internet search instead. Just use "MDN [term]" in the query, e.g. to search for the `parseInt` function. +Aunque a menudo es preferible una búsqueda en internet. Simplemente añade "MDN [término]" en la consulta, por ejemplo para buscar la función `parseInt`. -## Compatibility tables +## Tablas de compatibilidad -JavaScript is a developing language, new features get added regularly. +JavaScript es un lenguaje en evolución, regularmente se agregan nuevas características. -To see their support among browser-based and other engines, see: +Para ver la compatibilidad por navegador y otros motores, consultar: -- - per-feature tables of support, e.g. to see which engines support modern cryptography functions: . -- - a table with language features and engines that support those or don't support. +- - tablas de compatibilidad por característica. Por ejemplo, para comprobar qué motores soportan funciones modernas de criptografía: . +- - tabla que muestra la compatibilidad o no de las prestaciones del lenguaje por motor. -All these resources are useful in real-life development, as they contain valuable information about language details, their support, etc. +Todos estos recursos son de utilidad para el desarrollo con JavaScript, ya que incluyen información valiosa sobre los detalles del lenguaje, su compatibilidad, etc. -Please remember them (or this page) for the cases when you need in-depth information about a particular feature. +Por favor, tenlos en cuenta (o esta página) para cuando necesites información exhaustiva sobre una característica determinada. diff --git a/1-js/01-getting-started/3-code-editors/article.md b/1-js/01-getting-started/3-code-editors/article.md index ca6194741..467b71b24 100644 --- a/1-js/01-getting-started/3-code-editors/article.md +++ b/1-js/01-getting-started/3-code-editors/article.md @@ -1,49 +1,49 @@ -# Code editors +# Editores de Código -A code editor is the place where programmers spend most of their time. +Un editor de código es el lugar donde los programadores pasan la mayor parte de su tiempo. -There are two main types of code editors: IDEs and lightweight editors. Many people use one tool of each type. +Hay dos principales tipos de editores de código: IDEs y editores livianos. Muchas personas usan una herramienta de cada tipo. ## IDE -The term [IDE](https://en.wikipedia.org/wiki/Integrated_development_environment) (Integrated Development Environment) refers to a powerful editor with many features that usually operates on a "whole project." As the name suggests, it's not just an editor, but a full-scale "development environment." +El término [IDE](https://es.wikipedia.org/wiki/Entorno_de_desarrollo_integrado) (siglas en inglés para Integrated Development Environment, Ambiente Integrado de Desarrollo) se refiere a un poderoso editor con varias características que operan usualmente sobre un "proyecto completo". Como el nombre sugiere, no sólo es un editor, sino un completo "ambiente de desarrollo". -An IDE loads the project (which can be many files), allows navigation between files, provides autocompletion based on the whole project (not just the open file), and integrates with a version management system (like [git](https://git-scm.com/)), a testing environment, and other "project-level" stuff. +Un IDE carga el proyecto (el cual puede ser de varios archivos), permite navegar entre archivos, provee autocompletado basado en el proyecto completo (no sólo el archivo abierto), e integra un sistema de control de versiones (como [git](https://git-scm.com/)), un ambiente de pruebas, entre otras cosas a "nivel de proyecto". -If you haven't selected an IDE yet, consider the following options: +Si aún no has seleccionado un IDE, considera las siguientes opciones: -- [Visual Studio Code](https://code.visualstudio.com/) (cross-platform, free). -- [WebStorm](https://www.jetbrains.com/webstorm/) (cross-platform, paid). +- [Visual Studio Code](https://code.visualstudio.com/) (Multiplataforma, gratuito). +- [WebStorm](https://www.jetbrains.com/webstorm/) (Multiplataforma, de pago). -For Windows, there's also "Visual Studio", not to be confused with "Visual Studio Code". "Visual Studio" is a paid and mighty Windows-only editor, well-suited for the .NET platform. It's also good at JavaScript. There's also a free version [Visual Studio Community](https://www.visualstudio.com/vs/community/). +Para Windows, también está "Visual Studio", no lo confundamos con "Visual Studio Code". "Visual Studio" es un poderoso editor de pago sólo para Windows, idóneo para la plataforma .NET. Una versión gratuita es de este editor se llama [Visual Studio Community](https://www.visualstudio.com/vs/community/). -Many IDEs are paid, but have a trial period. Their cost is usually negligible compared to a qualified developer's salary, so just choose the best one for you. +Muchos IDEs son de pago, pero tienen un periodo de prueba. Su costo usualmente es pequeño si lo comparamos al salario de un desarrollador cualificado, así que sólo escoge el mejor para ti. -## Lightweight editors +## Editores livianos -"Lightweight editors" are not as powerful as IDEs, but they're fast, elegant and simple. +Los "editores livianos" no son tan poderosos como los IDEs, pero son rápidos, elegantes y simples. -They are mainly used to open and edit a file instantly. +Son usados principalmente para abrir y editar un archivo al instante. -The main difference between a "lightweight editor" and an "IDE" is that an IDE works on a project-level, so it loads much more data on start, analyzes the project structure if needed and so on. A lightweight editor is much faster if we need only one file. +La diferencia principal entre un "editor liviano" y un "IDE" es que un IDE trabaja a nivel de proyecto, por lo que carga mucha más información desde el inicio, analiza la estructura del proyecto si así lo requiere y continua. Un editor liviano es mucho más rápido si solo necesitamos un archivo. -In practice, lightweight editors may have a lot of plugins including directory-level syntax analyzers and autocompleters, so there's no strict border between a lightweight editor and an IDE. +En la práctica, los editores livianos pueden tener montones de plugins incluyendo analizadores de sintaxis a nivel de directorio y autocompletado, por lo que no hay un límite estricto entre un editor liviano y un IDE. -There are many options, for instance: +Existen muchas opciones, por ejemplo: -- [Sublime Text](https://www.sublimetext.com/) (cross-platform, shareware). -- [Notepad++](https://notepad-plus-plus.org/) (Windows, free). -- [Vim](https://www.vim.org/) and [Emacs](https://www.gnu.org/software/emacs/) are also cool if you know how to use them. +- [Sublime Text](https://www.sublimetext.com) (multiplataforma, shareware). +- [Notepad++](https://notepad-plus-plus.org/) (Windows, gratuito). +- [Vim](https://www.vim.org/) y [Emacs](https://www.gnu.org/software/emacs/) son también interesantes si sabes cómo usarlos. -## Let's not argue +## No discutamos -The editors in the lists above are those that either I or my friends whom I consider good developers have been using for a long time and are happy with. +Los editores en las listas anteriores son aquellos que yo o mis amigos a quienes considero buenos programadores hemos estado usando por un largo tiempo y con los que somos felices. -There are other great editors in our big world. Please choose the one you like the most. +Existen otros grandes editores en este gran mundo. Por favor escoge el que más te guste. -The choice of an editor, like any other tool, is individual and depends on your projects, habits, and personal preferences. +La elección de un editor, como la de cualquier otra herramienta, es individual y depende de tus proyectos, hábitos y preferencias personales. -The author's personal opinion: +Opinión personal del author: -- I'd use [Visual Studio Code](https://code.visualstudio.com/) if I develop mostly frontend. -- Otherwise, if it's mostly another language/platform and partially frontend, then consider other editors, such as XCode (Mac), Visual Studio (Windows) or Jetbrains family (Webstorm, PHPStorm, RubyMine etc, depending on the language). +- Usaría [Visual Studio Code](https://code.visualstudio.com/) si desarrollara mayormente "frontend". +- De otro modo, si es mayormente otro lenguaje, plataforma, y solo parcialmente frontend; entonces consideraría otros editores, como XCode (Mac), Visual Studio (Windows) o la familia Jetbrains (Webstorm, PHPStorm, RubyMine, etc.; dependiendo del lenguaje). diff --git a/1-js/01-getting-started/4-devtools/article.md b/1-js/01-getting-started/4-devtools/article.md index bbe8af920..5cac198c2 100644 --- a/1-js/01-getting-started/4-devtools/article.md +++ b/1-js/01-getting-started/4-devtools/article.md @@ -1,63 +1,63 @@ -# Developer console +# Consola de desarrollador -Code is prone to errors. You will quite likely make errors... Oh, what am I talking about? You are *absolutely* going to make errors, at least if you're a human, not a [robot](https://en.wikipedia.org/wiki/Bender_(Futurama)). +El código es propenso a errores. Es muy probable que cometas errores ... Oh, ¿de qué estoy hablando? *Definitivamente* vas a cometer errores, al menos si eres un humano, no un [robot](https://en.wikipedia.org/wiki/Bender_(Futurama)). -But in the browser, users don't see errors by default. So, if something goes wrong in the script, we won't see what's broken and can't fix it. +Pero el navegador, de forma predeterminada, no muestra los errores al usuario. Entonces si algo sale mal en el script, no veremos lo que está roto y no podemos arreglarlo. -To see errors and get a lot of other useful information about scripts, "developer tools" have been embedded in browsers. +Para ver los errores y obtener mucha otra información útil sobre los scripts, se han incorporado "herramientas de desarrollo" en los navegadores. -Most developers lean towards Chrome or Firefox for development because those browsers have the best developer tools. Other browsers also provide developer tools, sometimes with special features, but are usually playing "catch-up" to Chrome or Firefox. So most developers have a "favorite" browser and switch to others if a problem is browser-specific. +La mayoría de los desarrolladores se inclinan por Chrome o Firefox para el desarrollo porque esos navegadores tienen las mejores herramientas para desarrolladores. Otros navegadores también proporcionan herramientas de desarrollo, a veces con características especiales, pero generalmente están jugando a ponerse al día con Chrome o Firefox. Por lo tanto, la mayoría de los desarrolladores tienen un navegador "favorito" y cambian a otros si un problema es específico del navegador. -Developer tools are potent; they have many features. To start, we'll learn how to open them, look at errors, and run JavaScript commands. +Las herramientas de desarrollo son potentes; Tienen muchas características. Para comenzar, aprenderemos cómo abrirlas, observar errores y ejecutar comandos JavaScript. ## Google Chrome -Open the page [bug.html](bug.html). +Abre la página [bug.html](bug.html). -There's an error in the JavaScript code on it. It's hidden from a regular visitor's eyes, so let's open developer tools to see it. +Hay un error en el código JavaScript dentro de la página. Está oculto a los ojos de un visitante regular, así que abramos las herramientas de desarrollador para verlo. -Press `key:F12` or, if you're on Mac, then `key:Cmd+Opt+J`. +Presione `key:F12` o, si está en Mac, entonces combine `key:Cmd+Opt+J`. -The developer tools will open on the Console tab by default. +Las herramientas de desarrollador se abrirán en la pestaña Consola de forma predeterminada. -It looks somewhat like this: +Se ve algo así: ![chrome](chrome.webp) -The exact look of developer tools depends on your version of Chrome. It changes from time to time but should be similar. +El aspecto exacto de las herramientas de desarrollador depende de su versión de Chrome. Cambia de vez en cuando, pero debería ser similar. -- Here we can see the red-colored error message. In this case, the script contains an unknown "lalala" command. -- On the right, there is a clickable link to the source `bug.html:12` with the line number where the error has occurred. +- Aquí podemos ver el mensaje de error de color rojo. En este caso, el script contiene un comando desconocido "lalala". +- A la derecha, hay un enlace en el que se puede hacer clic en la fuente `bug.html:12` con el número de línea donde se produjo el error. -Below the error message, there is a blue `>` symbol. It marks a "command line" where we can type JavaScript commands. Press `key:Enter` to run them. +Debajo del mensaje de error, hay un símbolo azul `>`. Marca una "línea de comando" donde podemos escribir comandos JavaScript. Presione `key:Enter` para ejecutarlos. -Now we can see errors, and that's enough for a start. We'll come back to developer tools later and cover debugging more in-depth in the chapter . +Ahora podemos ver errores, y eso es suficiente para empezar. Volveremos a las herramientas de desarrollador más adelante y cubriremos la depuración más en profundidad en el capítulo . -```smart header="Multi-line input" -Usually, when we put a line of code into the console, and then press `key:Enter`, it executes. +```smart header="Entrada multilínea" +Por lo general, cuando colocamos una línea de código en la consola y luego presionamos Enter, se ejecuta. -To insert multiple lines, press `key:Shift+Enter`. This way one can enter long fragments of JavaScript code. +Para insertar varias líneas, presione `key:Shift+Enter`. De esta forma se pueden ingresar fragmentos largos de código JavaScript. ``` -## Firefox, Edge, and others +## Firefox, Edge, y otros -Most other browsers use `key:F12` to open developer tools. +La mayoría de los otros navegadores usan `key:F12` para abrir herramientas de desarrollador. -The look & feel of them is quite similar. Once you know how to use one of these tools (you can start with Chrome), you can easily switch to another. +La apariencia de ellos es bastante similar. Una vez que sepa cómo usar una de estas herramientas (puede comenzar con Chrome), puede cambiar fácilmente a otra. ## Safari -Safari (Mac browser, not supported by Windows/Linux) is a little bit special here. We need to enable the "Develop menu" first. +Safari (navegador Mac, no compatible con Windows/Linux) es un poco especial aquí. Necesitamos habilitar primero el "Menú de desarrollo". -Open Settings and go to the "Advanced" pane. There's a checkbox at the bottom: +Abra "Configuración" y vaya al panel "Avanzado". Hay una casilla de verificación en la parte inferior: ![safari](safari.png) -Now `key:Cmd+Opt+C` can toggle the console. Also, note that the new top menu item named "Develop" has appeared. It has many commands and options. +Ahora combine `key:Cmd+Opt+C` para alternar a consola. Además, tenga en cuenta que ha aparecido el nuevo elemento del menú superior denominado "Desarrollar". Tiene muchos comandos y opciones. -## Summary +## Resumen -- Developer tools allow us to see errors, run commands, examine variables, and much more. -- They can be opened with `key:F12` for most browsers on Windows. Chrome for Mac needs `key:Cmd+Opt+J`, Safari: `key:Cmd+Opt+C` (need to enable first). +- Las herramientas para desarrolladores nos permiten ver errores, ejecutar comandos, examinar variables y mucho más. +- Se pueden abrir con `key:F12` para la mayoría de los navegadores en Windows. Chrome para Mac necesita la combinación `key:Cmd+Opt+J`, Safari: `key:Cmd+Opt+C` (primero debe habilitarse). -Now we have the environment ready. In the next section, we'll get down to JavaScript. +Ahora tenemos el entorno listo. En la siguiente sección nos enfocaremos en JavaScript. diff --git a/1-js/01-getting-started/4-devtools/bug.html b/1-js/01-getting-started/4-devtools/bug.html index edb02375c..b50a64bef 100644 --- a/1-js/01-getting-started/4-devtools/bug.html +++ b/1-js/01-getting-started/4-devtools/bug.html @@ -7,7 +7,7 @@ - There is an error in the script on this page. + Hay un error en el script de esta página. diff --git a/1-js/01-getting-started/4-devtools/safari.png b/1-js/01-getting-started/4-devtools/safari.png index 4538827eb..37598a261 100644 Binary files a/1-js/01-getting-started/4-devtools/safari.png and b/1-js/01-getting-started/4-devtools/safari.png differ diff --git a/1-js/01-getting-started/4-devtools/safari@2x.png b/1-js/01-getting-started/4-devtools/safari@2x.png index 1561b2bd9..c59cebef2 100644 Binary files a/1-js/01-getting-started/4-devtools/safari@2x.png and b/1-js/01-getting-started/4-devtools/safari@2x.png differ diff --git a/1-js/01-getting-started/index.md b/1-js/01-getting-started/index.md index b327c7860..69c0dad47 100644 --- a/1-js/01-getting-started/index.md +++ b/1-js/01-getting-started/index.md @@ -1,3 +1,3 @@ -# An introduction +# Una introducción -About the JavaScript language and the environment to develop with it. +Acerca del lenguaje JavaScript y el entorno para desarrollar con él. diff --git a/1-js/02-first-steps/01-hello-world/1-hello-alert/index.html b/1-js/02-first-steps/01-hello-world/1-hello-alert/index.html index ff1d871b0..9473835c7 100644 --- a/1-js/02-first-steps/01-hello-world/1-hello-alert/index.html +++ b/1-js/02-first-steps/01-hello-world/1-hello-alert/index.html @@ -4,7 +4,7 @@ diff --git a/1-js/02-first-steps/01-hello-world/1-hello-alert/solution.md b/1-js/02-first-steps/01-hello-world/1-hello-alert/solution.md index 81552913b..77b3abbb5 100644 --- a/1-js/02-first-steps/01-hello-world/1-hello-alert/solution.md +++ b/1-js/02-first-steps/01-hello-world/1-hello-alert/solution.md @@ -1,2 +1,2 @@ - -[html src="index.html"] + +[html src="index.html"] diff --git a/1-js/02-first-steps/01-hello-world/1-hello-alert/solution.view/index.html b/1-js/02-first-steps/01-hello-world/1-hello-alert/solution.view/index.html index 45e6744b3..9473835c7 100644 --- a/1-js/02-first-steps/01-hello-world/1-hello-alert/solution.view/index.html +++ b/1-js/02-first-steps/01-hello-world/1-hello-alert/solution.view/index.html @@ -4,9 +4,9 @@ - \ No newline at end of file + diff --git a/1-js/02-first-steps/01-hello-world/1-hello-alert/task.md b/1-js/02-first-steps/01-hello-world/1-hello-alert/task.md index afed6a91d..881b6f50d 100644 --- a/1-js/02-first-steps/01-hello-world/1-hello-alert/task.md +++ b/1-js/02-first-steps/01-hello-world/1-hello-alert/task.md @@ -2,11 +2,11 @@ importance: 5 --- -# Show an alert +# Mostrar una alerta -Create a page that shows a message "I'm JavaScript!". +Crea una página que muestre el mensaje "¡Soy JavaScript!". -Do it in a sandbox, or on your hard drive, doesn't matter, just ensure that it works. +Hazlo en un sandbox o en tu disco duro, no importa, solo asegúrate de que funcione. [demo src="solution"] diff --git a/1-js/02-first-steps/01-hello-world/2-hello-alert-ext/alert.js b/1-js/02-first-steps/01-hello-world/2-hello-alert-ext/alert.js index 4de725971..2ed768e53 100644 --- a/1-js/02-first-steps/01-hello-world/2-hello-alert-ext/alert.js +++ b/1-js/02-first-steps/01-hello-world/2-hello-alert-ext/alert.js @@ -1 +1 @@ -alert("I'm JavaScript!"); \ No newline at end of file +alert("¡Soy JavaScript!"); \ No newline at end of file diff --git a/1-js/02-first-steps/01-hello-world/2-hello-alert-ext/solution.md b/1-js/02-first-steps/01-hello-world/2-hello-alert-ext/solution.md index f42c41e6d..e96aebe47 100644 --- a/1-js/02-first-steps/01-hello-world/2-hello-alert-ext/solution.md +++ b/1-js/02-first-steps/01-hello-world/2-hello-alert-ext/solution.md @@ -1,8 +1,8 @@ -The HTML code: +El código HTML: [html src="index.html"] -For the file `alert.js` in the same folder: +Para el archivo `alert.js` en la misma carpeta: [js src="alert.js"] diff --git a/1-js/02-first-steps/01-hello-world/2-hello-alert-ext/task.md b/1-js/02-first-steps/01-hello-world/2-hello-alert-ext/task.md index 26168d6a7..94a477a96 100644 --- a/1-js/02-first-steps/01-hello-world/2-hello-alert-ext/task.md +++ b/1-js/02-first-steps/01-hello-world/2-hello-alert-ext/task.md @@ -2,8 +2,8 @@ importance: 5 --- -# Show an alert with an external script +# Mostrar una alerta con un script externo -Take the solution of the previous task . Modify it by extracting the script content into an external file `alert.js`, residing in the same folder. +Toma la solución de la tarea anterior . Modificarla extrayendo el contenido del script a un archivo externo `alert.js`, ubicado en la misma carpeta. -Open the page, ensure that the alert works. +Abrir la página, asegurarse que la alerta funcione. diff --git a/1-js/02-first-steps/01-hello-world/article.md b/1-js/02-first-steps/01-hello-world/article.md index 35f82bf5d..004e55143 100644 --- a/1-js/02-first-steps/01-hello-world/article.md +++ b/1-js/02-first-steps/01-hello-world/article.md @@ -1,17 +1,17 @@ -# Hello, world! +# ¡Hola, mundo! -This part of the tutorial is about core JavaScript, the language itself. +Esta parte del tutorial trata sobre el núcleo de JavaScript, el lenguaje en sí. -But we need a working environment to run our scripts and, since this book is online, the browser is a good choice. We'll keep the amount of browser-specific commands (like `alert`) to a minimum so that you don't spend time on them if you plan to concentrate on another environment (like Node.js). We'll focus on JavaScript in the browser in the [next part](/ui) of the tutorial. +Pero necesitamos un entorno de trabajo para ejecutar nuestros scripts y, dado que este libro está en línea, el navegador es una buena opción. Mantendremos la cantidad de comandos específicos del navegador (como `alert`) al mínimo para que no pases tiempo en ellos si planeas concentrarte en otro entorno (como Node.js). Nos centraremos en JavaScript en el navegador en la [siguiente parte](/ui) del tutorial. -So first, let's see how we attach a script to a webpage. For server-side environments (like Node.js), you can execute the script with a command like `"node my.js"`. +Primero, veamos cómo adjuntamos un script a una página web. Para entornos del lado del servidor (como Node.js), puedes ejecutar el script con un comando como `"node my.js"`. -## The "script" tag +## La etiqueta "script" -JavaScript programs can be inserted almost anywhere into an HTML document using the ` */!* -

...After the script.

+

...Después del script.

@@ -35,24 +35,24 @@ For instance: ``` ```online -You can run the example by clicking the "Play" button in the right-top corner of the box above. +Puedes ejecutar el ejemplo haciendo clic en el botón "Play" en la esquina superior derecha del cuadro de arriba. ``` -The ` ``` - This trick isn't used in modern JavaScript. These comments hide JavaScript code from old browsers that didn't know how to process the ` ``` -Here, `/path/to/script.js` is an absolute path to the script from the site root. One can also provide a relative path from the current page. For instance, `src="script.js"`, just like `src="./script.js"`, would mean a file `"script.js"` in the current folder. +Aquí, `/path/to/script.js` es una ruta absoluta al archivo de script desde la raíz del sitio. También se puede proporcionar una ruta relativa desde la página actual. Por ejemplo, `src="script.js"` significaría un archivo `"script.js"` en la carpeta actual. -We can give a full URL as well. For instance: +También podemos dar una URL completa. Por ejemplo: ```html ``` -To attach several scripts, use multiple tags: +Para adjuntar varios scripts, usa varias etiquetas: ```html @@ -90,29 +90,29 @@ To attach several scripts, use multiple tags: ``` ```smart -As a rule, only the simplest scripts are put into HTML. More complex ones reside in separate files. +Como regla general, solo los scripts más simples se colocan en el HTML. Los más complejos residen en archivos separados. -The benefit of a separate file is that the browser will download it and store it in its [cache](https://en.wikipedia.org/wiki/Web_cache). +La ventaja de un archivo separado es que el navegador lo descargará y lo almacenará en [caché](https://es.wikipedia.org/wiki/Cach%C3%A9_(informática)). -Other pages that reference the same script will take it from the cache instead of downloading it, so the file is actually downloaded only once. +Otras páginas que hacen referencia al mismo script lo tomarán del caché en lugar de descargarlo, por lo que el archivo solo se descarga una vez. -That reduces traffic and makes pages faster. +Eso reduce el tráfico y hace que las páginas sean más rápidas. ``` -````warn header="If `src` is set, the script content is ignored." -A single ` ``` -We must choose either an external ` @@ -122,11 +122,11 @@ The example above can be split into two scripts to work: ``` ```` -## Summary +## Resumen -- We can use a ``. +- Podemos usar una etiqueta ``. -There is much more to learn about browser scripts and their interaction with the webpage. But let's keep in mind that this part of the tutorial is devoted to the JavaScript language, so we shouldn't distract ourselves with browser-specific implementations of it. We'll be using the browser as a way to run JavaScript, which is very convenient for online reading, but only one of many. +Hay mucho más que aprender sobre los scripts del navegador y su interacción con la página web. Pero tengamos en cuenta que esta parte del tutorial está dedicada al lenguaje JavaScript, por lo que no debemos distraernos con implementaciones específicas del navegador. Usaremos el navegador como una forma de ejecutar JavaScript, lo cual es muy conveniente para la lectura en línea, pero es solo una de muchas. diff --git a/1-js/02-first-steps/02-structure/article.md b/1-js/02-first-steps/02-structure/article.md index e81fd343d..c1ceab173 100644 --- a/1-js/02-first-steps/02-structure/article.md +++ b/1-js/02-first-steps/02-structure/article.md @@ -1,44 +1,44 @@ -# Code structure +# Estructura del código -The first thing we'll study is the building blocks of code. +Lo primero que estudiaremos son los bloques de construcción del código. -## Statements +## Sentencias -Statements are syntax constructs and commands that perform actions. +Las sentencias son construcciones sintácticas y comandos que realizan acciones. -We've already seen a statement, `alert('Hello, world!')`, which shows the message "Hello, world!". +Ya hemos visto una sentencia, `alert('¡Hola mundo!')`, que muestra el mensaje "¡Hola mundo!". -We can have as many statements in our code as we want. Statements can be separated with a semicolon. +Podemos tener tantas sentencias en nuestro código como queramos, las cuales se pueden separar con un punto y coma. -For example, here we split "Hello World" into two alerts: +Por ejemplo, aquí separamos "Hello World" en dos alerts: ```js run no-beautify -alert('Hello'); alert('World'); +alert('Hola'); alert('Mundo'); ``` -Usually, statements are written on separate lines to make the code more readable: +Generalmente, las sentencias se escriben en líneas separadas para hacer que el código sea más legible: ```js run no-beautify -alert('Hello'); -alert('World'); +alert('Hola'); +alert('Mundo'); ``` -## Semicolons [#semicolon] +## Punto y coma [#semicolon] -A semicolon may be omitted in most cases when a line break exists. +Se puede omitir un punto y coma en la mayoría de los casos cuando existe un salto de línea. -This would also work: +Esto también funcionaría: ```js run no-beautify -alert('Hello') -alert('World') +alert('Hola') +alert('Mundo') ``` -Here, JavaScript interprets the line break as an "implicit" semicolon. This is called an [automatic semicolon insertion](https://tc39.github.io/ecma262/#sec-automatic-semicolon-insertion). +Aquí, JavaScript interpreta el salto de línea como un punto y coma "implícito". Esto se denomina [inserción automática de punto y coma](https://tc39.github.io/ecma262/#sec-automatic-semicolon-insertion). -**In most cases, a newline implies a semicolon. But "in most cases" does not mean "always"!** +**En la mayoría de los casos, una nueva línea implica un punto y coma. Pero "en la mayoría de los casos" no significa "siempre"!** -There are cases when a newline does not mean a semicolon. For example: +Hay casos en que una nueva línea no significa un punto y coma. Por ejemplo: ```js run no-beautify alert(3 + @@ -46,14 +46,14 @@ alert(3 + + 2); ``` -The code outputs `6` because JavaScript does not insert semicolons here. It is intuitively obvious that if the line ends with a plus `"+"`, then it is an "incomplete expression", so a semicolon there would be incorrect. And in this case, that works as intended. +El código da como resultado `6` porque JavaScript no inserta punto y coma aquí. Es intuitivamente obvio que si la línea termina con un signo más `"+"`, es una "expresión incompleta", un punto y coma aquí sería incorrecto. Y en este caso eso funciona según lo previsto. -**But there are situations where JavaScript "fails" to assume a semicolon where it is really needed.** +**Pero hay situaciones en las que JavaScript "falla" al asumir un punto y coma donde realmente se necesita.** -Errors which occur in such cases are quite hard to find and fix. +Los errores que ocurren en tales casos son bastante difíciles de encontrar y corregir. -````smart header="An example of an error" -If you're curious to see a concrete example of such an error, check this code out: +````smart header="Un ejemplo de error" +Si tienes curiosidad por ver un ejemplo concreto de tal error, mira este código: ```js run alert("Hello"); @@ -61,9 +61,9 @@ alert("Hello"); [1, 2].forEach(alert); ``` -No need to think about the meaning of the brackets `[]` and `forEach` yet. We'll study them later. For now, just remember the result of running the code: it shows `Hello`, then `1`, then `2`. +No es necesario pensar en el significado de los corchetes `[]` y `forEach` todavía, los estudiaremos más adelante. Por ahora, solo recuerda el resultado del código: muestra `Hello`, luego `1`, luego `2`. -Now let's remove the semicolon after the `alert`: +Quitemos el punto y coma del alert: ```js run no-beautify alert("Hello") @@ -71,85 +71,86 @@ alert("Hello") [1, 2].forEach(alert); ``` -The difference compared to the code above is only one character: the semicolon at the end of the first line is gone. +La diferencia, comparando con el código anterior, es de solo un carácter: falta el punto y coma al final de la primera línea. -If we run this code, only the first `Hello` shows (and there's an error, you may need to open the console to see it). There are no numbers any more. +Esta vez, si ejecutamos el código, solo se ve el primer `Hello` (y un error pero necesitas abrir la consola para verlo). Los números no aparecen más. -That's because JavaScript does not assume a semicolon before square brackets `[...]`. So, the code in the last example is treated as a single statement. +Esto ocurre porque JavaScript no asume un punto y coma antes de los corchetes `[...]`, entonces el código del primer ejemplo se trata como una sola sentencia. -Here's how the engine sees it: +Así es como lo ve el motor: ```js run no-beautify alert("Hello")[1, 2].forEach(alert); ``` -Looks weird, right? Such merging in this case is just wrong. We need to put a semicolon after `alert` for the code to work correctly. +Se ve extraño, ¿verdad? Tal unión en este caso es simplemente incorrecta. Necesitamos poner un punto y coma después del `alert` para que el código funcione bien. -This can happen in other situations also. +Esto puede suceder en otras situaciones también. ```` -We recommend putting semicolons between statements even if they are separated by newlines. This rule is widely adopted by the community. Let's note once again -- *it is possible* to leave out semicolons most of the time. But it's safer -- especially for a beginner -- to use them. +Recomendamos colocar puntos y coma entre las sentencias, incluso si están separadas por saltos de línea. Esta regla está ampliamente adoptada por la comunidad. Notemos una vez más que es posible omitir los puntos y coma la mayoría del tiempo. Pero es más seguro, especialmente para un principiante, usarlos. -## Comments [#code-comments] +## Comentarios [#code-comments] -As time goes on, programs become more and more complex. It becomes necessary to add *comments* which describe what the code does and why. +A medida que pasa el tiempo, los programas se vuelven cada vez más complejos. Se hace necesario agregar *comentarios* que describan lo que hace el código y por qué. -Comments can be put into any place of a script. They don't affect its execution because the engine simply ignores them. +Los comentarios se pueden poner en cualquier lugar de un script. No afectan su ejecución porque el motor simplemente los ignora. -**One-line comments start with two forward slash characters `//`.** +**Los comentarios de una línea comienzan con dos caracteres de barra diagonal `//`.** -The rest of the line is a comment. It may occupy a full line of its own or follow a statement. +El resto de la línea es un comentario. Puede ocupar una línea completa propia o seguir una sentencia. -Like here: +Como aquí: ```js run -// This comment occupies a line of its own +// Este comentario ocupa una línea propia. alert('Hello'); -alert('World'); // This comment follows the statement +alert('World'); // Este comentario sigue a la sentencia. ``` -**Multiline comments start with a forward slash and an asterisk /* and end with an asterisk and a forward slash */.** +**Los comentarios de varias líneas comienzan con una barra inclinada y un asterisco /* y terminan con un asterisco y una barra inclinada */.** -Like this: +Como aquí: ```js run -/* An example with two messages. -This is a multiline comment. +/* Un ejemplo con dos mensajes. +Este es un comentario multilínea. */ -alert('Hello'); -alert('World'); +alert('Hola'); +alert('Mundo'); ``` -The content of comments is ignored, so if we put code inside /* ... */, it won't execute. +El contenido de los comentarios se ignora, por lo que si colocamos el código dentro de /* ... */, no se ejecutará. -Sometimes it can be handy to temporarily disable a part of code: +A veces puede ser útil deshabilitar temporalmente una parte del código: ```js run -/* Commenting out the code -alert('Hello'); +/* Comentando el código +alert('Hola'); */ -alert('World'); +alert('Mundo'); ``` -```smart header="Use hotkeys!" -In most editors, a line of code can be commented out by pressing the `key:Ctrl+/` hotkey for a single-line comment and something like `key:Ctrl+Shift+/` -- for multiline comments (select a piece of code and press the hotkey). For Mac, try `key:Cmd` instead of `key:Ctrl` and `key:Option` instead of `key:Shift`. +```smart header="¡Usa accesos rápidos del teclado!" +En la mayoría de los editores, se puede comentar una línea de código presionando `key:Ctrl+/` para un comentario de una sola línea y algo como `key:Ctrl+Shift+/` - para comentarios de varias líneas (selecciona una parte del código y pulsa la tecla de acceso rápido). Para Mac, intenta `key: Cmd` en lugar de `key: Ctrl` y `key:Option` en lugar de `key:Shift`. ``` -````warn header="Nested comments are not supported!" -There may not be `/*...*/` inside another `/*...*/`. +````warn header="¡Los comentarios anidados no son admitidos!" +No puede haber `/*...*/` dentro de otro `/*...*/`. -Such code will die with an error: +Dicho código terminará con un error: ```js run no-beautify /* - /* nested comment ?!? */ + /* comentario anidado ?!? */ */ -alert( 'World' ); +alert( 'Mundo' ); ``` ```` -Please, don't hesitate to comment your code. +Por favor, no dudes en comentar tu código. + +Los comentarios aumentan el tamaño general del código, pero eso no es un problema en absoluto. Hay muchas herramientas que minimizan el código antes de publicarlo en un servidor de producción. Eliminan los comentarios, por lo que no aparecen en los scripts de trabajo. Por lo tanto, los comentarios no tienen ningún efecto negativo en la producción. -Comments increase the overall code footprint, but that's not a problem at all. There are many tools which minify code before publishing to a production server. They remove comments, so they don't appear in the working scripts. Therefore, comments do not have negative effects on production at all. +Más adelante, en el tutorial, habrá un capítulo que también explica cómo escribir mejores comentarios. -Later in the tutorial there will be a chapter that also explains how to write better comments. diff --git a/1-js/02-first-steps/03-strict-mode/article.md b/1-js/02-first-steps/03-strict-mode/article.md index 9586733cc..05dd44122 100644 --- a/1-js/02-first-steps/03-strict-mode/article.md +++ b/1-js/02-first-steps/03-strict-mode/article.md @@ -1,89 +1,89 @@ -# The modern mode, "use strict" +# El modo moderno, "use strict" -For a long time, JavaScript evolved without compatibility issues. New features were added to the language while old functionality didn't change. +Durante mucho tiempo, JavaScript evolucionó sin problemas de compatibilidad. Se añadían nuevas características al lenguaje sin que la funcionalidad existente cambiase. -That had the benefit of never breaking existing code. But the downside was that any mistake or an imperfect decision made by JavaScript's creators got stuck in the language forever. +Esto tenía el beneficio de nunca romper código existente, pero lo malo era que cualquier error o decisión incorrecta tomada por los creadores de JavaScript se quedaba para siempre en el lenguaje. -This was the case until 2009 when ECMAScript 5 (ES5) appeared. It added new features to the language and modified some of the existing ones. To keep the old code working, most such modifications are off by default. You need to explicitly enable them with a special directive: `"use strict"`. +Esto fue así hasta 2009, cuando ECMAScript 5 (ES5) apareció. Esta versión añadió nuevas características al lenguaje y modificó algunas de las ya existentes. Para mantener el código antiguo funcionando, la mayoría de estas nuevas características están desactivadas por defecto. Tienes que activarlas explícitamente usando una directiva especial: `"use strict"`. ## "use strict" -The directive looks like a string: `"use strict"` or `'use strict'`. When it is located at the top of a script, the whole script works the "modern" way. +La directiva se presenta como un string: `"use strict"` o `'use strict'`. Cuando se sitúa al principio de un script, el script entero funciona de la manera "moderna". -For example: +Por ejemplo: ```js "use strict"; -// this code works the modern way +// este código funciona de la manera moderna ... ``` -Quite soon we're going to learn functions (a way to group commands), so let's note in advance that `"use strict"` can be put at the beginning of a function. Doing that enables strict mode in that function only. But usually people use it for the whole script. +Aprenderemos funciones (una manera de agrupar comandos) en breve, pero adelantemos que `"use strict"` se puede poner al inicio de una función. De esta manera, se activa el modo estricto únicamente en esa función. Pero normalmente se utiliza para el script entero. -````warn header="Ensure that \"use strict\" is at the top" -Please make sure that `"use strict"` is at the top of your scripts, otherwise strict mode may not be enabled. +````warn header="Asegúrate de que \"use strict\" está al inicio" +Por favor, asegúrate de que `"use strict"` está al principio de tus scripts. Si no, el modo estricto podría no estar activado. -Strict mode isn't enabled here: +El modo estricto no está activado aquí: ```js no-strict -alert("some code"); -// "use strict" below is ignored--it must be at the top +alert("algo de código"); +// la directiva "use strict" de abajo es ignorada, tiene que estar al principio "use strict"; -// strict mode is not activated +// el modo estricto no está activado ``` -Only comments may appear above `"use strict"`. +Únicamente pueden aparecer comentarios por encima de `"use strict"`. ```` -```warn header="There's no way to cancel `use strict`" -There is no directive like `"no use strict"` that reverts the engine to old behavior. +```warn header="No hay manera de cancelar `use strict`" +No hay ninguna directiva del tipo `"no use strict"` que haga al motor volver al comportamiento anterior. -Once we enter strict mode, there's no going back. +Una vez entramos en modo estricto, no hay vuelta atrás. ``` -## Browser console +## Consola del navegador -When you use a [developer console](info:devtools) to run code, please note that it doesn't `use strict` by default. +Cuando utilices la [consola del navegador](info:devtools) para ejecutar código, ten en cuenta que no utiliza `use strict` por defecto. -Sometimes, when `use strict` makes a difference, you'll get incorrect results. +En ocasiones, donde `use strict` cause diferencia, obtendrás resultados incorrectos. -So, how to actually `use strict` in the console? +Entonces, ¿como utilizar `use strict` en la consola? -First, you can try to press `key:Shift+Enter` to input multiple lines, and put `use strict` on top, like this: +Primero puedes intentar pulsando `key:Shift+Enter` para ingresar múltiples líneas y poner `use strict` al principio, como aquí: ```js -'use strict'; -// ...your code - +'use strict'; +// ...tu código + ``` -It works in most browsers, namely Firefox and Chrome. +Esto funciona para la mayoría de los navegadores, específicamente Firefox y Chrome. -If it doesn't, e.g. in an old browser, there's an ugly, but reliable way to ensure `use strict`. Put it inside this kind of wrapper: +Si esto no funciona, como en los viejos navegadores, hay una fea pero confiable manera de asegurar `use strict`. Ponlo dentro de esta especie de envoltura: ```js (function() { 'use strict'; - // ...your code here... + // ...tu código... })() ``` -## Should we "use strict"? +## ¿Deberíamos utilizar "use strict"? -The question may sound obvious, but it's not so. +La pregunta podría parecer obvia, pero no lo es. -One could recommend to start scripts with `"use strict"`... But you know what's cool? +Uno podría recomendar que se comiencen los script con `"use strict"`... ¿Pero sabes lo que es interesante? -Modern JavaScript supports "classes" and "modules" - advanced language structures (we'll surely get to them), that enable `use strict` automatically. So we don't need to add the `"use strict"` directive, if we use them. +El JavaScript moderno admite "clases" y "módulos", estructuras de lenguaje avanzadas (que seguramente llegaremos a ver), que automáticamente habilitan `use strict`. Entonces no necesitamos agregar la directiva `"use strict"` si las usamos. -**So, for now `"use strict";` is a welcome guest at the top of your scripts. Later, when your code is all in classes and modules, you may omit it.** +**Entonces, por ahora `"use strict";` es un invitado bienvenido al tope de tus scripts. Luego, cuando tu código sea todo clases y módulos, puedes omitirlo.** -As of now, we've got to know about `use strict` in general. +Hemos aprendido acerca de `use strict` en general. -In the next chapters, as we learn language features, we'll see the differences between the strict and old modes. Luckily, there aren't many and they actually make our lives better. +En los siguientes capítulos, a medida que aprendamos características del lenguaje, veremos las diferencias entre el modo estricto y el antiguo. Afortunadamente no hay muchas y realmente hacen nuestra vida mejor. -All examples in this tutorial assume strict mode unless (very rarely) specified otherwise. +Todos los ejemplos en este tutorial asumen modo estricto salvo que (muy raramente) se especifique lo contrario. diff --git a/1-js/02-first-steps/04-variables/1-hello-variables/solution.md b/1-js/02-first-steps/04-variables/1-hello-variables/solution.md index 9249e1c84..8c33ebd1c 100644 --- a/1-js/02-first-steps/04-variables/1-hello-variables/solution.md +++ b/1-js/02-first-steps/04-variables/1-hello-variables/solution.md @@ -1,7 +1,7 @@ -In the code below, each line corresponds to the item in the task list. +En el siguiente código, cada línea corresponde al elemento en la lista de tareas. ```js run -let admin, name; // can declare two variables at once +let admin, name; // Puedes declarar dos variables a la vez. name = "John"; diff --git a/1-js/02-first-steps/04-variables/1-hello-variables/task.md b/1-js/02-first-steps/04-variables/1-hello-variables/task.md index 84f009e8c..88079ac8a 100644 --- a/1-js/02-first-steps/04-variables/1-hello-variables/task.md +++ b/1-js/02-first-steps/04-variables/1-hello-variables/task.md @@ -2,9 +2,9 @@ importance: 2 --- -# Working with variables +# Trabajando con variables. -1. Declare two variables: `admin` and `name`. -2. Assign the value `"John"` to `name`. -3. Copy the value from `name` to `admin`. -4. Show the value of `admin` using `alert` (must output "John"). +1. Declara dos variables: `admin` y `name`. +2. Asigna el valor `"John"` a `name`. +3. Copia el valor de `name` a `admin`. +4. Muestra el valor de `admin` usando `alert` (debe salir "John"). diff --git a/1-js/02-first-steps/04-variables/2-declare-variables/solution.md b/1-js/02-first-steps/04-variables/2-declare-variables/solution.md index 392f4e26f..4d7ab3131 100644 --- a/1-js/02-first-steps/04-variables/2-declare-variables/solution.md +++ b/1-js/02-first-steps/04-variables/2-declare-variables/solution.md @@ -1,21 +1,21 @@ -## The variable for our planet +## La variable para nuestro planeta. -That's simple: +Eso es simple: ```js -let ourPlanetName = "Earth"; +let ourPlanetName = "Tierra"; ``` -Note, we could use a shorter name `planet`, but it might not be obvious what planet it refers to. It's nice to be more verbose. At least until the variable isNotTooLong. +Nota, podríamos usar un nombre más corto `planeta`, pero podría no ser obvio a qué planeta se refiere. Es una buena idea ser más detallado, siempre y cuando la variable noSeaMuyLarga. -## The name of the current visitor +## El nombre del usuario actual ```js -let currentUserName = "John"; +let currentUserName = "Juan"; ``` -Again, we could shorten that to `userName` if we know for sure that the user is current. +Una vez más, podríamos acortar eso a `userName` si estamos seguros que es el usuario actual (current). -Modern editors and autocomplete make long variable names easy to write. Don't save on them. A name with 3 words in it is fine. +Los editores modernos y el autocompletado hacen que los nombres de variables largos sean fáciles de escribir. No ahorres caracteres. Un nombre de 3 palabras está bien. -And if your editor does not have proper autocompletion, get [a new one](/code-editors). +Y si tu editor no tiene un autocompletado apropiado, consigue [uno nuevo](/code-editors). diff --git a/1-js/02-first-steps/04-variables/2-declare-variables/task.md b/1-js/02-first-steps/04-variables/2-declare-variables/task.md index f364badf4..97f001bd0 100644 --- a/1-js/02-first-steps/04-variables/2-declare-variables/task.md +++ b/1-js/02-first-steps/04-variables/2-declare-variables/task.md @@ -2,7 +2,7 @@ importance: 3 --- -# Giving the right name +# Dando el nombre correcto -1. Create a variable with the name of our planet. How would you name such a variable? -2. Create a variable to store the name of a current visitor to a website. How would you name that variable? +1. Crea una variable con el nombre de nuestro planeta. ¿Cómo nombrarías a dicha variable? +2. Crea una variable para almacenar el nombre del usuario actual de un sitio web. ¿Cómo nombrarías a dicha variable? diff --git a/1-js/02-first-steps/04-variables/3-uppercast-constant/solution.md b/1-js/02-first-steps/04-variables/3-uppercast-constant/solution.md index acd643fde..a1d80b393 100644 --- a/1-js/02-first-steps/04-variables/3-uppercast-constant/solution.md +++ b/1-js/02-first-steps/04-variables/3-uppercast-constant/solution.md @@ -1,5 +1,5 @@ -We generally use upper case for constants that are "hard-coded". Or, in other words, when the value is known prior to execution and directly written into the code. +Generalmente usamos mayúsculas para constantes que están "hard-codeadas". En otras palabras, cuando el valor se conoce antes de la ejecución y se escribe directamente en el código. -In this code, `birthday` is exactly like that. So we could use the upper case for it. +En este código, `birthday` es exactamente así, por lo que podemos escribirla en mayúsculas. -In contrast, `age` is evaluated in run-time. Today we have one age, a year after we'll have another one. It is constant in a sense that it does not change through the code execution. But it is a bit "less of a constant" than `birthday`: it is calculated, so we should keep the lower case for it. +En cambio, `age` es evaluada en ejecución. Hoy tenemos una edad, un año después tendremos otra. Es constante en el sentido que no cambia durante la ejecución del código, pero es un poco "menos constante" que `birthday` ya que se calcula, por lo que debemos mantenerla en minúscula. diff --git a/1-js/02-first-steps/04-variables/3-uppercast-constant/task.md b/1-js/02-first-steps/04-variables/3-uppercast-constant/task.md index f3c208a74..a96685311 100644 --- a/1-js/02-first-steps/04-variables/3-uppercast-constant/task.md +++ b/1-js/02-first-steps/04-variables/3-uppercast-constant/task.md @@ -2,9 +2,9 @@ importance: 4 --- -# Uppercase const? +# ¿const mayúsculas? -Examine the following code: +Examina el siguiente código: ```js const birthday = '18.04.1982'; @@ -12,14 +12,14 @@ const birthday = '18.04.1982'; const age = someCode(birthday); ``` -Here we have a constant `birthday` for the date, and also the `age` constant. +Aquí tenemos una constante `birthday` para la fecha de cumpleaños, y la edad `age`, que también es constante. -The `age` is calculated from `birthday` using `someCode()`, which means a function call that we didn't explain yet (we will soon!), but the details don't matter here, the point is that `age` is calculated somehow based on the `birthday`. +`age` es calculada desde `birthday` con la ayuda de "cierto código" `someCode()`, que es una llamada a función que no hemos explicado aún (¡lo haremos pronto!); los detalles no importan aquí, el punto es que `age` se calcula de alguna forma basándose en `birthday`. -Would it be right to use upper case for `birthday`? For `age`? Or even for both? +¿Sería correcto usar mayúsculas en `birthday`? ¿Y en `age`? ¿O en ambos? ```js -const BIRTHDAY = '18.04.1982'; // make birthday uppercase? +const BIRTHDAY = '18.04.1982'; // ¿birthday en mayúsculas? -const AGE = someCode(BIRTHDAY); // make age uppercase? +const AGE = someCode(BIRTHDAY); // ¿age en mayúsculas? ``` diff --git a/1-js/02-first-steps/04-variables/article.md b/1-js/02-first-steps/04-variables/article.md index 1741a09ce..7b609159c 100644 --- a/1-js/02-first-steps/04-variables/article.md +++ b/1-js/02-first-steps/04-variables/article.md @@ -1,274 +1,274 @@ # Variables -Most of the time, a JavaScript application needs to work with information. Here are two examples: -1. An online shop -- the information might include goods being sold and a shopping cart. -2. A chat application -- the information might include users, messages, and much more. +La mayor parte del tiempo, una aplicación JavaScript necesita trabajar con información. Dos ejemplos: +1. Una tienda en línea -- La información puede incluir los bienes a la venta y un "carrito de compras". +2. Una aplicación de chat -- La información puede incluir los usuarios, mensajes, y mucho más. -Variables are used to store this information. +Utilizamos las variables para almacenar esta información. -## A variable +## Una variable -A [variable](https://en.wikipedia.org/wiki/Variable_(computer_science)) is a "named storage" for data. We can use variables to store goodies, visitors, and other data. +Una [variable](https://es.wikipedia.org/wiki/Variable_(programaci%C3%B3n)) es un "almacén con un nombre" para guardar datos. Podemos usar variables para almacenar golosinas, visitantes, y otros datos. -To create a variable in JavaScript, use the `let` keyword. -The statement below creates (in other words: *declares*) a variable with the name "message": +Para generar una variable en JavaScript, se usa la palabra clave `let`. +La siguiente declaración genera (en otras palabras: *declara* o *define*) una variable con el nombre "message": ```js let message; ``` -Now, we can put some data into it by using the assignment operator `=`: +Ahora podemos introducir datos en ella, utilizando el operador de asignación `=`: ```js let message; *!* -message = 'Hello'; // store the string 'Hello' in the variable named message +message = 'Hola'; // almacenar la cadena 'Hola' en la variable llamada message */!* ``` -The string is now saved into the memory area associated with the variable. We can access it using the variable name: +La cadena ahora está almacenada en el área de la memoria asociada con la variable. La podemos acceder utilizando el nombre de la variable: ```js run let message; -message = 'Hello!'; +message = 'Hola!'; *!* -alert(message); // shows the variable content +alert(message); // muestra el contenido de la variable */!* ``` -To be concise, we can combine the variable declaration and assignment into a single line: +Para ser concisos, podemos combinar la declaración de la variable y su asignación en una sola línea: ```js run -let message = 'Hello!'; // define the variable and assign the value +let message = 'Hola!'; // define la variable y asigna un valor -alert(message); // Hello! +alert(message); // Hola! ``` -We can also declare multiple variables in one line: +También podemos declarar múltiples variables en una sola línea: ```js no-beautify -let user = 'John', age = 25, message = 'Hello'; +let user = 'John', age = 25, message = 'Hola'; ``` -That might seem shorter, but we don't recommend it. For the sake of better readability, please use a single line per variable. +Esto puede parecer más corto, pero no lo recomendamos. Por el bien de la legibilidad, por favor utiliza una línea por variable. -The multiline variant is a bit longer, but easier to read: +La versión de líneas múltiples es un poco más larga, pero se lee más fácil: ```js let user = 'John'; let age = 25; -let message = 'Hello'; +let message = 'Hola'; ``` -Some people also define multiple variables in this multiline style: +Hay quienes prefieren definir múltiples variables en estilo multilínea: ```js no-beautify let user = 'John', age = 25, - message = 'Hello'; + message = 'Hola'; ``` -...Or even in the "comma-first" style: +... o con el estilo "coma primero": ```js no-beautify let user = 'John' , age = 25 - , message = 'Hello'; + , message = 'Hola'; ``` -Technically, all these variants do the same thing. So, it's a matter of personal taste and aesthetics. +Técnicamente, todas estas variantes hacen lo mismo. Así que, es cuestión de gusto personal y preferencia estética. -````smart header="`var` instead of `let`" -In older scripts, you may also find another keyword: `var` instead of `let`: +````smart header="`var` en vez de `let`" +En scripts más viejos, a veces se encuentra otra palabra clave: `var` en lugar de `let`: ```js -*!*var*/!* message = 'Hello'; +*!*var*/!* mensaje = 'Hola'; ``` -The `var` keyword is *almost* the same as `let`. It also declares a variable but in a slightly different, "old-school" way. +La palabra clave `var` es *casi* lo mismo que `let`. También hace la declaración de una variable, aunque de un modo ligeramente distinto, y más antiguo. -There are subtle differences between `let` and `var`, but they do not matter to us yet. We'll cover them in detail in the chapter . +Existen sutiles diferencias entre `let` y `var`, pero no nos interesan en este momento. Cubriremos el tema a detalle en el capítulo . ```` -## A real-life analogy +## Una analogía de la vida real -We can easily grasp the concept of a "variable" if we imagine it as a "box" for data, with a uniquely-named sticker on it. +Podemos comprender fácilmente el concepto de una "variable" si nos la imaginamos como una "caja" con una etiqueta de nombre único pegada en ella. -For instance, the variable `message` can be imagined as a box labelled `"message"` with the value `"Hello!"` in it: +Por ejemplo, la variable `message` puede ser imaginada como una caja etiquetada `"message"` con el valor `"Hola!"` dentro: ![](variable.svg) -We can put any value in the box. +Podemos introducir cualquier valor a la caja. -We can also change it as many times as we want: +También la podemos cambiar cuantas veces queramos: ```js run let message; -message = 'Hello!'; +message = 'Hola!'; -message = 'World!'; // value changed +message = 'Mundo!'; // valor alterado alert(message); ``` -When the value is changed, the old data is removed from the variable: +Cuando el valor ha sido alterado, los datos antiguos serán removidos de la variable: ![](variable-change.svg) -We can also declare two variables and copy data from one into the other. +También podemos declarar dos variables y copiar datos de una a la otra. ```js run -let hello = 'Hello world!'; +let hello = 'Hola mundo!'; let message; *!* -// copy 'Hello world' from hello into message +// copia 'Hola mundo' de hello a message message = hello; */!* -// now two variables hold the same data -alert(hello); // Hello world! -alert(message); // Hello world! +// Ahora, ambas variables contienen los mismos datos +alert(hello); // Hola mundo! +alert(message); // Hola mundo! ``` -````warn header="Declaring twice triggers an error" -A variable should be declared only once. +````warn header="Declarar dos veces lanza un error" +Una variable debe ser declarada solamente una vez. -A repeated declaration of the same variable is an error: +Una declaración repetida de la misma variable es un error: ```js run let message = "This"; -// repeated 'let' leads to an error -let message = "That"; // SyntaxError: 'message' has already been declared +// 'let' repetidos lleva a un error +let message = "That"; // SyntaxError: 'message' ya fue declarado ``` -So, we should declare a variable once and then refer to it without `let`. +Debemos declarar una variable una sola vez y desde entonces referirnos a ella sin `let`. ```` -```smart header="Functional languages" -It's interesting to note that there exist so-called [pure functional](https://en.wikipedia.org/wiki/Purely_functional_programming) programming languages, such as [Haskell](https://en.wikipedia.org/wiki/Haskell), that forbid changing variable values. +```smart header="Lenguajes funcionales" +Es interesante notar la existencia de la [programación funcional](https://es.wikipedia.org/wiki/Programaci%C3%B3n_funcional). Los lenguajes funcionales "puros", como [Haskell](https://es.wikipedia.org/wiki/Haskell), prohíben cambiar el valor de las variables. -In such languages, once the value is stored "in the box", it's there forever. If we need to store something else, the language forces us to create a new box (declare a new variable). We can't reuse the old one. +En tales lenguajes, una vez que la variable ha sido almacenada "en la caja", permanece allí por siempre. Si necesitamos almacenar algo más, el lenguaje nos obliga a crear una nueva caja (generar una nueva variable). No podemos reusar la antigua. -Though it may seem a little odd at first sight, these languages are quite capable of serious development. More than that, there are areas like parallel computations where this limitation confers certain benefits. +Aunque a primera vista puede parecer un poco extraño, estos lenguajes son muy capaces de desarrollo serio. Más aún: existen áreas, como la computación en paralelo, en las cuales esta limitación otorga ciertas ventajas. ``` -## Variable naming [#variable-naming] +## Nombramiento de variables [#variable-naming] -There are two limitations on variable names in JavaScript: +Existen dos limitaciones de nombre de variables en JavaScript: -1. The name must contain only letters, digits, or the symbols `$` and `_`. -2. The first character must not be a digit. +1. El nombre únicamente puede incluir letras, dígitos, o los símbolos `$` y `_`. +2. El primer carácter no puede ser un dígito. -Examples of valid names: +Ejemplos de nombres válidos: ```js let userName; let test123; ``` -When the name contains multiple words, [camelCase](https://en.wikipedia.org/wiki/CamelCase) is commonly used. That is: words go one after another, with each word except the first starting with a capital letter: `myVeryLongName`. +Cuando el nombre contiene varias palabras, se suele usar el estilo [camelCase](https://es.wikipedia.org/wiki/Camel_case), donde las palabras van una detrás de otra y cada palabra, excepto la primera, empieza con mayúscula: `miNombreMuyLargo`. -What's interesting -- the dollar sign `'$'` and the underscore `'_'` can also be used in names. They are regular symbols, just like letters, without any special meaning. +Es interesante notar que el símbolo del dólar `'$'` y el guion bajo `'_'` también se utilizan en nombres. Son símbolos comunes, tal como las letras, sin ningún significado especial. -These names are valid: +Los siguientes nombres son válidos: ```js run untrusted -let $ = 1; // declared a variable with the name "$" -let _ = 2; // and now a variable with the name "_" +let $ = 1; // Declara una variable con el nombre "$" +let _ = 2; // y ahora una variable con el nombre "_" alert($ + _); // 3 ``` -Examples of incorrect variable names: +Ejemplos de nombres incorrectos: ```js no-beautify -let 1a; // cannot start with a digit +let 1a; // no puede iniciar con un dígito -let my-name; // hyphens '-' aren't allowed in the name +let my-name; // los guiones '-' no son permitidos en nombres ``` -```smart header="Case matters" -Variables named `apple` and `APPLE` are two different variables. +```smart header="La Capitalización es Importante" +Dos variables con nombres `manzana` y `MANZANA` son variables distintas. ``` -````smart header="Non-Latin letters are allowed, but not recommended" -It is possible to use any language, including Cyrillic letters, Chinese logograms and so on, like this: +````smart header="Las letras que no son del alfabeto inglés están permitidas, pero no se recomiendan" +Es posible utilizar letras de cualquier alfabeto, incluyendo letras del cirílico, logogramas chinos, etc.: ```js let имя = '...'; let 我 = '...'; ``` -Technically, there is no error here. Such names are allowed, but there is an international convention to use English in variable names. Even if we're writing a small script, it may have a long life ahead. People from other countries may need to read it sometime. +Técnicamente, no existe error aquí. Tales nombres están permitidos, pero internacionalmente existe la convención de utilizar el inglés para el nombre de las variables. Incluso si estamos escribiendo un script pequeño, este puede tener una larga vida por delante. Gente de otros países puede necesitar leerlo en algún momento. ```` -````warn header="Reserved names" -There is a [list of reserved words](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords), which cannot be used as variable names because they are used by the language itself. +````warn header="Nombres reservados" +Hay una [lista de palabras reservadas](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords), las cuales no pueden ser utilizadas como nombre de variable porque el lenguaje en sí las utiliza. -For example: `let`, `class`, `return`, and `function` are reserved. +Por ejemplo: `let`, `class`, `return`, y `function` están reservadas. -The code below gives a syntax error: +El siguiente código nos da un error de sintaxis: ```js run no-beautify -let let = 5; // can't name a variable "let", error! -let return = 5; // also can't name it "return", error! +let let = 5; // no se puede le nombrar "let" a una variable ¡Error! +let return = 5; // tampoco se le puede nombrar "return", ¡Error! ``` ```` -````warn header="An assignment without `use strict`" +````warn header="Una asignación sin utilizar `use strict`" -Normally, we need to define a variable before using it. But in the old times, it was technically possible to create a variable by a mere assignment of the value without using `let`. This still works now if we don't put `use strict` in our scripts to maintain compatibility with old scripts. +Normalmente, debemos definir una variable antes de utilizarla. Pero, en los viejos tiempos, era técnicamente posible crear una variable simplemente asignando un valor sin utilizar 'let'. Esto aún funciona si no ponemos 'use strict' en nuestros scripts para mantener la compatibilidad con scripts antiguos. ```js run no-strict -// note: no "use strict" in this example +// nota: no se utiliza "use strict" en este ejemplo -num = 5; // the variable "num" is created if it didn't exist +num = 5; // se crea la variable "num" si no existe antes alert(num); // 5 ``` -This is a bad practice and would cause an error in strict mode: +Esto es una mala práctica que causaría errores en 'strict mode': ```js "use strict"; *!* -num = 5; // error: num is not defined +num = 5; // error: num no está definida */!* ``` ```` -## Constants +## Constantes -To declare a constant (unchanging) variable, use `const` instead of `let`: +Para declarar una variable constante (inmutable) use `const` en vez de `let`: ```js const myBirthday = '18.04.1982'; ``` -Variables declared using `const` are called "constants". They cannot be reassigned. An attempt to do so would cause an error: +Las variables declaradas utilizando `const` se llaman "constantes". No pueden ser alteradas. Al intentarlo causaría un error: ```js run const myBirthday = '18.04.1982'; -myBirthday = '01.01.2001'; // error, can't reassign the constant! +myBirthday = '01.01.2001'; // ¡error, no se puede reasignar la constante! ``` -When a programmer is sure that a variable will never change, they can declare it with `const` to guarantee and communicate that fact to everyone. +Cuando un programador está seguro de que una variable nunca cambiará, puede declararla con `const` para garantizar esto y comunicarlo claramente a los demás. -### Uppercase constants +### Constantes mayúsculas -There is a widespread practice to use constants as aliases for difficult-to-remember values that are known before execution. +Existe una práctica utilizada ampliamente de utilizar constantes como aliases de valores difíciles-de-recordar y que se conocen previo a la ejecución. -Such constants are named using capital letters and underscores. +Tales constantes se nombran utilizando letras mayúsculas y guiones bajos. -For instance, let's make constants for colors in so-called "web" (hexadecimal) format: +Por ejemplo, creemos constantes para los colores en el formato "web" (hexadecimal): ```js run const COLOR_RED = "#F00"; @@ -276,70 +276,70 @@ const COLOR_GREEN = "#0F0"; const COLOR_BLUE = "#00F"; const COLOR_ORANGE = "#FF7F00"; -// ...when we need to pick a color +// ...cuando debemos elegir un color let color = COLOR_ORANGE; alert(color); // #FF7F00 ``` -Benefits: +Ventajas: -- `COLOR_ORANGE` is much easier to remember than `"#FF7F00"`. -- It is much easier to mistype `"#FF7F00"` than `COLOR_ORANGE`. -- When reading the code, `COLOR_ORANGE` is much more meaningful than `#FF7F00`. +- `COLOR_ORANGE` es mucho más fácil de recordar que `"#FF7F00"`. +- Es mucho más fácil escribir mal `"#FF7F00"` que `COLOR_ORANGE`. +- Al leer el código, `COLOR_ORANGE` tiene mucho más significado que `#FF7F00`. -When should we use capitals for a constant and when should we name it normally? Let's make that clear. +¿Cuándo se deben utilizar letras mayúsculas para una constante, y cuando se debe nombrarla de manera normal? Dejémoslo claro. -Being a "constant" just means that a variable's value never changes. But some constants are known before execution (like a hexadecimal value for red) and some constants are *calculated* in run-time, during the execution, but do not change after their initial assignment. +Ser una "constante" solo significa que el valor de la variable nunca cambia. Pero hay constantes que son conocidas previo a la ejecución (como el valor hexadecimal del color rojo) y hay constantes que son *calculadas* en el tiempo de ejecución, pero no cambian después de su asignación inicial. -For instance: +Por ejemplo: ```js -const pageLoadTime = /* time taken by a webpage to load */; +const pageLoadTime = /* el tiempo que tardó la página web para cargar */; ``` -The value of `pageLoadTime` is not known before the page load, so it's named normally. But it's still a constant because it doesn't change after the assignment. +El valor de `pageLoadTime` no está preestablecido. Como no se conoce antes de cargar la página, la nombramos normalmente. Pero podemos declararla como constante, porque después de su asignación inicial, no cambiará. -In other words, capital-named constants are only used as aliases for "hard-coded" values. +En otras palabras, las constantes en mayúsculas son utilizadas solamente como alias para valores invariables y preestablecidos. -## Name things right +## Nombrar cosas correctamente -Talking about variables, there's one more extremely important thing. +Estando en el tema de las variables, existe una cosa de mucha importancia. -A variable name should have a clean, obvious meaning, describing the data that it stores. +Una variable debe tener un nombre claro, de significado evidente, que describa el dato que almacena. -Variable naming is one of the most important and complex skills in programming. A glance at variable names can reveal which code was written by a beginner versus an experienced developer. +Nombrar variables es una de las habilidades más importantes y complejas en la programación. Un vistazo rápido a el nombre de las variables nos revela cuál código fue escrito por un principiante o por un desarrollador experimentado. -In a real project, most of the time is spent modifying and extending an existing code base rather than writing something completely separate from scratch. When we return to some code after doing something else for a while, it's much easier to find information that is well-labelled. Or, in other words, when the variables have good names. +En un proyecto real, se pasa mucho más tiempo modificando y extendiendo una base de código existente que escribiendo algo nuevo desde cero. Cuando regresamos a nuestro código luego de un tiempo, es mucho más fácil encontrar información que está bien etiquetada. O en otras palabras, cuando las variables tienen los nombres adecuados. -Please spend time thinking about the right name for a variable before declaring it. Doing so will repay you handsomely. +Por favor, dedica tiempo para pensar un nombre correcto para una variable antes de declararla. Hacer esto te rendirá muy bien. -Some good-to-follow rules are: +Algunas reglas buenas para seguir: -- Use human-readable names like `userName` or `shoppingCart`. -- Stay away from abbreviations or short names like `a`, `b`, and `c`, unless you know what you're doing. -- Make names maximally descriptive and concise. Examples of bad names are `data` and `value`. Such names say nothing. It's only okay to use them if the context of the code makes it exceptionally obvious which data or value the variable is referencing. -- Agree on terms within your team and in your mind. If a site visitor is called a "user" then we should name related variables `currentUser` or `newUser` instead of `currentVisitor` or `newManInTown`. +- Usa términos legibles para humanos como `userName` p `shoppingCart`. +- Evita abreviaciones o nombres cortos `a`, `b`, `c`, a menos que realmente sepas lo que estás haciendo. +- Crea nombres que describan al máximo lo que son y sean concisos. Ejemplos de nombres malos son `data` y `value`. Estos nombres no nos dicen nada, solo son adecuados en el contexto de un código que deje excepcionalmente obvio cuál dato o valor está referenciando la variable. +- Ponte de acuerdo con tu equipo, y con tu propia mente, cuáles términos se utilizarán. Si a un visitante se lo llamara "user", debemos llamar las variables relacionadas `currentUser` o `newUser` en vez de `currentVisitor` o `newManInTown`. -Sounds simple? Indeed it is, but creating descriptive and concise variable names in practice is not. Go for it. +¿Suena simple? De hecho lo es, pero no es tan fácil crear nombres de variables descriptivos y concisos a la hora de practicar. Inténtelo. -```smart header="Reuse or create?" -And the last note. There are some lazy programmers who, instead of declaring new variables, tend to reuse existing ones. +```smart header="¿Reusar o crear?" +Una última nota. Existen programadores haraganes que, en vez de declarar una variable nueva, tienden a reusar las existentes. -As a result, their variables are like boxes into which people throw different things without changing their stickers. What's inside the box now? Who knows? We need to come closer and check. +El resultado de esto es que sus variables son como cajas en las cuales la gente introduce cosas distintas sin cambiar sus etiquetas. ¿Que existe dentro de la caja? ¿Quién sabe? Necesitamos acercarnos y revisar. -Such programmers save a little bit on variable declaration but lose ten times more on debugging. +Dichos programadores se ahorran un poco durante la declaración de la variable, pero pierden diez veces más a la hora de depuración. -An extra variable is good, not evil. +Una variable extra es algo bueno, no algo diabólico. -Modern JavaScript minifiers and browsers optimize code well enough, so it won't create performance issues. Using different variables for different values can even help the engine optimize your code. +Los navegadores modernos y los minificadores de JavaScript optimizan el código, así que esto no impacta en el rendimiento. Utilizar diferentes variables para distintos valores incluso puede ayudar a optimizar tu código. ``` -## Summary +## Resumen -We can declare variables to store data by using the `var`, `let`, or `const` keywords. +Podemos declarar variables para almacenar datos al utilizar las palabra clave `var`, `let`, o `const`. -- `let` -- is a modern variable declaration. -- `var` -- is an old-school variable declaration. Normally we don't use it at all, but we'll cover subtle differences from `let` in the chapter , just in case you need them. -- `const` -- is like `let`, but the value of the variable can't be changed. +- `let` -- es la forma moderna de declaración de una variable. +- `var` -- es la declaración de variable de vieja escuela. Normalmente no lo utilizamos en absoluto. Cubriremos sus sutiles diferencias con `let` en el capítulo , por si lo necesitaras. +- `const` -- es como `let`, pero una vez asignado, el valor de la variable no podrá alterarse. -Variables should be named in a way that allows us to easily understand what's inside them. +Las variables deben ser nombradas de tal manera que entendamos fácilmente lo que está en su interior. diff --git a/1-js/02-first-steps/05-types/1-string-quotes/solution.md b/1-js/02-first-steps/05-types/1-string-quotes/solution.md index 68a13c15b..99868eccf 100644 --- a/1-js/02-first-steps/05-types/1-string-quotes/solution.md +++ b/1-js/02-first-steps/05-types/1-string-quotes/solution.md @@ -1,15 +1,15 @@ -Backticks embed the expression inside `${...}` into the string. +Los backticks incrustan la expresión dentro de `${...}` en la cadena. ```js run let name = "Ilya"; -// the expression is a number 1 -alert( `hello ${1}` ); // hello 1 +// la expresión es un número 1 +alert( `hola ${1}` ); // hola 1 -// the expression is a string "name" -alert( `hello ${"name"}` ); // hello name +// la expresión es la cadena "name" +alert( `hola ${"name"}` ); // hola name -// the expression is a variable, embed it -alert( `hello ${name}` ); // hello Ilya +// la expresión es una variable +alert( `hola ${name}` ); // hola Ilya ``` diff --git a/1-js/02-first-steps/05-types/1-string-quotes/task.md b/1-js/02-first-steps/05-types/1-string-quotes/task.md index 14ea6b4d6..88df9a9c9 100644 --- a/1-js/02-first-steps/05-types/1-string-quotes/task.md +++ b/1-js/02-first-steps/05-types/1-string-quotes/task.md @@ -2,16 +2,16 @@ importance: 5 --- -# String quotes +# Comillas -What is the output of the script? +¿Cuál es la salida del script? ```js let name = "Ilya"; -alert( `hello ${1}` ); // ? +alert( `Hola ${1}` ); // ? -alert( `hello ${"name"}` ); // ? +alert( `Hola ${"name"}` ); // ? -alert( `hello ${name}` ); // ? -``` \ No newline at end of file +alert( `Hola ${name}` ); // ? +``` diff --git a/1-js/02-first-steps/05-types/article.md b/1-js/02-first-steps/05-types/article.md index 04e8b2450..24bf592ec 100644 --- a/1-js/02-first-steps/05-types/article.md +++ b/1-js/02-first-steps/05-types/article.md @@ -1,18 +1,18 @@ -# Data types +# Tipos de datos -A value in JavaScript is always of a certain type. For example, a string or a number. +Un valor en JavaScript siempre pertenece a un tipo de dato determinado. Por ejemplo, un string o un número. -There are eight basic data types in JavaScript. Here, we'll cover them in general and in the next chapters we'll talk about each of them in detail. +Hay ocho tipos de datos básicos en JavaScript. En este capítulo los cubriremos en general y en los próximos hablaremos de cada uno de ellos en detalle. -We can put any type in a variable. For example, a variable can at one moment be a string and then store a number: +Podemos almacenar un valor de cualquier tipo dentro de una variable. Por ejemplo, una variable puede contener en un momento un string y luego almacenar un número: ```js -// no error -let message = "hello"; +// no hay error +let message = "hola"; message = 123456; ``` -Programming languages that allow such things, such as JavaScript, are called "dynamically typed", meaning that there exist data types, but variables are not bound to any of them. +Los lenguajes de programación que permiten estas cosas, como JavaScript, se denominan "dinámicamente tipados", lo que significa que allí hay tipos de datos, pero las variables no están vinculadas rígidamente a ninguno de ellos. ## Number @@ -21,32 +21,32 @@ let n = 123; n = 12.345; ``` -The *number* type represents both integer and floating point numbers. +El tipo *number* representa tanto números enteros como de punto flotante. -There are many operations for numbers, e.g. multiplication `*`, division `/`, addition `+`, subtraction `-`, and so on. +Hay muchas operaciones para números. Por ejemplo, multiplicación `*`, división `/`, suma `+`, resta `-`, y demás. -Besides regular numbers, there are so-called "special numeric values" which also belong to this data type: `Infinity`, `-Infinity` and `NaN`. +Además de los números comunes, existen los llamados "valores numéricos especiales" que también pertenecen a este tipo de datos: `Infinity`, `-Infinity` y `NaN`. -- `Infinity` represents the mathematical [Infinity](https://en.wikipedia.org/wiki/Infinity) ∞. It is a special value that's greater than any number. +- `Infinity` representa el [Infinito](https://es.wikipedia.org/wiki/Infinito) matemático ∞. Es un valor especial que es mayor que cualquier número. - We can get it as a result of division by zero: + Podemos obtenerlo como resultado de la división por cero: ```js run alert( 1 / 0 ); // Infinity ``` - Or just reference it directly: + O simplemente hacer referencia a él directamente: ```js run alert( Infinity ); // Infinity ``` -- `NaN` represents a computational error. It is a result of an incorrect or an undefined mathematical operation, for instance: +- `NaN` representa un error de cálculo. Es el resultado de una operación matemática incorrecta o indefinida, por ejemplo: ```js run - alert( "not a number" / 2 ); // NaN, such division is erroneous + alert( "no es un número" / 2 ); // NaN, tal división es errónea ``` - `NaN` is sticky. Any further mathematical operation on `NaN` returns `NaN`: + `NaN` es "pegajoso". Cualquier otra operación sobre `NaN` devuelve `NaN`: ```js run alert( NaN + 1 ); // NaN @@ -54,172 +54,172 @@ Besides regular numbers, there are so-called "special numeric values" which also alert( "not a number" / 2 - 1 ); // NaN ``` - So, if there's a `NaN` somewhere in a mathematical expression, it propagates to the whole result (there's only one exception to that: `NaN ** 0` is `1`). + Por lo tanto, si hay un `NaN` en alguna parte de una expresión matemática, se propaga a todo el resultado (con una única excepción: `NaN ** 0` es `1`). -```smart header="Mathematical operations are safe" -Doing maths is "safe" in JavaScript. We can do anything: divide by zero, treat non-numeric strings as numbers, etc. +```smart header="Las operaciones matemáticas son seguras" +Hacer matemáticas es "seguro" en JavaScript. Podemos hacer cualquier cosa: dividir por cero, tratar las cadenas no numéricas como números, etc. -The script will never stop with a fatal error ("die"). At worst, we'll get `NaN` as the result. +El script nunca se detendrá con un error fatal (y "morir"). En el peor de los casos, obtendremos `NaN` como resultado. ``` -Special numeric values formally belong to the "number" type. Of course they are not numbers in the common sense of this word. +Los valores numéricos especiales pertenecen formalmente al tipo "número". Por supuesto que no son números en el sentido estricto de la palabra. -We'll see more about working with numbers in the chapter . +Veremos más sobre el trabajo con números en el capítulo . ## BigInt [#bigint-type] -In JavaScript, the "number" type cannot safely represent integer values larger than (253-1) (that's `9007199254740991`), or less than -(253-1) for negatives. +En JavaScript, el tipo "number" no puede representar de forma segura valores enteros mayores que (253-1) (eso es `9007199254740991`), o menor que -(253-1) para negativos. -To be really precise, the "number" type can store larger integers (up to 1.7976931348623157 * 10308), but outside of the safe integer range ±(253-1) there'll be a precision error, because not all digits fit into the fixed 64-bit storage. So an "approximate" value may be stored. +Para ser realmente precisos, el tipo de dato "number" puede almacenar enteros muy grandes (hasta 1.7976931348623157 * 10308), pero fuera del rango de enteros seguros ±(253-1) habrá un error de precisión, porque no todos los dígitos caben en el almacén fijo de 64-bit. Así que es posible que se almacene un valor "aproximado". -For example, these two numbers (right above the safe range) are the same: +Por ejemplo, estos dos números (justo por encima del rango seguro) son iguales: ```js console.log(9007199254740991 + 1); // 9007199254740992 console.log(9007199254740991 + 2); // 9007199254740992 ``` -So to say, all odd integers greater than (253-1) can't be stored at all in the "number" type. +Podemos decir que ningún entero impar mayor que (253-1) puede almacenarse en el tipo de dato "number". -For most purposes ±(253-1) range is quite enough, but sometimes we need the entire range of really big integers, e.g. for cryptography or microsecond-precision timestamps. +Para la mayoría de los propósitos, el rango ±(253-1) es suficiente, pero a veces necesitamos números realmente grandes; por ejemplo, para criptografía o marcas de tiempo de precisión de microsegundos. -`BigInt` type was recently added to the language to represent integers of arbitrary length. +`BigInt` se agregó recientemente al lenguaje para representar enteros de longitud arbitraria. -A `BigInt` value is created by appending `n` to the end of an integer: +Un valor `BigInt` se crea agregando `n` al final de un entero: ```js -// the "n" at the end means it's a BigInt +// la "n" al final significa que es un BigInt const bigInt = 1234567890123456789012345678901234567890n; ``` -As `BigInt` numbers are rarely needed, we don't cover them here, but devoted them a separate chapter . Read it when you need such big numbers. +Como los números `BigInt` rara vez se necesitan, no los cubrimos aquí sino que les dedicamos un capítulo separado . Léelo cuando necesites números tan grandes. ## String -A string in JavaScript must be surrounded by quotes. +Un *string* en JavaScript es una cadena de caracteres y debe colocarse entre comillas. ```js -let str = "Hello"; -let str2 = 'Single quotes are ok too'; -let phrase = `can embed another ${str}`; +let str = "Hola"; +let str2 = 'Las comillas simples también están bien'; +let phrase = `se puede incrustar otro ${str}`; ``` -In JavaScript, there are 3 types of quotes. +En JavaScript, hay 3 tipos de comillas. -1. Double quotes: `"Hello"`. -2. Single quotes: `'Hello'`. -3. Backticks: `Hello`. +1. Comillas dobles: `"Hola"`. +2. Comillas simples: `'Hola'`. +3. Backticks (comillas invertidas): `Hola`. -Double and single quotes are "simple" quotes. There's practically no difference between them in JavaScript. +Las comillas dobles y simples son comillas "sencillas" (es decir, funcionan igual). No hay diferencia entre ellas en JavaScript. -Backticks are "extended functionality" quotes. They allow us to embed variables and expressions into a string by wrapping them in `${…}`, for example: +Los backticks son comillas de "funcionalidad extendida". Nos permiten incrustar variables y expresiones en una cadena de caracteres encerrándolas en `${...}`, por ejemplo: ```js run let name = "John"; -// embed a variable -alert( `Hello, *!*${name}*/!*!` ); // Hello, John! +// incrustar una variable +alert( `Hola, *!*${name}*/!*!` ); // Hola, John! -// embed an expression -alert( `the result is *!*${1 + 2}*/!*` ); // the result is 3 +// incrustar una expresión +alert( `el resultado es *!*${1 + 2}*/!*` ); //el resultado es 3 ``` -The expression inside `${…}` is evaluated and the result becomes a part of the string. We can put anything in there: a variable like `name` or an arithmetical expression like `1 + 2` or something more complex. +La expresión dentro de `${...}` se evalúa y el resultado pasa a formar parte de la cadena. Podemos poner cualquier cosa ahí dentro: una variable como `name`, una expresión aritmética como `1 + 2`, o algo más complejo. -Please note that this can only be done in backticks. Other quotes don't have this embedding functionality! +Toma en cuenta que esto sólo se puede hacer con los backticks. ¡Las otras comillas no tienen esta capacidad de incrustación! ```js run -alert( "the result is ${1 + 2}" ); // the result is ${1 + 2} (double quotes do nothing) +alert( "el resultado es ${1 + 2}" ); // el resultado es ${1 + 2} (las comillas dobles no hacen nada) ``` -We'll cover strings more thoroughly in the chapter . +En el capítulo trataremos más a fondo las cadenas. -```smart header="There is no *character* type." -In some languages, there is a special "character" type for a single character. For example, in the C language and in Java it is called "char". +```smart header="No existe el tipo *carácter*". +En algunos lenguajes, hay un tipo especial "carácter" para un solo carácter. Por ejemplo, en el lenguaje C y en Java es `char`. -In JavaScript, there is no such type. There's only one type: `string`. A string may consist of zero characters (be empty), one character or many of them. +En JavaScript no existe tal tipo. Sólo hay un tipo: `string`. Un string puede estar formado por un solo carácter, por ninguno, o por varios de ellos. ``` -## Boolean (logical type) +## Boolean (tipo lógico) -The boolean type has only two values: `true` and `false`. +El tipo *boolean* tiene sólo dos valores posibles: `true` y `false`. -This type is commonly used to store yes/no values: `true` means "yes, correct", and `false` means "no, incorrect". +Este tipo se utiliza comúnmente para almacenar valores de sí/no: `true` significa "sí, correcto, verdadero", y `false` significa "no, incorrecto, falso". -For instance: +Por ejemplo: ```js -let nameFieldChecked = true; // yes, name field is checked -let ageFieldChecked = false; // no, age field is not checked +let nameFieldChecked = true; // sí, el campo name está marcado +let ageFieldChecked = false; // no, el campo age no está marcado ``` -Boolean values also come as a result of comparisons: +Los valores booleanos también son el resultado de comparaciones: ```js run let isGreater = 4 > 1; -alert( isGreater ); // true (the comparison result is "yes") +alert( isGreater ); // verdadero (el resultado de la comparación es "sí") ``` -We'll cover booleans more deeply in the chapter . +En el capítulo trataremos más a fondo el tema de los booleanos. -## The "null" value +## El valor "null" (nulo) -The special `null` value does not belong to any of the types described above. +El valor especial `null` no pertenece a ninguno de los tipos descritos anteriormente. -It forms a separate type of its own which contains only the `null` value: +Forma un tipo propio separado que contiene sólo el valor `null`: ```js let age = null; ``` -In JavaScript, `null` is not a "reference to a non-existing object" or a "null pointer" like in some other languages. +En JavaScript, `null` no es una "referencia a un objeto inexistente" o un "puntero nulo" como en otros lenguajes. -It's just a special value which represents "nothing", "empty" or "value unknown". +Es sólo un valor especial que representa "nada", "vacío" o "valor desconocido". -The code above states that `age` is unknown. +El código anterior indica que el valor de `age` es desconocido o está vacío por alguna razón. -## The "undefined" value +## El valor "undefined" (indefinido) -The special value `undefined` also stands apart. It makes a type of its own, just like `null`. +El valor especial `undefined` también se distingue. Hace un tipo propio, igual que `null`. -The meaning of `undefined` is "value is not assigned". +El significado de `undefined` es "valor no asignado". -If a variable is declared, but not assigned, then its value is `undefined`: +Si una variable es declarada, pero no asignada, entonces su valor es `undefined`: ```js run let age; -alert(age); // shows "undefined" +alert(age); // muestra "undefined" ``` -Technically, it is possible to explicitly assign `undefined` to a variable: +Técnicamente, es posible asignar `undefined` a cualquier variable: ```js run let age = 100; -// change the value to undefined +// cambiando el valor a undefined age = undefined; alert(age); // "undefined" ``` -...But we don't recommend doing that. Normally, one uses `null` to assign an "empty" or "unknown" value to a variable, while `undefined` is reserved as a default initial value for unassigned things. +...Pero no recomendamos hacer eso. Normalmente, usamos `null` para asignar un valor "vacío" o "desconocido" a una variable, mientras `undefined` es un valor inicial reservado para cosas que no han sido asignadas. -## Objects and Symbols +## Object y Symbol -The `object` type is special. +El tipo `object` (objeto) es especial. -All other types are called "primitive" because their values can contain only a single thing (be it a string or a number or whatever). In contrast, objects are used to store collections of data and more complex entities. +Todos los demás tipos se llaman "primitivos" porque sus valores pueden contener una sola cosa (ya sea una cadena, un número, o lo que sea). Por el contrario, los objetos se utilizan para almacenar colecciones de datos y entidades más complejas. -Being that important, objects deserve a special treatment. We'll deal with them later in the chapter , after we learn more about primitives. +Siendo así de importantes, los objetos merecen un trato especial. Nos ocuparemos de ellos más adelante en el capítulo después de aprender más sobre los primitivos. -The `symbol` type is used to create unique identifiers for objects. We have to mention it here for the sake of completeness, but also postpone the details till we know objects. +El tipo `symbol` (símbolo) se utiliza para crear identificadores únicos para los objetos. Tenemos que mencionarlo aquí para una mayor integridad, pero es mejor estudiar este tipo después de los objetos. -## The typeof operator [#type-typeof] +## El operador typeof [#type-typeof] -The `typeof` operator returns the type of the operand. It's useful when we want to process values of different types differently or just want to do a quick check. +El operador `typeof` devuelve el tipo de dato del operando. Es útil cuando queremos procesar valores de diferentes tipos de forma diferente o simplemente queremos hacer una comprobación rápida. -A call to `typeof x` returns a string with the type name: +La llamada a `typeof x` devuelve una cadena con el nombre del tipo: ```js typeof undefined // "undefined" @@ -247,41 +247,39 @@ typeof alert // "function" (3) */!* ``` -The last three lines may need additional explanation: +Las últimas tres líneas pueden necesitar una explicación adicional: -1. `Math` is a built-in object that provides mathematical operations. We will learn it in the chapter . Here, it serves just as an example of an object. -2. The result of `typeof null` is `"object"`. That's an officially recognized error in `typeof`, coming from very early days of JavaScript and kept for compatibility. Definitely, `null` is not an object. It is a special value with a separate type of its own. The behavior of `typeof` is wrong here. -3. The result of `typeof alert` is `"function"`, because `alert` is a function. We'll study functions in the next chapters where we'll also see that there's no special "function" type in JavaScript. Functions belong to the object type. But `typeof` treats them differently, returning `"function"`. That also comes from the early days of JavaScript. Technically, such behavior isn't correct, but can be convenient in practice. +1. `Math` es un objeto incorporado que proporciona operaciones matemáticas. Lo aprenderemos en el capítulo . Aquí sólo sirve como ejemplo de un objeto. +2. El resultado de `typeof null` es `"object"`. Esto está oficialmente reconocido como un error de comportamiento de `typeof` que proviene de los primeros días de JavaScript y se mantiene por compatibilidad. Definitivamente `null` no es un objeto. Es un valor especial con un tipo propio separado. +3. El resultado de `typeof alert` es `"function"` porque `alert` es una función. Estudiaremos las funciones en los próximos capítulos donde veremos que no hay ningún tipo especial "function" en JavaScript. Las funciones pertenecen al tipo objeto. Pero `typeof` las trata de manera diferente, devolviendo `function`. Además proviene de los primeros días de JavaScript. Técnicamente dicho comportamiento es incorrecto, pero puede ser conveniente en la práctica. -```smart header="The `typeof(x)` syntax" -You may also come across another syntax: `typeof(x)`. It's the same as `typeof x`. +```smart header="Sintaxis de `typeof(x)`" +Se puede encontrar otra sintaxis en algún código: `typeof(x)`. Es lo mismo que `typeof x`. -To put it clear: `typeof` is an operator, not a function. The parentheses here aren't a part of `typeof`. It's the kind of parentheses used for mathematical grouping. +Para ponerlo en claro: `typeof` es un operador, no una función. Los paréntesis aquí no son parte del operador `typeof`. Son del tipo usado en agrupamiento matemático. -Usually, such parentheses contain a mathematical expression, such as `(2 + 2)`, but here they contain only one argument `(x)`. Syntactically, they allow to avoid a space between the `typeof` operator and its argument, and some people like it. +Usualmente, tales paréntesis contienen expresiones matemáticas tales como `(2 + 2)`, pero aquí solo tienen un argumento `(x)`. Sintácticamente, permiten evitar el espacio entre el operador `typeof` y su argumento, y a algunas personas les gusta así. -Some people prefer `typeof(x)`, although the `typeof x` syntax is much more common. +Algunos prefieren `typeof(x)`, aunque la sintaxis `typeof x` es mucho más común. ``` -## Summary +## Resumen -There are 8 basic data types in JavaScript. +Hay 8 tipos básicos en JavaScript. -- Seven primitive data types: - - `number` for numbers of any kind: integer or floating-point, integers are limited by ±(253-1). - - `bigint` for integer numbers of arbitrary length. - - `string` for strings. A string may have zero or more characters, there's no separate single-character type. - - `boolean` for `true`/`false`. - - `null` for unknown values -- a standalone type that has a single value `null`. - - `undefined` for unassigned values -- a standalone type that has a single value `undefined`. - - `symbol` for unique identifiers. -- And one non-primitive data type: - - `object` for more complex data structures. +- Siete tipos de datos primitivos + - `number` para números de cualquier tipo: enteros o de punto flotante, los enteros están limitados por ±(253-1). + - `bigint` para números enteros de longitud arbitraria. + - `string` para cadenas. Una cadena puede tener cero o más caracteres, no hay un tipo especial para un único carácter. + - `boolean` para verdadero y falso: `true`/`false`. + - `null` para valores desconocidos -- un tipo independiente que tiene un solo valor nulo: `null`. + - `undefined` para valores no asignados -- un tipo independiente que tiene un único valor "indefinido": `undefined`. + - `symbol` para identificadores únicos. +- Y un tipo de dato no primitivo: + - `object` para estructuras de datos complejas. -The `typeof` operator allows us to see which type is stored in a variable. +El operador `typeof` nos permite ver qué tipo está almacenado en una variable. -- Usually used as `typeof x`, but `typeof(x)` is also possible. -- Returns a string with the name of the type, like `"string"`. -- For `null` returns `"object"` -- this is an error in the language, it's not actually an object. - -In the next chapters, we'll concentrate on primitive values and once we're familiar with them, we'll move on to objects. +- Dos formas: `typeof x` o `typeof(x)`. +- Devuelve una cadena con el nombre del tipo. Por ejemplo `"string"`. +- Para `null` devuelve `"object"`: esto es un error en el lenguaje, en realidad no es un objeto. diff --git a/1-js/02-first-steps/06-alert-prompt-confirm/1-simple-page/solution.md b/1-js/02-first-steps/06-alert-prompt-confirm/1-simple-page/solution.md index 903ee7ff3..b604fa574 100644 --- a/1-js/02-first-steps/06-alert-prompt-confirm/1-simple-page/solution.md +++ b/1-js/02-first-steps/06-alert-prompt-confirm/1-simple-page/solution.md @@ -1,11 +1,11 @@ -JavaScript-code: +Código JavaScript: ```js demo run -let name = prompt("What is your name?", ""); +let name = prompt("¿Cuál es tu nombre?", ""); alert(name); ``` -The full page: +La página completa: ```html @@ -15,7 +15,7 @@ The full page: diff --git a/1-js/02-first-steps/06-alert-prompt-confirm/1-simple-page/task.md b/1-js/02-first-steps/06-alert-prompt-confirm/1-simple-page/task.md index a65a654e0..a0fa47447 100644 --- a/1-js/02-first-steps/06-alert-prompt-confirm/1-simple-page/task.md +++ b/1-js/02-first-steps/06-alert-prompt-confirm/1-simple-page/task.md @@ -2,8 +2,8 @@ importance: 4 --- -# A simple page +# Una página simple -Create a web-page that asks for a name and outputs it. +Crea una página web que pida un nombre y lo muestre. [demo] diff --git a/1-js/02-first-steps/06-alert-prompt-confirm/article.md b/1-js/02-first-steps/06-alert-prompt-confirm/article.md index ef0f333cb..eaf9150a8 100644 --- a/1-js/02-first-steps/06-alert-prompt-confirm/article.md +++ b/1-js/02-first-steps/06-alert-prompt-confirm/article.md @@ -1,105 +1,105 @@ -# Interaction: alert, prompt, confirm +# Interacción: alert, prompt, confirm -As we'll be using the browser as our demo environment, let's see a couple of functions to interact with the user: `alert`, `prompt` and `confirm`. +Como usaremos el navegador como nuestro entorno de demostración, veamos un par de funciones para interactuar con el usuario: `alert`, `prompt`, y `confirm`. ## alert -This one we've seen already. It shows a message and waits for the user to press "OK". +Ya la hemos visto. Muestra un mensaje y espera a que el usuario presione "Aceptar". -For example: +Por ejemplo: ```js run alert("Hello"); ``` -The mini-window with the message is called a *modal window*. The word "modal" means that the visitor can't interact with the rest of the page, press other buttons, etc, until they have dealt with the window. In this case -- until they press "OK". +La mini ventana con el mensaje se llama * ventana modal *. La palabra "modal" significa que el visitante no puede interactuar con el resto de la página, presionar otros botones, etc., hasta que se haya ocupado de la ventana. En este caso, hasta que presionen "OK". ## prompt -The function `prompt` accepts two arguments: +La función `prompt` acepta dos argumentos: ```js no-beautify result = prompt(title, [default]); ``` -It shows a modal window with a text message, an input field for the visitor, and the buttons OK/Cancel. +Muestra una ventana modal con un mensaje de texto, un campo de entrada para el visitante y los botones OK/CANCELAR. `title` -: The text to show the visitor. +: El texto a mostrar al usuario. `default` -: An optional second parameter, the initial value for the input field. +: Un segundo parámetro opcional, es el valor inicial del campo de entrada. -```smart header="The square brackets in syntax `[...]`" -The square brackets around `default` in the syntax above denote that the parameter is optional, not required. +```smart header="Corchetes en la sintaxis `[...]`" +Los corchetes alrededor de `default` en la sintaxis de arriba denotan que el parámetro es opcional, no requerido. ``` -The visitor can type something in the prompt input field and press OK. Then we get that text in the `result`. Or they can cancel the input by pressing Cancel or hitting the `key:Esc` key, then we get `null` as the `result`. +El usuario puede escribir algo en el campo de entrada de solicitud y presionar OK, así obtenemos ese texto en `result`. O puede cancelar la entrada, con el botón "Cancelar" o presionando la tecla `key:Esc`, de este modo se obtiene `null` en `result`. -The call to `prompt` returns the text from the input field or `null` if the input was canceled. +La llamada a `prompt` retorna el texto del campo de entrada o `null` si la entrada fue cancelada. -For instance: +Por ejemplo: ```js run -let age = prompt('How old are you?', 100); +let age = prompt ('¿Cuántos años tienes?', 100); -alert(`You are ${age} years old!`); // You are 100 years old! +alert(`Tienes ${age} años!`); //Tienes 100 años! ``` -````warn header="In IE: always supply a `default`" -The second parameter is optional, but if we don't supply it, Internet Explorer will insert the text `"undefined"` into the prompt. +````warn header="En IE: proporciónale un *predeterminado* siempre" +El segundo parámetro es opcional, pero si no lo proporcionamos, Internet Explorer insertará el texto `"undefined"` en el prompt. -Run this code in Internet Explorer to see: +Ejecuta este código en Internet Explorer para verlo: ```js run let test = prompt("Test"); ``` -So, for prompts to look good in IE, we recommend always providing the second argument: +Por lo tanto, para que las indicaciones se vean bien en IE, recomendamos siempre proporcionar el segundo argumento: ```js run -let test = prompt("Test", ''); // <-- for IE +let test = prompt("Test", ''); // <-- para IE ``` ```` ## confirm -The syntax: +La sintaxis: ```js -result = confirm(question); +result = confirm(pregunta); ``` -The function `confirm` shows a modal window with a `question` and two buttons: OK and Cancel. +La función `confirm` muestra una ventana modal con una `pregunta` y dos botones: OK y CANCELAR. -The result is `true` if OK is pressed and `false` otherwise. +El resultado es `true` si se pulsa OK y `false` en caso contrario. -For example: +Por ejemplo: ```js run -let isBoss = confirm("Are you the boss?"); +let isBoss = confirm("¿Eres el jefe?"); -alert( isBoss ); // true if OK is pressed +alert( isBoss ); // true si se pulsa OK ``` -## Summary +## Resumen -We covered 3 browser-specific functions to interact with visitors: +Cubrimos 3 funciones específicas del navegador para interactuar con los usuarios: `alert` -: shows a message. +: muestra un mensaje. `prompt` -: shows a message asking the user to input text. It returns the text or, if Cancel button or `key:Esc` is clicked, `null`. +: muestra un mensaje pidiendo al usuario que introduzca un texto. Retorna el texto o, si se hace clic en CANCELAR o se presiona `key:Esc`, retorna `null`. `confirm` -: shows a message and waits for the user to press "OK" or "Cancel". It returns `true` for OK and `false` for Cancel/`key:Esc`. +: muestra un mensaje y espera a que el usuario pulse "OK" o "CANCELAR". Retorna `true` si se presiona OK y `false` si se presiona CANCEL/`key:Esc`. -All these methods are modal: they pause script execution and don't allow the visitor to interact with the rest of the page until the window has been dismissed. +Todos estos métodos son modales: detienen la ejecución del script y no permiten que el usuario interactúe con el resto de la página hasta que la ventana se haya cerrado. -There are two limitations shared by all the methods above: +Hay dos limitaciones comunes a todos los métodos anteriores: -1. The exact location of the modal window is determined by the browser. Usually, it's in the center. -2. The exact look of the window also depends on the browser. We can't modify it. +1. La ubicación exacta de la ventana modal está determinada por el navegador. Normalmente, está en el centro. +2. El aspecto exacto de la ventana también depende del navegador. No podemos modificarlo. -That is the price for simplicity. There are other ways to show nicer windows and richer interaction with the visitor, but if "bells and whistles" do not matter much, these methods work just fine. +Ese es el precio de la simplicidad. Existen otras formas de mostrar ventanas más atractivas e interactivas para el usuario, pero si la apariencia no importa mucho, estos métodos funcionan bien. diff --git a/1-js/02-first-steps/07-type-conversions/article.md b/1-js/02-first-steps/07-type-conversions/article.md index 329556141..4dda571bb 100644 --- a/1-js/02-first-steps/07-type-conversions/article.md +++ b/1-js/02-first-steps/07-type-conversions/article.md @@ -1,150 +1,150 @@ -# Type Conversions +# Conversiones de Tipos -Most of the time, operators and functions automatically convert the values given to them to the right type. +La mayoría de las veces, los operadores y las funciones convierten automáticamente los valores que reciben al tipo de dato que necesitan. -For example, `alert` automatically converts any value to a string to show it. Mathematical operations convert values to numbers. +Por ejemplo: `alert` convierte automáticamente cualquier valor a string para mostrarlo, y las operaciones matemáticas convierten los valores a números. -There are also cases when we need to explicitly convert a value to the expected type. +También hay casos donde necesitamos convertir de manera explícita un valor al tipo esperado. -```smart header="Not talking about objects yet" -In this chapter, we won't cover objects. For now, we'll just be talking about primitives. +```smart header="No hablaremos de los objetos aún" +En este capítulo no cubrimos objetos. Por ahora, solamente veremos valores primitivos. -Later, after we learn about objects, in the chapter we'll see how objects fit in. +Más adelante, después de haberlos tratado, veremos en el capítulo cómo funciona la conversión. ``` -## String Conversion +## Conversión a String -String conversion happens when we need the string form of a value. +La conversión a string ocurre cuando necesitamos la representación textual de un valor. -For example, `alert(value)` does it to show the value. +Por ejemplo, `alert(value)` lo hace para mostrar `value` como texto. -We can also call the `String(value)` function to convert a value to a string: +También podemos llamar a la función `String(value)` para convertir `value` a string: ```js run let value = true; alert(typeof value); // boolean *!* -value = String(value); // now value is a string "true" +value = String(value); // ahora value es el string "true" alert(typeof value); // string */!* ``` -String conversion is mostly obvious. A `false` becomes `"false"`, `null` becomes `"null"`, etc. +La conversión a string es bastante evidente. El boolean `false` se convierte en `"false"`, `null` en `"null"`, etc. -## Numeric Conversion +## Conversión numérica -Numeric conversion in mathematical functions and expressions happens automatically. +La conversión numérica ocurre automáticamente en funciones matemáticas y expresiones. -For example, when division `/` is applied to non-numbers: +Por ejemplo, cuando se aplica la división `/` a valores no numéricos: ```js run -alert( "6" / "2" ); // 3, strings are converted to numbers +alert( "6" / "2" ); // 3, los strings son convertidos a números ``` -We can use the `Number(value)` function to explicitly convert a `value` to a number: +Podemos usar la función `Number(value)` para convertir `value` a un número de forma explícita: ```js run let str = "123"; alert(typeof str); // string -let num = Number(str); // becomes a number 123 +let num = Number(str); // se convierte en 123 alert(typeof num); // number ``` -Explicit conversion is usually required when we read a value from a string-based source like a text form but expect a number to be entered. +Usualmente, se requiere conversión explícita cuando leemos un valor desde una fuente basada en texto, como un formulario, pero esperamos que sea un número. -If the string is not a valid number, the result of such a conversion is `NaN`. For instance: +Si el string no es un número válido, el resultado de la conversión será `NaN`. Por ejemplo: ```js run -let age = Number("an arbitrary string instead of a number"); +let age = Number("un texto arbitrario en vez de un número"); -alert(age); // NaN, conversion failed +alert(age); // NaN, conversión fallida ``` -Numeric conversion rules: +Reglas de conversión numérica: -| Value | Becomes... | +| Valor | Se convierte en... | |-------|-------------| |`undefined`|`NaN`| |`null`|`0`| -|true and false | `1` and `0` | -| `string` | Whitespaces (includes spaces, tabs `\t`, newlines `\n` etc.) from the start and end are removed. If the remaining string is empty, the result is `0`. Otherwise, the number is "read" from the string. An error gives `NaN`. | +|true and false | `1` y `0` | +| `string` | Se eliminan los espacios en blanco (incluyendo espacios, tabs `\t`, saltos de línea `\n`, etc.) del inicio y final del texto. Si el string resultante es vacío, el resultado es `0`, en caso contrario el número es "leído" del string. Un error devuelve `NaN`. | -Examples: +Ejemplos: ```js run alert( Number(" 123 ") ); // 123 -alert( Number("123z") ); // NaN (error reading a number at "z") +alert( Number("123z") ); // NaN (error al leer el número cuando encuentra "z") alert( Number(true) ); // 1 alert( Number(false) ); // 0 ``` -Please note that `null` and `undefined` behave differently here: `null` becomes zero while `undefined` becomes `NaN`. +Es importante notar que `null` y `undefined` se comportan de distinta manera aquí: `null` se convierte en `0` mientras que `undefined` se convierte en `NaN`. -Most mathematical operators also perform such conversion, we'll see that in the next chapter. +La mayoría de los operadores matemáticos también ejecutan dicha conversión, esto lo veremos en el siguiente capítulo. -## Boolean Conversion +## Conversión booleana -Boolean conversion is the simplest one. +La conversión a boolean es la más simple. -It happens in logical operations (later we'll meet condition tests and other similar things) but can also be performed explicitly with a call to `Boolean(value)`. +Ocurre en operaciones lógicas (más adelante veremos test condicionales y otras cosas similares), pero también puede realizarse de forma explícita llamando a la función `Boolean(value)`. -The conversion rule: +Las reglas de conversión: -- Values that are intuitively "empty", like `0`, an empty string, `null`, `undefined`, and `NaN`, become `false`. -- Other values become `true`. +- Los valores que son intuitivamente "vacíos", como `0`, `""`, `null`, `undefined`, y `NaN`, se convierten en `false`. +- Otros valores se convierten en `true`. -For instance: +Por ejemplo: ```js run alert( Boolean(1) ); // true alert( Boolean(0) ); // false -alert( Boolean("hello") ); // true +alert( Boolean("hola") ); // true alert( Boolean("") ); // false ``` -````warn header="Please note: the string with zero `\"0\"` is `true`" -Some languages (namely PHP) treat `"0"` as `false`. But in JavaScript, a non-empty string is always `true`. +````warn header="Ten en cuenta: el string con un cero `\"0\"` es `true`" +Algunos lenguajes (como PHP) tratan `"0"` como `false`. Pero en JavaScript, un string no vacío es siempre `true`. ```js run alert( Boolean("0") ); // true -alert( Boolean(" ") ); // spaces, also true (any non-empty string is true) +alert( Boolean(" ") ); // sólo espacios, también true (cualquier string no vacío es true) ``` ```` -## Summary +## Resumen -The three most widely used type conversions are to string, to number, and to boolean. +Las tres conversiones de tipo más usadas son a string, a número y a boolean. -**`String Conversion`** -- Occurs when we output something. Can be performed with `String(value)`. The conversion to string is usually obvious for primitive values. +**`Conversión a String`** -- Ocurre cuando mostramos un valor. Se puede realizar con `String(value)`. La conversión a string es usualmente evidente para los valores primitivos. -**`Numeric Conversion`** -- Occurs in math operations. Can be performed with `Number(value)`. +**`Conversión numérica`** -- Ocurre en operaciones matemáticas. Se puede realizar con `Number(value)`. -The conversion follows the rules: +La conversión sigue las reglas: -| Value | Becomes... | +| Valor | Se convierte en... | |-------|-------------| |`undefined`|`NaN`| |`null`|`0`| |true / false | `1 / 0` | -| `string` | The string is read "as is", whitespaces (includes spaces, tabs `\t`, newlines `\n` etc.) from both sides are ignored. An empty string becomes `0`. An error gives `NaN`. | +| `string` | El string es leído "como es", los espacios en blanco (incluye espacios, tabs `\t`, saltos de línea `\n`, etc.) tanto al inicio como al final son ignorados. Un string vacío se convierte en `0`. Un error entrega `NaN`. | -**`Boolean Conversion`** -- Occurs in logical operations. Can be performed with `Boolean(value)`. +**`Conversión booleana`** -- Ocurren en operaciones lógicas. Se puede realizar con `Boolean(value)`. -Follows the rules: +Sigue las reglas: -| Value | Becomes... | +| Valor | Se convierte en... | |-------|-------------| |`0`, `null`, `undefined`, `NaN`, `""` |`false`| -|any other value| `true` | +|cualquier otro valor| `true` | -Most of these rules are easy to understand and memorize. The notable exceptions where people usually make mistakes are: +La mayoría de estas reglas son fáciles de entender y recordar. Las excepciones más notables donde se suele cometer errores son: -- `undefined` is `NaN` as a number, not `0`. -- `"0"` and space-only strings like `" "` are true as a boolean. +- `undefined` es `NaN` como número, no `0`. +- `"0"` y textos que solo contienen espacios como `" "` son `true` como boolean. -Objects aren't covered here. We'll return to them later in the chapter that is devoted exclusively to objects after we learn more basic things about JavaScript. +Los objetos no son cubiertos aquí. Volveremos a ellos más tarde en el capítulo que está dedicado exclusivamente a objetos después de que aprendamos más cosas básicas sobre JavaScript. diff --git a/1-js/02-first-steps/08-operators/1-increment-order/solution.md b/1-js/02-first-steps/08-operators/1-increment-order/solution.md index 8a44d798e..8d7847405 100644 --- a/1-js/02-first-steps/08-operators/1-increment-order/solution.md +++ b/1-js/02-first-steps/08-operators/1-increment-order/solution.md @@ -1,5 +1,5 @@ -The answer is: +La respuesta es: - `a = 2` - `b = 2` @@ -9,10 +9,9 @@ The answer is: ```js run no-beautify let a = 1, b = 1; -alert( ++a ); // 2, prefix form returns the new value -alert( b++ ); // 1, postfix form returns the old value +alert( ++a ); // 2, la forma de prefijo devuelve el nuevo valor +alert( b++ ); // 1, la forma de sufijo devuelve el antiguo valor -alert( a ); // 2, incremented once -alert( b ); // 2, incremented once +alert( a ); // 2, incrementado una vez +alert( b ); // 2, incrementado una vez ``` - diff --git a/1-js/02-first-steps/08-operators/1-increment-order/task.md b/1-js/02-first-steps/08-operators/1-increment-order/task.md index 7db092389..e2d88aba8 100644 --- a/1-js/02-first-steps/08-operators/1-increment-order/task.md +++ b/1-js/02-first-steps/08-operators/1-increment-order/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# The postfix and prefix forms +# Las formas sufijo y prefijo -What are the final values of all variables `a`, `b`, `c` and `d` after the code below? +¿Cuáles son los valores finales de todas las variables `a`, `b`, `c` y `d` después del código a continuación? ```js let a = 1, b = 1; diff --git a/1-js/02-first-steps/08-operators/2-assignment-result/solution.md b/1-js/02-first-steps/08-operators/2-assignment-result/solution.md index e3113b4cd..b52cc57fc 100644 --- a/1-js/02-first-steps/08-operators/2-assignment-result/solution.md +++ b/1-js/02-first-steps/08-operators/2-assignment-result/solution.md @@ -1,5 +1,5 @@ -The answer is: +La respuesta es: -- `a = 4` (multiplied by 2) -- `x = 5` (calculated as 1 + 4) +- `a = 4` (multiplicado por 2) +- `x = 5` (calculado como 1 + 4) diff --git a/1-js/02-first-steps/08-operators/2-assignment-result/task.md b/1-js/02-first-steps/08-operators/2-assignment-result/task.md index 5345c9485..436cfb9fa 100644 --- a/1-js/02-first-steps/08-operators/2-assignment-result/task.md +++ b/1-js/02-first-steps/08-operators/2-assignment-result/task.md @@ -2,9 +2,9 @@ importance: 3 --- -# Assignment result +# Resultado de asignación -What are the values of `a` and `x` after the code below? +¿Cuáles son los valores de 'a' y 'x' después del código a continuación? ```js let a = 2; diff --git a/1-js/02-first-steps/08-operators/3-primitive-conversions-questions/solution.md b/1-js/02-first-steps/08-operators/3-primitive-conversions-questions/solution.md index 7370b66af..6670211fd 100644 --- a/1-js/02-first-steps/08-operators/3-primitive-conversions-questions/solution.md +++ b/1-js/02-first-steps/08-operators/3-primitive-conversions-questions/solution.md @@ -16,10 +16,10 @@ undefined + 1 = NaN // (6) " \t \n" - 2 = -2 // (7) ``` -1. The addition with a string `"" + 1` converts `1` to a string: `"" + 1 = "1"`, and then we have `"1" + 0`, the same rule is applied. -2. The subtraction `-` (like most math operations) only works with numbers, it converts an empty string `""` to `0`. -3. The addition with a string appends the number `5` to the string. -4. The subtraction always converts to numbers, so it makes `" -9 "` a number `-9` (ignoring spaces around it). -5. `null` becomes `0` after the numeric conversion. -6. `undefined` becomes `NaN` after the numeric conversion. -7. Space characters are trimmed off string start and end when a string is converted to a number. Here the whole string consists of space characters, such as `\t`, `\n` and a "regular" space between them. So, similarly to an empty string, it becomes `0`. +1. La suma con una cadena `"" + 1` convierte `1` a un string: `"" + 1 = "1"`, y luego tenemos `"1" + 0`, la misma regla se aplica. +2. La resta `-` (como la mayoría de las operaciones matemáticas) sólo funciona con números, convierte una cadena vacía `""` a `0`. +3. La suma con una cadena concatena el número `5` a la cadena. +4. La resta siempre convierte a números, por lo tanto hace de `" -9 "` un número `-9` (ignorando los espacios que lo rodean). +5. `null` se convierte en `0` después de la conversión numérica. +6. `undefined` se convierte en `NaN` después de la conversión numérica. +7. Al convertir una cadena en número, se ignoran los espacios en blanco al principio y al final (espacio común " ", tabulador \t, salto de línea \n, etc.). Si la cadena contiene solo ellos, queda vacía y se convierte en 0. diff --git a/1-js/02-first-steps/08-operators/3-primitive-conversions-questions/task.md b/1-js/02-first-steps/08-operators/3-primitive-conversions-questions/task.md index 068420c7d..6092bbfca 100644 --- a/1-js/02-first-steps/08-operators/3-primitive-conversions-questions/task.md +++ b/1-js/02-first-steps/08-operators/3-primitive-conversions-questions/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# Type conversions +# Conversiones de tipos -What are results of these expressions? +¿Cuáles son los resultados de estas expresiones? ```js no-beautify "" + 1 + 0 @@ -23,4 +23,4 @@ undefined + 1 " \t \n" - 2 ``` -Think well, write down and then compare with the answer. +Piensa bien, anótalos y luego compara con la respuesta. diff --git a/1-js/02-first-steps/08-operators/4-fix-prompt/solution.md b/1-js/02-first-steps/08-operators/4-fix-prompt/solution.md index 209a0702c..640371408 100644 --- a/1-js/02-first-steps/08-operators/4-fix-prompt/solution.md +++ b/1-js/02-first-steps/08-operators/4-fix-prompt/solution.md @@ -1,32 +1,32 @@ -The reason is that prompt returns user input as a string. +La razón es que la captura devuelve la entrada del usuario como una cadena. -So variables have values `"1"` and `"2"` respectively. +Entonces las variables tienen valores `"1"` y `"2"` respectivamente. ```js run -let a = "1"; // prompt("First number?", 1); -let b = "2"; // prompt("Second number?", 2); +let a = "1"; // prompt("¿Primer número?", 1); +let b = "2"; // prompt("¿Segundo número?", 2); alert(a + b); // 12 ``` -What we should do is to convert strings to numbers before `+`. For example, using `Number()` or prepending them with `+`. +Lo que debemos hacer es convertir las cadenas de texto a números antes de `+`. Por ejemplo, utilizando `Number()` o anteponiendo `+`. -For example, right before `prompt`: +Por ejemplo, justo antes de `prompt`: ```js run -let a = +prompt("First number?", 1); -let b = +prompt("Second number?", 2); +let a = +prompt("¿Primer número?", 1); +let b = +prompt("¿Segundo número?", 2); alert(a + b); // 3 ``` -Or in the `alert`: +O en el `alert`: ```js run -let a = prompt("First number?", 1); -let b = prompt("Second number?", 2); +let a = prompt("¿Primer número?", 1); +let b = prompt("¿Segundo número?", 2); alert(+a + +b); // 3 ``` -Using both unary and binary `+` in the latest code. Looks funny, doesn't it? +Usar ambos unario y binario `+` en el último ejemplo, se ve raro, ¿no? diff --git a/1-js/02-first-steps/08-operators/4-fix-prompt/task.md b/1-js/02-first-steps/08-operators/4-fix-prompt/task.md index b3ea4a3a3..54c5342fa 100644 --- a/1-js/02-first-steps/08-operators/4-fix-prompt/task.md +++ b/1-js/02-first-steps/08-operators/4-fix-prompt/task.md @@ -2,17 +2,17 @@ importance: 5 --- -# Fix the addition +# Corregir la adición -Here's a code that asks the user for two numbers and shows their sum. +Aquí hay un código que le pide al usuario dos números y muestra su suma. -It works incorrectly. The output in the example below is `12` (for default prompt values). +Funciona incorrectamente. El resultado en el ejemplo a continuación es `12` (para valores de captura predeterminados). -Why? Fix it. The result should be `3`. +¿Por qué? Arreglalo. El resultado debería ser `3`. ```js run -let a = prompt("First number?", 1); -let b = prompt("Second number?", 2); +let a = prompt("¿Primer número?", 1); +let b = prompt("¿Segundo número?", 2); alert(a + b); // 12 ``` diff --git a/1-js/02-first-steps/08-operators/article.md b/1-js/02-first-steps/08-operators/article.md index 0c1663619..f822e7e07 100644 --- a/1-js/02-first-steps/08-operators/article.md +++ b/1-js/02-first-steps/08-operators/article.md @@ -1,15 +1,15 @@ -# Basic operators, maths +# Operadores básicos, matemáticas -We know many operators from school. They are things like addition `+`, multiplication `*`, subtraction `-`, and so on. +Conocemos varios operadores matemáticos porque nos los enseñaron en la escuela. Son cosas como la suma `+`, multiplicación `*`, resta `-`, etcétera. -In this chapter, we’ll start with simple operators, then concentrate on JavaScript-specific aspects, not covered by school arithmetic. +En este capítulo, nos vamos a concentrar en los aspectos de los operadores que no están cubiertos en la aritmética escolar. -## Terms: "unary", "binary", "operand" +## Términos: "unario", "binario", "operando" -Before we move on, let's grasp some common terminology. +Antes de continuar, comprendamos la terminología común. -- *An operand* -- is what operators are applied to. For instance, in the multiplication of `5 * 2` there are two operands: the left operand is `5` and the right operand is `2`. Sometimes, people call these "arguments" instead of "operands". -- An operator is *unary* if it has a single operand. For example, the unary negation `-` reverses the sign of a number: +- *Un operando* -- es a lo que se aplican los operadores. Por ejemplo, en la multiplicación de `5 * 2` hay dos operandos: el operando izquierdo es `5` y el operando derecho es `2`. A veces, la gente los llama "argumentos" en lugar de "operandos". +- Un operador es *unario* si tiene un solo operando. Por ejemplo, la negación unaria `-` invierte el signo de un número: ```js run let x = 1; @@ -17,51 +17,51 @@ Before we move on, let's grasp some common terminology. *!* x = -x; */!* - alert( x ); // -1, unary negation was applied + alert( x ); // -1, se aplicó negación unaria ``` -- An operator is *binary* if it has two operands. The same minus exists in binary form as well: +- Un operador es *binario* si tiene dos operandos. El mismo negativo también existe en forma binaria: ```js run no-beautify let x = 1, y = 3; - alert( y - x ); // 2, binary minus subtracts values + alert( y - x ); // 2, binario negativo resta valores ``` - Formally, in the examples above we have two different operators that share the same symbol: the negation operator, a unary operator that reverses the sign, and the subtraction operator, a binary operator that subtracts one number from another. + Formalmente, estamos hablando de dos operadores distintos: la negación unaria (un operando: revierte el símbolo) y la resta binaria (dos operandos: resta). -## Maths +## Matemáticas -The following math operations are supported: +Están soportadas las siguientes operaciones: -- Addition `+`, -- Subtraction `-`, -- Multiplication `*`, -- Division `/`, -- Remainder `%`, -- Exponentiation `**`. +- Suma `+`, +- Resta `-`, +- Multiplicación `*`, +- División `/`, +- Resto `%`, +- Exponenciación `**`. -The first four are straightforward, while `%` and `**` need a few words about them. +Los primeros cuatro son conocidos mientras que `%` y `**` deben ser explicados más ampliamente. -### Remainder % +## Resto % -The remainder operator `%`, despite its appearance, is not related to percents. +El operador resto `%`, a pesar de su apariencia, no está relacionado con porcentajes. -The result of `a % b` is the [remainder](https://en.wikipedia.org/wiki/Remainder) of the integer division of `a` by `b`. +El resultado de `a % b` es el [resto](https://es.wikipedia.org/wiki/Resto) de la división entera de `a` por `b`. -For instance: +Por ejemplo: ```js run -alert( 5 % 2 ); // 1, the remainder of 5 divided by 2 -alert( 8 % 3 ); // 2, the remainder of 8 divided by 3 -alert( 8 % 4 ); // 0, the remainder of 8 divided by 4 +alert( 5 % 2 ); // 1, es el resto de 5 dividido por 2 +alert( 8 % 3 ); // 2, es el resto de 8 dividido por 3 +alert( 8 % 4 ); // 0, es el resto de 8 dividido por 4 ``` -### Exponentiation ** +## Exponenciación ** -The exponentiation operator `a ** b` raises `a` to the power of `b`. +El operador exponenciación `a ** b` eleva `a` a la potencia de `b`. -In school maths, we write that as ab. +En matemáticas de la escuela, lo escribimos como ab. -For instance: +Por ejemplo: ```js run alert( 2 ** 2 ); // 2² = 4 @@ -69,72 +69,72 @@ alert( 2 ** 3 ); // 2³ = 8 alert( 2 ** 4 ); // 2⁴ = 16 ``` -Just like in maths, the exponentiation operator is defined for non-integer numbers as well. +Matemáticamente, la exponenciación está definida para operadores no enteros también. -For example, a square root is an exponentiation by ½: +Por ejemplo, la raíz cuadrada es el exponente ½: ```js run -alert( 4 ** (1/2) ); // 2 (power of 1/2 is the same as a square root) -alert( 8 ** (1/3) ); // 2 (power of 1/3 is the same as a cubic root) +alert( 4 ** (1/2) ); // 2 (potencia de 1/2 es lo mismo que raíz cuadrada) +alert( 8 ** (1/3) ); // 2 (potencia de 1/3 es lo mismo que raíz cúbica) ``` -## String concatenation with binary + +## Concatenación de cadenas con el binario + -Let's meet the features of JavaScript operators that are beyond school arithmetics. +Ahora veamos las características de los operadores de JavaScript que van más allá de la aritmética escolar. -Usually, the plus operator `+` sums numbers. +Normalmente el operador `+` suma números. -But, if the binary `+` is applied to strings, it merges (concatenates) them: +Pero si se aplica el `+` binario a una cadena, los une (concatena): ```js let s = "my" + "string"; alert(s); // mystring ``` -Note that if any of the operands is a string, then the other one is converted to a string too. +Tenga presente que si uno de los operandos es una cadena, el otro es convertido a una cadena también. -For example: +Por ejemplo: ```js run alert( '1' + 2 ); // "12" alert( 2 + '1' ); // "21" ``` -See, it doesn't matter whether the first operand is a string or the second one. +Vieron, no importa si el primer operando es una cadena o el segundo. -Here's a more complex example: +Aquí hay un ejemplo algo más complejo: ```js run -alert(2 + 2 + '1' ); // "41" and not "221" +alert(2 + 2 + '1' ); // "41" y no "221" ``` -Here, operators work one after another. The first `+` sums two numbers, so it returns `4`, then the next `+` adds the string `1` to it, so it's like `4 + '1' = '41'`. +Aquí, los operadores trabajan uno después de otro. El primer `+` suma dos números entonces devuelve `4`, luego el siguiente `+` le agrega la cadena `1`, así que se evalúa como `4 + '1' = 41`. ```js run -alert('1' + 2 + 2); // "122" and not "14" +alert('1' + 2 + 2); // "122", no es "14" ``` -Here, the first operand is a string, the compiler treats the other two operands as strings too. The `2` gets concatenated to `'1'`, so it's like `'1' + 2 = "12"` and `"12" + 2 = "122"`. +Aquí el primer operando es una cadena, el compilador trata los otros dos operandos como cadenas también. El `2` es concatenado a `'1'`, entonces es como `'1' + 2 = "12"` y `"12" + 2 = "122"`. -The binary `+` is the only operator that supports strings in such a way. Other arithmetic operators work only with numbers and always convert their operands to numbers. +El binario `+` es el único operador que soporta cadenas en esa forma. Otros operadores matemáticos trabajan solamente con números y siempre convierten sus operandos a números. -Here's the demo for subtraction and division: +Por ejemplo, resta y división: ```js run -alert( 6 - '2' ); // 4, converts '2' to a number -alert( '6' / '2' ); // 3, converts both operands to numbers +alert( 2 - '1' ); // 1 +alert( '6' / '2' ); // 3 ``` -## Numeric conversion, unary + +## Conversión numérica, unario + -The plus `+` exists in two forms: the binary form that we used above and the unary form. +La suma `+` existe en dos formas: la forma binaria que utilizamos arriba y la forma unaria. -The unary plus or, in other words, the plus operator `+` applied to a single value, doesn't do anything to numbers. But if the operand is not a number, the unary plus converts it into a number. +El unario suma o, en otras palabras, el operador suma `+` aplicado a un solo valor, no hace nada a los números. Pero si el operando no es un número, el unario suma lo convierte en un número. -For example: +Por ejemplo: ```js run -// No effect on numbers +// Sin efecto en números let x = 1; alert( +x ); // 1 @@ -142,77 +142,77 @@ let y = -2; alert( +y ); // -2 *!* -// Converts non-numbers +// Convierte los no números alert( +true ); // 1 alert( +"" ); // 0 */!* ``` -It actually does the same thing as `Number(...)`, but is shorter. +Realmente hace lo mismo que `Number(...)`, pero es más corto. -The need to convert strings to numbers arises very often. For example, if we are getting values from HTML form fields, they are usually strings. What if we want to sum them? +La necesidad de convertir cadenas en números surge con mucha frecuencia. Por ejemplo, si estamos obteniendo valores de campos de formulario HTML, generalmente son cadenas. -The binary plus would add them as strings: +El operador binario suma los agregaría como cadenas: ```js run let apples = "2"; let oranges = "3"; -alert( apples + oranges ); // "23", the binary plus concatenates strings +alert( apples + oranges ); // "23", el binario suma concatena las cadenas ``` -If we want to treat them as numbers, we need to convert and then sum them: +Si queremos tratarlos como números, necesitamos convertirlos y luego sumarlos: ```js run let apples = "2"; let oranges = "3"; *!* -// both values converted to numbers before the binary plus +// ambos valores convertidos a números antes del operador binario suma alert( +apples + +oranges ); // 5 */!* -// the longer variant +// la variante más larga // alert( Number(apples) + Number(oranges) ); // 5 ``` -From a mathematician's standpoint, the abundance of pluses may seem strange. But from a programmer's standpoint, there's nothing special: unary pluses are applied first, they convert strings to numbers, and then the binary plus sums them up. +Desde el punto de vista de un matemático, la abundancia de signos más puede parecer extraña. Pero desde el punto de vista de un programador no hay nada especial: primero se aplican los signos más unarios que convierten las cadenas en números, y luego el signo más binario los suma. -Why are unary pluses applied to values before the binary ones? As we're going to see, that's because of their *higher precedence*. +¿Por qué se aplican los signos más unarios a los valores antes que los binarios? Como veremos, eso se debe a su *mayor precedencia*. -## Operator precedence +## Precedencia del operador -If an expression has more than one operator, the execution order is defined by their *precedence*, or, in other words, the default priority order of operators. +Si una expresión tiene más de un operador, el orden de ejecución se define por su *precedencia* o, en otras palabras, el orden de prioridad predeterminado de los operadores. -From school, we all know that the multiplication in the expression `1 + 2 * 2` should be calculated before the addition. That's exactly the precedence thing. The multiplication is said to have *a higher precedence* than the addition. +Desde la escuela, todos sabemos que la multiplicación en la expresión `1 + 2 * 2` debe calcularse antes de la suma. Eso es exactamente la precedencia. Se dice que la multiplicación tiene *una mayor precedencia* que la suma. -Parentheses override any precedence, so if we're not satisfied with the default order, we can use them to change it. For example, write `(1 + 2) * 2`. +Los paréntesis anulan cualquier precedencia, por lo que si no estamos satisfechos con el orden predeterminado, podemos usarlos para cambiarlo. Por ejemplo, escriba `(1 + 2) * 2`. -There are many operators in JavaScript. Every operator has a corresponding precedence number. The one with the larger number executes first. If the precedence is the same, the execution order is from left to right. +Hay muchos operadores en JavaScript. Cada operador tiene un número de precedencia correspondiente. El que tiene el número más grande se ejecuta primero. Si la precedencia es la misma, el orden de ejecución es de izquierda a derecha. -Here's an extract from the [precedence table](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence) (you don't need to remember this, but note that unary operators are higher than corresponding binary ones): +Aquí hay un extracto de la [tabla de precedencia](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Operadores/Operator_Precedence) (no necesita recordar esto, pero tenga en cuenta que los operadores unarios son más altos que el operador binario correspondiente): -| Precedence | Name | Sign | +| Precedencia| Nombre | Signo | |------------|------|------| | ... | ... | ... | -| 14 | unary plus | `+` | -| 14 | unary negation | `-` | -| 13 | exponentiation | `**` | -| 12 | multiplication | `*` | -| 12 | division | `/` | -| 11 | addition | `+` | -| 11 | subtraction | `-` | +| 14 | suma unaria | `+` | +| 14 | negación unaria | `-` | +| 13 | exponenciación | `**` | +| 12 | multiplicación | `*` | +| 12 | división | `/` | +| 11 | suma | `+` | +| 11 | resta | `-` | | ... | ... | ... | -| 2 | assignment | `=` | +| 2 | asignación | `=` | | ... | ... | ... | -As we can see, the "unary plus" has a priority of `14` which is higher than the `11` of "addition" (binary plus). That's why, in the expression `"+apples + +oranges"`, unary pluses work before the addition. +Como podemos ver, la "suma unaria" tiene una prioridad de `14`, que es mayor que el `11` de "suma" (suma binaria). Es por eso que, en la expresión `"+apples + +oranges"`, las sumas unarias se hacen antes de la adición. -## Assignment +## Asignación -Let's note that an assignment `=` is also an operator. It is listed in the precedence table with the very low priority of `2`. +Tengamos en cuenta que una asignación `=` también es un operador. Está listado en la tabla de precedencia con la prioridad muy baja de `2`. -That's why, when we assign a variable, like `x = 2 * 2 + 1`, the calculations are done first and then the `=` is evaluated, storing the result in `x`. +Es por eso que, cuando asignamos una variable, como `x = 2 * 2 + 1`, los cálculos se realizan primero y luego se evalúa el `=`, almacenando el resultado en `x`. ```js let x = 2 * 2 + 1; @@ -220,15 +220,15 @@ let x = 2 * 2 + 1; alert( x ); // 5 ``` -### Assignment = returns a value +### Asignación = devuelve un valor -The fact of `=` being an operator, not a "magical" language construct has an interesting implication. +El hecho de que `=` sea un operador, no una construcción "mágica" del lenguaje, tiene un implicación interesante. -All operators in JavaScript return a value. That's obvious for `+` and `-`, but also true for `=`. +Todos los operadores en JavaScript devuelven un valor. Esto es obvio para `+` y `-`, pero también es cierto para `=`. -The call `x = value` writes the `value` into `x` *and then returns it*. +La llamada `x = value` escribe el `value` en `x` *y luego lo devuelve*. -Here's a demo that uses an assignment as part of a more complex expression: +Aquí hay una demostración que usa una asignación como parte de una expresión más compleja: ```js run let a = 1; @@ -242,15 +242,15 @@ alert( a ); // 3 alert( c ); // 0 ``` -In the example above, the result of expression `(a = b + 1)` is the value which was assigned to `a` (that is `3`). It is then used for further evaluations. +En el ejemplo anterior, el resultado de la expresión `(a = b + 1)` es el valor asignado a `a` (es decir, `3`). Luego se usa para evaluaciones adicionales. -Funny code, isn't it? We should understand how it works, because sometimes we see it in JavaScript libraries. +Código gracioso, ¿no? Deberíamos entender cómo funciona, porque a veces lo vemos en las bibliotecas de JavaScript. -Although, please don't write the code like that. Such tricks definitely don't make code clearer or readable. +Pero no deberíamos escribir algo así. Tales trucos definitivamente no hacen que el código sea más claro o legible. -### Chaining assignments +### Asignaciones encadenadas -Another interesting feature is the ability to chain assignments: +Otra característica interesante es la habilidad para encadenar asignaciones: ```js run let a, b, c; @@ -264,22 +264,22 @@ alert( b ); // 4 alert( c ); // 4 ``` -Chained assignments evaluate from right to left. First, the rightmost expression `2 + 2` is evaluated and then assigned to the variables on the left: `c`, `b` and `a`. At the end, all the variables share a single value. +Las asignaciones encadenadas evalúan de derecha a izquierda. Primero, se evalúa la expresión más a la derecha `2 + 2` y luego se asigna a las variables de la izquierda: `c`, `b` y `a`. Al final, todas las variables comparten un solo valor. -Once again, for the purposes of readability it's better to split such code into a few lines: +Una vez más, por motivos de legibilidad, es mejor separar este código en varias líneas: ```js c = 2 + 2; b = c; a = c; ``` -That's easier to read, especially when eye-scanning the code fast. +Es más fácil de leer, especialmente cuando se hace de un vistazo. -## Modify-in-place +## Modificar en el lugar -We often need to apply an operator to a variable and store the new result in that same variable. +A menudo necesitamos aplicar un operador a una variable y guardar el nuevo resultado en esa misma variable. -For example: +Por ejemplo: ```js let n = 2; @@ -287,67 +287,67 @@ n = n + 5; n = n * 2; ``` -This notation can be shortened using the operators `+=` and `*=`: +Esta notación puede ser acortada utilizando los operadores `+=` y `*=`: ```js run let n = 2; -n += 5; // now n = 7 (same as n = n + 5) -n *= 2; // now n = 14 (same as n = n * 2) +n += 5; // ahora n = 7 (es lo mismo que n = n + 5) +n *= 2; // ahora n = 14 (es lo mismo que n = n * 2) alert( n ); // 14 ``` -Short "modify-and-assign" operators exist for all arithmetical and bitwise operators: `/=`, `-=`, etc. +Los operadores cortos "modifica y asigna" existen para todos los operadores aritméticos y de nivel bit: `/=`, `-=`, etcétera. -Such operators have the same precedence as a normal assignment, so they run after most other calculations: +Tales operadores tienen la misma precedencia que la asignación normal, por lo tanto se ejecutan después de otros cálculos: ```js run let n = 2; -n *= 3 + 5; // right part evaluated first, same as n *= 8 +n *= 3 + 5; // el lado derecho es evaluado primero, es lo mismo que n *= 8 alert( n ); // 16 ``` -## Increment/decrement +## Incremento/decremento - + -Increasing or decreasing a number by one is among the most common numerical operations. +Aumentar o disminuir un número en uno es una de las operaciones numéricas más comunes. -So, there are special operators for it: +Entonces, hay operadores especiales para ello: -- **Increment** `++` increases a variable by 1: +- **Incremento** `++` incrementa una variable por 1: ```js run no-beautify let counter = 2; - counter++; // works the same as counter = counter + 1, but is shorter + counter++; // funciona igual que counter = counter + 1, pero es más corto alert( counter ); // 3 ``` -- **Decrement** `--` decreases a variable by 1: +- **Decremento** `--` decrementa una variable por 1: ```js run no-beautify let counter = 2; - counter--; // works the same as counter = counter - 1, but is shorter + counter--; // funciona igual que counter = counter - 1, pero es más corto alert( counter ); // 1 ``` ```warn -Increment/decrement can only be applied to variables. Trying to use it on a value like `5++` will give an error. +Incremento/decremento sólo puede ser aplicado a variables. Intentar utilizarlo en un valor como `5++` dará un error. ``` -The operators `++` and `--` can be placed either before or after a variable. +Los operadores `++` y `--` pueden ser colocados antes o después de una variable. -- When the operator goes after the variable, it is in "postfix form": `counter++`. -- The "prefix form" is when the operator goes before the variable: `++counter`. +- Cuando el operador va después de la variable, está en "forma de sufijo": `counter++`. +- La "forma de prefijo" es cuando el operador va antes de la variable: `++counter`. -Both of these statements do the same thing: increase `counter` by `1`. +Ambas sentencias hacen la misma cosa: aumentar `counter` por `1`. -Is there any difference? Yes, but we can only see it if we use the returned value of `++/--`. +¿Existe alguna diferencia? Sí, pero solamente la podemos ver si utilizamos el valor devuelto de `++/--`. -Let's clarify. As we know, all operators return a value. Increment/decrement is no exception. The prefix form returns the new value while the postfix form returns the old value (prior to increment/decrement). +Aclaremos. Tal como conocemos, todos los operadores devuelven un valor. Incremento/decremento no es una excepción. La forma prefijo devuelve el nuevo valor mientras que la forma sufijo devuelve el valor anterior (antes del incremento/decremento). -To see the difference, here's an example: +Para ver la diferencia, aquí hay un ejemplo: ```js run let counter = 1; @@ -356,64 +356,64 @@ let a = ++counter; // (*) alert(a); // *!*2*/!* ``` -In the line `(*)`, the *prefix* form `++counter` increments `counter` and returns the new value, `2`. So, the `alert` shows `2`. +En la línea `(*)`, la forma *prefijo* `++counter` incrementa `counter` y devuelve el nuevo valor, `2`. Por lo tanto, el `alert` muestra `2`. -Now, let's use the postfix form: +Ahora usemos la forma sufijo: ```js run let counter = 1; -let a = counter++; // (*) changed ++counter to counter++ +let a = counter++; // (*) cambiado ++counter a counter++ alert(a); // *!*1*/!* ``` -In the line `(*)`, the *postfix* form `counter++` also increments `counter` but returns the *old* value (prior to increment). So, the `alert` shows `1`. +En la línea `(*)`, la forma *sufijo* `counter++` también incrementa `counter`, pero devuelve el *antiguo* valor (antes de incrementar). Por lo tanto, el `alert` muestra `1`. -To summarize: +Para resumir: -- If the result of increment/decrement is not used, there is no difference in which form to use: +- Si no se usa el resultado del incremento/decremento, no hay diferencia en la forma de usar: ```js run let counter = 0; counter++; ++counter; - alert( counter ); // 2, the lines above did the same + alert( counter ); // 2, las líneas de arriba realizan lo mismo ``` -- If we'd like to increase a value *and* immediately use the result of the operator, we need the prefix form: +- Si queremos aumentar un valor *y* usar inmediatamente el resultado del operador, necesitamos la forma de prefijo: ```js run let counter = 0; alert( ++counter ); // 1 ``` -- If we'd like to increment a value but use its previous value, we need the postfix form: +- Si queremos incrementar un valor, pero usamos su valor anterior, necesitamos la forma sufijo: ```js run let counter = 0; alert( counter++ ); // 0 ``` -````smart header="Increment/decrement among other operators" -The operators `++/--` can be used inside expressions as well. Their precedence is higher than most other arithmetical operations. +````smart header="Incremento/decremento entre otros operadores" +Los operadores `++/--` también pueden ser usados dentro de expresiones. Su precedencia es más alta que la mayoría de los otros operadores aritméticos. -For instance: +Por ejemplo: ```js run let counter = 1; alert( 2 * ++counter ); // 4 ``` -Compare with: +Compara con: ```js run let counter = 1; -alert( 2 * counter++ ); // 2, because counter++ returns the "old" value +alert( 2 * counter++ ); // 2, porque counter++ devuelve el valor "antiguo" ``` -Though technically okay, such notation usually makes code less readable. One line does multiple things -- not good. +Aunque técnicamente está bien, tal notación generalmente hace que el código sea menos legible. Una línea hace varias cosas, no es bueno. -While reading code, a fast "vertical" eye-scan can easily miss something like `counter++` and it won't be obvious that the variable increased. +Mientras lee el código, un rápido escaneo ocular "vertical" puede pasar por alto fácilmente algo como 'counter++' y no será obvio que la variable aumentó. -We advise a style of "one line -- one action": +Aconsejamos un estilo de "una línea - una acción": ```js run let counter = 1; @@ -422,13 +422,13 @@ counter++; ``` ```` -## Bitwise operators +## Operadores a nivel de bit -Bitwise operators treat arguments as 32-bit integer numbers and work on the level of their binary representation. +Los operadores a nivel bit tratan los argumentos como números enteros de 32 bits y trabajan en el nivel de su representación binaria. -These operators are not JavaScript-specific. They are supported in most programming languages. +Estos operadores no son específicos de JavaScript. Son compatibles con la mayoría de los lenguajes de programación. -The list of operators: +La lista de operadores: - AND ( `&` ) - OR ( `|` ) @@ -438,43 +438,43 @@ The list of operators: - RIGHT SHIFT ( `>>` ) - ZERO-FILL RIGHT SHIFT ( `>>>` ) -These operators are used very rarely, when we need to fiddle with numbers on the very lowest (bitwise) level. We won't need these operators any time soon, as web development has little use of them, but in some special areas, such as cryptography, they are useful. You can read the [Bitwise Operators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#bitwise_operators) chapter on MDN when a need arises. +Estos operadores se usan muy raramente, cuando necesitamos manejar la representación de números en su más bajo nivel. No tenemos en vista usarlos pronto pues en el desarrollo web tiene poco uso; pero en ciertas áreas especiales, como la criptografía, son útiles. Puedes leer el artículo [Operadores a nivel de bit](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Operadores/Bitwise_Operators#Bitwise_AND) en MDN cuando surja la necesidad. -## Comma +## Coma -The comma operator `,` is one of the rarest and most unusual operators. Sometimes, it's used to write shorter code, so we need to know it in order to understand what's going on. +El operador coma `,` es uno de los operadores más raros e inusuales. A veces se utiliza para escribir código más corto, por lo que debemos conocerlo para poder entender lo que está sucediendo. -The comma operator allows us to evaluate several expressions, dividing them with a comma `,`. Each of them is evaluated but only the result of the last one is returned. +El operador coma nos permite evaluar varias expresiones, separándolas con una coma `,`. Cada expresión se evalúa, pero sólo se devuelve el resultado de la última. -For example: +Por ejemplo: ```js run *!* let a = (1 + 2, 3 + 4); */!* -alert( a ); // 7 (the result of 3 + 4) +alert( a ); // 7 (el resultado de 3 + 4) ``` -Here, the first expression `1 + 2` is evaluated and its result is thrown away. Then, `3 + 4` is evaluated and returned as the result. +Aquí, se evalúa la primera expresión `1 + 2` y se desecha su resultado. Luego, se evalúa `3 + 4` y se devuelve como resultado. -```smart header="Comma has a very low precedence" -Please note that the comma operator has very low precedence, lower than `=`, so parentheses are important in the example above. +```smart header="Coma tiene muy baja precedencia" +Tenga en cuenta que el operador coma tiene una precedencia muy baja, inferior a `=`, por lo que los paréntesis son importantes en el ejemplo anterior. -Without them: `a = 1 + 2, 3 + 4` evaluates `+` first, summing the numbers into `a = 3, 7`, then the assignment operator `=` assigns `a = 3`, and the rest is ignored. It's like `(a = 1 + 2), 3 + 4`. +Sin ellos: `a = 1 + 2, 3 + 4` se evalúa primero el `+`, sumando los números a `a = 3, 7`, luego el operador de asignación `=` asigna `a = 3`, y el resto es ignorado. Es igual que `(a = 1 + 2), 3 + 4`. ``` -Why do we need an operator that throws away everything except the last expression? +¿Por qué necesitamos un operador que deseche todo excepto la última expresión? -Sometimes, people use it in more complex constructs to put several actions in one line. +A veces se utilizan en construcciones más complejas para ejecutar varias acciones en una línea. -For example: +Por ejemplo: ```js -// three operations in one line +// tres operaciones en una línea for (*!*a = 1, b = 3, c = a * b*/!*; a < 10; a++) { ... } ``` -Such tricks are used in many JavaScript frameworks. That's why we're mentioning them. But usually they don't improve code readability so we should think well before using them. +Tales trucos se usan en muchos frameworks de JavaScript, por eso los mencionamos. Sin embargo, generalmente no mejoran la legibilidad del código, por lo que debemos pensar bien antes de usarlos. diff --git a/1-js/02-first-steps/09-comparison/1-comparison-questions/solution.md b/1-js/02-first-steps/09-comparison/1-comparison-questions/solution.md index 632b1cf4e..6bcaf0c60 100644 --- a/1-js/02-first-steps/09-comparison/1-comparison-questions/solution.md +++ b/1-js/02-first-steps/09-comparison/1-comparison-questions/solution.md @@ -10,12 +10,12 @@ null == "\n0\n" → false null === +"\n0\n" → false ``` -Some of the reasons: +Algunas de las razones: -1. Obviously, true. -2. Dictionary comparison, hence false. `"a"` is smaller than `"p"`. -3. Again, dictionary comparison, first char `"2"` is greater than the first char `"1"`. -4. Values `null` and `undefined` equal each other only. -5. Strict equality is strict. Different types from both sides lead to false. -6. Similar to `(4)`, `null` only equals `undefined`. -7. Strict equality of different types. +1. Obviamente, true. +2. Comparación lexicográfica, por lo tanto false. `"a"` es menor que `"p"`. +3. Una vez más, la comparación lexicográfica, el primer carácter de `"2"` es mayor que el primer carácter de `"1"`. +4. Los valores `null` y `undefined` son iguales entre sí solamente. +5. La igualdad estricta es estricta. Diferentes tipos de ambos lados conducen a false. +6. Similar a (4), `null` solamente es igual a `undefined`. +7. Igualdad estricta de diferentes tipos. diff --git a/1-js/02-first-steps/09-comparison/1-comparison-questions/task.md b/1-js/02-first-steps/09-comparison/1-comparison-questions/task.md index be7f75ddd..e87240146 100644 --- a/1-js/02-first-steps/09-comparison/1-comparison-questions/task.md +++ b/1-js/02-first-steps/09-comparison/1-comparison-questions/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# Comparisons +# Comparaciones -What will be the result for these expressions? +¿Cuál será el resultado de las siguientes expresiones? ```js no-beautify 5 > 4 @@ -15,4 +15,3 @@ undefined === null null == "\n0\n" null === +"\n0\n" ``` - diff --git a/1-js/02-first-steps/09-comparison/article.md b/1-js/02-first-steps/09-comparison/article.md index a24af5688..a4834321c 100644 --- a/1-js/02-first-steps/09-comparison/article.md +++ b/1-js/02-first-steps/09-comparison/article.md @@ -1,47 +1,47 @@ -# Comparisons +# Comparaciones -We know many comparison operators from maths. +Conocemos muchos operadores de comparación de las matemáticas: -In JavaScript they are written like this: +En Javascript se escriben así: -- Greater/less than: a > b, a < b. -- Greater/less than or equals: a >= b, a <= b. -- Equals: `a == b`, please note the double equality sign `==` means the equality test, while a single one `a = b` means an assignment. -- Not equals: In maths the notation is , but in JavaScript it's written as a != b. +- Mayor/menor que: a > b, a < b. +- Mayor/menor o igual que: a >= b, a <= b. +- Igual: `a == b` (ten en cuenta que el doble signo `==` significa comparación, mientras que un solo símbolo `a = b` significaría una asignación). +- Distinto. En matemáticas la notación es , pero en JavaScript se escribe como una asignación con un signo de exclamación delante: a != b. -In this article we'll learn more about different types of comparisons, how JavaScript makes them, including important peculiarities. +En este artículo, aprenderemos más sobre los diferentes tipos de comparaciones y de cómo las realiza JavaScript, incluidas las peculiaridades importantes. -At the end you'll find a good recipe to avoid "JavaScript quirks"-related issues. +Al final, encontrará una buena receta para evitar problemas relacionadas con las "peculiaridades" de JavaScript. -## Boolean is the result +## Booleano es el resultado -All comparison operators return a boolean value: +Como todos los demás operadores, una comparación retorna un valor. En este caso, el valor es un booleano. -- `true` -- means "yes", "correct" or "the truth". -- `false` -- means "no", "wrong" or "not the truth". +- `true` -- significa "sí", "correcto" o "verdad". +- `false` -- significa "no", "equivocado" o " no verdad". -For example: +Por ejemplo: ```js run -alert( 2 > 1 ); // true (correct) -alert( 2 == 1 ); // false (wrong) -alert( 2 != 1 ); // true (correct) +alert( 2 > 1 ); // true (correcto) +alert( 2 == 1 ); // false (incorrecto) +alert( 2 != 1 ); // true (correcto) ``` -A comparison result can be assigned to a variable, just like any value: +El resultado de una comparación puede asignarse a una variable, igual que cualquier valor: ```js run -let result = 5 > 4; // assign the result of the comparison +let result = 5 > 4; // asignar el resultado de la comparación alert( result ); // true ``` -## String comparison +## Comparación de cadenas -To see whether a string is greater than another, JavaScript uses the so-called "dictionary" or "lexicographical" order. +Para ver si una cadena es "mayor" que otra, JavaScript utiliza el llamado orden "de diccionario" o "lexicográfico". -In other words, strings are compared letter-by-letter. +En otras palabras, las cadenas se comparan letra por letra. -For example: +Por ejemplo: ```js run alert( 'Z' > 'A' ); // true @@ -49,55 +49,55 @@ alert( 'Glow' > 'Glee' ); // true alert( 'Bee' > 'Be' ); // true ``` -The algorithm to compare two strings is simple: +El algoritmo para comparar dos cadenas es simple: -1. Compare the first character of both strings. -2. If the first character from the first string is greater (or less) than the other string's, then the first string is greater (or less) than the second. We're done. -3. Otherwise, if both strings' first characters are the same, compare the second characters the same way. -4. Repeat until the end of either string. -5. If both strings end at the same length, then they are equal. Otherwise, the longer string is greater. +1. Compare el primer carácter de ambas cadenas. +2. Si el primer carácter de la primera cadena es mayor (o menor) que el de la otra cadena, entonces la primera cadena es mayor (o menor) que la segunda. Hemos terminado. +3. De lo contrario, si los primeros caracteres de ambas cadenas son los mismos, compare los segundos caracteres de la misma manera. +4. Repita hasta el final de cada cadena. +5. Si ambas cadenas tienen la misma longitud, entonces son iguales. De lo contrario, la cadena más larga es mayor. -In the first example above, the comparison `'Z' > 'A'` gets to a result at the first step. +En los ejemplos anteriores, la comparación `'Z' > 'A'` llega a un resultado en el primer paso. -The second comparison `'Glow'` and `'Glee'` needs more steps as strings are compared character-by-character: +La segunda comparación `"Glow"` y `"Glee"` necesitan más pasos, se comparan carácter por carácter: -1. `G` is the same as `G`. -2. `l` is the same as `l`. -3. `o` is greater than `e`. Stop here. The first string is greater. +1. `G` es igual que `G`. +2. `l` es igual que `l`. +3. `o` es mayor que `e`. Detente aquí. La primera cadena es mayor. -```smart header="Not a real dictionary, but Unicode order" -The comparison algorithm given above is roughly equivalent to the one used in dictionaries or phone books, but it's not exactly the same. +```smart header="No es un diccionario real, sino un orden Unicode" +El algoritmo de comparación dado arriba es aproximadamente equivalente al utilizado en los diccionarios o guías telefónicas, pero no es exactamente el mismo. -For instance, case matters. A capital letter `"A"` is not equal to the lowercase `"a"`. Which one is greater? The lowercase `"a"`. Why? Because the lowercase character has a greater index in the internal encoding table JavaScript uses (Unicode). We'll get back to specific details and consequences of this in the chapter . +Por ejemplo, las mayúsculas importan. Una letra mayúscula `"A"` no es igual a la minúscula `"a"`. ¿Cuál es mayor? La `"a"` minúscula. ¿Por qué? Porque el carácter en minúsculas tiene un mayor índice en la tabla de codificación interna que utiliza JavaScript (Unicode). Volveremos a los detalles específicos y las consecuencias de esto en el capítulo . ``` -## Comparison of different types +## Comparación de diferentes tipos -When comparing values of different types, JavaScript converts the values to numbers. +Al comparar valores de diferentes tipos, JavaScript convierte los valores a números. -For example: +Por ejemplo: ```js run -alert( '2' > 1 ); // true, string '2' becomes a number 2 -alert( '01' == 1 ); // true, string '01' becomes a number 1 +alert( '2' > 1 ); // true, la cadena '2' se convierte en el número 2 +alert( '01' == 1 ); // true, la cadena '01' se convierte en el número 1 ``` -For boolean values, `true` becomes `1` and `false` becomes `0`. +Para valores booleanos, `true` se convierte en `1` y `false` en `0`. -For example: +Por ejemplo: ```js run alert( true == 1 ); // true alert( false == 0 ); // true ``` -````smart header="A funny consequence" -It is possible that at the same time: +````smart header="Una consecuencia graciosa" +Es posible que al mismo tiempo: -- Two values are equal. -- One of them is `true` as a boolean and the other one is `false` as a boolean. +- Dos valores sean iguales. +- Uno de ellos sea `true` como booleano y el otro sea `false` como booleano. -For example: +Por ejemplo: ```js run let a = 0; @@ -106,86 +106,89 @@ alert( Boolean(a) ); // false let b = "0"; alert( Boolean(b) ); // true -alert(a == b); // true! +alert( a == b ); // true! ``` -From JavaScript's standpoint, this result is quite normal. An equality check converts values using the numeric conversion (hence `"0"` becomes `0`), while the explicit `Boolean` conversion uses another set of rules. +Desde el punto de vista de JavaScript, este resultado es bastante normal. Una comparación de igualdad convierte valores utilizando la conversión numérica (de ahí que `"0"` se convierta en `0`), mientras que la conversión explícita `Boolean` utiliza otro conjunto de reglas. ```` -## Strict equality +## Igualdad estricta -A regular equality check `==` has a problem. It cannot differentiate `0` from `false`: +Una comparación regular de igualdad `==` tiene un problema. No puede diferenciar `0` de `falso': ```js run alert( 0 == false ); // true ``` -The same thing happens with an empty string: +Lo mismo sucede con una cadena vacía: ```js run alert( '' == false ); // true ``` -This happens because operands of different types are converted to numbers by the equality operator `==`. An empty string, just like `false`, becomes a zero. +Esto sucede porque los operandos de diferentes tipos son convertidos a números por el operador de igualdad `==`. Una cadena vacía, al igual que `false`, se convierte en un cero. -What to do if we'd like to differentiate `0` from `false`? +¿Qué hacer si queremos diferenciar `0` de `false`? -**A strict equality operator `===` checks the equality without type conversion.** +**Un operador de igualdad estricto `===` comprueba la igualdad sin conversión de tipo.** -In other words, if `a` and `b` are of different types, then `a === b` immediately returns `false` without an attempt to convert them. +En otras palabras, si `a` y `b` son de diferentes tipos, entonces `a === b` retorna inmediatamente `false` sin intentar convertirlos. -Let's try it: +Intentémoslo: ```js run -alert( 0 === false ); // false, because the types are different +alert( 0 === false ); // falso, porque los tipos son diferentes ``` -There is also a "strict non-equality" operator `!==` analogous to `!=`. +Existe también un operador de "diferencia estricta" `!==` análogo a `!=`. -The strict equality operator is a bit longer to write, but makes it obvious what's going on and leaves less room for errors. +El operador de igualdad estricta es un poco más largo de escribir, pero hace obvio lo que está pasando y deja menos espacio a errores. -## Comparison with null and undefined +## Comparación con nulos e indefinidos -There's a non-intuitive behavior when `null` or `undefined` are compared to other values. +Veamos más casos extremos. -For a strict equality check `===` -: These values are different, because each of them is a different type. +Hay un comportamiento no intuitivo cuando se compara `null` o `undefined` con otros valores. + + +Para un control de igualdad estricto `===` +: Estos valores son diferentes, porque cada uno de ellos es de un tipo diferente. ```js run alert( null === undefined ); // false ``` -For a non-strict check `==` -: There's a special rule. These two are a "sweet couple": they equal each other (in the sense of `==`), but not any other value. +Para una comparación no estricta `==` +: Hay una regla especial. Estos dos son una " pareja dulce ": son iguales entre sí (en el sentido de `==`), pero no a ningún otro valor. ```js run alert( null == undefined ); // true ``` -For maths and other comparisons `< > <= >=` -: `null/undefined` are converted to numbers: `null` becomes `0`, while `undefined` becomes `NaN`. +Para matemáticas y otras comparaciones `< > <= >=` +: `null/undefined` se convierten en números: `null` se convierte en `0`, mientras que `undefined` se convierte en `NaN`. -Now let's see some funny things that happen when we apply these rules. And, what's more important, how to not fall into a trap with them. +Ahora veamos algunos hechos graciosos que suceden cuando aplicamos estas reglas. Y, lo que es más importante, cómo no caer en una trampa con ellas. -### Strange result: null vs 0 +### Resultado extraño: null vs 0 -Let's compare `null` with a zero: +Comparemos `null` con un cero: ```js run -alert( null > 0 ); // (1) false -alert( null == 0 ); // (2) false +alert( null > 0 ); /// (1) false +alert( null == 0 ); /// (2) false alert( null >= 0 ); // (3) *!*true*/!* ``` -Mathematically, that's strange. The last result states that "`null` is greater than or equal to zero", so in one of the comparisons above it must be `true`, but they are both false. +Matemáticamente, eso es extraño. El último resultado afirma que "`null` es mayor o igual a cero", así que en una de las comparaciones anteriores debe ser `true`, pero ambas son falsas. -The reason is that an equality check `==` and comparisons `> < >= <=` work differently. Comparisons convert `null` to a number, treating it as `0`. That's why (3) `null >= 0` is true and (1) `null > 0` is false. +La razón es que una comparación de igualdad `==` y las comparaciones `> < >= <=` funcionan de manera diferente. Las comparaciones convierten a `null` en un número, tratándolo como `0`. Es por eso que (3) `null >= 0` es verdadero y (1) `null > 0` es falso. -On the other hand, the equality check `==` for `undefined` and `null` is defined such that, without any conversions, they equal each other and don't equal anything else. That's why (2) `null == 0` is false. +Por otro lado, el control de igualdad `==` para `undefined` y `null` se define de tal manera que, sin ninguna conversión, son iguales entre sí y no son iguales a nada más. Es por eso que (2) `null == 0` es falso. -### An incomparable undefined +### Un indefinido incomparable -The value `undefined` shouldn't be compared to other values: +El valor `undefined` no debe compararse con otros valores: ```js run alert( undefined > 0 ); // false (1) @@ -193,24 +196,26 @@ alert( undefined < 0 ); // false (2) alert( undefined == 0 ); // false (3) ``` -Why does it dislike zero so much? Always false! +¿Por qué le desagrada tanto el cero? ¡Siempre falso! + +Obtenemos estos resultados porque: + +- Las comparaciones `(1)` y `(2)` retornan `falso` porque `no definido` se convierte en `NaN` y `NaN` es un valor numérico especial que retorna `falso` para todas las comparaciones. +- La comparación de igualdad `(3)` retorna `falso` porque `undefined` sólo equivale a `null` y a ningún otro valor. -We get these results because: +### Evitar los problemas -- Comparisons `(1)` and `(2)` return `false` because `undefined` gets converted to `NaN` and `NaN` is a special numeric value which returns `false` for all comparisons. -- The equality check `(3)` returns `false` because `undefined` only equals `null`, `undefined`, and no other value. +¿Por qué repasamos estos ejemplos? ¿Deberíamos recordar estas peculiaridades todo el tiempo? Bueno, en realidad no. De hecho, estas peculiaridades se volverán familiares con el tiempo, pero hay una manera sólida de evadir los problemas con ellas: -### Avoid problems +- Trata cualquier comparación con `undefined/null` (excepto la igualdad estricta `===`) con sumo cuidado. -Why did we go over these examples? Should we remember these peculiarities all the time? Well, not really. Actually, these tricky things will gradually become familiar over time, but there's a solid way to avoid problems with them: +- No uses comparaciones `>= > < <=` con una variable que puede ser `null/undefined`, a menos que estés realmente seguro de lo que estás haciendo. Si una variable puede tener estos valores, verifícalos por separado. -- Treat any comparison with `undefined/null` except the strict equality `===` with exceptional care. -- Don't use comparisons `>= > < <=` with a variable which may be `null/undefined`, unless you're really sure of what you're doing. If a variable can have these values, check for them separately. -## Summary +## Resumen -- Comparison operators return a boolean value. -- Strings are compared letter-by-letter in the "dictionary" order. -- When values of different types are compared, they get converted to numbers (with the exclusion of a strict equality check). -- The values `null` and `undefined` are equal `==` to themselves and each other, but do not equal any other value. -- Be careful when using comparisons like `>` or `<` with variables that can occasionally be `null/undefined`. Checking for `null/undefined` separately is a good idea. +- Los operadores de comparación devuelven un valor booleano. +- Las cadenas se comparan letra por letra, siguiendo el orden del diccionario. +- Cuando se comparan valores de distintos tipos, se convierten a números (excepto en la comprobación de igualdad estricta `===`). +- Los valores `null` y `undefined` son iguales `==` a sí mismos y entre sí, pero no equivalen a ningún otro valor. +- Ten cuidado al usar comparaciones como `>` o `<` con variables que ocasionalmente pueden ser `null` o `undefined`. Es recomendable revisar esos casos por separado. diff --git a/1-js/02-first-steps/10-ifelse/1-if-zero-string/solution.md b/1-js/02-first-steps/10-ifelse/1-if-zero-string/solution.md index 51f1d4680..abcc595ed 100644 --- a/1-js/02-first-steps/10-ifelse/1-if-zero-string/solution.md +++ b/1-js/02-first-steps/10-ifelse/1-if-zero-string/solution.md @@ -1,12 +1,11 @@ -**Yes, it will.** +**Sí lo hará.** -Any string except an empty one (and `"0"` is not empty) becomes `true` in the logical context. +Cualquier string excepto uno vacío (y `"0"` que no es vacío) se convierte en `true` en un contexto lógico. -We can run and check: +Podemos ejecutar y verificar: ```js run if ("0") { - alert( 'Hello' ); + alert( 'Hola' ); } ``` - diff --git a/1-js/02-first-steps/10-ifelse/1-if-zero-string/task.md b/1-js/02-first-steps/10-ifelse/1-if-zero-string/task.md index 5f16cda85..edb7e7fe3 100644 --- a/1-js/02-first-steps/10-ifelse/1-if-zero-string/task.md +++ b/1-js/02-first-steps/10-ifelse/1-if-zero-string/task.md @@ -2,13 +2,12 @@ importance: 5 --- -# if (a string with zero) +# if (un string con cero) -Will `alert` be shown? +Se mostrará el `alert`? ```js if ("0") { alert( 'Hello' ); } ``` - diff --git a/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2.svg b/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2.svg index 47b020aab..a5237759a 100644 --- a/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2.svg +++ b/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2.svg @@ -1 +1 @@ -BeginYou don't know? “ECMAScript”!Right!What's the “official” name of JavaScript?OtherECMAScript \ No newline at end of file +ComienzoNo sabes? “ECMAScript”!Correcto!What's the nombre "oficial" de JavaScript?OtroECMAScript \ No newline at end of file diff --git a/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2/index.html b/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2/index.html index ea9966653..d6e2d3a60 100644 --- a/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2/index.html +++ b/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2/index.html @@ -5,12 +5,12 @@ diff --git a/1-js/02-first-steps/10-ifelse/2-check-standard/task.md b/1-js/02-first-steps/10-ifelse/2-check-standard/task.md index 4305584fa..abc1f321d 100644 --- a/1-js/02-first-steps/10-ifelse/2-check-standard/task.md +++ b/1-js/02-first-steps/10-ifelse/2-check-standard/task.md @@ -2,11 +2,11 @@ importance: 2 --- -# The name of JavaScript +# El nombre de JavaScript -Using the `if..else` construct, write the code which asks: 'What is the "official" name of JavaScript?' +Usando el constructor `if..else`, escribe el código que pregunta: '¿Cuál es el nombre "oficial" de JavaScript?' -If the visitor enters "ECMAScript", then output "Right!", otherwise -- output: "You don't know? ECMAScript!" +Si el visitante escribe "ECMAScript", entonces muestra: "¡Correcto!", de lo contrario muestra: "¿No lo sabes? ¡ECMAScript!" ![](ifelse_task2.svg) diff --git a/1-js/02-first-steps/10-ifelse/3-sign/if_sign/index.html b/1-js/02-first-steps/10-ifelse/3-sign/if_sign/index.html index f168360dd..5216ec632 100644 --- a/1-js/02-first-steps/10-ifelse/3-sign/if_sign/index.html +++ b/1-js/02-first-steps/10-ifelse/3-sign/if_sign/index.html @@ -6,7 +6,7 @@ - An example for debugging. + Un ejemplo para hacer debugging. - + - + - +
- + diff --git a/1-js/03-code-quality/05-testing-mocha/beforeafter.view/test.js b/1-js/03-code-quality/05-testing-mocha/beforeafter.view/test.js index d3de82546..3276aa192 100644 --- a/1-js/03-code-quality/05-testing-mocha/beforeafter.view/test.js +++ b/1-js/03-code-quality/05-testing-mocha/beforeafter.view/test.js @@ -5,12 +5,11 @@ describe("test", function() { this.timeout(200000); // With this code we increase this - in this case to 200,000 milliseconds // This is because of the "alert" function, because if you delay pressing the "OK" button the tests will not pass! - - before(() => alert("Testing started – before all tests")); - after(() => alert("Testing finished – after all tests")); + before(() => alert("Inicio testing – antes de todos los tests")); + after(() => alert("Final testing – después de todos los tests")); - beforeEach(() => alert("Before a test – enter a test")); - afterEach(() => alert("After a test – exit a test")); + beforeEach(() => alert("Antes de un test – entramos al test")); + afterEach(() => alert("Después de un test – salimos del test")); it('test 1', () => alert(1)); it('test 2', () => alert(2)); diff --git a/1-js/03-code-quality/05-testing-mocha/index.html b/1-js/03-code-quality/05-testing-mocha/index.html index 28a2ea62b..823b95904 100644 --- a/1-js/03-code-quality/05-testing-mocha/index.html +++ b/1-js/03-code-quality/05-testing-mocha/index.html @@ -1,17 +1,17 @@ - + - + - + @@ -20,17 +20,17 @@ - + - +
- + diff --git a/1-js/03-code-quality/05-testing-mocha/pow-1.view/index.html b/1-js/03-code-quality/05-testing-mocha/pow-1.view/index.html index e48a8d3a2..823b95904 100644 --- a/1-js/03-code-quality/05-testing-mocha/pow-1.view/index.html +++ b/1-js/03-code-quality/05-testing-mocha/pow-1.view/index.html @@ -1,18 +1,17 @@ - + - + - + @@ -21,17 +20,17 @@ - + - +
- + diff --git a/1-js/03-code-quality/05-testing-mocha/pow-1.view/test.js b/1-js/03-code-quality/05-testing-mocha/pow-1.view/test.js index 89ba412ed..e78855dc5 100644 --- a/1-js/03-code-quality/05-testing-mocha/pow-1.view/test.js +++ b/1-js/03-code-quality/05-testing-mocha/pow-1.view/test.js @@ -1,6 +1,6 @@ describe("pow", function() { - it("raises to n-th power", function() { + it("eleva a la n-esima potencia", function() { assert.equal(pow(2, 3), 8); }); diff --git a/1-js/03-code-quality/05-testing-mocha/pow-2.view/index.html b/1-js/03-code-quality/05-testing-mocha/pow-2.view/index.html index e8d6be23d..638f3ed06 100644 --- a/1-js/03-code-quality/05-testing-mocha/pow-2.view/index.html +++ b/1-js/03-code-quality/05-testing-mocha/pow-2.view/index.html @@ -1,37 +1,35 @@ - + - + - + - - + - +
- + diff --git a/1-js/03-code-quality/05-testing-mocha/pow-2.view/test.js b/1-js/03-code-quality/05-testing-mocha/pow-2.view/test.js index c803f0e61..186ded1b3 100644 --- a/1-js/03-code-quality/05-testing-mocha/pow-2.view/test.js +++ b/1-js/03-code-quality/05-testing-mocha/pow-2.view/test.js @@ -1,10 +1,10 @@ describe("pow", function() { - it("2 raised to power 3 is 8", function() { + it("2 elevado a la potencia de 3 es 8", function() { assert.equal(pow(2, 3), 8); }); - it("3 raised to power 4 is 81", function() { + it("3 elevado a la potencia de 4 es 81", function() { assert.equal(pow(3, 4), 81); }); diff --git a/1-js/03-code-quality/05-testing-mocha/pow-3.view/index.html b/1-js/03-code-quality/05-testing-mocha/pow-3.view/index.html index c71b0d5d5..e9c23bec7 100644 --- a/1-js/03-code-quality/05-testing-mocha/pow-3.view/index.html +++ b/1-js/03-code-quality/05-testing-mocha/pow-3.view/index.html @@ -1,18 +1,17 @@ - + - + - + @@ -31,13 +30,13 @@ } - + - +
- + diff --git a/1-js/03-code-quality/05-testing-mocha/pow-3.view/test.js b/1-js/03-code-quality/05-testing-mocha/pow-3.view/test.js index 8663952aa..cbeeca542 100644 --- a/1-js/03-code-quality/05-testing-mocha/pow-3.view/test.js +++ b/1-js/03-code-quality/05-testing-mocha/pow-3.view/test.js @@ -2,7 +2,7 @@ describe("pow", function() { function makeTest(x) { let expected = x * x * x; - it(`${x} in the power 3 is ${expected}`, function() { + it(`${x} elevado a 3 es ${expected}`, function() { assert.equal(pow(x, 3), expected); }); } diff --git a/1-js/03-code-quality/05-testing-mocha/pow-4.view/index.html b/1-js/03-code-quality/05-testing-mocha/pow-4.view/index.html index c71b0d5d5..e9c23bec7 100644 --- a/1-js/03-code-quality/05-testing-mocha/pow-4.view/index.html +++ b/1-js/03-code-quality/05-testing-mocha/pow-4.view/index.html @@ -1,18 +1,17 @@ - + - + - + @@ -31,13 +30,13 @@ } - + - +
- + diff --git a/1-js/03-code-quality/05-testing-mocha/pow-4.view/test.js b/1-js/03-code-quality/05-testing-mocha/pow-4.view/test.js index e5ce2ce43..afe60360f 100644 --- a/1-js/03-code-quality/05-testing-mocha/pow-4.view/test.js +++ b/1-js/03-code-quality/05-testing-mocha/pow-4.view/test.js @@ -1,10 +1,10 @@ describe("pow", function() { - describe("raises x to power 3", function() { + describe("eleva x a la potencia de 3", function() { function makeTest(x) { let expected = x * x * x; - it(`${x} in the power 3 is ${expected}`, function() { + it(`${x} elevado a 3 es ${expected}`, function() { assert.equal(pow(x, 3), expected); }); } diff --git a/1-js/03-code-quality/05-testing-mocha/pow-full.view/index.html b/1-js/03-code-quality/05-testing-mocha/pow-full.view/index.html index 076b1e5a9..7dc3a53d6 100644 --- a/1-js/03-code-quality/05-testing-mocha/pow-full.view/index.html +++ b/1-js/03-code-quality/05-testing-mocha/pow-full.view/index.html @@ -1,18 +1,17 @@ - + - + - + @@ -32,13 +31,13 @@ } - + - +
- + diff --git a/1-js/03-code-quality/05-testing-mocha/pow-full.view/test.js b/1-js/03-code-quality/05-testing-mocha/pow-full.view/test.js index 75ff5e99f..f0b399f6d 100644 --- a/1-js/03-code-quality/05-testing-mocha/pow-full.view/test.js +++ b/1-js/03-code-quality/05-testing-mocha/pow-full.view/test.js @@ -1,10 +1,10 @@ describe("pow", function() { - describe("raises x to power 3", function() { + describe("eleva x a la potencia 3", function() { function makeTest(x) { let expected = x * x * x; - it(`${x} in the power 3 is ${expected}`, function() { + it(`${x} elevado a 3 es ${expected}`, function() { assert.equal(pow(x, 3), expected); }); } @@ -15,11 +15,11 @@ describe("pow", function() { }); - it("if n is negative, the result is NaN", function() { + it("si n es negativo, el resultado es NaN", function() { assert.isNaN(pow(2, -1)); }); - it("if n is not integer, the result is NaN", function() { + it("si n no es un entero, el resultado es NaN", function() { assert.isNaN(pow(2, 1.5)); }); diff --git a/1-js/03-code-quality/05-testing-mocha/pow-min.view/index.html b/1-js/03-code-quality/05-testing-mocha/pow-min.view/index.html index d82a79dca..e5ddaf68b 100644 --- a/1-js/03-code-quality/05-testing-mocha/pow-min.view/index.html +++ b/1-js/03-code-quality/05-testing-mocha/pow-min.view/index.html @@ -1,18 +1,17 @@ - + - + - + @@ -21,17 +20,17 @@ - + - +
- + diff --git a/1-js/03-code-quality/05-testing-mocha/pow-min.view/test.js b/1-js/03-code-quality/05-testing-mocha/pow-min.view/test.js index 89ba412ed..172127905 100644 --- a/1-js/03-code-quality/05-testing-mocha/pow-min.view/test.js +++ b/1-js/03-code-quality/05-testing-mocha/pow-min.view/test.js @@ -1,6 +1,6 @@ describe("pow", function() { - it("raises to n-th power", function() { + it("eleva a la n-ésima potencia", function() { assert.equal(pow(2, 3), 8); }); diff --git a/1-js/03-code-quality/05-testing-mocha/pow-nan.view/index.html b/1-js/03-code-quality/05-testing-mocha/pow-nan.view/index.html index 523ae25ec..d0e6a1a0f 100644 --- a/1-js/03-code-quality/05-testing-mocha/pow-nan.view/index.html +++ b/1-js/03-code-quality/05-testing-mocha/pow-nan.view/index.html @@ -1,18 +1,17 @@ - + - + - + @@ -29,13 +28,13 @@ } - + - +
- + diff --git a/1-js/03-code-quality/05-testing-mocha/pow-nan.view/test.js b/1-js/03-code-quality/05-testing-mocha/pow-nan.view/test.js index 75ff5e99f..7c34ce439 100644 --- a/1-js/03-code-quality/05-testing-mocha/pow-nan.view/test.js +++ b/1-js/03-code-quality/05-testing-mocha/pow-nan.view/test.js @@ -1,10 +1,10 @@ describe("pow", function() { - describe("raises x to power 3", function() { + describe("eleva x a 3", function() { function makeTest(x) { let expected = x * x * x; - it(`${x} in the power 3 is ${expected}`, function() { + it(`${x} elevado a 3 es ${expected}`, function() { assert.equal(pow(x, 3), expected); }); } @@ -15,11 +15,11 @@ describe("pow", function() { }); - it("if n is negative, the result is NaN", function() { + it("si n es negativo, el resultado es NaN", function() { assert.isNaN(pow(2, -1)); }); - it("if n is not integer, the result is NaN", function() { + it("si n no es un entero, el resultado es NaN", function() { assert.isNaN(pow(2, 1.5)); }); diff --git a/1-js/03-code-quality/06-polyfills/article.md b/1-js/03-code-quality/06-polyfills/article.md index 5ca123908..18ce69019 100644 --- a/1-js/03-code-quality/06-polyfills/article.md +++ b/1-js/03-code-quality/06-polyfills/article.md @@ -1,89 +1,89 @@ -# Polyfills and transpilers +# Polyfills y transpiladores -The JavaScript language steadily evolves. New proposals to the language appear regularly, they are analyzed and, if considered worthy, are appended to the list at and then progress to the [specification](https://www.ecma-international.org/publications-and-standards/standards/ecma-262/). +El lenguaje JavaScript evoluciona constantemente. Nuevas propuestas al lenguaje aparecen regularmente, son analizadas y, si se consideran valiosas, se agregan a la lista en y luego avanzan a la [especificación](https://www.ecma-international.org/publications/standards/ecma-262.htm). -Teams behind JavaScript engines have their own ideas about what to implement first. They may decide to implement proposals that are in draft and postpone things that are already in the spec, because they are less interesting or just harder to do. +Los equipos de desarrollo detrás de los intérpretes (engines) de JavaScript tienen sus propias ideas sobre qué implementar primero. Pueden decidir implementar propuestas que están en borrador y posponer cosas que ya están en la especificación porque son menos interesantes o simplemente porque son más difíciles de hacer. -So it's quite common for an engine to implement only part of the standard. +Por lo tanto, es bastante común para un intérprete implementar solo parte del estándar. -A good page to see the current state of support for language features is (it's big, we have a lot to study yet). +Una buena página para ver el estado actual de soporte de características del lenguaje es (es grande, todavía tenemos mucho que aprender). -As programmers, we'd like to use most recent features. The more good stuff - the better! +Como programadores, queremos las características más recientes. Cuanto más, ¡mejor! -On the other hand, how to make our modern code work on older engines that don't understand recent features yet? +Por otro lado, ¿cómo hacer que nuestro código moderno funcione en intérpretes más viejos que aún no entienden las características más nuevas? -There are two tools for that: +Hay dos herramientas para ello: -1. Transpilers. +1. Transpiladores 2. Polyfills. -Here, in this chapter, our purpose is to get the gist of how they work, and their place in web development. +En este artículo nuestro propósito es llegar a la esencia de cómo trabajan y su lugar en el desarrollo web. -## Transpilers +## Transpiladores -A [transpiler](https://en.wikipedia.org/wiki/Source-to-source_compiler) is a special piece of software that translates source code to another source code. It can parse ("read and understand") modern code and rewrite it using older syntax constructs, so that it'll also work in outdated engines. +Un [transpilador](https://es.wikipedia.org/wiki/Transpilador) es un software que traduce un código fuente a otro código fuente. Puede analizar ("leer y entender") código moderno y rescribirlo usando sintaxis y construcciones más viejas para que también funcione en intérpretes antiguos. -E.g. JavaScript before year 2020 didn't have the "nullish coalescing operator" `??`. So, if a visitor uses an outdated browser, it may fail to understand the code like `height = height ?? 100`. +Por ejemplo, antes del año 2020 JavaScript no tenía el operador "nullish coalescing" `??`. Entonces, si un visitante lo usa en un navegador desactualizado, este fallaría en entender un código como `height = height ?? 100`. -A transpiler would analyze our code and rewrite `height ?? 100` into `(height !== undefined && height !== null) ? height : 100`. +Un transpilador analizaría nuestro código y rescribiría `height ?? 100` como `(height !== undefined && height !== null) ? height : 100`. ```js -// before running the transpiler +// antes de ejecutar el transpilador height = height ?? 100; -// after running the transpiler +// después de ejecutar el transpilador height = (height !== undefined && height !== null) ? height : 100; ``` -Now the rewritten code is suitable for older JavaScript engines. +Ahora el código rescrito es apto para los intérpretes de JavaScript más viejos. -Usually, a developer runs the transpiler on their own computer, and then deploys the transpiled code to the server. +Usualmente, un desarrollador ejecuta el transpilador en su propia computadora y luego despliega el código transpilado al servidor. -Speaking of names, [Babel](https://babeljs.io) is one of the most prominent transpilers out there. +Acerca de nombres, [Babel](https://babeljs.io) es uno de los más prominentes transpiladores circulando. -Modern project build systems, such as [webpack](https://webpack.js.org/), provide a means to run a transpiler automatically on every code change, so it's very easy to integrate into the development process. +Sistemas de desarrollo de proyectos modernos, tales como [webpack](https://webpack.js.org/), brindan los medios para ejecutar la transpilación automática en cada cambio de código, haciendo muy fácil la integración al proceso de desarrollo. ## Polyfills -New language features may include not only syntax constructs and operators, but also built-in functions. +Nuevas características en el lenguaje pueden incluir no solo construcciones sintácticas y operadores, sino también funciones integradas. -For example, `Math.trunc(n)` is a function that "cuts off" the decimal part of a number, e.g `Math.trunc(1.23)` returns `1`. +Por ejemplo, `Math.trunc(n)` es una función que corta la parte decimal de un número, ej. `Math.trunc(1.23)` devuelve `1`. -In some (very outdated) JavaScript engines, there's no `Math.trunc`, so such code will fail. +En algunos (muy desactualizados) intérpretes JavaScript no existe `Math.trunc`, así que tal código fallará. -As we're talking about new functions, not syntax changes, there's no need to transpile anything here. We just need to declare the missing function. +Aquí estamos hablando de nuevas funciones, no de cambios de sintaxis. No hay necesidad de transpilar nada. Solo necesitamos declarar la función faltante. -A script that updates/adds new functions is called "polyfill". It "fills in" the gap and adds missing implementations. +Un script que actualiza o agrega funciones nuevas es llamado "polyfill". Este llena los vacíos agregando las implementaciones que faltan. -For this particular case, the polyfill for `Math.trunc` is a script that implements it, like this: +En este caso particular, el polyfill para `Math.trunc` es un script que lo implementa: ```js -if (!Math.trunc) { // if no such function - // implement it +if (!Math.trunc) { // no existe tal función + // implementarla Math.trunc = function(number) { - // Math.ceil and Math.floor exist even in ancient JavaScript engines - // they are covered later in the tutorial + // Math.ceil y Math.floor existen incluso en los intérpretes antiguos + // los cubriremos luego en el tutorial return number < 0 ? Math.ceil(number) : Math.floor(number); }; } ``` -JavaScript is a highly dynamic language. Scripts may add/modify any function, even built-in ones. +JavaScript es un lenguaje muy dinámico, los scripts pueden agregar o modificar cualquier función, incluso las integradas. -One interesting polyfill library is [core-js](https://github.com/zloirock/core-js), which supports a wide range of features and allows you to include only the ones you need. +Una librería interesante polyfill es [core js](https://github.com/zloirock/core-js), que brinda una amplia gama de catacterísticas y te permite incluir solo las que necesitas. -## Summary +## Resumen -In this chapter we'd like to motivate you to study modern and even "bleeding-edge" language features, even if they aren't yet well-supported by JavaScript engines. +En este artículo queremos motivarte a estudiar las características más modernas y hasta experimentales del lenguaje, incluso si aún no tienen buen soporte en los intérpretes JavaScript. -Just don't forget to use a transpiler (if using modern syntax or operators) and polyfills (to add functions that may be missing). They'll ensure that the code works. +Pero no olvides usar transpiladores (si usas sintaxis u operadores modernos) y polyfills (para añadir funciones que pueden estar ausentes). Ellos se asegurarán de que el código funcione. -For example, later when you're familiar with JavaScript, you can setup a code build system based on [webpack](https://webpack.js.org/) with the [babel-loader](https://github.com/babel/babel-loader) plugin. +Por ejemplo, cuando estés más familiarizado con JavaScript puedes configurar la construcción de código basado en [webpack](https://webpack.js.org/) con el plugin [babel-loader](https://github.com/babel/babel-loader). -Good resources that show the current state of support for various features: -- - for pure JavaScript. -- - for browser-related functions. +Buenos recursos que muestran el estado actual de soporte para varias característica: +- - para JavaScript puro. +- - para funciones relacionadas al navegador. -P.S. Google Chrome is usually the most up-to-date with language features, try it if a tutorial demo fails. Most tutorial demos work with any modern browser though. +P.S. Google Chrome usualmente es el más actualizado con las características del lenguaje, pruébalo si algún demo del tutorial falla. Aunque la mayoría de los demos funciona con cualquier navegador moderno. diff --git a/1-js/03-code-quality/index.md b/1-js/03-code-quality/index.md index 2ef64fa69..7b9808f85 100644 --- a/1-js/03-code-quality/index.md +++ b/1-js/03-code-quality/index.md @@ -1,3 +1,3 @@ -# Code quality +# Calidad del código -This chapter explains coding practices that we'll use further in the development. +Este capítulo explica las prácticas en programación que más usaremos en el desarrollo. diff --git a/1-js/04-object-basics/01-object/2-hello-object/task.md b/1-js/04-object-basics/01-object/2-hello-object/task.md index 2841a058f..2a76f05a2 100644 --- a/1-js/04-object-basics/01-object/2-hello-object/task.md +++ b/1-js/04-object-basics/01-object/2-hello-object/task.md @@ -2,13 +2,12 @@ importance: 5 --- -# Hello, object +# Hola, objeto -Write the code, one line for each action: - -1. Create an empty object `user`. -2. Add the property `name` with the value `John`. -3. Add the property `surname` with the value `Smith`. -4. Change the value of the `name` to `Pete`. -5. Remove the property `name` from the object. +Escribe el código, una línea para cada acción: +1. Crea un objeto `user` vacío. +2. Agrega la propiedad `name` con el valor `John`. +3. Agrega la propiedad `surname` con el valor `Smith`. +4. Cambia el valor de `name` a `Pete`. +5. Remueve la propiedad `name` del objeto. diff --git a/1-js/04-object-basics/01-object/3-is-empty/_js.view/solution.js b/1-js/04-object-basics/01-object/3-is-empty/_js.view/solution.js index db3283e49..15eadf371 100644 --- a/1-js/04-object-basics/01-object/3-is-empty/_js.view/solution.js +++ b/1-js/04-object-basics/01-object/3-is-empty/_js.view/solution.js @@ -1,6 +1,6 @@ function isEmpty(obj) { for (let key in obj) { - // if the loop has started, there is a property + // Si el bucle ha comenzado quiere decir que sí hay al menos una propiedad return false; } return true; diff --git a/1-js/04-object-basics/01-object/3-is-empty/_js.view/test.js b/1-js/04-object-basics/01-object/3-is-empty/_js.view/test.js index 4db5efabe..399a20533 100644 --- a/1-js/04-object-basics/01-object/3-is-empty/_js.view/test.js +++ b/1-js/04-object-basics/01-object/3-is-empty/_js.view/test.js @@ -1,9 +1,9 @@ describe("isEmpty", function() { - it("returns true for an empty object", function() { + it("retorna true para un objeto vacío", function() { assert.isTrue(isEmpty({})); }); - it("returns false if a property exists", function() { + it("retorna false si existe una propiedad", function() { assert.isFalse(isEmpty({ anything: false })); diff --git a/1-js/04-object-basics/01-object/3-is-empty/solution.md b/1-js/04-object-basics/01-object/3-is-empty/solution.md index b876973b5..2ba2b89e0 100644 --- a/1-js/04-object-basics/01-object/3-is-empty/solution.md +++ b/1-js/04-object-basics/01-object/3-is-empty/solution.md @@ -1 +1 @@ -Just loop over the object and `return false` immediately if there's at least one property. +Solo crea un bucle sobre el objeto y, si hay al menos una propiedad, devuelve `false` inmediatamente. diff --git a/1-js/04-object-basics/01-object/3-is-empty/task.md b/1-js/04-object-basics/01-object/3-is-empty/task.md index c438d36a2..ab3eff3d0 100644 --- a/1-js/04-object-basics/01-object/3-is-empty/task.md +++ b/1-js/04-object-basics/01-object/3-is-empty/task.md @@ -2,18 +2,18 @@ importance: 5 --- -# Check for emptiness +# Verificar los vacíos -Write the function `isEmpty(obj)` which returns `true` if the object has no properties, `false` otherwise. +Escribe la función `isEmpty(obj)` que devuelva el valor `true` si el objeto no tiene propiedades, en caso contrario `false`. -Should work like that: +Debería funcionar así: ```js let schedule = {}; alert( isEmpty(schedule) ); // true -schedule["8:30"] = "get up"; +schedule["8:30"] = "Hora de levantarse"; alert( isEmpty(schedule) ); // false ``` diff --git a/1-js/04-object-basics/01-object/5-sum-object/task.md b/1-js/04-object-basics/01-object/5-sum-object/task.md index 7e3e048d0..99680c9fd 100644 --- a/1-js/04-object-basics/01-object/5-sum-object/task.md +++ b/1-js/04-object-basics/01-object/5-sum-object/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# Sum object properties +# Suma de propiedades de un objeto -We have an object storing salaries of our team: +Tenemos un objeto que almacena los salarios de nuestro equipo: ```js let salaries = { @@ -14,6 +14,6 @@ let salaries = { } ``` -Write the code to sum all salaries and store in the variable `sum`. Should be `390` in the example above. +Escribe el código para sumar todos los salarios y almacenar el resultado en la variable `sum`. En el ejemplo de arriba nos debería dar `390`. -If `salaries` is empty, then the result must be `0`. \ No newline at end of file +Si `salaries` está vacío entonces el resultado será `0`. \ No newline at end of file diff --git a/1-js/04-object-basics/01-object/8-multiply-numeric/_js.view/source.js b/1-js/04-object-basics/01-object/8-multiply-numeric/_js.view/source.js index a02b1e1cb..8aa93a62f 100644 --- a/1-js/04-object-basics/01-object/8-multiply-numeric/_js.view/source.js +++ b/1-js/04-object-basics/01-object/8-multiply-numeric/_js.view/source.js @@ -1,17 +1,16 @@ let menu = { width: 200, height: 300, - title: "My menu" + title: "Mi menú" }; function multiplyNumeric(obj) { - /* your code */ + /* tu código */ } multiplyNumeric(menu); -alert( "menu width=" + menu.width + " height=" + menu.height + " title=" + menu.title ); - +alert( "ancho del menú=" + menu.width + " alto=" + menu.height + " título=" + menu.title ); diff --git a/1-js/04-object-basics/01-object/8-multiply-numeric/_js.view/test.js b/1-js/04-object-basics/01-object/8-multiply-numeric/_js.view/test.js index 064e5414f..7e2234a7b 100644 --- a/1-js/04-object-basics/01-object/8-multiply-numeric/_js.view/test.js +++ b/1-js/04-object-basics/01-object/8-multiply-numeric/_js.view/test.js @@ -1,17 +1,17 @@ describe("multiplyNumeric", function() { - it("multiplies all numeric properties by 2", function() { + it("multiplicar todas las propiedades numéricas por 2", function() { let menu = { width: 200, height: 300, - title: "My menu" + title: "Mi menú" }; let result = multiplyNumeric(menu); assert.equal(menu.width, 400); assert.equal(menu.height, 600); - assert.equal(menu.title, "My menu"); + assert.equal(menu.title, "Mi menú"); }); - it("returns nothing", function() { + it("No devuelve nada", function() { assert.isUndefined( multiplyNumeric({}) ); }); diff --git a/1-js/04-object-basics/01-object/8-multiply-numeric/task.md b/1-js/04-object-basics/01-object/8-multiply-numeric/task.md index 6878ca088..8e4d26ae2 100644 --- a/1-js/04-object-basics/01-object/8-multiply-numeric/task.md +++ b/1-js/04-object-basics/01-object/8-multiply-numeric/task.md @@ -2,32 +2,32 @@ importance: 3 --- -# Multiply numeric property values by 2 +# Multiplicar propiedades numéricas por 2 -Create a function `multiplyNumeric(obj)` that multiplies all numeric property values of `obj` by `2`. +Crea una función `multiplyNumeric(obj)` que multiplique todas las propiedades numéricas de `obj` por `2`. -For instance: +Por ejemplo: ```js -// before the call +// Antes de la llamada let menu = { width: 200, height: 300, - title: "My menu" + title: "Mi menú" }; multiplyNumeric(menu); -// after the call +// Después de la llamada menu = { width: 400, height: 600, - title: "My menu" + title: "Mi menú" }; ``` -Please note that `multiplyNumeric` does not need to return anything. It should modify the object in-place. +Nota que `multiplyNumeric` no necesita devolver nada. Debe modificar el objeto en su lugar. -P.S. Use `typeof` to check for a number here. +P.D. Usa `typeof` para verificar si hay un número aquí. diff --git a/1-js/04-object-basics/01-object/article.md b/1-js/04-object-basics/01-object/article.md index dc3ec1a64..c040b8d0d 100644 --- a/1-js/04-object-basics/01-object/article.md +++ b/1-js/04-object-basics/01-object/article.md @@ -1,60 +1,60 @@ -# Objects +# Objetos -As we know from the chapter , there are eight data types in JavaScript. Seven of them are called "primitive", because their values contain only a single thing (be it a string or a number or whatever). +Como aprendimos en el capítulo , hay ocho tipos de datos en JavaScript. Siete de ellos se denominan "primitivos", porque sus valores contienen solo un dato (sea un `string`, un número o lo que sea). -In contrast, objects are used to store keyed collections of various data and more complex entities. In JavaScript, objects penetrate almost every aspect of the language. So we must understand them first before going in-depth anywhere else. +En contraste, los objetos son usados para almacenar colecciones de varios datos y entidades más complejas asociados con un nombre clave. En JavaScript, los objetos penetran casi todos los aspectos del lenguaje. Por lo tanto, debemos comprenderlos primero antes de profundizar en cualquier otro lugar. -An object can be created with curly braces `{…}` with an optional list of *properties*. A property is a "key: value" pair, where `key` is a string (also called a "property name"), and `value` can be anything. +Un objeto se puede crear con llaves `{ … }` y una lista opcional de *propiedades*. Cada propiedad es un par `"key: value"` ("clave: valor"), donde `key` es un string (también llamado "nombre de propiedad") y `value` puede ser cualquier cosa. -We can imagine an object as a cabinet with signed files. Every piece of data is stored in its file by the key. It's easy to find a file by its name or add/remove a file. +Podemos imaginar un objeto como un gabinete con archivos firmados. Cada pieza de datos es almacenada en su archivo por la clave. Es fácil encontrar un archivo por su nombre o agregar/eliminar un archivo. ![](object.svg) -An empty object ("empty cabinet") can be created using one of two syntaxes: +Se puede crear un objeto vacío ("gabinete vacío") utilizando una de estas dos sintaxis: ```js -let user = new Object(); // "object constructor" syntax -let user = {}; // "object literal" syntax +let user = new Object(); // sintaxis de "constructor de objetos" +let user = {}; // sintaxis de "objeto literal" ``` ![](object-user-empty.svg) -Usually, the curly braces `{...}` are used. That declaration is called an *object literal*. +Normalmente se utilizan las llaves `{...}`. Esa declaración se llama *objeto literal*. -## Literals and properties +## Literales y propiedades -We can immediately put some properties into `{...}` as "key: value" pairs: +Podemos poner inmediatamente algunas propiedades dentro de `{...}` como pares "clave:valor": ```js -let user = { // an object - name: "John", // by key "name" store value "John" - age: 30 // by key "age" store value 30 +let user = { // un objeto + name: "John", // En la clave "name" se almacena el valor "John" + age: 30 // En la clave "age" se almacena el valor 30 }; ``` -A property has a key (also known as "name" or "identifier") before the colon `":"` and a value to the right of it. +Una propiedad tiene una clave (también conocida como "nombre" o "identificador") antes de los dos puntos `":"` y un valor a la derecha. -In the `user` object, there are two properties: +En el objeto `user` hay dos propiedades: -1. The first property has the name `"name"` and the value `"John"`. -2. The second one has the name `"age"` and the value `30`. +1. La primera propiedad tiene la clave `"name"` y el valor `"John"`. +2. La segunda tienen la clave `"age"` y el valor `30`. -The resulting `user` object can be imagined as a cabinet with two signed files labeled "name" and "age". +Podemos imaginar al objeto `user` resultante como un gabinete con dos archivos firmados con las etiquetas "name" y "age". ![user object](object-user.svg) -We can add, remove and read files from it at any time. +Podemos agregar, eliminar y leer archivos de él en cualquier momento. -Property values are accessible using the dot notation: +Se puede acceder a los valores de las propiedades utilizando la notación de punto: ```js -// get property values of the object: +// Obteniendo los valores de las propiedades del objeto: alert( user.name ); // John alert( user.age ); // 30 ``` -The value can be of any type. Let's add a boolean one: +El valor puede ser de cualquier tipo. Agreguemos uno booleano: ```js user.isAdmin = true; @@ -62,7 +62,7 @@ user.isAdmin = true; ![user object 2](object-user-isadmin.svg) -To remove a property, we can use the `delete` operator: +Para eliminar una propiedad podemos usar el operador `delete`: ```js delete user.age; @@ -70,70 +70,70 @@ delete user.age; ![user object 3](object-user-delete.svg) -We can also use multiword property names, but then they must be quoted: +También podemos nombrar propiedades con más de una palabra. Pero, de ser así, debemos colocar la clave entre comillas `"..."`: ```js let user = { name: "John", age: 30, - "likes birds": true // multiword property name must be quoted + "likes birds": true // Las claves con más de una palabra deben ir entre comillas }; ``` ![](object-user-props.svg) -The last property in the list may end with a comma: +La última propiedad en la lista puede terminar con una coma: ```js let user = { name: "John", age: 30*!*,*/!* } ``` -That is called a "trailing" or "hanging" comma. Makes it easier to add/remove/move around properties, because all lines become alike. +Eso se llama una coma "final" o "colgante". Facilita agregar, eliminar y mover propiedades, porque todas las líneas se vuelven similares. -## Square brackets +## Corchetes -For multiword properties, the dot access doesn't work: +La notación de punto no funciona para acceder a propiedades con claves de más de una palabra: ```js run -// this would give a syntax error +// Esto nos daría un error de sintaxis user.likes birds = true ``` -JavaScript doesn't understand that. It thinks that we address `user.likes`, and then gives a syntax error when comes across unexpected `birds`. +JavaScript no entiende eso. Piensa que hemos accedido a `user.likes` y entonces nos da un error de sintaxis cuando aparece el inesperado `birds`. -The dot requires the key to be a valid variable identifier. That implies: contains no spaces, doesn't start with a digit and doesn't include special characters (`$` and `_` are allowed). +El punto requiere que la clave sea un identificador de variable válido. Eso implica que: no contenga espacios, no comience con un dígito y no incluya caracteres especiales (`$` y `_` sí se permiten). -There's an alternative "square bracket notation" that works with any string: +Existe una "notación de corchetes" alternativa que funciona con cualquier string: ```js run let user = {}; -// set +// asignando user["likes birds"] = true; -// get +// obteniendo alert(user["likes birds"]); // true -// delete +// eliminando delete user["likes birds"]; ``` -Now everything is fine. Please note that the string inside the brackets is properly quoted (any type of quotes will do). +Ahora todo está bien. Nota que el string dentro de los corchetes está adecuadamente entre comillas (cualquier tipo de comillas servirían). -Square brackets also provide a way to obtain the property name as the result of any expression -- as opposed to a literal string -- like from a variable as follows: +Los corchetes también brindan una forma de obtener el nombre de la propiedad desde el resultado de una expresión (a diferencia de la cadena literal). Por ejemplo, a través de una variable: ```js let key = "likes birds"; -// same as user["likes birds"] = true; +// Tal cual: user["likes birds"] = true; user[key] = true; ``` -Here, the variable `key` may be calculated at run-time or depend on the user input. And then we use it to access the property. That gives us a great deal of flexibility. +Aquí la variable `key` puede calcularse en tiempo de ejecución o depender de la entrada del usuario y luego lo usamos para acceder a la propiedad. Eso nos da mucha flexibilidad. -For instance: +Por ejemplo: ```js run let user = { @@ -141,13 +141,13 @@ let user = { age: 30 }; -let key = prompt("What do you want to know about the user?", "name"); +let key = prompt("¿Qué te gustaría saber acerca del usuario?", "name"); -// access by variable -alert( user[key] ); // John (if enter "name") +// acceso por medio de una variable +alert( user[key] ); // John (si se ingresara "name") ``` -The dot notation cannot be used in a similar way: +La notación de punto no puede ser usada de manera similar: ```js run let user = { @@ -159,40 +159,40 @@ let key = "name"; alert( user.key ) // undefined ``` -### Computed properties +### Propiedades calculadas -We can use square brackets in an object literal, when creating an object. That's called *computed properties*. +Podemos usar corchetes en un objeto literal al crear un objeto. A esto se le llama *propiedades calculadas*. -For instance: +Por ejemplo: ```js run -let fruit = prompt("Which fruit to buy?", "apple"); +let fruit = prompt("¿Qué fruta comprar?", "Manzana"); let bag = { *!* - [fruit]: 5, // the name of the property is taken from the variable fruit + [fruit]: 5, // El nombre de la propiedad se obtiene de la variable fruit */!* }; -alert( bag.apple ); // 5 if fruit="apple" +alert( bag.apple ); // 5 si fruit es="apple" ``` -The meaning of a computed property is simple: `[fruit]` means that the property name should be taken from `fruit`. +El significado de una propiedad calculada es simple: `[fruit]` significa que se debe tomar la clave de la propiedad `fruit`. -So, if a visitor enters `"apple"`, `bag` will become `{apple: 5}`. +Entonces, si un visitante ingresa `"apple"`, `bag` se convertirá en `{apple: 5}`. -Essentially, that works the same as: +Esencialmente esto funciona igual que: ```js run -let fruit = prompt("Which fruit to buy?", "apple"); +let fruit = prompt("¿Qué fruta comprar?", "Manzana"); let bag = {}; -// take property name from the fruit variable +// Toma el nombre de la propiedad de la variable fruit bag[fruit] = 5; ``` -...But looks nicer. +...Pero luce mejor. -We can use more complex expressions inside square brackets: +Podemos usar expresiones más complejas dentro de los corchetes: ```js let fruit = 'apple'; @@ -201,22 +201,22 @@ let bag = { }; ``` -Square brackets are much more powerful than dot notation. They allow any property names and variables. But they are also more cumbersome to write. +Los corchetes son mucho más potentes que la notación de punto. Permiten cualquier nombre de propiedad, incluso variables. Pero también es más engorroso escribirlos. -So most of the time, when property names are known and simple, the dot is used. And if we need something more complex, then we switch to square brackets. +Entonces, la mayoría de las veces, cuando los nombres de propiedad son conocidos y simples, se utiliza el punto. Y si necesitamos algo más complejo, entonces cambiamos a corchetes. -## Property value shorthand +## Atajo para valores de propiedad -In real code, we often use existing variables as values for property names. +En el código real, a menudo usamos variables existentes como valores de los nombres de propiedades. -For instance: +Por ejemplo: ```js run function makeUser(name, age) { return { name: name, age: age, - // ...other properties + // ...otras propiedades }; } @@ -224,40 +224,40 @@ let user = makeUser("John", 30); alert(user.name); // John ``` -In the example above, properties have the same names as variables. The use-case of making a property from a variable is so common, that there's a special *property value shorthand* to make it shorter. +En el ejemplo anterior las propiedades tienen los mismos nombres que las variables. El uso de variables para la creación de propiedades es tán común que existe un *atajo para valores de propiedad* especial para hacerla más corta. -Instead of `name:name` we can just write `name`, like this: +En lugar de `name:name`, simplemente podemos escribir `name`, tal cual: ```js function makeUser(name, age) { *!* return { - name, // same as name: name - age, // same as age: age + name, // igual que name:name + age, // igual que age:age // ... }; */!* } ``` -We can use both normal properties and shorthands in the same object: +Podemos usar ambos tipos de notación en un mismo objeto, la normal y el atajo: ```js let user = { - name, // same as name:name + name, // igual que name:name age: 30 }; ``` -## Property names limitations +## Limitaciones de nombres de propiedad -As we already know, a variable cannot have a name equal to one of the language-reserved words like "for", "let", "return" etc. +Como sabemos, una variable no puede tener un nombre igual a una de las palabras reservadas del lenguaje, como "for", "let", "return", etc. -But for an object property, there's no such restriction: +Pero para una propiedad de objeto no existe tal restricción: ```js run -// these properties are all right +// Estas propiedades están bien let obj = { for: 1, let: 2, @@ -267,107 +267,107 @@ let obj = { alert( obj.for + obj.let + obj.return ); // 6 ``` -In short, there are no limitations on property names. They can be any strings or symbols (a special type for identifiers, to be covered later). +En resumen, no hay limitaciones en los nombres de propiedades. Pueden ser cadenas o símbolos (un tipo especial para identificadores que se cubrirán más adelante). -Other types are automatically converted to strings. +Otros tipos se convierten automáticamente en cadenas. -For instance, a number `0` becomes a string `"0"` when used as a property key: +Por ejemplo, un número `0` se convierte en cadena `"0"` cuando se usa como clave de propiedad: ```js run let obj = { - 0: "test" // same as "0": "test" + 0: "test" // igual que "0": "test" }; -// both alerts access the same property (the number 0 is converted to string "0") +// ambos alerts acceden a la misma propiedad (el número 0 se convierte a una cadena "0") alert( obj["0"] ); // test -alert( obj[0] ); // test (same property) +alert( obj[0] ); // test (la misma propiedad) ``` -There's a minor gotcha with a special property named `__proto__`. We can't set it to a non-object value: +Hay una pequeña sorpresa por una propiedad especial llamada `__proto__`. No podemos establecerlo dentro de un valor que no sea de objeto: ```js run let obj = {}; -obj.__proto__ = 5; // assign a number -alert(obj.__proto__); // [object Object] - the value is an object, didn't work as intended +obj.__proto__ = 5; // asignando un número +alert(obj.__proto__); // [objeto Object] - el valor es un objeto, no funciona como se "debería" ``` -As we see from the code, the assignment to a primitive `5` is ignored. +Como podemos ver en el código, se ignora la asignación de un valor primitivo `5`. -We'll cover the special nature of `__proto__` in [subsequent chapters](info:prototype-inheritance), and suggest the [ways to fix](info:prototype-methods) such behavior. +Veremos la naturaleza especial de `__proto__` en los [capítulos siguientes](info:prototype-inheritance), y sugeriremos las [formas de arreglar](info:prototype-methods) tal comportamiento. -## Property existence test, "in" operator +## La prueba de propiedad existente, el operador "in" -A notable feature of objects in JavaScript, compared to many other languages, is that it's possible to access any property. There will be no error if the property doesn't exist! +Una notable característica de los objetos en JavaScript, en comparación con muchos otros lenguajes, es que es posible acceder a cualquier propiedad. ¡No habrá error si la propiedad no existe! -Reading a non-existing property just returns `undefined`. So we can easily test whether the property exists: +La lectura de una propiedad no existente solo devuelve `undefined`. Así que podemos probar fácilmente si la propiedad existe: ```js run let user = {}; -alert( user.noSuchProperty === undefined ); // true means "no such property" +alert( user.noSuchProperty === undefined ); // true significa que "no existe tal propiedad" ``` -There's also a special operator `"in"` for that. +También existe un operador especial para ello: `"in"`. -The syntax is: +La sintaxis es: ```js "key" in object ``` -For instance: +Por ejemplo: ```js run let user = { name: "John", age: 30 }; -alert( "age" in user ); // true, user.age exists -alert( "blabla" in user ); // false, user.blabla doesn't exist +alert( "age" in user ); // mostrará "true", porque user.age sí existe +alert( "blabla" in user ); // mostrará false, porque user.blabla no existe ``` -Please note that on the left side of `in` there must be a *property name*. That's usually a quoted string. +Nota que a la izquierda de `in` debe estar el *nombre de la propiedad* que suele ser un string entre comillas. -If we omit quotes, that means a variable should contain the actual name to be tested. For instance: +Si omitimos las comillas, significa que es una variable. Esta variable debe almacenar la clave real que será probada. Por ejemplo: ```js run let user = { age: 30 }; let key = "age"; -alert( *!*key*/!* in user ); // true, property "age" exists +alert( *!*key*/!* in user ); // true, porque su propiedad "age" sí existe dentro del objeto ``` -Why does the `in` operator exist? Isn't it enough to compare against `undefined`? +Pero... ¿Por qué existe el operador `in`? ¿No es suficiente comparar con `undefined`? -Well, most of the time the comparison with `undefined` works fine. But there's a special case when it fails, but `"in"` works correctly. +La mayoría de las veces las comparaciones con `undefined` funcionan bien. Pero hay un caso especial donde esto falla y aún así `"in"` funciona correctamente. -It's when an object property exists, but stores `undefined`: +Es cuando existe una propiedad de objeto, pero almacena `undefined`: ```js run let obj = { test: undefined }; -alert( obj.test ); // it's undefined, so - no such property? +alert( obj.test ); // es undefined, entonces... ¿Quiere decir realmente existe tal propiedad? -alert( "test" in obj ); // true, the property does exist! +alert( "test" in obj ); //es true, ¡La propiedad sí existe! ``` -In the code above, the property `obj.test` technically exists. So the `in` operator works right. +En el código anterior, la propiedad `obj.test` técnicamente existe. Entonces el operador `in` funciona correctamente. -Situations like this happen very rarely, because `undefined` should not be explicitly assigned. We mostly use `null` for "unknown" or "empty" values. So the `in` operator is an exotic guest in the code. +Situaciones como esta suceden raramente ya que `undefined` no debe ser explícitamente asignado. Comúnmente usamos `null` para valores "desconocidos" o "vacíos". Por lo que el operador `in` es un invitado exótico en nuestro código. -## The "for..in" loop [#forin] +## El bucle "for..in" [#forin] -To walk over all keys of an object, there exists a special form of the loop: `for..in`. This is a completely different thing from the `for(;;)` construct that we studied before. +Para recorrer todas las claves de un objeto existe una forma especial de bucle: `for..in`. Esto es algo completamente diferente a la construcción `for(;;)` que estudiaremos más adelante. -The syntax: +La sintaxis: ```js for (key in object) { - // executes the body for each key among object properties + // se ejecuta el cuerpo para cada clave entre las propiedades del objeto } ``` -For instance, let's output all properties of `user`: +Por ejemplo, mostremos todas las propiedades de `user`: ```js run let user = { @@ -377,24 +377,24 @@ let user = { }; for (let key in user) { - // keys + // claves alert( key ); // name, age, isAdmin - // values for the keys + // valores de las claves alert( user[key] ); // John, 30, true } ``` -Note that all "for" constructs allow us to declare the looping variable inside the loop, like `let key` here. +Nota que todas las construcciones "for" nos permiten declarar variables para bucle dentro del bucle, como `let key` aquí. -Also, we could use another variable name here instead of `key`. For instance, `"for (let prop in obj)"` is also widely used. +Además podríamos usar otros nombres de variables en lugar de `key`. Por ejemplo, `"for (let prop in obj)"` también se usa bastante. -### Ordered like an object +### Ordenado como un objeto -Are objects ordered? In other words, if we loop over an object, do we get all properties in the same order they were added? Can we rely on this? +¿Los objetos están ordenados? Es decir, si creamos un bucle sobre un objeto, ¿obtenemos todas las propiedades en el mismo orden en el que se agregaron? ¿Podemos confiar en ello? -The short answer is: "ordered in a special fashion": integer properties are sorted, others appear in creation order. The details follow. +La respuesta corta es: "ordenados de una forma especial": las propiedades de números enteros se ordenan, los demás aparecen en el orden de la creación. Entremos en detalle. -As an example, let's consider an object with the phone codes: +Como ejemplo, consideremos un objeto con códigos telefónicos: ```js run let codes = { @@ -412,49 +412,49 @@ for (let code in codes) { */!* ``` -The object may be used to suggest a list of options to the user. If we're making a site mainly for a German audience then we probably want `49` to be the first. +El objeto puede usarse para sugerir al usuario una lista de opciones. Si estamos haciendo un sitio principalmente para el público alemán, probablemente queremos que `49` sea el primero. -But if we run the code, we see a totally different picture: +Pero si ejecutamos el código, veremos una imagen totalmente diferente: -- USA (1) goes first -- then Switzerland (41) and so on. +- USA (1) va primero +- Luego Switzerland (41) y así sucesivamente. -The phone codes go in the ascending sorted order, because they are integers. So we see `1, 41, 44, 49`. +Los códigos telefónicos van en orden ascendente porque son números enteros. Entonces vemos `1, 41, 44, 49`. -````smart header="Integer properties? What's that?" -The "integer property" term here means a string that can be converted to-and-from an integer without a change. +````smart header="¿Propiedades de números enteros? ¿Qué es eso?" +El término "propiedad de números enteros" aquí significa que una cadena se puede convertir a y desde desde un entero sin ningún cambio. -So, `"49"` is an integer property name, because when it's transformed to an integer number and back, it's still the same. But `"+49"` and `"1.2"` are not: +Entonces, "49" es un nombre de propiedad entero, porque cuando este se transforma a un entero y viceversa continúa siendo el mismo. Pero "+49" y "1.2" no lo son: ```js run -// Number(...) explicitly converts to a number -// Math.trunc is a built-in function that removes the decimal part -alert( String(Math.trunc(Number("49"))) ); // "49", same, integer property -alert( String(Math.trunc(Number("+49"))) ); // "49", not same "+49" ⇒ not integer property -alert( String(Math.trunc(Number("1.2"))) ); // "1", not same "1.2" ⇒ not integer property +// Number(...) convierte explícitamente a number +// Math.trunc es una función nativa que elimina la parte decimal +alert( String(Math.trunc(Number("49"))) ); // "49", es igual, una propiedad entera +alert( String(Math.trunc(Number("+49"))) ); // "49", no es igual "+49" ⇒ no es una propiedad entera +alert( String(Math.trunc(Number("1.2"))) ); // "1", no es igual "1.2" ⇒ no es una propiedad entera ``` ```` -...On the other hand, if the keys are non-integer, then they are listed in the creation order, for instance: +...Por otro lado, si las claves no son enteras, se enumeran en el orden de creación, por ejemplo: ```js run let user = { name: "John", surname: "Smith" }; -user.age = 25; // add one more +user.age = 25; // Se agrega una propiedad más *!* -// non-integer properties are listed in the creation order +// Las propiedades que no son enteras se enumeran en el orden de creación */!* for (let prop in user) { alert( prop ); // name, surname, age } ``` -So, to fix the issue with the phone codes, we can "cheat" by making the codes non-integer. Adding a plus `"+"` sign before each code is enough. +Entonces, para solucionar el problema con los códigos telefónicos, podemos "hacer trampa" haciendo que los códigos no sean enteros. Agregar un signo más `"+"` antes de cada código será más que suficiente. -Like this: +Justo así: ```js run let codes = { @@ -470,34 +470,34 @@ for (let code in codes) { } ``` -Now it works as intended. +Ahora sí funciona como debería. -## Summary +## Resumen -Objects are associative arrays with several special features. +Los objetos son arreglos asociativos con varias características especiales. -They store properties (key-value pairs), where: -- Property keys must be strings or symbols (usually strings). -- Values can be of any type. +Almacenan propiedades (pares de clave-valor), donde: +- Las claves de propiedad deben ser cadenas o símbolos (generalmente strings). +- Los valores pueden ser de cualquier tipo. -To access a property, we can use: -- The dot notation: `obj.property`. -- Square brackets notation `obj["property"]`. Square brackets allow taking the key from a variable, like `obj[varWithKey]`. +Para acceder a una propiedad, podemos usar: +- La notación de punto: `obj.property`. +- La notación de corchetes `obj["property"]`. Los corchetes permiten tomar la clave de una variable, como `obj[varWithKey]`. -Additional operators: -- To delete a property: `delete obj.prop`. -- To check if a property with the given key exists: `"key" in obj`. -- To iterate over an object: `for (let key in obj)` loop. +Operadores adicionales: +- Para eliminar una propiedad: `delete obj.prop`. +- Para comprobar si existe una propiedad con la clave proporcionada: `"key" in obj`. +- Para crear bucles sobre un objeto: bucle `for (let key in obj)`. -What we've studied in this chapter is called a "plain object", or just `Object`. +Lo que hemos estudiado en este capítulo se llama "objeto simple", o solamente `Object`. -There are many other kinds of objects in JavaScript: +Hay muchos otros tipos de objetos en JavaScript: -- `Array` to store ordered data collections, -- `Date` to store the information about the date and time, -- `Error` to store the information about an error. -- ...And so on. +- `Array` para almacenar colecciones de datos ordenados, +- `Date` para almacenar la información sobre fecha y hora, +- `Error` para almacenar información sobre un error. +- ...Y así. -They have their special features that we'll study later. Sometimes people say something like "Array type" or "Date type", but formally they are not types of their own, but belong to a single "object" data type. And they extend it in various ways. +Tienen sus características especiales que estudiaremos más adelante. A veces las personas dicen algo como "Tipo `array`" o "Tipo `date`", pero formalmente no son tipos en sí, sino que pertenecen a un tipo de datos de "objeto" simple y lo amplían a varias maneras. -Objects in JavaScript are very powerful. Here we've just scratched the surface of a topic that is really huge. We'll be closely working with objects and learning more about them in further parts of the tutorial. +Los objetos en JavaScript son muy poderosos. Aquí acabamos de arañar la superficie de un tema que es realmente enorme. Trabajaremos estrechamente con los objetos y aprenderemos más sobre ellos en otras partes del tutorial. diff --git a/1-js/04-object-basics/01-object/object-user-empty.svg b/1-js/04-object-basics/01-object/object-user-empty.svg index 99edb0269..8f1b1cf92 100644 --- a/1-js/04-object-basics/01-object/object-user-empty.svg +++ b/1-js/04-object-basics/01-object/object-user-empty.svg @@ -1 +1 @@ -emptyuser \ No newline at end of file +vacíouser \ No newline at end of file diff --git a/1-js/04-object-basics/02-object-copy/article.md b/1-js/04-object-basics/02-object-copy/article.md index e80f748ab..02610d5d9 100644 --- a/1-js/04-object-basics/02-object-copy/article.md +++ b/1-js/04-object-basics/02-object-copy/article.md @@ -1,29 +1,29 @@ -# Object references and copying +# Referencias de objetos y copia -One of the fundamental differences of objects versus primitives is that objects are stored and copied "by reference", whereas primitive values: strings, numbers, booleans, etc -- are always copied "as a whole value". +Una de las diferencias fundamentales entre objetos y primitivos es que los objetos son almacenados y copiados "por referencia", en cambio los primitivos: strings, number, boolean, etc.; son asignados y copiados "como un valor completo". -That's easy to understand if we look a bit under the hood of what happens when we copy a value. +Esto es fácil de entender si miramos un poco "bajo cubierta" de lo que pasa cuando copiamos por valor. -Let's start with a primitive, such as a string. +Empecemos por un primitivo como string. -Here we put a copy of `message` into `phrase`: +Aquí ponemos una copia de `message` en `phrase`: ```js let message = "Hello!"; let phrase = message; ``` -As a result we have two independent variables, each one storing the string `"Hello!"`. +Como resultado tenemos dos variables independientes, cada una almacenando la cadena `"Hello!"`. ![](variable-copy-value.svg) -Quite an obvious result, right? +Bastante obvio, ¿verdad? -Objects are not like that. +Los objetos no son así. -**A variable assigned to an object stores not the object itself, but its "address in memory" -- in other words "a reference" to it.** +**Una variable no almacena el objeto mismo sino su "dirección en memoria", en otras palabras "una referencia" a él.** -Let's look at an example of such a variable: +Veamos un ejemplo de tal variable: ```js let user = { @@ -31,35 +31,35 @@ let user = { }; ``` -And here's how it's actually stored in memory: +Y así es como se almacena en la memoria: ![](variable-contains-reference.svg) -The object is stored somewhere in memory (at the right of the picture), while the `user` variable (at the left) has a "reference" to it. +El objeto es almacenado en algún lugar de la memoria (a la derecha de la imagen), mientras que la variable `user` (a la izquierda) tiene una "referencia" a él. -We may think of an object variable, such as `user`, like a sheet of paper with the address of the object on it. +Podemos pensar de una variable objeto, como `user`, como una hoja de papel con la dirección del objeto escrita en ella. -When we perform actions with the object, e.g. take a property `user.name`, the JavaScript engine looks at what's at that address and performs the operation on the actual object. +Cuando ejecutamos acciones con el objeto, por ejemplo tomar una propiedad `user.name`, el motor JavaScript busca aquella dirección y ejecuta la operación en el objeto mismo. -Now here's why it's important. +Ahora, por qué esto es importante. -**When an object variable is copied, the reference is copied, but the object itself is not duplicated.** +**Cuando una variable de objeto es copiada, se copia solo la referencia. El objeto no es duplicado.** -For instance: +Por ejemplo: ```js no-beautify let user = { name: "John" }; -let admin = user; // copy the reference +let admin = user; // copia la referencia ``` -Now we have two variables, each storing a reference to the same object: +Ahora tenemos dos variables, cada una con una referencia al mismo objeto: ![](variable-copy-reference.svg) -As you can see, there's still one object, but now with two variables that reference it. +Como puedes ver, aún hay un objeto, ahora con dos variables haciendo referencia a él. -We can use either variable to access the object and modify its contents: +Podemos usar cualquiera de las variables para acceder al objeto y modificar su contenido: ```js run let user = { name: 'John' }; @@ -67,43 +67,43 @@ let user = { name: 'John' }; let admin = user; *!* -admin.name = 'Pete'; // changed by the "admin" reference +admin.name = 'Pete'; // cambiado por la referencia "admin" */!* -alert(*!*user.name*/!*); // 'Pete', changes are seen from the "user" reference +alert(*!*user.name*/!*); // 'Pete', los cambios se ven desde la referencia "user" ``` -It's as if we had a cabinet with two keys and used one of them (`admin`) to get into it and make changes. Then, if we later use another key (`user`), we are still opening the same cabinet and can access the changed contents. +Es como si tuviéramos un gabinete con dos llaves y usáramos una de ellas (`admin`) para acceder a él y hacer cambios. Si más tarde usamos la llave (`user`), estaríamos abriendo el mismo gabinete y accediendo al contenido cambiado. -## Comparison by reference +## Comparación por referencia -Two objects are equal only if they are the same object. +Dos objetos son iguales solamente si ellos son el mismo objeto. -For instance, here `a` and `b` reference the same object, thus they are equal: +Por ejemplo, aquí `a` y `b` tienen referencias al mismo objeto, por lo tanto son iguales: ```js run let a = {}; -let b = a; // copy the reference +let b = a; // copia la referencia -alert( a == b ); // true, both variables reference the same object +alert( a == b ); // true, verdadero. Ambas variables hacen referencia al mismo objeto alert( a === b ); // true ``` -And here two independent objects are not equal, even though they look alike (both are empty): +Y aquí dos objetos independientes no son iguales, aunque se vean iguales (ambos están vacíos): ```js run let a = {}; -let b = {}; // two independent objects +let b = {}; // dos objetos independientes alert( a == b ); // false ``` -For comparisons like `obj1 > obj2` or for a comparison against a primitive `obj == 5`, objects are converted to primitives. We'll study how object conversions work very soon, but to tell the truth, such comparisons are needed very rarely -- usually they appear as a result of a programming mistake. +Para comparaciones como `obj1 > obj2`, o comparaciones contra un primitivo `obj == 5`, los objetos son convertidos a primitivos. Estudiaremos cómo funciona la conversión de objetos pronto, pero a decir verdad tales comparaciones ocurren raramente y suelen ser errores de código. -````smart header="Const objects can be modified" -An important side effect of storing objects as references is that an object declared as `const` *can* be modified. +````smart header="Los objetos "const" pueden ser modificados" +Un efecto importante de almacenar objetos como referencias es que un objeto declarado como `const` *puede* ser modificado. -For instance: +Por ejemplo: ```js run const user = { @@ -117,22 +117,22 @@ user.name = "Pete"; // (*) alert(user.name); // Pete ``` -It might seem that the line `(*)` would cause an error, but it does not. The value of `user` is constant, it must always reference the same object, but properties of that object are free to change. +Puede parecer que la línea `(*)` causaría un error, pero no lo hace. El valor de `user` es constante, este valor debe siempre hacer referencia al mismo objeto, pero las propiedades de dicho objeto pueden cambiar. -In other words, the `const user` gives an error only if we try to set `user=...` as a whole. +En otras palabras: `const user` da un error solamente si tratamos de establecer `user=...` como un todo. -That said, if we really need to make constant object properties, it's also possible, but using totally different methods. We'll mention that in the chapter . +Dicho esto, si realmente necesitamos hacer constantes las propiedades del objeto, también es posible, pero usando métodos totalmente diferentes. Los mencionaremos en el capítulo . ```` -## Cloning and merging, Object.assign [#cloning-and-merging-object-assign] +## Clonación y mezcla, Object.assign [#cloning-and-merging-object-assign] -So, copying an object variable creates one more reference to the same object. +Entonces copiar una variable de objeto crea una referencia adicional al mismo objeto. -But what if we need to duplicate an object? +Pero ¿y si necesitamos duplicar un objeto? -We can create a new object and replicate the structure of the existing one, by iterating over its properties and copying them on the primitive level. +Podemos crear un nuevo objeto y replicar la estructura del existente iterando a través de sus propiedades y copiándolas en el nivel primitivo. -Like this: +Como esto: ```js run let user = { @@ -141,35 +141,34 @@ let user = { }; *!* -let clone = {}; // the new empty object +let clone = {}; // el nuevo objeto vacío -// let's copy all user properties into it +// copiemos todas las propiedades de user en él for (let key in user) { clone[key] = user[key]; } */!* -// now clone is a fully independent object with the same content -clone.name = "Pete"; // changed the data in it +// ahora clone es un objeto totalmente independiente con el mismo contenido +clone.name = "Pete"; // cambiamos datos en él -alert( user.name ); // still John in the original object +alert( user.name ); // John aún está en el objeto original ``` -We can also use the method [Object.assign](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign). +También podemos usar el método [Object.assign](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/assign). -The syntax is: +La sintaxis es: ```js Object.assign(dest, ...sources) ``` -- The first argument `dest` is a target object. -- Further arguments is a list of source objects. +- El primer argumento `dest` es el objeto destinatario. +- Los argumentos que siguen son una lista de objetos fuentes. -It copies the properties of all source objects into the target `dest`, and then returns it as the result. - -For example, we have `user` object, let's add a couple of permissions to it: +Esto copia las propiedades de todos los objetos fuentes dentro del destino `dest` y lo devuelve como resultado +Por ejemplo, tenemos el objeto `user`, agreguemos un par de permisos: ```js run let user = { name: "John" }; @@ -177,27 +176,27 @@ let permissions1 = { canView: true }; let permissions2 = { canEdit: true }; *!* -// copies all properties from permissions1 and permissions2 into user +// copia todas las propiedades desde permissions1 y permissions2 en user Object.assign(user, permissions1, permissions2); */!* -// now user = { name: "John", canView: true, canEdit: true } +// ahora es user = { name: "John", canView: true, canEdit: true } alert(user.name); // John alert(user.canView); // true alert(user.canEdit); // true ``` -If the copied property name already exists, it gets overwritten: +Si la propiedad por copiar ya existe, se sobrescribe: ```js run let user = { name: "John" }; Object.assign(user, { name: "Pete" }); -alert(user.name); // now user = { name: "Pete" } +alert(user.name); // ahora user = { name: "Pete" } ``` -We also can use `Object.assign` to perform a simple object cloning: +También podemos usar `Object.assign` para hacer una clonación simple: ```js run let user = { @@ -213,15 +212,15 @@ alert(clone.name); // John alert(clone.age); // 30 ``` -Here it copies all properties of `user` into the empty object and returns it. +Aquí, copia todas las propiedades de `user` en un objeto vacío y lo devuelve. -There are also other methods of cloning an object, e.g. using the [spread syntax](info:rest-parameters-spread) `clone = {...user}`, covered later in the tutorial. +También hay otras formas de clonar un objeto, por ejemplo usando la [sintaxis spread](info:rest-parameters-spread) `clone = {...user}`, cubierto más adelante en el tutorial. -## Nested cloning +## Clonación anidada -Until now we assumed that all properties of `user` are primitive. But properties can be references to other objects. +Hasta ahora supusimos que todas las propiedades de `user` eran primitivas. Pero las propiedades pueden ser referencias a otros objetos. -Like this: +Como esto: ```js run let user = { name: "John", @@ -234,7 +233,7 @@ let user = { alert( user.sizes.height ); // 182 ``` -Now it's not enough to copy `clone.sizes = user.sizes`, because `user.sizes` is an object, and will be copied by reference, so `clone` and `user` will share the same sizes: +Ahora no es suficiente copiar `clone.sizes = user.sizes`, porque `user.sizes` es un objeto y será copiado por referencia. Entonces `clone` y `user` compartirán las mismas tallas (.sizes): ```js run let user = { @@ -247,21 +246,21 @@ let user = { let clone = Object.assign({}, user); -alert( user.sizes === clone.sizes ); // true, same object +alert( user.sizes === clone.sizes ); // true, el mimo objeto -// user and clone share sizes -user.sizes.width = 60; // change a property from one place -alert(clone.sizes.width); // 60, get the result from the other one +// user y clone comparten sizes +user.sizes.width = 60; // cambia la propiedad en un lugar +alert(clone.sizes.width); // 60, obtiene el resultado desde el otro ``` -To fix that and make `user` and `clone` truly separate objects, we should use a cloning loop that examines each value of `user[key]` and, if it's an object, then replicate its structure as well. That is called a "deep cloning" or "structured cloning". There's [structuredClone](https://developer.mozilla.org/en-US/docs/Web/API/structuredClone) method that implements deep cloning. +Para corregir esto, debemos hacer que `user` y `clone` sean objetos completamente separados, debemos usar un bucle que examine cada valor de `user[key]` y, si es un objeto, que replique su estructura también. Esto es conocido como "clonación profunda" o "clonación estructurada". Existe un método [structuredClone](https://developer.mozilla.org/en-US/docs/Web/API/structuredClone) que implementa tal clonación profunda. ### structuredClone -The call `structuredClone(object)` clones the `object` with all nested properties. +La llamada a `structuredClone(object)` clona el `object` con todas sus propiedadas anidadas. -Here's how we can use it in our example: +Podemos usarlo en nuestro ejemplo: ```js run let user = { @@ -276,34 +275,34 @@ let user = { let clone = structuredClone(user); */!* -alert( user.sizes === clone.sizes ); // false, different objects +alert( user.sizes === clone.sizes ); // false, objetos diferentes -// user and clone are totally unrelated now -user.sizes.width = 60; // change a property from one place -alert(clone.sizes.width); // 50, not related +// ahora user y clone están totalmente separados +user.sizes.width = 60; // cambia una propiedad de un lugar +alert(clone.sizes.width); // 50, no están relacionados ``` -The `structuredClone` method can clone most data types, such as objects, arrays, primitive values. +El método `structuredClone` puede clonar la mayoría de los tipos de datos, como objetos, arrays, valores primitivos. -It also supports circular references, when an object property references the object itself (directly or via a chain or references). +También soporta referencias circulares, cuando una propiedad de objeto referencia el objeto mismo (directamente o por una cadena de referencias). -For instance: +Por ejemplo: ```js run let user = {}; -// let's create a circular reference: -// user.me references the user itself +// hagamos una referencia circular +// user.me referencia user a sí mismo user.me = user; let clone = structuredClone(user); alert(clone.me === clone); // true ``` -As you can see, `clone.me` references the `clone`, not the `user`! So the circular reference was cloned correctly as well. +Como puedes ver, `clone.me` hace referencia a `clone`, no a `user`! Así que la referencia circular fue clonada correctamente también. -Although, there are cases when `structuredClone` fails. +Pero hay casos en que `structuredClone` falla. -For instance, when an object has a function property: +Por ejemplo, cuando un objeto tienen una propiedad "function": ```js run // error @@ -312,14 +311,14 @@ structuredClone({ }); ``` -Function properties aren't supported. +Las propiedades de función no están soportadas. -To handle such complex cases we may need to use a combination of cloning methods, write custom code or, to not reinvent the wheel, take an existing implementation, for instance [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep) from the JavaScript library [lodash](https://lodash.com). +Para manejar estos casos complejos podemos necesitar una combinación de métodos de clonación, escribir código personalizado o, para no reinventar la rueda, tomar una implementación existente, por ejemplo [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep) de la librería JavaScript [lodash](https://lodash.com). -## Summary +## Resumen -Objects are assigned and copied by reference. In other words, a variable stores not the "object value", but a "reference" (address in memory) for the value. So copying such a variable or passing it as a function argument copies that reference, not the object itself. +Los objetos son asignados y copiados por referencia. En otras palabras, una variable almacena no el valor del objeto sino una referencia (la dirección en la memoria) del valor. Entonces, copiar tal variable o pasarla como argumento de función copia la referencia, no el objeto. -All operations via copied references (like adding/removing properties) are performed on the same single object. +Todas la operaciones a través de referencias copiadas (como agregar y borrar propiedades) son efectuadas en el mismo y único objeto . -To make a "real copy" (a clone) we can use `Object.assign` for the so-called "shallow copy" (nested objects are copied by reference) or a "deep cloning" function `structuredClone` or use a custom cloning implementation, such as [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep). +Para hacer una "verdadera copia" (un clon), podemos usar `Object.assign` para la denominada "clonación superficial" (los objetos anidados son copiados por referencia), o la función de "clonación profunda" `structuredClone` o usar una implementación personalizada como [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep). diff --git a/1-js/04-object-basics/03-garbage-collection/article.md b/1-js/04-object-basics/03-garbage-collection/article.md index 1b576d629..8a6cb10b9 100644 --- a/1-js/04-object-basics/03-garbage-collection/article.md +++ b/1-js/04-object-basics/03-garbage-collection/article.md @@ -1,38 +1,38 @@ -# Garbage collection +# Recolección de basura -Memory management in JavaScript is performed automatically and invisibly to us. We create primitives, objects, functions... All that takes memory. +La gestión de la memoria en JavaScript se realiza de forma automática e invisible para nosotros. Creamos datos primitivos, objetos, funciones... Todo ello requiere memoria. -What happens when something is not needed any more? How does the JavaScript engine discover it and clean it up? +¿Qué sucede cuando algo no se necesita más? ¿Cómo hace el motor de JavaScript para encontrarlo y limpiarlo? -## Reachability +## Alcance -The main concept of memory management in JavaScript is *reachability*. +El concepto principal del manejo de memoria en JavaScript es *alcance*. -Simply put, "reachable" values are those that are accessible or usable somehow. They are guaranteed to be stored in memory. +Puesto simple, los valores "alcanzables" son aquellos que se pueden acceder o utilizar de alguna manera: Se garantiza que serán conservados en la memoria. -1. There's a base set of inherently reachable values, that cannot be deleted for obvious reasons. +1. Hay un conjunto base de valores inherentemente accesibles, que no se pueden eliminar por razones obvias. + + Por ejemplo: - For instance: + - La función ejecutándose actualmente, sus variables locales y parámetros. + - Otras funciones en la cadena actual de llamadas anidadas, sus variables y parámetros. + - Variables Globales + - (Hay algunos otros internos también) - - The currently executing function, its local variables and parameters. - - Other functions on the current chain of nested calls, their local variables and parameters. - - Global variables. - - (there are some other, internal ones as well) + Estos valores se llaman *raíces*. - These values are called *roots*. +2. Cualquier otro valor se considera accesible si se lo puede alcanzar desde una raíz por una referencia o por una cadena de referencias. -2. Any other value is considered reachable if it's reachable from a root by a reference or by a chain of references. + Por ejemplo, si hay un objeto en una variable global, y ese objeto tiene una propiedad que hace referencia a otro objeto, este objeto también se considera accesible. Y aquellos a los que este objeto hace referencia también son accesibles. Ejemplos detallados a continuación. - For instance, if there's an object in a global variable, and that object has a property referencing another object, *that* object is considered reachable. And those that it references are also reachable. Detailed examples to follow. +Hay un proceso en segundo plano en el motor de JavaScript que se llama [recolector de basura](https://es.wikipedia.org/wiki/Recolector_de_basura). Este proceso monitorea todos los objetos y elimina aquellos que se han vuelto inalcanzables. -There's a background process in the JavaScript engine that is called [garbage collector](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)). It monitors all objects and removes those that have become unreachable. +## Un ejemplo sencillo -## A simple example - -Here's the simplest example: +Aquí va el ejemplo más simple: ```js -// user has a reference to the object +// `user` tiene una referencia al objeto let user = { name: "John" }; @@ -40,9 +40,9 @@ let user = { ![](memory-user-john.svg) -Here the arrow depicts an object reference. The global variable `"user"` references the object `{name: "John"}` (we'll call it John for brevity). The `"name"` property of John stores a primitive, so it's painted inside the object. +Aquí la flecha representa una referencia de objeto. La variable global `"user"` hace referencia al objeto `{name: "John"}` (lo llamaremos John por brevedad). La propiedad `"name"`' de John almacena un dato primitivo, por lo que está pintada dentro del objeto. -If the value of `user` is overwritten, the reference is lost: +Si se sobrescribe el valor de `user`, se pierde la referencia: ```js user = null; @@ -50,14 +50,14 @@ user = null; ![](memory-user-john-lost.svg) -Now John becomes unreachable. There's no way to access it, no references to it. Garbage collector will junk the data and free the memory. +Ahora John se vuelve inalcanzable. No hay forma de acceder a él, no hay referencias a él. El recolector de basura desechará los datos y liberará la memoria. -## Two references +## Dos referencias -Now let's imagine we copied the reference from `user` to `admin`: +Ahora imaginemos que copiamos la referencia de `user` a `admin`: ```js -// user has a reference to the object +// `user` tiene una referencia al objeto let user = { name: "John" }; @@ -69,16 +69,16 @@ let admin = user; ![](memory-user-john-admin.svg) -Now if we do the same: +Ahora si hacemos lo mismo ```js user = null; ``` -...Then the object is still reachable via `admin` global variable, so it must stay in memory. If we overwrite `admin` too, then it can be removed. +... el objeto todavía es accesible a través de la variable global `admin`, por lo que debe quedar en la memoria. Si también sobrescribimos `admin`, entonces se puede eliminar. -## Interlinked objects +## Objetos entrelazados -Now a more complex example. The family: +Ahora un ejemplo más complejo. La familia: ```js function marry(man, woman) { @@ -98,15 +98,15 @@ let family = marry({ }); ``` -Function `marry` "marries" two objects by giving them references to each other and returns a new object that contains them both. +La función `marry` "casa" dos objetos dándoles referencias entre sí y devuelve un nuevo objeto que los contiene a ambos. -The resulting memory structure: +La estructura de memoria resultante: ![](family.svg) -As of now, all objects are reachable. +Por ahora, todos los objetos son accesibles. -Now let's remove two references: +Ahora borremos estas dos referencias: ```js delete family.father; @@ -115,98 +115,98 @@ delete family.mother.husband; ![](family-delete-refs.svg) -It's not enough to delete only one of these two references, because all objects would still be reachable. +No es suficiente eliminar solo una de estas dos referencias, porque todos los objetos aún serían accesibles. -But if we delete both, then we can see that John has no incoming reference any more: +Pero si eliminamos ambos, entonces podemos ver que John ya no tiene referencias entrantes: ![](family-no-father.svg) -Outgoing references do not matter. Only incoming ones can make an object reachable. So, John is now unreachable and will be removed from the memory with all its data that also became unaccessible. +Las referencias salientes no importan. Solo los entrantes pueden hacer que un objeto sea accesible. Entonces, John ahora es inalcanzable y será eliminado de la memoria con todos sus datos que también se volvieron inaccesibles. -After garbage collection: +Después de la recolección de basura: ![](family-no-father-2.svg) -## Unreachable island +## Isla inalcanzable -It is possible that the whole island of interlinked objects becomes unreachable and is removed from the memory. +Es posible que toda la isla de objetos interconectados se vuelva inalcanzable y se elimine de la memoria. -The source object is the same as above. Then: +El objeto fuente es el mismo que el anterior. Entonces: ```js family = null; ``` -The in-memory picture becomes: +La imagen en memoria se convierte en: ![](family-no-family.svg) -This example demonstrates how important the concept of reachability is. +Este ejemplo demuestra cuán importante es el concepto de alcance. -It's obvious that John and Ann are still linked, both have incoming references. But that's not enough. +Es obvio que John y Ann todavía están vinculados, ambos tienen referencias entrantes. Pero eso no es suficiente. -The former `"family"` object has been unlinked from the root, there's no reference to it any more, so the whole island becomes unreachable and will be removed. +El antiguo objeto `"family"` se ha desvinculado de la raíz, ya no se hace referencia a él, por lo que toda la isla se vuelve inalcanzable y se eliminará. -## Internal algorithms +## Algoritmos internos -The basic garbage collection algorithm is called "mark-and-sweep". +El algoritmo básico de recolección de basura se llama "marcar y barrer". -The following "garbage collection" steps are regularly performed: +Los siguientes pasos de "recolección de basura" se realizan regularmente: -- The garbage collector takes roots and "marks" (remembers) them. -- Then it visits and "marks" all references from them. -- Then it visits marked objects and marks *their* references. All visited objects are remembered, so as not to visit the same object twice in the future. -- ...And so on until every reachable (from the roots) references are visited. -- All objects except marked ones are removed. +- El recolector de basura busca las raíces y las "marca" (recuerda). +- Luego visita y "marca" todos los objetos referenciados por ellas. +- Luego visita los objetos marcados y marca *sus* referencias. Todos los objetos visitados son recordados, para no visitar el mismo objeto dos veces en el futuro. +- ...Y así sucesivamente hasta que cada referencia alcanzable (desde las raíces) sean visitadas. +- Todos los objetos que no fueron marcados se eliminan. -For instance, let our object structure look like this: +Por ejemplo, si nuestra estructura de objeto se ve así: ![](garbage-collection-1.svg) -We can clearly see an "unreachable island" to the right side. Now let's see how "mark-and-sweep" garbage collector deals with it. +Podemos ver claramente una "isla inalcanzable" al lado derecho. Ahora veamos cómo el recolector de basura maneja "marcar y barrer". -The first step marks the roots: +El primer paso marca las raíces: ![](garbage-collection-2.svg) -Then we follow their references and mark referenced objects: +Luego se buscan sus referencias salientes y se marcan los objetos referenciados: ![](garbage-collection-3.svg) -...And continue to follow further references, while possible: +... luego se continúa con las referencias salientes de estos objetos, y se continúa marcando mientras sea posible: ![](garbage-collection-4.svg) -Now the objects that could not be visited in the process are considered unreachable and will be removed: +Ahora los objetos que no se pudieron visitar en el proceso se consideran inalcanzables y se eliminarán: ![](garbage-collection-5.svg) -We can also imagine the process as spilling a huge bucket of paint from the roots, that flows through all references and marks all reachable objects. The unmarked ones are then removed. +Podemos imaginar el proceso como derramar un enorme cubo de pintura desde las raíces, que fluye a través de todas las referencias y marca todos los objetos alcanzables. Los elementos que no queden marcados son entonces eliminados. -That's the concept of how garbage collection works. JavaScript engines apply many optimizations to make it run faster and not introduce any delays into the code execution. +Ese es el concepto de cómo funciona la recolección de basura. El motor de JavaScript aplica muchas optimizaciones para que se ejecute más rápido y no introduzca retrasos en la ejecución de código. -Some of the optimizations: +Algunas de las optimizaciones: -- **Generational collection** -- objects are split into two sets: "new ones" and "old ones". In typical code, many objects have a short life span: they appear, do their job and die fast, so it makes sense to track new objects and clear the memory from them if that's the case. Those that survive for long enough, become "old" and are examined less often. -- **Incremental collection** -- if there are many objects, and we try to walk and mark the whole object set at once, it may take some time and introduce visible delays in the execution. So the engine splits the whole set of existing objects into multiple parts. And then clear these parts one after another. There are many small garbage collections instead of a total one. That requires some extra bookkeeping between them to track changes, but we get many tiny delays instead of a big one. -- **Idle-time collection** -- the garbage collector tries to run only while the CPU is idle, to reduce the possible effect on the execution. +- **Recolección generacional** -- los objetos se dividen en dos conjuntos: "nuevos" y "antiguos". En un código típico, muchos objetos tienen corta vida: aparecen, hacen su trabajo y mueren rápido, entonces tiene sentido rastrear los objetos nuevos y eliminarlos de la memoria si corresponde. Aquellos que sobreviven el tiempo suficiente, se vuelven "viejos" y son examinados con menos frecuencia. +- **Recolección incremental** -- Si hay muchos objetos, y tratamos de recorrer y marcar todo el conjunto de objetos a la vez, puede llevar algún tiempo e introducir retrasos notables en la ejecución. Entonces el motor divide la recolección de basura en partes. Luego limpia esas partes, una tras otra. Hay muchas tareas de recolección pequeñas en lugar de una grande. Eso requiere un registro adicional entre ellas para rastrear los cambios, pero tenemos muchos pequeños retrasos en lugar de uno grande. +- **Recolección de tiempo inactivo** -- el recolector de basura trata de ejecutarse solo mientras la CPU está inactiva, para reducir el posible efecto en la ejecución. -There exist other optimizations and flavours of garbage collection algorithms. As much as I'd like to describe them here, I have to hold off, because different engines implement different tweaks and techniques. And, what's even more important, things change as engines develop, so studying deeper "in advance", without a real need is probably not worth that. Unless, of course, it is a matter of pure interest, then there will be some links for you below. +Hay otras optimizaciones y tipos de algoritmos de recolección de basura. Por mucho que quiera describirlos aquí, tengo que evitarlo porque diferentes motores implementan diferentes ajustes y técnicas. Y, lo que es aún más importante, las cosas cambian a medida que se desarrollan los motores, por lo que probablemente no vale la pena profundizar sin una necesidad real. Por supuesto, si tienes verdadero interés, a continuación hay algunos enlaces para ti. -## Summary +## Resumen -The main things to know: +Los principales puntos a saber: -- Garbage collection is performed automatically. We cannot force or prevent it. -- Objects are retained in memory while they are reachable. -- Being referenced is not the same as being reachable (from a root): a pack of interlinked objects can become unreachable as a whole, as we've seen in the example above. +- La recolección de basura se ejecuta automáticamente. No la podemos forzar o evitar. +- Los objetos se retienen en la memoria mientras son accesibles. +- Ser referenciado no es lo mismo que ser accesible (desde una raíz): un conjunto de objetos interconectados pueden volverse inalcanzables como un todo, como vimos en el ejemplo de arriba. -Modern engines implement advanced algorithms of garbage collection. +Los motores modernos implementan algoritmos avanzados de recolección de basura. -A general book "The Garbage Collection Handbook: The Art of Automatic Memory Management" (R. Jones et al) covers some of them. +Un libro general "The Garbage Collection Handbook: The Art of Automatic Memory Management" (R. Jones et al) cubre algunos de ellos. -If you are familiar with low-level programming, more detailed information about V8's garbage collector is in the article [A tour of V8: Garbage Collection](https://jayconrod.com/posts/55/a-tour-of-v8-garbage-collection). +Si estás familiarizado con la programación de bajo nivel, la información más detallada sobre el recolector de basura V8 se encuentra en el artículo [A tour of V8: Garbage Collection](https://jayconrod.com/posts/55/a-tour-of-v8-garbage-collection). -The [V8 blog](https://v8.dev/) also publishes articles about changes in memory management from time to time. Naturally, to learn more about garbage collection, you'd better prepare by learning about V8 internals in general and read the blog of [Vyacheslav Egorov](https://mrale.ph) who worked as one of the V8 engineers. I'm saying: "V8", because it is best covered by articles on the internet. For other engines, many approaches are similar, but garbage collection differs in many aspects. +[V8 blog](https://v8project.blogspot.com/) también publica artículos sobre cambios en la administración de memoria de vez en cuando. Naturalmente, para aprender la recolección de basura, es mejor que se prepare aprendiendo sobre los componentes internos de V8 en general y lea el blog de [Vyacheslav Egorov](https://mrale.ph) que trabajó como uno de los ingenieros de V8. Estoy diciendo: "V8", porque se cubre mejor con artículos en Internet. Para otros motores, muchos enfoques son similares, pero la recolección de basura difiere en muchos aspectos. -In-depth knowledge of engines is good when you need low-level optimizations. It would be wise to plan that as the next step after you're familiar with the language. +Es bueno tener un conocimiento profundo de los motores cuando se necesitan optimizaciones de bajo nivel. Sería prudente planificar eso como el siguiente paso después de que esté familiarizado con el lenguaje. diff --git a/1-js/04-object-basics/03-garbage-collection/garbage-collection-5.svg b/1-js/04-object-basics/03-garbage-collection/garbage-collection-5.svg index 2d85432bc..313954e37 100644 --- a/1-js/04-object-basics/03-garbage-collection/garbage-collection-5.svg +++ b/1-js/04-object-basics/03-garbage-collection/garbage-collection-5.svg @@ -1 +1 @@ -<global>unreachables \ No newline at end of file +<global>inalcanzables \ No newline at end of file diff --git a/1-js/04-object-basics/04-object-methods/4-object-property-this/solution.md b/1-js/04-object-basics/04-object-methods/4-object-property-this/solution.md index f33c9310e..04c5a970e 100644 --- a/1-js/04-object-basics/04-object-methods/4-object-property-this/solution.md +++ b/1-js/04-object-basics/04-object-methods/4-object-property-this/solution.md @@ -1,6 +1,6 @@ -**Answer: an error.** +**Respuesta: un error.** -Try it: +Pruébalo: ```js run function makeUser() { return { @@ -11,29 +11,29 @@ function makeUser() { let user = makeUser(); -alert( user.ref.name ); // Error: Cannot read property 'name' of undefined +alert( user.ref.name ); // Error: No se puede leer la propiedad 'name' de undefined ``` -That's because rules that set `this` do not look at object definition. Only the moment of call matters. +Esto es porque las reglas que establecen el `this` no buscan en la definición del objeto. Solamente importa el momento en que se llama. -Here the value of `this` inside `makeUser()` is `undefined`, because it is called as a function, not as a method with "dot" syntax. +Aquí el valor de `this` dentro de `makeUser()` es `undefined`, porque es llamado como una función, no como un método con sintaxis de punto. -The value of `this` is one for the whole function, code blocks and object literals do not affect it. +El valor de `this` es uno para la función entera; bloques de código y objetos literales no lo afectan. -So `ref: this` actually takes current `this` of the function. +Entonces `ref: this` en realidad toma el `this` actual de la función. -We can rewrite the function and return the same `this` with `undefined` value: +Podemos reescribir la función y devolver el mismo `this` con valor `undefined`: ```js run function makeUser(){ - return this; // this time there's no object literal + return this; // esta vez no hay objeto literal } -alert( makeUser().name ); // Error: Cannot read property 'name' of undefined +alert( makeUser().name ); // Error: No se puede leer la propiedad 'name' de undefined ``` -As you can see the result of `alert( makeUser().name )` is the same as the result of `alert( user.ref.name )` from the previous example. +Como puedes ver el resultado de `alert( makeUser().name )` es el mismo que el resultado de `alert( user.ref.name )` del ejemplo anterior. -Here's the opposite case: +Aquí está el caso opuesto: ```js run function makeUser() { @@ -52,4 +52,4 @@ let user = makeUser(); alert( user.ref().name ); // John ``` -Now it works, because `user.ref()` is a method. And the value of `this` is set to the object before dot `.`. +Ahora funciona, porque `user.ref()` es un método. Y el valor de `this` es establecido al del objeto delante del punto `.`. diff --git a/1-js/04-object-basics/04-object-methods/4-object-property-this/task.md b/1-js/04-object-basics/04-object-methods/4-object-property-this/task.md index c6f8f9658..c3c7b2e47 100644 --- a/1-js/04-object-basics/04-object-methods/4-object-property-this/task.md +++ b/1-js/04-object-basics/04-object-methods/4-object-property-this/task.md @@ -2,11 +2,11 @@ importance: 5 --- -# Using "this" in object literal +# Usando el "this" en un objeto literal -Here the function `makeUser` returns an object. +Aquí la función `makeUser` devuelve un objeto. -What is the result of accessing its `ref`? Why? +¿Cuál es el resultado de acceder a su `ref`? ¿Por qué? ```js function makeUser() { @@ -18,6 +18,5 @@ function makeUser() { let user = makeUser(); -alert( user.ref.name ); // What's the result? +alert( user.ref.name ); // ¿Cuál es el resultado? ``` - diff --git a/1-js/04-object-basics/04-object-methods/7-calculator/task.md b/1-js/04-object-basics/04-object-methods/7-calculator/task.md index 82d0da030..b1a088ff8 100644 --- a/1-js/04-object-basics/04-object-methods/7-calculator/task.md +++ b/1-js/04-object-basics/04-object-methods/7-calculator/task.md @@ -2,17 +2,17 @@ importance: 5 --- -# Create a calculator +# Crea una calculadora -Create an object `calculator` with three methods: +Crea un objeto `calculator` con tres métodos: -- `read()` prompts for two values and saves them as object properties with names `a` and `b` respectively. -- `sum()` returns the sum of saved values. -- `mul()` multiplies saved values and returns the result. +- `read()` pide dos valores y los almacena como propiedades de objeto con nombres `a` y `b`. +- `sum()` devuelve la suma de los valores almacenados. +- `mul()` multiplica los valores almacenados y devuelve el resultado. ```js let calculator = { - // ... your code ... + // ... tu código ... }; calculator.read(); diff --git a/1-js/04-object-basics/04-object-methods/8-chain-calls/solution.md b/1-js/04-object-basics/04-object-methods/8-chain-calls/solution.md index f215461dd..645cf39fe 100644 --- a/1-js/04-object-basics/04-object-methods/8-chain-calls/solution.md +++ b/1-js/04-object-basics/04-object-methods/8-chain-calls/solution.md @@ -1,4 +1,4 @@ -The solution is to return the object itself from every call. +La solución es devolver el objeto mismo desde cada llamado. ```js run demo let ladder = { @@ -26,7 +26,7 @@ let ladder = { ladder.up().up().down().showStep().down().showStep(); // shows 1 then 0 ``` -We also can write a single call per line. For long chains it's more readable: +También podemos escribir una simple llamada por línea. Para cadenas largas es más legible: ```js ladder diff --git a/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md b/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md index 7d2ef8c15..cf8c8a5ed 100644 --- a/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md +++ b/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md @@ -2,9 +2,9 @@ importance: 2 --- -# Chaining +# Encadenamiento -There's a `ladder` object that allows you to go up and down: +Hay un objeto `ladder` que permite subir y bajar: ```js let ladder = { @@ -15,13 +15,13 @@ let ladder = { down() { this.step--; }, - showStep: function() { // shows the current step + showStep: function() { // muestra el peldaño actual alert( this.step ); } }; ``` -Now, if we need to make several calls in sequence, we can do it like this: +Si ahora necesitamos hacer varios llamados en secuencia, podemos hacer algo como esto: ```js ladder.up(); @@ -32,10 +32,10 @@ ladder.down(); ladder.showStep(); // 0 ``` -Modify the code of `up`, `down`, and `showStep` to make the calls chainable, like this: +Modifica el código de "arriba" `up`, "abajo" `down` y "mostrar peldaño" `showStep` para hacer los llamados encadenables. Así: ```js -ladder.up().up().down().showStep().down().showStep(); // shows 1 then 0 +ladder.up().up().down().showStep().down().showStep(); // muestra 1 luego 0 ``` -Such an approach is widely used across JavaScript libraries. +Tal enfoque es ampliamente usado en librerías JavaScript. diff --git a/1-js/04-object-basics/04-object-methods/article.md b/1-js/04-object-basics/04-object-methods/article.md index cea2b6a70..97aa7ce23 100644 --- a/1-js/04-object-basics/04-object-methods/article.md +++ b/1-js/04-object-basics/04-object-methods/article.md @@ -1,6 +1,6 @@ -# Object methods, "this" +# Métodos de objeto, "this" -Objects are usually created to represent entities of the real world, like users, orders and so on: +Los objetos son creados usualmente para representar entidades del mundo real, como usuarios, órdenes, etc.: ```js let user = { @@ -9,13 +9,13 @@ let user = { }; ``` -And, in the real world, a user can *act*: select something from the shopping cart, login, logout etc. +Y en el mundo real un usuario puede *actuar*: seleccionar algo del carrito de compras, hacer login, logout, etc. -Actions are represented in JavaScript by functions in properties. +Estas acciones se implementan asignando funciones a las propiedades del objeto. -## Method examples +## Ejemplos de métodos -For a start, let's teach the `user` to say hello: +Para empezar, enseñemos al usuario `user` a decir hola: ```js run let user = { @@ -25,22 +25,22 @@ let user = { *!* user.sayHi = function() { - alert("Hello!"); + alert("¡Hola!"); }; */!* -user.sayHi(); // Hello! +user.sayHi(); // ¡Hola! ``` -Here we've just used a Function Expression to create a function and assign it to the property `user.sayHi` of the object. +Aquí simplemente usamos una expresión de función para crear la función y asignarla a la propiedad `user.sayHi` del objeto. -Then we can call it as `user.sayHi()`. The user can now speak! +Entonces la llamamos con `user.sayHi()`. ¡El usuario ahora puede hablar! -A function that is a property of an object is called its *method*. +Una función que es la propiedad de un objeto es denominada su *método*. -So, here we've got a method `sayHi` of the object `user`. +Así, aquí tenemos un método `sayHi` del objeto `user`. -Of course, we could use a pre-declared function as a method, like this: +Por supuesto, podríamos usar una función pre-declarada como un método, parecido a esto: ```js run let user = { @@ -48,29 +48,29 @@ let user = { }; *!* -// first, declare +// primero, declara function sayHi() { - alert("Hello!"); -} + alert("¡Hola!"); +}; -// then add as a method +// entonces la agrega como un método user.sayHi = sayHi; */!* -user.sayHi(); // Hello! +user.sayHi(); // ¡Hola! ``` -```smart header="Object-oriented programming" -When we write our code using objects to represent entities, that's called [object-oriented programming](https://en.wikipedia.org/wiki/Object-oriented_programming), in short: "OOP". +```smart header="Programación orientada a objetos" +Cuando escribimos nuestro código usando objetos que representan entidades, eso es llamado [Programación Orientada a Objetos](https://es.wikipedia.org/wiki/Programaci%C3%B3n_orientada_a_objetos), abreviado: "POO". -OOP is a big thing, an interesting science of its own. How to choose the right entities? How to organize the interaction between them? That's architecture, and there are great books on that topic, like "Design Patterns: Elements of Reusable Object-Oriented Software" by E. Gamma, R. Helm, R. Johnson, J. Vissides or "Object-Oriented Analysis and Design with Applications" by G. Booch, and more. +POO (OOP sus siglas en inglés) es una cosa grande, una ciencia interesante en sí misma. ¿Cómo elegir las entidades correctas? ¿Cómo organizar la interacción entre ellas? Eso es arquitectura, y hay muy buenos libros del tópico como "Patrones de diseño: Elementos de software orientado a objetos reutilizable" de E. Gamma, R. Helm, R. Johnson, J. Vissides o "Análisis y Diseño Orientado a Objetos" de G. Booch, y otros. ``` -### Method shorthand +### Formas abreviadas para los métodos -There exists a shorter syntax for methods in an object literal: +Existe una sintaxis más corta para los métodos en objetos literales: ```js -// these objects do the same +// estos objetos hacen lo mismo user = { sayHi: function() { @@ -78,31 +78,31 @@ user = { } }; -// method shorthand looks better, right? +// la forma abreviada se ve mejor, ¿verdad? user = { *!* - sayHi() { // same as "sayHi: function(){...}" + sayHi() { // igual que "sayHi: function(){...}" */!* alert("Hello"); } }; ``` -As demonstrated, we can omit `"function"` and just write `sayHi()`. +Como se demostró, podemos omitir `"function"` y simplemente escribir `sayHi()`. -To tell the truth, the notations are not fully identical. There are subtle differences related to object inheritance (to be covered later), but for now they do not matter. In almost all cases, the shorter syntax is preferred. +A decir verdad, las notaciones no son completamente idénticas. Hay diferencias sutiles relacionadas a la herencia de objetos (por cubrir más adelante) que por ahora no son relevantes. En casi todos los casos la sintaxis abreviada es la preferida. -## "this" in methods +## "this" en métodos -It's common that an object method needs to access the information stored in the object to do its job. +Es común que un método de objeto necesite acceder a la información almacenada en el objeto para cumplir su tarea. -For instance, the code inside `user.sayHi()` may need the name of the `user`. +Por ejemplo, el código dentro de `user.sayHi()` puede necesitar el nombre del usuario `user`. -**To access the object, a method can use the `this` keyword.** +**Para acceder al objeto, un método puede usar la palabra clave `this`.** -The value of `this` is the object "before dot", the one used to call the method. +El valor de `this` es el objeto "antes del punto", el usado para llamar al método. -For instance: +Por ejemplo: ```js run let user = { @@ -111,7 +111,7 @@ let user = { sayHi() { *!* - // "this" is the "current object" + // "this" es el "objeto actual" alert(this.name); */!* } @@ -121,9 +121,9 @@ let user = { user.sayHi(); // John ``` -Here during the execution of `user.sayHi()`, the value of `this` will be `user`. +Aquí durante la ejecución de `user.sayHi()`, el valor de `this` será `user`. -Technically, it's also possible to access the object without `this`, by referencing it via the outer variable: +Técnicamente, también es posible acceder al objeto sin `this`, haciendo referencia a él por medio de la variable externa: ```js let user = { @@ -132,16 +132,16 @@ let user = { sayHi() { *!* - alert(user.name); // "user" instead of "this" + alert(user.name); // "user" en vez de "this" */!* } }; ``` -...But such code is unreliable. If we decide to copy `user` to another variable, e.g. `admin = user` and overwrite `user` with something else, then it will access the wrong object. +...Pero tal código no es confiable. Si decidimos copiar `user` a otra variable, por ejemplo `admin = user` y sobrescribir `user` con otra cosa, entonces accederá al objeto incorrecto. -That's demonstrated below: +Eso queda demostrado en las siguientes lineas: ```js run let user = { @@ -150,7 +150,7 @@ let user = { sayHi() { *!* - alert( user.name ); // leads to an error + alert( user.name ); // lleva a un error */!* } @@ -158,20 +158,20 @@ let user = { let admin = user; -user = null; // overwrite to make things obvious +user = null; // sobrescribimos para hacer las cosas evidentes *!* -admin.sayHi(); // TypeError: Cannot read property 'name' of null +admin.sayHi(); // TypeError: No se puede leer la propiedad 'name' de null */!* ``` -If we used `this.name` instead of `user.name` inside the `alert`, then the code would work. +Si usamos `this.name` en vez de `user.name` dentro de `alert`, entonces el código funciona. -## "this" is not bound +## "this" no es vinculado -In JavaScript, keyword `this` behaves unlike most other programming languages. It can be used in any function, even if it's not a method of an object. +En JavaScript, la palabra clave `this` se comporta de manera distinta a la mayoría de otros lenguajes de programación. Puede ser usado en cualquier función, incluso si no es el método de un objeto. -There's no syntax error in the following example: +No hay error de sintaxis en el siguiente ejemplo: ```js function sayHi() { @@ -179,9 +179,9 @@ function sayHi() { } ``` -The value of `this` is evaluated during the run-time, depending on the context. +El valor de `this` es evaluado durante el tiempo de ejecución, dependiendo del contexto. -For instance, here the same function is assigned to two different objects and has different "this" in the calls: +Por ejemplo, aquí la función es asignada a dos objetos diferentes y tiene diferentes "this" en sus llamados: ```js run let user = { name: "John" }; @@ -192,23 +192,23 @@ function sayHi() { } *!* -// use the same function in two objects +// usa la misma función en dos objetos user.f = sayHi; admin.f = sayHi; */!* -// these calls have different this -// "this" inside the function is the object "before the dot" +// estos llamados tienen diferente "this" +// "this" dentro de la función es el objeto "antes del punto" user.f(); // John (this == user) admin.f(); // Admin (this == admin) -admin['f'](); // Admin (dot or square brackets access the method – doesn't matter) +admin['f'](); // Admin (punto o corchetes para acceder al método, no importa) ``` -The rule is simple: if `obj.f()` is called, then `this` is `obj` during the call of `f`. So it's either `user` or `admin` in the example above. +La regla es simple: si `obj.f()` es llamado, entonces `this` es `obj` durante el llamado de `f`. Entonces es tanto `user` o `admin` en el ejemplo anterior. -````smart header="Calling without an object: `this == undefined`" -We can even call the function without an object at all: +````smart header="Llamado sin un objeto: `this == undefined`" +Podemos incluso llamar la función sin un objeto en absoluto: ```js run function sayHi() { @@ -218,28 +218,28 @@ function sayHi() { sayHi(); // undefined ``` -In this case `this` is `undefined` in strict mode. If we try to access `this.name`, there will be an error. +En este caso `this` es `undefined` en el modo estricto. Si tratamos de acceder a `this.name`, habrá un error. -In non-strict mode the value of `this` in such case will be the *global object* (`window` in a browser, we'll get to it later in the chapter [](info:global-object)). This is a historical behavior that `"use strict"` fixes. +En modo no estricto el valor de `this` en tal caso será el *objeto global* (`window` en un navegador, llegaremos a ello en el capítulo [](info:global-object)). Este es un comportamiento histórico que `"use strict"` corrige. -Usually such call is a programming error. If there's `this` inside a function, it expects to be called in an object context. +Usualmente tal llamado es un error de programa. Si hay `this` dentro de una función, se espera que sea llamada en un contexto de objeto. ```` -```smart header="The consequences of unbound `this`" -If you come from another programming language, then you are probably used to the idea of a "bound `this`", where methods defined in an object always have `this` referencing that object. +```smart header="Las consecuencias de un `this` desvinculado" +Si vienes de otro lenguaje de programación, probablemente estés habituado a la idea de un "`this` vinculado", donde los método definidos en un objeto siempre tienen `this` referenciando ese objeto. -In JavaScript `this` is "free", its value is evaluated at call-time and does not depend on where the method was declared, but rather on what object is "before the dot". +En JavaScript `this` es "libre", su valor es evaluado al momento de su llamado y no depende de dónde fue declarado el método sino de cuál es el objeto "delante del punto". -The concept of run-time evaluated `this` has both pluses and minuses. On the one hand, a function can be reused for different objects. On the other hand, the greater flexibility creates more possibilities for mistakes. +El concepto de `this` evaluado en tiempo de ejecución tiene sus pros y sus contras. Por un lado, una función puede ser reusada por diferentes objetos. Por otro, la mayor flexibilidad crea más posibilidades para equivocaciones. -Here our position is not to judge whether this language design decision is good or bad. We'll understand how to work with it, how to get benefits and avoid problems. +Nuestra posición no es juzgar si la decisión del diseño de lenguaje es buena o mala. Vamos a entender cómo trabajar con ello, obtener beneficios y evitar problemas. ``` -## Arrow functions have no "this" +## Las funciones de flecha no tienen "this" -Arrow functions are special: they don't have their "own" `this`. If we reference `this` from such a function, it's taken from the outer "normal" function. +Las funciones de flecha son especiales: ellas no tienen su "propio" `this`. Si nosotros hacemos referencia a `this` desde tales funciones, esta será tomada desde afuera de la función "normal". -For instance, here `arrow()` uses `this` from the outer `user.sayHi()` method: +Por ejemplo, aquí `arrow()` usa `this` desde fuera del método `user.sayHi()`: ```js run let user = { @@ -253,18 +253,18 @@ let user = { user.sayHi(); // Ilya ``` -That's a special feature of arrow functions, it's useful when we actually do not want to have a separate `this`, but rather to take it from the outer context. Later in the chapter we'll go more deeply into arrow functions. +Esto es una característica especial de las funciones de flecha, útil cuando no queremos realmente un `this` separado sino tomarlo de un contexto externo. Más adelante en el capítulo las trataremos en profundidad. -## Summary +## Resumen -- Functions that are stored in object properties are called "methods". -- Methods allow objects to "act" like `object.doSomething()`. -- Methods can reference the object as `this`. +- Las funciones que son almacenadas en propiedades de objeto son llamadas "métodos". +- Los método permiten a los objetos "actuar", como `object.doSomething()`. +- Los métodos pueden hacer referencia al objeto con `this`. -The value of `this` is defined at run-time. -- When a function is declared, it may use `this`, but that `this` has no value until the function is called. -- A function can be copied between objects. -- When a function is called in the "method" syntax: `object.method()`, the value of `this` during the call is `object`. +El valor de `this` es definido en tiempo de ejecución. +- Cuando una función es declarada, puede usar `this`, pero ese `this` no tiene valor hasta que la función es llamada. +- Una función puede ser copiada entre objetos. +- Cuando una función es llamada en la sintaxis de método: `object.method()`, el valor de `this` durante el llamado es `object`. -Please note that arrow functions are special: they have no `this`. When `this` is accessed inside an arrow function, it is taken from outside. +Ten en cuenta que las funciones de flecha son especiales: ellas no tienen `this`. Cuando `this` es accedido dentro de una función de flecha, su valor es tomado desde el exterior. diff --git a/1-js/04-object-basics/06-constructor-new/1-two-functions-one-object/solution.md b/1-js/04-object-basics/06-constructor-new/1-two-functions-one-object/solution.md index 7d8edd7ca..59f037447 100644 --- a/1-js/04-object-basics/06-constructor-new/1-two-functions-one-object/solution.md +++ b/1-js/04-object-basics/06-constructor-new/1-two-functions-one-object/solution.md @@ -1,8 +1,8 @@ -Yes, it's possible. +Si, es posible. -If a function returns an object then `new` returns it instead of `this`. +Si una función devuelve un objeto, entonces `new` lo devuelve en vez de `this`. -So they can, for instance, return the same externally defined object `obj`: +Por lo tanto pueden, por ejemplo, devolver el mismo objeto definido externamente `obj`: ```js run no-beautify let obj = {}; diff --git a/1-js/04-object-basics/06-constructor-new/1-two-functions-one-object/task.md b/1-js/04-object-basics/06-constructor-new/1-two-functions-one-object/task.md index e932a201a..7c6b62059 100644 --- a/1-js/04-object-basics/06-constructor-new/1-two-functions-one-object/task.md +++ b/1-js/04-object-basics/06-constructor-new/1-two-functions-one-object/task.md @@ -2,9 +2,9 @@ importance: 2 --- -# Two functions – one object +# Dos funciones – un objeto -Is it possible to create functions `A` and `B` so that `new A() == new B()`? +¿Es posible crear las funciones `A` y `B` para que se cumpla `new A() == new B()`? ```js no-beautify function A() { ... } @@ -16,4 +16,4 @@ let b = new B(); alert( a == b ); // true ``` -If it is, then provide an example of their code. +Si es posible, entonces proporcione un ejemplo de su código. diff --git a/1-js/04-object-basics/06-constructor-new/2-calculator-constructor/_js.view/test.js b/1-js/04-object-basics/06-constructor-new/2-calculator-constructor/_js.view/test.js index bba80e5c2..2752461ab 100644 --- a/1-js/04-object-basics/06-constructor-new/2-calculator-constructor/_js.view/test.js +++ b/1-js/04-object-basics/06-constructor-new/2-calculator-constructor/_js.view/test.js @@ -16,11 +16,11 @@ describe("calculator", function() { assert.equal(calculator.b, 3); }); - it("when 2 and 3 are entered, the sum is 5", function() { + it("cuando se ingresa 2 y 3, la suma es 5", function() { assert.equal(calculator.sum(), 5); }); - it("when 2 and 3 are entered, the product is 6", function() { + it("cuando se ingresa 2 y 3, el producto es 6", function() { assert.equal(calculator.mul(), 6); }); diff --git a/1-js/04-object-basics/06-constructor-new/2-calculator-constructor/task.md b/1-js/04-object-basics/06-constructor-new/2-calculator-constructor/task.md index c862bec40..df59c55e6 100644 --- a/1-js/04-object-basics/06-constructor-new/2-calculator-constructor/task.md +++ b/1-js/04-object-basics/06-constructor-new/2-calculator-constructor/task.md @@ -2,15 +2,15 @@ importance: 5 --- -# Create new Calculator +# Crear nueva Calculadora -Create a constructor function `Calculator` that creates objects with 3 methods: +Crear una función constructora `Calculator` que crea objetos con 3 métodos: -- `read()` prompts for two values and saves them as object properties with names `a` and `b` respectively. -- `sum()` returns the sum of these properties. -- `mul()` returns the multiplication product of these properties. +- `read()` pide dos valores usando `prompt` y los guarda en las propiedades del objeto con los nombres `a` y `b`. +- `sum()` devuelve la suma de estas propiedades. +- `mul()` devuelve el producto de la multiplicación de estas propiedades. -For instance: +Por ejemplo: ```js let calculator = new Calculator(); diff --git a/1-js/04-object-basics/06-constructor-new/3-accumulator/_js.view/test.js b/1-js/04-object-basics/06-constructor-new/3-accumulator/_js.view/test.js index a719cf45c..61fe29bbb 100644 --- a/1-js/04-object-basics/06-constructor-new/3-accumulator/_js.view/test.js +++ b/1-js/04-object-basics/06-constructor-new/3-accumulator/_js.view/test.js @@ -8,23 +8,23 @@ describe("Accumulator", function() { prompt.restore(); }); - it("initial value is the argument of the constructor", function() { + it("valor inicial es el argumento del constructor", function() { let accumulator = new Accumulator(1); assert.equal(accumulator.value, 1); }); - it("after reading 0, the value is 1", function() { + it("después de leer 0, el valor es 1", function() { let accumulator = new Accumulator(1); prompt.returns("0"); accumulator.read(); assert.equal(accumulator.value, 1); }); - it("after reading 1, the value is 2", function() { + it("después de leer 1, el valor es 2", function() { let accumulator = new Accumulator(1); prompt.returns("1"); accumulator.read(); assert.equal(accumulator.value, 2); }); -}); +}); \ No newline at end of file diff --git a/1-js/04-object-basics/06-constructor-new/3-accumulator/solution.md b/1-js/04-object-basics/06-constructor-new/3-accumulator/solution.md index eb145e79d..8d1dfc5ca 100644 --- a/1-js/04-object-basics/06-constructor-new/3-accumulator/solution.md +++ b/1-js/04-object-basics/06-constructor-new/3-accumulator/solution.md @@ -5,7 +5,7 @@ function Accumulator(startingValue) { this.value = startingValue; this.read = function() { - this.value += +prompt('How much to add?', 0); + this.value += +prompt('Cuánto más agregar?', 0); }; } diff --git a/1-js/04-object-basics/06-constructor-new/3-accumulator/task.md b/1-js/04-object-basics/06-constructor-new/3-accumulator/task.md index c2c44881e..799c6286e 100644 --- a/1-js/04-object-basics/06-constructor-new/3-accumulator/task.md +++ b/1-js/04-object-basics/06-constructor-new/3-accumulator/task.md @@ -2,26 +2,26 @@ importance: 5 --- -# Create new Accumulator +# Crear nuevo Acumulador -Create a constructor function `Accumulator(startingValue)`. +Crear una función constructor `Accumulator(startingValue)`. -Object that it creates should: +El objeto que crea debería: -- Store the "current value" in the property `value`. The starting value is set to the argument of the constructor `startingValue`. -- The `read()` method should use `prompt` to read a new number and add it to `value`. +- Almacene el "valor actual" en la propiedad `value`. El valor inicial se establece en el argumento del constructor `startingValue`. +- El método `read()` debe usar `prompt` para leer un nuevo número y agregarlo a `value`. -In other words, the `value` property is the sum of all user-entered values with the initial value `startingValue`. +En otras palabras, la propiedad `value` es la suma de todos los valores ingresados por el usuario con el valor inicial `startingValue`. -Here's the demo of the code: +Aquí está la demostración del código: ```js -let accumulator = new Accumulator(1); // initial value 1 +let accumulator = new Accumulator(1); // valor inicial 1 -accumulator.read(); // adds the user-entered value -accumulator.read(); // adds the user-entered value +accumulator.read(); // agrega el valor introducido por el usuario +accumulator.read(); // agrega el valor introducido por el usuario -alert(accumulator.value); // shows the sum of these values +alert(accumulator.value); // muestra la suma de estos valores ``` [demo] diff --git a/1-js/04-object-basics/06-constructor-new/article.md b/1-js/04-object-basics/06-constructor-new/article.md index a335464f1..35154b981 100644 --- a/1-js/04-object-basics/06-constructor-new/article.md +++ b/1-js/04-object-basics/06-constructor-new/article.md @@ -1,17 +1,17 @@ -# Constructor, operator "new" +# Constructor, operador "new" -The regular `{...}` syntax allows us to create one object. But often we need to create many similar objects, like multiple users or menu items and so on. +El sintaxis habitual `{...}` nos permite crear un objeto. Pero a menudo necesitamos crear varios objetos similares, como múltiples usuarios, elementos de menú, etcétera. -That can be done using constructor functions and the `"new"` operator. +Esto se puede realizar utilizando el constructor de funciones y el operador `"new"`. -## Constructor function +## Función constructora -Constructor functions technically are regular functions. There are two conventions though: +La función constructora es técnicamente una función normal. Aunque hay dos convenciones: -1. They are named with capital letter first. -2. They should be executed only with `"new"` operator. +1. Son nombradas con la primera letra mayúscula. +2. Sólo deben ejecutarse con el operador `"new"`. -For instance: +Por ejemplo: ```js run function User(name) { @@ -27,31 +27,31 @@ alert(user.name); // Jack alert(user.isAdmin); // false ``` -When a function is executed with `new`, it does the following steps: +Cuando una función es ejecutada con `new`, realiza los siguientes pasos: -1. A new empty object is created and assigned to `this`. -2. The function body executes. Usually it modifies `this`, adds new properties to it. -3. The value of `this` is returned. +1. Se crea un nuevo objeto vacío y se asigna a `this`. +2. Se ejecuta el cuerpo de la función. Normalmente se modifica `this` y se le agrega nuevas propiedades. +3. Se devuelve el valor de `this`. -In other words, `new User(...)` does something like: +En otras palabras, `new User(...)` realiza algo como: ```js function User(name) { *!* - // this = {}; (implicitly) + // this = {}; (implícitamente) */!* - // add properties to this + // agrega propiedades a this this.name = name; this.isAdmin = false; *!* - // return this; (implicitly) + // return this; (implícitamente) */!* } ``` -So `let user = new User("Jack")` gives the same result as: +Entonces `let user = new User("Jack")` da el mismo resultado que: ```js let user = { @@ -60,149 +60,149 @@ let user = { }; ``` -Now if we want to create other users, we can call `new User("Ann")`, `new User("Alice")` and so on. Much shorter than using literals every time, and also easy to read. +Ahora si queremos crear otros usuarios, podemos llamar a `new User("Ann")`, `new User("Alice")`, etcétera. Mucho más corto que usar literales todo el tiempo y también fácil de leer. -That's the main purpose of constructors -- to implement reusable object creation code. +Este es el principal propósito del constructor -- implementar código de creación de objetos re-utilizables. -Let's note once again -- technically, any function (except arrow functions, as they don't have `this`) can be used as a constructor. It can be run with `new`, and it will execute the algorithm above. The "capital letter first" is a common agreement, to make it clear that a function is to be run with `new`. +Tomemos nota otra vez: técnicamente cualquier función (excepto las de flecha pues no tienen this) puede ser utilizada como constructor. Puede ser llamada con `new`, y ejecutará el algoritmo de arriba. La "primera letra mayúscula" es un acuerdo general, para dejar en claro que la función debe ser ejecutada con `new`. ````smart header="new function() { ... }" -If we have many lines of code all about creation of a single complex object, we can wrap them in an immediately called constructor function, like this: +Si tenemos muchas líneas de código todas sobre la creación de un único objeto complejo, podemos agruparlas en un constructor de función que es llamado inmediatamente de esta manera: ```js -// create a function and immediately call it with new +// crea una función e inmediatamente la llama con new let user = new function() { this.name = "John"; this.isAdmin = false; - // ...other code for user creation - // maybe complex logic and statements - // local variables etc + // ...otro código para creación de usuario + // tal vez lógica compleja y sentencias + // variables locales etc }; ``` -This constructor can't be called again, because it is not saved anywhere, just created and called. So this trick aims to encapsulate the code that constructs the single object, without future reuse. +Este constructor no puede ser llamado de nuevo porque no es guardado en ninguna parte, sólo es creado y llamado. Por lo tanto este truco apunta a encapsular el código que construye el objeto individual, sin reutilización futura. ```` -## Constructor mode test: new.target +## Constructor modo test: new.target -```smart header="Advanced stuff" -The syntax from this section is rarely used, skip it unless you want to know everything. +```smart header="Temas avanzados" +La sintaxis de esta sección es raramente utilizada, puedes omitirla a menos que quieras saber todo. ``` -Inside a function, we can check whether it was called with `new` or without it, using a special `new.target` property. +Dentro de una función, podemos verificar si ha sido llamada con o sin el `new` utilizando una propiedad especial: `new.target`. -It is undefined for regular calls and equals the function if called with `new`: +En las llamadas normales devuelve `undefined`, y cuando es llamada con `new` devuelve la función: ```js run function User() { alert(new.target); } -// without "new": +// sin "new": *!* User(); // undefined */!* -// with "new": +// con "new": *!* new User(); // function User { ... } */!* ``` -That can be used inside the function to know whether it was called with `new`, "in constructor mode", or without it, "in regular mode". +Esto puede ser utilizado dentro de la función para conocer si ha sido llamada con `new`, "en modo constructor "; o sin él, "en modo regular". -We can also make both `new` and regular calls to do the same, like this: +También podemos hacer que ambas formas de llamarla, con `new` y "regular", realicen lo mismo: ```js run function User(name) { - if (!new.target) { // if you run me without new - return new User(name); // ...I will add new for you + if (!new.target) { // si me ejecutas sin new + return new User(name); // ...Agregaré new por ti } this.name = name; } -let john = User("John"); // redirects call to new User +let john = User("John"); // redirige llamado a new User alert(john.name); // John ``` -This approach is sometimes used in libraries to make the syntax more flexible. So that people may call the function with or without `new`, and it still works. +Este enfoque es utilizado a veces en las librerías para hacer el sintaxis más flexible. Así la gente puede llamar a la función con o sin `new` y aún funciona. -Probably not a good thing to use everywhere though, because omitting `new` makes it a bit less obvious what's going on. With `new` we all know that the new object is being created. +Sin embargo, probablemente no sea algo bueno para usar en todas partes, porque omitir `new` hace que sea un poco menos obvio lo que está sucediendo. Con `new` todos sabemos que se está creando el nuevo objeto. -## Return from constructors +## Return desde constructores -Usually, constructors do not have a `return` statement. Their task is to write all necessary stuff into `this`, and it automatically becomes the result. +Normalmente, los constructores no tienen una sentencia `return`. Su tarea es escribir todo lo necesario al `this`, y automáticamente este se convierte en el resultado. -But if there is a `return` statement, then the rule is simple: +Pero si hay una sentencia `return`, entonces la regla es simple: -- If `return` is called with an object, then the object is returned instead of `this`. -- If `return` is called with a primitive, it's ignored. +- Si `return` es llamado con un objeto, entonces se devuelve tal objeto en vez de `this`. +- Si `return` es llamado con un tipo de dato primitivo, es ignorado. -In other words, `return` with an object returns that object, in all other cases `this` is returned. +En otras palabras, `return` con un objeto devuelve ese objeto, en todos los demás casos se devuelve `this`. -For instance, here `return` overrides `this` by returning an object: +Por ejemplo, aquí `return` anula `this` al devolver un objeto: ```js run function BigUser() { this.name = "John"; - return { name: "Godzilla" }; // <-- returns this object + return { name: "Godzilla" }; // <-- devuelve este objeto } -alert( new BigUser().name ); // Godzilla, got that object +alert( new BigUser().name ); // Godzilla, recibió ese objeto ``` -And here's an example with an empty `return` (or we could place a primitive after it, doesn't matter): +Y aquí un ejemplo con un `return` vacío (o podemos colocar un primitivo después de él, no importa): ```js run function SmallUser() { this.name = "John"; - return; // <-- returns this + return; // <-- devuelve this } alert( new SmallUser().name ); // John ``` -Usually constructors don't have a `return` statement. Here we mention the special behavior with returning objects mainly for the sake of completeness. +Normalmente los constructores no tienen una sentencia `return`. Aquí mencionamos el comportamiento especial con devolución de objetos principalmente por el bien de la integridad. -````smart header="Omitting parentheses" -By the way, we can omit parentheses after `new`: +````smart header="Omitir paréntesis" +Por cierto, podemos omitir paréntesis después de `new`: ```js -let user = new User; // <-- no parentheses -// same as +let user = new User; // <-- sin paréntesis +// lo mismo que let user = new User(); ``` -Omitting parentheses here is not considered a "good style", but the syntax is permitted by specification. +Omitir paréntesis aquí no se considera "buen estilo", pero la especificación permite esa sintaxis. ```` -## Methods in constructor +## Métodos en constructor -Using constructor functions to create objects gives a great deal of flexibility. The constructor function may have parameters that define how to construct the object, and what to put in it. +Utilizar constructor de funciones para crear objetos nos da mucha flexibilidad. La función constructor puede tener argumentos que definan cómo construir el objeto y qué colocar dentro. -Of course, we can add to `this` not only properties, but methods as well. +Por supuesto podemos agregar a `this` no sólo propiedades, sino también métodos. -For instance, `new User(name)` below creates an object with the given `name` and the method `sayHi`: +Por ejemplo, `new User(name)` de abajo, crea un objeto con el `name` dado y el método `sayHi`: ```js run function User(name) { this.name = name; this.sayHi = function() { - alert( "My name is: " + this.name ); + alert( "Mi nombre es: " + this.name ); }; } *!* let john = new User("John"); -john.sayHi(); // My name is: John +john.sayHi(); // Mi nombre es: John */!* /* @@ -213,19 +213,19 @@ john = { */ ``` -To create complex objects, there's a more advanced syntax, [classes](info:classes), that we'll cover later. +Para crear objetos complejos existe una sintaxis más avanzada, [classes](info:classes), que cubriremos más adelante. -## Summary +## Resumen -- Constructor functions or, briefly, constructors, are regular functions, but there's a common agreement to name them with capital letter first. -- Constructor functions should only be called using `new`. Such a call implies a creation of empty `this` at the start and returning the populated one at the end. +- Las funciones Constructoras o, más corto, constructores, son funciones normales, pero existe un común acuerdo para nombrarlas con la primera letra en mayúscula. +- Las funciones Constructoras sólo deben ser llamadas utilizando `new`. Tal llamado implica la creación de un `this` vacío al comienzo y devolver el `this` rellenado al final. -We can use constructor functions to make multiple similar objects. +Podemos utilizar funciones constructoras para crear múltiples objetos similares. -JavaScript provides constructor functions for many built-in language objects: like `Date` for dates, `Set` for sets and others that we plan to study. +JavaScript proporciona funciones constructoras para varios objetos de lenguaje incorporados: como `Date` para fechas, `Set` para conjuntos y otros que planeamos estudiar. -```smart header="Objects, we'll be back!" -In this chapter we only cover the basics about objects and constructors. They are essential for learning more about data types and functions in the next chapters. +```smart header="Objetos, ¡volveremos!" +En este capítulo solo cubrimos los conceptos básicos sobre objetos y constructores. Son esenciales para aprender más sobre tipos de datos y funciones en los próximos capítulos. -After we learn that, we return to objects and cover them in-depth in the chapters and . +Después de aprender aquello, volvemos a los objetos y los cubrimos en profundidad en los capítulos y . ``` diff --git a/1-js/04-object-basics/07-optional-chaining/article.md b/1-js/04-object-basics/07-optional-chaining/article.md index 4c6029423..b5db6063e 100644 --- a/1-js/04-object-basics/07-optional-chaining/article.md +++ b/1-js/04-object-basics/07-optional-chaining/article.md @@ -1,112 +1,112 @@ -# Optional chaining '?.' +# Encadenamiento opcional '?.' [recent browser="new"] -The optional chaining `?.` is a safe way to access nested object properties, even if an intermediate property doesn't exist. +El encadenamiento opcional `?.` es una forma a prueba de errores para acceder a las propiedades anidadas de los objetos, incluso si no existe una propiedad intermedia. -## The "non-existing property" problem +## El problema de la propiedad que no existe -If you've just started to read the tutorial and learn JavaScript, maybe the problem hasn't touched you yet, but it's quite common. +Si acaba de comenzar a leer el tutorial y aprender JavaScript, quizás el problema aún no lo haya tocado, pero es bastante común. -As an example, let's say we have `user` objects that hold the information about our users. +Como ejemplo, digamos que tenemos objetos `user` que contienen información de nuestros usuarios. -Most of our users have addresses in `user.address` property, with the street `user.address.street`, but some did not provide them. +La mayoría de nuestros usuarios tienen la dirección en la propiedad `user.address`, con la calle en `user.address.street`, pero algunos no la proporcionaron. -In such case, when we attempt to get `user.address.street`, and the user happens to be without an address, we get an error: +En tal caso, cuando intentamos obtener `user.address.street`en un usuario sin dirección obtendremos un error: ```js run -let user = {}; // a user without "address" property +let user = {}; // usuario sin propiedad "address" alert(user.address.street); // Error! ``` -That's the expected result. JavaScript works like this. As `user.address` is `undefined`, an attempt to get `user.address.street` fails with an error. +Este es el resultado esperado. JavaScript funciona así, como `user.address` es `undefined`, el intento de obtener `user.address.street` falla dando un error. -In many practical cases we'd prefer to get `undefined` instead of an error here (meaning "no street"). +En muchos casos prácticos preferiríamos obtener `undefined` en lugar del error (dando a entender "sin calle") -...and another example. In Web development, we can get an object that corresponds to a web page element using a special method call, such as `document.querySelector('.elem')`, and it returns `null` when there's no such element. +... y otro ejemplo. En desarrollo web, podemos obtener un objeto que corresponde a un elemento de página web usando el llamado a un método especial como `document.querySelector('.elem')`, que devuelve `null` cuando no existe tal elemento. ```js run -// document.querySelector('.elem') is null if there's no element -let html = document.querySelector('.elem').innerHTML; // error if it's null +// Error si el resultado de querySelector (...) es null +let html = document.querySelector('.my-element').innerHTML; ``` -Once again, if the element doesn't exist, we'll get an error accessing `.innerHTML` property of `null`. And in some cases, when the absence of the element is normal, we'd like to avoid the error and just accept `html = null` as the result. +Una vez más, si el elemento no existe, obtendremos un error al intentar acceder a la propiedad `.innerHTML` de `null`. Y en algunos casos, cuando la ausencia del elemento es normal, quisiéramos evitar el error y simplemente aceptar `html = null` como resultado. -How can we do this? +¿Cómo podemos hacer esto? -The obvious solution would be to check the value using `if` or the conditional operator `?`, before accessing its property, like this: +La solución obvia sería chequear el valor usando `if` o el operador condicional `?` antes de usar la propiedad: -```js +```js let user = {}; alert(user.address ? user.address.street : undefined); ``` -It works, there's no error... But it's quite inelegant. As you can see, the `"user.address"` appears twice in the code. +Esto funciona, no hay error... Pero es bastante poco elegante. Como puedes ver, `"user.address"` aparece dos veces en el código. -Here's how the same would look for `document.querySelector`: +El mismo caso, pero con la búsqueda de `document.querySelector`: ```js run let html = document.querySelector('.elem') ? document.querySelector('.elem').innerHTML : null; ``` -We can see that the element search `document.querySelector('.elem')` is actually called twice here. Not good. +Podemos ver que el elemento de búsqueda `document.querySelector('.elem')` es llamado dos veces aquí. Nada bueno. -For more deeply nested properties, it becomes even uglier, as more repetitions are required. +En propiedades anidadas más profundamente, esto se vuelve un problema porque se requerirán más repeticiones. -E.g. let's get `user.address.street.name` in a similar fashion. +Ejemplo: Tratemos de obtener `user.address.street.name` de manera similar. ```js -let user = {}; // user has no address +let user = {}; // El usuario no tiene dirección alert(user.address ? user.address.street ? user.address.street.name : null : null); ``` -That's just awful, one may even have problems understanding such code. +Esto es horrible, podemos tener problemas para siquiera entender tal código. -There's a little better way to write it, using the `&&` operator: +Hay una mejor manera de escribirlo, usando el operador `&&`: ```js run -let user = {}; // user has no address +let user = {}; // usuario sin dirección -alert( user.address && user.address.street && user.address.street.name ); // undefined (no error) +alert( user.address && user.address.street && user.address.street.name ); // undefined (sin error) ``` -AND'ing the whole path to the property ensures that all components exist (if not, the evaluation stops), but also isn't ideal. +Poniendo AND en el camino completo a la propiedad asegura que todos los componentes existen (si no, la evaluación se detiene), pero no es lo ideal. -As you can see, property names are still duplicated in the code. E.g. in the code above, `user.address` appears three times. +Como puedes ver, los nombres de propiedad aún están duplicados en el código. Por ejemplo en el código de arriba `user.address` aparece tres veces. -That's why the optional chaining `?.` was added to the language. To solve this problem once and for all! +Es por ello que el encadenamiento opcional `?.` fue agregado al lenguaje. ¡Para resolver este problema de una vez por todas! -## Optional chaining +## Encadenamiento opcional -The optional chaining `?.` stops the evaluation if the value before `?.` is `undefined` or `null` and returns `undefined`. +El encadenamiento opcional `?.` detiene la evaluación y devuelve `undefined` si el valor antes del `?.` es `undefined` o `null`. -**Further in this article, for brevity, we'll be saying that something "exists" if it's not `null` and not `undefined`.** +**De aquí en adelante en este artículo, por brevedad, diremos que algo "existe" si no es `null` o `undefined`.** -In other words, `value?.prop`: -- works as `value.prop`, if `value` exists, -- otherwise (when `value` is `undefined/null`) it returns `undefined`. +En otras palabras, `value?.prop`: +- funciona como `value.prop` si `value` existe, +- de otro modo (cuando `value` es `undefined/null`) devuelve `undefined`. -Here's the safe way to access `user.address.street` using `?.`: +Aquí está la forma segura de acceder a `user.address.street` usando `?.`: ```js run -let user = {}; // user has no address +let user = {}; // El usuario no tiene dirección -alert( user?.address?.street ); // undefined (no error) +alert( user?.address?.street ); // undefined (no hay error) ``` -The code is short and clean, there's no duplication at all. +El código es corto y claro, no hay duplicación en absoluto -Here's an example with `document.querySelector`: +Aquí tenemos un ejemplo con `document.querySelector`: ```js run -let html = document.querySelector('.elem')?.innerHTML; // will be undefined, if there's no element +let html = document.querySelector('.elem')?.innerHTML; // será undefined si no existe el elemento ``` -Reading the address with `user?.address` works even if `user` object doesn't exist: +Leer la dirección con `user?.Address` funciona incluso si el objeto `user` no existe: ```js run let user = null; @@ -115,52 +115,52 @@ alert( user?.address ); // undefined alert( user?.address.street ); // undefined ``` -Please note: the `?.` syntax makes optional the value before it, but not any further. +Tenga en cuenta: la sintaxis `?.` hace opcional el valor delante de él, pero no más allá. -E.g. in `user?.address.street.name` the `?.` allows `user` to safely be `null/undefined` (and returns `undefined` in that case), but that's only for `user`. Further properties are accessed in a regular way. If we want some of them to be optional, then we'll need to replace more `.` with `?.`. +Por ejemplo, en `user?.address.street.name`, el `?.` permite que `user` sea `null/undefined` (y devuelve `undefined` en tal caso), pero solo a `user`. El resto de las propiedades son accedidas de la manera normal. Si queremos que algunas de ellas sean opcionales, necesitamos reemplazar más `.` con `?.`. -```warn header="Don't overuse the optional chaining" -We should use `?.` only where it's ok that something doesn't exist. +```warn header="No abuses del encadenamiento opcional" +Deberíamos usar `?.` solo donde está bien que algo no exista. -For example, if according to our code logic `user` object must exist, but `address` is optional, then we should write `user.address?.street`, but not `user?.address?.street`. +Por ejemplo, si de acuerdo con la lógica de nuestro código, el objeto `user` debe existir, pero `address` es opcional, entonces deberíamos escribir `user.address?.street` y no `user?.address?.street`. -Then, if `user` happens to be undefined, we'll see a programming error about it and fix it. Otherwise, if we overuse `?.`, coding errors can be silenced where not appropriate, and become more difficult to debug. +De esta forma, si por un error `user` no está definido, lo sabremos y lo arreglaremos. De lo contrario, los errores de codificación pueden silenciarse donde no sea apropiado y volverse más difíciles de depurar. ``` -````warn header="The variable before `?.` must be declared" -If there's no variable `user` at all, then `user?.anything` triggers an error: +````warn header="La variable antes de `?.` debe declararse" +Si no hay una variable `user` declarada, entonces `user?.anything` provocará un error: ```js run -// ReferenceError: user is not defined +// ReferenceError: user no está definido user?.address; ``` -The variable must be declared (e.g. `let/const/var user` or as a function parameter). The optional chaining works only for declared variables. +La variable debe ser declarada (con `let/const/var user` o como parámetro de función). El encadenamiento opcional solo funciona para variables declaradas. ```` -## Short-circuiting +## Short-circuiting (Cortocircuitos) -As it was said before, the `?.` immediately stops ("short-circuits") the evaluation if the left part doesn't exist. +Como se dijo antes, el `?.` detiene inmediatamente ("cortocircuito") la evaluación si la parte izquierda no existe. -So, if there are any further function calls or operations to the right of `?.`, they won't be made. +Entonces, si a la derecha de `?.` hay funciones u operaciones adicionales, estas no se ejecutarán: -For instance: +Por ejemplo: ```js run let user = null; let x = 0; -user?.sayHi(x++); // no "user", so the execution doesn't reach sayHi call and x++ +user?.sayHi(x++); // no hay "user", por lo que la ejecución no alcanza a sayHi ni a x++ -alert(x); // 0, value not incremented +alert(x); // 0, el valor no se incrementa ``` -## Other variants: ?.(), ?.[] +## Otros casos: ?.(), ?.[] -The optional chaining `?.` is not an operator, but a special syntax construct, that also works with functions and square brackets. +El encadenamiento opcional `?.` no es un operador, es una construcción de sintaxis especial que también funciona con funciones y corchetes. -For example, `?.()` is used to call a function that may not exist. +Por ejemplo, `?.()` se usa para llamar a una función que puede no existir. -In the code below, some of our users have `admin` method, and some don't: +En el siguiente código, algunos de nuestros usuarios tienen el método `admin`, y otros no: ```js run let userAdmin = { @@ -176,15 +176,15 @@ userAdmin.admin?.(); // I am admin */!* *!* -userGuest.admin?.(); // nothing happens (no such method) +userGuest.admin?.(); // no pasa nada (no existe tal método) */!* ``` -Here, in both lines we first use the dot (`userAdmin.admin`) to get `admin` property, because we assume that the `user` object exists, so it's safe read from it. +Aquí, en ambas líneas, primero usamos el punto (`userAdmin.admin`) para obtener la propiedad `admin`, porque asumimos que el objeto user existe y es seguro leerlo. -Then `?.()` checks the left part: if the `admin` function exists, then it runs (that's so for `userAdmin`). Otherwise (for `userGuest`) the evaluation stops without errors. +Entonces `?.()` comprueba la parte izquierda: si la función `admin` existe, entonces se ejecuta (para `userAdmin`). De lo contrario (para `userGuest`) la evaluación se detiene sin errores. -The `?.[]` syntax also works, if we'd like to use brackets `[]` to access properties instead of dot `.`. Similar to previous cases, it allows to safely read a property from an object that may not exist. +La sintaxis `?.[]` también funciona si quisiéramos usar corchetes `[]` para acceder a las propiedades en lugar de punto `.`. Al igual que en casos anteriores, permite leer de forma segura una propiedad de un objeto que puede no existir. ```js run let key = "firstName"; @@ -199,35 +199,35 @@ alert( user1?.[key] ); // John alert( user2?.[key] ); // undefined ``` -Also we can use `?.` with `delete`: +También podemos usar `?.` con `delete`: ```js run -delete user?.name; // delete user.name if user exists +delete user?.name; // Eliminar user.name si el usuario existe ``` -````warn header="We can use `?.` for safe reading and deleting, but not writing" -The optional chaining `?.` has no use on the left side of an assignment. +````warn header="Podemos usar `?.` para una lectura y eliminación segura, pero no para escribir" +El encadenamiento opcional `?.` no puede usarse en el lado izquierdo de una asignación: -For example: +Por ejemplo: ```js run let user = null; -user?.name = "John"; // Error, doesn't work -// because it evaluates to: undefined = "John" +user?.name = "John"; // Error, no funciona +// porque se evalúa como: undefined = "John" ``` ```` -## Summary +## Resumen -The optional chaining `?.` syntax has three forms: +La sintaxis de encadenamiento opcional `?.` tiene tres formas: -1. `obj?.prop` -- returns `obj.prop` if `obj` exists, otherwise `undefined`. -2. `obj?.[prop]` -- returns `obj[prop]` if `obj` exists, otherwise `undefined`. -3. `obj.method?.()` -- calls `obj.method()` if `obj.method` exists, otherwise returns `undefined`. +1. `obj?.prop` -- devuelve `obj.prop` si `obj` existe, si no, `undefined`. +2. `obj?.[prop]` -- devuelve `obj[prop]` si `obj` existe, si no, `undefined`. +3. `obj.method?.()` -- llama a `obj.method()` si `obj.method` existe, si no devuelve `undefined`. -As we can see, all of them are straightforward and simple to use. The `?.` checks the left part for `null/undefined` and allows the evaluation to proceed if it's not so. +Como podemos ver, todos ellos son sencillos y fáciles de usar. El `?.` comprueba si la parte izquierda es `null/undefined` y permite que la evaluación continúe si no es así. -A chain of `?.` allows to safely access nested properties. +Una cadena de `?.` permite acceder de forma segura a las propiedades anidadas. -Still, we should apply `?.` carefully, only where it's acceptable, according to our code logic, that the left part doesn't exist. So that it won't hide programming errors from us, if they occur. +Aún así, debemos aplicar `?.` con cuidado, solamente donde sea aceptable que, de acuerdo con nuestra lógica, la parte izquierda no exista. Esto es para que no nos oculte errores de programación, si ocurren. diff --git a/1-js/04-object-basics/08-symbol/article.md b/1-js/04-object-basics/08-symbol/article.md index 10a98af0a..924c493c0 100644 --- a/1-js/04-object-basics/08-symbol/article.md +++ b/1-js/04-object-basics/08-symbol/article.md @@ -1,37 +1,37 @@ -# Symbol type +# Tipo Symbol -By specification, only two primitive types may serve as object property keys: +Según la especificación, solo dos de los tipos primitivos pueden servir como clave de propiedad de objetos: -- string type, or -- symbol type. +- string, o +- symbol. -Otherwise, if one uses another type, such as number, it's autoconverted to string. So that `obj[1]` is the same as `obj["1"]`, and `obj[true]` is the same as `obj["true"]`. +Si se usa otro tipo, como un número, este se autoconvertirá a string. Así, `obj[1]` es lo mismo que `obj["1"]`, y `obj[true]` es lo mismo que `obj["true"]`. -Until now we've been using only strings. +Hasta ahora solo estuvimos usando strings. -Now let's explore symbols, see what they can do for us. +Ahora exploremos symbols y ver lo que pueden hacer por nosotros. ## Symbols -A "symbol" represents a unique identifier. +El valor de "Symbol" representa un identificador único. -A value of this type can be created using `Symbol()`: +Un valor de este tipo puede ser creado usando `Symbol()`: ```js let id = Symbol(); ``` -Upon creation, we can give symbols a description (also called a symbol name), mostly useful for debugging purposes: +Al crearlo, podemos agregarle una descripción (también llamada symbol name), que será útil en la depuración de código: -```js -// id is a symbol with the description "id" +```js run +// id es un symbol con la descripción "id" let id = Symbol("id"); ``` -Symbols are guaranteed to be unique. Even if we create many symbols with exactly the same description, they are different values. The description is just a label that doesn't affect anything. +Se garantiza que los símbolos son únicos. Aunque declaremos varios Symbols con la misma descripción, éstos tendrán valores distintos. La descripción es solamente una etiqueta que no afecta nada más. -For instance, here are two symbols with the same description -- they are not equal: +Por ejemplo, aquí hay dos Symbols con la misma descripción... pero no son iguales: ```js run let id1 = Symbol("id"); @@ -42,34 +42,34 @@ alert(id1 == id2); // false */!* ``` -If you are familiar with Ruby or another language that also has some sort of "symbols" -- please don't be misguided. JavaScript symbols are different. +Si estás familiarizado con Ruby u otro lenguaje que también tiene symbols, por favor no te confundas. Los Symbols de Javascript son diferentes. -So, to summarize, a symbol is a "primitive unique value" with an optional description. Let's see where we can use them. +Para resumir: un symbol es un "valor primitivo único" con una descripción opcional. Veamos dónde podemos usarlos. -````warn header="Symbols don't auto-convert to a string" -Most values in JavaScript support implicit conversion to a string. For instance, we can `alert` almost any value, and it will work. Symbols are special. They don't auto-convert. +````warn header="Symbols no se autoconvierten a String" +La mayoría de los valores en JavaScript soportan la conversión implícita a string. Por ejemplo, podemos hacer un ´alert´ con casi cualquier valor y funcionará. Los Symbols son especiales, éstos no se autoconvierten. -For instance, this `alert` will show an error: +Por ejemplo, este `alert` mostrará un error: ```js run let id = Symbol("id"); *!* -alert(id); // TypeError: Cannot convert a Symbol value to a string +alert(id); // TypeError: No puedes convertir un valor Symbol en string */!* ``` -That's a "language guard" against messing up, because strings and symbols are fundamentally different and should not accidentally convert one into another. +Esta es una "protección del lenguaje" para evitar errores, ya que String y Symbol son fundamentalmente diferentes y no deben convertirse accidentalmente uno en otro. -If we really want to show a symbol, we need to explicitly call `.toString()` on it, like here: +Si realmente queremos mostrar un Symbol, necesitamos llamar el método `.toString()` explícitamente: ```js run let id = Symbol("id"); *!* -alert(id.toString()); // Symbol(id), now it works +alert(id.toString()); // Symbol(id), ahora sí funciona */!* ``` -Or get `symbol.description` property to show the description only: +U obtener `symbol.description` para mostrar solamente la descripción: ```js run let id = Symbol("id"); @@ -80,17 +80,16 @@ alert(id.description); // id ```` -## "Hidden" properties - +## Claves "Ocultas" -Symbols allow us to create "hidden" properties of an object, that no other part of code can accidentally access or overwrite. +Los Symbols nos permiten crear propiedades "ocultas" en un objeto, a las cuales ninguna otra parte del código puede accesar ni sobrescribir accidentalmente. -For instance, if we're working with `user` objects, that belong to a third-party code. We'd like to add identifiers to them. +Por ejemplo, si estamos trabajando con objetos `user` que pertenecen a código de terceros y queremos agregarles identificadores: -Let's use a symbol key for it: +Utilicemos una clave symbol para ello: ```js run -let user = { // belongs to another code +let user = { // pertenece a otro código name: "John" }; @@ -98,45 +97,45 @@ let id = Symbol("id"); user[id] = 1; -alert( user[id] ); // we can access the data using the symbol as the key +alert( user[id] ); // podemos accesar a la información utilizando el symbol como nombre de clave ``` -What's the benefit of using `Symbol("id")` over a string `"id"`? +¿Cuál es la ventaja de usar `Symbol("id")` y no un string `"id"`? -As `user` objects belong to another codebase, it's unsafe to add fields to them, since we might affect pre-defined behavior in that other codebase. However, symbols cannot be accessed accidentally. The third-party code won't be aware of newly defined symbols, so it's safe to add symbols to the `user` objects. +Como los objetos `user` pertenecen a otro código, es inseguro agregarles campos pues podría afectar su comportamiento predefinido en ese otro código. Sin embargo, los símbolos no pueden ser accedidos accidentalmente. El código de terceros no se percataría de los símbolos nuevos, por lo que se considera seguro agregar símbolos a los objetos `user`. -Also, imagine that another script wants to have its own identifier inside `user`, for its own purposes. +Además, imagina que otro script quiere tener su propio identificador "id" dentro de `user` para sus propios fines. -Then that script can create its own `Symbol("id")`, like this: +Entonces ese script puede crear su propio `Symbol("id")`, como aquí: ```js // ... let id = Symbol("id"); -user[id] = "Their id value"; +user[id] = "Su valor de id"; ``` -There will be no conflict between our and their identifiers, because symbols are always different, even if they have the same name. +No habrá conflicto porque los Symbols siempre son diferentes, incluso si tienen el mismo nombre. -...But if we used a string `"id"` instead of a symbol for the same purpose, then there *would* be a conflict: +... pero si utilizamos un string `"id"` en lugar de un Symbol para el mismo propósito, ciertamente *habrá* un conflicto: ```js let user = { name: "John" }; -// Our script uses "id" property -user.id = "Our id value"; +// Nuestro script usa la propiedad "id" +user.id = "Nuestro valor id"; -// ...Another script also wants "id" for its purposes... +// ...Otro script también quiere usar "id" ... -user.id = "Their id value" -// Boom! overwritten by another script! +user.id = "Su valor de id" +// Boom! sobreescrito por otro script! ``` -### Symbols in an object literal +### Symbols en objetos literales -If we want to use a symbol in an object literal `{...}`, we need square brackets around it. +Si queremos usar un Symbol en un objeto literal, debemos usar corchetes. -Like this: +Como se muestra a continuación: ```js let id = Symbol("id"); @@ -144,17 +143,17 @@ let id = Symbol("id"); let user = { name: "John", *!* - [id]: 123 // not "id": 123 + [id]: 123 // no "id": 123 */!* }; ``` -That's because we need the value from the variable `id` as the key, not the string "id". +Se hace así porque necesitamos que el valor de la variable `id` sea la clave, no el string "id". -### Symbols are skipped by for..in +### Los Symbols son omitidos en for..in -Symbolic properties do not participate in `for..in` loop. +Las claves de Symbol no participan dentro de los ciclos `for..in`. -For instance: +Por ejemplo: ```js run let id = Symbol("id"); @@ -165,16 +164,16 @@ let user = { }; *!* -for (let key in user) alert(key); // name, age (no symbols) +for (let key in user) alert(key); // nombre, edad (no aparecen symbols) */!* -// the direct access by the symbol works +// el acceso directo por medio de symbol funciona alert( "Direct: " + user[id] ); // Direct: 123 ``` -[Object.keys(user)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys) also ignores them. That's a part of the general "hiding symbolic properties" principle. If another script or a library loops over our object, it won't unexpectedly access a symbolic property. +[Object.keys(user)](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Object/keys) también los ignora. Esto forma parte del principio general de "ocultamiento de propiedades simbólicas". Si otro script o si otra librería itera sobre nuestro objeto, este no accesará inesperadamente a la clave de Symbol. -In contrast, [Object.assign](mdn:js/Object/assign) copies both string and symbol properties: +En contraste, [Object.assign](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/assign) copia tanto las claves string como symbol: ```js run let id = Symbol("id"); @@ -187,103 +186,103 @@ let clone = Object.assign({}, user); alert( clone[id] ); // 123 ``` -There's no paradox here. That's by design. The idea is that when we clone an object or merge objects, we usually want *all* properties to be copied (including symbols like `id`). +No hay paradoja aquí. Es así por diseño. La idea es que cuando clonamos un objeto o cuando fusionamos objetos, generalmente queremos que se copien *todas* las claves (incluidos los Symbol como `id`). -## Global symbols +## Symbols Globales -As we've seen, usually all symbols are different, even if they have the same name. But sometimes we want same-named symbols to be same entities. For instance, different parts of our application want to access symbol `"id"` meaning exactly the same property. +Como hemos visto, normalmente todos los Symbols son diferentes aunque tengan el mismo nombre. Pero algunas veces necesitamos que symbols con el mismo nombre sean la misma entidad. -To achieve that, there exists a *global symbol registry*. We can create symbols in it and access them later, and it guarantees that repeated accesses by the same name return exactly the same symbol. +Para lograr esto, existe un *global symbol registry*. Ahí podemos crear symbols y accesarlos después, lo cual nos garantiza que cada vez que se acceda a la clave con el mismo nombre, esta te devuelva exactamente el mismo symbol. -In order to read (create if absent) a symbol from the registry, use `Symbol.for(key)`. +Para crear u accesar a un symbol en el registro global, usa `Symbol.for(key)`. -That call checks the global registry, and if there's a symbol described as `key`, then returns it, otherwise creates a new symbol `Symbol(key)` and stores it in the registry by the given `key`. +Esta llamada revisa el registro global, y si existe un symbol descrito como `key`, lo retornará; de lo contrario creará un nuevo symbol `Symbol(key)` y lo almacenará en el registro con el `key` dado. -For instance: +Por ejemplo: ```js run -// read from the global registry -let id = Symbol.for("id"); // if the symbol did not exist, it is created +// leer desde el registro global +let id = Symbol.for("id"); // si el símbolo no existe, se crea -// read it again (maybe from another part of the code) +// léelo nuevamente (tal vez de otra parte del código) let idAgain = Symbol.for("id"); -// the same symbol +// el mismo symbol alert( id === idAgain ); // true ``` -Symbols inside the registry are called *global symbols*. If we want an application-wide symbol, accessible everywhere in the code -- that's what they are for. +Los Symbols dentro de este registro son llamados *global symbols* y están disponibles y al alcance de todo el código en la aplicación. -```smart header="That sounds like Ruby" -In some programming languages, like Ruby, there's a single symbol per name. +```smart header="Eso suena a Ruby" +En algunos lenguajes de programación, como Ruby, hay un solo Symbol por cada nombre. -In JavaScript, as we can see, that's true for global symbols. +En Javascript, como podemos ver, eso es verdad para los global symbols. ``` ### Symbol.keyFor -We have seen that for global symbols, `Symbol.for(key)` returns a symbol by name. To do the opposite -- return a name by global symbol -- we can use: `Symbol.keyFor(sym)`: +Hemos visto que para los global symbols, `Symbol.for(key)` devuelve un symbol por su nombre. Para hacer lo opuesto, -- devolver el nombre de un global symbol -- podemos usar: `Symbol.keyFor(sym)`. -For instance: +Por ejemplo: ```js run -// get symbol by name -let sym = Symbol.for("name"); +// tomar symbol por nombre +let sym = Symbol.for("nombre"); let sym2 = Symbol.for("id"); -// get name by symbol -alert( Symbol.keyFor(sym) ); // name +// tomar name por symbol +alert( Symbol.keyFor(sym) ); // nombre alert( Symbol.keyFor(sym2) ); // id ``` -The `Symbol.keyFor` internally uses the global symbol registry to look up the key for the symbol. So it doesn't work for non-global symbols. If the symbol is not global, it won't be able to find it and returns `undefined`. +El `Symbol.keyFor` utiliza internamente el registro "global symbol registry" para buscar la clave del symbol, por lo tanto, no funciona para los symbol que no están dentro del registro. Si el symbol no es global, no será capaz de encontrarlo y por lo tanto devolverá `undefined`. -That said, all symbols have the `description` property. +Dicho esto, todo symbol tiene la propiedad `description`. -For instance: +Por ejemplo: ```js run -let globalSymbol = Symbol.for("name"); -let localSymbol = Symbol("name"); +let globalSymbol = Symbol.for("nombre"); +let localSymbol = Symbol("nombre"); -alert( Symbol.keyFor(globalSymbol) ); // name, global symbol -alert( Symbol.keyFor(localSymbol) ); // undefined, not global +alert( Symbol.keyFor(globalSymbol) ); // nombre, global symbol +alert( Symbol.keyFor(localSymbol) ); // undefined, no global -alert( localSymbol.description ); // name +alert( localSymbol.description ); // nombre ``` ## System symbols -There exist many "system" symbols that JavaScript uses internally, and we can use them to fine-tune various aspects of our objects. +Existen varios symbols del sistema que JavaScript utiliza internamente, y que podemos usar para ajustar varios aspectos de nuestros objetos. -They are listed in the specification in the [Well-known symbols](https://tc39.github.io/ecma262/#sec-well-known-symbols) table: +Se encuentran listados en [Well-known symbols](https://tc39.github.io/ecma262/#sec-well-known-symbols) : - `Symbol.hasInstance` - `Symbol.isConcatSpreadable` - `Symbol.iterator` - `Symbol.toPrimitive` -- ...and so on. +- ...y así. -For instance, `Symbol.toPrimitive` allows us to describe object to primitive conversion. We'll see its use very soon. +Por ejemplo, `Symbol.toPrimitive` nos permite describir el objeto para su conversión primitiva. Más adelante veremos su uso. -Other symbols will also become familiar when we study the corresponding language features. +Otros symbols también te serán más familiares cuando estudiemos las características correspondientes. -## Summary +## Resumen -`Symbol` is a primitive type for unique identifiers. +`Symbol` es un tipo de dato primitivo para identificadores únicos. -Symbols are created with `Symbol()` call with an optional description (name). +Symbols son creados al llamar `Symbol()` con una descripción opcional. -Symbols are always different values, even if they have the same name. If we want same-named symbols to be equal, then we should use the global registry: `Symbol.for(key)` returns (creates if needed) a global symbol with `key` as the name. Multiple calls of `Symbol.for` with the same `key` return exactly the same symbol. +Symbols son siempre valores distintos aunque tengan el mismo nombre. Si queremos que symbols con el mismo nombre tengan el mismo valor, entonces debemos guardarlos en el registro global: `Symbol.for(key)` retornará un symbol (en caso de no existir, lo creará) con el `key` como su nombre. Todas las llamadas de `Symbol.for` con ese nombre retornarán siempre el mismo symbol. -Symbols have two main use cases: +Symbols se utilizan principalmente en dos casos: -1. "Hidden" object properties. +1. Propiedades de objeto "Ocultas" - If we want to add a property into an object that "belongs" to another script or a library, we can create a symbol and use it as a property key. A symbolic property does not appear in `for..in`, so it won't be accidentally processed together with other properties. Also it won't be accessed directly, because another script does not have our symbol. So the property will be protected from accidental use or overwrite. + Si queremos agregar una propiedad a un objeto que "pertenece" a otro script u otra librería, podemos crear un symbol y usarlo como clave. Una clave symbol no aparecerá en los ciclos `for..in`, por lo que no podrá ser procesada accidentalmente junto con las demás propiedades. Tampoco puede ser accesada directamente, porque un script ajeno no tiene nuestro symbol. Por lo tanto la propiedad estará protegida contra uso y escritura accidentales. - So we can "covertly" hide something into objects that we need, but others should not see, using symbolic properties. + Podemos "ocultar" ciertos valores dentro de un objeto que solo estarán disponibles dentro de ese script usando las claves de symbol. -2. There are many system symbols used by JavaScript which are accessible as `Symbol.*`. We can use them to alter some built-in behaviors. For instance, later in the tutorial we'll use `Symbol.iterator` for [iterables](info:iterable), `Symbol.toPrimitive` to setup [object-to-primitive conversion](info:object-toprimitive) and so on. +2. Existen diversos symbols del sistema que utiliza Javascript, a los cuales podemos accesar por medio de `Symbol.*`. Podemos usarlos para alterar algunos comportamientos. Por ejemplo, más adelante en el tutorial, usaremos `Symbol.iterator` para [iterables](info:iterable), `Symbol.toPrimitive` para configurar [object-to-primitive conversion](info:object-toprimitive). -Technically, symbols are not 100% hidden. There is a built-in method [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) that allows us to get all symbols. Also there is a method named [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) that returns *all* keys of an object including symbolic ones. But most libraries, built-in functions and syntax constructs don't use these methods. +Técnicamente, los symbols no están 100% ocultos. Existe un método incorporado [Object.getOwnPropertySymbols(obj)](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/getOwnPropertySymbols) que nos permite obtener todos los symbols. También existe un método llamado [Reflect.ownKeys(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/ownKeys) que devuelve *todas* las claves de un objeto, incluyendo las que son de tipo symbol. Pero la mayoría de las librerías, los métodos incorporados y las construcciones de sintaxis no usan estos métodos. diff --git a/1-js/04-object-basics/09-object-toprimitive/article.md b/1-js/04-object-basics/09-object-toprimitive/article.md index fa68da583..9b08dfc4f 100644 --- a/1-js/04-object-basics/09-object-toprimitive/article.md +++ b/1-js/04-object-basics/09-object-toprimitive/article.md @@ -1,117 +1,117 @@ -# Object to primitive conversion +# Conversión de objeto a valor primitivo -What happens when objects are added `obj1 + obj2`, subtracted `obj1 - obj2` or printed using `alert(obj)`? +¿Qué sucede cuando los objetos se suman `obj1 + obj2`, se restan `obj1 - obj2` o se imprimen utilizando `alert(obj)`? -JavaScript doesn't allow you to customize how operators work on objects. Unlike some other programming languages, such as Ruby or C++, we can't implement a special object method to handle addition (or other operators). +JavaScript no permite personalizar cómo los operadores trabajan con los objetos. Al contrario de otros lenguajes de programación como Ruby o C++, no podemos implementar un método especial para manejar una suma (u otros operadores). -In case of such operations, objects are auto-converted to primitives, and then the operation is carried out over these primitives and results in a primitive value. +En ese caso, los objetos se convierten automáticamente en valores primitivos, y luego se lleva a cabo la operación sobre esos primitivos, y resultan en un valor primitivo. -That's an important limitation: the result of `obj1 + obj2` (or another math operation) can't be another object! +Esto es una limitación importante: el resultado de `obj1 + obj2` (u otra operación) ¡no puede ser otro objeto! -E.g. we can't make objects representing vectors or matrices (or achievements or whatever), add them and expect a "summed" object as the result. Such architectural feats are automatically "off the board". +Por ejemplo no podemos hacer objetos que representen vectores o matrices (o conquistas o lo que sea), sumarlas y esperar un objeto "sumado" como resultado. Tal objetivo arquitectural cae automáticamente "fuera del tablero". -So, because we can't technically do much here, there's no maths with objects in real projects. When it happens, with rare exceptions, it's because of a coding mistake. +Como técnicamente no podemos hacer mucho aquí, no se hacen matemáticas con objetos en proyectos reales. Cuando ocurre, con alguna rara excepción es por un error de código. -In this chapter we'll cover how an object converts to primitive and how to customize it. +En este capítulo cubriremos cómo un objeto se convierte a primitivo y cómo podemos personalizarlo. -We have two purposes: +Tenemos dos propósitos: -1. It will allow us to understand what's going on in case of coding mistakes, when such an operation happened accidentally. -2. There are exceptions, where such operations are possible and look good. E.g. subtracting or comparing dates (`Date` objects). We'll come across them later. +1. Nos permitirá entender qué ocurre en caso de errores de código, cuando tal operación ocurre accidentalmente. +2. Hay excepciones, donde tales operaciones son posibles y se ven bien. Por ejemplo al restar o comparar fechas (objetos `Date`). Las discutiremos más adelante. -## Conversion rules +## Reglas de conversión -In the chapter we've seen the rules for numeric, string and boolean conversions of primitives. But we left a gap for objects. Now, as we know about methods and symbols it becomes possible to fill it. +En el capítulo , hemos visto las reglas para las conversiones de valores primitivos numéricos, strings y booleanos. Pero dejamos un hueco en los objetos. Ahora, como sabemos sobre métodos y símbolos, es posible completarlo. -1. There's no conversion to boolean. All objects are `true` in a boolean context, as simple as that. There exist only numeric and string conversions. -2. The numeric conversion happens when we subtract objects or apply mathematical functions. For instance, `Date` objects (to be covered in the chapter ) can be subtracted, and the result of `date1 - date2` is the time difference between two dates. -3. As for the string conversion -- it usually happens when we output an object with `alert(obj)` and in similar contexts. +1. No hay conversión a boolean. Todos los objetos son `true` en un contexto booleano, tan simple como eso. Solo hay conversiones numéricas y de strings. +2. La conversión numérica ocurre cuando restamos objetos o aplicamos funciones matemáticas. Por ejemplo, los objetos de tipo `Date` (que se cubrirán en el capítulo ) se pueden restar, y el resultado de `date1 - date2` es la diferencia horaria entre dos fechas. +3. En cuanto a la conversión de strings: generalmente ocurre cuando imprimimos un objeto como en `alert(obj)` y en contextos similares. -We can implement string and numeric conversion by ourselves, using special object methods. +Podemos implementar la conversión de tipo string y numérica por nuestra cuenta, utilizando métodos de objeto especiales. -Now let's get into technical details, because it's the only way to cover the topic in-depth. +Ahora entremos en los detalles técnicos, porque es la única forma de cubrir el tópico en profundidad. -## Hints +## Hints (sugerencias) -How does JavaScript decide which conversion to apply? +¿Cómo decide JavaScript cuál conversión aplicar? -There are three variants of type conversion, that happen in various situations. They're called "hints", as described in the [specification](https://tc39.github.io/ecma262/#sec-toprimitive): +Hay tres variantes de conversión de tipos que ocurren en varias situaciones. Son llamadas "hints" y están descriptas en la [especificación](https://tc39.github.io/ecma262/#sec-toprimitive): `"string"` -: For an object-to-string conversion, when we're doing an operation on an object that expects a string, like `alert`: +: Para una conversión de objeto a string, cuando hacemos una operación que espera un string en un objeto, como `alert`: ```js - // output + // salida alert(obj); - // using object as a property key + // utilizando un objeto como clave anotherObj[obj] = 123; ``` `"number"` -: For an object-to-number conversion, like when we're doing maths: +: Para una conversión de objeto a número, como cuando hacemos operaciones matemáticas: ```js - // explicit conversion + // conversión explícita let num = Number(obj); - // maths (except binary plus) - let n = +obj; // unary plus + // matemáticas (excepto + binario) + let n = +obj; // + unario let delta = date1 - date2; - // less/greater comparison + // comparación menor que / mayor que let greater = user1 > user2; ``` - Most built-in mathematical functions also include such conversion. + La mayoría de las funciones matemáticas nativas también incluyen tal conversión. `"default"` -: Occurs in rare cases when the operator is "not sure" what type to expect. +: Ocurre en casos raros cuando el operador "no está seguro" de qué tipo esperar. - For instance, binary plus `+` can work both with strings (concatenates them) and numbers (adds them). So if a binary plus gets an object as an argument, it uses the `"default"` hint to convert it. + Por ejemplo, el operador binario `+` puede funcionar con strings (los concatena) y números (los suma). Entonces, si el + binario obtiene un objeto como argumento, utiliza la sugerencia `"default"` para convertirlo. - Also, if an object is compared using `==` with a string, number or a symbol, it's also unclear which conversion should be done, so the `"default"` hint is used. + También, si un objeto es comparado utilizando `==` con un string, un número o un símbolo, tampoco está claro qué conversión se debe realizar, por lo que se utiliza la sugerencia `"default"`. ```js - // binary plus uses the "default" hint + // + binario utiliza la sugerencia "default" let total = obj1 + obj2; - // obj == number uses the "default" hint + // obj == número utiliza la sugerencia "default" if (user == 1) { ... }; ``` - The greater and less comparison operators, such as `<` `>`, can work with both strings and numbers too. Still, they use the `"number"` hint, not `"default"`. That's for historical reasons. + Los operadores de comparación mayor que y menor que, como `<` `>`, también pueden funcionar con strings y números. Aún así, utilizan la sugerencia `"number"`, y no `"default"`. Eso es por razones históricas. -In practice though, things are a bit simpler. +Aunque en la práctica las cosas son más simples. -All built-in objects except for one case (`Date` object, we'll learn it later) implement `"default"` conversion the same way as `"number"`. And we probably should do the same. +Todos los objetos nativos -excepto un caso (objeto `Date`, lo veremos más adelante)- implementan la conversión `"default"` del mismo modo que `"number"`. Y probablemente debiéramos hacer lo mismo. -Still, it's important to know about all 3 hints, soon we'll see why. +Aún así, es importante conocer los 3 "hints", pronto veremos el porqué. -**To do the conversion, JavaScript tries to find and call three object methods:** +**Para realizar la conversión, JavaScript intenta buscar y llamar a tres métodos del objeto:** -1. Call `obj[Symbol.toPrimitive](hint)` - the method with the symbolic key `Symbol.toPrimitive` (system symbol), if such method exists, -2. Otherwise if hint is `"string"` - - try calling `obj.toString()` or `obj.valueOf()`, whatever exists. -3. Otherwise if hint is `"number"` or `"default"` - - try calling `obj.valueOf()` or `obj.toString()`, whatever exists. +1. Busca y llama, si el método existe, a `obj[Symbol.toPrimitive](hint)`: el método con la clave simbólica `Symbol.toPrimitive` (símbolo del sistema); +2. Si no lo encuentra y "hint" es `"string"`: + - intenta llamar a `obj.toString()` y `obj.valueOf()`, lo que exista. +3. Si no lo encuentra y "hint" es `"number"` o `"default"` + - intenta llamar a `obj.valueOf()` y `obj.toString()`, lo que exista. ## Symbol.toPrimitive -Let's start from the first method. There's a built-in symbol named `Symbol.toPrimitive` that should be used to name the conversion method, like this: +Empecemos por el primer método. Hay un símbolo incorporado llamado `Symbol.toPrimitive` que debe utilizarse para nombrar el método de conversión, así: ```js obj[Symbol.toPrimitive] = function(hint) { - // here goes the code to convert this object to a primitive - // it must return a primitive value - // hint = one of "string", "number", "default" + // aquí va el código para convertir este objeto a un primitivo + // debe devolver un valor primitivo + // hint = "sugerencia", uno de: "string", "number", "default" }; ``` -If the method `Symbol.toPrimitive` exists, it's used for all hints, and no more methods are needed. +Si el método `Symbol.toPrimitive` existe, es usado para todos los hints y no serán necesarios más métodos. -For instance, here `user` object implements it: +Por ejemplo, aquí el objeto `user` lo implementa: ```js run let user = { @@ -119,36 +119,36 @@ let user = { money: 1000, [Symbol.toPrimitive](hint) { - alert(`hint: ${hint}`); + alert(`sugerencia: ${hint}`); return hint == "string" ? `{name: "${this.name}"}` : this.money; } }; -// conversions demo: -alert(user); // hint: string -> {name: "John"} -alert(+user); // hint: number -> 1000 -alert(user + 500); // hint: default -> 1500 +// demostración de conversiones: +alert(user); // sugerencia: string -> {name: "John"} +alert(+user); // sugerencia: number -> 1000 +alert(user + 500); // sugerencia: default -> 1500 ``` -As we can see from the code, `user` becomes a self-descriptive string or a money amount, depending on the conversion. The single method `user[Symbol.toPrimitive]` handles all conversion cases. +Como podemos ver en el código, `user` se convierte en un string autodescriptivo o en una cantidad de dinero, depende de la conversión. Un único método `user[Symbol.toPrimitive]` maneja todos los casos de conversión. ## toString/valueOf -If there's no `Symbol.toPrimitive` then JavaScript tries to find methods `toString` and `valueOf`: +Si no existe `Symbol.toPrimitive` entonces JavaScript trata de encontrar los métodos `toString` y `valueOf`: -- For the `"string"` hint: call `toString` method, and if it doesn't exist or if it returns an object instead of a primitive value, then call `valueOf` (so `toString` has the priority for string conversions). -- For other hints: call `valueOf`, and if it doesn't exist or if it returns an object instead of a primitive value, then call `toString` (so `valueOf` has the priority for maths). +- Para una sugerencia "string": trata de llamar a `toString` primero; pero si no existe, o si devuelve un objeto en lugar de un valor primitivo, llama a `valueOf` (así, `toString` tiene prioridad en conversiones string). +- Para otras sugerencias: trata de llamar a `valueOf` primero; y si no existe, o si devuelve un objeto en lugar de un valor primitivo, llama a `toString` (así, `valueOf` tiene prioridad para matemáticas). -Methods `toString` and `valueOf` come from ancient times. They are not symbols (symbols did not exist that long ago), but rather "regular" string-named methods. They provide an alternative "old-style" way to implement the conversion. +Los métodos `toString` y `valueOf` provienen de tiempos remotos. No son símbolos (los símbolos no existían en aquel tiempo) sino métodos "normales" nombrados con strings. Proporcionan una forma alternativa "al viejo estilo" de implementar la conversión. -These methods must return a primitive value. If `toString` or `valueOf` returns an object, then it's ignored (same as if there were no method). +Estos métodos deben devolver un valor primitivo. Si `toString` o `valueOf` devuelve un objeto, entonces se ignora (lo mismo que si no hubiera un método). -By default, a plain object has following `toString` and `valueOf` methods: +De forma predeterminada, un objeto simple tiene los siguientes métodos `toString` y `valueOf`: -- The `toString` method returns a string `"[object Object]"`. -- The `valueOf` method returns the object itself. +- El método `toString` devuelve un string `"[object Object]"`. +- El método `valueOf` devuelve el objeto en sí. -Here's the demo: +Aquí está la demostración: ```js run let user = {name: "John"}; @@ -157,25 +157,25 @@ alert(user); // [object Object] alert(user.valueOf() === user); // true ``` -So if we try to use an object as a string, like in an `alert` or so, then by default we see `[object Object]`. +Por lo tanto, si intentamos utilizar un objeto como un string, como en un `alert` o algo así, entonces por defecto vemos `[object Object]`. -The default `valueOf` is mentioned here only for the sake of completeness, to avoid any confusion. As you can see, it returns the object itself, and so is ignored. Don't ask me why, that's for historical reasons. So we can assume it doesn't exist. +El `valueOf` predeterminado se menciona aquí solo en favor de la integridad, para evitar confusiones. Como puede ver, devuelve el objeto en sí, por lo que se ignora. No me pregunte por qué, es por razones históricas. Entonces podemos asumir que no existe. -Let's implement these methods to customize the conversion. +Implementemos estos métodos para personalizar la conversión. -For instance, here `user` does the same as above using a combination of `toString` and `valueOf` instead of `Symbol.toPrimitive`: +Por ejemplo, aquí `user` hace lo mismo que el ejemplo anterior utilizando una combinación de `toString` y `valueOf` en lugar de `Symbol.toPrimitive`: ```js run let user = { name: "John", money: 1000, - // for hint="string" + // para sugerencia="string" toString() { return `{name: "${this.name}"}`; }, - // for hint="number" or "default" + // para sugerencia="number" o "default" valueOf() { return this.money; } @@ -187,9 +187,9 @@ alert(+user); // valueOf -> 1000 alert(user + 500); // valueOf -> 1500 ``` -As we can see, the behavior is the same as the previous example with `Symbol.toPrimitive`. +Como podemos ver, el comportamiento es el mismo que en el ejemplo anterior con `Symbol.toPrimitive`. -Often we want a single "catch-all" place to handle all primitive conversions. In this case, we can implement `toString` only, like this: +A menudo queremos un único lugar "general" para manejar todas las conversiones primitivas. En este caso, podemos implementar solo `toString`, así: ```js run let user = { @@ -204,47 +204,47 @@ alert(user); // toString -> John alert(user + 500); // toString -> John500 ``` -In the absence of `Symbol.toPrimitive` and `valueOf`, `toString` will handle all primitive conversions. +En ausencia de `Symbol.toPrimitive` y `valueOf`, `toString` manejará todas las conversiones primitivas. -### A conversion can return any primitive type +### Una conversión puede devolver cualquier tipo primitivo -The important thing to know about all primitive-conversion methods is that they do not necessarily return the "hinted" primitive. +Lo importante que debe saber acerca de todos los métodos de conversión primitiva es que no necesariamente devuelven la primitiva "sugerida". -There is no control whether `toString` returns exactly a string, or whether `Symbol.toPrimitive` method returns a number for the hint `"number"`. +No hay control para que `toString` devuelva exactamente un string, ni para que el método `Symbol.toPrimitive` con una sugerencia `"number"` devuelva un número. -The only mandatory thing: these methods must return a primitive, not an object. +Lo único obligatorio: estos métodos deben devolver un valor primitivo, no un objeto. -```smart header="Historical notes" -For historical reasons, if `toString` or `valueOf` returns an object, there's no error, but such value is ignored (like if the method didn't exist). That's because in ancient times there was no good "error" concept in JavaScript. +```smart header="Notas históricas" +Por razones históricas, si `toString` o `valueOf` devuelve un objeto, no hay ningún error, pero dicho valor se ignora (como si el método no existiera). Esto se debe a que en la antigüedad no existía un buen concepto de "error" en JavaScript. -In contrast, `Symbol.toPrimitive` is stricter, it *must* return a primitive, otherwise there will be an error. +Por el contrario, `Symbol.toPrimitive` es más estricto, *debe* devolver un valor primitivo, en caso contrario habrá un error. ``` -## Further conversions +## Más conversiones -As we know already, many operators and functions perform type conversions, e.g. multiplication `*` converts operands to numbers. +Como ya sabemos, muchos operadores y funciones realizan conversiones de tipo, por ejemplo la multiplicación `*` convierte operandos en números. -If we pass an object as an argument, then there are two stages of calculations: -1. The object is converted to a primitive (using the rules described above). -2. If necessary for further calculations, the resulting primitive is also converted. +Si pasamos un objeto como argumento, entonces hay dos etapas de cómputo: +1. El objeto se convierte en un valor primitivo (utilizando las reglas descritas anteriormente). +2. Si es necesario para más cómputo, el valor primitivo resultante también se convierte. -For instance: +Por ejemplo: ```js run let obj = { - // toString handles all conversions in the absence of other methods + // toString maneja todas las conversiones en ausencia de otros métodos toString() { return "2"; } }; -alert(obj * 2); // 4, object converted to primitive "2", then multiplication made it a number +alert(obj * 2); // 4, objeto convertido a valor primitivo "2", luego la multiplicación lo convirtió en un número ``` -1. The multiplication `obj * 2` first converts the object to primitive (that's a string `"2"`). -2. Then `"2" * 2` becomes `2 * 2` (the string is converted to number). +1. La multiplicación `obj * 2` primero convierte el objeto en valor primitivo (que es un string `"2"`). +2. Luego `"2" * 2` se convierte en `2 * 2` (el string se convierte en número). -Binary plus will concatenate strings in the same situation, as it gladly accepts a string: +El `+` binario concatenará los strings en la misma situación, ya que acepta con gusto un string: ```js run let obj = { @@ -253,28 +253,30 @@ let obj = { } }; -alert(obj + 2); // "22" ("2" + 2), conversion to primitive returned a string => concatenation +alert(obj + 2); // 22 ("2" + 2), la conversión a valor primitivo devolvió un string => concatenación ``` -## Summary +## Resumen -The object-to-primitive conversion is called automatically by many built-in functions and operators that expect a primitive as a value. +La conversión de objeto a valor primitivo es llamada automáticamente por muchas funciones y operadores incorporados que esperan un valor primitivo. -There are 3 types (hints) of it: -- `"string"` (for `alert` and other operations that need a string) -- `"number"` (for maths) -- `"default"` (few operators, usually objects implement it the same way as `"number"`) +Hay 3 tipos (hints o sugerencias) de estas: +- `"string"` (para `alert` y otras operaciones que necesitan un string) +- `"number"` (para matemáticas) +- `"default"` (pocos operadores, usualmente los objetos lo implementan del mismo modo que `"number"`) -The specification describes explicitly which operator uses which hint. +La especificación describe explícitamente qué operador utiliza qué sugerencia. -The conversion algorithm is: +El algoritmo de conversión es: -1. Call `obj[Symbol.toPrimitive](hint)` if the method exists, -2. Otherwise if hint is `"string"` - - try calling `obj.toString()` or `obj.valueOf()`, whatever exists. -3. Otherwise if hint is `"number"` or `"default"` - - try calling `obj.valueOf()` or `obj.toString()`, whatever exists. +1. Llamar a `obj[Symbol.toPrimitive](hint)` si el método existe, +2. En caso contrario, si la sugerencia es `"string"` + - intentar llamar a `obj.toString()` y `obj.valueOf()`, lo que exista. +3. En caso contrario, si la sugerencia es `"number"` o `"default"` + - intentar llamar a `obj.valueOf()` y `obj.toString()`, lo que exista. -All these methods must return a primitive to work (if defined). +Todos estos métodos deben devolver un primitivo para funcionar (si está definido). -In practice, it's often enough to implement only `obj.toString()` as a "catch-all" method for string conversions that should return a "human-readable" representation of an object, for logging or debugging purposes. +En la práctica, a menudo es suficiente implementar solo `obj.toString()` como un método "atrapatodo" para todas las conversiones a string que deben devolver la representación "legible por humanos" de un objeto, con fines de registro o depuración. + +Como en las operaciones matemáticas, JavaScript no ofrece una forma de "sobrescribir" operadores usando métodos. Así que en proyectos de la vida real raramente se los usa en objetos. diff --git a/1-js/04-object-basics/index.md b/1-js/04-object-basics/index.md index d2387aafa..66ca07a66 100644 --- a/1-js/04-object-basics/index.md +++ b/1-js/04-object-basics/index.md @@ -1 +1 @@ -# Objects: the basics +# Objetos: lo básico diff --git a/1-js/05-data-types/01-primitives-methods/1-string-new-property/solution.md b/1-js/05-data-types/01-primitives-methods/1-string-new-property/solution.md index fd22a4653..fe4a37f6c 100644 --- a/1-js/05-data-types/01-primitives-methods/1-string-new-property/solution.md +++ b/1-js/05-data-types/01-primitives-methods/1-string-new-property/solution.md @@ -1,5 +1,5 @@ -Try running it: +Prueba ejecutándolo: ```js run let str = "Hello"; @@ -9,16 +9,16 @@ str.test = 5; // (*) alert(str.test); ``` -Depending on whether you have `use strict` or not, the result may be: -1. `undefined` (no strict mode) -2. An error (strict mode). +Depende de si usas el modo estricto "use strict" o no, el resultado será: +1. `undefined` (sin strict mode) +2. Un error. (strict mode) -Why? Let's replay what's happening at line `(*)`: +¿Por qué? Repasemos lo que ocurre en la línea `(*)`: -1. When a property of `str` is accessed, a "wrapper object" is created. -2. In strict mode, writing into it is an error. -3. Otherwise, the operation with the property is carried on, the object gets the `test` property, but after that the "wrapper object" disappears, so in the last line `str` has no trace of the property. +1. Cuando se accede a una propiedad de `str`, se crea un "wrapper object" (objeto envolvente ). +2. Con modo estricto, tratar de alterarlo produce error. +3. Sin modo estricto, la operación es llevada a cabo y el objeto obtiene la propiedad `test`, pero después de ello el "objeto envolvente" desaparece, entonces en la última linea `str` queda sin rastros de la propiedad. -**This example clearly shows that primitives are not objects.** +**Este ejemplo claramente muestra que los tipos primitivos no son objetos.** -They can't store additional data. +Ellos no pueden almacenar datos adicionales. diff --git a/1-js/05-data-types/01-primitives-methods/1-string-new-property/task.md b/1-js/05-data-types/01-primitives-methods/1-string-new-property/task.md index 208f84cc7..9986bf827 100644 --- a/1-js/05-data-types/01-primitives-methods/1-string-new-property/task.md +++ b/1-js/05-data-types/01-primitives-methods/1-string-new-property/task.md @@ -2,10 +2,10 @@ importance: 5 --- -# Can I add a string property? +# ¿Puedo agregar una propiedad a un string? -Consider the following code: +Considera el siguiente código: ```js let str = "Hello"; @@ -15,4 +15,4 @@ str.test = 5; alert(str.test); ``` -What do you think, will it work? What will be shown? +Qué piensas: ¿funcionará? ¿Qué mostrará? diff --git a/1-js/05-data-types/01-primitives-methods/article.md b/1-js/05-data-types/01-primitives-methods/article.md index 69e7196e9..38d53a50b 100644 --- a/1-js/05-data-types/01-primitives-methods/article.md +++ b/1-js/05-data-types/01-primitives-methods/article.md @@ -1,20 +1,20 @@ -# Methods of primitives +# Métodos en tipos primitivos -JavaScript allows us to work with primitives (strings, numbers, etc.) as if they were objects. They also provide methods to call as such. We will study those soon, but first we'll see how it works because, of course, primitives are not objects (and here we will make it even clearer). +JavaScript nos permite trabajar con tipos de datos primitivos (string, number, etc) como si fueran objetos. Los primitivos también ofrecen métodos que podemos llamar. Los estudiaremos pronto, pero primero veamos cómo trabajan porque, por supuesto, los primitivos no son objetos (y aquí lo haremos aún más evidente). -Let's look at the key distinctions between primitives and objects. +Veamos las diferencias fundamentales entre primitivos y objetos. -A primitive +Un primitivo -- Is a value of a primitive type. -- There are 7 primitive types: `string`, `number`, `bigint`, `boolean`, `symbol`, `null` and `undefined`. +- Es un valor de tipo primitivo. +- Hay 7 tipos primitivos: `string`, `number`, `bigint`, `boolean`, `symbol`, `null` y `undefined`. -An object +Un objeto -- Is capable of storing multiple values as properties. -- Can be created with `{}`, for instance: `{name: "John", age: 30}`. There are other kinds of objects in JavaScript: functions, for example, are objects. +- Es capaz de almacenar múltiples valores como propiedades. +- Puede ser creado con `{}`. Ejemplo: `{name: "John", age: 30}`. Hay otras clases de objetos en JavaScript; las funciones, por ejemplo, son objetos. -One of the best things about objects is that we can store a function as one of its properties. +Una de las mejores cosas de los objetos es que podemos almacenar una función como una de sus propiedades. ```js run let john = { @@ -27,32 +27,32 @@ let john = { john.sayHi(); // Hi buddy! ``` -So here we've made an object `john` with the method `sayHi`. +Aquí hemos creado un objeto `john` con el método `sayHi`. -Many built-in objects already exist, such as those that work with dates, errors, HTML elements, etc. They have different properties and methods. +Ya existen muchos objetos integrados al lenguaje, como los que trabajan con fechas, errores, elementos HTML, etc. Ellos tienen diferentes propiedades y métodos. -But, these features come with a cost! +¡Pero estas características tienen un precio! -Objects are "heavier" than primitives. They require additional resources to support the internal machinery. +Los objetos son más "pesados" que los primitivos. Ellos requieren recursos adicionales para soportar su maquinaria interna. -## A primitive as an object +## Un primitivo como objeto -Here's the paradox faced by the creator of JavaScript: +Aquí el dilema que enfrentó el creador de JavaScript: -- There are many things one would want to do with a primitive, like a string or a number. It would be great to access them using methods. -- Primitives must be as fast and lightweight as possible. +- Hay muchas cosas que uno querría hacer con los tipos primitivos, como un string o un number. Sería grandioso accederlas usando métodos. +- Los Primitivos deben ser tan rápidos y livianos como sea posible. -The solution looks a little bit awkward, but here it is: +La solución es algo enrevesada, pero aquí está: -1. Primitives are still primitive. A single value, as desired. -2. The language allows access to methods and properties of strings, numbers, booleans and symbols. -3. In order for that to work, a special "object wrapper" that provides the extra functionality is created, and then is destroyed. +1. Los primitivos son aún primitivos. Con un valor único, como es deseable. +2. El lenguaje permite el acceso a métodos y propiedades de strings, numbers, booleans y symbols. +3. Para que esto funcione, se crea una envoltura especial, un "object wrapper" (objeto envoltorio) que provee la funcionalidad extra y luego es destruido. -The "object wrappers" are different for each primitive type and are called: `String`, `Number`, `Boolean`, `Symbol` and `BigInt`. Thus, they provide different sets of methods. +Los "object wrappers" son diferentes para cada primitivo y son llamados: `String`, `Number`, `Boolean`, `Symbol` y `BigInt`. Así, proveen diferentes sets de métodos. -For instance, there exists a string method [str.toUpperCase()](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase) that returns a capitalized `str`. +Por ejemplo, existe un método [str.toUpperCase()](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/String/toUpperCase) que devuelve un string en mayúsculas. -Here's how it works: +Aquí el funcionamiento: ```js run let str = "Hello"; @@ -60,17 +60,17 @@ let str = "Hello"; alert( str.toUpperCase() ); // HELLO ``` -Simple, right? Here's what actually happens in `str.toUpperCase()`: +Simple, ¿no es así? Lo que realmente ocurre en `str.toUpperCase()`: -1. The string `str` is a primitive. So in the moment of accessing its property, a special object is created that knows the value of the string, and has useful methods, like `toUpperCase()`. -2. That method runs and returns a new string (shown by `alert`). -3. The special object is destroyed, leaving the primitive `str` alone. +1. El string `str` es primitivo. Al momento de acceder a su propiedad, un objeto especial es creado, uno que conoce el valor del string y tiene métodos útiles como `toUpperCase()`. +2. Ese método se ejecuta y devuelve un nuevo string (mostrado con `alert`). +3. El objeto especial es destruido, dejando solo el primitivo `str`. -So primitives can provide methods, but they still remain lightweight. +Así los primitivos pueden proveer métodos y aún permanecer livianos. -The JavaScript engine highly optimizes this process. It may even skip the creation of the extra object at all. But it must still adhere to the specification and behave as if it creates one. +El motor JavaScript optimiza este proceso enormemente. Incluso puede saltarse la creación del objeto extra por completo. Pero aún se debe adherir a la especificación y comportarse como si creara uno. -A number has methods of its own, for instance, [toFixed(n)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed) rounds the number to the given precision: +Un number tiene sus propios métodos, por ejemplo [toFixed(n)](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Number/toFixed) redondea el número a la precisión dada: ```js run let n = 1.23456; @@ -78,15 +78,15 @@ let n = 1.23456; alert( n.toFixed(2) ); // 1.23 ``` -We'll see more specific methods in chapters and . +Veremos más métodos específicos en los capítulos y . -````warn header="Constructors `String/Number/Boolean` are for internal use only" -Some languages like Java allow us to explicitly create "wrapper objects" for primitives using a syntax like `new Number(1)` or `new Boolean(false)`. +````warn header="Los constructores `String/Number/Boolean` son de uso interno solamente" +Algunos lenguajes como Java permiten crear "wrapper objects" para primitivos explícitamente usando una sintaxis como `new Number(1)` o `new Boolean(false)`. -In JavaScript, that's also possible for historical reasons, but highly **unrecommended**. Things will go crazy in several places. +En JavaScript, eso también es posible por razones históricas, pero firmemente **desaconsejado**. Las cosas enloquecerían en varios lugares. -For instance: +Por ejemplo: ```js run alert( typeof 0 ); // "number" @@ -94,36 +94,36 @@ alert( typeof 0 ); // "number" alert( typeof new Number(0) ); // "object"! ``` -Objects are always truthy in `if`, so here the alert will show up: +Los objetos siempre son true en un `if`, entonces el alert mostrará: ```js run -let zero = new Number(0); +let cero = new Number(0); -if (zero) { // zero is true, because it's an object - alert( "zero is truthy!?!" ); +if (cero) { // cero es true, porque es un objeto + alert( "¿cero es verdadero?!?" ); } ``` -On the other hand, using the same functions `String/Number/Boolean` without `new` is totally fine and useful thing. They convert a value to the corresponding type: to a string, a number, or a boolean (primitive). +Por otro lado, usar las mismas funciones `String/Number/Boolean` sin `new` es totalmente sano y útil. Ellas convierten un valor al tipo primitivo correspondiente: a un string, number, o boolean. -For example, this is entirely valid: +Por ejemplo, esto es perfectamente válido: ```js -let num = Number("123"); // convert a string to number +let num = Number("123"); // convierte string a number ``` ```` -````warn header="null/undefined have no methods" -The special primitives `null` and `undefined` are exceptions. They have no corresponding "wrapper objects" and provide no methods. In a sense, they are "the most primitive". +````warn header="null/undefined no poseen métodos" +Los primitivos especiales `null` y `undefined` son excepciones. No tienen "wrapper objects" correspondientes y no proveen métodos. En ese sentido son "lo más primitivo". -An attempt to access a property of such value would give the error: +El intento de acceder a una propiedad de tal valor daría error: ```js run alert(null.test); // error ```` -## Summary +## Resumen -- Primitives except `null` and `undefined` provide many helpful methods. We will study those in the upcoming chapters. -- Formally, these methods work via temporary objects, but JavaScript engines are well tuned to optimize that internally, so they are not expensive to call. +- Los primitivos excepto `null` y `undefined` proveen muchos métodos útiles. Los estudiaremos en los próximos capítulos. +- Formalmente, estos métodos trabajan a través de objetos temporales, pero los motores de JavaScript están bien afinados para optimizarlos internamente así que llamarlos no es costoso. diff --git a/1-js/05-data-types/02-number/1-sum-interface/solution.md b/1-js/05-data-types/02-number/1-sum-interface/solution.md index f2c81437d..89a2e7db2 100644 --- a/1-js/05-data-types/02-number/1-sum-interface/solution.md +++ b/1-js/05-data-types/02-number/1-sum-interface/solution.md @@ -1,12 +1,12 @@ ```js run demo -let a = +prompt("The first number?", ""); -let b = +prompt("The second number?", ""); +let a = +prompt("¿El primer número?", ""); +let b = +prompt("¿El segundo número?", ""); alert( a + b ); ``` -Note the unary plus `+` before `prompt`. It immediately converts the value to a number. +Toma nota del más unario `+` antes del `prompt`. Este convierte inmediatamente el valor a `number`. -Otherwise, `a` and `b` would be string their sum would be their concatenation, that is: `"1" + "2" = "12"`. \ No newline at end of file +De otra manera `a` and `b` serían `string`, y la suma, su concatenación: `"1" + "2" = "12"`. diff --git a/1-js/05-data-types/02-number/1-sum-interface/task.md b/1-js/05-data-types/02-number/1-sum-interface/task.md index 780126640..182883ffc 100644 --- a/1-js/05-data-types/02-number/1-sum-interface/task.md +++ b/1-js/05-data-types/02-number/1-sum-interface/task.md @@ -2,10 +2,10 @@ importance: 5 --- -# Sum numbers from the visitor +# Suma números del visitante -Create a script that prompts the visitor to enter two numbers and then shows their sum. +Crea un script que pida al visitante que ingrese dos números y muestre su suma. [demo] -P.S. There is a gotcha with types. +P.D. Hay una trampa con los tipos de valores. diff --git a/1-js/05-data-types/02-number/2-why-rounded-down/solution.md b/1-js/05-data-types/02-number/2-why-rounded-down/solution.md index 4bcd74512..279619266 100644 --- a/1-js/05-data-types/02-number/2-why-rounded-down/solution.md +++ b/1-js/05-data-types/02-number/2-why-rounded-down/solution.md @@ -1,33 +1,32 @@ -Internally the decimal fraction `6.35` is an endless binary. As always in such cases, it is stored with a precision loss. +Internamente, la fracción decimal `6.35` resulta en binario sin fin. Como siempre en estos casos, es almacenado con pérdida de precisión. -Let's see: +Veamos: ```js run alert( 6.35.toFixed(20) ); // 6.34999999999999964473 ``` -The precision loss can cause both increase and decrease of a number. In this particular case the number becomes a tiny bit less, that's why it rounded down. +La pérdida de precisión puede causar que el número incremente o decremente. En este caso particular el número se vuelve ligeramente menor, por ello es redondeado hacia abajo. -And what's for `1.35`? +¿Y qué pasa con `1.35`? ```js run alert( 1.35.toFixed(20) ); // 1.35000000000000008882 ``` -Here the precision loss made the number a little bit greater, so it rounded up. +Aquí la pérdida de precisión hace el número algo mayor, por ello redondea hacia arriba. -**How can we fix the problem with `6.35` if we want it to be rounded the right way?** +**¿Cómo podemos arreglar el problema con `6.35` si queremos redondearlo de manera correcta?** -We should bring it closer to an integer prior to rounding: +Debemos llevarlo más cerca de un entero antes del redondeo: ```js run alert( (6.35 * 10).toFixed(20) ); // 63.50000000000000000000 ``` -Note that `63.5` has no precision loss at all. That's because the decimal part `0.5` is actually `1/2`. Fractions divided by powers of `2` are exactly represented in the binary system, now we can round it: +Observa que `63.5` no tiene pérdida de precisión en absoluto. Esto es porque la parte decimal `0.5` es realmente `1/2`. Fracciones divididas por potencias de `2` son representadas exactamente en el sistema binario, ahora podemos redondearlo: ```js run -alert( Math.round(6.35 * 10) / 10 ); // 6.35 -> 63.5 -> 64(rounded) -> 6.4 +alert( Math.round(6.35 * 10) / 10 ); // 6.35 -> 63.5 -> 64(redondeado) -> 6.4 ``` - diff --git a/1-js/05-data-types/02-number/2-why-rounded-down/task.md b/1-js/05-data-types/02-number/2-why-rounded-down/task.md index 568c26480..6e24bb801 100644 --- a/1-js/05-data-types/02-number/2-why-rounded-down/task.md +++ b/1-js/05-data-types/02-number/2-why-rounded-down/task.md @@ -2,21 +2,21 @@ importance: 4 --- -# Why 6.35.toFixed(1) == 6.3? +# ¿Por qué 6.35.toFixed(1) == 6.3? -According to the documentation `Math.round` and `toFixed` both round to the nearest number: `0..4` lead down while `5..9` lead up. +Según la documentación `Math.round` y `toFixed` redondean al número más cercano: `0..4` hacia abajo mientras `5..9` hacia arriba. -For instance: +Por ejemplo: ```js run alert( 1.35.toFixed(1) ); // 1.4 ``` -In the similar example below, why is `6.35` rounded to `6.3`, not `6.4`? +En el ejemplo similar que sigue, ¿por qué `6.35` es redondeado a `6.3`, y no a `6.4`? ```js run alert( 6.35.toFixed(1) ); // 6.3 ``` -How to round `6.35` the right way? +¿Cómo redondear `6.35` de manera correcta? diff --git a/1-js/05-data-types/02-number/3-repeat-until-number/solution.md b/1-js/05-data-types/02-number/3-repeat-until-number/solution.md index 005116d17..345eee607 100644 --- a/1-js/05-data-types/02-number/3-repeat-until-number/solution.md +++ b/1-js/05-data-types/02-number/3-repeat-until-number/solution.md @@ -4,7 +4,7 @@ function readNumber() { let num; do { - num = prompt("Enter a number please?", 0); + num = prompt("Ingrese un número por favor:", 0); } while ( !isFinite(num) ); if (num === null || num === '') return null; @@ -15,9 +15,8 @@ function readNumber() { alert(`Read: ${readNumber()}`); ``` -The solution is a little bit more intricate that it could be because we need to handle `null`/empty lines. +La solución es un poco más intrincada de lo que podría ser porque necesitamos manejar `null` y líneas vacías. -So we actually accept the input until it is a "regular number". Both `null` (cancel) and empty line also fit that condition, because in numeric form they are `0`. - -After we stopped, we need to treat `null` and empty line specially (return `null`), because converting them to a number would return `0`. +Entonces aceptamos entrada de datos hasta que sea un "número regular". También `null` (cancel) y las líneas vacías encajan en esa condición porque un su forma numérica estos son `0`. +Una vez detenido el ingreso, necesitamos tratar especialmente los casos `null` y línea vacía (return `null`), porque al convertirlos devolverían `0`. diff --git a/1-js/05-data-types/02-number/3-repeat-until-number/task.md b/1-js/05-data-types/02-number/3-repeat-until-number/task.md index 9b172fa8a..885570ba8 100644 --- a/1-js/05-data-types/02-number/3-repeat-until-number/task.md +++ b/1-js/05-data-types/02-number/3-repeat-until-number/task.md @@ -2,13 +2,13 @@ importance: 5 --- -# Repeat until the input is a number +# Repetir hasta que lo ingresado sea un número -Create a function `readNumber` which prompts for a number until the visitor enters a valid numeric value. +Crea una función `readNumber` que pida un número hasta que el visitante ingrese un valor numérico válido. -The resulting value must be returned as a number. +El valor resultante debe ser devuelto como number. -The visitor can also stop the process by entering an empty line or pressing "CANCEL". In that case, the function should return `null`. +El visitante puede también detener el proceso ingresando una linea vacía o presionando "CANCEL". En tal caso la función debe devolver `null`. [demo] diff --git a/1-js/05-data-types/02-number/4-endless-loop-error/solution.md b/1-js/05-data-types/02-number/4-endless-loop-error/solution.md index 8bc55bd02..90d7dec88 100644 --- a/1-js/05-data-types/02-number/4-endless-loop-error/solution.md +++ b/1-js/05-data-types/02-number/4-endless-loop-error/solution.md @@ -1,6 +1,6 @@ -That's because `i` would never equal `10`. +Es porque `i` nunca sería igual a `10`. -Run it to see the *real* values of `i`: +Ejecuta esto para ver los valores *reales* de `i`: ```js run let i = 0; @@ -10,8 +10,8 @@ while (i < 11) { } ``` -None of them is exactly `10`. +Ninguno de ellos es exactamente `10`. -Such things happen because of the precision losses when adding fractions like `0.2`. +Tales cosas suceden por las pérdidas de precisión cuando sumamos decimales como `0.2`. -Conclusion: evade equality checks when working with decimal fractions. \ No newline at end of file +Conclusión: evita chequeos de igualdad al trabajar con números decimales. \ No newline at end of file diff --git a/1-js/05-data-types/02-number/4-endless-loop-error/task.md b/1-js/05-data-types/02-number/4-endless-loop-error/task.md index 592ece31c..531c0a00a 100644 --- a/1-js/05-data-types/02-number/4-endless-loop-error/task.md +++ b/1-js/05-data-types/02-number/4-endless-loop-error/task.md @@ -2,9 +2,9 @@ importance: 4 --- -# An occasional infinite loop +# Un bucle infinito ocasional -This loop is infinite. It never ends. Why? +Este bucle es infinito. Nunca termina, ¿por qué? ```js let i = 0; diff --git a/1-js/05-data-types/02-number/8-random-min-max/solution.md b/1-js/05-data-types/02-number/8-random-min-max/solution.md index 8736c3d56..de0499473 100644 --- a/1-js/05-data-types/02-number/8-random-min-max/solution.md +++ b/1-js/05-data-types/02-number/8-random-min-max/solution.md @@ -1,11 +1,11 @@ -We need to "map" all values from the interval 0..1 into values from `min` to `max`. +Necesitamos hacer un "mapeo" de todos los valores del intervalo 0..1 a valores desde `min` a `max`. -That can be done in two stages: +Esto puede hacerse en dos pasos: -1. If we multiply a random number from 0..1 by `max-min`, then the interval of possible values increases `0..1` to `0..max-min`. -2. Now if we add `min`, the possible interval becomes from `min` to `max`. +1. Si multiplicamos el número aleatorio 0..1 por `max-min`, entonces el intervalo de valores posibles va de `0..1` a `0..max-min`. +2. Ahora si sumamos `min`, el intervalo posible se vuelve desde `min` a `max`. -The function: +La función: ```js run function random(min, max) { diff --git a/1-js/05-data-types/02-number/8-random-min-max/task.md b/1-js/05-data-types/02-number/8-random-min-max/task.md index 7037cfcbb..80e1e0982 100644 --- a/1-js/05-data-types/02-number/8-random-min-max/task.md +++ b/1-js/05-data-types/02-number/8-random-min-max/task.md @@ -2,13 +2,13 @@ importance: 2 --- -# A random number from min to max +# Un número aleatorio entre min y max -The built-in function `Math.random()` creates a random value from `0` to `1` (not including `1`). +La función incorporada `Math.random()` crea un valor aleatorio entre `0` y `1` (no incluyendo `1`). -Write the function `random(min, max)` to generate a random floating-point number from `min` to `max` (not including `max`). +Escribe una función `random(min, max)` para generar un número de punto flotante entre `min` y `max` (no incluyendo `max`). -Examples of its work: +Ejemplos de su funcionamiento: ```js alert( random(1, 5) ); // 1.2345623452 diff --git a/1-js/05-data-types/02-number/9-random-int-min-max/solution.md b/1-js/05-data-types/02-number/9-random-int-min-max/solution.md index 0950ff812..e59ed43c7 100644 --- a/1-js/05-data-types/02-number/9-random-int-min-max/solution.md +++ b/1-js/05-data-types/02-number/9-random-int-min-max/solution.md @@ -1,6 +1,6 @@ -# The simple but wrong solution +# La solución simple, pero equivocada -The simplest, but wrong solution would be to generate a value from `min` to `max` and round it: +La solución más simple, pero equivocada, sería generar un valor entre `min` y `max` y redondearlo: ```js run function randomInteger(min, max) { @@ -11,28 +11,28 @@ function randomInteger(min, max) { alert( randomInteger(1, 3) ); ``` -The function works, but it is incorrect. The probability to get edge values `min` and `max` is two times less than any other. +La función funciona, pero es incorrecta. La probabilidad de obtener los valores extremos `min` y `max` es la mitad de la de los demás. -If you run the example above many times, you would easily see that `2` appears the most often. +Si ejecutas el ejemplo que sigue muchas veces, fácilmente verás que `2` aparece más a menudo. -That happens because `Math.round()` gets random numbers from the interval `1..3` and rounds them as follows: +Esto ocurre porque `Math.round()` obtiene los números del intervalo `1..3` y los redondea como sigue: ```js no-beautify -values from 1 ... to 1.4999999999 become 1 -values from 1.5 ... to 2.4999999999 become 2 -values from 2.5 ... to 2.9999999999 become 3 +valores desde 1 ... hasta 1.4999999999 se vuelven 1 +valores desde 1.5 ... hasta 2.4999999999 se vuelven 2 +valores desde 2.5 ... hasta 2.9999999999 se vuelven 3 ``` -Now we can clearly see that `1` gets twice less values than `2`. And the same with `3`. +Ahora podemos ver claramente que `1` obtiene la mitad de valores que `2`. Y lo mismo con `3`. -# The correct solution +# La solución correcta -There are many correct solutions to the task. One of them is to adjust interval borders. To ensure the same intervals, we can generate values from `0.5 to 3.5`, thus adding the required probabilities to the edges: +Hay muchas soluciones correctas para la tarea. una es ajustar los bordes del intervalo. Para asegurarse los mismos intervalos, podemos generar valores entre `0.5 a 3.5`, así sumando las probabilidades requeridas a los extremos: ```js run *!* function randomInteger(min, max) { - // now rand is from (min-0.5) to (max+0.5) + // ahora rand es desde (min-0.5) hasta (max+0.5) let rand = min - 0.5 + Math.random() * (max - min + 1); return Math.round(rand); } @@ -41,12 +41,12 @@ function randomInteger(min, max) { alert( randomInteger(1, 3) ); ``` -An alternative way could be to use `Math.floor` for a random number from `min` to `max+1`: +Una alternativa es el uso de `Math.floor` para un número aleatorio entre `min` y `max+1`: ```js run *!* function randomInteger(min, max) { - // here rand is from min to (max+1) + // aquí rand es desde min a (max+1) let rand = min + Math.random() * (max + 1 - min); return Math.floor(rand); } @@ -55,12 +55,12 @@ function randomInteger(min, max) { alert( randomInteger(1, 3) ); ``` -Now all intervals are mapped this way: +Ahora todos los intervalos son mapeados de esta forma: ```js no-beautify -values from 1 ... to 1.9999999999 become 1 -values from 2 ... to 2.9999999999 become 2 -values from 3 ... to 3.9999999999 become 3 +valores desde 1 ... hasta 1.9999999999 se vuelven 1 +valores desde 2 ... hasta 2.9999999999 se vuelven 2 +valores desde 3 ... hasta 3.9999999999 se vuelven 3 ``` -All intervals have the same length, making the final distribution uniform. +Todos los intervalos tienen el mismo largo, haciendo la distribución final uniforme. diff --git a/1-js/05-data-types/02-number/9-random-int-min-max/task.md b/1-js/05-data-types/02-number/9-random-int-min-max/task.md index 4ac7b5fbb..de7aa31c5 100644 --- a/1-js/05-data-types/02-number/9-random-int-min-max/task.md +++ b/1-js/05-data-types/02-number/9-random-int-min-max/task.md @@ -2,14 +2,14 @@ importance: 2 --- -# A random integer from min to max +# Un entero aleatorio entre min y max -Create a function `randomInteger(min, max)` that generates a random *integer* number from `min` to `max` including both `min` and `max` as possible values. +Crea una función `randomInteger(min, max)` que genere un número *entero* aleatorio entre `min` y `max` incluyendo ambos, `min` y `max`, como valores posibles. -Any number from the interval `min..max` must appear with the same probability. +Todo número del intervalo `min..max` debe aparecer con la misma probabilidad. -Examples of its work: +Ejemplos de funcionamiento: ```js alert( randomInteger(1, 5) ); // 1 @@ -17,4 +17,4 @@ alert( randomInteger(1, 5) ); // 3 alert( randomInteger(1, 5) ); // 5 ``` -You can use the solution of the [previous task](info:task/random-min-max) as the base. +Puedes usar la solución de la [tarea previa](info:task/random-min-max) como base. diff --git a/1-js/05-data-types/02-number/article.md b/1-js/05-data-types/02-number/article.md index 8e41f673d..0e1d2b0a8 100644 --- a/1-js/05-data-types/02-number/article.md +++ b/1-js/05-data-types/02-number/article.md @@ -1,101 +1,101 @@ -# Numbers +# Números -In modern JavaScript, there are two types of numbers: +En JavaScript moderno, hay dos tipos de números: -1. Regular numbers in JavaScript are stored in 64-bit format [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754), also known as "double precision floating point numbers". These are numbers that we're using most of the time, and we'll talk about them in this chapter. +1. Los números regulares en JavaScript son almacenados con el formato de 64-bit [IEEE-754](https://es.wikipedia.org/wiki/IEEE_754), conocido como "números de doble precisión de coma flotante". Estos números son los que estaremos usando la mayor parte del tiempo, y hablaremos de ellos en este capítulo. -2. BigInt numbers represent integers of arbitrary length. They are sometimes needed because a regular integer number can't safely exceed (253-1) or be less than -(253-1), as we mentioned earlier in the chapter . As bigints are used in a few special areas, we devote them to a special chapter . +2. Los números BigInt representan enteros de longitud arbitraria. A veces son necesarios porque un número entero regular no puede exceder 253 ni ser menor a -253 manteniendo la precisión, algo que mencionamos antes en el capítulo . Como los bigints son usados en áreas muy especiales, les dedicamos un capítulo especial . -So here we'll talk about regular numbers. Let's expand our knowledge of them. +Aquí hablaremos de números regulares. Ampliemos lo que ya sabemos de ellos. -## More ways to write a number +## Más formas de escribir un número -Imagine we need to write 1 billion. The obvious way is: +Imagina que necesitamos escribir mil millones (En inglés "1 billion"). La forma obvia es: ```js let billion = 1000000000; ``` -We also can use underscore `_` as the separator: +También podemos usar guion bajo `_` como separador: ```js let billion = 1_000_000_000; ``` -Here the underscore `_` plays the role of the "[syntactic sugar](https://en.wikipedia.org/wiki/Syntactic_sugar)", it makes the number more readable. The JavaScript engine simply ignores `_` between digits, so it's exactly the same one billion as above. +Aquí `_` es "azúcar sintáctica", hace el número más legible. El motor JavaScript simplemente ignora `_` entre dígitos, así que es exactamente igual al "billion" de más arriba. -In real life though, we try to avoid writing long sequences of zeroes. We're too lazy for that. We'll try to write something like `"1bn"` for a billion or `"7.3bn"` for 7 billion 300 million. The same is true for most large numbers. +Pero en la vida real tratamos de evitar escribir una larga cadena de ceros porque es fácil tipear mal. -In JavaScript, we can shorten a number by appending the letter `"e"` to it and specifying the zeroes count: +En JavaScript, acortamos un número agregando la letra `"e"` y especificando la cantidad de ceros: ```js run -let billion = 1e9; // 1 billion, literally: 1 and 9 zeroes +let billion = 1e9; // 1 billion, literalmente: 1 y 9 ceros -alert( 7.3e9 ); // 7.3 billions (same as 7300000000 or 7_300_000_000) +alert( 7.3e9 ); // 7.3 billions (tanto 7300000000 como 7_300_000_000) ``` -In other words, `e` multiplies the number by `1` with the given zeroes count. +En otras palabras, `"e"` multiplica el número por el `1` seguido de la cantidad de ceros dada. ```js -1e3 === 1 * 1000; // e3 means *1000 -1.23e6 === 1.23 * 1000000; // e6 means *1000000 +1e3 === 1 * 1000; // e3 significa *1000 +1.23e6 === 1.23 * 1000000; // e6 significa *1000000 ``` -Now let's write something very small. Say, 1 microsecond (one-millionth of a second): +Ahora escribamos algo muy pequeño. Digamos 1 microsegundo (un millonésimo de segundo): ```js let mсs = 0.000001; ``` -Just like before, using `"e"` can help. If we'd like to avoid writing the zeroes explicitly, we could write the same as: +Igual que antes, el uso de `"e"` puede ayudar. Si queremos evitar la escritura de ceros explícitamente, podríamos expresar lo mismo como: ```js -let mcs = 1e-6; // five zeroes to the left from 1 +let mcs = 1e-6; // cinco ceros a la izquierda de 1 ``` -If we count the zeroes in `0.000001`, there are 6 of them. So naturally it's `1e-6`. +Si contamos los ceros en `0.000001`, hay 6 de ellos en total. Entonces naturalmente es `1e-6`. -In other words, a negative number after `"e"` means a division by 1 with the given number of zeroes: +En otras palabras, un número negativo detrás de `"e"` significa una división por el 1 seguido de la cantidad dada de ceros: ```js -// -3 divides by 1 with 3 zeroes +// -3 divide por 1 con 3 ceros 1e-3 === 1 / 1000; // 0.001 -// -6 divides by 1 with 6 zeroes +// -6 divide por 1 con 6 ceros 1.23e-6 === 1.23 / 1000000; // 0.00000123 -// an example with a bigger number -1234e-2 === 1234 / 100; // 12.34, decimal point moves 2 times +// un ejemplo con un número mayor +1234e-2 === 1234 / 100; // 12.34, el punto decimal se mueve 2 veces ``` -### Hex, binary and octal numbers +### Números hexadecimales, binarios y octales -[Hexadecimal](https://en.wikipedia.org/wiki/Hexadecimal) numbers are widely used in JavaScript to represent colors, encode characters, and for many other things. So naturally, there exists a shorter way to write them: `0x` and then the number. +Los números [Hexadecimales](https://es.wikipedia.org/wiki/Sistema_hexadecimal) son ampliamente usados en JavaScript para representar colores, codificar caracteres y muchas otras cosas. Es natural que exista una forma breve de escribirlos: `0x` y luego el número. -For instance: +Por ejemplo: ```js run alert( 0xff ); // 255 -alert( 0xFF ); // 255 (the same, case doesn't matter) +alert( 0xFF ); // 255 (lo mismo en mayúsculas o minúsculas ) ``` -Binary and octal numeral systems are rarely used, but also supported using the `0b` and `0o` prefixes: +Los sistemas binario y octal son raramente usados, pero también soportados mediante el uso de los prefijos `0b` y `0o`: ```js run -let a = 0b11111111; // binary form of 255 -let b = 0o377; // octal form of 255 +let a = 0b11111111; // binario de 255 +let b = 0o377; // octal de 255 -alert( a == b ); // true, the same number 255 at both sides +alert( a == b ); // true, el mismo número 255 en ambos lados ``` -There are only 3 numeral systems with such support. For other numeral systems, we should use the function `parseInt` (which we will see later in this chapter). +Solo 3 sistemas numéricos tienen tal soporte. Para otros sistemas numéricos, debemos usar la función `parseInt` (que veremos luego en este capítulo). ## toString(base) -The method `num.toString(base)` returns a string representation of `num` in the numeral system with the given `base`. +El método `num.toString(base)` devuelve la representación `num` en una cadena, en el sistema numérico con la `base` especificada. -For example: +Ejemplo: ```js run let num = 255; @@ -103,46 +103,46 @@ alert( num.toString(16) ); // ff alert( num.toString(2) ); // 11111111 ``` -The `base` can vary from `2` to `36`. By default, it's `10`. +La `base` puede variar entre `2` y `36`. La predeterminada es `10`. -Common use cases for this are: +Casos de uso común son: -- **base=16** is used for hex colors, character encodings etc, digits can be `0..9` or `A..F`. -- **base=2** is mostly for debugging bitwise operations, digits can be `0` or `1`. -- **base=36** is the maximum, digits can be `0..9` or `A..Z`. The whole Latin alphabet is used to represent a number. A funny, but useful case for `36` is when we need to turn a long numeric identifier into something shorter, for example, to make a short url. Can simply represent it in the numeral system with base `36`: +- **base=16** usada para colores en hexa, codificación de caracteres, etc.; los dígitos pueden ser `0..9` o `A..F`. +- **base=2** mayormente usada para la depuración de operaciones de bit, los dígitos pueden ser `0` o `1`. +- **base=36** Es la base máxima, los dígitos pueden ser `0..9` o `A..Z`. Aquí el alfabeto inglés completo es usado para representar un número. Un uso peculiar pero práctico para la base `36` es cuando necesitamos convertir un largo identificador numérico en algo más corto, por ejemplo para abreviar una url. Podemos simplemente representarlo en el sistema numeral de base `36`: ```js run alert( 123456..toString(36) ); // 2n9c ``` -```warn header="Two dots to call a method" -Please note that two dots in `123456..toString(36)` is not a typo. If we want to call a method directly on a number, like `toString` in the example above, then we need to place two dots `..` after it. +```warn header="Dos puntos para llamar un método" +Por favor observa que los dos puntos en `123456..toString(36)` no son un error tipográfico. Si queremos llamar un método directamente sobre el número, como `toString` del ejemplo anterior, necesitamos ubicar los dos puntos `..` tras él. -If we placed a single dot: `123456.toString(36)`, then there would be an error, because JavaScript syntax implies the decimal part after the first dot. And if we place one more dot, then JavaScript knows that the decimal part is empty and now uses the method. +Si pusiéramos un único punto: `123456.toString(36)`, habría un error, porque la sintaxis de JavaScript espera una parte decimal después del primer punto. Al ver el segundo punto, JavaScript reconoce que la parte decimal está vacía y le sigue un método. -Also could write `(123456).toString(36)`. +También podríamos escribir `(123456).toString(36)`. ``` -## Rounding +## Redondeo -One of the most used operations when working with numbers is rounding. +Una de las operaciones más usadas cuando se trabaja con números es el redondeo. -There are several built-in functions for rounding: +Hay varias funciones incorporadas para el redondeo: `Math.floor` -: Rounds down: `3.1` becomes `3`, and `-1.1` becomes `-2`. +: Redondea hacia abajo: `3.1` se convierte en `3`, y `-1.1` se hace `-2`. `Math.ceil` -: Rounds up: `3.1` becomes `4`, and `-1.1` becomes `-1`. +: Redondea hacia arriba: `3.1` torna en `4`, y `-1.1` torna en `-1`. `Math.round` -: Rounds to the nearest integer: `3.1` becomes `3`, `3.6` becomes `4`. In the middle cases `3.5` rounds up to `4`, and `-3.5` rounds up to `-3`. +: Redondea hacia el entero más cercano: `3.1` redondea a `3`, `3.6` redondea a `4`; los casos medios `3.5` redondea a `4`, y `-3.5` redondea a `-3`. -`Math.trunc` (not supported by Internet Explorer) -: Removes anything after the decimal point without rounding: `3.1` becomes `3`, `-1.1` becomes `-1`. +`Math.trunc` (no soportado en Internet Explorer) +: Remueve lo que haya tras el punto decimal sin redondear: `3.1` torna en `3`, `-1.1` torna en `-1`. -Here's the table to summarize the differences between them: +Aquí, la tabla que resume las diferencias entre ellos: | | `Math.floor` | `Math.ceil` | `Math.round` | `Math.trunc` | |---|---------|--------|---------|---------| @@ -154,75 +154,75 @@ Here's the table to summarize the differences between them: |`-1.6`| `-2` | `-1` | `-2` | `-1` | -These functions cover all of the possible ways to deal with the decimal part of a number. But what if we'd like to round the number to `n-th` digit after the decimal? +Estas funciones cubren todas las posibles formas de lidiar con la parte decimal de un número. Pero ¿si quisiéramos redondear al enésimo `n-th` dígito tras el decimal? -For instance, we have `1.2345` and want to round it to 2 digits, getting only `1.23`. +Por ejemplo, tenemos `1.2345` y queremos redondearlo a 2 dígitos obteniendo solo `1.23`. -There are two ways to do so: +Hay dos formas de hacerlo: -1. Multiply-and-divide. +1. Multiplicar y dividir. - For example, to round the number to the 2nd digit after the decimal, we can multiply the number by `100`, call the rounding function and then divide it back. + Por ejemplo, para redondear el número a dos dígitos tras el decimal, podemos multiplicarlo por `100`, llamar la función de redondeo y entonces volverlo a dividir. ```js run let num = 1.23456; alert( Math.round(num * 100) / 100 ); // 1.23456 -> 123.456 -> 123 -> 1.23 ``` -2. The method [toFixed(n)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed) rounds the number to `n` digits after the point and returns a string representation of the result. +2. El método [toFixed(n)](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Number/toFixed) redondea el número a `n` dígitos después del punto decimal y devuelve una cadena que representa el resultado. ```js run let num = 12.34; alert( num.toFixed(1) ); // "12.3" ``` - This rounds up or down to the nearest value, similar to `Math.round`: + Redondea hacia arriba o abajo al valor más cercano, similar a `Math.round`: ```js run let num = 12.36; alert( num.toFixed(1) ); // "12.4" ``` - Please note that the result of `toFixed` is a string. If the decimal part is shorter than required, zeroes are appended to the end: + Ten en cuenta que el resultado de `toFixed` es una cadena. Si la parte decimal es más corta que lo requerido, se agregan ceros hasta el final: ```js run let num = 12.34; - alert( num.toFixed(5) ); // "12.34000", added zeroes to make exactly 5 digits + alert( num.toFixed(5) ); // "12.34000", con ceros agregados para dar exactamente 5 dígitos ``` - We can convert it to a number using the unary plus or a `Number()` call, e.g. write `+num.toFixed(5)`. + Podemos convertirlo a `number` usando el operador unario más `+` o llamando a `Number()`. Por ejemplo, `+num.toFixed(5)`. -## Imprecise calculations +## Cálculo impreciso -Internally, a number is represented in 64-bit format [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754), so there are exactly 64 bits to store a number: 52 of them are used to store the digits, 11 of them store the position of the decimal point, and 1 bit is for the sign. +Internamente, un número es representado en formato de 64-bit [IEEE-754](https://es.wikipedia.org/wiki/IEEE_754), donde hay exactamente 64 bits para almacenar un número: 52 de ellos son usados para almacenar los dígitos, 11 para almacenar la posición del punto decimal, y 1 bit es para el signo. -If a number is really huge, it may overflow the 64-bit storage and become a special numeric value `Infinity`: +Si un número es verdaderamente grande, puede rebasar el almacén de 64 bit y obtenerse el valor numérico `Infinity`: ```js run alert( 1e500 ); // Infinity ``` -What may be a little less obvious, but happens quite often, is the loss of precision. +Lo que puede ser algo menos obvio, pero ocurre a menudo, es la pérdida de precisión. -Consider this (falsy!) equality test: +Considera este (¡falso!) test de igualdad: ```js run alert( 0.1 + 0.2 == 0.3 ); // *!*false*/!* ``` -That's right, if we check whether the sum of `0.1` and `0.2` is `0.3`, we get `false`. +Es así, al comprobar si la suma de `0.1` y `0.2` es `0.3`, obtenemos `false`. -Strange! What is it then if not `0.3`? +¡Qué extraño! ¿Qué es si no `0.3`? ```js run alert( 0.1 + 0.2 ); // 0.30000000000000004 ``` -Ouch! Imagine you're making an e-shopping site and the visitor puts `$0.10` and `$0.20` goods into their cart. The order total will be `$0.30000000000000004`. That would surprise anyone. +¡Ay! Imagina que estás haciendo un sitio de compras electrónicas y el visitante pone `$0.10` y `$0.20` en productos en su carrito. El total de la orden será `$0.30000000000000004`. Eso sorprendería a cualquiera.. -But why does this happen? +¿Pero por qué pasa esto? -A number is stored in memory in its binary form, a sequence of bits - ones and zeroes. But fractions like `0.1`, `0.2` that look simple in the decimal numeric system are actually unending fractions in their binary form. +Un número es almacenado en memoria en su forma binaria, una secuencia de bits, unos y ceros. Pero decimales como `0.1`, `0.2` que se ven simples en el sistema decimal son realmente fracciones sin fin en su forma binaria. ```js run alert(0.1.toString(2)); // 0.0001100110011001100110011001100110011001100110011001101 @@ -230,217 +230,216 @@ alert(0.2.toString(2)); // 0.001100110011001100110011001100110011001100110011001 alert((0.1 + 0.2).toString(2)); // 0.0100110011001100110011001100110011001100110011001101 ``` -What is `0.1`? It is one divided by ten `1/10`, one-tenth. In the decimal numeral system, such numbers are easily representable. Compare it to one-third: `1/3`. It becomes an endless fraction `0.33333(3)`. +¿Qué es `0.1`? Es un uno dividido por 10, `1/10`, un décimo. En sistema decimal es fácilmente representable. Compáralo con un tercio: `1/3`, que se vuelve una fracción sin fin `0.33333(3)`. -So, division by powers `10` is guaranteed to work well in the decimal system, but division by `3` is not. For the same reason, in the binary numeral system, the division by powers of `2` is guaranteed to work, but `1/10` becomes an endless binary fraction. +Así, la división en potencias de diez garantizan un buen funcionamiento en el sistema decimal, pero divisiones por `3` no. Por la misma razón, en el sistema binario la división en potencias de `2` garantizan su funcionamiento, pero `1/10` se vuelve una fracción binaria sin fin. -There's just no way to store *exactly 0.1* or *exactly 0.2* using the binary system, just like there is no way to store one-third as a decimal fraction. +Simplemente no hay manera de guardar *exactamente 0.1* o *exactamente 0.2* usando el sistema binario, así como no hay manera de guardar un tercio en fracción decimal. -The numeric format IEEE-754 solves this by rounding to the nearest possible number. These rounding rules normally don't allow us to see that "tiny precision loss", but it exists. +El formato numérico IEEE-754 resuelve esto redondeando al número posible más cercano. Estas reglas de redondeo normalmente no nos permiten percibir aquella "pequeña pérdida de precisión", pero existe. -We can see this in action: +Podemos verlo en acción: ```js run alert( 0.1.toFixed(20) ); // 0.10000000000000000555 ``` -And when we sum two numbers, their "precision losses" add up. +Y cuando sumamos dos números, sus "pérdidas de precisión" se acumulan. -That's why `0.1 + 0.2` is not exactly `0.3`. +Y es por ello que `0.1 + 0.2` no es exactamente `0.3`. -```smart header="Not only JavaScript" -The same issue exists in many other programming languages. +```smart header="No solo JavaScript" +El mismo problema existe en muchos otros lenguajes de programación. -PHP, Java, C, Perl, and Ruby give exactly the same result, because they are based on the same numeric format. +PHP, Java, C, Perl, Ruby, dan exactamente el mismo resultado, porque ellos están basados en el mismo formato numérico. ``` -Can we work around the problem? Sure, the most reliable method is to round the result with the help of a method [toFixed(n)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed): +¿Podemos resolver el problema? Seguro, la forma más confiable es redondear el resultado con la ayuda de un método. [toFixed(n)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed): ```js run let sum = 0.1 + 0.2; alert( sum.toFixed(2) ); // "0.30" ``` -Please note that `toFixed` always returns a string. It ensures that it has 2 digits after the decimal point. That's actually convenient if we have an e-shopping and need to show `$0.30`. For other cases, we can use the unary plus to coerce it into a number: +Ten en cuenta que `toFixed` siempre devuelve un string. Esto asegura que tiene 2 dígitos después del punto decimal. Esto es en verdad conveniente si tenemos un sitio de compras y necesitamos mostrar `$0.30`. Para otros casos, podemos usar el + unario para forzar un número: ```js run let sum = 0.1 + 0.2; alert( +sum.toFixed(2) ); // 0.3 ``` -We also can temporarily multiply the numbers by 100 (or a bigger number) to turn them into integers, do the maths, and then divide back. Then, as we're doing maths with integers, the error somewhat decreases, but we still get it on division: +También podemos multiplicar temporalmente por 100 (o un número mayor) para transformarlos a enteros, hacer las cuentas, y volverlos a dividir. Como hacemos las cuentas con enteros el error se reduce, pero aún lo tenemos en la división: ```js run alert( (0.1 * 10 + 0.2 * 10) / 10 ); // 0.3 alert( (0.28 * 100 + 0.14 * 100) / 100); // 0.4200000000000001 ``` -So, the multiply/divide approach reduces the error, but doesn't remove it totally. +Entonces el enfoque de multiplicar/dividir reduce el error, pero no lo elimina por completo. -Sometimes we could try to evade fractions at all. Like if we're dealing with a shop, then we can store prices in cents instead of dollars. But what if we apply a discount of 30%? In practice, totally evading fractions is rarely possible. Just round them to cut "tails" when needed. +A veces podemos tratar de evitar los decimales del todo. Si estamos tratando con una tienda, podemos almacenar precios en centavos en lugar de dólares. Pero ¿y si aplicamos un descuento de 30%? En la práctica, evitar la parte decimal por completo es raramente posible. Simplemente se redondea y se corta el "rabo" decimal cuando es necesario. -````smart header="The funny thing" -Try running this: +````smart header="Algo peculiar" +Prueba ejecutando esto: ```js run -// Hello! I'm a self-increasing number! -alert( 9999999999999999 ); // shows 10000000000000000 +// ¡Hola! ¡Soy un número que se autoincrementa! +alert( 9999999999999999 ); // muestra 10000000000000000 ``` -This suffers from the same issue: a loss of precision. There are 64 bits for the number, 52 of them can be used to store digits, but that's not enough. So the least significant digits disappear. +Esto sufre del mismo problema: Una pérdida de precisión. Hay 64 bits para el número, 52 de ellos pueden ser usados para almacenar dígitos, pero no es suficiente. Entonces los dígitos menos significativos desaparecen. -JavaScript doesn't trigger an error in such events. It does its best to fit the number into the desired format, but unfortunately, this format is not big enough. +JavaScript no dispara error en tales eventos. Hace lo mejor que puede para ajustar el número al formato deseado, pero desafortunadamente este formato no es suficientemente grande. ```` -```smart header="Two zeroes" -Another funny consequence of the internal representation of numbers is the existence of two zeroes: `0` and `-0`. +```smart header="Dos ceros" +Otra consecuencia peculiar de la representación interna de los números es la existencia de dos ceros: `0` y `-0`. -That's because a sign is represented by a single bit, so it can be set or not set for any number including a zero. +Esto es porque el signo es representado por un bit, así cada número puede ser positivo o negativo, incluyendo al cero. -In most cases, the distinction is unnoticeable, because operators are suited to treat them as the same. +En la mayoría de los casos la distinción es imperceptible, porque los operadores están adaptados para tratarlos como iguales. ``` -## Tests: isFinite and isNaN +## Tests: isFinite e isNaN -Remember these two special numeric values? +¿Recuerdas estos dos valores numéricos especiales? -- `Infinity` (and `-Infinity`) is a special numeric value that is greater (less) than anything. -- `NaN` represents an error. +- `Infinity` (y `-Infinity`) es un valor numérico especial que es mayor (menor) que cualquier otra cosa. +- `NaN` ("No un Número") representa un error. -They belong to the type `number`, but are not "normal" numbers, so there are special functions to check for them: +Ambos pertenecen al tipo `number`, pero no son números "normales", así que hay funciones especiales para chequearlos: -- `isNaN(value)` converts its argument to a number and then tests it for being `NaN`: +- `isNaN(value)` convierte su argumento a número entonces testea si es `NaN`: ```js run alert( isNaN(NaN) ); // true alert( isNaN("str") ); // true ``` - But do we need this function? Can't we just use the comparison `=== NaN`? Unfortunately not. The value `NaN` is unique in that it does not equal anything, including itself: + Pero ¿necesitamos esta función? ¿No podemos simplemente usar la comparación `=== NaN`? Desafortunadamente no. El valor `NaN` es único en que no es igual a nada, incluyendo a sí mismo: ```js run alert( NaN === NaN ); // false ``` -- `isFinite(value)` converts its argument to a number and returns `true` if it's a regular number, not `NaN/Infinity/-Infinity`: +- `isFinite(value)` convierte su argumento a un número y devuelve `true` si es un número regular, no `NaN/Infinity/-Infinity`: ```js run alert( isFinite("15") ); // true - alert( isFinite("str") ); // false, because a special value: NaN - alert( isFinite(Infinity) ); // false, because a special value: Infinity + alert( isFinite("str") ); // false, porque es un valor especial: NaN + alert( isFinite(Infinity) ); // false, porque es un valor especial: Infinity ``` -Sometimes `isFinite` is used to validate whether a string value is a regular number: +A veces `isFinite` es usado para validar si un valor string es un número regular: ```js run let num = +prompt("Enter a number", ''); -// will be true unless you enter Infinity, -Infinity or not a number +// siempre true salvo que ingreses Infinity, -Infinity o un valor no numérico alert( isFinite(num) ); ``` -Please note that an empty or a space-only string is treated as `0` in all numeric functions including `isFinite`. +Ten en cuenta que un valor vacío o un string de solo espacios es tratado como `0` en todas las funciones numéricas incluyendo `isFinite`. -````smart header="`Number.isNaN` and `Number.isFinite`" -[Number.isNaN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN) and [Number.isFinite](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isFinite) methods are the more "strict" versions of `isNaN` and `isFinite` functions. They do not autoconvert their argument into a number, but check if it belongs to the `number` type instead. +````smart header="`Number.isNaN` y `Number.isFinite`" +Los métodos [Number.isNaN](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN) y [Number.isFinite](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Number/isFinite) son versiones más estrictas de las funciones `isNaN` e `isFinite`. No autoconvierten sus argumentos a `number`, en cambio verifican que pertenezcan al tipo de dato `number`. -- `Number.isNaN(value)` returns `true` if the argument belongs to the `number` type and it is `NaN`. In any other case, it returns `false`. +- `Number.isNaN(value)` devuelve `true` si el argumento pertenece al tipo de dato `number` y si es `NaN`. En cualquier otro caso devuelve `false`. ```js run alert( Number.isNaN(NaN) ); // true alert( Number.isNaN("str" / 2) ); // true - // Note the difference: - alert( Number.isNaN("str") ); // false, because "str" belongs to the string type, not the number type - alert( isNaN("str") ); // true, because isNaN converts string "str" into a number and gets NaN as a result of this conversion + // Note la diferencia: + alert( Number.isNaN("str") ); // false, porque "str" pertenece a al tipo string, no al tipo number + alert( isNaN("str") ); // true, porque isNaN convierte el string "str" a number y obtiene NaN como resultado de su conversión ``` -- `Number.isFinite(value)` returns `true` if the argument belongs to the `number` type and it is not `NaN/Infinity/-Infinity`. In any other case, it returns `false`. +- `Number.isFinite(value)` devuelve `true` si el argumento pertenece al tipo de dato `number` y no es `NaN/Infinity/-Infinity`. En cualquier otro caso devuelve `false`. ```js run alert( Number.isFinite(123) ); // true alert( Number.isFinite(Infinity) ); // false alert( Number.isFinite(2 / 0) ); // false - // Note the difference: - alert( Number.isFinite("123") ); // false, because "123" belongs to the string type, not the number type - alert( isFinite("123") ); // true, because isFinite converts string "123" into a number 123 + // Note la diferencia: + alert( Number.isFinite("123") ); // false, porque "123" pertenece a "string", no a "number" + alert( isFinite("123") ); // true, porque isFinite convierte el string "123" al number 123 ``` -In a way, `Number.isNaN` and `Number.isFinite` are simpler and more straightforward than `isNaN` and `isFinite` functions. In practice though, `isNaN` and `isFinite` are mostly used, as they're shorter to write. +En un sentido, `Number.isNaN` y `Number.isFinite` son más simples y directas que las funciones `isNaN` e `isFinite`. Pero en la práctica `isNaN` e `isFinite` son las más usadas, porque son más cortas. ```` -```smart header="Comparison with `Object.is`" -There is a special built-in method `Object.is` that compares values like `===`, but is more reliable for two edge cases: +```smart header="Comparación con `Object.is`" +Existe un método nativo especial, `Object.is`, que compara valores al igual que `===`, pero es más confiable para dos casos extremos: -1. It works with `NaN`: `Object.is(NaN, NaN) === true`, that's a good thing. -2. Values `0` and `-0` are different: `Object.is(0, -0) === false`, technically that's correct because internally the number has a sign bit that may be different even if all other bits are zeroes. +1. Funciona con `NaN`: `Object.is(NaN, NaN) === true`, lo que es una buena cosa. +2. Los valores `0` y `-0` son diferentes: `Object.is(0, -0) === false`. `false` es técnicamente correcto, porque internamente el número puede tener el bit de signo diferente incluso aunque todos los demás bits sean ceros. -In all other cases, `Object.is(a, b)` is the same as `a === b`. +En todos los demás casos, `Object.is(a, b)` equivale a `a === b`. -We mention `Object.is` here, because it's often used in JavaScript specification. When an internal algorithm needs to compare two values for being exactly the same, it uses `Object.is` (internally called [SameValue](https://tc39.github.io/ecma262/#sec-samevalue)). +Mencionamos `Object.is` aquí porque se usa a menudo en la especificación JavaScript. Cuando un algoritmo interno necesita comparar que dos valores sean exactamente iguales, usa `Object.is` (internamente llamado [SameValue](https://tc39.github.io/ecma262/#sec-samevalue)). ``` +## parseInt y parseFloat -## parseInt and parseFloat - -Numeric conversion using a plus `+` or `Number()` is strict. If a value is not exactly a number, it fails: +La conversión numérica usando un más `+` o `Number()` es estricta. Si un valor no es exactamente un número, falla: ```js run alert( +"100px" ); // NaN ``` -The sole exception is spaces at the beginning or at the end of the string, as they are ignored. +Siendo la única excepción los espacios al principio y al final del string, pues son ignorados. -But in real life, we often have values in units, like `"100px"` or `"12pt"` in CSS. Also in many countries, the currency symbol goes after the amount, so we have `"19€"` and would like to extract a numeric value out of that. +Pero en la vida real a menudo tenemos valores en unidades como `"100px"` o `"12pt"` en CSS. También el símbolo de moneda que en varios países va después del monto, tenemos `"19€"` y queremos extraerle la parte numérica. -That's what `parseInt` and `parseFloat` are for. +Para eso sirven `parseInt` y `parseFloat`. -They "read" a number from a string until they can't. In case of an error, the gathered number is returned. The function `parseInt` returns an integer, whilst `parseFloat` will return a floating-point number: +Estas "leen" el número desde un string hasta que dejan de poder hacerlo. Cuando se topa con un error devuelve el número que haya registrado hasta ese momento. La función `parseInt` devuelve un entero, mientras que `parseFloat` devolverá un punto flotante: ```js run alert( parseInt('100px') ); // 100 alert( parseFloat('12.5em') ); // 12.5 -alert( parseInt('12.3') ); // 12, only the integer part is returned -alert( parseFloat('12.3.4') ); // 12.3, the second point stops the reading +alert( parseInt('12.3') ); // 12, devuelve solo la parte entera +alert( parseFloat('12.3.4') ); // 12.3, el segundo punto detiene la lectura ``` -There are situations when `parseInt/parseFloat` will return `NaN`. It happens when no digits could be read: +Hay situaciones en que `parseInt/parseFloat` devolverán `NaN`. Ocurre cuando no puedo encontrar dígitos: ```js run -alert( parseInt('a123') ); // NaN, the first symbol stops the process +alert( parseInt('a123') ); // NaN, el primer símbolo detiene la lectura ``` -````smart header="The second argument of `parseInt(str, radix)`" -The `parseInt()` function has an optional second parameter. It specifies the base of the numeral system, so `parseInt` can also parse strings of hex numbers, binary numbers and so on: +````smart header="El segundo argumento de `parseInt(str, radix)`" +La función `parseInt()` tiene un segundo parámetro opcional. Este especifica la base de sistema numérico, entonces `parseInt` puede también analizar cadenas de números hexa, binarios y otros: ```js run alert( parseInt('0xff', 16) ); // 255 -alert( parseInt('ff', 16) ); // 255, without 0x also works +alert( parseInt('ff', 16) ); // 255, sin 0x también funciona alert( parseInt('2n9c', 36) ); // 123456 ``` ```` -## Other math functions +## Otras funciones matemáticas -JavaScript has a built-in [Math](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Math) object which contains a small library of mathematical functions and constants. +JavaScript tiene un objeto incorporado [Math](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Math) que contiene una pequeña biblioteca de funciones matemáticas y constantes. -A few examples: +Unos ejemplos: `Math.random()` -: Returns a random number from 0 to 1 (not including 1). +: Devuelve un número aleatorio entre 0 y 1 (no incluyendo 1) ```js run alert( Math.random() ); // 0.1234567894322 alert( Math.random() ); // 0.5435252343232 - alert( Math.random() ); // ... (any random numbers) + alert( Math.random() ); // ... (cualquier número aleatorio) ``` -`Math.max(a, b, c...)` and `Math.min(a, b, c...)` -: Returns the greatest and smallest from the arbitrary number of arguments. +`Math.max(a, b, c...)` y `Math.min(a, b, c...)` +: Devuelven el mayor y el menor de entre una cantidad arbitraria de argumentos. ```js run alert( Math.max(3, 5, -10, 0, 1) ); // 5 @@ -448,43 +447,43 @@ A few examples: ``` `Math.pow(n, power)` -: Returns `n` raised to the given power. +: Devuelve `n` elevado a la potencia `power` dada ```js run - alert( Math.pow(2, 10) ); // 2 in power 10 = 1024 + alert( Math.pow(2, 10) ); // 2 elevado a la potencia de 10 = 1024 ``` -There are more functions and constants in `Math` object, including trigonometry, which you can find in the [docs for the Math object](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Math). +Hay más funciones y constantes en el objeto `Math`, incluyendo trigonometría, que puedes encontrar en la [documentación del objeto Math](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Math). -## Summary +## Resumen -To write numbers with many zeroes: +Para escribir números con muchos ceros: -- Append `"e"` with the zeroes count to the number. Like: `123e6` is the same as `123` with 6 zeroes `123000000`. -- A negative number after `"e"` causes the number to be divided by 1 with given zeroes. E.g. `123e-6` means `0.000123` (`123` millionths). +- Agregar `"e"` con la cantidad de ceros al número. Como: `123e6` es `123` con 6 ceros `123000000`. +- un número negativo después de `"e"` causa que el número sea dividido por 1 con los ceros dados:. `123e-6` significa `0.000123` (`123` millonésimos). -For different numeral systems: +Para sistemas numéricos diferentes: -- Can write numbers directly in hex (`0x`), octal (`0o`) and binary (`0b`) systems. -- `parseInt(str, base)` parses the string `str` into an integer in numeral system with given `base`, `2 ≤ base ≤ 36`. -- `num.toString(base)` converts a number to a string in the numeral system with the given `base`. +- Se pueden escribir números directamente en sistemas hexa (`0x`), octal (`0o`) y binario (`0b`). +- `parseInt(str, base)` convierte un string a un entero en el sistema numérico de la `base` dada `base`, `2 ≤ base ≤ 36`. +- `num.toString(base)` convierte un número a string en el sistema de la `base` dada. -For regular number tests: +Para tests de números regulares: -- `isNaN(value)` converts its argument to a number and then tests it for being `NaN` -- `Number.isNaN(value)` checks whether its argument belongs to the `number` type, and if so, tests it for being `NaN` -- `isFinite(value)` converts its argument to a number and then tests it for not being `NaN/Infinity/-Infinity` -- `Number.isFinite(value)` checks whether its argument belongs to the `number` type, and if so, tests it for not being `NaN/Infinity/-Infinity` +- `isNaN(value)` convierte su argumento a number y luego verifica si es `NaN` +- - `Number.isNaN(value)` verifica que el tipo de dato sea `number`, y si lo es, verifica si es `NaN` +- `isFinite(value)` convierte su argumento a number y devuelve `true` si es un número regular, no `NaN/Infinity/-Infinity` +- `Number.isFinite(value)` verifica que el tipo de dato sea `number`, y si lo es, verifica que no sea `NaN/Infinity/-Infinity` -For converting values like `12pt` and `100px` to a number: +Para convertir valores como `12pt` y `100px` a un número: -- Use `parseInt/parseFloat` for the "soft" conversion, which reads a number from a string and then returns the value they could read before the error. +- Usa `parseInt/parseFloat` para una conversión "suave", que lee un número desde un string y devuelve el valor del número que pudiera leer antes de encontrar error. -For fractions: +Para números con decimales: -- Round using `Math.floor`, `Math.ceil`, `Math.trunc`, `Math.round` or `num.toFixed(precision)`. -- Make sure to remember there's a loss of precision when working with fractions. +- Redondea usando `Math.floor`, `Math.ceil`, `Math.trunc`, `Math.round` o `num.toFixed(precision)`. +- Asegúrate de recordar que hay pérdida de precisión cuando se trabaja con decimales. -More mathematical functions: +Más funciones matemáticas: -- See the [Math](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Math) object when you need them. The library is very small but can cover basic needs. +- Revisa el documento del objeto [Math](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Math) cuando las necesites. La biblioteca es pequeña, pero puede cubrir las necesidades básicas. diff --git a/1-js/05-data-types/03-string/1-ucfirst/_js.view/solution.js b/1-js/05-data-types/03-string/1-ucfirst/_js.view/solution.js index 20e687a4d..9d8e469b4 100644 --- a/1-js/05-data-types/03-string/1-ucfirst/_js.view/solution.js +++ b/1-js/05-data-types/03-string/1-ucfirst/_js.view/solution.js @@ -2,4 +2,4 @@ function ucFirst(str) { if (!str) return str; return str[0].toUpperCase() + str.slice(1); -} \ No newline at end of file +} diff --git a/1-js/05-data-types/03-string/1-ucfirst/solution.md b/1-js/05-data-types/03-string/1-ucfirst/solution.md index be5dd2aaf..26f33c206 100644 --- a/1-js/05-data-types/03-string/1-ucfirst/solution.md +++ b/1-js/05-data-types/03-string/1-ucfirst/solution.md @@ -1,14 +1,14 @@ -We can't "replace" the first character, because strings in JavaScript are immutable. +No podemos "reemplazar" el primer carácter, debido a que los strings en JavaScript son inmutables. -But we can make a new string based on the existing one, with the uppercased first character: +Pero podemos hacer un nuevo string basado en el existente, con el primer carácter en mayúsculas: ```js let newStr = str[0].toUpperCase() + str.slice(1); ``` -There's a small problem though. If `str` is empty, then `str[0]` is `undefined`, and as `undefined` doesn't have the `toUpperCase()` method, we'll get an error. +Sin embargo, hay un pequeño problema. Si `str` está vacío, entonces `str[0]` es `undefined`, y como `undefined` no tiene el método `toUpperCase()`, obtendremos un error. -The easiest way out is to add a test for an empty string, like this: +Lo más fácil es agregar una verificación de cadena vacía: ```js run demo function ucFirst(str) { diff --git a/1-js/05-data-types/03-string/1-ucfirst/task.md b/1-js/05-data-types/03-string/1-ucfirst/task.md index ed8a1e6a7..0a0f0e2bc 100644 --- a/1-js/05-data-types/03-string/1-ucfirst/task.md +++ b/1-js/05-data-types/03-string/1-ucfirst/task.md @@ -2,11 +2,10 @@ importance: 5 --- -# Uppercase the first character +# Hacer mayúscula el primer carácter -Write a function `ucFirst(str)` that returns the string `str` with the uppercased first character, for instance: +Escribe una función `ucFirst(str)` que devuelva el string `str` con el primer carácter en mayúscula, por ejemplo: ```js ucFirst("john") == "John"; ``` - diff --git a/1-js/05-data-types/03-string/2-check-spam/solution.md b/1-js/05-data-types/03-string/2-check-spam/solution.md index de8dde57d..1634cc306 100644 --- a/1-js/05-data-types/03-string/2-check-spam/solution.md +++ b/1-js/05-data-types/03-string/2-check-spam/solution.md @@ -1,4 +1,4 @@ -To make the search case-insensitive, let's bring the string to lower case and then search: +Para que la búsqueda no distinga entre mayúsculas y minúsculas, llevemos el string a minúsculas y luego busquemos: ```js run demo function checkSpam(str) { @@ -7,8 +7,8 @@ function checkSpam(str) { return lowerStr.includes('viagra') || lowerStr.includes('xxx'); } -alert( checkSpam('buy ViAgRA now') ); -alert( checkSpam('free xxxxx') ); -alert( checkSpam("innocent rabbit") ); +alert( checkSpam('compra ViAgRA ahora') ); +alert( checkSpam('xxxxx gratis') ); +alert( checkSpam("coneja inocente") ); ``` diff --git a/1-js/05-data-types/03-string/2-check-spam/task.md b/1-js/05-data-types/03-string/2-check-spam/task.md index 98b5dd8a0..e32a74365 100644 --- a/1-js/05-data-types/03-string/2-check-spam/task.md +++ b/1-js/05-data-types/03-string/2-check-spam/task.md @@ -2,15 +2,15 @@ importance: 5 --- -# Check for spam +# Buscar spam -Write a function `checkSpam(str)` that returns `true` if `str` contains 'viagra' or 'XXX', otherwise `false`. +Escribe una función `checkSpam(str)` que devuelva `true` si `str` contiene 'viagra' o 'XXX', de lo contrario `false`. -The function must be case-insensitive: +La función debe ser insensible a mayúsculas y minúsculas: ```js -checkSpam('buy ViAgRA now') == true -checkSpam('free xxxxx') == true -checkSpam("innocent rabbit") == false +checkSpam('compra ViAgRA ahora') == true +checkSpam('xxxxx gratis') == true +checkSpam("coneja inocente") == false ``` diff --git a/1-js/05-data-types/03-string/3-truncate/solution.md b/1-js/05-data-types/03-string/3-truncate/solution.md index d51672ae6..e122b0c9b 100644 --- a/1-js/05-data-types/03-string/3-truncate/solution.md +++ b/1-js/05-data-types/03-string/3-truncate/solution.md @@ -1,6 +1,6 @@ -The maximal length must be `maxlength`, so we need to cut it a little shorter, to give space for the ellipsis. +La longitud máxima debe ser 'maxlength', por lo que debemos acortarla un poco para dar espacio a los puntos suspensivos. -Note that there is actually a single Unicode character for an ellipsis. That's not three dots. +Tener en cuenta que en realidad hay un único carácter unicode para puntos suspensivos. Eso no son tres puntos. ```js run demo function truncate(str, maxlength) { diff --git a/1-js/05-data-types/03-string/3-truncate/task.md b/1-js/05-data-types/03-string/3-truncate/task.md index c99a5f15a..274449b1a 100644 --- a/1-js/05-data-types/03-string/3-truncate/task.md +++ b/1-js/05-data-types/03-string/3-truncate/task.md @@ -2,16 +2,16 @@ importance: 5 --- -# Truncate the text +# Truncar el texto -Create a function `truncate(str, maxlength)` that checks the length of the `str` and, if it exceeds `maxlength` -- replaces the end of `str` with the ellipsis character `"…"`, to make its length equal to `maxlength`. +Crea una función `truncate(str, maxlength)` que verifique la longitud de `str` y, si excede `maxlength` - reemplaza el final de `str` con el carácter de puntos suspensivos `"…"`, para hacer su longitud igual a `maxlength`. -The result of the function should be the truncated (if needed) string. +El resultado de la función debe ser la cadena truncada (si es necesario). -For instance: +Por ejemplo: ```js -truncate("What I'd like to tell on this topic is:", 20) == "What I'd like to te…" +truncate("Lo que me gustaría contar sobre este tema es:", 20) = "Lo que me gustaría c…" -truncate("Hi everyone!", 20) == "Hi everyone!" +truncate("Hola a todos!", 20) = "Hola a todos!" ``` diff --git a/1-js/05-data-types/03-string/4-extract-currency/task.md b/1-js/05-data-types/03-string/4-extract-currency/task.md index feb16e642..11e454e61 100644 --- a/1-js/05-data-types/03-string/4-extract-currency/task.md +++ b/1-js/05-data-types/03-string/4-extract-currency/task.md @@ -2,13 +2,13 @@ importance: 4 --- -# Extract the money +# Extraer el dinero -We have a cost in the form `"$120"`. That is: the dollar sign goes first, and then the number. +Tenemos un costo en forma de "$120". Es decir: el signo de dólar va primero y luego el número. -Create a function `extractCurrencyValue(str)` that would extract the numeric value from such string and return it. +Crea una función `extractCurrencyValue(str)` que extraiga el valor numérico de dicho string y lo devuelva. -The example: +Por ejemplo: ```js alert( extractCurrencyValue('$120') === 120 ); // true diff --git a/1-js/05-data-types/03-string/article.md b/1-js/05-data-types/03-string/article.md index 60ce2b6f0..e5f7e6046 100644 --- a/1-js/05-data-types/03-string/article.md +++ b/1-js/05-data-types/03-string/article.md @@ -1,23 +1,23 @@ # Strings -In JavaScript, the textual data is stored as strings. There is no separate type for a single character. +En JavaScript, los datos textuales son almacenados como strings (cadena de caracteres). No hay un tipo de datos separado para caracteres unitarios. -The internal format for strings is always [UTF-16](https://en.wikipedia.org/wiki/UTF-16), it is not tied to the page encoding. +El formato interno para strings es siempre [UTF-16](https://es.wikipedia.org/wiki/UTF-16), no está vinculado a la codificación de la página. -## Quotes +## Comillas -Let's recall the kinds of quotes. +Recordemos los tipos de comillas. -Strings can be enclosed within either single quotes, double quotes or backticks: +Los strings pueden estar entre comillas simples, comillas dobles o backticks (acento grave): ```js -let single = 'single-quoted'; -let double = "double-quoted"; +let single = 'comillas simples'; +let double = "comillas dobles"; let backticks = `backticks`; ``` -Single and double quotes are essentially the same. Backticks, however, allow us to embed any expression into the string, by wrapping it in `${…}`: +Comillas simples y dobles son esencialmente lo mismo. En cambio, los "backticks" nos permiten además ingresar expresiones dentro del string envolviéndolos en `${…}`: ```js run function sum(a, b) { @@ -27,238 +27,238 @@ function sum(a, b) { alert(`1 + 2 = ${sum(1, 2)}.`); // 1 + 2 = 3. ``` -Another advantage of using backticks is that they allow a string to span multiple lines: +Otra ventaja de usar backticks es que nos permiten extender en múltiples líneas el string: ```js run -let guestList = `Guests: - * John - * Pete - * Mary +let guestList = `Invitados: + * Juan + * Pedro + * Maria `; -alert(guestList); // a list of guests, multiple lines +alert(guestList); // una lista de invitados, en múltiples líneas ``` -Looks natural, right? But single or double quotes do not work this way. +Se ve natural, ¿no es cierto? Pero las comillas simples y dobles no funcionan de esa manera. -If we use them and try to use multiple lines, there'll be an error: +Si intentamos usar comillas simples o dobles de la misma forma, obtendremos un error: ```js run -let guestList = "Guests: // Error: Unexpected token ILLEGAL - * John"; +let guestList = "Invitados: // Error: Unexpected token ILLEGAL + * Juan"; ``` -Single and double quotes come from ancient times of language creation, when the need for multiline strings was not taken into account. Backticks appeared much later and thus are more versatile. +Las comillas simples y dobles provienen de la creación de lenguajes en tiempos ancestrales, cuando la necesidad de múltiples líneas no era tomada en cuenta. Los backticks aparecieron mucho después y por ende son más versátiles. -Backticks also allow us to specify a "template function" before the first backtick. The syntax is: func`string`. The function `func` is called automatically, receives the string and embedded expressions and can process them. This feature is called "tagged templates", it's rarely seen, but you can read about it in the MDN: [Template literals](mdn:/JavaScript/Reference/Template_literals#Tagged_templates). +Los backticks además nos permiten especificar una "función de plantilla" antes del primer backtick. La sintaxis es: func`string`. La función `func` es llamada automáticamente, recibe el string y la expresión insertada, y los puede procesar. Eso se llama "plantillas etiquetadas". Es raro verlo implementado, pero puedes leer más sobre esto en el [manual](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates). -## Special characters +## Caracteres especiales -It is still possible to create multiline strings with single and double quotes by using a so-called "newline character", written as `\n`, which denotes a line break: +Es posible crear strings de múltiples líneas usando comillas simples, usando un llamado "carácter de nueva línea", escrito como `\n`, lo que denota un salto de línea: ```js run -let guestList = "Guests:\n * John\n * Pete\n * Mary"; +let guestList = 'Invitados:\n * Juan\n * Pedro\n * Maria'; -alert(guestList); // a multiline list of guests, same as above +alert(guestList); // lista de invitados en múltiples líneas, igual a la de más arriba ``` -As a simpler example, these two lines are equal, just written differently: +Como ejemplo más simple, estas dos líneas son iguales, pero escritas en forma diferente: ```js run -let str1 = "Hello\nWorld"; // two lines using a "newline symbol" +let str1 = "Hello\nWorld"; // dos líneas usando el "símbolo de nueva línea" -// two lines using a normal newline and backticks +// dos líneas usando nueva línea normal y backticks let str2 = `Hello World`; alert(str1 == str2); // true ``` -There are other, less common special characters: +Existen otros caracteres especiales, menos comunes. -| Character | Description | +| Carácter | Descripción | |-----------|-------------| -|`\n`|New line| -|`\r`|In Windows text files a combination of two characters `\r\n` represents a new break, while on non-Windows OS it's just `\n`. That's for historical reasons, most Windows software also understands `\n`. | -|`\'`, `\"`, \\`|Quotes| -|`\\`|Backslash| -|`\t`|Tab| -|`\b`, `\f`, `\v`| Backspace, Form Feed, Vertical Tab -- mentioned for completeness, coming from old times, not used nowadays (you can forget them right now). | +|`\n`|Nueva línea| +|`\r`|En Windows, los archivos de texto usan una combinación de dos caracteres `\r\n` para representar un corte de línea, mientras que en otros SO es simplemente '\n'. Esto es por razones históricas, la mayoría del software para Windows también reconoce '\n'. | +|`\'`, `\"`, \\`|Comillas| +|`\\`|Barra invertida| +|`\t`|Tabulación| +|`\b`, `\f`, `\v`| Retroceso, avance de formulario, tabulación vertical -- Se mencionan para ser exhaustivos. Vienen de muy viejos tiempos y no se usan actualmente (puedes olvidarlos ya). | -As you can see, all special characters start with a backslash character `\`. It is also called an "escape character". +Como puedes ver, todos los caracteres especiales empiezan con la barra invertida `\`. Se lo llama "carácter de escape". -Because it's so special, if we need to show an actual backslash `\` within the string, we need to double it: +Y como es tan especial, si necesitamos mostrar el verdadero carácter `\` dentro de un string, necesitamos duplicarlo: ```js run -alert( `The backslash: \\` ); // The backslash: \ +alert( `La barra invertida: \\` ); // La barra invertida: \ ``` -So-called "escaped" quotes `\'`, `\"`, \\` are used to insert a quote into the same-quoted string. +Las llamadas comillas "escapadas" `\'`, `\"`, \\` se usan para insertar una comilla en un string entrecomillado con el mismo tipo de comilla. -For instance: +Por ejemplo: ```js run -alert( 'I*!*\'*/!*m the Walrus!' ); // *!*I'm*/!* the Walrus! +alert('¡Yo soy la \'morsa\'!'); // ¡Yo soy la 'morsa'! ``` -As you can see, we have to prepend the inner quote by the backslash `\'`, because otherwise it would indicate the string end. +Como puedes ver, debimos anteponer un carácter de escape `\` antes de cada comilla ya que de otra manera hubiera indicado el final del string. -Of course, only the quotes that are the same as the enclosing ones need to be escaped. So, as a more elegant solution, we could switch to double quotes or backticks instead: +Obviamente, solo necesitan ser escapadas las comillas que son iguales a las que están rodeando al string. Una solución más elegante es cambiar a comillas dobles o backticks: ```js run -alert( "I'm the Walrus!" ); // I'm the Walrus! +alert("¡Yo soy la 'morsa'!"); // ¡Yo soy la 'morsa'! ``` -Besides these special characters, there's also a special notation for Unicode codes `\u…`, it's rarely used and is covered in the optional chapter about [Unicode](info:unicode). +Además de estos caracteres especiales, también hay una notación especial para códigos Unicode `\u…` que se usa raramente. Los cubrimos en el capítulo opcional acerca de [Unicode](info:unicode). -## String length +## Largo del string -The `length` property has the string length: +La propiedad 'length' contiene el largo del string: ```js run -alert( `My\n`.length ); // 3 +alert(`Mi\n`.length); // 3 ``` -Note that `\n` is a single "special" character, so the length is indeed `3`. +Nota que `\n` es un solo carácter, por lo que el largo total es `3`. -```warn header="`length` is a property" -People with a background in some other languages sometimes mistype by calling `str.length()` instead of just `str.length`. That doesn't work. +```warn header="`length` es una propiedad" +Quienes tienen experiencia en otros lenguajes pueden cometer el error de escribir `str.length()` en vez de `str.length`. Eso no funciona. -Please note that `str.length` is a numeric property, not a function. There is no need to add parenthesis after it. Not `.length()`, but `.length`. +Nota que `str.length` es una propiedad numérica, no una función. No hay que agregar paréntesis después de ella. No es `.length()`, sino `.length`. ``` -## Accessing characters +## Accediendo caracteres -To get a character at position `pos`, use square brackets `[pos]` or call the method [str.at(pos)](mdn:js/String/at). The first character starts from the zero position: +Para acceder a un carácter en la posición `pos`, se debe usar corchetes, `[pos]`, o llamar al método [str.at(pos)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/at). El primer carácter comienza desde la posición cero: ```js run -let str = `Hello`; +let str = `Hola`; -// the first character +// el primer carácter alert( str[0] ); // H alert( str.at(0) ); // H -// the last character -alert( str[str.length - 1] ); // o +// el último carácter +alert( str[str.length - 1] ); // a alert( str.at(-1) ); ``` -As you can see, the `.at(pos)` method has a benefit of allowing negative position. If `pos` is negative, then it's counted from the end of the string. +Como puedes ver, el método `.at(pos)` tiene el beneficio de permitir una posición negativa. Si `pos` es negativa, se cuenta desde el final del string. -So `.at(-1)` means the last character, and `.at(-2)` is the one before it, etc. +Así, `.at(-1)` significa el último carácter, y `.at(-2)` es el anterior a él, etc. -The square brackets always return `undefined` for negative indexes, for instance: +Los corchetes siempre devuelven `undefined` para índices negativos: ```js run -let str = `Hello`; +let str = `Hola`; alert( str[-2] ); // undefined alert( str.at(-2) ); // l ``` -We can also iterate over characters using `for..of`: +Podemos además iterar sobre los caracteres usando `for..of`: ```js run -for (let char of "Hello") { - alert(char); // H,e,l,l,o (char becomes "H", then "e", then "l" etc) +for (let char of 'Hola') { + alert(char); // H,o,l,a (char se convierte en "H", luego "o", luego "l", etc.) } ``` -## Strings are immutable +## Los strings son inmutables -Strings can't be changed in JavaScript. It is impossible to change a character. +Los strings no pueden ser modificados en JavaScript. Es imposible modificar un carácter. -Let's try it to show that it doesn't work: +Intentémoslo para demostrar que no funciona: ```js run -let str = 'Hi'; +let str = 'Hola'; str[0] = 'h'; // error -alert( str[0] ); // doesn't work +alert(str[0]); // no funciona ``` -The usual workaround is to create a whole new string and assign it to `str` instead of the old one. +Lo usual para resolverlo es crear un nuevo string y asignarlo a `str` reemplazando el string completo. -For instance: +Por ejemplo: ```js run -let str = 'Hi'; +let str = 'Hola'; -str = 'h' + str[1]; // replace the string +str = 'h' + str[1] + str[2] + str[3]; // reemplaza el string -alert( str ); // hi +alert( str ); // hola ``` -In the following sections we'll see more examples of this. +En las secciones siguientes veremos más ejemplos de esto. -## Changing the case +## Cambiando capitalización -Methods [toLowerCase()](mdn:js/String/toLowerCase) and [toUpperCase()](mdn:js/String/toUpperCase) change the case: +Los métodos [toLowerCase()](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/String/toLowerCase) y [toUpperCase()](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/String/toUpperCase) cambian los caracteres a minúscula y mayúscula respectivamente: ```js run -alert( 'Interface'.toUpperCase() ); // INTERFACE -alert( 'Interface'.toLowerCase() ); // interface +alert('Interfaz'.toUpperCase()); // INTERFAZ +alert('Interfaz'.toLowerCase()); // interfaz ``` -Or, if we want a single character lowercased: +Si queremos un solo carácter en minúscula: ```js run -alert( 'Interface'[0].toLowerCase() ); // 'i' +alert('Interfaz'[0].toLowerCase()); // 'i' ``` -## Searching for a substring +## Buscando una subcadena de caracteres -There are multiple ways to look for a substring within a string. +Existen muchas formas de buscar por subcadenas de caracteres dentro de una cadena completa. ### str.indexOf -The first method is [str.indexOf(substr, pos)](mdn:js/String/indexOf). +El primer método es [str.indexOf(substr, pos)](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/String/indexOf). -It looks for the `substr` in `str`, starting from the given position `pos`, and returns the position where the match was found or `-1` if nothing can be found. +Este busca un `substr` en `str`, comenzando desde la posición entregada `pos`, y retorna la posición donde es encontrada la coincidencia o `-1` en caso de no encontrar nada. -For instance: +Por ejemplo: ```js run -let str = 'Widget with id'; +let str = 'Widget con id'; -alert( str.indexOf('Widget') ); // 0, because 'Widget' is found at the beginning -alert( str.indexOf('widget') ); // -1, not found, the search is case-sensitive +alert(str.indexOf('Widget')); // 0, ya que 'Widget' es encontrado al comienzo +alert(str.indexOf('widget')); // -1, no es encontrado, la búsqueda toma en cuenta minúsculas y mayúsculas. -alert( str.indexOf("id") ); // 1, "id" is found at the position 1 (..idget with id) +alert(str.indexOf('id')); // 1, "id" es encontrado en la posición 1 (..idget con id) ``` -The optional second parameter allows us to start searching from a given position. +El segundo parámetro es opcional y nos permite buscar desde la posición entregada. -For instance, the first occurrence of `"id"` is at position `1`. To look for the next occurrence, let's start the search from position `2`: +Por ejemplo, la primera ocurrencia de `"id"` es en la posición `1`. Para buscar por la siguiente ocurrencia, comencemos a buscar desde la posición `2`: ```js run -let str = 'Widget with id'; +let str = 'Widget con id'; -alert( str.indexOf('id', 2) ) // 12 +alert(str.indexOf('id', 2)); // 11 ``` -If we're interested in all occurrences, we can run `indexOf` in a loop. Every new call is made with the position after the previous match: +Si estamos interesados en todas las ocurrencias, podemos correr `indexOf` en un bucle. Cada nuevo llamado es hecho utilizando la posición posterior a la encontrada anteriormente: ```js run -let str = 'As sly as a fox, as strong as an ox'; +let str = 'Astuto como un zorro, fuerte como un buey'; -let target = 'as'; // let's look for it +let target = 'como'; // busquemos por él let pos = 0; while (true) { let foundPos = str.indexOf(target, pos); if (foundPos == -1) break; - alert( `Found at ${foundPos}` ); - pos = foundPos + 1; // continue the search from the next position + alert(`Encontrado en ${foundPos}`); + pos = foundPos + 1; // continuar la búsqueda desde la siguiente posición } ``` -The same algorithm can be layed out shorter: +Podemos escribir el mismo algoritmo, pero más corto: ```js run -let str = "As sly as a fox, as strong as an ox"; -let target = "as"; +let str = 'Astuto como un zorro, fuerte como un buey'; +let target = "como"; *!* let pos = -1; @@ -269,192 +269,192 @@ while ((pos = str.indexOf(target, pos + 1)) != -1) { ``` ```smart header="`str.lastIndexOf(substr, position)`" -There is also a similar method [str.lastIndexOf(substr, position)](mdn:js/String/lastIndexOf) that searches from the end of a string to its beginning. +También hay un método similar [str.lastIndexOf(substr, position)](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/String/lastIndexOf) que busca desde el final del string hasta el comienzo. -It would list the occurrences in the reverse order. +Este imprimirá las ocurrencias en orden invertido. ``` -There is a slight inconvenience with `indexOf` in the `if` test. We can't put it in the `if` like this: +Existe un leve inconveniente con `indexOf` en la prueba `if`. No podemos utilizarlo en el `if` como sigue: ```js run -let str = "Widget with id"; +let str = "Widget con id"; if (str.indexOf("Widget")) { - alert("We found it"); // doesn't work! + alert("Lo encontramos"); // no funciona! } ``` -The `alert` in the example above doesn't show because `str.indexOf("Widget")` returns `0` (meaning that it found the match at the starting position). Right, but `if` considers `0` to be `false`. +La `alerta` en el ejemplo anterior no se muestra ya que `str.indexOf("Widget")` retorna `0` (lo que significa que encontró el string en la posición inicial). Eos correcto, pero `if` considera `0` como `falso`. -So, we should actually check for `-1`, like this: +Por ello debemos preguntar por `-1`: ```js run -let str = "Widget with id"; +let str = "Widget con id"; *!* if (str.indexOf("Widget") != -1) { */!* - alert("We found it"); // works now! + alert("Lo encontramos"); // ahora funciona! } ``` ### includes, startsWith, endsWith -The more modern method [str.includes(substr, pos)](mdn:js/String/includes) returns `true/false` depending on whether `str` contains `substr` within. +El método más moderno [str.includes(substr, pos)](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/String/includes) devuelve `true` o `false` si `str` contiene `substr` o no. -It's the right choice if we need to test for the match, but don't need its position: +Es la opción adecuada si lo que necesitamos es verificar que exista, pero no su posición. ```js run -alert( "Widget with id".includes("Widget") ); // true +alert('Widget con id'.includes('Widget')); // true -alert( "Hello".includes("Bye") ); // false +alert('Hola'.includes('Adiós')); // false ``` -The optional second argument of `str.includes` is the position to start searching from: +El segundo argumento opcional de `str.includes` es la posición desde donde comienza a buscar: ```js run -alert( "Widget".includes("id") ); // true -alert( "Widget".includes("id", 3) ); // false, from position 3 there is no "id" +alert('Midget'.includes('id')); // true +alert('Midget'.includes('id', 3)); // false, desde la posición 3 no hay "id" ``` -The methods [str.startsWith](mdn:js/String/startsWith) and [str.endsWith](mdn:js/String/endsWith) do exactly what they say: +Los métodos [str.startsWith](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/String/startsWith) (comienza con) y [str.endsWith](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/String/endsWith) (termina con) hacen exactamente lo que dicen: ```js run -alert( "*!*Wid*/!*get".startsWith("Wid") ); // true, "Widget" starts with "Wid" -alert( "Wid*!*get*/!*".endsWith("get") ); // true, "Widget" ends with "get" +alert( "*!*Wid*/!*get".startsWith("Wid") ); // true, "Widget" comienza con "Wid" +alert( "Wid*!*get*/!*".endsWith("get") ); // true, "Widget" termina con "get" ``` -## Getting a substring +## Obteniendo un substring -There are 3 methods in JavaScript to get a substring: `substring`, `substr` and `slice`. +Existen 3 métodos en JavaScript para obtener un substring: `substring`, `substr` y `slice`. -`str.slice(start [, end])` -: Returns the part of the string from `start` to (but not including) `end`. +`str.slice(comienzo [, final])` +: Retorna la parte del string desde `comienzo` hasta (pero sin incluir) `final`. - For instance: + Por ejemplo: ```js run let str = "stringify"; - alert( str.slice(0, 5) ); // 'strin', the substring from 0 to 5 (not including 5) - alert( str.slice(0, 1) ); // 's', from 0 to 1, but not including 1, so only character at 0 + alert( str.slice(0, 5) ); // 'strin', el substring desde 0 hasta 5 (sin incluir 5) + alert( str.slice(0, 1) ); // 's', desde 0 hasta 1, pero sin incluir 1, por lo que sólo el carácter en 0 ``` - If there is no second argument, then `slice` goes till the end of the string: + Si no existe el segundo argumento, entonces `slice` va hasta el final del string: ```js run let str = "st*!*ringify*/!*"; - alert( str.slice(2) ); // 'ringify', from the 2nd position till the end + alert( str.slice(2) ); // ringify, desde la 2nda posición hasta el final ``` - Negative values for `start/end` are also possible. They mean the position is counted from the string end: + También son posibles valores negativos para `comienzo/final`. Estos indican que la posición es contada desde el final del string. + ```js run let str = "strin*!*gif*/!*y"; - - // start at the 4th position from the right, end at the 1st from the right + // comienza en la 4ta posición desde la derecha, finaliza en la 1era posición desde la derecha alert( str.slice(-4, -1) ); // 'gif' ``` -`str.substring(start [, end])` -: Returns the part of the string *between* `start` and `end` (not including `end`). +`str.substring(comienzo [, final])` +: Devuelve la parte del string *entre* `comienzo` y `final` (no incluyendo `final`). - This is almost the same as `slice`, but it allows `start` to be greater than `end` (in this case it simply swaps `start` and `end` values). + Esto es casi lo mismo que `slice`, pero permite que `comienzo` sea mayor que `final` (en este caso solo intercambia los valores de `comienzo` y `final`). - For instance: + Por ejemplo: ```js run let str = "st*!*ring*/!*ify"; - // these are same for substring + // esto es lo mismo para substring alert( str.substring(2, 6) ); // "ring" alert( str.substring(6, 2) ); // "ring" - // ...but not for slice: - alert( str.slice(2, 6) ); // "ring" (the same) - alert( str.slice(6, 2) ); // "" (an empty string) + // ...pero no para slice: + alert( str.slice(2, 6) ); // "ring" (lo mismo) + alert( str.slice(6, 2) ); // "" (un string vacío) ``` - Negative arguments are (unlike slice) not supported, they are treated as `0`. + Los argumentos negativos (al contrario de slice) no son soportados, son tratados como `0`. -`str.substr(start [, length])` -: Returns the part of the string from `start`, with the given `length`. +`str.substr(comienzo [, largo])` +: Retorna la parte del string desde `comienzo`, con el `largo` dado. - In contrast with the previous methods, this one allows us to specify the `length` instead of the ending position: + A diferencia de los métodos anteriores, este nos permite especificar el `largo` en lugar de la posición final: ```js run let str = "st*!*ring*/!*ify"; - alert( str.substr(2, 4) ); // 'ring', from the 2nd position get 4 characters + alert( str.substr(2, 4) ); // ring, desde la 2nda posición toma 4 caracteres ``` - The first argument may be negative, to count from the end: + El primer argumento puede ser negativo, para contar desde el final: ```js run let str = "strin*!*gi*/!*fy"; - alert( str.substr(-4, 2) ); // 'gi', from the 4th position get 2 characters + alert( str.substr(-4, 2) ); // gi, desde la 4ta posición toma 2 caracteres ``` - This method resides in the [Annex B](https://tc39.es/ecma262/#sec-string.prototype.substr) of the language specification. It means that only browser-hosted Javascript engines should support it, and it's not recommended to use it. In practice, it's supported everywhere. + Este método reside en el [Anexo B](https://tc39.es/ecma262/#sec-string.prototype.substr) de la especificación del lenguaje. Esto significa que solo necesitan darle soporte los motores Javascript de los navegadores, y no es recomendable su uso. Pero en la práctica, es soportado en todos lados. -Let's recap these methods to avoid any confusion: +Recapitulemos los métodos para evitar confusiones: -| method | selects... | negatives | -|--------|-----------|-----------| -| `slice(start, end)` | from `start` to `end` (not including `end`) | allows negatives | -| `substring(start, end)` | between `start` and `end` (not including `end`)| negative values mean `0` | -| `substr(start, length)` | from `start` get `length` characters | allows negative `start` | +| método | selecciona... | negativos | +|--------|---------------|-----------| +| `slice(comienzo, final)` | desde `comienzo` hasta `final` (sin incluir `final`) | permite negativos | +| `substring(comienzo, final)` | entre `comienzo` y `final` (no incluye `final`)| valores negativos significan `0` | +| `substr(comienzo, largo)` | desde `comienzo` toma `largo` caracteres | permite negativos `comienzo` | -```smart header="Which one to choose?" -All of them can do the job. Formally, `substr` has a minor drawback: it is described not in the core JavaScript specification, but in Annex B, which covers browser-only features that exist mainly for historical reasons. So, non-browser environments may fail to support it. But in practice it works everywhere. +```smart header="¿Cuál elegir?" +Todos son capaces de hacer el trabajo. Formalmente, `substr` tiene una pequeña desventaja: no es descrito en la especificación central de JavaScript, sino en el anexo B, el cual cubre características sólo de navegadores, que existen principalmente por razones históricas. Por lo que entornos sin navegador pueden fallar en compatibilidad. Pero en la práctica, funciona en todos lados. -Of the other two variants, `slice` is a little bit more flexible, it allows negative arguments and shorter to write. +De las otras dos variantes, `slice` es algo más flexible, permite argumentos negativos y es más corta. -So, for practical use it's enough to remember only `slice`. +Entones, es suficiente recordar únicamente `slice`. ``` -## Comparing strings +## Comparando strings -As we know from the chapter , strings are compared character-by-character in alphabetical order. +Como aprendimos en el capítulo , los strings son comparados carácter por carácter en orden alfabético. -Although, there are some oddities. +Aunque existen algunas singularidades. -1. A lowercase letter is always greater than the uppercase: +1. Una letra minúscula es siempre mayor que una mayúscula: - ```js run - alert( 'a' > 'Z' ); // true - ``` + ```js run + alert('a' > 'Z'); // true + ``` -2. Letters with diacritical marks are "out of order": +2. Las letras con marcas diacríticas están "fuera de orden": - ```js run - alert( 'Österreich' > 'Zealand' ); // true - ``` + ```js run + alert('Österreich' > 'Zealand'); // true + ``` - This may lead to strange results if we sort these country names. Usually people would expect `Zealand` to come after `Österreich` in the list. + Esto puede conducir a resultados extraños si ordenamos los nombres de estos países. Usualmente, se esperaría que `Zealand` apareciera después de `Österreich` en la lista. -To understand what happens, we should be aware that strings in Javascript are encoded using [UTF-16](https://en.wikipedia.org/wiki/UTF-16). That is: each character has a corresponding numeric code. +Para entender lo que pasa, debemos tener en cuenta que los strings en JavaScript son codificados usando [UTF-16](https://es.wikipedia.org/wiki/UTF-16). Esto significa: cada carácter tiene un código numérico correspondiente. -There are special methods that allow to get the character for the code and back: +Existen métodos especiales que permiten obtener el carácter para el código y viceversa. `str.codePointAt(pos)` -: Returns a decimal number representing the code for the character at position `pos`: +: Devuelve un número decimal que representa el código de carácter en la posición `pos`: ```js run - // different case letters have different codes + // mayúsculas y minúsculas tienen códigos diferentes alert( "Z".codePointAt(0) ); // 90 alert( "z".codePointAt(0) ); // 122 - alert( "z".codePointAt(0).toString(16) ); // 7a (if we need a hexadecimal value) + alert( "z".codePointAt(0).toString(16) ); // 7a (si necesitamos el valor del código en hexadecimal) ``` `String.fromCodePoint(code)` -: Creates a character by its numeric `code` +: Crea un carácter por su `código` numérico: ```js run alert( String.fromCodePoint(90) ); // Z - alert( String.fromCodePoint(0x5a) ); // Z (we can also use a hex value as an argument) + alert( String.fromCodePoint(0x5a) ); // Z (también podemos usar un valor hexa como argumento) ``` -Now let's see the characters with codes `65..220` (the latin alphabet and a little bit extra) by making a string of them: +Ahora veamos los caracteres con códigos `65..220` (el alfabeto latino y algo más) transformándolos a string: ```js run let str = ''; @@ -463,60 +463,60 @@ for (let i = 65; i <= 220; i++) { str += String.fromCodePoint(i); } alert( str ); -// Output: +// salida: // ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„ // ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜ ``` -See? Capital characters go first, then a few special ones, then lowercase characters, and `Ö` near the end of the output. +¿Lo ves? Caracteres en mayúsculas van primero, luego unos cuantos caracteres especiales, luego las minúsculas. -Now it becomes obvious why `a > Z`. +Ahora se vuelve obvio por qué `a > Z`. -The characters are compared by their numeric code. The greater code means that the character is greater. The code for `a` (97) is greater than the code for `Z` (90). +Los caracteres son comparados por su código numérico. Código mayor significa que el carácter es mayor. El código para `a` (97) es mayor que el código para `Z` (90). -- All lowercase letters go after uppercase letters because their codes are greater. -- Some letters like `Ö` stand apart from the main alphabet. Here, its code is greater than anything from `a` to `z`. +- Todas las letras minúsculas van después de las mayúsculas ya que sus códigos son mayores. +- Algunas letras como `Ö` se mantienen apartadas del alfabeto principal. Aquí el código es mayor que cualquiera desde `a` hasta `z`. -### Correct comparisons [#correct-comparisons] +### Comparaciones correctas [#correct-comparisons] -The "right" algorithm to do string comparisons is more complex than it may seem, because alphabets are different for different languages. +El algoritmo "correcto" para realizar comparaciones de strings es más complejo de lo que parece, debido a que los alfabetos son diferentes para diferentes lenguajes. Una letra que se ve igual en dos alfabetos distintos, pueden tener distintas posiciones. -So, the browser needs to know the language to compare. +Por lo que el navegador necesita saber el lenguaje para comparar. -Luckily, modern browsers support the internationalization standard [ECMA-402](https://www.ecma-international.org/publications-and-standards/standards/ecma-402/). +Por suerte, todos los navegadores modernos mantienen la internacionalización del estándar [ECMA 402](https://www.ecma-international.org/publications-and-standards/standards/ecma-402/). -It provides a special method to compare strings in different languages, following their rules. +Este provee un método especial para comparar strings en distintos lenguajes, siguiendo sus reglas. -The call [str.localeCompare(str2)](mdn:js/String/localeCompare) returns an integer indicating whether `str` is less, equal or greater than `str2` according to the language rules: +El llamado a [str.localeCompare(str2)](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/String/localeCompare) devuelve un entero indicando si `str` es menor, igual o mayor que `str2` de acuerdo a las reglas del lenguaje: -- Returns a negative number if `str` is less than `str2`. -- Returns a positive number if `str` is greater than `str2`. -- Returns `0` if they are equivalent. +- Retorna `1` si `str` es mayor que `str2`. +- Retorna `-1` si `str` es menor que `str2`. +- Retorna `0` si son equivalentes. -For instance: +Por ejemplo: ```js run -alert( 'Österreich'.localeCompare('Zealand') ); // -1 +alert('Österreich'.localeCompare('Zealand')); // -1 ``` -This method actually has two additional arguments specified in [the documentation](mdn:js/String/localeCompare), which allows it to specify the language (by default taken from the environment, letter order depends on the language) and setup additional rules like case sensitivity or should `"a"` and `"á"` be treated as the same etc. +Este método tiene dos argumentos adicionales especificados en [la documentación](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/String/localeCompare), la cual le permite especificar el lenguaje (por defecto lo toma del entorno) y configura reglas adicionales como sensibilidad a las mayúsculas y minúsculas, o si `"a"` y `"á"` deben ser tratadas como iguales, etc. -## Summary +## Resumen -- There are 3 types of quotes. Backticks allow a string to span multiple lines and embed expressions `${…}`. -- We can use special characters, such as a line break `\n`. -- To get a character, use: `[]` or `at` method. -- To get a substring, use: `slice` or `substring`. -- To lowercase/uppercase a string, use: `toLowerCase/toUpperCase`. -- To look for a substring, use: `indexOf`, or `includes/startsWith/endsWith` for simple checks. -- To compare strings according to the language, use: `localeCompare`, otherwise they are compared by character codes. +- Existen 3 tipos de entrecomillado. Los backticks permiten que una cadena abarque varias líneas e insertar expresiones `${…}`. +- Podemos usar caracteres especiales como el salto de línea `\n`. +- Para obtener un carácter, usa: `[]` o el método `at`. +- Para obtener un substring, usa: `slice` o `substring`. +- Para convertir un string en minúsculas/mayúsculas, usa: `toLowerCase/toUpperCase`. +- Para buscar un substring, usa: `indexOf`, o para chequeos simples `includes/startsWith/endsWith`. +- Para comparar strings de acuerdo al idioma, usa: `localeCompare`, de otra manera serán comparados por sus códigos de carácter. -There are several other helpful methods in strings: +Existen otros métodos útiles: -- `str.trim()` -- removes ("trims") spaces from the beginning and end of the string. -- `str.repeat(n)` -- repeats the string `n` times. -- ...and more to be found in the [manual](mdn:js/String). +- `str.trim()` -- remueve ("recorta") espacios desde el comienzo y final de un string. +- `str.repeat(n)` -- repite el string `n` veces. +- ... y más. Puedes ver el [manual](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/String) para más detalles. -Strings also have methods for doing search/replace with regular expressions. But that's big topic, so it's explained in a separate tutorial section . +Los strings también tienen métodos para buscar/reemplazar que usan "expresiones regulares". Este es un tema muy amplio, por ello es explicado en una sección separada del tutorial . -Also, as of now it's important to know that strings are based on Unicode encoding, and hence there're issues with comparisons. There's more about Unicode in the chapter . +Además, es importante saber que los strings están basados en la codificación Unicode, y se presentan algunas complicaciones en las comparaciones de string. Hay más acerca de Unicode en el capítulo . diff --git a/1-js/05-data-types/04-array/1-item-value/solution.md b/1-js/05-data-types/04-array/1-item-value/solution.md index e631f1c70..6f93b6775 100644 --- a/1-js/05-data-types/04-array/1-item-value/solution.md +++ b/1-js/05-data-types/04-array/1-item-value/solution.md @@ -1,4 +1,4 @@ -The result is `4`: +El resultado es `4`: ```js run @@ -13,5 +13,5 @@ alert( fruits.length ); // 4 */!* ``` -That's because arrays are objects. So both `shoppingCart` and `fruits` are the references to the same array. +Esto es porque los arrays son objetos. Entonces ambos, `shoppingCart` y `fruits` son referencias al mismo array. diff --git a/1-js/05-data-types/04-array/1-item-value/task.md b/1-js/05-data-types/04-array/1-item-value/task.md index 4fcf384fb..86cf589eb 100644 --- a/1-js/05-data-types/04-array/1-item-value/task.md +++ b/1-js/05-data-types/04-array/1-item-value/task.md @@ -2,18 +2,18 @@ importance: 3 --- -# Is array copied? +# ¿El array es copiado? -What is this code going to show? +¿Qué va a mostrar este código? ```js let fruits = ["Apples", "Pear", "Orange"]; -// push a new value into the "copy" +// introduce un valor nuevo dentro de una copia let shoppingCart = fruits; shoppingCart.push("Banana"); -// what's in fruits? -alert( fruits.length ); // ? +// ¿Qué hay en "fruits"? +alert( fruits.length ); // ¿? ``` diff --git a/1-js/05-data-types/04-array/10-maximal-subarray/solution.md b/1-js/05-data-types/04-array/10-maximal-subarray/solution.md index 7e1ca3bde..d7e89d8c3 100644 --- a/1-js/05-data-types/04-array/10-maximal-subarray/solution.md +++ b/1-js/05-data-types/04-array/10-maximal-subarray/solution.md @@ -1,43 +1,43 @@ -# Slow solution +# Solución lenta -We can calculate all possible subsums. +Podemos calcular todas las subsumas. -The simplest way is to take every element and calculate sums of all subarrays starting from it. +La forma más simple es tomar cada elemento y calcular las sumas de todos los subarrays que comienzan con él. -For instance, for `[-1, 2, 3, -9, 11]`: +Por ejemplo, para `[-1, 2, 3, -9, 11]`: ```js no-beautify -// Starting from -1: +// Comenzando desde -1: -1 -1 + 2 -1 + 2 + 3 -1 + 2 + 3 + (-9) -1 + 2 + 3 + (-9) + 11 -// Starting from 2: +// Comenzando desde 2: 2 2 + 3 2 + 3 + (-9) 2 + 3 + (-9) + 11 -// Starting from 3: +// Comenzando desde 3: 3 3 + (-9) 3 + (-9) + 11 -// Starting from -9 +// Comenzando desde -9 -9 -9 + 11 -// Starting from 11 +// Comenzando desde 11 11 ``` -The code is actually a nested loop: the external loop over array elements, and the internal counts subsums starting with the current element. +El código es un bucle anidado. El bucle externo itera sobre los elementos del array, y el interno cuenta subsumas comenzando con cada uno de ellos. ```js run function getMaxSubSum(arr) { - let maxSum = 0; // if we take no elements, zero will be returned + let maxSum = 0; // si no obtenemos elementos, devolverá cero for (let i = 0; i < arr.length; i++) { let sumFixedStart = 0; @@ -57,25 +57,25 @@ alert( getMaxSubSum([1, 2, 3]) ); // 6 alert( getMaxSubSum([100, -9, 2, -3, 5]) ); // 100 ``` -The solution has a time complexity of [O(n2)](https://en.wikipedia.org/wiki/Big_O_notation). In other words, if we increase the array size 2 times, the algorithm will work 4 times longer. +La solución tiene una complejidad 2 en notación Landau [O(n2)](https://es.wikipedia.org/wiki/Notaci%C3%B3n_de_Landau) (coste respecto al tiempo). Es decir, si multiplicamos el tamaño del array por 2, el tiempo del algoritmo se multiplicará por 4. -For big arrays (1000, 10000 or more items) such algorithms can lead to serious sluggishness. +Para arrays muy grandes (1000, 10000 o más items) tales algoritmos llevarán a una severa lentitud. -# Fast solution +# Solución rápida -Let's walk the array and keep the current partial sum of elements in the variable `s`. If `s` becomes negative at some point, then assign `s=0`. The maximum of all such `s` will be the answer. +Recorramos el array y registremos la suma parcial actual de los elementos en la variable `s`. Si `s` se vuelve cero en algún punto, le asignamos `s=0`. El máximo entre todas las sumas parciales `s` será la respuesta. -If the description is too vague, please see the code, it's short enough: +Si la descripción te resulta demasiado vaga, por favor mira el código. Es bastante corto: ```js run demo function getMaxSubSum(arr) { let maxSum = 0; let partialSum = 0; - for (let item of arr) { // for each item of arr - partialSum += item; // add it to partialSum - maxSum = Math.max(maxSum, partialSum); // remember the maximum - if (partialSum < 0) partialSum = 0; // zero if negative + for (let item of arr) { // por cada item de arr + partialSum += item; // se lo suma a partialSum + maxSum = Math.max(maxSum, partialSum); // registra el máximo + if (partialSum < 0) partialSum = 0; // cero si se vuelve negativo } return maxSum; @@ -89,6 +89,6 @@ alert( getMaxSubSum([1, 2, 3]) ); // 6 alert( getMaxSubSum([-1, -2, -3]) ); // 0 ``` -The algorithm requires exactly 1 array pass, so the time complexity is O(n). +El algoritmo requiere exactamente una pasada, entonces la complejidad es O(n). -You can find more detailed information about the algorithm here: [Maximum subarray problem](http://en.wikipedia.org/wiki/Maximum_subarray_problem). If it's still not obvious why that works, then please trace the algorithm on the examples above, see how it works, that's better than any words. +Puedes encontrar información más detallada acerca del algoritmo: [Subvector de suma máxima](https://es.wikibooks.org/wiki/Algoritmia/Divide_y_vencer%C3%A1s#Subvector_de_suma_m%C3%A1xima). Si aún no es obvio cómo funciona, traza el algoritmo en los ejemplos de arriba y observa cómo trabaja, es mejor que cualquier explicación. diff --git a/1-js/05-data-types/04-array/10-maximal-subarray/task.md b/1-js/05-data-types/04-array/10-maximal-subarray/task.md index f1a1d9f95..b572bb762 100644 --- a/1-js/05-data-types/04-array/10-maximal-subarray/task.md +++ b/1-js/05-data-types/04-array/10-maximal-subarray/task.md @@ -2,29 +2,29 @@ importance: 2 --- -# A maximal subarray +# Subarray máximo -The input is an array of numbers, e.g. `arr = [1, -2, 3, 4, -9, 6]`. +La entrada es un array de números, por ejemplo `arr = [1, -2, 3, 4, -9, 6]`. -The task is: find the contiguous subarray of `arr` with the maximal sum of items. +La tarea es encontrar, dentro de ’arr’, el subarray de elementos contiguos que tenga la suma máxima. -Write the function `getMaxSubSum(arr)` that will return that sum. +Escribe la función `getMaxSubSum(arr)` que devuelva el resultado de tal suma. -For instance: +Por ejemplo: ```js -getMaxSubSum([-1, *!*2, 3*/!*, -9]) == 5 (the sum of highlighted items) +getMaxSubSum([-1, *!*2, 3*/!*, -9]) == 5 (la suma de items resaltados) getMaxSubSum([*!*2, -1, 2, 3*/!*, -9]) == 6 getMaxSubSum([-1, 2, 3, -9, *!*11*/!*]) == 11 getMaxSubSum([-2, -1, *!*1, 2*/!*]) == 3 getMaxSubSum([*!*100*/!*, -9, 2, -3, 5]) == 100 -getMaxSubSum([*!*1, 2, 3*/!*]) == 6 (take all) +getMaxSubSum([*!*1, 2, 3*/!*]) == 6 (toma todo) ``` -If all items are negative, it means that we take none (the subarray is empty), so the sum is zero: +Si todos los elementos son negativos, no toma ninguno (el subarray queda vacío) y la suma es cero: ```js getMaxSubSum([-1, -2, -3]) = 0 ``` -Please try to think of a fast solution: [O(n2)](https://en.wikipedia.org/wiki/Big_O_notation) or even O(n) if you can. +Trata de pensar en una solución rápida: [O(n2)](https://es.wikipedia.org/wiki/Notaci%C3%B3n_de_Landau), o incluso O(n) si puedes. diff --git a/1-js/05-data-types/04-array/2-create-array/task.md b/1-js/05-data-types/04-array/2-create-array/task.md index d4551c79c..88a58c4ef 100644 --- a/1-js/05-data-types/04-array/2-create-array/task.md +++ b/1-js/05-data-types/04-array/2-create-array/task.md @@ -2,17 +2,17 @@ importance: 5 --- -# Array operations. +# Operaciones en arrays. -Let's try 5 array operations. +Tratemos 5 operaciones de array. -1. Create an array `styles` with items "Jazz" and "Blues". -2. Append "Rock-n-Roll" to the end. -3. Replace the value in the middle with "Classics". Your code for finding the middle value should work for any arrays with odd length. -4. Strip off the first value of the array and show it. -5. Prepend `Rap` and `Reggae` to the array. +1. Crear un array `styles` con los items "Jazz" y "Blues". +2. Agregar "Rock-n-Roll" al final. +3. Reemplazar el valor en el medio por "Classics". Tu código para encontrar el valor medio debe funcionar con cualquier array de longitud impar. +4. Quitar el primer valor del array y mostrarlo. +5. Anteponer `Rap` y `Reggae` al array. -The array in the process: +El array durante el proceso: ```js no-beautify Jazz, Blues @@ -21,4 +21,3 @@ Jazz, Classics, Rock-n-Roll Classics, Rock-n-Roll Rap, Reggae, Classics, Rock-n-Roll ``` - diff --git a/1-js/05-data-types/04-array/3-call-array-this/solution.md b/1-js/05-data-types/04-array/3-call-array-this/solution.md index 3cb0317cf..a1acd85f4 100644 --- a/1-js/05-data-types/04-array/3-call-array-this/solution.md +++ b/1-js/05-data-types/04-array/3-call-array-this/solution.md @@ -1,6 +1,6 @@ -The call `arr[2]()` is syntactically the good old `obj[method]()`, in the role of `obj` we have `arr`, and in the role of `method` we have `2`. +El llamado a `arr[2]()` es sintácticamente el buen y viejo `obj[method]()`, en el rol de `obj` tenemos `arr`, y en el rol de `method` tenemos `2`. -So we have a call of the function `arr[2]` as an object method. Naturally, it receives `this` referencing the object `arr` and outputs the array: +Entonces tenemos una llamada a función `arr[2]` como un método de objeto. Naturalmente, recibe `this` referenciando el objeto `arr` y su salida es el array: ```js run let arr = ["a", "b"]; @@ -12,4 +12,4 @@ arr.push(function() { arr[2](); // a,b,function(){...} ``` -The array has 3 values: initially it had two, plus the function. +El array tiene 3 valores: Inicialmente tenía 2 y se agregó la función. diff --git a/1-js/05-data-types/04-array/3-call-array-this/task.md b/1-js/05-data-types/04-array/3-call-array-this/task.md index f1e13499c..55388430b 100644 --- a/1-js/05-data-types/04-array/3-call-array-this/task.md +++ b/1-js/05-data-types/04-array/3-call-array-this/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# Calling in an array context +# LLamados en un contexto de array -What is the result? Why? +¿Cuál es el resultado y por qué? ```js let arr = ["a", "b"]; @@ -15,4 +15,3 @@ arr.push(function() { arr[2](); // ? ``` - diff --git a/1-js/05-data-types/04-array/5-array-input-sum/solution.md b/1-js/05-data-types/04-array/5-array-input-sum/solution.md index 75bd683b5..b137c7971 100644 --- a/1-js/05-data-types/04-array/5-array-input-sum/solution.md +++ b/1-js/05-data-types/04-array/5-array-input-sum/solution.md @@ -1,4 +1,4 @@ -Please note the subtle, but important detail of the solution. We don't convert `value` to number instantly after `prompt`, because after `value = +value` we would not be able to tell an empty string (stop sign) from the zero (valid number). We do it later instead. +Toma nota del sutil pero importante detalle de la solución. No convertimos `value` a número instantáneamente después de `prompt`, porque después de `value = +value` no seríamos capaces de diferenciar una cadena vacía (señal de detención) de un cero (un número válido). Lo hacemos más adelante. ```js run demo @@ -8,9 +8,9 @@ function sumInput() { while (true) { - let value = prompt("A number please?", 0); + let value = prompt("Un número, por favor...", 0); - // should we cancel? + // ¿Debemos cancelar? if (value === "" || value === null || !isFinite(value)) break; numbers.push(+value); @@ -25,4 +25,3 @@ function sumInput() { alert( sumInput() ); ``` - diff --git a/1-js/05-data-types/04-array/5-array-input-sum/task.md b/1-js/05-data-types/04-array/5-array-input-sum/task.md index 4af8e7c95..f863015f0 100644 --- a/1-js/05-data-types/04-array/5-array-input-sum/task.md +++ b/1-js/05-data-types/04-array/5-array-input-sum/task.md @@ -2,14 +2,14 @@ importance: 4 --- -# Sum input numbers +# Suma de números ingresados -Write the function `sumInput()` that: +Escribe una función `sumInput()` que: -- Asks the user for values using `prompt` and stores the values in the array. -- Finishes asking when the user enters a non-numeric value, an empty string, or presses "Cancel". -- Calculates and returns the sum of array items. +- Pida al usuario valores usando `prompt` y los almacene en el array. +- Termine de pedirlos cuando el usuario ingrese un valor no numérico, una cadena vacía, o presione "Escape". +- Calcule y devuelva la suma de los items del array. -P.S. A zero `0` is a valid number, please don't stop the input on zero. +P.D. Un cero `0` es un número válido, por favor no detengas los ingresos con el cero. [demo] diff --git a/1-js/05-data-types/04-array/array-pop.svg b/1-js/05-data-types/04-array/array-pop.svg index 82b112b4a..d418614f9 100644 --- a/1-js/05-data-types/04-array/array-pop.svg +++ b/1-js/05-data-types/04-array/array-pop.svg @@ -1 +1 @@ -0123"Apple""Orange""Pear""Lemon"length = 4clear012"Apple""Orange""Pear"length = 3 \ No newline at end of file +0123"Apple""Orange""Pear""Lemon"length = 4limpiar012"Apple""Orange""Pear"length = 3 \ No newline at end of file diff --git a/1-js/05-data-types/04-array/array-shift.svg b/1-js/05-data-types/04-array/array-shift.svg index 9485a3c96..c4f246a1a 100644 --- a/1-js/05-data-types/04-array/array-shift.svg +++ b/1-js/05-data-types/04-array/array-shift.svg @@ -1 +1 @@ -123"Orange""Pear""Lemon"length = 423"Orange""Pear""Lemon"length = 3clearmove elements to the left0"Apple"012"Orange""Pear""Lemon"11 \ No newline at end of file +123"Orange""Pear""Lemon"length = 423"Orange""Pear""Lemon"length = 3limpiarmover elementos a la izquierda0"Apple"012"Orange""Pear""Lemon"11 \ No newline at end of file diff --git a/1-js/05-data-types/04-array/article.md b/1-js/05-data-types/04-array/article.md index 629a7c77f..6495e5c87 100644 --- a/1-js/05-data-types/04-array/article.md +++ b/1-js/05-data-types/04-array/article.md @@ -1,31 +1,31 @@ # Arrays -Objects allow you to store keyed collections of values. That's fine. +Los objetos te permiten almacenar colecciones de datos a través de nombres. Eso está bien. -But quite often we find that we need an *ordered collection*, where we have a 1st, a 2nd, a 3rd element and so on. For example, we need that to store a list of something: users, goods, HTML elements etc. +Pero a menudo necesitamos una *colección ordenada*, donde tenemos un 1ro, un 2do, un 3er elemento y así sucesivamente. Por ejemplo, necesitamos almacenar una lista de algo: usuarios, bienes, elementos HTML, etc. -It is not convenient to use an object here, because it provides no methods to manage the order of elements. We can’t insert a new property “between” the existing ones. Objects are just not meant for such use. +No es conveniente usar objetos aquí, porque no proveen métodos para manejar el orden de los elementos. No podemos insertar una nueva propiedad “entre” los existentes. Los objetos no están hechos para eso. -There exists a special data structure named `Array`, to store ordered collections. +Existe una estructura llamada `Array` (llamada en español arreglo o matriz/vector) para almacenar colecciones ordenadas. -## Declaration +## Declaración -There are two syntaxes for creating an empty array: +Hay dos sintaxis para crear un array vacío: ```js let arr = new Array(); let arr = []; ``` -Almost all the time, the second syntax is used. We can supply initial elements in the brackets: +Casi siempre se usa la segunda. Podemos suministrar elementos iniciales entre los corchetes: ```js let fruits = ["Apple", "Orange", "Plum"]; ``` -Array elements are numbered, starting with zero. +Los elementos del array están numerados comenzando desde cero. -We can get an element by its number in square brackets: +Podemos obtener un elemento por su número entre corchetes: ```js run let fruits = ["Apple", "Orange", "Plum"]; @@ -35,19 +35,19 @@ alert( fruits[1] ); // Orange alert( fruits[2] ); // Plum ``` -We can replace an element: +Podemos reemplazar un elemento: ```js -fruits[2] = 'Pear'; // now ["Apple", "Orange", "Pear"] +fruits[2] = 'Pear'; // ahora ["Apple", "Orange", "Pear"] ``` -...Or add a new one to the array: +...o agregar uno nuevo al array: ```js -fruits[3] = 'Lemon'; // now ["Apple", "Orange", "Pear", "Lemon"] +fruits[3] = 'Lemon'; // ahora ["Apple", "Orange", "Pear", "Lemon"] ``` -The total count of the elements in the array is its `length`: +La cuenta total de elementos en el array es su longitud `length`: ```js run let fruits = ["Apple", "Orange", "Plum"]; @@ -55,7 +55,7 @@ let fruits = ["Apple", "Orange", "Plum"]; alert( fruits.length ); // 3 ``` -We can also use `alert` to show the whole array. +También podemos usar `alert` para mostrar el array completo. ```js run let fruits = ["Apple", "Orange", "Plum"]; @@ -63,24 +63,24 @@ let fruits = ["Apple", "Orange", "Plum"]; alert( fruits ); // Apple,Orange,Plum ``` -An array can store elements of any type. +Un array puede almacenar elementos de cualquier tipo. -For instance: +Por ejemplo: ```js run no-beautify -// mix of values +// mezcla de valores let arr = [ 'Apple', { name: 'John' }, true, function() { alert('hello'); } ]; -// get the object at index 1 and then show its name +// obtener el objeto del índice 1 y mostrar su nombre alert( arr[1].name ); // John -// get the function at index 3 and run it +// obtener la función del índice 3 y ejecutarla arr[3](); // hello ``` -````smart header="Trailing comma" -An array, just like an object, may end with a comma: +````smart header="Coma residual" +Un array, al igual que un objeto, puede tener una coma final: ```js let fruits = [ "Apple", @@ -89,20 +89,20 @@ let fruits = [ ]; ``` -The "trailing comma" style makes it easier to insert/remove items, because all lines become alike. +La "coma final" hace más simple insertar y remover items, porque todas la líneas se vuelven similares. ```` -## Get last elements with "at" +## Obtener los últimos elementos con "at" [recent browser="new"] -Let's say we want the last element of the array. +Digamos que queremos el último elemento de un array. -Some programming languages allow the use of negative indexes for the same purpose, like `fruits[-1]`. +Algunos lenguajes de programación permiten el uso de índices negativos para este propósito, como `fruits[-1]`. -However, in JavaScript it won't work. The result will be `undefined`, because the index in square brackets is treated literally. +Sin embargo, en JavaScript esto no funcionará. El resultado será `undefined`, porque el índice entre corchetes se interpreta literalmente. -We can explicitly calculate the last element index and then access it: `fruits[fruits.length - 1]`. +Podemos calcular explícitamente el último índice y luego acceder al elemento: `fruits[fruits.length - 1]`. ```js run let fruits = ["Apple", "Orange", "Plum"]; @@ -110,70 +110,70 @@ let fruits = ["Apple", "Orange", "Plum"]; alert( fruits[fruits.length-1] ); // Plum ``` -A bit cumbersome, isn't it? We need to write the variable name twice. +Un poco engorroso, ¿no es cierto? Necesitamos escribir el nombre de la variable dos veces. -Luckily, there's a shorter syntax: `fruits.at(-1)`: +Afortunadamente, hay una sintaxis más corta: `fruits.at(-1)`: ```js run let fruits = ["Apple", "Orange", "Plum"]; -// same as fruits[fruits.length-1] +// es lo mismo que fruits[fruits.length-1] alert( fruits.at(-1) ); // Plum ``` -In other words, `arr.at(i)`: -- is exactly the same as `arr[i]`, if `i >= 0`. -- for negative values of `i`, it steps back from the end of the array. +En otras palabras, `arr.at(i)`: +- es exactamente lo mismo que `arr[i]`, si `i >= 0`. +- para valores negativos de `i`, salta hacia atrás desde el final del array. -## Methods pop/push, shift/unshift +## Métodos pop/push, shift/unshift -A [queue](https://en.wikipedia.org/wiki/Queue_(abstract_data_type)) is one of the most common uses of an array. In computer science, this means an ordered collection of elements which supports two operations: +Una [cola](https://es.wikipedia.org/wiki/Cola_(inform%C3%A1tica)) es uno de los usos más comunes de un array. En ciencias de la computación, significa una colección ordenada de elementos que soportan dos operaciones: -- `push` appends an element to the end. -- `shift` get an element from the beginning, advancing the queue, so that the 2nd element becomes the 1st. +- `push` inserta un elemento al final. +- `shift` obtiene el elemento del principio, avanzando la cola, y así el segundo elemento se vuelve primero. ![](queue.svg) -Arrays support both operations. +Los arrays soportan ambas operaciones. -In practice we need it very often. For example, a queue of messages that need to be shown on-screen. +En la práctica los necesitamos muy a menudo. Por ejemplo, una cola de mensajes que necesitamos mostrar en pantalla. -There's another use case for arrays -- the data structure named [stack](https://en.wikipedia.org/wiki/Stack_(abstract_data_type)). +Hay otro caso de uso para los arrays -- la estructura de datos llamada [pila](https://es.wikipedia.org/wiki/Pila_(inform%C3%A1tica)). -It supports two operations: +Ella soporta dos operaciones: -- `push` adds an element to the end. -- `pop` takes an element from the end. +- `push` agrega un elemento al final. +- `pop` toma un elemento desde el final. -So new elements are added or taken always from the "end". +Entonces los elementos nuevos son agregados o tomados siempre desde el "final". -A stack is usually illustrated as a pack of cards: new cards are added to the top or taken from the top: +Una pila es usualmente mostrada como un mazo de cartas, donde las nuevas cartas son agregadas al tope o tomadas desde el tope: ![](stack.svg) -For stacks, the latest pushed item is received first, that's also called LIFO (Last-In-First-Out) principle. For queues, we have FIFO (First-In-First-Out). +Para las pilas, la última introducida es la primera en ser recibida, en inglés esto es llamado principio LIFO (Last-In-First-Out, última en entrar primera en salir). Para las colas, tenemos FIFO (First-In-First-Out primera en entrar, primera en salir). -Arrays in JavaScript can work both as a queue and as a stack. They allow you to add/remove elements, both to/from the beginning or the end. +Los arrays en JavaScript pueden trabajar como colas o pilas. Ellos permiten agregar/quitar elementos al/del principio o al/del final. -In computer science, the data structure that allows this, is called [deque](https://en.wikipedia.org/wiki/Double-ended_queue). +En ciencias de la computación, la estructura de datos que permite esto se denomina cola de doble extremo o [bicola](https://es.wikipedia.org/wiki/Bicola). -**Methods that work with the end of the array:** +**Métodos que trabajan sobre el final del array:** `pop` -: Extracts the last element of the array and returns it: +: Extrae el último elemento del array y lo devuelve: ```js run let fruits = ["Apple", "Orange", "Pear"]; - alert( fruits.pop() ); // remove "Pear" and alert it + alert( fruits.pop() ); // quita "Pear" y lo muestra en un alert alert( fruits ); // Apple, Orange ``` - Both `fruits.pop()` and `fruits.at(-1)` return the last element of the array, but `fruits.pop()` also modifies the array by removing it. + Tanto `fruits.pop()` como `fruits.at(-1)` devuelven el último elemento del array, pero `fruits.pop()` también modifica el array eliminando tal elemento. `push` -: Append the element to the end of the array: +: Agrega el elemento al final del array: ```js run let fruits = ["Apple", "Orange"]; @@ -183,23 +183,23 @@ In computer science, the data structure that allows this, is called [deque](http alert( fruits ); // Apple, Orange, Pear ``` - The call `fruits.push(...)` is equal to `fruits[fruits.length] = ...`. + El llamado a `fruits.push(...)` es igual a `fruits[fruits.length] = ...`. -**Methods that work with the beginning of the array:** +**Métodos que trabajan con el principio del array:** `shift` -: Extracts the first element of the array and returns it: +: Extrae el primer elemento del array y lo devuelve: ```js run let fruits = ["Apple", "Orange", "Pear"]; - alert( fruits.shift() ); // remove Apple and alert it + alert( fruits.shift() ); // quita Apple y lo muestra en un alert alert( fruits ); // Orange, Pear ``` `unshift` -: Add the element to the beginning of the array: +: Agrega el elemento al principio del array: ```js run let fruits = ["Orange", "Pear"]; @@ -209,7 +209,7 @@ In computer science, the data structure that allows this, is called [deque](http alert( fruits ); // Apple, Orange, Pear ``` -Methods `push` and `unshift` can add multiple elements at once: +Los métodos `push` y `unshift` pueden agregar múltiples elementos de una vez: ```js run let fruits = ["Apple"]; @@ -221,97 +221,97 @@ fruits.unshift("Pineapple", "Lemon"); alert( fruits ); ``` -## Internals +## Interiores -An array is a special kind of object. The square brackets used to access a property `arr[0]` actually come from the object syntax. That's essentially the same as `obj[key]`, where `arr` is the object, while numbers are used as keys. +Un array es una clase especial de objeto. Los corchetes usados para acceder a una propiedad `arr[0]` vienen de la sintaxis de objeto. Son esencialmente lo mismo que `obj[key]`, donde `arr` es el objeto mientras los números son usados como claves. -They extend objects providing special methods to work with ordered collections of data and also the `length` property. But at the core it's still an object. +Ellos extienden los objetos proveyendo métodos especiales para trabajar con colecciones ordenadas de datos y también la propiedad `length`. Pero en el corazón es aún un objeto. -Remember, there are only eight basic data types in JavaScript (see the [Data types](info:types) chapter for more info). Array is an object and thus behaves like an object. +Recuerde, solo hay ocho tipos de datos básicos en JavaScript (consulte el capítulo [Tipos de datos](info:types) para obtener más información). Array es un objeto y, por tanto, se comporta como un objeto. -For instance, it is copied by reference: +Por ejemplo, es copiado por referencia: ```js run let fruits = ["Banana"] -let arr = fruits; // copy by reference (two variables reference the same array) +let arr = fruits; // copiado por referencia (dos variables referencian al mismo array) alert( arr === fruits ); // true -arr.push("Pear"); // modify the array by reference +arr.push("Pear"); // modifica el array por referencia -alert( fruits ); // Banana, Pear - 2 items now +alert( fruits ); // Banana, Pear - ahora con 2 items ``` -...But what makes arrays really special is their internal representation. The engine tries to store its elements in the contiguous memory area, one after another, just as depicted on the illustrations in this chapter, and there are other optimizations as well, to make arrays work really fast. +...Pero lo que hace a los array realmente especiales es su representación interna. El motor trata de almacenarlos en áreas de memoria contigua, uno tras otro, justo como muestra la ilustración en este capítulo. Hay otras optimizaciones también para hacer que los arrays trabajen verdaderamente rápido. -But they all break if we quit working with an array as with an "ordered collection" and start working with it as if it were a regular object. +Pero todo esto se puede malograr si dejamos de trabajarlos como arrays de colecciones ordenadas y comenzamos a usarlos como si fueran objetos comunes. -For instance, technically we can do this: +Por ejemplo, técnicamente podemos hacer esto: ```js -let fruits = []; // make an array +let fruits = []; // crea un array -fruits[99999] = 5; // assign a property with the index far greater than its length +fruits[99999] = 5; // asigna una propiedad con un índice mucho mayor que su longitud -fruits.age = 25; // create a property with an arbitrary name +fruits.age = 25; // crea una propiedad con un nombre arbitrario ``` -That's possible, because arrays are objects at their base. We can add any properties to them. +Esto es posible porque los arrays son objetos en su base. Podemos agregar cualquier propiedad en ellos. -But the engine will see that we're working with the array as with a regular object. Array-specific optimizations are not suited for such cases and will be turned off, their benefits disappear. +Pero el motor verá que estamos tratándolo como un objeto común. Las optimizaciones específicas no son aptas para tales casos y serán desechadas, y sus beneficios desaparecerán. -The ways to misuse an array: +Las formas de malograr un array: -- Add a non-numeric property like `arr.test = 5`. -- Make holes, like: add `arr[0]` and then `arr[1000]` (and nothing between them). -- Fill the array in the reverse order, like `arr[1000]`, `arr[999]` and so on. +- Agregar una propiedad no numérica como `arr.test = 5`. +- Generar agujeros como: agregar `arr[0]` y luego `arr[1000]` (y nada entre ellos). +- Llenar el array en orden inverso, como `arr[1000]`, `arr[999]` y así. -Please think of arrays as special structures to work with the *ordered data*. They provide special methods for that. Arrays are carefully tuned inside JavaScript engines to work with contiguous ordered data, please use them this way. And if you need arbitrary keys, chances are high that you actually require a regular object `{}`. +Piensa en los arrays como estructuras especiales para trabajar con *datos ordenados*. Ellos proveen métodos especiales para ello. Los arrays están cuidadosamente afinados dentro de los motores JavaScript para funcionar con datos ordenados contiguos, por favor úsalos de esa manera. Y si necesitas claves arbitrarias, hay altas chances de que en realidad necesites objetos comunes `{}`. ## Performance -Methods `push/pop` run fast, while `shift/unshift` are slow. +Los métodos `push/pop` son rápidos, mientras que `shift/unshift` son lentos. ![](array-speed.svg) -Why is it faster to work with the end of an array than with its beginning? Let's see what happens during the execution: +¿Por qué es más rápido trabajar con el final del array que con el principio? Veamos qué pasa durante la ejecución: ```js -fruits.shift(); // take 1 element from the start +fruits.shift(); // toma 1 elemento del principio ``` -It's not enough to take and remove the element with the index `0`. Other elements need to be renumbered as well. +No es suficiente tomar y eliminar el elemento con el índice `0`. Los demás elementos necesitan ser renumerados también. -The `shift` operation must do 3 things: +La operación `shift` debe hacer 3 cosas: -1. Remove the element with the index `0`. -2. Move all elements to the left, renumber them from the index `1` to `0`, from `2` to `1` and so on. -3. Update the `length` property. +1. Remover el elemento con índice `0`. +2. Mover todos lo elementos hacia la izquierda y renumerarlos: desde el índice `1` a `0`, de `2` a `1` y así sucesivamente. +3. Actualizar la longitud: la propiedad `length`. ![](array-shift.svg) -**The more elements in the array, the more time to move them, more in-memory operations.** +**Cuanto más elementos haya en el array, más tiempo tomará moverlos, más operaciones en memoria.** -The similar thing happens with `unshift`: to add an element to the beginning of the array, we need first to move existing elements to the right, increasing their indexes. +Algo similar ocurre con `unshift`: para agregar un elemento al principio del array, necesitamos primero mover todos los elementos hacia la derecha, incrementando sus índices. -And what's with `push/pop`? They do not need to move anything. To extract an element from the end, the `pop` method cleans the index and shortens `length`. +¿Y qué pasa con `push/pop`? Ellos no necesitan mover nada. Para extraer un elemento del final, el método `pop` limpia el índice y acorta `length`. -The actions for the `pop` operation: +Las acciones para la operación `pop`: ```js -fruits.pop(); // take 1 element from the end +fruits.pop(); // toma 1 elemento del final ``` ![](array-pop.svg) -**The `pop` method does not need to move anything, because other elements keep their indexes. That's why it's blazingly fast.** +**El método `pop` no necesita mover nada, porque los demás elementos mantienen sus índices. Es por ello que es muy rápido.** -The similar thing with the `push` method. +Algo similar ocurre con el método `push`. -## Loops +## Bucles -One of the oldest ways to cycle array items is the `for` loop over indexes: +Una de las formas más viejas de iterar los items de un array es el bucle `for` sobre sus índices: ```js run let arr = ["Apple", "Orange", "Pear"]; @@ -323,20 +323,20 @@ for (let i = 0; i < arr.length; i++) { } ``` -But for arrays there is another form of loop, `for..of`: +Pero para los arrays también hay otra forma de bucle,`for..of`: ```js run let fruits = ["Apple", "Orange", "Plum"]; -// iterates over array elements +// itera sobre los elementos del array for (let fruit of fruits) { alert( fruit ); } ``` -The `for..of` doesn't give access to the number of the current element, just its value, but in most cases that's enough. And it's shorter. +`for..of` no da acceso al número del elemento en curso, solamente a su valor, pero en la mayoría de los casos eso es suficiente. Y es más corto. -Technically, because arrays are objects, it is also possible to use `for..in`: +Técnicamente, y porque los arrays son objetos, es también posible usar `for..in`: ```js run let arr = ["Apple", "Orange", "Pear"]; @@ -348,22 +348,22 @@ for (let key in arr) { } ``` -But that's actually a bad idea. There are potential problems with it: +Pero es una mala idea. Existen problemas potenciales con esto: -1. The loop `for..in` iterates over *all properties*, not only the numeric ones. +1. El bucle `for..in` itera sobre *todas las propiedades*, no solo las numéricas. - There are so-called "array-like" objects in the browser and in other environments, that *look like arrays*. That is, they have `length` and indexes properties, but they may also have other non-numeric properties and methods, which we usually don't need. The `for..in` loop will list them though. So if we need to work with array-like objects, then these "extra" properties can become a problem. + Existen objetos "simil-array" en el navegador y otros ambientes que *parecen arrays*. Esto es, tienen `length` y propiedades indexadas, pero pueden también tener propiedades no numéricas y métodos que usualmente no necesitemos. Y el bucle `for..in` los listará. Entonces si necesitamos trabajar con objetos simil-array, estas propiedades "extras" pueden volverse un problema. -2. The `for..in` loop is optimized for generic objects, not arrays, and thus is 10-100 times slower. Of course, it's still very fast. The speedup may only matter in bottlenecks. But still we should be aware of the difference. +2. El bucle `for..in` está optimizado para objetos genéricos, no para arrays, y es de 10 a 100 veces más lento. Por supuesto es aún muy rápido. Una optimización puede que solo sea importante en cuellos de botella, pero necesitamos ser concientes de la diferencia. -Generally, we shouldn't use `for..in` for arrays. +En general, no deberíamos usar `for..in` en arrays. -## A word about "length" +## Acerca de "length" -The `length` property automatically updates when we modify the array. To be precise, it is actually not the count of values in the array, but the greatest numeric index plus one. +La propiedad `length` automáticamente se actualiza cuando se modifica el array. Para ser precisos, no es la cuenta de valores del array sino el mayor índice más uno. -For instance, a single element with a large index gives a big length: +Por ejemplo, un elemento simple con un índice grande da una longitud grande: ```js run let fruits = []; @@ -372,52 +372,52 @@ fruits[123] = "Apple"; alert( fruits.length ); // 124 ``` -Note that we usually don't use arrays like that. +Nota que usualmente no usamos arrays de este modo. -Another interesting thing about the `length` property is that it's writable. +Otra cosa interesante acerca de la propiedad `length` es que se puede sobrescribir. -If we increase it manually, nothing interesting happens. But if we decrease it, the array is truncated. The process is irreversible, here's the example: +Si la incrementamos manualmente, nada interesante ocurre. Pero si la decrementamos, el array se trunca. El proceso es irreversible, aquí el ejemplo: ```js run let arr = [1, 2, 3, 4, 5]; -arr.length = 2; // truncate to 2 elements +arr.length = 2; // truncamos a 2 elementos alert( arr ); // [1, 2] -arr.length = 5; // return length back -alert( arr[3] ); // undefined: the values do not return +arr.length = 5; // reponemos la longitud length +alert( arr[3] ); // undefined: el valor no se recupera ``` -So, the simplest way to clear the array is: `arr.length = 0;`. +Entonces la forma más simple de limpiar un array es: `arr.length = 0;`. ## new Array() [#new-array] -There is one more syntax to create an array: +Hay una sintaxis más para crear un array: ```js let arr = *!*new Array*/!*("Apple", "Pear", "etc"); ``` -It's rarely used, because square brackets `[]` are shorter. Also, there's a tricky feature with it. +Es raramente usada porque con corchetes `[]` es más corto. También hay una característica peculiar con ella. -If `new Array` is called with a single argument which is a number, then it creates an array *without items, but with the given length*. +Si `new Array` es llamado con un único argumento numérico, se crea un array *sin items, pero con la longitud "length" dada*. -Let's see how one can shoot themselves in the foot: +Veamos cómo uno puede dispararse en el pie: ```js run -let arr = new Array(2); // will it create an array of [2] ? +let arr = new Array(2); // ¿Creará un array de [2]? -alert( arr[0] ); // undefined! no elements. +alert( arr[0] ); // undefined! sin elementos. -alert( arr.length ); // length 2 +alert( arr.length ); // longitud 2 ``` -To avoid such surprises, we usually use square brackets, unless we really know what we're doing. +Para evitar sorpresas solemos usar corchetes, salvo que sepamos lo que estamos haciendo. -## Multidimensional arrays +## Arrays multidimensionales -Arrays can have items that are also arrays. We can use it for multidimensional arrays, for example to store matrices: +Los arrays pueden tener items que a su vez sean arrays. Podemos usarlos como arrays multidimensionales, por ejemplo para almacenar matrices: ```js run let matrix = [ @@ -426,14 +426,14 @@ let matrix = [ [7, 8, 9] ]; -alert( matrix[0][1] ); // 2, the second value of the first inner array +alert( matrix[0][1] ); // 2, el segundo valor del primer array interno ``` ## toString -Arrays have their own implementation of `toString` method that returns a comma-separated list of elements. +Los arrays tienen su propia implementación del método `toString` que devuelve un lista de elementos separados por coma. -For instance: +Por ejemplo: ```js run @@ -443,7 +443,7 @@ alert( arr ); // 1,2,3 alert( String(arr) === '1,2,3' ); // true ``` -Also, let's try this: +Probemos esto también: ```js run alert( [] + 1 ); // "1" @@ -451,9 +451,9 @@ alert( [1] + 1 ); // "11" alert( [1,2] + 1 ); // "1,21" ``` -Arrays do not have `Symbol.toPrimitive`, neither a viable `valueOf`, they implement only `toString` conversion, so here `[]` becomes an empty string, `[1]` becomes `"1"` and `[1,2]` becomes `"1,2"`. +Los arrays no tienen `Symbol.toPrimitive` ni un `valueOf` viable, ellos implementan la conversión `toString` solamente, así `[]` se vuelve una cadena vacía, `[1]` se vuelve `"1"` y `[1,2]` se vuelve `"1,2"`. -When the binary plus `"+"` operator adds something to a string, it converts it to a string as well, so the next step looks like this: +Cuando el operador binario más `"+"` suma algo a una cadena, lo convierte a cadena también, entonces lo siguiente se ve así: ```js run alert( "" + 1 ); // "1" @@ -461,91 +461,91 @@ alert( "1" + 1 ); // "11" alert( "1,2" + 1 ); // "1,21" ``` -## Don't compare arrays with == +## No compares arrays con == -Arrays in JavaScript, unlike some other programming languages, shouldn't be compared with operator `==`. +Las arrays en JavaScript, a diferencia de otros lenguajes de programación, no deben ser comparadas con el operador `==`. -This operator has no special treatment for arrays, it works with them as with any objects. +Este operador no tiene un tratamiento especial para arrays, trabaja con ellas como con cualquier objeto. -Let's recall the rules: +Recordemos las reglas: -- Two objects are equal `==` only if they're references to the same object. -- If one of the arguments of `==` is an object, and the other one is a primitive, then the object gets converted to primitive, as explained in the chapter . -- ...With an exception of `null` and `undefined` that equal `==` each other and nothing else. +- Dos objetos son iguales `==` solo si hacen referencia al mismo objeto. +- Si uno de los argumentos de `==` es un objeto y el otro es un primitivo, entonces el objeto se convierte en primitivo, como se explica en el capítulo . +- ...Con la excepción de `null` y `undefined` que son iguales `==` entre sí y nada más. -The strict comparison `===` is even simpler, as it doesn't convert types. +La comparación estricta `===` es aún más simple, ya que no convierte tipos. -So, if we compare arrays with `==`, they are never the same, unless we compare two variables that reference exactly the same array. +Entonces, si comparamos arrays con `==`, nunca son iguales, a no ser que comparemos dos variables que hacen referencia exactamente a la misma array. -For example: +Por ejemplo: ```js run -alert( [] == [] ); // false -alert( [0] == [0] ); // false +alert( [] == [] ); // falso +alert( [0] == [0] ); // falso ``` -These arrays are technically different objects. So they aren't equal. The `==` operator doesn't do item-by-item comparison. +Estas arrays son técnicamente objetos diferentes. Así que no son iguales. El operador `==` no hace comparaciones de elemento a elemento. -Comparison with primitives may give seemingly strange results as well: +Comparaciones con primitivos también pueden dar resultados aparentemente extraños: ```js run -alert( 0 == [] ); // true +alert( 0 == [] ); // verdadero -alert('0' == [] ); // false +alert('0' == [] ); // falso ``` -Here, in both cases, we compare a primitive with an array object. So the array `[]` gets converted to primitive for the purpose of comparison and becomes an empty string `''`. +Aquí, en ambos casos, comparamos un primitivo con un objeto array. Entonces la array `[]` se convierte a primitivo para el propósito de comparar y se convierte en una string vacía `''`. -Then the comparison process goes on with the primitives, as described in the chapter : +Luego el proceso de comparación continúa con los primitivos, como se describe en el capítulo : ```js run -// after [] was converted to '' -alert( 0 == '' ); // true, as '' becomes converted to number 0 +// después de que [] se convierta en '' +alert( 0 == '' ); // verdadero, ya que '' se convierte en el número 0 -alert('0' == '' ); // false, no type conversion, different strings +alert('0' == '' ); // falso, sin conversión de tipos, strings diferentes ``` -So, how to compare arrays? +Entonces, ¿cómo comparamos arrays? -That's simple: don't use the `==` operator. Instead, compare them item-by-item in a loop or using iteration methods explained in the next chapter. +Simple: no utilices el operador `==`. En lugar, compáralas elemento a elemento en un bucle o utilizando métodos de iteración explicados en el siguiente capítulo. -## Summary +## Resumen -Array is a special kind of object, suited to storing and managing ordered data items. +Los arrays son una clase especial de objeto, adecuados para almacenar y manejar items de datos ordenados. -The declaration: +La declaración: ```js -// square brackets (usual) +// corchetes (lo usual) let arr = [item1, item2...]; -// new Array (exceptionally rare) +// new Array (excepcionalmente raro) let arr = new Array(item1, item2...); ``` -The call to `new Array(number)` creates an array with the given length, but without elements. +El llamado a `new Array(number)` crea un array con la longitud dada, pero sin elementos. -- The `length` property is the array length or, to be precise, its last numeric index plus one. It is auto-adjusted by array methods. -- If we shorten `length` manually, the array is truncated. +- La propiedad `length` es la longitud del array o, para ser preciso, el último índice numérico más uno. Se autoajusta al usar los métodos de array. +- Si acortamos `length` manualmente, el array se trunca. -Getting the elements: +Obtener los elementos: -- we can get element by its index, like `arr[0]` -- also we can use `at(i)` method that allows negative indexes. For negative values of `i`, it steps back from the end of the array. If `i >= 0`, it works same as `arr[i]`. +- Podemos obtener un elemento por su índice, como `arr[0]` +- También podemos usar el método `at(i)`, que permite índices negativos. Para valores negativos de `i`, cuenta hacia atrás desde el final del array. Cuando `i >= 0`, funciona igual que `arr[i]`. -We can use an array as a deque with the following operations: +Podemos usar un array como una pila "deque" o "bicola" con las siguientes operaciones: -- `push(...items)` adds `items` to the end. -- `pop()` removes the element from the end and returns it. -- `shift()` removes the element from the beginning and returns it. -- `unshift(...items)` adds `items` to the beginning. +- `push(...items)` agrega `items` al final. +- `pop()` remueve el elemento del final y lo devuelve. +- `shift()` remueve el elemento del principio y lo devuelve. +- `unshift(...items)` agrega `items` al principio. -To loop over the elements of the array: - - `for (let i=0; i`, `<` and others), as they have no special treatment for arrays. They handle them as any objects, and it's not what we usually want. +Para comparar arrays, no uses el operador `==` (como tampoco `>`, `<` y otros), ya que no tienen un tratamiento especial para arrays. Lo manejan como cualquier objeto y no es lo que normalmente queremos. -Instead you can use `for..of` loop to compare arrays item-by-item. +En su lugar puedes utilizar el bucle `for..of` para comparar arrays elemento a elemento. -We will continue with arrays and study more methods to add, remove, extract elements and sort arrays in the next chapter . +Volveremos a los arrays y estudiaremos más métodos para agregar, quitar, extraer elementos y ordenar arrays en el capítulo . diff --git a/1-js/05-data-types/05-array-methods/1-camelcase/_js.view/solution.js b/1-js/05-data-types/05-array-methods/1-camelcase/_js.view/solution.js index 490f570ad..e390cf5cb 100644 --- a/1-js/05-data-types/05-array-methods/1-camelcase/_js.view/solution.js +++ b/1-js/05-data-types/05-array-methods/1-camelcase/_js.view/solution.js @@ -1,10 +1,10 @@ function camelize(str) { return str - .split('-') // splits 'my-long-word' into array ['my', 'long', 'word'] + .split('-') // separa 'my-long-word' en el array ['my', 'long', 'word'] .map( - // capitalizes first letters of all array items except the first one - // converts ['my', 'long', 'word'] into ['my', 'Long', 'Word'] + // convierte en mayúscula todas las primeras letras de los elementos del array excepto por el primero + // convierte ['my', 'long', 'word'] en ['my', 'Long', 'Word'] (word, index) => index == 0 ? word : word[0].toUpperCase() + word.slice(1) ) - .join(''); // joins ['my', 'Long', 'Word'] into 'myLongWord' + .join(''); // une ['my', 'Long', 'Word'] en 'myLongWord' } diff --git a/1-js/05-data-types/05-array-methods/1-camelcase/task.md b/1-js/05-data-types/05-array-methods/1-camelcase/task.md index ef5944636..4b8b311d1 100644 --- a/1-js/05-data-types/05-array-methods/1-camelcase/task.md +++ b/1-js/05-data-types/05-array-methods/1-camelcase/task.md @@ -2,13 +2,13 @@ importance: 5 --- -# Translate border-left-width to borderLeftWidth +# Transforma border-left-width en borderLeftWidth -Write the function `camelize(str)` that changes dash-separated words like "my-short-string" into camel-cased "myShortString". +Escribe la función `camelize(str)` que convierta palabras separadas por guión como "mi-cadena-corta" en palabras con mayúscula "miCadenaCorta". -That is: removes all dashes, each word after dash becomes uppercased. +Esto sería: remover todos los guiones y que cada palabra después de un guión comience con mayúscula. -Examples: +Ejemplos: ```js camelize("background-color") == 'backgroundColor'; @@ -16,4 +16,4 @@ camelize("list-style-image") == 'listStyleImage'; camelize("-webkit-transition") == 'WebkitTransition'; ``` -P.S. Hint: use `split` to split the string into an array, transform it and `join` back. +P.D. Pista: usa `split` para dividir el string en un array, transfórmalo y vuelve a unirlo (`join`). diff --git a/1-js/05-data-types/05-array-methods/10-average-age/task.md b/1-js/05-data-types/05-array-methods/10-average-age/task.md index bf5f85df3..f32a5a8c9 100644 --- a/1-js/05-data-types/05-array-methods/10-average-age/task.md +++ b/1-js/05-data-types/05-array-methods/10-average-age/task.md @@ -2,13 +2,13 @@ importance: 4 --- -# Get average age +# Obtener edad promedio -Write the function `getAverageAge(users)` that gets an array of objects with property `age` and returns the average age. +Escribe la función `getAverageAge(users)` que obtenga un array de objetos con la propiedad `age` y devuelva el promedio de `age`. -The formula for the average is `(age1 + age2 + ... + ageN) / N`. +La fórmula de promedio es `(age1 + age2 + ... + ageN) / N`. -For instance: +Por ejemplo: ```js no-beautify let john = { name: "John", age: 25 }; diff --git a/1-js/05-data-types/05-array-methods/11-array-unique/solution.md b/1-js/05-data-types/05-array-methods/11-array-unique/solution.md index b9d627a0a..6ca9f741e 100644 --- a/1-js/05-data-types/05-array-methods/11-array-unique/solution.md +++ b/1-js/05-data-types/05-array-methods/11-array-unique/solution.md @@ -1,6 +1,6 @@ -Let's walk the array items: -- For each item we'll check if the resulting array already has that item. -- If it is so, then ignore, otherwise add to results. +Recorramos los elementos dentro del array: +- Para cada elemento vamos a comprobar si el array resultante ya tiene ese elemento. +- Si ya lo tiene, ignora. Si no, agrega el resultado. ```js run demo function unique(arr) { @@ -22,18 +22,18 @@ let strings = ["Hare", "Krishna", "Hare", "Krishna", alert( unique(strings) ); // Hare, Krishna, :-O ``` -The code works, but there's a potential performance problem in it. +El código funciona, pero tiene un problema potencial de desempeño. -The method `result.includes(str)` internally walks the array `result` and compares each element against `str` to find the match. +El método `result.includes(str)` internamente recorre el array `result` y compara cada elemento con `str` para encontrar una coincidencia. -So if there are `100` elements in `result` and no one matches `str`, then it will walk the whole `result` and do exactly `100` comparisons. And if `result` is large, like `10000`, then there would be `10000` comparisons. +Por lo tanto, si hay `100` elementos en `result` y ninguno coincide con `str`, entonces habrá recorrido todo el array `result` y ejecutado `100` comparaciones. Y si `result` es tan grande como `10000`, entonces habrá `10000` comparaciones. -That's not a problem by itself, because JavaScript engines are very fast, so walk `10000` array is a matter of microseconds. +Esto no es un problema en sí mismo, porque los motores JavaScript son muy rápidos, por lo que recorrer `10000` elementos de un array solo le tomaría microsegundos. -But we do such test for each element of `arr`, in the `for` loop. +Pero ejecutamos dicha comprobación para cada elemento de `arr` en el loop `for`. -So if `arr.length` is `10000` we'll have something like `10000*10000` = 100 millions of comparisons. That's a lot. +Entonces si `arr.length` es `10000` vamos a tener algo como `10000*10000` = 100 millones de comparaciones. Esto es realmente mucho. -So the solution is only good for small arrays. +Por lo que la solución solo es buena para arrays pequeños. -Further in the chapter we'll see how to optimize it. +Más adelante en el capítulo vamos a ver como optimizarlo. diff --git a/1-js/05-data-types/05-array-methods/11-array-unique/task.md b/1-js/05-data-types/05-array-methods/11-array-unique/task.md index 5b56d3621..11495a299 100644 --- a/1-js/05-data-types/05-array-methods/11-array-unique/task.md +++ b/1-js/05-data-types/05-array-methods/11-array-unique/task.md @@ -2,17 +2,17 @@ importance: 4 --- -# Filter unique array members +# Filtrar elementos únicos de un array -Let `arr` be an array. +Partiendo del array `arr`. -Create a function `unique(arr)` that should return an array with unique items of `arr`. +Crea una función `unique(arr)` que devuelva un array con los elementos que se encuentran una sola vez dentro de `arr`. -For instance: +Por ejemplo: ```js function unique(arr) { - /* your code */ + /* tu código */ } let strings = ["Hare", "Krishna", "Hare", "Krishna", diff --git a/1-js/05-data-types/05-array-methods/12-reduce-object/task.md b/1-js/05-data-types/05-array-methods/12-reduce-object/task.md index 7f0082357..a6a283819 100644 --- a/1-js/05-data-types/05-array-methods/12-reduce-object/task.md +++ b/1-js/05-data-types/05-array-methods/12-reduce-object/task.md @@ -2,13 +2,13 @@ importance: 4 --- -# Create keyed object from array +# Crea un objeto a partir de un array -Let's say we received an array of users in the form `{id:..., name:..., age:... }`. +Supongamos que recibimos un array de usuarios con la forma `{id:..., name:..., age:... }`. -Create a function `groupById(arr)` that creates an object from it, with `id` as the key, and array items as values. +Crea una función `groupById(arr)` que cree un objeto, con `id` como clave (key) y los elementos del array como valores. -For example: +Por ejemplo: ```js let users = [ @@ -20,7 +20,7 @@ let users = [ let usersById = groupById(users); /* -// after the call we should have: +// después de llamar a la función deberíamos tener: usersById = { john: {id: 'john', name: "John Smith", age: 20}, @@ -30,8 +30,8 @@ usersById = { */ ``` -Such function is really handy when working with server data. +Dicha función es realmente útil cuando trabajamos con información del servidor. -In this task we assume that `id` is unique. There may be no two array items with the same `id`. +Para esta actividad asumimos que cada `id` es único. No existen dos elementos del array con el mismo `id`. -Please use array `.reduce` method in the solution. +Usa el método de array `.reduce` en la solución. diff --git a/1-js/05-data-types/05-array-methods/2-filter-range/_js.view/solution.js b/1-js/05-data-types/05-array-methods/2-filter-range/_js.view/solution.js index 0bdfbae5a..05f81afaf 100644 --- a/1-js/05-data-types/05-array-methods/2-filter-range/_js.view/solution.js +++ b/1-js/05-data-types/05-array-methods/2-filter-range/_js.view/solution.js @@ -1,5 +1,5 @@ function filterRange(arr, a, b) { - // added brackets around the expression for better readability + // agregamos paréntesis en torno a la expresión para mayor legibilidad return arr.filter(item => (a <= item && item <= b)); } \ No newline at end of file diff --git a/1-js/05-data-types/05-array-methods/2-filter-range/solution.md b/1-js/05-data-types/05-array-methods/2-filter-range/solution.md index 73993a07a..e8b70be6c 100644 --- a/1-js/05-data-types/05-array-methods/2-filter-range/solution.md +++ b/1-js/05-data-types/05-array-methods/2-filter-range/solution.md @@ -1,6 +1,6 @@ ```js run demo function filterRange(arr, a, b) { - // added brackets around the expression for better readability + // agregamos paréntesis en torno a la expresión para mayor legibilidad return arr.filter(item => (a <= item && item <= b)); } @@ -8,7 +8,7 @@ let arr = [5, 3, 8, 1]; let filtered = filterRange(arr, 1, 4); -alert( filtered ); // 3,1 (matching values) +alert( filtered ); // 3,1 (valores dentro del rango) -alert( arr ); // 5,3,8,1 (not modified) +alert( arr ); // 5,3,8,1 (array original no modificado) ``` diff --git a/1-js/05-data-types/05-array-methods/2-filter-range/task.md b/1-js/05-data-types/05-array-methods/2-filter-range/task.md index 46e47c93d..8acd45e2c 100644 --- a/1-js/05-data-types/05-array-methods/2-filter-range/task.md +++ b/1-js/05-data-types/05-array-methods/2-filter-range/task.md @@ -2,21 +2,21 @@ importance: 4 --- -# Filter range +# Filtrar un rango -Write a function `filterRange(arr, a, b)` that gets an array `arr`, looks for elements with values higher or equal to `a` and lower or equal to `b` and return a result as an array. +Escribe una función `filterRange(arr, a, b)` que obtenga un array `arr`, busque los elementos con valor mayor o igual a `a` y menor o igual a `b` y devuelva un array con los resultados. -The function should not modify the array. It should return the new array. +La función no debe modificar el array. Debe devolver un nuevo array. -For instance: +Por ejemplo: ```js let arr = [5, 3, 8, 1]; let filtered = filterRange(arr, 1, 4); -alert( filtered ); // 3,1 (matching values) +alert( filtered ); // 3,1 (valores dentro del rango) -alert( arr ); // 5,3,8,1 (not modified) +alert( arr ); // 5,3,8,1 (array original no modificado) ``` diff --git a/1-js/05-data-types/05-array-methods/3-filter-range-in-place/_js.view/solution.js b/1-js/05-data-types/05-array-methods/3-filter-range-in-place/_js.view/solution.js index 488db3755..f21e5be67 100644 --- a/1-js/05-data-types/05-array-methods/3-filter-range-in-place/_js.view/solution.js +++ b/1-js/05-data-types/05-array-methods/3-filter-range-in-place/_js.view/solution.js @@ -4,7 +4,7 @@ function filterRangeInPlace(arr, a, b) { for (let i = 0; i < arr.length; i++) { let val = arr[i]; - // remove if outside of the interval + // remueve aquellos elementos que se encuentran fuera del intervalo if (val < a || val > b) { arr.splice(i, 1); i--; diff --git a/1-js/05-data-types/05-array-methods/3-filter-range-in-place/solution.md b/1-js/05-data-types/05-array-methods/3-filter-range-in-place/solution.md index 36e3130ff..a18159879 100644 --- a/1-js/05-data-types/05-array-methods/3-filter-range-in-place/solution.md +++ b/1-js/05-data-types/05-array-methods/3-filter-range-in-place/solution.md @@ -4,7 +4,7 @@ function filterRangeInPlace(arr, a, b) { for (let i = 0; i < arr.length; i++) { let val = arr[i]; - // remove if outside of the interval + // remueve aquellos elementos que se encuentran fuera del intervalo if (val < a || val > b) { arr.splice(i, 1); i--; @@ -15,7 +15,7 @@ function filterRangeInPlace(arr, a, b) { let arr = [5, 3, 8, 1]; -filterRangeInPlace(arr, 1, 4); // removed the numbers except from 1 to 4 +filterRangeInPlace(arr, 1, 4); // remueve los números excepto aquellos entre 1 y 4 alert( arr ); // [3, 1] ``` diff --git a/1-js/05-data-types/05-array-methods/3-filter-range-in-place/task.md b/1-js/05-data-types/05-array-methods/3-filter-range-in-place/task.md index 7066a51ab..b131b0952 100644 --- a/1-js/05-data-types/05-array-methods/3-filter-range-in-place/task.md +++ b/1-js/05-data-types/05-array-methods/3-filter-range-in-place/task.md @@ -2,17 +2,17 @@ importance: 4 --- -# Filter range "in place" +# Filtrar rango "en el lugar" -Write a function `filterRangeInPlace(arr, a, b)` that gets an array `arr` and removes from it all values except those that are between `a` and `b`. The test is: `a ≤ arr[i] ≤ b`. +Escribe una función `filterRangeInPlace(arr, a, b)` que obtenga un array `arr` y remueva del mismo todos los valores excepto aquellos que se encuentran entre `a` y `b`. El test es: `a ≤ arr[i] ≤ b`. -The function should only modify the array. It should not return anything. +La función solo debe modificar el array. No debe devolver nada. -For instance: +Por ejemplo: ```js let arr = [5, 3, 8, 1]; -filterRangeInPlace(arr, 1, 4); // removed the numbers except from 1 to 4 +filterRangeInPlace(arr, 1, 4); // remueve los números excepto aquellos entre 1 y 4 alert( arr ); // [3, 1] ``` diff --git a/1-js/05-data-types/05-array-methods/4-sort-back/task.md b/1-js/05-data-types/05-array-methods/4-sort-back/task.md index 0e3eeab76..d09e47b2b 100644 --- a/1-js/05-data-types/05-array-methods/4-sort-back/task.md +++ b/1-js/05-data-types/05-array-methods/4-sort-back/task.md @@ -2,12 +2,12 @@ importance: 4 --- -# Sort in decreasing order +# Ordenar en orden decreciente ```js let arr = [5, 2, 1, -10, 8]; -// ... your code to sort it in decreasing order +// ... tu código para ordenar en orden decreciente alert( arr ); // 8, 5, 2, 1, -10 ``` diff --git a/1-js/05-data-types/05-array-methods/5-copy-sort-array/solution.md b/1-js/05-data-types/05-array-methods/5-copy-sort-array/solution.md index 8537b129e..3c09c956a 100644 --- a/1-js/05-data-types/05-array-methods/5-copy-sort-array/solution.md +++ b/1-js/05-data-types/05-array-methods/5-copy-sort-array/solution.md @@ -1,4 +1,4 @@ -We can use `slice()` to make a copy and run the sort on it: +Podemos usar `slice()` para crear una copia y realizar el ordenamiento en ella: ```js run function copySorted(arr) { diff --git a/1-js/05-data-types/05-array-methods/5-copy-sort-array/task.md b/1-js/05-data-types/05-array-methods/5-copy-sort-array/task.md index c1395b4ad..14e0a8c13 100644 --- a/1-js/05-data-types/05-array-methods/5-copy-sort-array/task.md +++ b/1-js/05-data-types/05-array-methods/5-copy-sort-array/task.md @@ -2,11 +2,11 @@ importance: 5 --- -# Copy and sort array +# Copia y ordena un array -We have an array of strings `arr`. We'd like to have a sorted copy of it, but keep `arr` unmodified. +Supongamos que tenemos un array `arr`. Nos gustaría tener una copia ordenada del mismo, pero mantener `arr` sin modificar. -Create a function `copySorted(arr)` that returns such a copy. +Crea una función `copySorted(arr)` que devuelva esa copia. ```js let arr = ["HTML", "JavaScript", "CSS"]; @@ -14,5 +14,5 @@ let arr = ["HTML", "JavaScript", "CSS"]; let sorted = copySorted(arr); alert( sorted ); // CSS, HTML, JavaScript -alert( arr ); // HTML, JavaScript, CSS (no changes) +alert( arr ); // HTML, JavaScript, CSS (sin cambios) ``` diff --git a/1-js/05-data-types/05-array-methods/6-array-get-names/task.md b/1-js/05-data-types/05-array-methods/6-array-get-names/task.md index 74c8a9d74..6ed10e946 100644 --- a/1-js/05-data-types/05-array-methods/6-array-get-names/task.md +++ b/1-js/05-data-types/05-array-methods/6-array-get-names/task.md @@ -2,11 +2,11 @@ importance: 5 --- -# Map to names +# Mapa a nombres -You have an array of `user` objects, each one has `user.name`. Write the code that converts it into an array of names. +Tienes un array de objetos `user`, cada uno tiene `user.name`. Escribe el código que lo convierta en un array de nombres. -For instance: +Por ejemplo: ```js no-beautify let john = { name: "John", age: 25 }; @@ -15,7 +15,7 @@ let mary = { name: "Mary", age: 28 }; let users = [ john, pete, mary ]; -let names = /* ... your code */ +let names = /* ... tu código */ alert( names ); // John, Pete, Mary ``` diff --git a/1-js/05-data-types/05-array-methods/6-calculator-extendable/solution.md b/1-js/05-data-types/05-array-methods/6-calculator-extendable/solution.md index ebe0714cf..e44346749 100644 --- a/1-js/05-data-types/05-array-methods/6-calculator-extendable/solution.md +++ b/1-js/05-data-types/05-array-methods/6-calculator-extendable/solution.md @@ -1,3 +1,3 @@ -- Please note how methods are stored. They are simply added to `this.methods` property. -- All tests and numeric conversions are done in the `calculate` method. In future it may be extended to support more complex expressions. +- Por favor ten en cuenta cómo son almacenados los métodos. Simplemente son agregados a la propiedad `this.methods`. +- Todos los test y conversiones son hechas con el método `calculate`. En el futuro puede ser extendido para soportar expresiones más complejas. diff --git a/1-js/05-data-types/05-array-methods/6-calculator-extendable/task.md b/1-js/05-data-types/05-array-methods/6-calculator-extendable/task.md index e0d302f4c..a272c4d40 100644 --- a/1-js/05-data-types/05-array-methods/6-calculator-extendable/task.md +++ b/1-js/05-data-types/05-array-methods/6-calculator-extendable/task.md @@ -2,24 +2,24 @@ importance: 5 --- -# Create an extendable calculator +# Crea una calculadora extensible -Create a constructor function `Calculator` that creates "extendable" calculator objects. +Crea una función `Calculator` que cree objetos calculadores "extensibles". -The task consists of two parts. +La actividad consiste de dos partes. -1. First, implement the method `calculate(str)` that takes a string like `"1 + 2"` in the format "NUMBER operator NUMBER" (space-delimited) and returns the result. Should understand plus `+` and minus `-`. +1. Primero, implementar el método `calculate(str)` que toma un string como `"1 + 2"` en el formato "NUMERO operador NUMERO" (delimitado por espacios) y devuelve el resultado. Debe entender más `+` y menos `-`. - Usage example: + Ejemplo de uso: ```js let calc = new Calculator; alert( calc.calculate("3 + 7") ); // 10 ``` -2. Then add the method `addMethod(name, func)` that teaches the calculator a new operation. It takes the operator `name` and the two-argument function `func(a,b)` that implements it. +2. Luego agrega el método `addMethod(name, func)` que enseñe a la calculadora una nueva operación. Toma el operador `name` y la función con dos argumentos `func(a,b)` que lo implementa. - For instance, let's add the multiplication `*`, division `/` and power `**`: + Por ejemplo, vamos a agregar la multiplicación `*`, division `/` y potencia `**`: ```js let powerCalc = new Calculator; @@ -31,6 +31,6 @@ The task consists of two parts. alert( result ); // 8 ``` -- No parentheses or complex expressions in this task. -- The numbers and the operator are delimited with exactly one space. -- There may be error handling if you'd like to add it. +- Sin paréntesis ni expresiones complejas en esta tarea. +- Los números y el operador deben estar delimitados por exactamente un espacio. +- Puede haber manejo de errores si quisieras agregarlo. diff --git a/1-js/05-data-types/05-array-methods/7-map-objects/solution.md b/1-js/05-data-types/05-array-methods/7-map-objects/solution.md index 2d8d4fb0e..5c98a0b7c 100644 --- a/1-js/05-data-types/05-array-methods/7-map-objects/solution.md +++ b/1-js/05-data-types/05-array-methods/7-map-objects/solution.md @@ -25,9 +25,9 @@ alert( usersMapped[0].id ); // 1 alert( usersMapped[0].fullName ); // John Smith ``` -Please note that in the arrow functions we need to use additional brackets. +Ten en cuenta que para las funciones arrow necesitamos usar paréntesis adicionales. -We can't write like this: +No podemos escribirlo de la siguiente manera: ```js let usersMapped = users.map(user => *!*{*/!* fullName: `${user.name} ${user.surname}`, @@ -35,9 +35,9 @@ let usersMapped = users.map(user => *!*{*/!* }); ``` -As we remember, there are two arrow functions: without body `value => expr` and with body `value => {...}`. +Como recordarás, existen dos funciones arrow: sin cuerpo `value => expr` y con cuerpo `value => {...}`. -Here JavaScript would treat `{` as the start of function body, not the start of the object. The workaround is to wrap them in the "normal" brackets: +Acá JavaScript tratará `{` como el inicio de cuerpo de la función, no el inicio del objeto. La manera de resolver esto es encerrarlo dentro de paréntesis: ```js let usersMapped = users.map(user => *!*({*/!* @@ -46,6 +46,4 @@ let usersMapped = users.map(user => *!*({*/!* })); ``` -Now fine. - - +Ahora funciona. diff --git a/1-js/05-data-types/05-array-methods/7-map-objects/task.md b/1-js/05-data-types/05-array-methods/7-map-objects/task.md index b11d12155..a66e98e2b 100644 --- a/1-js/05-data-types/05-array-methods/7-map-objects/task.md +++ b/1-js/05-data-types/05-array-methods/7-map-objects/task.md @@ -2,13 +2,13 @@ importance: 5 --- -# Map to objects +# Mapa a objetos -You have an array of `user` objects, each one has `name`, `surname` and `id`. +Tienes un array de objetos `user`, cada uno tiene `name`, `surname` e `id`. -Write the code to create another array from it, of objects with `id` and `fullName`, where `fullName` is generated from `name` and `surname`. +Escribe el código para crear otro array a partir de este, de objetos con `id` y `fullName`, donde `fullName` es generado a partir de `name` y `surname`. -For instance: +Por ejemplo: ```js no-beautify let john = { name: "John", surname: "Smith", id: 1 }; @@ -18,7 +18,7 @@ let mary = { name: "Mary", surname: "Key", id: 3 }; let users = [ john, pete, mary ]; *!* -let usersMapped = /* ... your code ... */ +let usersMapped = /* ... tu código ... */ */!* /* @@ -33,4 +33,4 @@ alert( usersMapped[0].id ) // 1 alert( usersMapped[0].fullName ) // John Smith ``` -So, actually you need to map one array of objects to another. Try using `=>` here. There's a small catch. \ No newline at end of file +Entonces, en realidad lo que necesitas es mapear un array de objetos a otro. Intenta usar `=>` en este caso. Hay un pequeño truco. \ No newline at end of file diff --git a/1-js/05-data-types/05-array-methods/8-sort-objects/solution.md b/1-js/05-data-types/05-array-methods/8-sort-objects/solution.md index cfaf9761a..2acc5314c 100644 --- a/1-js/05-data-types/05-array-methods/8-sort-objects/solution.md +++ b/1-js/05-data-types/05-array-methods/8-sort-objects/solution.md @@ -11,7 +11,7 @@ let arr = [ pete, john, mary ]; sortByAge(arr); -// now sorted is: [john, mary, pete] +// ahora ordenado es: [john, mary, pete] alert(arr[0].name); // John alert(arr[1].name); // Mary alert(arr[2].name); // Pete diff --git a/1-js/05-data-types/05-array-methods/8-sort-objects/task.md b/1-js/05-data-types/05-array-methods/8-sort-objects/task.md index 9a215c9f4..768eaafe5 100644 --- a/1-js/05-data-types/05-array-methods/8-sort-objects/task.md +++ b/1-js/05-data-types/05-array-methods/8-sort-objects/task.md @@ -2,11 +2,11 @@ importance: 5 --- -# Sort users by age +# Ordena usuarios por edad -Write the function `sortByAge(users)` that gets an array of objects with the `age` property and sorts them by `age`. +Escribe la función `sortByAge(users)` que cree un array de objetos con al propiedad `age` y los ordene según `age`. -For instance: +Por ejemplo: ```js no-beautify let john = { name: "John", age: 25 }; @@ -17,7 +17,7 @@ let arr = [ pete, john, mary ]; sortByAge(arr); -// now: [john, mary, pete] +// ahora: [john, mary, pete] alert(arr[0].name); // John alert(arr[1].name); // Mary alert(arr[2].name); // Pete diff --git a/1-js/05-data-types/05-array-methods/9-shuffle/solution.md b/1-js/05-data-types/05-array-methods/9-shuffle/solution.md index 6674c444f..11e6be1b5 100644 --- a/1-js/05-data-types/05-array-methods/9-shuffle/solution.md +++ b/1-js/05-data-types/05-array-methods/9-shuffle/solution.md @@ -1,4 +1,4 @@ -The simple solution could be: +Una solución simple podría ser: ```js run *!* @@ -12,18 +12,18 @@ shuffle(arr); alert(arr); ``` -That somewhat works, because `Math.random() - 0.5` is a random number that may be positive or negative, so the sorting function reorders elements randomly. +Eso funciona de alguna manera, porque `Math.random() - 0.5` es un número aleatorio que puede ser positivo o negativo, por lo tanto, la función de ordenamiento reordena los elementos de forma aleatoria. -But because the sorting function is not meant to be used this way, not all permutations have the same probability. +Pero debido a que la función de ordenamiento no está hecha para ser usada de esta manera, no todas las permutaciones tienen la misma probabilidad. -For instance, consider the code below. It runs `shuffle` 1000000 times and counts appearances of all possible results: +Por ejemplo, consideremos el código siguiente. Ejecuta `shuffle` 1000000 veces y cuenta las apariciones de todos los resultados posibles: ```js run function shuffle(array) { array.sort(() => Math.random() - 0.5); } -// counts of appearances for all possible permutations +// cuenta las apariciones para todas las permutaciones posibles let count = { '123': 0, '132': 0, @@ -39,13 +39,13 @@ for (let i = 0; i < 1000000; i++) { count[array.join('')]++; } -// show counts of all possible permutations +// muestra conteo de todas las permutaciones posibles for (let key in count) { alert(`${key}: ${count[key]}`); } ``` -An example result (depends on JS engine): +Un resultado de ejemplo (depende del motor JS): ```js 123: 250706 @@ -56,30 +56,30 @@ An example result (depends on JS engine): 321: 125223 ``` -We can see the bias clearly: `123` and `213` appear much more often than others. +Podemos ver una clara tendencia: `123` y `213` aparecen mucho más seguido que otros. -The result of the code may vary between JavaScript engines, but we can already see that the approach is unreliable. +El resultado del código puede variar entre distintos motores JavaScript, pero ya podemos ver que esta forma de abordar el problema es poco confiable. -Why it doesn't work? Generally speaking, `sort` is a "black box": we throw an array and a comparison function into it and expect the array to be sorted. But due to the utter randomness of the comparison the black box goes mad, and how exactly it goes mad depends on the concrete implementation that differs between engines. +¿Por qué no funciona? Generalmente hablando, `sort` es una "caja negra": tiramos dentro un array y una función de ordenamiento y esperamos que el array se ordene. Pero debido a la total aleatoriedad de la comparación, la caja negra se vuelve loca y exactamente en que sentido se vuelve loca depende de la implementación específica, que difiere de un motor a otro. -There are other good ways to do the task. For instance, there's a great algorithm called [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle). The idea is to walk the array in the reverse order and swap each element with a random one before it: +Existen otra formas mejores de realizar la tarea. Por ejemplo, hay un excelente algoritmo llamado [Algoritmo de Fisher-Yates](https://es.wikipedia.org/wiki/Algoritmo_de_Fisher-Yates). La idea es recorrer el array en sentido inverso e intercambiar cada elemento con un elemento aleatorio anterior: ```js function shuffle(array) { for (let i = array.length - 1; i > 0; i--) { - let j = Math.floor(Math.random() * (i + 1)); // random index from 0 to i + let j = Math.floor(Math.random() * (i + 1)); // índice aleatorio entre 0 e i - // swap elements array[i] and array[j] - // we use "destructuring assignment" syntax to achieve that - // you'll find more details about that syntax in later chapters - // same can be written as: + // intercambia elementos array[i] y array[j] + // usamos la sintaxis "asignación de desestructuración" para lograr eso + // encontrarás más información acerca de esa sintaxis en los capítulos siguientes + // lo mismo puede ser escrito como: // let t = array[i]; array[i] = array[j]; array[j] = t [array[i], array[j]] = [array[j], array[i]]; } } ``` -Let's test it the same way: +Probémoslo de la misma manera: ```js run function shuffle(array) { @@ -89,7 +89,7 @@ function shuffle(array) { } } -// counts of appearances for all possible permutations +// conteo de apariciones para todas las permutaciones posibles let count = { '123': 0, '132': 0, @@ -105,13 +105,13 @@ for (let i = 0; i < 1000000; i++) { count[array.join('')]++; } -// show counts of all possible permutations +// muestra el conteo para todas las permutaciones posibles for (let key in count) { alert(`${key}: ${count[key]}`); } ``` -The example output: +La salida del ejemplo: ```js 123: 166693 @@ -122,6 +122,6 @@ The example output: 321: 166316 ``` -Looks good now: all permutations appear with the same probability. +Ahora sí se ve bien: todas las permutaciones aparecen con la misma probabilidad. -Also, performance-wise the Fisher-Yates algorithm is much better, there's no "sorting" overhead. +Además, en cuanto al rendimiento el algoritmo de Fisher-Yates es mucho mejor, no hay "ordenamiento" superpuesto. diff --git a/1-js/05-data-types/05-array-methods/9-shuffle/task.md b/1-js/05-data-types/05-array-methods/9-shuffle/task.md index 970c53417..5abdb2557 100644 --- a/1-js/05-data-types/05-array-methods/9-shuffle/task.md +++ b/1-js/05-data-types/05-array-methods/9-shuffle/task.md @@ -2,11 +2,11 @@ importance: 3 --- -# Shuffle an array +# Barajar un array -Write the function `shuffle(array)` that shuffles (randomly reorders) elements of the array. +Escribe la función `shuffle(array)` que baraje (reordene de forma aleatoria) los elementos del array. -Multiple runs of `shuffle` may lead to different orders of elements. For instance: +Múltiples ejecuciones de `shuffle` puede conducir a diferentes órdenes de elementos. Por ejemplo: ```js let arr = [1, 2, 3]; @@ -22,4 +22,4 @@ shuffle(arr); // ... ``` -All element orders should have an equal probability. For instance, `[1,2,3]` can be reordered as `[1,2,3]` or `[1,3,2]` or `[3,1,2]` etc, with equal probability of each case. +Todos los reordenamientos de elementos tienen que tener la misma probabilidad. Por ejemplo, `[1,2,3]` puede ser reordenado como `[1,2,3]` o `[1,3,2]` o `[3,1,2]` etc, con igual probabilidad en cada caso. diff --git a/1-js/05-data-types/05-array-methods/article.md b/1-js/05-data-types/05-array-methods/article.md index 853645958..f0891d95e 100644 --- a/1-js/05-data-types/05-array-methods/article.md +++ b/1-js/05-data-types/05-array-methods/article.md @@ -1,111 +1,111 @@ -# Array methods +# Métodos de arrays -Arrays provide a lot of methods. To make things easier, in this chapter, they are split into groups. +Los arrays (también llamados arreglos o matrices) cuentan con muchos métodos. Para hacer las cosas más sencillas, en este capítulo los separamos en grupos. -## Add/remove items +## Agregar/remover ítems -We already know methods that add and remove items from the beginning or the end: +Ya conocemos algunos métodos que agregan o extraen elementos del inicio o final de un array: -- `arr.push(...items)` -- adds items to the end, -- `arr.pop()` -- extracts an item from the end, -- `arr.shift()` -- extracts an item from the beginning, -- `arr.unshift(...items)` -- adds items to the beginning. +- `arr.push(...items)` -- agrega ítems al final, +- `arr.pop()` -- extrae un ítem del final, +- `arr.shift()` -- extrae un ítem del inicio, +- `arr.unshift(...items)` -- agrega ítems al principio. -Here are a few others. +Veamos algunos métodos más. ### splice -How to delete an element from the array? +¿Cómo podemos borrar un elemento de un array? -The arrays are objects, so we can try to use `delete`: +Los arrays son objetos, por lo que podemos intentar con `delete`: ```js run -let arr = ["I", "go", "home"]; +let arr = ["voy", "a", "casa"]; -delete arr[1]; // remove "go" +delete arr[1]; // remueve "a" alert( arr[1] ); // undefined -// now arr = ["I", , "home"]; +// ahora arr = ["voy", , "casa"]; alert( arr.length ); // 3 ``` -The element was removed, but the array still has 3 elements, we can see that `arr.length == 3`. +El elemento fue borrado, pero el array todavía tiene 3 elementos; podemos ver que `arr.length == 3`. -That's natural, because `delete obj.key` removes a value by the `key`. It's all it does. Fine for objects. But for arrays we usually want the rest of the elements to shift and occupy the freed place. We expect to have a shorter array now. +Es natural, porque `delete obj.key` borra el valor de `key`, pero es todo lo que hace. Esto está bien en los objetos, pero en general lo que buscamos en los arrays es que el resto de los elementos se desplace y se ocupe el lugar libre. Lo que esperamos es un array más corto. -So, special methods should be used. +Por lo tanto, necesitamos utilizar métodos especiales. -The [arr.splice](mdn:js/Array/splice) method is a Swiss army knife for arrays. It can do everything: insert, remove and replace elements. +El método [arr.splice](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/splice) funciona como una navaja suiza para arrays. Puede hacer todo: insertar, remover y remplazar elementos. -The syntax is: +La sintaxis es: ```js arr.splice(start[, deleteCount, elem1, ..., elemN]) ``` -It modifies `arr` starting from the index `start`: removes `deleteCount` elements and then inserts `elem1, ..., elemN` at their place. Returns the array of removed elements. +Esto modifica `arr` comenzando en el índice `start`: remueve la cantidad `deleteCount` de elementos y luego inserta `elem1, ..., elemN` en su lugar. Lo que devuelve es un array de los elementos removidos. -This method is easy to grasp by examples. +Este método es más fácil de entender con ejemplos. -Let's start with the deletion: +Empecemos removiendo elementos: ```js run -let arr = ["I", "study", "JavaScript"]; +let arr = ["Yo", "estudio", "JavaScript"]; *!* -arr.splice(1, 1); // from index 1 remove 1 element +arr.splice(1, 1); // desde el índice 1, remover 1 elemento */!* -alert( arr ); // ["I", "JavaScript"] +alert( arr ); // ["Yo", "JavaScript"] ``` -Easy, right? Starting from the index `1` it removed `1` element. +¿Fácil, no? Empezando desde el índice `1` removió `1` elemento. -In the next example, we remove 3 elements and replace them with the other two: +En el próximo ejemplo, removemos 3 elementos y los reemplazamos con otros 2: ```js run -let arr = [*!*"I", "study", "JavaScript",*/!* "right", "now"]; +let arr = [*!*"Yo", "estudio", "JavaScript",*/!* "ahora", "mismo"]; -// remove 3 first elements and replace them with another -arr.splice(0, 3, "Let's", "dance"); +// remueve los primeros 3 elementos y los reemplaza con otros +arr.splice(0, 3, "a", "bailar"); -alert( arr ) // now [*!*"Let's", "dance"*/!*, "right", "now"] +alert( arr ) // ahora [*!*"a", "bailar"*/!*, "ahora", "mismo"] ``` -Here we can see that `splice` returns the array of removed elements: +Aquí podemos ver que `splice` devuelve un array con los elementos removidos: ```js run -let arr = [*!*"I", "study",*/!* "JavaScript", "right", "now"]; +let arr = [*!*"Yo", "estudio",*/!* "JavaScript", "ahora", "mismo"]; -// remove 2 first elements +// remueve los 2 primeros elementos let removed = arr.splice(0, 2); -alert( removed ); // "I", "study" <-- array of removed elements +alert( removed ); // "Yo", "estudio" <-- array de los elementos removidos ``` -The `splice` method is also able to insert the elements without any removals. For that, we need to set `deleteCount` to `0`: +El método `splice` también es capaz de insertar elementos sin remover ningún otro. Para eso necesitamos establecer `deleteCount` en `0`: ```js run -let arr = ["I", "study", "JavaScript"]; +let arr = ["Yo", "estudio", "JavaScript"]; -// from index 2 -// delete 0 -// then insert "complex" and "language" -arr.splice(2, 0, "complex", "language"); +// desde el index 2 +// remover 0 +// después insertar "el", "complejo" y "language" +arr.splice(2, 0,"el", "complejo", "language"); -alert( arr ); // "I", "study", "complex", "language", "JavaScript" +alert( arr ); // "Yo", "estudio","el", "complejo", "language", "JavaScript" ``` -````smart header="Negative indexes allowed" -Here and in other array methods, negative indexes are allowed. They specify the position from the end of the array, like here: +````smart header="Los índices negativos están permitidos" +En este y en otros métodos de arrays, los índices negativos están permitidos. Estos índices indican la posición comenzando desde el final del array, de la siguiente manera: ```js run let arr = [1, 2, 5]; -// from index -1 (one step from the end) -// delete 0 elements, -// then insert 3 and 4 +// desde el index -1 (un lugar desde el final) +// remover 0 elementos, +// después insertar 3 y 4 arr.splice(-1, 0, 3, 4); alert( arr ); // 1,2,3,4,5 @@ -114,62 +114,62 @@ alert( arr ); // 1,2,3,4,5 ### slice -The method [arr.slice](mdn:js/Array/slice) is much simpler than the similar-looking `arr.splice`. +El método [arr.slice](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/slice) es mucho más simple que su similar `arr.splice`. -The syntax is: +La sintaxis es: ```js -arr.slice([start], [end]) +arr.slice([principio], [final]) ``` -It returns a new array copying to it all items from index `start` to `end` (not including `end`). Both `start` and `end` can be negative, in that case position from array end is assumed. +Devuelve un nuevo array copiando en el mismo todos los elementos desde `principio` hasta `final` (sin incluir `final`). `principio` y `final` pueden ser negativos, en cuyo caso se asume la posición desde el final del array. -It's similar to a string method `str.slice`, but instead of substrings, it makes subarrays. +Es similar al método para strings `str.slice`, pero en lugar de substrings, genera subarrays. -For instance: +Por ejemplo: ```js run let arr = ["t", "e", "s", "t"]; -alert( arr.slice(1, 3) ); // e,s (copy from 1 to 3) +alert( arr.slice(1, 3) ); // e,s (copia desde 1 hasta 3) -alert( arr.slice(-2) ); // s,t (copy from -2 till the end) +alert( arr.slice(-2) ); // s,t (copia desde -2 hasta el final) ``` -We can also call it without arguments: `arr.slice()` creates a copy of `arr`. That's often used to obtain a copy for further transformations that should not affect the original array. +También podemos invocarlo sin argumentos: `arr.slice()` crea una copia de `arr`. Se utiliza a menudo para obtener una copia que se puede transformar sin afectar el array original. ### concat -The method [arr.concat](mdn:js/Array/concat) creates a new array that includes values from other arrays and additional items. +El método [arr.concat](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/concat) crea un nuevo array que incluye los valores de otros arrays y elementos adicionales. -The syntax is: +La sintaxis es: ```js arr.concat(arg1, arg2...) ``` -It accepts any number of arguments -- either arrays or values. +Este acepta cualquier número de argumentos, tanto arrays como valores. -The result is a new array containing items from `arr`, then `arg1`, `arg2` etc. +El resultado es un nuevo array conteniendo los elementos de `arr`, después `arg1`, `arg2` etc. -If an argument `argN` is an array, then all its elements are copied. Otherwise, the argument itself is copied. +Si un argumento `argN` es un array, entonces todos sus elementos son copiados. De otro modo el argumento en sí es copiado. -For instance: +Por ejemplo: ```js run let arr = [1, 2]; -// create an array from: arr and [3,4] +// crea un array a partir de: arr y [3,4] alert( arr.concat([3, 4]) ); // 1,2,3,4 -// create an array from: arr and [3,4] and [5,6] +// crea un array a partir de: arr y [3,4] y [5,6] alert( arr.concat([3, 4], [5, 6]) ); // 1,2,3,4,5,6 -// create an array from: arr and [3,4], then add values 5 and 6 +// crea un array a partir de: arr y [3,4], luego agrega los valores 5 y 6 alert( arr.concat([3, 4], 5, 6) ); // 1,2,3,4,5,6 ``` -Normally, it only copies elements from arrays. Other objects, even if they look like arrays, are added as a whole: +Normalmente, solo copia elementos desde arrays. Otros objetos, incluso si parecen arrays, son agregados como un todo: ```js run let arr = [1, 2]; @@ -182,7 +182,7 @@ let arrayLike = { alert( arr.concat(arrayLike) ); // 1,2,[object Object] ``` -...But if an array-like object has a special `Symbol.isConcatSpreadable` property, then it's treated as an array by `concat`: its elements are added instead: +...Pero si un objeto similar a un array tiene la propiedad especial `Symbol.isConcatSpreadable`, entonces `concat` lo trata como un array y en lugar de añadirlo como un todo, solo añade sus elementos. ```js run let arr = [1, 2]; @@ -199,25 +199,25 @@ let arrayLike = { alert( arr.concat(arrayLike) ); // 1,2,something,else ``` -## Iterate: forEach +## Iteración: forEach -The [arr.forEach](mdn:js/Array/forEach) method allows to run a function for every element of the array. +El método [arr.forEach](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/forEach) permite ejecutar una función a cada elemento del array. -The syntax: +La sintaxis: ```js arr.forEach(function(item, index, array) { - // ... do something with an item + // ... hacer algo con un elemento }); ``` -For instance, this shows each element of the array: +Por ejemplo, el siguiente código muestra cada elemento del array: ```js run -// for each element call alert +// para cada elemento ejecuta alert ["Bilbo", "Gandalf", "Nazgul"].forEach(alert); ``` -And this code is more elaborate about their positions in the target array: +Y este caso más detallado da la posición del elemento en el array: ```js run ["Bilbo", "Gandalf", "Nazgul"].forEach((item, index, array) => { @@ -225,23 +225,23 @@ And this code is more elaborate about their positions in the target array: }); ``` -The result of the function (if it returns any) is thrown away and ignored. +El resultado de la función (si lo hay) se descarta y se ignora. -## Searching in array +## Buscar dentro de un array -Now let's cover methods that search in an array. +Ahora vamos a ver métodos que buscan elementos dentro de un array. -### indexOf/lastIndexOf and includes +### indexOf/lastIndexOf e includes -The methods [arr.indexOf](mdn:js/Array/indexOf) and [arr.includes](mdn:js/Array/includes) have the similar syntax and do essentially the same as their string counterparts, but operate on items instead of characters: +Los métodos [arr.indexOf](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/indexOf) y [arr.includes](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/includes) tienen una sintaxis similar y hacen básicamente lo mismo que sus contrapartes de strings, pero operan sobre elementos en lugar de caracteres: -- `arr.indexOf(item, from)` -- looks for `item` starting from index `from`, and returns the index where it was found, otherwise `-1`. -- `arr.includes(item, from)` -- looks for `item` starting from index `from`, returns `true` if found. +- `arr.indexOf(item, from)` -- busca `item` comenzando desde el index `from`, y devuelve el index donde fue encontrado, de otro modo devuelve `-1`. +- `arr.includes(item, from)` -- busca `item` comenzando desde el índice `from`, devuelve `true` en caso de ser encontrado. -Usually, these methods are used with only one argument: the `item` to search. By default, the search is from the beginning. +Usualmente estos métodos se usan con un solo argumento: el `item` a buscar. De manera predeterminada, la búsqueda es desde el principio. -For instance: +Por ejemplo: ```js run let arr = [1, 0, false]; @@ -253,75 +253,75 @@ alert( arr.indexOf(null) ); // -1 alert( arr.includes(1) ); // true ``` -Please note that `indexOf` uses the strict equality `===` for comparison. So, if we look for `false`, it finds exactly `false` and not the zero. +Tener en cuenta que el método usa la comparación estricta (`===`). Por lo tanto, si buscamos `false`, encontrará exactamente `false` y no cero. -If we want to check if `item` exists in the array and don't need the index, then `arr.includes` is preferred. +Si queremos comprobar si un elemento existe en el array, pero no necesitamos saber su ubicación exacta, es preferible usar `arr.includes` -The method [arr.lastIndexOf](mdn:js/Array/lastIndexOf) is the same as `indexOf`, but looks for from right to left. +El método [arr.lastIndexOf](mdn:js/Array/lastIndexOf) es lo mismo que `indexOf`, pero busca de derecha a izquierda. ```js run let fruits = ['Apple', 'Orange', 'Apple'] -alert( fruits.indexOf('Apple') ); // 0 (first Apple) -alert( fruits.lastIndexOf('Apple') ); // 2 (last Apple) +alert( fruits.indexOf('Apple') ); // 0 (primera "Apple") +alert( fruits.lastIndexOf('Apple') ); // 2 (última "Apple") ``` -````smart header="The `includes` method handles `NaN` correctly" -A minor, but noteworthy feature of `includes` is that it correctly handles `NaN`, unlike `indexOf`: +````smart header="El método `includes` maneja `NaN` correctamente" +Una característica menor pero notable de `includes` es que, a diferencia de `indexOf`, maneja correctamente `NaN`: ```js run const arr = [NaN]; -alert( arr.indexOf(NaN) ); // -1 (wrong, should be 0) -alert( arr.includes(NaN) );// true (correct) +alert( arr.indexOf(NaN) ); // -1 (debería ser 0, pero la igualdad === no funciona para NaN) +alert( arr.includes(NaN) );// true (correcto) ``` -That's because `includes` was added to JavaScript much later and uses the more up-to-date comparison algorithm internally. +Esto es porque `includes` fue agregado a JavaScript mucho después, su algoritmo interno de comparación está actualizado. ```` -### find and findIndex/findLastIndex +### find y findIndex/findLastIndex -Imagine we have an array of objects. How do we find an object with a specific condition? +Imaginemos que tenemos un array de objetos. ¿Cómo podríamos encontrar un objeto con una condición específica? -Here the [arr.find(fn)](mdn:js/Array/find) method comes in handy. +Para este tipo de casos es útil el método [arr.find(fn)](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/find) -The syntax is: +La sintaxis es: ```js let result = arr.find(function(item, index, array) { - // if true is returned, item is returned and iteration is stopped - // for falsy scenario returns undefined + // si true es devuelto aquí, find devuelve el ítem y la iteración se detiene + // para el caso en que sea false, devuelve undefined }); ``` -The function is called for elements of the array, one after another: +La función es llamada para cada elemento del array, uno después del otro: -- `item` is the element. -- `index` is its index. -- `array` is the array itself. +- `item` es el elemento. +- `index` es su índice. +- `array` es el array mismo. -If it returns `true`, the search is stopped, the `item` is returned. If nothing is found, `undefined` is returned. +Si devuelve `true`, la búsqueda se detiene y el `item` es devuelto. Si no encuentra nada, entonces devuelve `undefined`. -For example, we have an array of users, each with the fields `id` and `name`. Let's find the one with `id == 1`: +Por ejemplo, si tenemos un array de usuarios, cada uno con los campos `id` y `name`. Encontremos el elemento con `id == 1`: ```js run let users = [ - {id: 1, name: "John"}, - {id: 2, name: "Pete"}, - {id: 3, name: "Mary"} + {id: 1, name: "Celina"}, + {id: 2, name: "David"}, + {id: 3, name: "Federico"} ]; let user = users.find(item => item.id == 1); -alert(user.name); // John +alert(user.name); // Celina ``` -In real life, arrays of objects are a common thing, so the `find` method is very useful. +En la vida real, los arrays de objetos son bastante comunes, por lo que el método `find` resulta muy útil. -Note that in the example we provide to `find` the function `item => item.id == 1` with one argument. That's typical, other arguments of this function are rarely used. +Ten en cuenta que en el ejemplo anterior le pasamos a `find` la función `item => item.id == 1` con un argumento. Esto es lo más común, otros argumentos son raramente usados en esta función. -The [arr.findIndex](mdn:js/Array/findIndex) method has the same syntax but returns the index where the element was found instead of the element itself. The value of `-1` is returned if nothing is found. +El método [arr.findIndex](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/findIndex) tiene la misma sintaxis, pero devuelve el índice donde el elemento fue encontrado en lugar del elemento en sí. Devuelve `-1` cuando no lo encuentra. -The [arr.findLastIndex](mdn:js/Array/findLastIndex) method is like `findIndex`, but searches from right to left, similar to `lastIndexOf`. +El método [arr.findLastIndex](mdn:js/Array/findLastIndex) es como `findIndex`, pero busca de derecha a izquierda, similar a `lastIndexOf`. -Here's an example: +Un ejemplo: ```js run let users = [ @@ -331,62 +331,62 @@ let users = [ {id: 4, name: "John"} ]; -// Find the index of the first John +// Encontrar el índice del primer John alert(users.findIndex(user => user.name == 'John')); // 0 -// Find the index of the last John +// Encontrar el índice del último John alert(users.findLastIndex(user => user.name == 'John')); // 3 ``` ### filter -The `find` method looks for a single (first) element that makes the function return `true`. +El método `find` busca un único elemento (el primero) que haga a la función devolver `true`. -If there may be many, we can use [arr.filter(fn)](mdn:js/Array/filter). +Si existieran varios elementos que cumplen la condición, podemos usar [arr.filter(fn)](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/filter). -The syntax is similar to `find`, but `filter` returns an array of all matching elements: +La sintaxis es similar a `find`, pero `filter` devuelve un array con todos los elementos encontrados: ```js let results = arr.filter(function(item, index, array) { - // if true item is pushed to results and the iteration continues - // returns empty array if nothing found + // si devuelve true, el elemento es ingresado al array y la iteración continua + // si nada es encontrado, devuelve un array vacío }); ``` -For instance: +Por ejemplo: ```js run let users = [ - {id: 1, name: "John"}, - {id: 2, name: "Pete"}, - {id: 3, name: "Mary"} + {id: 1, name: "Celina"}, + {id: 2, name: "David"}, + {id: 3, name: "Federico"} ]; -// returns array of the first two users +// devuelve un array con los dos primeros usuarios let someUsers = users.filter(item => item.id < 3); alert(someUsers.length); // 2 ``` -## Transform an array +## Transformar un array -Let's move on to methods that transform and reorder an array. +Pasamos ahora a los métodos que transforman y reordenan un array. ### map -The [arr.map](mdn:js/Array/map) method is one of the most useful and often used. +El método [arr.map](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/map) es uno de los métodos más comunes y ampliamente usados. -It calls the function for each element of the array and returns the array of results. +Este método llama a la función para cada elemento del array y devuelve un array con los resultados. -The syntax is: +La sintaxis es: ```js let result = arr.map(function(item, index, array) { - // returns the new value instead of item + // devuelve el nuevo valor en lugar de item }); ``` -For instance, here we transform each element into its length: +Por ejemplo, acá transformamos cada elemento en el valor de su respectivo largo (length): ```js run let lengths = ["Bilbo", "Gandalf", "Nazgul"].map(item => item.length); @@ -395,42 +395,42 @@ alert(lengths); // 5,7,6 ### sort(fn) -The call to [arr.sort()](mdn:js/Array/sort) sorts the array *in place*, changing its element order. +Cuando usamos [arr.sort()](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/sort), este ordena el propio array cambiando el orden de los elementos. -It also returns the sorted array, but the returned value is usually ignored, as `arr` itself is modified. +También devuelve un nuevo array ordenado, pero este usualmente se descarta ya que `arr` en sí mismo es modificado. -For instance: +Por ejemplo: ```js run let arr = [ 1, 2, 15 ]; -// the method reorders the content of arr +// el método reordena el contenido de arr arr.sort(); alert( arr ); // *!*1, 15, 2*/!* ``` -Did you notice anything strange in the outcome? +¿Notas algo extraño en los valores de salida? -The order became `1, 15, 2`. Incorrect. But why? +Los elementos fueron reordenados a `1, 15, 2`. Pero ¿por qué pasa esto? -**The items are sorted as strings by default.** +**Los elementos son ordenados como strings (cadenas de caracteres) por defecto** -Literally, all elements are converted to strings for comparisons. For strings, lexicographic ordering is applied and indeed `"2" > "15"`. +Todos los elementos son literalmente convertidos a string para ser comparados. En el caso de strings se aplica el orden lexicográfico, por lo que efectivamente `"2" > "15"`. -To use our own sorting order, we need to supply a function as the argument of `arr.sort()`. +Para usar nuestro propio criterio de reordenamiento, necesitamos proporcionar una función como argumento de `arr.sort()`. -The function should compare two arbitrary values and return: +La función debe comparar dos valores arbitrarios, y devolver: ```js function compare(a, b) { - if (a > b) return 1; // if the first value is greater than the second - if (a == b) return 0; // if values are equal - if (a < b) return -1; // if the first value is less than the second + if (a > b) return 1; // si el primer valor es mayor que el segundo + if (a == b) return 0; // si ambos valores son iguales + if (a < b) return -1; // si el primer valor es menor que el segundo } ``` -For instance, to sort as numbers: +Por ejemplo, para ordenar como números: ```js run function compareNumeric(a, b) { @@ -448,13 +448,13 @@ arr.sort(compareNumeric); alert(arr); // *!*1, 2, 15*/!* ``` -Now it works as intended. +Ahora sí funciona como esperábamos. -Let's step aside and think about what's happening. The `arr` can be an array of anything, right? It may contain numbers or strings or objects or whatever. We have a set of *some items*. To sort it, we need an *ordering function* that knows how to compare its elements. The default is a string order. +Detengámonos a pensar, qué está pasando. El array `arr` puede ser un array de cualquier cosa, ¿verdad? Puede contener números, strings, objetos o lo que sea. Podemos decir que tenemos un conjunto de *ciertos items*. Para ordenarlos, necesitamos una *función de ordenamiento* que sepa cómo comparar los elementos. El orden predeterminado es hacerlo como strings. -The `arr.sort(fn)` method implements a generic sorting algorithm. We don't need to care how it internally works (an optimized [quicksort](https://en.wikipedia.org/wiki/Quicksort) or [Timsort](https://en.wikipedia.org/wiki/Timsort) most of the time). It will walk the array, compare its elements using the provided function and reorder them, all we need is to provide the `fn` which does the comparison. +El método `arr.sort(fn)` implementa un algoritmo genérico de orden. No necesitamos preocuparnos de cómo funciona internamente (la mayoría de las veces es una forma optimizada del algoritmo [quicksort](https://es.wikipedia.org/wiki/Quicksort) o [Timsort](https://en.wikipedia.org/wiki/Timsort)). Este método va a recorrer el array, comparar sus elementos usando la función dada y, finalmente, reordenarlos. Todo los que necesitamos hacer es proveer la `fn` que realiza la comparación. -By the way, if we ever want to know which elements are compared -- nothing prevents us from alerting them: +Por cierto, si queremos ver qué elementos están siendo comparados, nada nos impide mostrarlos en un alert(): ```js run [1, -2, 15, 2, 0, 8].sort(function(a, b) { @@ -463,12 +463,12 @@ By the way, if we ever want to know which elements are compared -- nothing preve }); ``` -The algorithm may compare an element with multiple others in the process, but it tries to make as few comparisons as possible. +El algoritmo puede comparar un elemento con muchos otros en el proceso, pero trata de hacer la menor cantidad de comparaciones posible. -````smart header="A comparison function may return any number" -Actually, a comparison function is only required to return a positive number to say "greater" and a negative number to say "less". +````smart header="Una función de comparación puede devolver cualquier número" +En realidad, una función de comparación solo es requerida para devolver un número positivo para "mayor" y uno negativo para "menor". -That allows to write shorter functions: +Esto nos permite escribir una función más corta: ```js run let arr = [ 1, 2, 15 ]; @@ -479,37 +479,37 @@ alert(arr); // *!*1, 2, 15*/!* ``` ```` -````smart header="Arrow functions for the best" -Remember [arrow functions](info:arrow-functions-basics)? We can use them here for neater sorting: +````smart header="Mejor, con funciones de flecha" +¿Recuerdas las [arrow functions](info:arrow-functions-basics)? Podemos usarlas en este caso para un ordenamiento más prolijo: ```js arr.sort( (a, b) => a - b ); ``` -This works exactly the same as the longer version above. +Esto funciona exactamente igual que la versión más larga de arriba. ```` -````smart header="Use `localeCompare` for strings" -Remember [strings](info:string#correct-comparisons) comparison algorithm? It compares letters by their codes by default. +````smart header="Usa `localeCompare` para strings" +¿Recuerdas el algoritmo de comparación [strings](info:string#correct-comparisons)? Este compara letras por su código por defecto. -For many alphabets, it's better to use `str.localeCompare` method to correctly sort letters, such as `Ö`. +Para muchos alfabetos, es mejor usar el método `str.localeCompare` para ordenar correctamente letras como por ejemplo `Ö`. -For example, let's sort a few countries in German: +Por ejemplo, vamos a ordenar algunos países en alemán: ```js run -let countries = ['Österreich', 'Andorra', 'Vietnam']; +let paises = ['Österreich', 'Andorra', 'Vietnam']; -alert( countries.sort( (a, b) => a > b ? 1 : -1) ); // Andorra, Vietnam, Österreich (wrong) +alert( paises.sort( (a, b) => a > b ? 1 : -1) ); // Andorra, Vietnam, Österreich (incorrecto) -alert( countries.sort( (a, b) => a.localeCompare(b) ) ); // Andorra,Österreich,Vietnam (correct!) +alert( paises.sort( (a, b) => a.localeCompare(b) ) ); // Andorra,Österreich,Vietnam (¡correcto!) ``` ```` ### reverse -The method [arr.reverse](mdn:js/Array/reverse) reverses the order of elements in `arr`. +El método [arr.reverse](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/reverse) revierte el orden de los elementos en `arr`. -For instance: +Por ejemplo: ```js run let arr = [1, 2, 3, 4, 5]; @@ -518,27 +518,27 @@ arr.reverse(); alert( arr ); // 5,4,3,2,1 ``` -It also returns the array `arr` after the reversal. +También devuelve el array `arr` después de revertir el orden. -### split and join +### split y join -Here's the situation from real life. We are writing a messaging app, and the person enters the comma-delimited list of receivers: `John, Pete, Mary`. But for us an array of names would be much more comfortable than a single string. How to get it? +Analicemos una situación de la vida real. Estamos programando una app de mensajería y y el usuario ingresa una lista de receptores delimitada por comas: `Celina, David, Federico`. Pero para nosotros un array sería mucho más práctico que una simple string. ¿Cómo podemos hacer para obtener un array? -The [str.split(delim)](mdn:js/String/split) method does exactly that. It splits the string into an array by the given delimiter `delim`. +El método [str.split(delim)](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/String/split) hace precisamente eso. Separa la string en elementos según el delimitante `delim` dado y los devuelve como un array. -In the example below, we split by a comma followed by a space: +En el ejemplo de abajo, separamos por "coma seguida de espacio": ```js run -let names = 'Bilbo, Gandalf, Nazgul'; +let nombres = 'Bilbo, Gandalf, Nazgul'; -let arr = names.split(', '); +let arr = nombres.split(', '); for (let name of arr) { - alert( `A message to ${name}.` ); // A message to Bilbo (and other names) + alert( `Un mensaje para ${name}.` ); // Un mensaje para Bilbo (y los otros nombres) } ``` -The `split` method has an optional second numeric argument -- a limit on the array length. If it is provided, then the extra elements are ignored. In practice it is rarely used though: +El método `split` tiene un segundo argumento numérico opcional: un límite en la extensión del array. Si se provee este argumento, entonces el resto de los elementos son ignorados. Sin embargo en la práctica rara vez se utiliza: ```js run let arr = 'Bilbo, Gandalf, Nazgul, Saruman'.split(', ', 2); @@ -546,8 +546,8 @@ let arr = 'Bilbo, Gandalf, Nazgul, Saruman'.split(', ', 2); alert(arr); // Bilbo, Gandalf ``` -````smart header="Split into letters" -The call to `split(s)` with an empty `s` would split the string into an array of letters: +````smart header="Separar en letras" +El llamado a `split(s)` con un `s` vacío separará el string en un array de letras: ```js run let str = "test"; @@ -556,27 +556,27 @@ alert( str.split('') ); // t,e,s,t ``` ```` -The call [arr.join(glue)](mdn:js/Array/join) does the reverse to `split`. It creates a string of `arr` items joined by `glue` between them. +[arr.join(glue)](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/join) hace lo opuesto a `split`. Crea una string de `arr` elementos unidos con `glue` (pegamento) entre ellos. -For instance: +Por ejemplo: ```js run let arr = ['Bilbo', 'Gandalf', 'Nazgul']; -let str = arr.join(';'); // glue the array into a string using ; +let str = arr.join(';'); // une el array en una string usando ; alert( str ); // Bilbo;Gandalf;Nazgul ``` ### reduce/reduceRight -When we need to iterate over an array -- we can use `forEach`, `for` or `for..of`. +Cuando necesitamos iterar sobre un array podemos usar `forEach`, `for` o `for..of`. -When we need to iterate and return the data for each element -- we can use `map`. +Cuando necesitamos iterar y devolver un valor por cada elemento podemos usar `map`. -The methods [arr.reduce](mdn:js/Array/reduce) and [arr.reduceRight](mdn:js/Array/reduceRight) also belong to that breed, but are a little bit more intricate. They are used to calculate a single value based on the array. +Los métodos [arr.reduce](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/reduce) y [arr.reduceRight](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/reduceRight) también pertenecen a ese grupo de acciones, pero son un poco más complejos. Se los utiliza para calcular un único valor a partir del array. -The syntax is: +La sintaxis es la siguiente: ```js let value = arr.reduce(function(accumulator, item, index, array) { @@ -584,24 +584,24 @@ let value = arr.reduce(function(accumulator, item, index, array) { }, [initial]); ``` -The function is applied to all array elements one after another and "carries on" its result to the next call. +La función es aplicada a todos los elementos del array, uno tras de otro, y va arrastrando el resultado parcial al próximo llamado. -Arguments: +Argumentos: -- `accumulator` -- is the result of the previous function call, equals `initial` the first time (if `initial` is provided). -- `item` -- is the current array item. -- `index` -- is its position. -- `array` -- is the array. +- `accumulator` -- es el resultado del llamado previo de la función, equivale a `initial` la primera vez (si `initial` es dado como argumento). +- `item` -- es el elemento actual del array. +- `index` -- es la posición. +- `array` -- es el array. -As the function is applied, the result of the previous function call is passed to the next one as the first argument. +Mientras la función sea llamada, el resultado del llamado anterior se pasa al siguiente como primer argumento. -So, the first argument is essentially the accumulator that stores the combined result of all previous executions. And at the end, it becomes the result of `reduce`. +Entonces, el primer argumento es un acumulador que almacena el resultado combinado de todas las ejecuciones previas, y al final se convierte en el resultado de `reduce`. -Sounds complicated? +¿Suena complicado? -The easiest way to grasp that is by example. +La forma más simple de entender algo es con un ejemplo. -Here we get a sum of an array in one line: +Acá tenemos la suma de un array en una línea: ```js run let arr = [1, 2, 3, 4, 5]; @@ -611,73 +611,73 @@ let result = arr.reduce((sum, current) => sum + current, 0); alert(result); // 15 ``` -The function passed to `reduce` uses only 2 arguments, that's typically enough. +La función pasada a `reduce` utiliza solo 2 argumentos, esto generalmente es suficiente. -Let's see the details of what's going on. +Veamos los detalles de lo que está pasando. -1. On the first run, `sum` is the `initial` value (the last argument of `reduce`), equals `0`, and `current` is the first array element, equals `1`. So the function result is `1`. -2. On the second run, `sum = 1`, we add the second array element (`2`) to it and return. -3. On the 3rd run, `sum = 3` and we add one more element to it, and so on... +1. En la primera pasada, `sum` es el valor `initial` (el último argumento de `reduce`), equivale a `0`, y `current` es el primer elemento de array, equivale a `1`. Entonces el resultado de la función es `1`. +2. En la segunda pasada, `sum = 1`, agregamos el segundo elemento del array (`2`) y devolvemos el valor. +3. En la tercera pasada, `sum = 3` y le agregamos un elemento más, y así sucesivamente... -The calculation flow: +El flujo de cálculos: ![](reduce.svg) -Or in the form of a table, where each row represents a function call on the next array element: +O en la forma de una tabla, donde cada fila representa un llamado a una función en el próximo elemento del array: | |`sum`|`current`|result| |---|-----|---------|---------| -|the first call|`0`|`1`|`1`| -|the second call|`1`|`2`|`3`| -|the third call|`3`|`3`|`6`| -|the fourth call|`6`|`4`|`10`| -|the fifth call|`10`|`5`|`15`| +|primer llamado|`0`|`1`|`1`| +|segundo llamado|`1`|`2`|`3`| +|tercer llamado|`3`|`3`|`6`| +|cuarto llamado|`6`|`4`|`10`| +|quinto llamado|`10`|`5`|`15`| -Here we can clearly see how the result of the previous call becomes the first argument of the next one. +Acá podemos ver claramente como el resultado del llamado anterior se convierte en el primer argumento del llamado siguiente. -We also can omit the initial value: +También podemos omitir el valor inicial: ```js run let arr = [1, 2, 3, 4, 5]; -// removed initial value from reduce (no 0) +// valor inicial removido (no 0) let result = arr.reduce((sum, current) => sum + current); alert( result ); // 15 ``` -The result is the same. That's because if there's no initial, then `reduce` takes the first element of the array as the initial value and starts the iteration from the 2nd element. +El resultado es el mismo. Esto es porque en el caso de no haber valor inicial, `reduce` toma el primer elemento del array como valor inicial y comienza la iteración a partir del segundo elemento. -The calculation table is the same as above, minus the first row. +La tabla de cálculos es igual a la anterior menos la primer fila. -But such use requires an extreme care. If the array is empty, then `reduce` call without initial value gives an error. +Pero este tipo de uso requiere tener extremo cuidado. Si el array está vacío, entonces el llamado a `reduce` sin valor inicial devuelve error. -Here's an example: +Acá vemos un ejemplo: ```js run let arr = []; -// Error: Reduce of empty array with no initial value -// if the initial value existed, reduce would return it for the empty arr. +// Error: Reduce en un array vacío sin valor inicial +// si el valor inicial existe, reduce lo devuelve en el arr vacío. arr.reduce((sum, current) => sum + current); ``` -So it's advised to always specify the initial value. +Por lo tanto siempre se recomienda especificar un valor inicial. -The method [arr.reduceRight](mdn:js/Array/reduceRight) does the same but goes from right to left. +El método [arr.reduceRight](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/reduceRight) realiza lo mismo, pero va de derecha a izquierda. ## Array.isArray -Arrays do not form a separate language type. They are based on objects. +Los arrays no conforman un tipo diferente. Están basados en objetos. -So `typeof` does not help to distinguish a plain object from an array: +Por eso `typeof` no ayuda a distinguir un objeto común de un array: ```js run alert(typeof {}); // object -alert(typeof []); // object (same) +alert(typeof []); // object (lo mismo) ``` -...But arrays are used so often that there's a special method for that: [Array.isArray(value)](mdn:js/Array/isArray). It returns `true` if the `value` is an array, and `false` otherwise. +...Pero los arrays son utilizados tan a menudo que tienen un método especial para eso: [Array.isArray(value)](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/isArray). Este devuelve `true` si el `valor` es un array y `false` si no lo es. ```js run alert(Array.isArray({})); // false @@ -685,25 +685,25 @@ alert(Array.isArray({})); // false alert(Array.isArray([])); // true ``` -## Most methods support "thisArg" +## La mayoría de los métodos aceptan "thisArg" -Almost all array methods that call functions -- like `find`, `filter`, `map`, with a notable exception of `sort`, accept an optional additional parameter `thisArg`. +Casi todos los métodos para arrays que realizan llamados a funciones -- como `find`, `filter`, `map`, con la notable excepción de `sort`-- aceptan un parámetro opcional adicional `thisArg`. -That parameter is not explained in the sections above, because it's rarely used. But for completeness, we have to cover it. +Ese parámetro no está explicado en la sección anterior porque es raramente usado. Pero para ser exhaustivos necesitamos verlo. -Here's the full syntax of these methods: +Esta es la sintaxis completa de estos métodos: ```js arr.find(func, thisArg); arr.filter(func, thisArg); arr.map(func, thisArg); // ... -// thisArg is the optional last argument +// thisArg es el último argumento opcional ``` -The value of `thisArg` parameter becomes `this` for `func`. +EL valor del parámetro `thisArg` se convierte en `this` para `func`. -For example, here we use a method of `army` object as a filter, and `thisArg` passes the context: +Por ejemplo, acá usamos un método del objeto `army` como un filtro y `thisArg` da el contexto: ```js run let army = { @@ -722,7 +722,7 @@ let users = [ ]; *!* -// find users, for who army.canJoin returns true +// encuentra usuarios para los cuales army.canJoin devuelve true let soldiers = users.filter(army.canJoin, army); */!* @@ -731,53 +731,54 @@ alert(soldiers[0].age); // 20 alert(soldiers[1].age); // 23 ``` -If in the example above we used `users.filter(army.canJoin)`, then `army.canJoin` would be called as a standalone function, with `this=undefined`, thus leading to an instant error. +Si en el ejemplo anterior usáramos `users.filter(army.canJoin)`, entonces `army.canJoin` sería llamada como una función independiente con `this=undefined`, lo que llevaría a un error inmediato. -A call to `users.filter(army.canJoin, army)` can be replaced with `users.filter(user => army.canJoin(user))`, that does the same. The latter is used more often, as it's a bit easier to understand for most people. +La llamada a `users.filter(army.canJoin, army)` puede ser reemplazada con `users.filter(user => army.canJoin(user))` que realiza lo mismo. Esta última se usa más a menudo ya que es un poco más fácil de entender. -## Summary +## Resumen -A cheat sheet of array methods: +Veamos el ayudamemoria de métodos para arrays: -- To add/remove elements: - - `push(...items)` -- adds items to the end, - - `pop()` -- extracts an item from the end, - - `shift()` -- extracts an item from the beginning, - - `unshift(...items)` -- adds items to the beginning. - - `splice(pos, deleteCount, ...items)` -- at index `pos` deletes `deleteCount` elements and inserts `items`. - - `slice(start, end)` -- creates a new array, copies elements from index `start` till `end` (not inclusive) into it. - - `concat(...items)` -- returns a new array: copies all members of the current one and adds `items` to it. If any of `items` is an array, then its elements are taken. +- Para agregar/remover elementos: + - `push(...items)` -- agrega ítems al final, + - `pop()` -- extrae un ítem del final, + - `shift()` -- extrae un ítem del inicio, + - `unshift(...items)` -- agrega ítems al inicio. + - `splice(pos, deleteCount, ...items)` -- desde el índice `pos` borra `deleteCount` elementos e inserta `items`. + - `slice(start, end)` -- crea un nuevo array y copia elementos desde la posición `start` hasta `end` (no incluido) en el nuevo array. + - `concat(...items)` -- devuelve un nuevo array: copia todos los elementos del array actual y le agrega `items`. Si alguno de los `items` es un array, se toman sus elementos. -- To search among elements: - - `indexOf/lastIndexOf(item, pos)` -- look for `item` starting from position `pos`, and return the index or `-1` if not found. - - `includes(value)` -- returns `true` if the array has `value`, otherwise `false`. - - `find/filter(func)` -- filter elements through the function, return first/all values that make it return `true`. - - `findIndex` is like `find`, but returns the index instead of a value. +- Para buscar entre elementos: + - `indexOf/lastIndexOf(item, pos)` -- busca por `item` comenzando desde la posición `pos` y devuelve su índice, o `-1` si no lo encuentra. + - `includes(value)` -- devuelve `true` si el array contiene `value`, o `false` en caso contrario. + - `find/filter(func)` -- filtra elementos a través de 'func', devuelve el primero/todos los valores que devolvieron `true`. + - `findIndex` es similar a `find`, pero devuelve el índice en lugar del valor. -- To iterate over elements: - - `forEach(func)` -- calls `func` for every element, does not return anything. +- Para iterar sobre elementos: + - `forEach(func)` -- llama la `func` para cada elemento, no devuelve nada. -- To transform the array: - - `map(func)` -- creates a new array from results of calling `func` for every element. - - `sort(func)` -- sorts the array in-place, then returns it. - - `reverse()` -- reverses the array in-place, then returns it. - - `split/join` -- convert a string to array and back. - - `reduce/reduceRight(func, initial)` -- calculate a single value over the array by calling `func` for each element and passing an intermediate result between the calls. +- Para transformar el array: + - `map(func)` -- crea un nuevo array a partir de los resultados de llamar a la `func` para cada elemento. + - `sort(func)` -- ordena el array y lo devuelve. + - `reverse()` -- ordena el array de forma inversa y lo devuelve. + - `split/join` -- convierte una cadena en un array y viceversa. + - `reduce/reduceRight(func, initial)` -- calcula un solo valor para todo el array, llamando a la `func` para cada elemento, obteniendo un resultado parcial en cada llamada y pasándolo a la siguiente. -- Additionally: - - `Array.isArray(value)` checks `value` for being an array, if so returns `true`, otherwise `false`. +- Adicional: + - `Array.isArray(value)` comprueba si `value` es un array. -Please note that methods `sort`, `reverse` and `splice` modify the array itself. +Por favor tener en cuenta que `sort`, `reverse` y `splice` modifican el propio array. -These methods are the most used ones, they cover 99% of use cases. But there are few others: +Estos métodos son los más utilizados y cubren el 99% de los casos. Pero existen algunos más: -- [arr.some(fn)](mdn:js/Array/some)/[arr.every(fn)](mdn:js/Array/every) check the array. +- [arr.some(fn)](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/some)/[arr.every(fn)](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/every) comprueba el array. - The function `fn` is called on each element of the array similar to `map`. If any/all results are `true`, returns `true`, otherwise `false`. + La función `fn` es llamada para cada elemento del array de manera similar a `map`. Si alguno/todos los resultados son `true`, devuelve `true`, si no, `false`. + + Estos métodos se comportan con similitud a los operadores `||` y `&&`: si `fn` devuelve un valor verdadero, `arr.some()` devuelve `true` y detiene la iteración de inmediato; si `fn` devuelve un valor falso, `arr.every()` devuelve `false` y detiene la iteración también. - These methods behave sort of like `||` and `&&` operators: if `fn` returns a truthy value, `arr.some()` immediately returns `true` and stops iterating over the rest of items; if `fn` returns a falsy value, `arr.every()` immediately returns `false` and stops iterating over the rest of items as well. +Podemos usar `every` para comparar arrays: - We can use `every` to compare arrays: ```js run function arraysEqual(arr1, arr2) { @@ -787,16 +788,16 @@ These methods are the most used ones, they cover 99% of use cases. But there are alert( arraysEqual([1, 2], [1, 2])); // true ``` -- [arr.fill(value, start, end)](mdn:js/Array/fill) -- fills the array with repeating `value` from index `start` to `end`. +- [arr.fill(value, start, end)](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/fill) -- llena el array repitiendo `value` desde el índice `start` hasta `end`. -- [arr.copyWithin(target, start, end)](mdn:js/Array/copyWithin) -- copies its elements from position `start` till position `end` into *itself*, at position `target` (overwrites existing). +- [arr.copyWithin(target, start, end)](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/copyWithin) -- copia sus elementos desde la posición `start` hasta la posición `end` en *si mismo*, a la posición `target` (reescribe lo existente). -- [arr.flat(depth)](mdn:js/Array/flat)/[arr.flatMap(fn)](mdn:js/Array/flatMap) create a new flat array from a multidimensional array. +- [arr.flat(depth)](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/flat)/[arr.flatMap(fn)](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/flatMap) crea un nuevo array plano desde un array multidimensional . -For the full list, see the [manual](mdn:js/Array). +Para la lista completa, ver [manual](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array). -At first sight, it may seem that there are so many methods, quite difficult to remember. But actually, that's much easier. +A primera vista puede parecer que hay demasiados métodos y difíciles de recordar. En realidad es mucho más fácil de lo que se ve. -Look through the cheat sheet just to be aware of them. Then solve the tasks of this chapter to practice, so that you have experience with array methods. +Revisa el ayudamemoria para conocerlos. Después realiza las prácticas de este capítulo para ganar experiencia con los métodos para arrays. -Afterwards whenever you need to do something with an array, and you don't know how -- come here, look at the cheat sheet and find the right method. Examples will help you to write it correctly. Soon you'll automatically remember the methods, without specific efforts from your side. +Finalmente si en algún momento necesitas hacer algo con un array y no sabes cómo, vuelve a esta página, mira el ayudamemoria y encuentra el método correcto. Los ejemplos te ayudarán a escribirlos correctamente y pronto los recordarás automáticamente y sin esfuerzo. diff --git a/1-js/05-data-types/06-iterable/article.md b/1-js/05-data-types/06-iterable/article.md index e2c0d4f97..c5de4c981 100644 --- a/1-js/05-data-types/06-iterable/article.md +++ b/1-js/05-data-types/06-iterable/article.md @@ -1,20 +1,20 @@ # Iterables -*Iterable* objects are a generalization of arrays. That's a concept that allows us to make any object useable in a `for..of` loop. +Los objetos *iterables* son una generalización de *arrays*. Es un concepto que permite que cualquier objeto pueda ser utilizado en un bucle `for..of`. -Of course, Arrays are iterable. But there are many other built-in objects, that are iterable as well. For instance, strings are also iterable. +Por supuesto, las matrices o *arrays* son iterables. Pero hay muchos otros objetos integrados que también lo son. Por ejemplo, las cadenas o *strings* son iterables también. Como veremos, muchos operadores y métodos se basan en la iterabilidad. -If an object isn't technically an array, but represents a collection (list, set) of something, then `for..of` is a great syntax to loop over it, so let's see how to make it work. +Si un objeto no es técnicamente una matriz, pero representa una colección (lista, conjunto) de algo, entonces el uso de la sintaxis `for..of` es una gran forma de recorrerlo. Veamos cómo funciona. ## Symbol.iterator -We can easily grasp the concept of iterables by making one of our own. +Podemos comprender fácilmente el concepto de iterables construyendo uno. -For instance, we have an object that is not an array, but looks suitable for `for..of`. +Por ejemplo: tenemos un objeto que no es un array, pero parece adecuado para `for..of`. -Like a `range` object that represents an interval of numbers: +Como un objeto `range` que representa un intervalo de números: ```js let range = { @@ -22,18 +22,18 @@ let range = { to: 5 }; -// We want the for..of to work: +// Queremos que el for..of funcione de la siguiente manera: // for(let num of range) ... num=1,2,3,4,5 ``` -To make the `range` object iterable (and thus let `for..of` work) we need to add a method to the object named `Symbol.iterator` (a special built-in symbol just for that). +Para hacer que el objeto `range` sea iterable (y así permitir que `for..of` funcione) necesitamos agregarle un método llamado `Symbol.iterator` (un símbolo incorporado especial usado solo para realizar esa función). -1. When `for..of` starts, it calls that method once (or errors if not found). The method must return an *iterator* -- an object with the method `next`. -2. Onward, `for..of` works *only with that returned object*. -3. When `for..of` wants the next value, it calls `next()` on that object. -4. The result of `next()` must have the form `{done: Boolean, value: any}`, where `done=true` means that the loop is finished, otherwise `value` is the next value. +1. Cuando se inicia `for..of`, éste llama al método `Symbol.iterator` una vez (o genera un error si no lo encuentra). El método debe devolver un *iterador* : un objeto con el método `next()`. +2. En adelante, `for..of` trabaja *solamente con ese objeto devuelto*. +3. Cuando `for..of` quiere el siguiente valor, llama a `next()` en ese objeto. +4. El resultado de `next()` debe tener la forma `{done: Boolean, value: any}`, donde `done=true` significa que el bucle ha finalizado; de lo contrario, el nuevo valor es `value`. -Here's the full implementation for `range` with remarks: +Aquí está la implementación completa de `range`: ```js run let range = { @@ -41,18 +41,18 @@ let range = { to: 5 }; -// 1. call to for..of initially calls this +// 1. Una llamada a for..of inicializa una llamada a esto: range[Symbol.iterator] = function() { - // ...it returns the iterator object: - // 2. Onward, for..of works only with the iterator object below, asking it for next values + // ... devuelve el objeto iterador: + // 2. En adelante, for..of trabaja solo con el objeto iterador debajo, pidiéndole los siguientes valores return { current: this.from, last: this.to, - // 3. next() is called on each iteration by the for..of loop + // 3. next() es llamado en cada iteración por el bucle for..of next() { - // 4. it should return the value as an object {done:.., value :...} + // 4. debe devolver el valor como un objeto {done:.., value :...} if (this.current <= this.last) { return { done: false, value: this.current++ }; } else { @@ -62,22 +62,22 @@ range[Symbol.iterator] = function() { }; }; -// now it works! +// ¡Ahora funciona! for (let num of range) { - alert(num); // 1, then 2, 3, 4, 5 + alert(num); // 1, luego 2, 3, 4, 5 } ``` -Please note the core feature of iterables: separation of concerns. +Note una característica fundamental de los iterables: separación de conceptos. -- The `range` itself does not have the `next()` method. -- Instead, another object, a so-called "iterator" is created by the call to `range[Symbol.iterator]()`, and its `next()` generates values for the iteration. +- El `range` en sí mismo no tiene el método `next()`. +- En cambio, la llamada a `range[Symbol.iterator]()` crea un otro objeto llamado "iterador", y su `next()` genera valores para la iteración. -So, the iterator object is separate from the object it iterates over. +Por lo tanto, el objeto iterador está separado del objeto sobre el que itera. -Technically, we may merge them and use `range` itself as the iterator to make the code simpler. +Técnicamente, podríamos fusionarlos y usar el `range` mismo como iterador para simplificar el código. -Like this: +De esta manera: ```js run let range = { @@ -99,55 +99,56 @@ let range = { }; for (let num of range) { - alert(num); // 1, then 2, 3, 4, 5 + alert(num); // 1, luego 2, 3, 4, 5 } ``` -Now `range[Symbol.iterator]()` returns the `range` object itself: it has the necessary `next()` method and remembers the current iteration progress in `this.current`. Shorter? Yes. And sometimes that's fine too. +Ahora `range[Symbol.iterator]()` devuelve el objeto `range` en sí: tiene el método `next()` necesario y recuerda el progreso de iteración actual en `this.current`. ¿Más corto? Sí. Y a veces eso también está bien. -The downside is that now it's impossible to have two `for..of` loops running over the object simultaneously: they'll share the iteration state, because there's only one iterator -- the object itself. But two parallel for-ofs is a rare thing, even in async scenarios. +La desventaja es que ahora es imposible tener dos bucles `for..of` corriendo sobre el objeto simultáneamente: compartirán el estado de iteración, porque solo hay un iterador: el objeto en sí. Pero dos for-of paralelos es algo raro, incluso en escenarios asíncronos. -```smart header="Infinite iterators" -Infinite iterators are also possible. For instance, the `range` becomes infinite for `range.to = Infinity`. Or we can make an iterable object that generates an infinite sequence of pseudorandom numbers. Also can be useful. +```smart header="Iteradores Infinitos" +También son posibles los iteradores infinitos. Por ejemplo, el objeto `range` se vuelve infinito así: `range.to = Infinity`. O podemos hacer un objeto iterable que genere una secuencia infinita de números pseudoaleatorios. También puede ser útil. -There are no limitations on `next`, it can return more and more values, that's normal. +No hay limitaciones en `next`, puede devolver más y más valores, eso es normal. -Of course, the `for..of` loop over such an iterable would be endless. But we can always stop it using `break`. +Por supuesto, el bucle `for..of` sobre un iterable de este tipo sería interminable. Pero siempre podemos detenerlo usando `break`. ``` -## String is iterable +## *String* es iterable -Arrays and strings are most widely used built-in iterables. +Las matrices y cadenas son los iterables integrados más utilizados. -For a string, `for..of` loops over its characters: +En una cadena o *string*, el bucle `for..of` recorre sus caracteres: ```js run for (let char of "test") { - // triggers 4 times: once for each character - alert( char ); // t, then e, then s, then t + // Se dispara 4 veces: una vez por cada carácter + alert( char ); // t, luego e, luego s, luego t } ``` -And it works correctly with surrogate pairs! +¡Y trabaja correctamente con valores de pares sustitutos (codificación UTF-16)! ```js run let str = '𝒳😂'; for (let char of str) { - alert( char ); // 𝒳, and then 😂 + alert( char ); // 𝒳, y luego 😂 } ``` -## Calling an iterator explicitly +## Llamar a un iterador explícitamente -For deeper understanding, let's see how to use an iterator explicitly. +Para una comprensión más profunda, veamos cómo usar un iterador explícitamente. + +Vamos a iterar sobre una cadena exactamente de la misma manera que `for..of`, pero con llamadas directas. Este código crea un iterador de cadena y obtiene valores de él "manualmente": -We'll iterate over a string in exactly the same way as `for..of`, but with direct calls. This code creates a string iterator and gets values from it "manually": ```js run -let str = "Hello"; +let str = "Hola"; -// does the same as +// hace lo mismo que // for (let char of str) alert(char); *!* @@ -157,97 +158,96 @@ let iterator = str[Symbol.iterator](); while (true) { let result = iterator.next(); if (result.done) break; - alert(result.value); // outputs characters one by one + alert(result.value); // retorna los caracteres uno por uno } ``` +Rara vez se necesita esto, pero nos da más control sobre el proceso que `for..of`. Por ejemplo, podemos dividir el proceso de iteración: iterar un poco, luego parar, hacer otra cosa y luego continuar. -That is rarely needed, but gives us more control over the process than `for..of`. For instance, we can split the iteration process: iterate a bit, then stop, do something else, and then resume later. - -## Iterables and array-likes [#array-like] +## Iterables y simil-array (array-like) [#array-like] -Two official terms look similar, but are very different. Please make sure you understand them well to avoid the confusion. +Los dos son términos oficiales que se parecen, pero son muy diferentes. Asegúrese de comprenderlos bien para evitar confusiones. -- *Iterables* are objects that implement the `Symbol.iterator` method, as described above. -- *Array-likes* are objects that have indexes and `length`, so they look like arrays. +- *Iterables* son objetos que implementan el método `Symbol.iterator`, como se describió anteriormente. +- *simil-array* son objetos que tienen índices y longitud o `length`, por lo que se "ven" como arrays. -When we use JavaScript for practical tasks in a browser or any other environment, we may meet objects that are iterables or array-likes, or both. +Cuando usamos JavaScript para tareas prácticas en el navegador u otros entornos, podemos encontrar objetos que son iterables o array-like, o ambos. -For instance, strings are both iterable (`for..of` works on them) and array-like (they have numeric indexes and `length`). +Por ejemplo, las cadenas son iterables (`for..of` funciona en ellas) y array-like (tienen índices numéricos y `length`). -But an iterable may not be array-like. And vice versa an array-like may not be iterable. +Pero un iterable puede que no sea array-like. Y viceversa, un array-like puede no ser iterable. -For example, the `range` in the example above is iterable, but not array-like, because it does not have indexed properties and `length`. +Por ejemplo, `range` en el ejemplo anterior es iterable, pero no es array-like porque no tiene propiedades indexadas ni `length`. -And here's the object that is array-like, but not iterable: +Y aquí el objeto tiene forma de matriz, pero no es iterable: ```js run -let arrayLike = { // has indexes and length => array-like - 0: "Hello", - 1: "World", +let arrayLike = { // tiene índices y longitud => array-like + 0: "Hola", + 1: "Mundo", length: 2 }; *!* -// Error (no Symbol.iterator) +// Error (sin Symbol.iterator) for (let item of arrayLike) {} */!* ``` -Both iterables and array-likes are usually *not arrays*, they don't have `push`, `pop` etc. That's rather inconvenient if we have such an object and want to work with it as with an array. E.g. we would like to work with `range` using array methods. How to achieve that? +Tanto los iterables como los array-like generalmente no son *arrays*, no tienen "push", "pop", etc. Eso es bastante inconveniente si tenemos un objeto de este tipo y queremos trabajar con él como con una matriz. P.ej. nos gustaría trabajar con `range` utilizando métodos de matriz. ¿Cómo lograr eso? ## Array.from -There's a universal method [Array.from](mdn:js/Array/from) that takes an iterable or array-like value and makes a "real" `Array` from it. Then we can call array methods on it. +Existe un método universal [Array.from](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/from) que toma un valor iterable o simil-array y crea un `Array` ¨real¨ a partir de él. De esta manera podemos llamar y usar métodos que pertenecen a una matriz. -For instance: +Por ejemplo: ```js run let arrayLike = { - 0: "Hello", - 1: "World", + 0: "Hola", + 1: "Mundo", length: 2 }; *!* let arr = Array.from(arrayLike); // (*) */!* -alert(arr.pop()); // World (method works) +alert(arr.pop()); // Mundo (el método pop funciona) ``` -`Array.from` at the line `(*)` takes the object, examines it for being an iterable or array-like, then makes a new array and copies all items to it. +`Array.from` en la línea `(*)` toma el objeto, y si es iterable o simil-array crea un nuevo array y copia allí todos los elementos. -The same happens for an iterable: +Lo mismo sucede para un iterable: ```js run -// assuming that range is taken from the example above +// suponiendo que range se toma del ejemplo anterior let arr = Array.from(range); -alert(arr); // 1,2,3,4,5 (array toString conversion works) +alert(arr); // 1,2,3,4,5 (la conversión de matriz a cadena funciona) ``` -The full syntax for `Array.from` also allows us to provide an optional "mapping" function: +La sintaxis completa para `Array.from` también nos permite proporcionar una función opcional de "mapeo": ```js Array.from(obj[, mapFn, thisArg]) ``` -The optional second argument `mapFn` can be a function that will be applied to each element before adding it to the array, and `thisArg` allows us to set `this` for it. +El segundo argumento opcional `mapFn` puede ser una función que se aplicará a cada elemento antes de agregarlo a la matriz, y `thisArg` permite establecer el `this` para ello. -For instance: +Por ejemplo: ```js run -// assuming that range is taken from the example above +// suponiendo que range se toma del ejemplo anterior -// square each number +// el cuadrado de cada número let arr = Array.from(range, num => num * num); alert(arr); // 1,4,9,16,25 ``` -Here we use `Array.from` to turn a string into an array of characters: +Aquí usamos `Array.from` para convertir una cadena en una matriz de caracteres: ```js run let str = '𝒳😂'; -// splits str into array of characters +// separa str en un array de caracteres let chars = Array.from(str); alert(chars[0]); // 𝒳 @@ -255,14 +255,14 @@ alert(chars[1]); // 😂 alert(chars.length); // 2 ``` -Unlike `str.split`, it relies on the iterable nature of the string and so, just like `for..of`, correctly works with surrogate pairs. +A diferencia de `str.split`, `Array.from` se basa en la naturaleza iterable de la cadena y, por lo tanto, al igual que `for..of`, funciona correctamente con pares sustitutos. -Technically here it does the same as: +Técnicamente aquí hace lo mismo que: ```js run let str = '𝒳😂'; -let chars = []; // Array.from internally does the same loop +let chars = []; // Array.from internamente hace el mismo bucle for (let char of str) { chars.push(char); } @@ -270,9 +270,9 @@ for (let char of str) { alert(chars); ``` -...But it is shorter. +... Pero es más corto. -We can even build surrogate-aware `slice` on it: +Incluso podemos construir un `segmento` o `slice` compatible con sustitutos en él: ```js run function slice(str, start, end) { @@ -283,25 +283,26 @@ let str = '𝒳😂𩷶'; alert( slice(str, 1, 3) ); // 😂𩷶 -// the native method does not support surrogate pairs -alert( str.slice(1, 3) ); // garbage (two pieces from different surrogate pairs) +// el método nativo no admite pares sustitutos +alert( str.slice(1, 3) ); // garbage (dos piezas de diferentes pares sustitutos) ``` -## Summary +## Resumen + +Los objetos que se pueden usar en `for..of` se denominan *iterables*. -Objects that can be used in `for..of` are called *iterable*. +- Técnicamente, los iterables deben implementar el método llamado `Symbol.iterator`. + - El resultado de `obj[Symbol.iterator]()` se llama *iterador*. Maneja el proceso de iteración adicional. + - Un iterador debe tener el método llamado `next()` que devuelve un objeto `{done: Boolean, value: any}`, donde `done: true` marca el fin de la iteración; de lo contrario, `value` es el siguiente valor. +- El método `Symbol.iterator` se llama automáticamente por `for..of`, pero también podemos llamarlo directamente. +- Los iterables integrados, como cadenas o matrices, también implementan `Symbol.iterator`. +- El iterador de cadena es capaz de manejar los pares sustitutos. -- Technically, iterables must implement the method named `Symbol.iterator`. - - The result of `obj[Symbol.iterator]()` is called an *iterator*. It handles further iteration process. - - An iterator must have the method named `next()` that returns an object `{done: Boolean, value: any}`, here `done:true` denotes the end of the iteration process, otherwise the `value` is the next value. -- The `Symbol.iterator` method is called automatically by `for..of`, but we also can do it directly. -- Built-in iterables like strings or arrays, also implement `Symbol.iterator`. -- String iterator knows about surrogate pairs. +Los objetos que tienen propiedades indexadas y `longitud` o *length* se llaman *array-like*. Dichos objetos también pueden tener otras propiedades y métodos, pero carecen de los métodos integrados de las matrices. -Objects that have indexed properties and `length` are called *array-like*. Such objects may also have other properties and methods, but lack the built-in methods of arrays. +Si miramos dentro de la especificación, veremos que la mayoría de los métodos incorporados suponen que funcionan con iterables o array-likes en lugar de matrices "reales", porque eso es más abstracto. -If we look inside the specification -- we'll see that most built-in methods assume that they work with iterables or array-likes instead of "real" arrays, because that's more abstract. +`Array.from (obj[, mapFn, thisArg])` crea un verdadero `Array` de un `obj` iterable o array-like, y luego podemos usar métodos de matriz en él. Los argumentos opcionales `mapFn` y `thisArg` nos permiten aplicar una función a cada elemento. -`Array.from(obj[, mapFn, thisArg])` makes a real `Array` from an iterable or array-like `obj`, and we can then use array methods on it. The optional arguments `mapFn` and `thisArg` allow us to apply a function to each item. diff --git a/1-js/05-data-types/07-map-set/01-array-unique-map/task.md b/1-js/05-data-types/07-map-set/01-array-unique-map/task.md index d68030032..b0688c6fc 100644 --- a/1-js/05-data-types/07-map-set/01-array-unique-map/task.md +++ b/1-js/05-data-types/07-map-set/01-array-unique-map/task.md @@ -2,26 +2,26 @@ importance: 5 --- -# Filter unique array members +# Filtrar miembros únicos del array -Let `arr` be an array. +Digamos que `arr` es un array. -Create a function `unique(arr)` that should return an array with unique items of `arr`. +Cree una función `unique(arr)` que debería devolver un array con elementos únicos de `arr`. -For instance: +Por ejemplo: ```js function unique(arr) { - /* your code */ + /* tu código */ } let values = ["Hare", "Krishna", "Hare", "Krishna", "Krishna", "Krishna", "Hare", "Hare", ":-O" ]; -alert( unique(values) ); // Hare, Krishna, :-O +alert( unique(values) ); // Hare, Krishna, :-O ``` -P.S. Here strings are used, but can be values of any type. +P.D. Aquí se usan strings, pero pueden ser valores de cualquier tipo. -P.P.S. Use `Set` to store unique values. +P.D.S. Use `Set` para almacenar valores únicos. diff --git a/1-js/05-data-types/07-map-set/02-filter-anagrams/solution.md b/1-js/05-data-types/07-map-set/02-filter-anagrams/solution.md index 160675185..8ed04cf55 100644 --- a/1-js/05-data-types/07-map-set/02-filter-anagrams/solution.md +++ b/1-js/05-data-types/07-map-set/02-filter-anagrams/solution.md @@ -1,6 +1,6 @@ -To find all anagrams, let's split every word to letters and sort them. When letter-sorted, all anagrams are same. +Para encontrar todos los anagramas, dividamos cada palabra en letras y las ordenamos. Cuando se clasifican las letras, todos los anagramas son iguales. -For instance: +Por ejemplo: ``` nap, pan -> anp @@ -9,14 +9,14 @@ cheaters, hectares, teachers -> aceehrst ... ``` -We'll use the letter-sorted variants as map keys to store only one value per each key: +Utilizaremos las variantes ordenadas por letras como claves de Map para almacenar solo un valor por cada clave: ```js run function aclean(arr) { let map = new Map(); for (let word of arr) { - // split the word by letters, sort them and join back + // dividir la palabra por letras, ordenarlas y volver a unir *!* let sorted = word.toLowerCase().split('').sort().join(''); // (*) */!* @@ -31,9 +31,9 @@ let arr = ["nap", "teachers", "cheaters", "PAN", "ear", "era", "hectares"]; alert( aclean(arr) ); ``` -Letter-sorting is done by the chain of calls in the line `(*)`. +La clasificación de letras se realiza mediante la cadena de llamadas en la línea `(*)`. -For convenience let's split it into multiple lines: +Por conveniencia la dividimos en múltiples líneas: ```js let sorted = word // PAN @@ -43,21 +43,21 @@ let sorted = word // PAN .join(''); // anp ``` -Two different words `'PAN'` and `'nap'` receive the same letter-sorted form `'anp'`. +Dos palabras diferentes`'PAN'` y `'nap'` reciben la misma forma ordenada por letras `'anp'`. -The next line put the word into the map: +La siguiente línea pone la palabra en el Map: ```js map.set(sorted, word); ``` -If we ever meet a word the same letter-sorted form again, then it would overwrite the previous value with the same key in the map. So we'll always have at maximum one word per letter-form. +Si alguna vez volvemos a encontrar una palabra con la misma forma ordenada por letras, sobrescribiría el valor anterior con la misma clave en Map. Por lo tanto, siempre tendremos como máximo una palabra ordenada por letras. -At the end `Array.from(map.values())` takes an iterable over map values (we don't need keys in the result) and returns an array of them. +Al final, `Array.from (map.values())` toma un valor iterativo sobre los valores de Map (no necesitamos claves en el resultado) y devuelve un array de ellos. -Here we could also use a plain object instead of the `Map`, because keys are strings. +Aquí también podríamos usar un objeto plano en lugar del `Map`, porque las claves son strings. -That's how the solution can look: +Así es como puede verse la solución: ```js run demo function aclean(arr) { diff --git a/1-js/05-data-types/07-map-set/02-filter-anagrams/task.md b/1-js/05-data-types/07-map-set/02-filter-anagrams/task.md index 731fd2c25..5025b6c97 100644 --- a/1-js/05-data-types/07-map-set/02-filter-anagrams/task.md +++ b/1-js/05-data-types/07-map-set/02-filter-anagrams/task.md @@ -2,11 +2,11 @@ importance: 4 --- -# Filter anagrams +# Filtrar anagramas -[Anagrams](https://en.wikipedia.org/wiki/Anagram) are words that have the same number of same letters, but in different order. +[Anagramas](https://es.wikipedia.org/wiki/Anagrama) son palabras que tienen el mismo número de letras, pero en diferente orden. -For instance: +Por ejemplo: ``` nap - pan @@ -14,15 +14,15 @@ ear - are - era cheaters - hectares - teachers ``` -Write a function `aclean(arr)` that returns an array cleaned from anagrams. +Escriba una función `aclean(arr)` que devuelva un array limpio de anagramas. -For instance: +Por ejemplo: ```js let arr = ["nap", "teachers", "cheaters", "PAN", "ear", "era", "hectares"]; -alert( aclean(arr) ); // "nap,teachers,ear" or "PAN,cheaters,era" +alert( aclean(arr) ); // "nap,teachers,ear" o "PAN,cheaters,era" ``` -From every anagram group should remain only one word, no matter which one. +Es decir, de cada grupo de anagramas debe quedar solo una palabra, sin importar cual. diff --git a/1-js/05-data-types/07-map-set/03-iterable-keys/solution.md b/1-js/05-data-types/07-map-set/03-iterable-keys/solution.md index 7310d1d36..968c78dfd 100644 --- a/1-js/05-data-types/07-map-set/03-iterable-keys/solution.md +++ b/1-js/05-data-types/07-map-set/03-iterable-keys/solution.md @@ -1,7 +1,7 @@ -That's because `map.keys()` returns an iterable, but not an array. +Eso es porque `map.keys()` devuelve un iterable, pero no un array. -We can convert it into an array using `Array.from`: +Podemos convertirlo en un array usando `Array.from`: ```js run diff --git a/1-js/05-data-types/07-map-set/03-iterable-keys/task.md b/1-js/05-data-types/07-map-set/03-iterable-keys/task.md index 81507647f..c40a3f087 100644 --- a/1-js/05-data-types/07-map-set/03-iterable-keys/task.md +++ b/1-js/05-data-types/07-map-set/03-iterable-keys/task.md @@ -2,11 +2,11 @@ importance: 5 --- -# Iterable keys +# Claves iterables -We'd like to get an array of `map.keys()` in a variable and then apply array-specific methods to it, e.g. `.push`. +Nos gustaría obtener un array de `map.keys()` en una variable y luego aplicarle métodos específicos de array, ej. `.push`. -But that doesn't work: +Pero eso no funciona: ```js run let map = new Map(); @@ -16,9 +16,9 @@ map.set("name", "John"); let keys = map.keys(); *!* -// Error: keys.push is not a function +// Error: keys.push no es una función keys.push("more"); */!* ``` -Why? How can we fix the code to make `keys.push` work? +¿Por qué? ¿Cómo podemos arreglar el código para que funcione `keys.push`? diff --git a/1-js/05-data-types/07-map-set/article.md b/1-js/05-data-types/07-map-set/article.md index 37f5e48c2..906499c82 100644 --- a/1-js/05-data-types/07-map-set/article.md +++ b/1-js/05-data-types/07-map-set/article.md @@ -1,157 +1,157 @@ -# Map and Set +# Map y Set -Till now, we've learned about the following complex data structures: +Hasta este momento, hemos aprendido sobre las siguientes estructuras de datos: -- Objects are used for storing keyed collections. -- Arrays are used for storing ordered collections. +- Objetos, para almacenar colecciones de datos accesibles a través de una clave. +- Arrays, para almacenar colecciones ordenadas de datos. -But that's not enough for real life. That's why `Map` and `Set` also exist. +Pero eso no es suficiente para la vida real. Por eso también existen `Map` y `Set`. ## Map -[Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) is a collection of keyed data items, just like an `Object`. But the main difference is that `Map` allows keys of any type. +[Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) es, al igual que `Object`, una colección de datos identificados por claves. La principal diferencia es que `Map` permite claves de cualquier tipo. -Methods and properties are: +Los métodos y propiedades son: -- [`new Map()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/Map) -- creates the map. -- [`map.set(key, value)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set) -- stores the value by the key. -- [`map.get(key)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get) -- returns the value by the key, `undefined` if `key` doesn't exist in map. -- [`map.has(key)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has) -- returns `true` if the `key` exists, `false` otherwise. -- [`map.delete(key)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete) -- removes the element (the key/value pair) by the key. -- [`map.clear()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/clear) -- removes everything from the map. -- [`map.size`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/size) -- returns the current element count. +- [`new Map()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/Map) -- crea el mapa. +- [`map.set(clave, valor)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set)) -- almacena el valor asociado a la clave. +- [`map.get(clave)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get) -- devuelve el valor de la clave. Será `undefined` si la `clave` no existe en map. +- [`map.has(clave)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has) -- devuelve `true` si la `clave` existe en map, `false` si no existe. +- [`map.delete(clave)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete) -- elimina el elemento con esa clave. +- [`map.clear()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/clear) -- elimina todo del map. +- [`map.size`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/size) -- tamaño, devuelve la cantidad de elementos actual. -For instance: +Por ejemplo: ```js run let map = new Map(); -map.set('1', 'str1'); // a string key -map.set(1, 'num1'); // a numeric key -map.set(true, 'bool1'); // a boolean key +map.set('1', 'str1'); // un string como clave +map.set(1, 'num1'); // un número como clave +map.set(true, 'bool1'); // un booleano como clave -// remember the regular Object? it would convert keys to string -// Map keeps the type, so these two are different: +// ¿recuerda el objeto regular? convertiría las claves a string. +// Map mantiene el tipo de dato en las claves, por lo que estas dos son diferentes: alert( map.get(1) ); // 'num1' alert( map.get('1') ); // 'str1' alert( map.size ); // 3 ``` -As we can see, unlike objects, keys are not converted to strings. Any type of key is possible. +Podemos ver que, a diferencia de los objetos, las claves no se convierten en strings. Cualquier tipo de clave es posible en un Map. -```smart header="`map[key]` isn't the right way to use a `Map`" -Although `map[key]` also works, e.g. we can set `map[key] = 2`, this is treating `map` as a plain JavaScript object, so it implies all corresponding limitations (only string/symbol keys and so on). +```smart header="map[clave] no es la forma correcta de usar Map" +Aunque `map[clave]` también funciona (por ejemplo podemos establecer `map[clave]` = 2), esto es tratar a `map` como un objeto JavaScript simple, lo que implica tener todas las limitaciones correspondientes (que solo se permita string/symbol como clave, etc.). -So we should use `map` methods: `set`, `get` and so on. +Por lo tanto, debemos usar los métodos de `Map`: `set`, `get` y demás. ``` -**Map can also use objects as keys.** +**También podemos usar objetos como claves.** -For instance: +Por ejemplo: ```js run let john = { name: "John" }; -// for every user, let's store their visits count +// para cada usuario, almacenemos el recuento de visitas let visitsCountMap = new Map(); -// john is the key for the map +// john es la clave para el Map visitsCountMap.set(john, 123); alert( visitsCountMap.get(john) ); // 123 ``` -Using objects as keys is one of the most notable and important `Map` features. The same does not count for `Object`. String as a key in `Object` is fine, but we can't use another `Object` as a key in `Object`. +El uso de objetos como claves es una de las características de `Map` más notables e importantes. Esto no se aplica a los objetos: una clave de tipo `string` está bien en un `Object`, pero no podemos usar otro `Object` como clave. -Let's try: +Intentémoslo: ```js run let john = { name: "John" }; let ben = { name: "Ben" }; -let visitsCountObj = {}; // try to use an object +let visitsCountObj = {}; // intenta usar un objeto -visitsCountObj[ben] = 234; // try to use ben object as the key -visitsCountObj[john] = 123; // try to use john object as the key, ben object will get replaced +visitsCountObj[ben] = 234; // intenta usar el objeto ben como clave +visitsCountObj[john] = 123; // intenta usar el objeto john como clave, el objeto ben es reemplazado *!* -// That's what got written! -alert( visitsCountObj["[object Object]"] ); // 123 +// Esto es lo que se escribió! +alert( visitsCountObj["[object Object]"] ); // 123 */!* ``` -As `visitsCountObj` is an object, it converts all `Object` keys, such as `john` and `ben` above, to same string `"[object Object]"`. Definitely not what we want. +Como `visitsCountObj` es un objeto, convierte todas los objetos como `john` y `ben` en el mismo string `"[objeto Objeto]"`. Definitivamente no es lo que queremos. -```smart header="How `Map` compares keys" -To test keys for equivalence, `Map` uses the algorithm [SameValueZero](https://tc39.github.io/ecma262/#sec-samevaluezero). It is roughly the same as strict equality `===`, but the difference is that `NaN` is considered equal to `NaN`. So `NaN` can be used as the key as well. +```smart header="Cómo `Map` compara las claves" +Para probar la equivalencia de claves, `Map` utiliza el algoritmo [SameValueZero](https://tc39.github.io/ecma262/#sec-samevaluezero). Es aproximadamente lo mismo que la igualdad estricta `===`, pero la diferencia es que `NaN` se considera igual a `NaN`. Por lo tanto, `NaN` también se puede usar como clave. -This algorithm can't be changed or customized. +Este algoritmo no se puede cambiar ni personalizar. ``` -````smart header="Chaining" -Every `map.set` call returns the map itself, so we can "chain" the calls: +````smart header="Encadenamiento" +Cada llamada a `map.set` devuelve map en sí, así que podamos "encadenar" las llamadas: ```js map.set('1', 'str1') - .set(1, 'num1') - .set(true, 'bool1'); + .set(1, 'num1') + .set(true, 'bool1'); ``` ```` -## Iteration over Map +## Iteración sobre Map -For looping over a `map`, there are 3 methods: +Para recorrer un `map`, hay 3 métodos: -- [`map.keys()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/keys) -- returns an iterable for keys, -- [`map.values()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/values) -- returns an iterable for values, -- [`map.entries()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/entries) -- returns an iterable for entries `[key, value]`, it's used by default in `for..of`. +- [`map.keys()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/keys) –- devuelve un iterable con las claves. +- [`map.values()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/values) -– devuelve un iterable con los valores. +- [`map.entries()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/entries) -– devuelve un iterable para las entradas `[clave, valor]`. Es el que usa por defecto en `for..of`. -For instance: +Por ejemplo: ```js run let recipeMap = new Map([ - ['cucumber', 500], - ['tomatoes', 350], - ['onion', 50] + ['pepino', 500], + ['tomates', 350], + ['cebollas', 50] ]); -// iterate over keys (vegetables) +// iterando sobre las claves (verduras) for (let vegetable of recipeMap.keys()) { - alert(vegetable); // cucumber, tomatoes, onion + alert(vegetable); // pepino, tomates, cebollas } -// iterate over values (amounts) +// iterando sobre los valores (precios) for (let amount of recipeMap.values()) { alert(amount); // 500, 350, 50 } -// iterate over [key, value] entries -for (let entry of recipeMap) { // the same as of recipeMap.entries() - alert(entry); // cucumber,500 (and so on) +// iterando sobre las entradas [clave, valor] +for (let entry of recipeMap) { // lo mismo que recipeMap.entries() + alert(entry); // pepino,500 (etc) } ``` -```smart header="The insertion order is used" -The iteration goes in the same order as the values were inserted. `Map` preserves this order, unlike a regular `Object`. +```smart header="Se utiliza el orden de inserción." +La iteración va en el mismo orden en que se insertaron los valores. `Map` conserva este orden, a diferencia de un `Objet` normal. ``` -Besides that, `Map` has a built-in `forEach` method, similar to `Array`: +Además, `Map` tiene un método `forEach` incorporado, similar al de `Array`: ```js -// runs the function for each (key, value) pair +// recorre la función para cada par (clave, valor) recipeMap.forEach( (value, key, map) => { - alert(`${key}: ${value}`); // cucumber: 500 etc + alert(`${key}: ${value}`); // pepino: 500 etc }); ``` -## Object.entries: Map from Object +## Object.entries: Map desde Objeto -When a `Map` is created, we can pass an array (or another iterable) with key/value pairs for initialization, like this: +Al crear un `Map`, podemos pasarle un array (u otro iterable) con pares clave/valor para la inicialización: ```js run -// array of [key, value] pairs +// array de [clave, valor] let map = new Map([ ['1', 'str1'], [1, 'num1'], @@ -161,9 +161,9 @@ let map = new Map([ alert( map.get('1') ); // str1 ``` -If we have a plain object, and we'd like to create a `Map` from it, then we can use built-in method [Object.entries(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries) that returns an array of key/value pairs for an object exactly in that format. +Si tenemos un objeto plano, y queremos crear un `Map` a partir de él, podemos usar el método incorporado [Object.entries(obj)](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/entries) que devuelve un array de pares clave/valor para un objeto en ese preciso formato. -So we can create a map from an object like this: +Entonces podemos inicializar un map desde un objeto: ```js run let obj = { @@ -178,14 +178,14 @@ let map = new Map(Object.entries(obj)); alert( map.get('name') ); // John ``` -Here, `Object.entries` returns the array of key/value pairs: `[ ["name","John"], ["age", 30] ]`. That's what `Map` needs. +Aquí, `Object.entries` devuelve el array de pares clave/valor: `[ ["name","John"], ["age", 30] ]`. Es lo que necesita `Map`. -## Object.fromEntries: Object from Map +## Object.fromEntries: Objeto desde Map -We've just seen how to create `Map` from a plain object with `Object.entries(obj)`. +Acabamos de ver cómo crear un `Map` a partir de un objeto simple con `Object.entries (obj)`. -There's `Object.fromEntries` method that does the reverse: given an array of `[key, value]` pairs, it creates an object from them: +Existe el método `Object.fromEntries` que hace lo contrario: dado un array de pares [clave, valor], crea un objeto a partir de ellos: ```js run let prices = Object.fromEntries([ @@ -194,16 +194,16 @@ let prices = Object.fromEntries([ ['meat', 4] ]); -// now prices = { banana: 1, orange: 2, meat: 4 } +// ahora prices = { banana: 1, orange: 2, meat: 4 } alert(prices.orange); // 2 ``` -We can use `Object.fromEntries` to get a plain object from `Map`. +Podemos usar `Object.fromEntries` para obtener un objeto desde `Map`. -E.g. we store the data in a `Map`, but we need to pass it to a 3rd-party code that expects a plain object. +Ejemplo: almacenamos los datos en un `Map`, pero necesitamos pasarlos a un código de terceros que espera un objeto simple. -Here we go: +Aquí vamos: ```js run let map = new Map(); @@ -212,42 +212,42 @@ map.set('orange', 2); map.set('meat', 4); *!* -let obj = Object.fromEntries(map.entries()); // make a plain object (*) +let obj = Object.fromEntries(map.entries()); // hace un objeto simple (*) */!* -// done! +// Hecho! // obj = { banana: 1, orange: 2, meat: 4 } alert(obj.orange); // 2 ``` -A call to `map.entries()` returns an iterable of key/value pairs, exactly in the right format for `Object.fromEntries`. +Una llamada a `map.entries()` devuelve un array de pares clave/valor, exactamente en el formato correcto para `Object.fromEntries.` -We could also make line `(*)` shorter: +También podríamos acortar la línea `(*)`: ```js -let obj = Object.fromEntries(map); // omit .entries() +let obj = Object.fromEntries(map); // omitimos .entries() ``` -That's the same, because `Object.fromEntries` expects an iterable object as the argument. Not necessarily an array. And the standard iteration for `map` returns same key/value pairs as `map.entries()`. So we get a plain object with same key/values as the `map`. +Es lo mismo, porque `Object.fromEntries` espera un objeto iterable como argumento. No necesariamente un array. Y la iteración estándar para el `Map` devuelve los mismos pares clave/valor que `map.entries()`. Entonces obtenemos un objeto simple con las mismas claves/valores que `Map`. ## Set -A [`Set`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) is a special type collection - "set of values" (without keys), where each value may occur only once. +Un [`Set`](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Set) es una colección de tipo especial: "conjunto de valores" (sin claves), donde cada valor puede aparecer solo una vez. -Its main methods are: +Sus principales métodos son: -- [`new Set([iterable])`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/Set) -- creates the set, and if an `iterable` object is provided (usually an array), copies values from it into the set. -- [`set.add(value)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/add) -- adds a value, returns the set itself. -- [`set.delete(value)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/delete) -- removes the value, returns `true` if `value` existed at the moment of the call, otherwise `false`. -- [`set.has(value)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/has) -- returns `true` if the value exists in the set, otherwise `false`. -- [`set.clear()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/clear) -- removes everything from the set. -- [`set.size`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/size) -- is the elements count. +- [`new Set([iterable])`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/Set) -- crea el set. El argumento opcional es un objeto iterable (generalmente un array) con los valores para inicializarlo. +- [`set.add(valor)`](mdn:js/Set/add) -- agrega un valor, y devuelve el set en sí. +- [`set.delete(valor)`](mdn:js/Set/delete) -- elimina el valor, y devuelve `true` si el `valor` existía al momento de la llamada; si no, devuelve `false`. +- [`set.has(valor)`](mdn:js/Set/has) -- devuelve `true` si el valor existe en el set, si no, devuelve `false`. +- [`set.clear()`](mdn:js/Set/clear) -- elimina todo el continido del set. +- [`set.size`](mdn:js/Set/size) -- es la cantidad de elementos. -The main feature is that repeated calls of `set.add(value)` with the same value don't do anything. That's the reason why each value appears in a `Set` only once. +La característica principal es que llamadas repetidas de `set.add(valor)` con el mismo valor no hacen nada. Esa es la razón por la cual cada valor aparece en `Set` solo una vez. -For example, we have visitors coming, and we'd like to remember everyone. But repeated visits should not lead to duplicates. A visitor must be "counted" only once. +Por ejemplo, vienen visitantes y queremos recordarlos a todos. Pero las visitas repetidas no deberían llevar a duplicados. Un visitante debe ser "contado" solo una vez. -`Set` is just the right thing for that: +`Set` es lo correcto para eso: ```js run let set = new Set(); @@ -256,76 +256,76 @@ let john = { name: "John" }; let pete = { name: "Pete" }; let mary = { name: "Mary" }; -// visits, some users come multiple times +// visitas, algunos usuarios lo hacen varias veces set.add(john); set.add(pete); set.add(mary); set.add(john); set.add(mary); -// set keeps only unique values +// set solo guarda valores únicos alert( set.size ); // 3 for (let user of set) { - alert(user.name); // John (then Pete and Mary) + alert(user.name); // John (luego Pete y Mary) } ``` -The alternative to `Set` could be an array of users, and the code to check for duplicates on every insertion using [arr.find](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find). But the performance would be much worse, because this method walks through the whole array checking every element. `Set` is much better optimized internally for uniqueness checks. +La alternativa a `Set` podría ser un array de usuarios, y código para verificar si hay duplicados en cada inserción usando [arr.find](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array/find). Pero el rendimiento sería mucho peor, porque este método recorre el array completo comprobando cada elemento. `Set` está optimizado internamente para verificar unicidad. -## Iteration over Set +## Iteración sobre Set -We can loop over a set either with `for..of` or using `forEach`: +Podemos recorrer `Set` con `for..of` o usando `forEach`: ```js run let set = new Set(["oranges", "apples", "bananas"]); for (let value of set) alert(value); -// the same with forEach: +// lo mismo que forEach: set.forEach((value, valueAgain, set) => { alert(value); }); ``` -Note the funny thing. The callback function passed in `forEach` has 3 arguments: a `value`, then *the same value* `valueAgain`, and then the target object. Indeed, the same value appears in the arguments twice. +Tenga en cuenta algo peculiar: la función callback pasada en forEach tiene 3 argumentos: un valor, luego el mismo valor "valueAgain" y luego el objeto de destino que es set. El mismo valor aparece en los argumentos dos veces. -That's for compatibility with `Map` where the callback passed `forEach` has three arguments. Looks a bit strange, for sure. But this may help to replace `Map` with `Set` in certain cases with ease, and vice versa. +Eso es por compatibilidad con `Map` donde la función callback tiene tres argumentos. Parece un poco extraño, seguro. Pero en ciertos casos puede ayudar a reemplazar `Map` con `Set` y viceversa con facilidad. -The same methods `Map` has for iterators are also supported: +También soporta los mismos métodos que `Map` tiene para los iteradores: -- [`set.keys()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/keys) -- returns an iterable object for values, -- [`set.values()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/values) -- same as `set.keys()`, for compatibility with `Map`, -- [`set.entries()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/entries) -- returns an iterable object for entries `[value, value]`, exists for compatibility with `Map`. +- [`set.keys()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/keys) – devuelve un iterable para las claves. +- [`set.values()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/values) – lo mismo que `set.keys()`, por su compatibilidad con `Map`. +- [`set.entries()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/entries) – devuelve un iterable para las entradas `[clave, valor]`, por su compatibilidad con `Map`. -## Summary +## Resumen -[`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) -- is a collection of keyed values. +[`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) -- es una colección de valores con clave. -Methods and properties: +Métodos y propiedades: -- [`new Map([iterable])`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/Map) -- creates the map, with optional `iterable` (e.g. array) of `[key,value]` pairs for initialization. -- [`map.set(key, value)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set) -- stores the value by the key, returns the map itself. -- [`map.get(key)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get) -- returns the value by the key, `undefined` if `key` doesn't exist in map. -- [`map.has(key)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has) -- returns `true` if the `key` exists, `false` otherwise. -- [`map.delete(key)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete) -- removes the element by the key, returns `true` if `key` existed at the moment of the call, otherwise `false`. -- [`map.clear()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/clear) -- removes everything from the map. -- [`map.size`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/size) -- returns the current element count. +- [`new Map([iterable])`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/Map) -- crea el mapa, con un `iterable` (p.ej. array) de pares `[clave,valor]` para su inicialización. +- [`map.set(clave, valor)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set) -- almacena el valor para la clave. +- [`map.get(clave)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get) -- devuelve el valor de la clave: será `undefined` si la `clave` no existe en Map. +- [`map.has(clave)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has) -- devuelve`true` si la `clave` existe, y `false` si no existe. +- [`map.delete(clave)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete) -- elimina del map el elemento con esa clave. +- [`map.clear()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/clear) -- vacía el Map. +- [`map.size`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/size) -- devuelve la cantidad de elementos del Map. -The differences from a regular `Object`: +La diferencia con un `Objeto` regular: -- Any keys, objects can be keys. -- Additional convenient methods, the `size` property. +- Cualquier clave. Los objetos también pueden ser claves. +- Métodos adicionales convenientes, y la propiedad `size`. -[`Set`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) -- is a collection of unique values. +[`Set`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) -- es una colección de valores únicos (sin duplicados). -Methods and properties: +Métodos y propiedades: -- [`new Set([iterable])`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/Set) -- creates the set, with optional `iterable` (e.g. array) of values for initialization. -- [`set.add(value)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/add) -- adds a value (does nothing if `value` exists), returns the set itself. -- [`set.delete(value)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/delete) -- removes the value, returns `true` if `value` existed at the moment of the call, otherwise `false`. -- [`set.has(value)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/has) -- returns `true` if the value exists in the set, otherwise `false`. -- [`set.clear()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/clear) -- removes everything from the set. -- [`set.size`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/size) -- is the elements count. +- [`new Set([iterable])`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/Set) -- crea el set. El argumento opcional es un objeto iterable (por ejemplo un array) de valores para inicializarlo. +- [`set.add(valor)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/add) -- agrega un valor, devuelve el set en sí. +- [`set.delete(valor)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/delete) -- elimina el valor, devuelve `true` si `valor` existe al momento de la llamada; si no, devuelve `false`. +- [`set.has(valor)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/has) -- devuelve `true` si el valor existe en el set, si no, devuelve `false`. +- [`set.clear()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/clear) -- elimina todo del set. +- [`set.size`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/size) -- es la cantidad de elementos. -Iteration over `Map` and `Set` is always in the insertion order, so we can't say that these collections are unordered, but we can't reorder elements or directly get an element by its number. +La iteración sobre `Map` y `Set` siempre está en el orden de inserción, por lo que no podemos decir que estas colecciones están desordenadas, pero no podemos reordenar elementos u obtener un elemento directamente por su número. diff --git a/1-js/05-data-types/08-weakmap-weakset/01-recipients-read/solution.md b/1-js/05-data-types/08-weakmap-weakset/01-recipients-read/solution.md index e2147ccfa..c0a9f3f59 100644 --- a/1-js/05-data-types/08-weakmap-weakset/01-recipients-read/solution.md +++ b/1-js/05-data-types/08-weakmap-weakset/01-recipients-read/solution.md @@ -1,4 +1,4 @@ -Let's store read messages in `WeakSet`: +Guardemos los mensajes leídos en `WeakSet`: ```js run let messages = [ @@ -9,35 +9,35 @@ let messages = [ let readMessages = new WeakSet(); -// two messages have been read +// se han leído dos mensajes readMessages.add(messages[0]); readMessages.add(messages[1]); -// readMessages has 2 elements +// readMessages tiene 2 elementos -// ...let's read the first message again! +// ...¡leamos nuevamente el primer mensaje! readMessages.add(messages[0]); -// readMessages still has 2 unique elements +// readMessages todavía tiene dos únicos elementos -// answer: was the message[0] read? +// respuesta: ¿se leyó el mensaje [0]? alert("Read message 0: " + readMessages.has(messages[0])); // true messages.shift(); -// now readMessages has 1 element (technically memory may be cleaned later) +// ahora readMessages tiene 1 elemento (técnicamente la memoria puede limpiarse más tarde) ``` -The `WeakSet` allows to store a set of messages and easily check for the existence of a message in it. +El `WeakSet` permite almacenar un conjunto de mensajes y verificar fácilmente la existencia de un mensaje en él. -It cleans up itself automatically. The tradeoff is that we can't iterate over it, can't get "all read messages" from it directly. But we can do it by iterating over all messages and filtering those that are in the set. +Se limpia automáticamente. La desventaja es que no podemos iterar sobre él, no podemos obtener "todos los mensajes leídos" directamente. Pero podemos hacerlo iterando sobre todos los mensajes y filtrando los que están en el conjunto. -Another, different solution could be to add a property like `message.isRead=true` to a message after it's read. As messages objects are managed by another code, that's generally discouraged, but we can use a symbolic property to avoid conflicts. +Otra solución diferente podría ser agregar una propiedad como `message.isRead = true` a un mensaje después de leerlo. Como los objetos de mensajes son administrados por otro código, generalmente se desaconseja, pero podemos usar una propiedad simbólica para evitar conflictos. -Like this: +Como esto: ```js -// the symbolic property is only known to our code +// la propiedad simbólica solo es conocida por nuestro código let isRead = Symbol("isRead"); messages[0][isRead] = true; ``` -Now third-party code probably won't see our extra property. +Ahora el código de terceros probablemente no verá nuestra propiedad adicional. -Although symbols allow to lower the probability of problems, using `WeakSet` is better from the architectural point of view. +Aunque los símbolos permiten reducir la probabilidad de problemas, usar `WeakSet` es mejor desde el punto de vista arquitectónico. diff --git a/1-js/05-data-types/08-weakmap-weakset/01-recipients-read/task.md b/1-js/05-data-types/08-weakmap-weakset/01-recipients-read/task.md index fd31a891b..69a0d2246 100644 --- a/1-js/05-data-types/08-weakmap-weakset/01-recipients-read/task.md +++ b/1-js/05-data-types/08-weakmap-weakset/01-recipients-read/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# Store "unread" flags +# Almacenar banderas "no leídas" -There's an array of messages: +Hay un array de mensajes: ```js let messages = [ @@ -14,10 +14,10 @@ let messages = [ ]; ``` -Your code can access it, but the messages are managed by someone else's code. New messages are added, old ones are removed regularly by that code, and you don't know the exact moments when it happens. +Su código puede acceder a él, pero los mensajes son administrados por el código de otra persona. Se agregan mensajes nuevos, los códigos viejos se eliminan regularmente con ese código, y usted no sabe los momentos exactos en que sucede. -Now, which data structure could you use to store information about whether the message "has been read"? The structure must be well-suited to give the answer "was it read?" for the given message object. +Ahora, ¿qué estructura de datos podría usar para almacenar información sobre si el mensaje "ha sido leído"? La estructura debe ser adecuada para dar la respuesta "¿se leyó?" para el objeto del mensaje dado. -P.S. When a message is removed from `messages`, it should disappear from your structure as well. +P.D Cuando un mensaje se elimina de `messages`, también debería desaparecer de su estructura. -P.P.S. We shouldn't modify message objects, add our properties to them. As they are managed by someone else's code, that may lead to bad consequences. +P.P.D. No debemos modificar los objetos del mensaje, o agregarles nuestras propiedades. Como son administrados por el código de otra persona, eso puede generarnos resultados no deseados. diff --git a/1-js/05-data-types/08-weakmap-weakset/02-recipients-when-read/solution.md b/1-js/05-data-types/08-weakmap-weakset/02-recipients-when-read/solution.md index 2af0547c1..0525aab46 100644 --- a/1-js/05-data-types/08-weakmap-weakset/02-recipients-when-read/solution.md +++ b/1-js/05-data-types/08-weakmap-weakset/02-recipients-when-read/solution.md @@ -1,5 +1,5 @@ -To store a date, we can use `WeakMap`: +Para almacenar una fecha, podemos usar `WeakMap`: ```js let messages = [ @@ -11,5 +11,5 @@ let messages = [ let readMap = new WeakMap(); readMap.set(messages[0], new Date(2017, 1, 1)); -// Date object we'll study later +// // Objeto Date que estudiaremos más tarde ``` diff --git a/1-js/05-data-types/08-weakmap-weakset/02-recipients-when-read/task.md b/1-js/05-data-types/08-weakmap-weakset/02-recipients-when-read/task.md index 8e341c184..98d60fa4f 100644 --- a/1-js/05-data-types/08-weakmap-weakset/02-recipients-when-read/task.md +++ b/1-js/05-data-types/08-weakmap-weakset/02-recipients-when-read/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# Store read dates +# Almacenar fechas de lectura -There's an array of messages as in the [previous task](info:task/recipients-read). The situation is similar. +Hay un array semejante al de la [actividad anterior](info:task/recipients-read). La situación es similar: ```js let messages = [ @@ -14,8 +14,8 @@ let messages = [ ]; ``` -The question now is: which data structure you'd suggest to store the information: "when the message was read?". +La pregunta ahora es: ¿qué estructura de datos es la adecuada para almacenar la información: "¿cuándo se leyó el mensaje?". -In the previous task we only needed to store the "yes/no" fact. Now we need to store the date, and it should only remain in memory until the message is garbage collected. +En la tarea anterior solo necesitábamos almacenar el hecho de "sí/no". Ahora necesitamos almacenar la fecha, y solo debe permanecer en la memoria hasta que el mensaje sea recolectado como basura. -P.S. Dates can be stored as objects of built-in `Date` class, that we'll cover later. +P.D Las fechas se pueden almacenar como objetos de la clase incorporada `Date`, que cubriremos más adelante. diff --git a/1-js/05-data-types/08-weakmap-weakset/article.md b/1-js/05-data-types/08-weakmap-weakset/article.md index 9795017d4..e2e9e444f 100644 --- a/1-js/05-data-types/08-weakmap-weakset/article.md +++ b/1-js/05-data-types/08-weakmap-weakset/article.md @@ -1,46 +1,46 @@ -# WeakMap and WeakSet +# WeakMap y WeakSet -As we know from the chapter , JavaScript engine keeps a value in memory while it is "reachable" and can potentially be used. +Como vimos en el artículo , el motor de JavaScript mantiene un valor en la memoria mientras sea "accesible" y pueda ser potencialmente usado. -For instance: +Por ejemplo: ```js let john = { name: "John" }; -// the object can be accessed, john is the reference to it +// se puede acceder al objeto, john hace referencia a él -// overwrite the reference +// sobrescribe la referencia john = null; *!* -// the object will be removed from memory +// el objeto ya no es accesible y será eliminado de la memoria */!* ``` -Usually, properties of an object or elements of an array or another data structure are considered reachable and kept in memory while that data structure is in memory. +Normalmente, los elementos de una estructura, como las propiedades de un objeto o los elementos de un array, permanecen en memoria mientras dicha estructura esté en uso. -For instance, if we put an object into an array, then while the array is alive, the object will be alive as well, even if there are no other references to it. +Por ejemplo, si agregamos un objeto a un array, mientras el array permanezca en memoria, el objeto también estará en memoria, incluso si no hay otras referencias a él. -Like this: +Como aquí: ```js let john = { name: "John" }; let array = [ john ]; -john = null; // overwrite the reference +john = null; // sobrescribe la referencia *!* -// the object previously referenced by john is stored inside the array -// therefore it won't be garbage-collected -// we can get it as array[0] +// El objeto referenciado por John se almacena dentro del array, +// por lo que no será borrado por el recolector de basura +// Lo podemos obtener como array[0] */!* ``` -Similar to that, if we use an object as the key in a regular `Map`, then while the `Map` exists, that object exists as well. It occupies memory and may not be garbage collected. +Del mismo modo, si usamos un objeto como la clave en un `Map` regular, entonces mientras exista el` Map`, ese objeto también existe. Este objeto ocupa memoria y no puede ser reclamado por el recolector de basura. -For instance: +Por ejemplo: ```js let john = { name: "John" }; @@ -48,36 +48,36 @@ let john = { name: "John" }; let map = new Map(); map.set(john, "..."); -john = null; // overwrite the reference +john = null; // sobreescribe la referencia *!* -// john is stored inside the map, -// we can get it by using map.keys() +// john se almacena dentro de map, +// podemos obtenerlo usando map.keys () */!* ``` -[`WeakMap`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap) is fundamentally different in this aspect. It doesn't prevent garbage-collection of key objects. +[`WeakMap`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap) es fundamentalmente diferente en este aspecto. No impide la recolección de basura de objetos usados como claves. -Let's see what it means on examples. +Veamos qué significa esto en los ejemplos. ## WeakMap -The first difference between [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) and [`WeakMap`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap) is that keys must be objects, not primitive values: +La primera diferencia con [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) es que en [`WeakMap`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap) las claves deben ser objetos, no valores primitivos: ```js run let weakMap = new WeakMap(); let obj = {}; -weakMap.set(obj, "ok"); // works fine (object key) +weakMap.set(obj, "ok"); // funciona bien (la clave es un objeto) *!* -// can't use a string as the key -weakMap.set("test", "Whoops"); // Error, because "test" is not an object +// no puede usar un string como clave +weakMap.set("test", "Whoops"); // Error, porque "test" no es un objeto */!* ``` -Now, if we use an object as the key in it, and there are no other references to that object -- it will be removed from memory (and from the map) automatically. +Ahora, si usamos un objeto como clave y no hay otras referencias a ese objeto, se eliminará de la memoria (y del map) automáticamente. ```js let john = { name: "John" }; @@ -85,103 +85,103 @@ let john = { name: "John" }; let weakMap = new WeakMap(); weakMap.set(john, "..."); -john = null; // overwrite the reference +john = null; // sobreescribe la referencia -// john is removed from memory! +// ¡John se eliminó de la memoria! ``` -Compare it with the regular `Map` example above. Now if `john` only exists as the key of `WeakMap` -- it will be automatically deleted from the map (and memory). +Compárelo con el ejemplo del `Map` regular anterior. Ahora, si `john` solo existe como la clave de` WeakMap`, se eliminará automáticamente del map (y de la memoria). -`WeakMap` does not support iteration and methods `keys()`, `values()`, `entries()`, so there's no way to get all keys or values from it. +`WeakMap` no admite la iteración ni los métodos `keys()`, `values()`, `entries()`, así que no hay forma de obtener todas las claves o valores de él. -`WeakMap` has only the following methods: +`WeakMap` tiene solo los siguientes métodos: -- [`weakMap.set(key, value)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/set) -- [`weakMap.get(key)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/get) -- [`weakMap.delete(key)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/delete) -- [`weakMap.has(key)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/has) +- [`weakMap.set(clave, valor)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/set) +- [`weakMap.get(clave)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/get) +- [`weakMap.delete(clave)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/delete) +- [`weakMap.has(clave)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/has) -Why such a limitation? That's for technical reasons. If an object has lost all other references (like `john` in the code above), then it is to be garbage-collected automatically. But technically it's not exactly specified *when the cleanup happens*. +¿Por qué tanta limitación? Eso es por razones técnicas. Si un objeto ha perdido todas las demás referencias (como `john` en el código anterior), entonces se debe recolectar automáticamente como basura. Pero técnicamente no se especifica exactamente *cuándo se realiza la limpieza*. -The JavaScript engine decides that. It may choose to perform the memory cleanup immediately or to wait and do the cleaning later when more deletions happen. So, technically, the current element count of a `WeakMap` is not known. The engine may have cleaned it up or not, or did it partially. For that reason, methods that access all keys/values are not supported. +El motor de JavaScript decide eso. Puede optar por realizar la limpieza de la memoria inmediatamente o esperar y realizar la limpieza más tarde cuando ocurran más eliminaciones. Por lo tanto, técnicamente no se conoce el recuento actual de elementos de un `WeakMap`. El motor puede haberlo limpiado o no, o lo hizo parcialmente. Por esa razón, los métodos que acceden a todas las claves/valores no son soportados. -Now, where do we need such a data structure? +Ahora, ¿dónde necesitamos esta estructura de datos? -## Use case: additional data +## Caso de uso: datos adicionales -The main area of application for `WeakMap` is an *additional data storage*. +El área principal de aplicación de `WeakMap` es como *almacenamiento de datos adicional*. -If we're working with an object that "belongs" to another code, maybe even a third-party library, and would like to store some data associated with it, that should only exist while the object is alive - then `WeakMap` is exactly what's needed. +Si estamos trabajando con un objeto que "pertenece" a otro código (tal vez incluso una biblioteca de terceros), y queremos almacenar algunos datos asociados a él que solo deberían existir mientras el objeto esté vivo, entonces `WeakMap` es exactamente lo que se necesita. -We put the data to a `WeakMap`, using the object as the key, and when the object is garbage collected, that data will automatically disappear as well. +Ponemos los datos en un `WeakMap` utilizando el objeto como clave, y cuando el objeto sea recolectado por el recolector de basura, esos datos también desaparecerán automáticamente. ```js weakMap.set(john, "secret documents"); -// if john dies, secret documents will be destroyed automatically +// si John muere, secret documents será destruido automáticamente ``` -Let's look at an example. +Veamos un ejemplo. -For instance, we have code that keeps a visit count for users. The information is stored in a map: a user object is the key and the visit count is the value. When a user leaves (its object gets garbage collected), we don't want to store their visit count anymore. +Por ejemplo, tenemos un código que mantiene un recuento de visitas para los usuarios. La información se almacena en un map: un objeto de usuario es la clave y el recuento de visitas es el valor. Cuando un usuario se va (su objeto será recolectado por el recolector de basura), ya no queremos almacenar su recuento de visitas. -Here's an example of a counting function with `Map`: +Aquí hay un ejemplo de una función de conteo con `Map`: ```js // 📁 visitsCount.js let visitsCountMap = new Map(); // map: user => visits count -// increase the visits count +// incrementar el recuento de visitas function countUser(user) { let count = visitsCountMap.get(user) || 0; visitsCountMap.set(user, count + 1); } ``` -And here's another part of the code, maybe another file using it: +Y aquí hay otra parte del código, tal vez otro archivo usándolo: ```js // 📁 main.js let john = { name: "John" }; -countUser(john); // count his visits +countUser(john); // cuenta sus visitas -// later john leaves us +// luego John nos deja john = null; ``` -Now, `john` object should be garbage collected, but remains in memory, as it's a key in `visitsCountMap`. +Ahora el objeto `john` debería ser recolectado como basura, pero permanece en la memoria, ya que es una propiedad en` visitCountMap`. -We need to clean `visitsCountMap` when we remove users, otherwise it will grow in memory indefinitely. Such cleaning can become a tedious task in complex architectures. +Necesitamos limpiar `visitCountMap` cuando eliminamos usuarios, de lo contrario, crecerá en la memoria indefinidamente. Tal limpieza puede convertirse en una tarea tediosa en arquitecturas complejas. -We can avoid it by switching to `WeakMap` instead: +Lo podemos evitar cambiando a `WeakMap` en su lugar: ```js // 📁 visitsCount.js let visitsCountMap = new WeakMap(); // weakmap: user => visits count -// increase the visits count +// incrementar el recuento de visitas function countUser(user) { let count = visitsCountMap.get(user) || 0; visitsCountMap.set(user, count + 1); } ``` -Now we don't have to clean `visitsCountMap`. After `john` object becomes unreachable, by all means except as a key of `WeakMap`, it gets removed from memory, along with the information by that key from `WeakMap`. +Ahora no tenemos que limpiar `visitasCountMap`. Después de que el objeto `john` se vuelve inalcanzable por todos los medios excepto como una propiedad de` WeakMap`, se elimina de la memoria junto con la información asociada a esa clave de `WeakMap`. -## Use case: caching +## Caso de uso: almacenamiento en caché -Another common example is caching. We can store ("cache") results from a function, so that future calls on the same object can reuse it. +Otro ejemplo común es el almacenamiento en caché: cuando se debe recordar el resultado de una función ("en caché"), para que las llamadas futuras en el mismo objeto lo reutilicen. -To achieve that, we can use `Map` (not optimal scenario): +Podemos usar `Map` para almacenar resultados: ```js run // 📁 cache.js let cache = new Map(); -// calculate and remember the result +// calcular y recordar el resultado function process(obj) { if (!cache.has(obj)) { - let result = /* calculations of the result for */ obj; + let result = /* cálculo de resultado para */ obj; cache.set(obj, result); return result; @@ -191,26 +191,26 @@ function process(obj) { } *!* -// Now we use process() in another file: +// Ahora usamos process() en otro archivo: */!* // 📁 main.js -let obj = {/* let's say we have an object */}; +let obj = {/* digamos que tenemos un objeto */}; -let result1 = process(obj); // calculated +let result1 = process(obj); // calculado -// ...later, from another place of the code... -let result2 = process(obj); // remembered result taken from cache +// ...después, en otro lugar del código... +let result2 = process(obj); // resultado recordado tomado de la memoria caché -// ...later, when the object is not needed any more: +// ...después, cuando el objeto no se necesita más: obj = null; -alert(cache.size); // 1 (Ouch! The object is still in cache, taking memory!) +alert(cache.size); // 1 (¡Ouch! ¡El objeto todavía está en caché, tomando memoria!) ``` -For multiple calls of `process(obj)` with the same object, it only calculates the result the first time, and then just takes it from `cache`. The downside is that we need to clean `cache` when the object is not needed any more. +Para múltiples llamadas de `proceso (obj)` con el mismo objeto, solo calcula el resultado la primera vez, y luego lo toma de `caché`. La desventaja es que necesitamos limpiar el 'caché' cuando el objeto ya no es necesario. -If we replace `Map` with `WeakMap`, then this problem disappears. The cached result will be removed from memory automatically after the object gets garbage collected. +Si reemplazamos `Map` por `WeakMap`, este problema desaparece: el resultado en caché se eliminará de la memoria automáticamente después de que el objeto se recolecte. ```js run // 📁 cache.js @@ -218,10 +218,10 @@ If we replace `Map` with `WeakMap`, then this problem disappears. The cached res let cache = new WeakMap(); */!* -// calculate and remember the result +// calcular y recordad el resultado function process(obj) { if (!cache.has(obj)) { - let result = /* calculate the result for */ obj; + let result = /* calcular el resultado para */ obj; cache.set(obj, result); return result; @@ -231,30 +231,30 @@ function process(obj) { } // 📁 main.js -let obj = {/* some object */}; +let obj = {/* algún objeto */}; let result1 = process(obj); let result2 = process(obj); -// ...later, when the object is not needed any more: +// ...después, cuando el objeto no se necesitará más: obj = null; -// Can't get cache.size, as it's a WeakMap, -// but it's 0 or soon be 0 -// When obj gets garbage collected, cached data will be removed as well +// No se puede obtener cache.size, ya que es un WeakMap, +// pero es 0 o pronto será 0 +// Cuando obj se recolecte como basura, los datos en caché también se eliminarán ``` ## WeakSet -[`WeakSet`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet) behaves similarly: +[`WeakSet`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet) se comporta de manera similar: -- It is analogous to `Set`, but we may only add objects to `WeakSet` (not primitives). -- An object exists in the set while it is reachable from somewhere else. -- Like `Set`, it supports [`add`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Weakset/add), [`has`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Weakset/has) and [`delete`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Weakset/delete), but not `size`, `keys()` and no iterations. +- Es análogo a `Set`, pero en `WeakSet` solo podemos agregar objetos (no tipos primitivos). +- Un objeto en la colección existe mientras sea accesible desde otro lugar. +- Al igual que `Set`, admite[`add`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Weakset/add), [`has`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Weakset/has) y [`delete`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Weakset/delete), pero no `size`,` keys()` ni iteraciones. -Being "weak", it also serves as additional storage. But not for arbitrary data, rather for "yes/no" facts. A membership in `WeakSet` may mean something about the object. +Al ser "débil", también sirve como almacenamiento adicional. Pero no para datos arbitrarios, sino para hechos "sí/no". Una membresía en `WeakSet` puede significar algo sobre el objeto. -For instance, we can add users to `WeakSet` to keep track of those who visited our site: +Por ejemplo, podemos agregar usuarios a `WeakSet` para realizar un seguimiento de los que visitaron nuestro sitio: ```js run let visitedSet = new WeakSet(); @@ -263,33 +263,33 @@ let john = { name: "John" }; let pete = { name: "Pete" }; let mary = { name: "Mary" }; -visitedSet.add(john); // John visited us -visitedSet.add(pete); // Then Pete -visitedSet.add(john); // John again +visitedSet.add(john); // John nos visita +visitedSet.add(pete); // luego Pete +visitedSet.add(john); // John otra vez -// visitedSet has 2 users now +// visitedSet tiene 2 usuarios ahora -// check if John visited? +// comprobar si John nos visitó? alert(visitedSet.has(john)); // true -// check if Mary visited? +// comprobar si Mary nos visitó? alert(visitedSet.has(mary)); // false john = null; -// visitedSet will be cleaned automatically +// visitedSet se limpiará automáticamente ``` -The most notable limitation of `WeakMap` and `WeakSet` is the absence of iterations, and the inability to get all current content. That may appear inconvenient, but does not prevent `WeakMap/WeakSet` from doing their main job -- be an "additional" storage of data for objects which are stored/managed at another place. + La limitación más notable de `WeakMap` y` WeakSet` es la ausencia de iteraciones y la imposibilidad de obtener todo el contenido actual. Esto puede parecer inconveniente, pero no impide que `WeakMap / WeakSet` haga su trabajo principal: ser un almacenamiento "adicional" de datos para objetos que se almacenan/administran en otro lugar. -## Summary +## Resumen -[`WeakMap`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap) is `Map`-like collection that allows only objects as keys and removes them together with associated value once they become inaccessible by other means. +[`WeakMap`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap) es una colección similar a `Map` que permite solo objetos como propiedades y los elimina junto con el valor asociado una vez que se vuelven inaccesibles por otros medios. -[`WeakSet`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet) is `Set`-like collection that stores only objects and removes them once they become inaccessible by other means. +[`WeakSet`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet) es una colección tipo `Set` que almacena solo objetos y los elimina una vez que se vuelven inaccesibles por otros medios. -Their main advantages are that they have weak reference to objects, so they can easily be removed by garbage collector. +Sus principales ventajas son que tienen referencias débiles a los objetos, así pueden ser fácilmente eliminados por el recolector de basura. -That comes at the cost of not having support for `clear`, `size`, `keys`, `values`... +Esto viene al costo de no tener soporte para `clear`, `size`, `keys`, `values`... -`WeakMap` and `WeakSet` are used as "secondary" data structures in addition to the "primary" object storage. Once the object is removed from the primary storage, if it is only found as the key of `WeakMap` or in a `WeakSet`, it will be cleaned up automatically. +`WeakMap` y` WeakSet` se utilizan como estructuras de dato "secundarias" además del almacenamiento de objetos "principal". Una vez que el objeto se elimina del almacenamiento principal, si solo se encuentra como la clave de `WeakMap` o en un` WeakSet`, se limpiará automáticamente. diff --git a/1-js/05-data-types/09-keys-values-entries/01-sum-salaries/_js.view/test.js b/1-js/05-data-types/09-keys-values-entries/01-sum-salaries/_js.view/test.js index 684b0894a..abc4aae91 100644 --- a/1-js/05-data-types/09-keys-values-entries/01-sum-salaries/_js.view/test.js +++ b/1-js/05-data-types/09-keys-values-entries/01-sum-salaries/_js.view/test.js @@ -1,5 +1,5 @@ describe("sumSalaries", function() { - it("returns sum of salaries", function() { + it("devuelve suma de salarios", function() { let salaries = { "John": 100, "Pete": 300, @@ -9,7 +9,7 @@ describe("sumSalaries", function() { assert.equal( sumSalaries(salaries), 650 ); }); - it("returns 0 for the empty object", function() { + it("devuelve 0 para el objeto vacío", function() { assert.strictEqual( sumSalaries({}), 0); }); }); \ No newline at end of file diff --git a/1-js/05-data-types/09-keys-values-entries/01-sum-salaries/solution.md b/1-js/05-data-types/09-keys-values-entries/01-sum-salaries/solution.md index 27a7b418a..341959a8a 100644 --- a/1-js/05-data-types/09-keys-values-entries/01-sum-salaries/solution.md +++ b/1-js/05-data-types/09-keys-values-entries/01-sum-salaries/solution.md @@ -17,12 +17,12 @@ let salaries = { alert( sumSalaries(salaries) ); // 650 ``` -Or, optionally, we could also get the sum using `Object.values` and `reduce`: +Otra opción, también podemos obtener la suma utilizando `Object.values` y `reduce`: ```js -// reduce loops over array of salaries, -// adding them up -// and returns the result +// reduce recorre el array de salarios, +// sumándolos +// y devuelve el resultado function sumSalaries(salaries) { return Object.values(salaries).reduce((a, b) => a + b, 0) // 650 } diff --git a/1-js/05-data-types/09-keys-values-entries/01-sum-salaries/task.md b/1-js/05-data-types/09-keys-values-entries/01-sum-salaries/task.md index 211357d03..8c26c102e 100644 --- a/1-js/05-data-types/09-keys-values-entries/01-sum-salaries/task.md +++ b/1-js/05-data-types/09-keys-values-entries/01-sum-salaries/task.md @@ -2,15 +2,15 @@ importance: 5 --- -# Sum the properties +# Suma las propiedades -There is a `salaries` object with arbitrary number of salaries. +Hay un objeto `salaries` con un número arbitrario de salarios. -Write the function `sumSalaries(salaries)` that returns the sum of all salaries using `Object.values` and the `for..of` loop. +Escriba la función `sumSalaries(salaries)` que devuelva la suma de todos los salarios utilizando `Object.values` y el bucle `for..of`. -If `salaries` is empty, then the result must be `0`. +Si `salaries` está vacío, entonces el resultado debe ser `0`. -For instance: +Por ejemplo: ```js let salaries = { diff --git a/1-js/05-data-types/09-keys-values-entries/02-count-properties/_js.view/test.js b/1-js/05-data-types/09-keys-values-entries/02-count-properties/_js.view/test.js index e568c3205..ea32e9085 100644 --- a/1-js/05-data-types/09-keys-values-entries/02-count-properties/_js.view/test.js +++ b/1-js/05-data-types/09-keys-values-entries/02-count-properties/_js.view/test.js @@ -1,13 +1,13 @@ describe("count", function() { - it("counts the number of properties", function() { + it("cuenta el número de propiedades", function() { assert.equal( count({a: 1, b: 2}), 2 ); }); - it("returns 0 for an empty object", function() { + it("devuelve 0 para un objeto vacío", function() { assert.equal( count({}), 0 ); }); - it("ignores symbolic properties", function() { + it("ignora propiedades simbólicas", function() { assert.equal( count({ [Symbol('id')]: 1 }), 0 ); }); }); \ No newline at end of file diff --git a/1-js/05-data-types/09-keys-values-entries/02-count-properties/task.md b/1-js/05-data-types/09-keys-values-entries/02-count-properties/task.md index d7aebb1fa..a06e5141f 100644 --- a/1-js/05-data-types/09-keys-values-entries/02-count-properties/task.md +++ b/1-js/05-data-types/09-keys-values-entries/02-count-properties/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# Count properties +# Contar propiedades -Write a function `count(obj)` that returns the number of properties in the object: +Escriba una función `count(obj)` que devuelva el número de propiedades en el objeto: ```js let user = { @@ -15,7 +15,7 @@ let user = { alert( count(user) ); // 2 ``` -Try to make the code as short as possible. +Trate de hacer el código lo más corto posible. -P.S. Ignore symbolic properties, count only "regular" ones. +PD: Ignore propiedades simbólicas, solamente cuente las propiedades "regulares". diff --git a/1-js/05-data-types/09-keys-values-entries/article.md b/1-js/05-data-types/09-keys-values-entries/article.md index bef678f53..ba3ebdcaf 100644 --- a/1-js/05-data-types/09-keys-values-entries/article.md +++ b/1-js/05-data-types/09-keys-values-entries/article.md @@ -1,42 +1,42 @@ # Object.keys, values, entries -Let's step away from the individual data structures and talk about the iterations over them. +Alejémonos de las estructuras de datos individuales y hablemos sobre las iteraciones sobre ellas. -In the previous chapter we saw methods `map.keys()`, `map.values()`, `map.entries()`. +En el capítulo anterior vimos métodos `map.keys()`, `map.values()`, `map.entries()`. -These methods are generic, there is a common agreement to use them for data structures. If we ever create a data structure of our own, we should implement them too. +Estos métodos son genéricos, existe un acuerdo común para usarlos para estructuras de datos. Si alguna vez creamos una estructura de datos propia, también deberíamos implementarla. -They are supported for: +Son compatibles para: - `Map` - `Set` - `Array` -Plain objects also support similar methods, but the syntax is a bit different. +Los objetos simples también admiten métodos similares, pero la sintaxis es un poco diferente. ## Object.keys, values, entries -For plain objects, the following methods are available: +Para objetos simples, los siguientes métodos están disponibles: -- [Object.keys(obj)](mdn:js/Object/keys) -- returns an array of keys. -- [Object.values(obj)](mdn:js/Object/values) -- returns an array of values. -- [Object.entries(obj)](mdn:js/Object/entries) -- returns an array of `[key, value]` pairs. +- [Object.keys(obj)](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/keys) -- devuelve un array de propiedades. +- [Object.values(obj)](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/values) -- devuelve un array de valores. +- [Object.entries(obj)](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/entries) -- devuelve un array de pares `[propiedad, valor]`. -Please note the distinctions (compared to map for example): +Observe las diferencias (en comparación con Map, por ejemplo): -| | Map | Object | +| | Map | Objeto | |-------------|------------------|--------------| -| Call syntax | `map.keys()` | `Object.keys(obj)`, but not `obj.keys()` | -| Returns | iterable | "real" Array | +| Sintaxis de llamada | `map.keys()` | `Object.keys(obj)`, pero no `obj.keys()` | +| Devuelve | iterable | un Array "real" | -The first difference is that we have to call `Object.keys(obj)`, and not `obj.keys()`. +La primera diferencia es que tenemos que llamar `Object.keys(obj)`, y no `obj.keys()`. -Why so? The main reason is flexibility. Remember, objects are a base of all complex structures in JavaScript. So we may have an object of our own like `data` that implements its own `data.values()` method. And we still can call `Object.values(data)` on it. +¿Por qué? La razón principal es la flexibilidad. Recuerda que los objetos son la base de todas las estructuras complejas en JavaScript. Entonces, podemos tener un objeto propio como `data` que implementa su propio método `data.values ()`: todavía podemos llamar a `Object.values(data)` en él. -The second difference is that `Object.*` methods return "real" array objects, not just an iterable. That's mainly for historical reasons. +La segunda diferencia es que los métodos `Object.*` devuelven objetos array "reales", no solo un iterable. Eso es principalmente por razones históricas. -For instance: +Por ejemplo: ```js let user = { @@ -49,7 +49,7 @@ let user = { - `Object.values(user) = ["John", 30]` - `Object.entries(user) = [ ["name","John"], ["age",30] ]` -Here's an example of using `Object.values` to loop over property values: +Aquí hay un ejemplo del uso de `Object.values` para recorrer los valores de propiedad: ```js run let user = { @@ -57,30 +57,30 @@ let user = { age: 30 }; -// loop over values +// bucle sobre los valores for (let value of Object.values(user)) { - alert(value); // John, then 30 + alert(value); // John, luego 30 } ``` -```warn header="Object.keys/values/entries ignore symbolic properties" -Just like a `for..in` loop, these methods ignore properties that use `Symbol(...)` as keys. +```warn header="Object.keys/values/entries ignoran propiedades simbólicas" +Al igual que un bucle `for..in`, estos métodos ignoran propiedades que utilizan `Symbol(...)` como nombre de propiedades. -Usually that's convenient. But if we want symbolic keys too, then there's a separate method [Object.getOwnPropertySymbols](mdn:js/Object/getOwnPropertySymbols) that returns an array of only symbolic keys. Also, there exist a method [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) that returns *all* keys. +Normalmente, esto es conveniente. Pero si también queremos propiedades simbólicas, entonces hay un método aparte [Object.getOwnPropertySymbols](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/getOwnPropertySymbols) que devuelve un array de únicamente propiedades simbólicas. También existe un método [Reflect.ownKeys(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/ownKeys) que devuelve *todas* las propiedades. ``` -## Transforming objects +## Transformando objetos -Objects lack many methods that exist for arrays, e.g. `map`, `filter` and others. +Los objetos carecen de muchos métodos que existen para los arrays, tales como `map`,` filter` y otros. -If we'd like to apply them, then we can use `Object.entries` followed by `Object.fromEntries`: +Si queremos aplicarlos, entonces podemos usar `Object.entries` seguido de `Object.fromEntries`: -1. Use `Object.entries(obj)` to get an array of key/value pairs from `obj`. -2. Use array methods on that array, e.g. `map`, to transform these key/value pairs. -3. Use `Object.fromEntries(array)` on the resulting array to turn it back into an object. +1. Use `Object.entries(obj)` para obtener un array de pares clave/valor de `obj`. +2. Use métodos de array en ese array, por ejemplo `map` para transformar estos pares clave/valor. +3. Use `Object.fromEntries(array)` en el array resultante para convertirlo nuevamente en un objeto. -For example, we have an object with prices, and would like to double them: +Por ejemplo, tenemos un objeto con precios y queremos duplicarlos: ```js run let prices = { @@ -91,13 +91,13 @@ let prices = { *!* let doublePrices = Object.fromEntries( - // convert prices to array, map each key/value pair into another pair - // and then fromEntries gives back the object - Object.entries(prices).map(entry => [entry[0], entry[1] * 2]) + // convertir precios a array, map - cada par clave/valor en otro par + // y luego fromEntries nos devuelve el objeto + Object.entries(prices).map(([key, value]) => [key, value * 2]) ); */!* alert(doublePrices.meat); // 8 ``` -It may look difficult at first sight, but becomes easy to understand after you use it once or twice. We can make powerful chains of transforms this way. +Puede parecer difícil a primera vista, pero se vuelve fácil de entender después de usarlo una o dos veces. Podemos hacer poderosas cadenas de transformaciones de esta manera. diff --git a/1-js/05-data-types/10-destructuring-assignment/1-destruct-user/task.md b/1-js/05-data-types/10-destructuring-assignment/1-destruct-user/task.md index b68db5c59..e0c5506e9 100644 --- a/1-js/05-data-types/10-destructuring-assignment/1-destruct-user/task.md +++ b/1-js/05-data-types/10-destructuring-assignment/1-destruct-user/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# Destructuring assignment +# Asignación desestructurante -We have an object: +Tenemos un objeto: ```js let user = { @@ -13,18 +13,18 @@ let user = { }; ``` -Write the destructuring assignment that reads: +Escriba la asignación desestructurante que asigne las propiedades: -- `name` property into the variable `name`. -- `years` property into the variable `age`. -- `isAdmin` property into the variable `isAdmin` (false, if no such property) +- `name` en la variable `name`. +- `years` en la variable `age`. +- `isAdmin` en la variable `isAdmin` (false, si no existe tal propiedad) -Here's an example of the values after your assignment: +Este es un ejemplo de los valores después de su asignación: ```js let user = { name: "John", years: 30 }; -// your code to the left side: +// tu código al lado izquierdo: // ... = user alert( name ); // John diff --git a/1-js/05-data-types/10-destructuring-assignment/6-max-salary/_js.view/test.js b/1-js/05-data-types/10-destructuring-assignment/6-max-salary/_js.view/test.js index e1da754ba..9e596ec45 100644 --- a/1-js/05-data-types/10-destructuring-assignment/6-max-salary/_js.view/test.js +++ b/1-js/05-data-types/10-destructuring-assignment/6-max-salary/_js.view/test.js @@ -1,5 +1,5 @@ describe("topSalary", function() { - it("returns top-paid person", function() { + it("devuelvo persona mejor pagada", function() { let salaries = { "John": 100, "Pete": 300, @@ -9,7 +9,7 @@ describe("topSalary", function() { assert.equal( topSalary(salaries), "Pete" ); }); - it("returns null for the empty object", function() { + it("devuelve null para objeto vacío", function() { assert.isNull( topSalary({}) ); }); }); \ No newline at end of file diff --git a/1-js/05-data-types/10-destructuring-assignment/6-max-salary/task.md b/1-js/05-data-types/10-destructuring-assignment/6-max-salary/task.md index 9f33de089..fe50ab236 100644 --- a/1-js/05-data-types/10-destructuring-assignment/6-max-salary/task.md +++ b/1-js/05-data-types/10-destructuring-assignment/6-max-salary/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# The maximal salary +# El salario máximo -There is a `salaries` object: +Hay un objeto `salaries`: ```js let salaries = { @@ -14,9 +14,9 @@ let salaries = { }; ``` -Create the function `topSalary(salaries)` that returns the name of the top-paid person. +Crear la función `topSalary(salaries)` que devuelva el nombre de la persona mejor pagada. -- If `salaries` is empty, it should return `null`. -- If there are multiple top-paid persons, return any of them. +- Si `salaries` es vacío, debe devolver `null`. +- Si hay varias personas con la mejor paga, devolver cualquiera de ellas. -P.S. Use `Object.entries` and destructuring to iterate over key/value pairs. +PD: Utilice `Object.entries` y desestructuración para iterar sobre pares de claves/valores. diff --git a/1-js/05-data-types/10-destructuring-assignment/article.md b/1-js/05-data-types/10-destructuring-assignment/article.md index c8693b2c4..ca141a835 100644 --- a/1-js/05-data-types/10-destructuring-assignment/article.md +++ b/1-js/05-data-types/10-destructuring-assignment/article.md @@ -1,28 +1,28 @@ -# Destructuring assignment +# Asignación desestructurante -The two most used data structures in JavaScript are `Object` and `Array`. +Las dos estructuras de datos más usadas en JavaScript son `Object` y `Array`. -- Objects allow us to create a single entity that stores data items by key. -- Arrays allow us to gather data items into an ordered list. +- Los objetos nos permiten crear una simple entidad que almacena items con una clave cada uno. +- los arrays nos permiten reunir items en una lista ordenada. -However, when we pass these to a function, we may not need all of it. The function might only require certain elements or properties. +Pero cuando los pasamos a una función, tal vez no necesitemos el objeto o array completo. La función podría requerir solamente ciertos elementos o propiedades. -*Destructuring assignment* is a special syntax that allows us to "unpack" arrays or objects into a bunch of variables, as sometimes that's more convenient. +La *asignación desestructurante* es una sintaxis especial que nos permite "desempaquetar" arrays u objetos en un manojo de variables, porque a veces es lo más conveniente. -Destructuring also works well with complex functions that have a lot of parameters, default values, and so on. Soon we'll see that. +La desestructuración también funciona bien con funciones complejas que tienen muchos argumentos, valores por defecto, etcétera. Pronto lo veremos. -## Array destructuring +## Desestructuración de Arrays -Here's an example of how an array is destructured into variables: +Un ejemplo de cómo el array es desestructurado en variables: ```js -// we have an array with a name and surname +// tenemos un array con un nombre y apellido let arr = ["John", "Smith"] *!* -// destructuring assignment -// sets firstName = arr[0] -// and surname = arr[1] +// asignación desestructurante +// fija firstName = arr[0] +// y surname = arr[1] let [firstName, surname] = arr; */!* @@ -30,9 +30,9 @@ alert(firstName); // John alert(surname); // Smith ``` -Now we can work with variables instead of array members. +Ahora podemos trabajar con variables en lugar de miembros de array. -It looks great when combined with `split` or other array-returning methods: +Se ve genial cuando se combina con `split` u otro método que devuelva un array: ```js run let [firstName, surname] = "John Smith".split(' '); @@ -40,12 +40,12 @@ alert(firstName); // John alert(surname); // Smith ``` -As you can see, the syntax is simple. There are several peculiar details though. Let's see more examples to understand it better. +Como puedes ver, la sintaxis es simple. Aunque hay varios detalles peculiares. Veamos más ejemplos para entenderlo mejor. -````smart header="\"Destructuring\" does not mean \"destructive\"." -It's called "destructuring assignment," because it "destructurizes" by copying items into variables. However, the array itself is not modified. +````smart header="\"Desestructuración\" no significa \"destructivo\"." +Se llama "asignación desestructurante" porque "desestructura" al copiar elementos dentro de variables. Sin embargo, el array en sí no es modificado. -It's just a shorter way to write: +Es sólo una manera más simple de escribir: ```js // let [firstName, surname] = arr; let firstName = arr[0]; @@ -53,37 +53,37 @@ let surname = arr[1]; ``` ```` -````smart header="Ignore elements using commas" -Unwanted elements of the array can also be thrown away via an extra comma: +````smart header="Ignorar elementos utilizando comas" +Los elementos no deseados de un array también pueden ser descartados por medio de una coma extra: ```js run *!* -// second element is not needed +// segundo elemento no es necesario let [firstName, , title] = ["Julius", "Caesar", "Consul", "of the Roman Republic"]; */!* alert( title ); // Consul ``` -In the code above, the second element of the array is skipped, the third one is assigned to `title`, and the rest of the array items are also skipped (as there are no variables for them). +En el código de arriba, el segundo elemento del array es omitido, el tercero es asignado a `title`, y el resto de los elementos del array también se omiten (debido a que no hay variables para ellos). ```` -````smart header="Works with any iterable on the right-side" +````smart header="Funciona con cualquier iterable en el lado derecho" -...Actually, we can use it with any iterable, not only arrays: +...Incluso lo podemos usar con cualquier iterable, no sólo arrays: ```js let [a, b, c] = "abc"; // ["a", "b", "c"] let [one, two, three] = new Set([1, 2, 3]); ``` -That works, because internally a destructuring assignment works by iterating over the right value. It's a kind of syntax sugar for calling `for..of` over the value to the right of `=` and assigning the values. +Esto funciona, porque internamente una desestructuración trabaja iterando sobre el valor de la derecha. Es una clase de azúcar sintáctica para llamar `for..of` sobre el valor a la derecha del `=` y asignar esos valores. ```` -````smart header="Assign to anything on the left-side" -We can use any "assignables" on the left side. +````smart header="Asignar a cualquier cosa en el lado izquierdo" +Podemos usar cualquier "asignable" en el lado izquierdo. -For instance, an object property: +Por ejemplo, una propiedad de objeto: ```js run let user = {}; [user.name, user.surname] = "John Smith".split(' '); @@ -94,10 +94,10 @@ alert(user.surname); // Smith ```` -````smart header="Looping with .entries()" -In the previous chapter, we saw the [Object.entries(obj)](mdn:js/Object/entries) method. +````smart header="Bucle con .entries()" +En el capítulo anterior vimos el método [Object.entries(obj)](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/entries). -We can use it with destructuring to loop over the keys-and-values of an object: +Podemos usarlo con la desestructuración para recorrer claves-y-valores de un objeto: ```js run let user = { @@ -105,15 +105,15 @@ let user = { age: 30 }; -// loop over the keys-and-values +// recorrer claves-y-valores *!* for (let [key, value] of Object.entries(user)) { */!* - alert(`${key}:${value}`); // name:John, then age:30 + alert(`${key}:${value}`); // name:John, luego age:30 } ``` -The similar code for a `Map` is simpler, as it's iterable: +El código equivalente para `Map` es más simple, porque es iterable: ```js run let user = new Map(); @@ -121,73 +121,73 @@ user.set("name", "John"); user.set("age", "30"); *!* -// Map iterates as [key, value] pairs, very convenient for destructuring +// Map itera como pares [key, value], muy conveniente para desestructurar for (let [key, value] of user) { */!* - alert(`${key}:${value}`); // name:John, then age:30 + alert(`${key}:${value}`); // name:John, luego age:30 } ``` ```` -````smart header="Swap variables trick" -There's a well-known trick for swapping values of two variables using a destructuring assignment: +````smart header="Truco para intercambiar variables" +Hay un conocido truco para intercambiar los valores de dos variables usando asignación desestructurante: ```js run let guest = "Jane"; let admin = "Pete"; -// Let's swap the values: make guest=Pete, admin=Jane +// Intercambiemos valores: hagamos guest=Pete, admin=Jane *!* [guest, admin] = [admin, guest]; */!* -alert(`${guest} ${admin}`); // Pete Jane (successfully swapped!) +alert(`${guest} ${admin}`); // Pete Jane (¡intercambiados con éxito!) ``` -Here we create a temporary array of two variables and immediately destructure it in swapped order. +Aquí creamos un array temporal de dos variables e inmediatamente lo desestructuramos con el orden cambiado. -We can swap more than two variables this way. +Podemos intercambiar más de dos variables de este modo. ```` -### The rest '...' +### El resto '...' -Usually, if the array is longer than the list at the left, the "extra" items are omitted. +En general, si el array es mayor que la lista de la izquierda, los ítems extras son omitidos. -For example, here only two items are taken, and the rest is just ignored: +Por ejemplo, aquí solo dos items son tomados, el resto simplemente es ignorado: ```js run let [name1, name2] = ["Julius", "Caesar", "Consul", "of the Roman Republic"]; alert(name1); // Julius alert(name2); // Caesar -// Further items aren't assigned anywhere +// items posteriores no serán asignados a ningún lugar ``` -If we'd like also to gather all that follows -- we can add one more parameter that gets "the rest" using three dots `"..."`: +si queremos también obtener todo lo que sigue, podemos agregarle un parámetro que obtiene "el resto" usando puntos suspensivos "..."`: ```js run let [name1, name2, *!*...rest*/!*] = ["Julius", "Caesar", *!*"Consul", "of the Roman Republic"*/!*]; *!* -// rest is an array of items, starting from the 3rd one +// `rest` es un array de ítems, comenzando en este caso por el tercero. alert(rest[0]); // Consul alert(rest[1]); // of the Roman Republic alert(rest.length); // 2 */!* ``` -The value of `rest` is the array of the remaining array elements. +El valor de `rest` es un array con los elementos restantes del array original. -We can use any other variable name in place of `rest`, just make sure it has three dots before it and goes last in the destructuring assignment. +Podemos usar cualquier otro nombre de variable en lugar de `rest`, sólo hay que asegurar que tenga tres puntos que lo antecedan y que esté último en la asignación desestructurante. ```js run let [name1, name2, *!*...titles*/!*] = ["Julius", "Caesar", "Consul", "of the Roman Republic"]; -// now titles = ["Consul", "of the Roman Republic"] +// ahora titles = ["Consul", "of the Roman Republic"] ``` -### Default values +### Valores predeterminados -If the array is shorter than the list of variables on the left, there will be no errors. Absent values are considered undefined: +Si el array es más corto que la lista de variables de la izquierda, no habrá errores. Los valores ausentes quedan `undefined`: ```js run *!* @@ -198,45 +198,45 @@ alert(firstName); // undefined alert(surname); // undefined ``` -If we want a "default" value to replace the missing one, we can provide it using `=`: +Si queremos un valor "predeterminado" para reemplazar el valor faltante, podemos proporcionarlo utilizando `=`: ```js run *!* -// default values +// valores predeterminados let [name = "Guest", surname = "Anonymous"] = ["Julius"]; */!* -alert(name); // Julius (from array) -alert(surname); // Anonymous (default used) +alert(name); // Julius (desde array) +alert(surname); // Anonymous (predeterminado utilizado) ``` -Default values can be more complex expressions or even function calls. They are evaluated only if the value is not provided. +Los valores predeterminados pueden ser expresiones más complejas e incluso llamadas a función, que serán evaluadas sólo si el valor no ha sido proporcionado. -For instance, here we use the `prompt` function for two defaults: +Por ejemplo, aquí utilizamos la función `prompt` para dos valores predeterminados. ```js run -// runs only prompt for surname -let [name = prompt('name?'), surname = prompt('surname?')] = ["Julius"]; +// sólo ejecuta la captura para surname +let [name = prompt('nombre?'), surname = prompt('apellido?')] = ["Julius"]; -alert(name); // Julius (from array) -alert(surname); // whatever prompt gets +alert(name); // Julius (desde array) +alert(surname); // lo que reciba la captura ``` -Please note: the `prompt` will run only for the missing value (`surname`). +Observa que el `prompt` se ejecuta solamente para el valor faltante (`surname`). -## Object destructuring +## Desestructuración de objetos -The destructuring assignment also works with objects. +La asignación desestructurante también funciona con objetos. -The basic syntax is: +La sintaxis básica es: ```js let {var1, var2} = {var1:…, var2:…} ``` -We should have an existing object on the right side, that we want to split into variables. The left side contains an object-like "pattern" for corresponding properties. In the simplest case, that's a list of variable names in `{...}`. +Debemos tener un símil-objeto en el lado derecho, el que queremos separar en variables. El lado izquierdo contiene un símil-objeto "pattern" para sus propiedades correspondientes. En el caso más simple, es la lista de nombres de variables en `{...}`. -For instance: +Por ejemplo: ```js run let options = { @@ -254,18 +254,18 @@ alert(width); // 100 alert(height); // 200 ``` -Properties `options.title`, `options.width` and `options.height` are assigned to the corresponding variables. +Las propiedades `options.title`, `options.width` y `options.height` son asignadas a las variables correspondientes. -The order does not matter. This works too: +No importa el orden sino los nombres. Esto también funciona: ```js -// changed the order in let {...} +// cambiado el orden en let {...} let {height, width, title} = { title: "Menu", height: 200, width: 100 } ``` -The pattern on the left side may be more complex and specify the mapping between properties and variables. +El patrón de la izquierda puede ser más complejo y especificar el mapeo entre propiedades y variables. -If we want to assign a property to a variable with another name, for instance, make `options.width` go into the variable named `w`, then we can set the variable name using a colon: +Si queremos asignar una propiedad a una variable con otro nombre, por ejemplo que `options.width` vaya en la variable llamada `w`, lo podemos establecer usando dos puntos: ```js run let options = { @@ -275,7 +275,7 @@ let options = { }; *!* -// { sourceProperty: targetVariable } +// { propiedadOrigen: variableObjetivo } let {width: w, height: h, title} = options; */!* @@ -288,9 +288,9 @@ alert(w); // 100 alert(h); // 200 ``` -The colon shows "what : goes where". In the example above the property `width` goes to `w`, property `height` goes to `h`, and `title` is assigned to the same name. +Los dos puntos muestran "qué : va dónde". En el ejemplo de arriba la propiedad `width` va a `w`, `height` va a `h`, y `title` es asignado al mismo nombre. -For potentially missing properties we can set default values using `"="`, like this: +Para propiedades potencialmente faltantes podemos establecer valores predeterminados utilizando `"="`, de esta manera: ```js run let options = { @@ -306,9 +306,9 @@ alert(width); // 100 alert(height); // 200 ``` -Just like with arrays or function parameters, default values can be any expressions or even function calls. They will be evaluated if the value is not provided. +Al igual que con arrays o argumentos de función, los valores predeterminados pueden ser cualquier expresión e incluso llamados a función, las que serán evaluadas si el valor no ha sido proporcionado. -In the code below `prompt` asks for `width`, but not for `title`: +En el código de abajo `prompt` pregunta por `width`, pero no por `title`: ```js run let options = { @@ -316,14 +316,14 @@ let options = { }; *!* -let {width = prompt("width?"), title = prompt("title?")} = options; +let {width = prompt("¿ancho?"), title = prompt("¿título?")} = options; */!* alert(title); // Menu -alert(width); // (whatever the result of prompt is) +alert(width); // (lo que sea el resultado de la captura) ``` -We also can combine both the colon and equality: +También podemos combinar ambos, los dos puntos y la igualdad: ```js run let options = { @@ -339,7 +339,7 @@ alert(w); // 100 alert(h); // 200 ``` -If we have a complex object with many properties, we can extract only what we need: +Si tenemos un objeto complejo con muchas propiedades, podemos extraer solamente las que necesitamos: ```js run let options = { @@ -348,19 +348,19 @@ let options = { height: 200 }; -// only extract title as a variable +// sólo extrae título como variable let { title } = options; alert(title); // Menu ``` -### The rest pattern "..." +### El patrón resto "..." -What if the object has more properties than we have variables? Can we take some and then assign the "rest" somewhere? +¿Qué pasa si el objeto tiene más propiedades que las variables que tenemos? ¿Podemos tomar algunas y luego asignar el "resto" en alguna parte? -We can use the rest pattern, just like we did with arrays. It's not supported by some older browsers (IE, use Babel to polyfill it), but works in modern ones. +Podemos usar el patrón resto de la misma forma que lo usamos con arrays. Esto no es soportado en algunos navegadores antiguos (para IE, use el polyfill Babel), pero funciona en los navegadores modernos. -It looks like this: +Se ve así: ```js run let options = { @@ -370,57 +370,57 @@ let options = { }; *!* -// title = property named title -// rest = object with the rest of properties +// title = propiedad llamada title +// rest = objeto con el resto de las propiedades let {title, ...rest} = options; */!* -// now title="Menu", rest={height: 200, width: 100} +// ahora title="Menu", rest={height: 200, width: 100} alert(rest.height); // 200 alert(rest.width); // 100 ``` -````smart header="Gotcha if there's no `let`" -In the examples above variables were declared right in the assignment: `let {…} = {…}`. Of course, we could use existing variables too, without `let`. But there's a catch. +````smart header="La trampa si no hay `let`" +En los ejemplos de arriba, las variables fueron declaradas en la asignación: `let {…} = {…}`. Por supuesto que también podemos usar variables existentes, sin `let`. Pero hay una trampa. -This won't work: +Esto no funcionará: ```js run let title, width, height; -// error in this line +// error en esta línea {title, width, height} = {title: "Menu", width: 200, height: 100}; ``` -The problem is that JavaScript treats `{...}` in the main code flow (not inside another expression) as a code block. Such code blocks can be used to group statements, like this: +El problema es que JavaScript trata al `{...}` como un bloque de código en el flujo principal de código (no dentro de otra expresión). Estos bloques de código pueden ser usados para agrupar sentencias, de esta manera: ```js run { - // a code block - let message = "Hello"; + // una bloque de código + let message = "Hola"; // ... alert( message ); } ``` -So here JavaScript assumes that we have a code block, that's why there's an error. We want destructuring instead. +Aquí JavaScript supone que tenemos un bloque de código, es por eso que hay un error. Nosotros en cambio queremos desestructuración. -To show JavaScript that it's not a code block, we can wrap the expression in parentheses `(...)`: +Para mostrarle a JavaScript que no es un bloque de código, podemos rodear la expresión entre paréntesis `(...)`: ```js run let title, width, height; -// okay now +// ahora está bien *!*(*/!*{title, width, height} = {title: "Menu", width: 200, height: 100}*!*)*/!*; alert( title ); // Menu ``` ```` -## Nested destructuring +## Desestructuración anidada -If an object or an array contains other nested objects and arrays, we can use more complex left-side patterns to extract deeper portions. +Si un objeto o array contiene otros objetos y arrays anidados, podemos utilizar patrones más complejos en el lado izquierdo para extraer porciones más profundas. -In the code below `options` has another object in the property `size` and an array in the property `items`. The pattern on the left side of the assignment has the same structure to extract values from them: +En el código de abajo `options` tiene otro objeto en la propiedad `size` y un array en la propiedad `items`. El patrón en el lado izquierdo de la asignación tiene la misma estructura para extraer valores de ellos: ```js run let options = { @@ -432,14 +432,14 @@ let options = { extra: true }; -// destructuring assignment split in multiple lines for clarity +// la asignación desestructurante fue dividida en varias líneas para mayor claridad let { - size: { // put size here + size: { // colocar tamaño aquí width, height }, - items: [item1, item2], // assign items here - title = "Menu" // not present in the object (default value is used) + items: [item1, item2], // asignar ítems aquí + title = "Menu" // no se encuentra en el objeto (se utiliza valor predeterminado) } = options; alert(title); // Menu @@ -449,19 +449,19 @@ alert(item1); // Cake alert(item2); // Donut ``` -All properties of `options` object except `extra` which is absent in the left part, are assigned to corresponding variables: +Todas las propiedades del objeto `options`, con excepción de `extra` que no está en el lado izquierdo, son asignadas a las variables correspondientes: ![](destructuring-complex.svg) -Finally, we have `width`, `height`, `item1`, `item2` and `title` from the default value. +Por último tenemos `width`, `height`, `item1`, `item2` y `title` desde el valor predeterminado. -Note that there are no variables for `size` and `items`, as we take their content instead. +Nota que no hay variables para `size` e `items`, ya que tomamos su contenido en su lugar. -## Smart function parameters +## Argumentos de función inteligentes -There are times when a function has many parameters, most of which are optional. That's especially true for user interfaces. Imagine a function that creates a menu. It may have a width, a height, a title, an item list and so on. +Hay momentos en que una función tiene muchos parámetros, la mayoría de los cuales son opcionales. Eso es especialmente cierto para las interfaces de usuario. Imagine una función que crea un menú. Puede tener ancho, altura, título, elementos de lista, etcétera. -Here's a bad way to write such a function: +Esta es una forma incorrecta de escribir tal función: ```js function showMenu(title = "Untitled", width = 200, height = 100, items = []) { @@ -469,32 +469,32 @@ function showMenu(title = "Untitled", width = 200, height = 100, items = []) { } ``` -In real-life, the problem is how to remember the order of arguments. Usually, IDEs try to help us, especially if the code is well-documented, but still... Another problem is how to call a function when most parameters are ok by default. +En la vida real, el problema es cómo recordar el orden de los argumentos. Normalmente los IDEs ayudan, especialmente si el código está bien documentado, pero aún así... Otro problema es cómo llamar a una función si queremos que la mayoría de los argumentos usen sus valores predeterminados. -Like this? +¿Así? ```js -// undefined where default values are fine +// undefined para que use los valores predeterminados showMenu("My Menu", undefined, undefined, ["Item1", "Item2"]) ``` -That's ugly. And becomes unreadable when we deal with more parameters. +Esto no es nada grato. Y se torna ilegible cuando tratamos con muchos argumentos. -Destructuring comes to the rescue! +¡La desestructuración llega al rescate! -We can pass parameters as an object, and the function immediately destructurizes them into variables: +Podemos pasar los argumentos como un objeto, y la función inmediatamente los desestructura en variables: ```js run -// we pass object to function +// pasamos un objeto a la función let options = { title: "My menu", items: ["Item1", "Item2"] }; -// ...and it immediately expands it to variables +// ...y los expande inmediatamente a variables function showMenu(*!*{title = "Untitled", width = 200, height = 100, items = []}*/!*) { - // title, items – taken from options, - // width, height – defaults used + // title, items – desde options + // width, height – usan los predeterminados alert( `${title} ${width} ${height}` ); // My Menu 200 100 alert( items ); // Item1, Item2 } @@ -502,7 +502,7 @@ function showMenu(*!*{title = "Untitled", width = 200, height = 100, items = []} showMenu(options); ``` -We can also use more complex destructuring with nested objects and colon mappings: +También podemos usar desestructuración más compleja con objetos anidados y mapeo de dos puntos: ```js run let options = { @@ -513,9 +513,9 @@ let options = { *!* function showMenu({ title = "Untitled", - width: w = 100, // width goes to w - height: h = 200, // height goes to h - items: [item1, item2] // items first element goes to item1, second to item2 + width: w = 100, // width va a w + height: h = 200, // height va a h + items: [item1, item2] // el primer elemento de items va a item1, el segundo a item2 }) { */!* alert( `${title} ${w} ${h}` ); // My Menu 100 200 @@ -526,25 +526,25 @@ function showMenu({ showMenu(options); ``` -The full syntax is the same as for a destructuring assignment: +La sintaxis completa es la misma que para una asignación desestructurante: ```js function({ - incomingProperty: varName = defaultValue + incomingProperty: varName = defaultValue // propiedadEntrante: nombreVariable = valorPredeterminado ... }) ``` -Then, for an object of parameters, there will be a variable `varName` for the property `incomingProperty`, with `defaultValue` by default. +Entonces, para un objeto de parámetros, habrá una variable `varName` para la propiedad `incomingProperty`, con `defaultValue` por defecto. -Please note that such destructuring assumes that `showMenu()` does have an argument. If we want all values by default, then we should specify an empty object: +Por favor observe que tal desestructuración supone que `showMenu()` tiene un argumento. Si queremos todos los valores predeterminados, debemos especificar un objeto vacío: ```js -showMenu({}); // ok, all values are default +showMenu({}); // ok, todos los valores son predeterminados -showMenu(); // this would give an error +showMenu(); // esto daría un error ``` -We can fix this by making `{}` the default value for the whole object of parameters: +Podemos solucionar esto, poniendo `{}` como valor predeterminado para todo el objeto de argumentos: ```js run function showMenu({ title = "Menu", width = 100, height = 200 }*!* = {}*/!*) { @@ -554,26 +554,26 @@ function showMenu({ title = "Menu", width = 100, height = 200 }*!* = {}*/!*) { showMenu(); // Menu 100 200 ``` -In the code above, the whole arguments object is `{}` by default, so there's always something to destructurize. +En el código de arriba, todo el objeto de argumentos es `{}` por defecto, por lo tanto siempre hay algo para desestructurar. -## Summary +## Resumen -- Destructuring assignment allows for instantly mapping an object or array onto many variables. -- The full object syntax: +- La asignación desestructurante permite mapear instantáneamente un objeto o array en varias variables. +- La sintaxis completa para objeto: ```js let {prop : varName = defaultValue, ...rest} = object ``` - This means that property `prop` should go into the variable `varName` and, if no such property exists, then the `default` value should be used. + Esto significa que la propiedad `prop` se asigna a la variable `varName`; pero si no existe tal propiedad, se usa el valor `default`. - Object properties that have no mapping are copied to the `rest` object. + Las propiedades de objeto que no fueron mapeadas son copiadas al objeto `rest`. -- The full array syntax: +- La sintaxis completa para array: ```js - let [item1 = defaultValue, item2, ...rest] = array + let [item1 = default, item2, ...resto] = array ``` - The first item goes to `item1`; the second goes into `item2`, and all the rest makes the array `rest`. + El primer item va a `item1`, el segundo a `item2`, todos los ítems restantes crean el array `resto`. -- It's possible to extract data from nested arrays/objects, for that the left side must have the same structure as the right one. +- Es posible extraer información desde arrays/objetos anidados, para esto el lado izquierdo debe tener la misma estructura que el lado derecho. diff --git a/1-js/05-data-types/11-date/1-new-date/solution.md b/1-js/05-data-types/11-date/1-new-date/solution.md index 18286c336..eaf1fa7f7 100644 --- a/1-js/05-data-types/11-date/1-new-date/solution.md +++ b/1-js/05-data-types/11-date/1-new-date/solution.md @@ -1,15 +1,15 @@ -The `new Date` constructor uses the local time zone. So the only important thing to remember is that months start from zero. +El constructor `new Date` utiliza la zona horaria local. Lo único importante por recordar es que los meses se cuentan desde el 0. -So February has number 1. +Por ejemplo, febrero es el mes 1. -Here's an example with numbers as date components: +Aquí hay un ejemplo con números como componentes de fecha: ```js run -//new Date(year, month, date, hour, minute, second, millisecond) +//new Date(año, mes, día, hora, minuto, segundo, milisegundo) let d1 = new Date(2012, 1, 20, 3, 12); alert( d1 ); ``` -We could also create a date from a string, like this: +También podríamos crear una fecha a partir de un string, así: ```js run //new Date(datastring) diff --git a/1-js/05-data-types/11-date/1-new-date/task.md b/1-js/05-data-types/11-date/1-new-date/task.md index 1b40d5ac0..eb334a2dc 100644 --- a/1-js/05-data-types/11-date/1-new-date/task.md +++ b/1-js/05-data-types/11-date/1-new-date/task.md @@ -2,8 +2,8 @@ importance: 5 --- -# Create a date +# Crea una fecha -Create a `Date` object for the date: Feb 20, 2012, 3:12am. The time zone is local. +Crea un objeto `Date` para la fecha: Feb 20, 2012, 3:12am. La zona horaria es local. -Show it using `alert`. +Muéstralo en pantalla utilizando `alert`. diff --git a/1-js/05-data-types/11-date/2-get-week-day/solution.md b/1-js/05-data-types/11-date/2-get-week-day/solution.md index 58d75c1c3..dfc6ffa01 100644 --- a/1-js/05-data-types/11-date/2-get-week-day/solution.md +++ b/1-js/05-data-types/11-date/2-get-week-day/solution.md @@ -1,6 +1,6 @@ -The method `date.getDay()` returns the number of the weekday, starting from sunday. +El método `date.getDay()` devuelve el número del día de la semana, empezando por el domingo. -Let's make an array of weekdays, so that we can get the proper day name by its number: +Hagamos un array de días de la semana, así podemos obtener el nombre del día a través de su número correspondiente. ```js run demo function getWeekDay(date) { diff --git a/1-js/05-data-types/11-date/2-get-week-day/task.md b/1-js/05-data-types/11-date/2-get-week-day/task.md index 5cf31565d..efc639412 100644 --- a/1-js/05-data-types/11-date/2-get-week-day/task.md +++ b/1-js/05-data-types/11-date/2-get-week-day/task.md @@ -2,13 +2,13 @@ importance: 5 --- -# Show a weekday +# Muestra en pantalla un día de la semana -Write a function `getWeekDay(date)` to show the weekday in short format: 'MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU'. +Escribe una función `getWeekDay(date)` para mostrar el día de la semana en formato corto: 'MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU'. -For instance: +Por ejemplo: ```js no-beautify let date = new Date(2012, 0, 3); // 3 Jan 2012 -alert( getWeekDay(date) ); // should output "TU" +alert( getWeekDay(date) ); // debería mostrar "TU" ``` diff --git a/1-js/05-data-types/11-date/3-weekday/task.md b/1-js/05-data-types/11-date/3-weekday/task.md index ba62790cf..bb40ad88f 100644 --- a/1-js/05-data-types/11-date/3-weekday/task.md +++ b/1-js/05-data-types/11-date/3-weekday/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# European weekday +# Día de la semana europeo -European countries have days of week starting with Monday (number 1), then Tuesday (number 2) and till Sunday (number 7). Write a function `getLocalDay(date)` that returns the "European" day of week for `date`. +En los países europeos se cuentan los días de la semana a partir del lunes (número 1), seguido del martes (número 2), hasta el domingo (número 7). Escribe una función `getLocalDay(date)` que devuelva el día de la semana "europeo" para la variable `date`. ```js no-beautify let date = new Date(2012, 0, 3); // 3 Jan 2012 diff --git a/1-js/05-data-types/11-date/4-get-date-ago/solution.md b/1-js/05-data-types/11-date/4-get-date-ago/solution.md index 5c394c100..f392bd0aa 100644 --- a/1-js/05-data-types/11-date/4-get-date-ago/solution.md +++ b/1-js/05-data-types/11-date/4-get-date-ago/solution.md @@ -1,4 +1,4 @@ -The idea is simple: to substract given number of days from `date`: +La idea es simple: restarle a la fecha `date` la cantidad de días especificada. ```js function getDateAgo(date, days) { @@ -7,9 +7,9 @@ function getDateAgo(date, days) { } ``` -...But the function should not change `date`. That's an important thing, because the outer code which gives us the date does not expect it to change. +...Pero la función no debería modificar la fecha `date`. Esto es importante, ya que no se espera que cambie la variable externa que contiene la fecha. -To implement it let's clone the date, like this: +Para hacerlo, clonemos la fecha de esta manera: ```js run demo function getDateAgo(date, days) { diff --git a/1-js/05-data-types/11-date/4-get-date-ago/task.md b/1-js/05-data-types/11-date/4-get-date-ago/task.md index 058d39c7e..80a553285 100644 --- a/1-js/05-data-types/11-date/4-get-date-ago/task.md +++ b/1-js/05-data-types/11-date/4-get-date-ago/task.md @@ -2,13 +2,13 @@ importance: 4 --- -# Which day of month was many days ago? +# ¿Qué día del mes era hace algunos días atrás? -Create a function `getDateAgo(date, days)` to return the day of month `days` ago from the `date`. +Crea una función `getDateAgo(date, days)` que devuelva el día del mes que corresponde, contando la cantidad de días `days` respecto de la fecha `date`. -For instance, if today is 20th, then `getDateAgo(new Date(), 1)` should be 19th and `getDateAgo(new Date(), 2)` should be 18th. +Por ejemplo, si hoy es 20, entonces `getDateAgo(new Date(), 1)` debería ser 19 y `getDateAgo(new Date(), 2)` debería ser 18. -Should work reliably for `days=365` or more: +Debe poder funcionar para `days=365` o más: ```js let date = new Date(2015, 0, 2); @@ -18,4 +18,4 @@ alert( getDateAgo(date, 2) ); // 31, (31 Dec 2014) alert( getDateAgo(date, 365) ); // 2, (2 Jan 2014) ``` -P.S. The function should not modify the given `date`. +P.D.: La función no debería modificar la fecha `date` pasada como argumento. diff --git a/1-js/05-data-types/11-date/5-last-day-of-month/solution.md b/1-js/05-data-types/11-date/5-last-day-of-month/solution.md index 4f642536e..06508d761 100644 --- a/1-js/05-data-types/11-date/5-last-day-of-month/solution.md +++ b/1-js/05-data-types/11-date/5-last-day-of-month/solution.md @@ -1,4 +1,4 @@ -Let's create a date using the next month, but pass zero as the day: +Creemos una fecha utilizando el mes próximo, pero pasando 0 como número de día: ```js run demo function getLastDayOfMonth(year, month) { let date = new Date(year, month + 1, 0); @@ -10,4 +10,4 @@ alert( getLastDayOfMonth(2012, 1) ); // 29 alert( getLastDayOfMonth(2013, 1) ); // 28 ``` -Normally, dates start from 1, but technically we can pass any number, the date will autoadjust itself. So when we pass 0, then it means "one day before 1st day of the month", in other words: "the last day of the previous month". +Normalmente, las fechas comienzan a partir del 1, sin embargo podemos pasar como argumento cualquier número, ya que se corregirá automáticamente. De esta manera, si pasamos el número 0 como día, se interpreta como "el día anterior al primer día del mes", o en otras palabras: "el último día del mes anterior". diff --git a/1-js/05-data-types/11-date/5-last-day-of-month/task.md b/1-js/05-data-types/11-date/5-last-day-of-month/task.md index 10dfb7a7a..d7e21bd5d 100644 --- a/1-js/05-data-types/11-date/5-last-day-of-month/task.md +++ b/1-js/05-data-types/11-date/5-last-day-of-month/task.md @@ -2,13 +2,13 @@ importance: 5 --- -# Last day of month? +# ¿Cuál es el último día del mes? -Write a function `getLastDayOfMonth(year, month)` that returns the last day of month. Sometimes it is 30th, 31st or even 28/29th for Feb. +Escribe una función `getLastDayOfMonth(year, month)` que devuelva el último día del mes dado. A veces es 30, 31 o incluso 28/29 para febrero. -Parameters: +Parámetros: -- `year` -- four-digits year, for instance 2012. -- `month` -- month, from 0 to 11. +- `year` -- el año en formato de cuatro dígitos, por ejemplo 2012. +- `month` -- el mes, de 0 a 11. -For instance, `getLastDayOfMonth(2012, 1) = 29` (leap year, Feb). +Por ejemplo, `getLastDayOfMonth(2012, 1) = 29` (febrero, año bisiesto). diff --git a/1-js/05-data-types/11-date/6-get-seconds-today/solution.md b/1-js/05-data-types/11-date/6-get-seconds-today/solution.md index 8f8e52b68..b50e2e3dd 100644 --- a/1-js/05-data-types/11-date/6-get-seconds-today/solution.md +++ b/1-js/05-data-types/11-date/6-get-seconds-today/solution.md @@ -1,22 +1,22 @@ -To get the number of seconds, we can generate a date using the current day and time 00:00:00, then substract it from "now". +Para obtener la cantidad de segundos, podemos generar una fecha en la variable "today" utilizando el día de hoy con la hora en 00:00:00, y luego restárselo a la variable "now". -The difference is the number of milliseconds from the beginning of the day, that we should divide by 1000 to get seconds: +El resultado será la cantidad de milisegundos transcurridos desde el comienzo del día, el cual debemos dividir por 1000 para pasarlo a segundos: ```js run function getSecondsToday() { let now = new Date(); - // create an object using the current day/month/year + // creamos un objeto que contenga el día/mes/año actual let today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); - let diff = now - today; // ms difference - return Math.round(diff / 1000); // make seconds + let diff = now - today; // diferencia entre fechas, representado en ms + return Math.round(diff / 1000); // pasaje a segundos } alert( getSecondsToday() ); ``` -An alternative solution would be to get hours/minutes/seconds and convert them to seconds: +Una solución alternativa sería obtener las horas/minutos/segundos actuales y pasar todo a segundos: ```js run function getSecondsToday() { diff --git a/1-js/05-data-types/11-date/6-get-seconds-today/task.md b/1-js/05-data-types/11-date/6-get-seconds-today/task.md index 456790928..0dba4d4d8 100644 --- a/1-js/05-data-types/11-date/6-get-seconds-today/task.md +++ b/1-js/05-data-types/11-date/6-get-seconds-today/task.md @@ -2,14 +2,14 @@ importance: 5 --- -# How many seconds have passed today? +# ¿Cuántos segundos transcurrieron el día de hoy? -Write a function `getSecondsToday()` that returns the number of seconds from the beginning of today. +Escribe una función `getSecondsToday()` que devuelva la cantidad de segundos transcurridos desde el comienzo del día. -For instance, if now were `10:00 am`, and there was no daylight savings shift, then: +Por ejemplo, si en este momento fueran las `10:00 am`, sin horario de verano, entonces: ```js getSecondsToday() == 36000 // (3600 * 10) ``` -The function should work in any day. That is, it should not have a hard-coded value of "today". +La función debe poder funcionar correctamente cualquier día. Es decir, no debe poseer valores fijos en el código, como por ej. "today". diff --git a/1-js/05-data-types/11-date/7-get-seconds-to-tomorrow/solution.md b/1-js/05-data-types/11-date/7-get-seconds-to-tomorrow/solution.md index c337d1199..5c5b96c0b 100644 --- a/1-js/05-data-types/11-date/7-get-seconds-to-tomorrow/solution.md +++ b/1-js/05-data-types/11-date/7-get-seconds-to-tomorrow/solution.md @@ -1,20 +1,20 @@ -To get the number of milliseconds till tomorrow, we can from "tomorrow 00:00:00" substract the current date. +Para obtener la cantidad de milisegundos que faltan para mañana, podemos restarle la fecha actual a "mañana 00:00:00". -First, we generate that "tomorrow", and then do it: +Primero generamos ese "mañana" y luego restamos: ```js run function getSecondsToTomorrow() { let now = new Date(); - // tomorrow date + // el día de mañana let tomorrow = new Date(now.getFullYear(), now.getMonth(), *!*now.getDate()+1*/!*); - let diff = tomorrow - now; // difference in ms - return Math.round(diff / 1000); // convert to seconds + let diff = tomorrow - now; // diferencia en ms + return Math.round(diff / 1000); // conversión a segundos } ``` -Alternative solution: +Solución alternativa: ```js run function getSecondsToTomorrow() { @@ -29,4 +29,4 @@ function getSecondsToTomorrow() { } ``` -Please note that many countries have Daylight Savings Time (DST), so there may be days with 23 or 25 hours. We may want to treat such days separately. +Ten en cuenta que algunos países tienen horarios de verano (DST), así que es posible que existan días con 23 o 25 horas. Podríamos querer tratar estos días por separado. diff --git a/1-js/05-data-types/11-date/7-get-seconds-to-tomorrow/task.md b/1-js/05-data-types/11-date/7-get-seconds-to-tomorrow/task.md index e05903026..537963169 100644 --- a/1-js/05-data-types/11-date/7-get-seconds-to-tomorrow/task.md +++ b/1-js/05-data-types/11-date/7-get-seconds-to-tomorrow/task.md @@ -2,14 +2,14 @@ importance: 5 --- -# How many seconds till tomorrow? +# ¿Cuantos segundos faltan para el día de mañana? -Create a function `getSecondsToTomorrow()` that returns the number of seconds till tomorrow. +Crea una función `getSecondsToTomorrow()` que devuelva la cantidad de segundos que faltan para el día de mañana. -For instance, if now is `23:00`, then: +Por ejemplo, si ahora son las `23:00`, entonces: ```js getSecondsToTomorrow() == 3600 ``` -P.S. The function should work at any day, the "today" is not hardcoded. +P.D.: La función debe poder funcionar para cualquier día, sin valores fijos en el código como "today". diff --git a/1-js/05-data-types/11-date/8-format-date-relative/solution.md b/1-js/05-data-types/11-date/8-format-date-relative/solution.md index 372485685..f0f2264d4 100644 --- a/1-js/05-data-types/11-date/8-format-date-relative/solution.md +++ b/1-js/05-data-types/11-date/8-format-date-relative/solution.md @@ -1,26 +1,26 @@ -To get the time from `date` till now -- let's substract the dates. +Para obtener el tiempo que transcurrió desde la fecha `date` hasta ahora, restemos ambas fechas entre sí. ```js run demo function formatDate(date) { - let diff = new Date() - date; // the difference in milliseconds + let diff = new Date() - date; // la diferencia entre ambas, representada en milisegundos - if (diff < 1000) { // less than 1 second - return 'right now'; + if (diff < 1000) { // menos de 1 segundo + return 'ahora mismo'; } - let sec = Math.floor(diff / 1000); // convert diff to seconds + let sec = Math.floor(diff / 1000); // convierte el resultado en segundos if (sec < 60) { - return sec + ' sec. ago'; + return 'hace ' sec + ' seg.'; } - let min = Math.floor(diff / 60000); // convert diff to minutes + let min = Math.floor(diff / 60000); // convierte el resultado en minutos if (min < 60) { - return min + ' min. ago'; + return 'hace ' + min + ' min.'; } - // format the date - // add leading zeroes to single-digit day/month/hours/minutes + // cambia le formato de la fecha + // se le agrega un dígito 0 al día/mes/horas/minutos que contenga un único digito. let d = date; d = [ '0' + d.getDate(), @@ -28,23 +28,23 @@ function formatDate(date) { '' + d.getFullYear(), '0' + d.getHours(), '0' + d.getMinutes() - ].map(component => component.slice(-2)); // take last 2 digits of every component + ].map(component => component.slice(-2)); // toma los últimos 2 dígitos de cada componente - // join the components into date + // une los componentes para formar una única fecha return d.slice(0, 3).join('.') + ' ' + d.slice(3).join(':'); } -alert( formatDate(new Date(new Date - 1)) ); // "right now" +alert( formatDate(new Date(new Date - 1)) ); // "ahora mismo" -alert( formatDate(new Date(new Date - 30 * 1000)) ); // "30 sec. ago" +alert( formatDate(new Date(new Date - 30 * 1000)) ); // "hace 30 seg." -alert( formatDate(new Date(new Date - 5 * 60 * 1000)) ); // "5 min. ago" +alert( formatDate(new Date(new Date - 5 * 60 * 1000)) ); // "hace 5 min." -// yesterday's date like 31.12.2016 20:00 +// la fecha de ayer en formato 31.12.2016 20:00 alert( formatDate(new Date(new Date - 86400 * 1000)) ); ``` -Alternative solution: +Solución alternativa: ```js run function formatDate(date) { @@ -58,7 +58,7 @@ function formatDate(date) { let diffMin = diffSec / 60; let diffHour = diffMin / 60; - // formatting + // dándole formato year = year.toString().slice(-2); month = month < 10 ? '0' + month : month; dayOfMonth = dayOfMonth < 10 ? '0' + dayOfMonth : dayOfMonth; @@ -66,11 +66,11 @@ function formatDate(date) { minutes = minutes < 10 ? '0' + minutes : minutes; if (diffSec < 1) { - return 'right now'; + return 'ahora mismo'; } else if (diffMin < 1) { - return `${diffSec} sec. ago` + return `hace ${diffSec} seg.` } else if (diffHour < 1) { - return `${diffMin} min. ago` + return `hace ${diffMin} min.` } else { return `${dayOfMonth}.${month}.${year} ${hour}:${minutes}` } diff --git a/1-js/05-data-types/11-date/8-format-date-relative/task.md b/1-js/05-data-types/11-date/8-format-date-relative/task.md index 9651b305f..8d585d630 100644 --- a/1-js/05-data-types/11-date/8-format-date-relative/task.md +++ b/1-js/05-data-types/11-date/8-format-date-relative/task.md @@ -2,24 +2,24 @@ importance: 4 --- -# Format the relative date +# Cambia el formato a fecha relativa -Write a function `formatDate(date)` that should format `date` as follows: +Escribe una función `formatDate(date)` que muestre la fecha en el siguiente formato: -- If since `date` passed less than 1 second, then `"right now"`. -- Otherwise, if since `date` passed less than 1 minute, then `"n sec. ago"`. -- Otherwise, if less than an hour, then `"m min. ago"`. -- Otherwise, the full date in the format `"DD.MM.YY HH:mm"`. That is: `"day.month.year hours:minutes"`, all in 2-digit format, e.g. `31.12.16 10:00`. +- Si a partir de la fecha `date` pasó menos de 1 segundo, debe devolver `"ahora mismo"`. +- De no ser así, si a partir de la fecha `date` pasó menos de 1 minuto, debe retornar `"hace n seg,"`. +- De no ser así, si pasó menos de una hora, debe retornar `"hace n min."`. +- De no ser así, debe retornar la fecha completa en el formato `"DD.MM.AA HH:mm"`. Es decir: `"día.mes.año horas:minutos"`, cada uno de ellos en formato de 2 dígitos, por ej. `31.12.16 10:00`. For instance: ```js -alert( formatDate(new Date(new Date - 1)) ); // "right now" +alert( formatDate(new Date(new Date - 1)) ); // "ahora mismo" -alert( formatDate(new Date(new Date - 30 * 1000)) ); // "30 sec. ago" +alert( formatDate(new Date(new Date - 30 * 1000)) ); // "hace 30 seg." -alert( formatDate(new Date(new Date - 5 * 60 * 1000)) ); // "5 min. ago" +alert( formatDate(new Date(new Date - 5 * 60 * 1000)) ); // "hace 5 min." -// yesterday's date like 31.12.16 20:00 +// la fecha de ayer en formato 31.12.16 20:00 alert( formatDate(new Date(new Date - 86400 * 1000)) ); ``` diff --git a/1-js/05-data-types/11-date/article.md b/1-js/05-data-types/11-date/article.md index 6958a3a97..9a33702c7 100644 --- a/1-js/05-data-types/11-date/article.md +++ b/1-js/05-data-types/11-date/article.md @@ -1,39 +1,39 @@ -# Date and time +# Fecha y Hora -Let's meet a new built-in object: [Date](mdn:js/Date). It stores the date, time and provides methods for date/time management. +Aprendamos un nuevo objeto incorporado de JS: [Date](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Date). Este objeto almacena la fecha, la hora, y brinda métodos para administrarlas. -For instance, we can use it to store creation/modification times, to measure time, or just to print out the current date. +Por ejemplo, podemos usarlo para almacenar horas de creación o modificación, medir tiempo, o simplemente mostrar en pantalla la fecha actual. -## Creation +## Creación -To create a new `Date` object call `new Date()` with one of the following arguments: +Para crear un nuevo objeto `Date` se lo instancia con `new Date()` junto con uno de los siguientes argumentos: `new Date()` -: Without arguments -- create a `Date` object for the current date and time: +: Sin argumentos -- crea un objeto `Date` para la fecha y la hora actuales: ```js run let now = new Date(); - alert( now ); // shows current date/time + alert( now ); // muestra en pantalla la fecha y la hora actuales ``` `new Date(milliseconds)` -: Create a `Date` object with the time equal to number of milliseconds (1/1000 of a second) passed after the Jan 1st of 1970 UTC+0. +: Crea un objeto `Date` con la cantidad de tiempo igual al número de milisegundos (1/1000 de un segundo) transcurrido a partir del 1° de enero de 1970 UTC+0. ```js run - // 0 means 01.01.1970 UTC+0 + // 0 significa 01.01.1970 UTC+0 let Jan01_1970 = new Date(0); alert( Jan01_1970 ); - // now add 24 hours, get 02.01.1970 UTC+0 + // ahora se le agregan 24 horas, se obtiene 02.01.1970 UTC+0 let Jan02_1970 = new Date(24 * 3600 * 1000); alert( Jan02_1970 ); ``` - An integer number representing the number of milliseconds that has passed since the beginning of 1970 is called a *timestamp*. + Un *timestamp* es un número entero que representa la cantidad de milisegundos transcurridos desde el inicio de 1970. - It's a lightweight numeric representation of a date. We can always create a date from a timestamp using `new Date(timestamp)` and convert the existing `Date` object to a timestamp using the `date.getTime()` method (see below). + Este *timestamp* es una representación numérica liviana de una fecha. Es posible crear una fecha a partir de un *timestamp* usando `new Date(timestamp)`, y convertir el objeto `Date` actual a un *timestamp* utilizando el método `date.getTime()` (ver abajo). - Dates before 01.01.1970 have negative timestamps, e.g.: + Las fechas anteriores a 01.01.1970 tienen *timestamps* negativos, por ejemplo: ```js run // 31 Dec 1969 let Dec31_1969 = new Date(-24 * 3600 * 1000); @@ -41,141 +41,141 @@ To create a new `Date` object call `new Date()` with one of the following argume ``` `new Date(datestring)` -: If there is a single argument, and it's a string, then it is parsed automatically. The algorithm is the same as `Date.parse` uses, we'll cover it later. +: Si se pasa un único argumento, y es de tipo string, entonces es analizado y convertido a fecha automáticamente. El algoritmo es el mismo que el que utiliza `Date.parse`, lo veremos mas en detalle luego. ```js run let date = new Date("2017-01-26"); alert(date); - // The time is not set, so it's assumed to be midnight GMT and - // is adjusted according to the timezone the code is run in - // So the result could be - // Thu Jan 26 2017 11:00:00 GMT+1100 (Australian Eastern Daylight Time) - // or - // Wed Jan 25 2017 16:00:00 GMT-0800 (Pacific Standard Time) + // La hora no está definida, por lo que se asume que es la medianoche GMT (0 hs. de la fecha) y + // se ajusta de acuerdo al huso horario de la zona geográfica en la que está ejecutándose el código. + // Por consiguiente, el resultado podría ser + // Thu Jan 26 2017 11:00:00 GMT+1100 (Hora Estándar del Este de Australia) + // o + // Wed Jan 25 2017 16:00:00 GMT-0800 (Hora Estándar del Pacífico) ``` -`new Date(year, month, date, hours, minutes, seconds, ms)` -: Create the date with the given components in the local time zone. Only the first two arguments are obligatory. +`new Date(año, mes, fecha, horas, minutos, segundos, ms)` +: Crea una fecha con los componentes pasados como argumentos en la zona horaria local. Sólo los primeros dos parámetros son obligatorios. - - The `year` should have 4 digits. For compatibility, 2 digits are also accepted and considered `19xx`, e.g. `98` is the same as `1998` here, but always using 4 digits is strongly encouraged. - - The `month` count starts with `0` (Jan), up to `11` (Dec). - - The `date` parameter is actually the day of month, if absent then `1` is assumed. - - If `hours/minutes/seconds/ms` is absent, they are assumed to be equal `0`. + - El `año` debería tener 4 dígitos. Por compatibilidad, aquí 2 dígitos serán considerados '19xx', pero 4 dígitos es lo firmemente sugerido. + - La cuenta del `mes` comienza desde el `0` (enero), y termina en el `11` (diciembre). + - El parámetro `fecha` efectivamente es el día del mes, si está ausente se asume su valor en `1`. + - Si los parámetros `horas/minutos/segundos/ms` están ausentes, se asumen sus valores iguales a `0`. - For instance: + Por ejemplo: - ```js + ```js run new Date(2011, 0, 1, 0, 0, 0, 0); // 1 Jan 2011, 00:00:00 - new Date(2011, 0, 1); // the same, hours etc are 0 by default + new Date(2011, 0, 1); // Igual que la línea de arriba, sólo que a los últimos 4 parámetros se les asigna '0' por defecto. ``` - The maximal precision is 1 ms (1/1000 sec): + La precisión máxima es de 1 ms (1/1000 de segundo): ```js run let date = new Date(2011, 0, 1, 2, 3, 4, 567); alert( date ); // 1.01.2011, 02:03:04.567 ``` -## Access date components +## Acceso a los componentes de la fecha -There are methods to access the year, month and so on from the `Date` object: +Existen métodos que sirven para obtener el año, el mes, y los demás componentes a partir de un objeto de tipo `Date`: -[getFullYear()](mdn:js/Date/getFullYear) -: Get the year (4 digits) +[getFullYear()](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Date/getFullYear) +: Devuelve el año (4 dígitos) -[getMonth()](mdn:js/Date/getMonth) -: Get the month, **from 0 to 11**. +[getMonth()](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Date/getMonth) +: Devuelve el mes, **de 0 a 11**. -[getDate()](mdn:js/Date/getDate) -: Get the day of month, from 1 to 31, the name of the method does look a little bit strange. +[getDate()](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Date/getDate) +: Devuelve el día del mes desde 1 a 31. Nótese que el nombre del método no es muy intuitivo. -[getHours()](mdn:js/Date/getHours), [getMinutes()](mdn:js/Date/getMinutes), [getSeconds()](mdn:js/Date/getSeconds), [getMilliseconds()](mdn:js/Date/getMilliseconds) -: Get the corresponding time components. +[getHours()](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Date/getHours), [getMinutes()](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Date/getMinutes), [getSeconds()](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Date/getSeconds), [getMilliseconds()](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Date/getMilliseconds) +: Devuelve los componentes del horario correspondientes. -```warn header="Not `getYear()`, but `getFullYear()`" -Many JavaScript engines implement a non-standard method `getYear()`. This method is deprecated. It returns 2-digit year sometimes. Please never use it. There is `getFullYear()` for the year. +```warn header="No `getYear()`, sino `getFullYear()`" +Algunos motores de JavaScript poseen implementado un método no estándar llamado `getYear()`. Este método actualmente está obsoleto. A veces devuelve un año de 2 dígitos. Por favor, nunca lo uses. Usa `getFullYear()` para obtener el año. ``` -Additionally, we can get a day of week: +Además, podemos obtener un día de la semana: -[getDay()](mdn:js/Date/getDay) -: Get the day of week, from `0` (Sunday) to `6` (Saturday). The first day is always Sunday, in some countries that's not so, but can't be changed. +[getDay()](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Date/getDay) +: Devuelve el día de la semana, partiendo de `0` (Domingo) hasta `6` (Sábado). El primer día siempre es el Domingo. Por más que en algunos países no sea así, no se puede modificar. -**All the methods above return the components relative to the local time zone.** +**Todos los métodos mencionados anteriormente devuelven los componentes correspondientes a la zona horaria local.** -There are also their UTC-counterparts, that return day, month, year and so on for the time zone UTC+0: [getUTCFullYear()](mdn:js/Date/getUTCFullYear), [getUTCMonth()](mdn:js/Date/getUTCMonth), [getUTCDay()](mdn:js/Date/getUTCDay). Just insert the `"UTC"` right after `"get"`. +También existen sus contrapartes UTC, que devuelven el día, mes, año, y demás componentes, para la zona horaria UTC+0: [getUTCFullYear()](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Date/getUTCFullYear), [getUTCMonth()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getUTCMonth), [getUTCDay()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getUTCDay). Solo debemos agregarle el `"UTC"` justo después de `"get"`. -If your local time zone is shifted relative to UTC, then the code below shows different hours: +Si tu zona horaria está desplazada respecto de UTC el código de abajo va a mostrar horas diferentes: ```js run -// current date +// fecha actual let date = new Date(); -// the hour in your current time zone +// la hora en tu zona horaria actual alert( date.getHours() ); -// the hour in UTC+0 time zone (London time without daylight savings) +// la hora respecto de la zona horaria UTC+0 (Hora de Londres sin horario de verano) alert( date.getUTCHours() ); ``` -Besides the given methods, there are two special ones that do not have a UTC-variant: +Además de los anteriormente mencionados, hay dos métodos especiales que no poseen una variante de UTC: -[getTime()](mdn:js/Date/getTime) -: Returns the timestamp for the date -- a number of milliseconds passed from the January 1st of 1970 UTC+0. +[getTime()](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Date/getTime) +: Devuelve el _timestamp_ para una fecha determinada -- cantidad de milisegundos transcurridos a partir del 1° de Enero de 1970 UTC+0. -[getTimezoneOffset()](mdn:js/Date/getTimezoneOffset) -: Returns the difference between UTC and the local time zone, in minutes: +[getTimezoneOffset()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset) +: Devuelve la diferencia entre UTC y el huso horario de la zona actual, en minutos: ```js run - // if you are in timezone UTC-1, outputs 60 - // if you are in timezone UTC+3, outputs -180 + // Si estás en la zona horaria UTC-1, devuelve 60 + // Si estás en la zona horaria UTC+3, devuelve -180 alert( new Date().getTimezoneOffset() ); ``` -## Setting date components +## Estableciendo los componentes de la fecha -The following methods allow to set date/time components: +Los siguientes métodos permiten establecer los componentes de fecha y hora: -- [`setFullYear(year, [month], [date])`](mdn:js/Date/setFullYear) -- [`setMonth(month, [date])`](mdn:js/Date/setMonth) -- [`setDate(date)`](mdn:js/Date/setDate) -- [`setHours(hour, [min], [sec], [ms])`](mdn:js/Date/setHours) -- [`setMinutes(min, [sec], [ms])`](mdn:js/Date/setMinutes) -- [`setSeconds(sec, [ms])`](mdn:js/Date/setSeconds) -- [`setMilliseconds(ms)`](mdn:js/Date/setMilliseconds) -- [`setTime(milliseconds)`](mdn:js/Date/setTime) (sets the whole date by milliseconds since 01.01.1970 UTC) +- [`setFullYear(year, [month], [date])`](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Date/setFullYear) +- [`setMonth(month, [date])`](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Date/setMonth) +- [`setDate(date)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setDate) +- [`setHours(hour, [min], [sec], [ms])`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setHours) +- [`setMinutes(min, [sec], [ms])`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setMinutes) +- [`setSeconds(sec, [ms])`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setSeconds) +- [`setMilliseconds(ms)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setMilliseconds) +- [`setTime(milliseconds)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setTime) (Establece la cantidad de segundos transcurridos desde 01.01.1970 GMT+0) -Every one of them except `setTime()` has a UTC-variant, for instance: `setUTCHours()`. +A excepción de `setTime()`, todos los demás métodos poseen una variante UTC, por ejemplo: `setUTCHours()`. -As we can see, some methods can set multiple components at once, for example `setHours`. The components that are not mentioned are not modified. +Como podemos ver, algunos métodos nos permiten fijar varios componentes al mismo tiempo, por ej. `setHours`. Los componentes que no son mencionados no se modifican. -For instance: +Por ejemplo: ```js run let today = new Date(); today.setHours(0); -alert(today); // still today, but the hour is changed to 0 +alert(today); // Sigue siendo el día de hoy, pero con la hora cambiada a 0. today.setHours(0, 0, 0, 0); -alert(today); // still today, now 00:00:00 sharp. +alert(today); // Sigue siendo la fecha de hoy, pero ahora en formato 00:00:00 en punto. ``` -## Autocorrection +## Autocorrección -The *autocorrection* is a very handy feature of `Date` objects. We can set out-of-range values, and it will auto-adjust itself. +La _autocorrección_ es una característica muy útil de los objetos `Date`. Podemos fijar valores fuera de rango, y se ajustarán automáticamente. -For instance: +Por ejemplo: ```js run -let date = new Date(2013, 0, *!*32*/!*); // 32 Jan 2013 ?!? -alert(date); // ...is 1st Feb 2013! +let date = new Date(2013, 0, *!*32*/!*); // ¿32 de Enero 2013? +alert(date); // ¡Se autocorrigió al 1° de Febrero de 2013! ``` -Out-of-range date components are distributed automatically. +Los componentes de la fecha que están fuera de rango se distribuyen automáticamente. -Let's say we need to increase the date "28 Feb 2016" by 2 days. It may be "2 Mar" or "1 Mar" in case of a leap-year. We don't need to think about it. Just add 2 days. The `Date` object will do the rest: +Por ejemplo, supongamos que necesitamos incrementar la fecha "28 Feb 2016" en 2 días. El resultado puede ser "2 Mar" o "1 Mar" dependiendo de si es año bisiesto. Afortunadamente, no tenemos de qué preocuparnos. Sólo debemos agregarle los 2 días y el objeto `Date` se encargará del resto: ```js run let date = new Date(2016, 1, 28); @@ -186,109 +186,109 @@ date.setDate(date.getDate() + 2); alert( date ); // 1 Mar 2016 ``` -That feature is often used to get the date after the given period of time. For instance, let's get the date for "70 seconds after now": +Esta característica se usa frecuentemente para obtener la fecha, a partir de un período de tiempo específico. Por ejemplo, supongamos que queremos obtener "la fecha de hoy pero transcurridos 70 segundos a partir de este preciso instante." ```js run let date = new Date(); date.setSeconds(date.getSeconds() + 70); -alert( date ); // shows the correct date +alert( date ); // Se muestra la fecha correcta. ``` -We can also set zero or even negative values. For example: +También podemos fijar valores en 0 o incluso valores negativos. Por ejemplo: ```js run let date = new Date(2016, 0, 2); // 2 Jan 2016 -date.setDate(1); // set day 1 of month +date.setDate(1); // Fija '1' día del mes alert( date ); -date.setDate(0); // min day is 1, so the last day of the previous month is assumed +date.setDate(0); // el día mínimo es 1, entonces asume el último día del mes anterior alert( date ); // 31 Dec 2015 ``` -## Date to number, date diff +## Conversión de fechas a números y diferencia entre fechas. -When a `Date` object is converted to number, it becomes the timestamp same as `date.getTime()`: +Cuando convertimos un objeto `Date` a número toma el valor del _timestamp_ actual, al igual que el método `date.getTime()`: ```js run let date = new Date(); -alert(+date); // the number of milliseconds, same as date.getTime() +alert(+date); // devuelve el número de milisegundos, al igual que date.getTime() ``` -The important side effect: dates can be subtracted, the result is their difference in ms. +El efecto secundario importante: las fechas pueden ser restadas, el resultado es su diferencia en ms. -That can be used for time measurements: +Esto puede ser usado para medición de tiempo: ```js run -let start = new Date(); // start measuring time +let start = new Date(); // comienza a medir el tiempo (valor inicial) -// do the job +// la función hace su trabajo for (let i = 0; i < 100000; i++) { let doSomething = i * i * i; } -let end = new Date(); // end measuring time +let end = new Date(); // termina de medir el tiempo (valor final) -alert( `The loop took ${end - start} ms` ); +alert(`El tiempo transcurrido es de ${end - start} ms`); ``` ## Date.now() -If we only want to measure time, we don't need the `Date` object. +Si lo único que queremos es medir el tiempo transcurrido, no es necesario utilizar el objeto `Date`. -There's a special method `Date.now()` that returns the current timestamp. +Podemos utilizar el método especial `Date.now()` que nos devuelve el _timestamp_ actual. -It is semantically equivalent to `new Date().getTime()`, but it doesn't create an intermediate `Date` object. So it's faster and doesn't put pressure on garbage collection. +Es el equivalente semántico a `new Date().getTime()`, pero no crea una instancia intermediaria del objeto `Date`. De esta manera, el proceso es mas rápido y, por consiguiente, no afecta a la recolección de basura. -It is used mostly for convenience or when performance matters, like in games in JavaScript or other specialized applications. +Mayormente se utiliza por conveniencia o cuando la performance del código es fundamental, como por ejemplo en juegos de JavaScript u otras aplicaciones específicas. -So this is probably better: +Por lo tanto, es mejor hacerlo de esta manera: ```js run *!* -let start = Date.now(); // milliseconds count from 1 Jan 1970 +let start = Date.now(); // milisegundos transcurridos a partir del 1° de Enero de 1970 */!* -// do the job +// la función realiza su trabajo for (let i = 0; i < 100000; i++) { let doSomething = i * i * i; } *!* -let end = Date.now(); // done +let end = Date.now(); // listo */!* -alert( `The loop took ${end - start} ms` ); // subtract numbers, not dates +alert( `El bucle tardó ${end - start} ms` ); // restamos números en lugar de fechas ``` ## Benchmarking -If we want a reliable benchmark of CPU-hungry function, we should be careful. +Si queremos realizar una medición de performance confiable de una función que vaya a consumir muchos recursos de CPU, debemos hacerlo con precaución. -For instance, let's measure two functions that calculate the difference between two dates: which one is faster? +En este caso, vamos a medir dos funciones que calculen la diferencia entre dos fechas determinadas: ¿Cuál es la más rápida? -Such performance measurements are often called "benchmarks". +Estas evaluaciones de performance son comúnmente denominadas _"benchmarks"_. ```js -// we have date1 and date2, which function faster returns their difference in ms? +// Tenemos date1 y date2. ¿Cuál de las siguientes funciones nos devuelve su diferencia, expresada en ms, más rápido? function diffSubtract(date1, date2) { return date2 - date1; } -// or +// o function diffGetTime(date1, date2) { return date2.getTime() - date1.getTime(); } ``` -These two do exactly the same thing, but one of them uses an explicit `date.getTime()` to get the date in ms, and the other one relies on a date-to-number transform. Their result is always the same. +Ambas funciones hacen exactamente lo mismo, pero una de ellas utiliza explícitamente `date.getTime()` para obtener la fecha expresada en ms, y la otra se basa en la autoconversión de fecha a número. Sin embargo, su resultado es el mismo. -So, which one is faster? +Pero entonces, ¿Cuál de las dos es más rápida? -The first idea may be to run them many times in a row and measure the time difference. For our case, functions are very simple, so we have to do it at least 100000 times. +La primera idea sería ejecutar las funciones varias veces seguidas y medir la diferencia de tiempo de ejecución. En nuestro caso, las funciones son bastante simples, por lo que debemos hacerlo al menos unas 100000 veces. -Let's measure: +Midamos: ```js run function diffSubtract(date1, date2) { @@ -308,23 +308,23 @@ function bench(f) { return Date.now() - start; } -alert( 'Time of diffSubtract: ' + bench(diffSubtract) + 'ms' ); -alert( 'Time of diffGetTime: ' + bench(diffGetTime) + 'ms' ); +alert("Tiempo de ejecución de diffSubtract: " + bench(diffSubtract) + "ms"); +alert("Tiempo de ejecución de diffGetTime: " + bench(diffGetTime) + "ms"); ``` -Wow! Using `getTime()` is so much faster! That's because there's no type conversion, it is much easier for engines to optimize. +¡Guau! ¡Utilizando el método `getTime()` es mucho más rápido! Esto es debido a que no se produce ninguna conversión de tipo de dato, por lo que se le hace mucho mas fácil de optimizar a los motores. -Okay, we have something. But that's not a good benchmark yet. +Bueno, ya tenemos algo. Pero todavía no es un _benchmark_ completo. -Imagine that at the time of running `bench(diffSubtract)` CPU was doing something in parallel, and it was taking resources. And by the time of running `bench(diffGetTime)` that work has finished. +Imaginemos que en el momento en el que `bench(diffSubtract)` estaba corriendo, la CPU estaba ejecutando otra tarea en paralelo que consumía recursos y al momento de correr `bench(diffGetTime)` esa tarea ya había concluido. -A pretty real scenario for a modern multi-process OS. +Es un escenario bastante posible para los sistemas operativos multi-procesos de hoy en día. -As a result, the first benchmark will have less CPU resources than the second. That may lead to wrong results. +Como consecuencia, el primer _benchmark_ dispondrá de una menor cantidad de recursos de CPU que el segundo, lo que podría generar resultados engañosos. -**For more reliable benchmarking, the whole pack of benchmarks should be rerun multiple times.** +**Para realizar un _benchmarking_ más confiable, todas las _benchmarks_ deberían ser ejecutadas múltiples veces.** -For example, like this: +Como por ejemplo: ```js run function diffSubtract(date1, date2) { @@ -348,86 +348,86 @@ let time1 = 0; let time2 = 0; *!* -// run bench(diffSubtract) and bench(diffGetTime) each 10 times alternating +// ejecuta bench(diffSubtract) y bench(diffGetTime) cada 10 iteraciones alternándolas for (let i = 0; i < 10; i++) { time1 += bench(diffSubtract); time2 += bench(diffGetTime); } */!* -alert( 'Total time for diffSubtract: ' + time1 ); -alert( 'Total time for diffGetTime: ' + time2 ); +alert( 'Tiempo total de diffSubtract: ' + time1 ); +alert( 'Tiempo total de diffGetTime: ' + time2 ); ``` -Modern JavaScript engines start applying advanced optimizations only to "hot code" that executes many times (no need to optimize rarely executed things). So, in the example above, first executions are not well-optimized. We may want to add a heat-up run: +Los motores modernos de JavaScript realizan una optimización avanzada únicamente a los bloques de código que se ejecutan varias veces (no es necesario optimizar código que raramente se ejecuta). En el ejemplo de abajo, las primeras ejecuciones no están bien optimizadas, por lo que quizás querríamos agregar ejecuciones antes de realizar el _benchmark_, a modo de "precalentamiento": ```js -// added for "heating up" prior to the main loop +// Agregamos las funciones, antes de realizar el *benchmark*, a modo de "precalentamiento" bench(diffSubtract); bench(diffGetTime); -// now benchmark +// Ahora sí realizamos el benchmark for (let i = 0; i < 10; i++) { time1 += bench(diffSubtract); time2 += bench(diffGetTime); } ``` -```warn header="Be careful doing microbenchmarking" -Modern JavaScript engines perform many optimizations. They may tweak results of "artificial tests" compared to "normal usage", especially when we benchmark something very small, such as how an operator works, or a built-in function. So if you seriously want to understand performance, then please study how the JavaScript engine works. And then you probably won't need microbenchmarks at all. +```warn header="Cuidado con los micro-benchmarks" +Los motores Modernos de JavaScript realizan varias optimizaciones al ejecutar código. Esto podría alterar los resultados de las "pruebas artificiales" respecto del "uso normal", especialmente cuando hacemos un *benchmark* tan pequeño, como por ejemplo: el funcionamiento de un operador o una función incorporada de JavaScript. Por esta razón, si se quiere entender más en profundidad cómo funciona la performance, se recomienda estudiar el funcionamiento del motor de JavaScript. Probablemente no necesites realizar *microbenchmarks* en absoluto. -The great pack of articles about V8 can be found at . +Existe un excelente conjunto de artículos acerca del motor V8 en . ``` -## Date.parse from a string +## Date.parse a partir de un string -The method [Date.parse(str)](mdn:js/Date/parse) can read a date from a string. +El método [Date.parse(str)](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Date/parse) permite leer una fecha desde un string. -The string format should be: `YYYY-MM-DDTHH:mm:ss.sssZ`, where: +El formato del string debe ser: `YYYY-MM-DDTHH:mm:ss.sssZ`, donde: -- `YYYY-MM-DD` -- is the date: year-month-day. -- The character `"T"` is used as the delimiter. -- `HH:mm:ss.sss` -- is the time: hours, minutes, seconds and milliseconds. -- The optional `'Z'` part denotes the time zone in the format `+-hh:mm`. A single letter `Z` would mean UTC+0. +- `YYYY-MM-DD` -- es la fecha: año-mes-día. +- El carácter `"T"` se usa como delimitador. +- `HH:mm:ss.sss` -- es la hora: horas, minutos, segundos y milisegundos. +- El carácter `'Z'` es opcional y especifica la zona horaria, con el formato `+-hh:mm`. Si se incluye únicamente la letra `Z` equivale a UTC+0. -Shorter variants are also possible, like `YYYY-MM-DD` or `YYYY-MM` or even `YYYY`. +También es posible pasar como string variantes abreviadas, tales como `YYYY-MM-DD` o `YYYY-MM` o incluso `YYYY`. -The call to `Date.parse(str)` parses the string in the given format and returns the timestamp (number of milliseconds from 1 Jan 1970 UTC+0). If the format is invalid, returns `NaN`. +La llamada del método `Date.parse(str)` convierte el string en el formato especificado y nos devuelve un _timestamp_ (cantidad de milisegundos transcurridos desde el 1° de Enero de 1970 UTC+0). Si el formato del string no es válido, devuelve es `NaN`. -For instance: +Por ejemplo: ```js run -let ms = Date.parse('2012-01-26T13:51:50.417-07:00'); +let ms = Date.parse("2012-01-26T13:51:50.417-07:00"); alert(ms); // 1327611110417 (timestamp) ``` -We can instantly create a `new Date` object from the timestamp: +Podemos crear un objeto `new Date` instantáneamente desde el timestamp: ```js run -let date = new Date( Date.parse('2012-01-26T13:51:50.417-07:00') ); +let date = new Date(Date.parse("2012-01-26T13:51:50.417-07:00")); alert(date); ``` -## Summary +## Resumen -- Date and time in JavaScript are represented with the [Date](mdn:js/Date) object. We can't create "only date" or "only time": `Date` objects always carry both. -- Months are counted from zero (yes, January is a zero month). -- Days of week in `getDay()` are also counted from zero (that's Sunday). -- `Date` auto-corrects itself when out-of-range components are set. Good for adding/subtracting days/months/hours. -- Dates can be subtracted, giving their difference in milliseconds. That's because a `Date` becomes the timestamp when converted to a number. -- Use `Date.now()` to get the current timestamp fast. +- En JavaScript, la fecha y la hora se representan con el objeto [Date](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Date). No es posible obtener sólo la fecha o sólo la hora: los objetos `Date` incluyen ambas. +- Los meses se cuentan desde el cero (sí: enero es el mes cero). +- Los días de la semana en `getDay()` también se cuentan desde el cero (que corresponde al día Domingo). +- El objeto `Date` se autocorrige cuando recibe un componente fuera de rango. Es útil para sumar o restar días/meses/horas. +- Las fechas se pueden restar entre sí, dando el resultado expresado en milisegundos: esto se debe a que el objeto `Date` toma el valor del _timestamp_ cuando es convertido a número. +- Para obtener el _timestamp_ actual de manera inmediata se utiliza `Date.now()`. -Note that unlike many other systems, timestamps in JavaScript are in milliseconds, not in seconds. +Nótese que, a diferencia de otros sistemas, los _timestamps_ en JavaScript están representados en milisegundos (ms), no en segundos. -Sometimes we need more precise time measurements. JavaScript itself does not have a way to measure time in microseconds (1 millionth of a second), but most environments provide it. For instance, browser has [performance.now()](mdn:api/Performance/now) that gives the number of milliseconds from the start of page loading with microsecond precision (3 digits after the point): +Suele suceder que necesitemos tomar medidas de tiempo más precisas. En sí, JavaScript no tiene incorporada una manera de medir el tiempo en microsegundos (1 millonésima parte de segundo), pero la mayoría de los entornos de ejecución sí lo permiten. Por ejemplo, el navegador posee [performance.now()](https://developer.mozilla.org/es/docs/Web/API/Performance/now) que nos permite saber la cantidad de milisegundos que tarda una página en cargar, con una precisión de microsegundos (3 dígitos después del punto): ```js run -alert(`Loading started ${performance.now()}ms ago`); -// Something like: "Loading started 34731.26000000001ms ago" -// .26 is microseconds (260 microseconds) -// more than 3 digits after the decimal point are precision errors, only the first 3 are correct +alert(`La carga de la página comenzó hace ${performance.now()}ms`); +// Devuelve algo así como: "La carga de la página comenzó hace 34731.26000000001ms" +// los dígitos .26 son microsegundos (260 microsegundos) +// Sólo los 3 primeros dígitos después del punto decimal son correctos, los demás son errores de precisión. ``` -Node.js has `microtime` module and other ways. Technically, almost any device and environment allows to get more precision, it's just not in `Date`. +Node.js posee el módulo `microtime`, entre otros. Prácticamente casi cualquier dispositivo y entorno de ejecución permite mayor precisión, sólo que no es posible almacenarla en `Date`. diff --git a/1-js/05-data-types/12-json/1-serialize-object/task.md b/1-js/05-data-types/12-json/1-serialize-object/task.md index 53343e4c3..fb9d9053d 100644 --- a/1-js/05-data-types/12-json/1-serialize-object/task.md +++ b/1-js/05-data-types/12-json/1-serialize-object/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# Turn the object into JSON and back +# Convierte el objeto en JSON y de vuelta -Turn the `user` into JSON and then read it back into another variable. +Convierte el `user` a JSON y luego léalo de vuelta en otra variable. ```js let user = { diff --git a/1-js/05-data-types/12-json/2-serialize-event-circular/solution.md b/1-js/05-data-types/12-json/2-serialize-event-circular/solution.md index 7a3a533b0..44ba79ef1 100644 --- a/1-js/05-data-types/12-json/2-serialize-event-circular/solution.md +++ b/1-js/05-data-types/12-json/2-serialize-event-circular/solution.md @@ -26,5 +26,5 @@ alert( JSON.stringify(meetup, function replacer(key, value) { */ ``` -Here we also need to test `key==""` to exclude the first call where it is normal that `value` is `meetup`. +Aquí también necesitamos verificar `key==""` para excluir el primer llamado donde es normal que `valor` sea `meetup`. diff --git a/1-js/05-data-types/12-json/2-serialize-event-circular/task.md b/1-js/05-data-types/12-json/2-serialize-event-circular/task.md index 3755a24aa..13d8038b1 100644 --- a/1-js/05-data-types/12-json/2-serialize-event-circular/task.md +++ b/1-js/05-data-types/12-json/2-serialize-event-circular/task.md @@ -2,13 +2,13 @@ importance: 5 --- -# Exclude backreferences +# Excluir referencias circulares -In simple cases of circular references, we can exclude an offending property from serialization by its name. +En casos simples de referencias circulares, podemos excluir una propiedad infractora de la serialización por su nombre. -But sometimes we can't just use the name, as it may be used both in circular references and normal properties. So we can check the property by its value. +Pero a veces no podemos usar el nombre, ya que puede usarse tanto en referencias circulares como en propiedades normales. Entonces podemos verificar la propiedad por su valor. -Write `replacer` function to stringify everything, but remove properties that reference `meetup`: +Escriba la función `replacer` para convertir todo a string, pero elimine las propiedades que hacen referencia a `meetup`: ```js run let room = { @@ -22,16 +22,16 @@ let meetup = { }; *!* -// circular references +// referencias circulares room.occupiedBy = meetup; meetup.self = meetup; */!* alert( JSON.stringify(meetup, function replacer(key, value) { - /* your code */ + /* tu código */ })); -/* result should be: +/* el resultado debería ser: { "title":"Conference", "occupiedBy":[{"name":"John"},{"name":"Alice"}], diff --git a/1-js/05-data-types/12-json/article.md b/1-js/05-data-types/12-json/article.md index 133ffb353..0a449bbfe 100644 --- a/1-js/05-data-types/12-json/article.md +++ b/1-js/05-data-types/12-json/article.md @@ -1,10 +1,10 @@ -# JSON methods, toJSON +# Métodos JSON, toJSON -Let's say we have a complex object, and we'd like to convert it into a string, to send it over a network, or just to output it for logging purposes. +Digamos que tenemos un objeto complejo y nos gustaría convertirlo en un string (cadena de caracteres), para enviarlos por la red, o simplemente mostrarlo para fines de registro. -Naturally, such a string should include all important properties. +Naturalmente, tal string debe incluir todas las propiedades importantes. -We could implement the conversion like this: +Podríamos implementar la conversión de esta manera: ```js run let user = { @@ -21,20 +21,20 @@ let user = { alert(user); // {name: "John", age: 30} ``` -...But in the process of development, new properties are added, old properties are renamed and removed. Updating such `toString` every time can become a pain. We could try to loop over properties in it, but what if the object is complex and has nested objects in properties? We'd need to implement their conversion as well. +...Pero en el proceso de desarrollo se agregan nuevas propiedades, y otras son renombradas y eliminadas. Actualizar el `toString` cada vez se vuelve penoso. Podemos intentar recorrer las propiedades, pero ¿qué pasa si el objeto es complejo y tiene objetos anidados en las propiedades? Vamos a necesitar implementar su conversión también. -Luckily, there's no need to write the code to handle all this. The task has been solved already. +Por suerte no hay necesidad de escribir el código para manejar todo esto. La tarea ya ha sido resuelta. ## JSON.stringify -The [JSON](https://en.wikipedia.org/wiki/JSON) (JavaScript Object Notation) is a general format to represent values and objects. It is described as in [RFC 4627](https://tools.ietf.org/html/rfc4627) standard. Initially it was made for JavaScript, but many other languages have libraries to handle it as well. So it's easy to use JSON for data exchange when the client uses JavaScript and the server is written on Ruby/PHP/Java/Whatever. +[JSON](https://es.wikipedia.org/wiki/JSON) (Notación de objeto JavaScript) es un formato general para representar valores y objetos. Se lo describe como el estándar [RFC 4627](http://tools.ietf.org/html/rfc4627). En un principio fue creado para Javascript, pero varios lenguajes tienen librerías para manejarlo también. Por lo tanto es fácil utilizar JSON para intercambio de información cuando el cliente utiliza JavaScript y el servidor está escrito en Ruby, PHP, Java, lo que sea. -JavaScript provides methods: +JavaScript proporciona métodos: -- `JSON.stringify` to convert objects into JSON. -- `JSON.parse` to convert JSON back into an object. +- `JSON.stringify` para convertir objetos a JSON. +- `JSON.parse` para convertir JSON de vuelta a un objeto. -For instance, here we `JSON.stringify` a student: +Por ejemplo, aquí hacemos `JSON.stringify` a student: ```js run let student = { name: 'John', @@ -48,11 +48,11 @@ let student = { let json = JSON.stringify(student); */!* -alert(typeof json); // we've got a string! +alert(typeof json); // ¡obtenemos un string! alert(json); *!* -/* JSON-encoded object: +/* Objeto JSON-codificado: { "name": "John", "age": 30, @@ -64,19 +64,19 @@ alert(json); */!* ``` -The method `JSON.stringify(student)` takes the object and converts it into a string. +El método `JSON.stringify(student)` toma al objeto y lo convierte a un string. -The resulting `json` string is called a *JSON-encoded* or *serialized* or *stringified* or *marshalled* object. We are ready to send it over the wire or put into a plain data store. +La cadena de caracteres `json` resultante se llama objeto *JSON-codificado* o *serializado* o *convertido a String* o *reunido*. Estamos listos para enviarlo por la red o colocarlo en el almacenamiento de información simple. -Please note that a JSON-encoded object has several important differences from the object literal: +Por favor tomar nota que el objeto JSON-codificado tiene varias diferencias importantes con el objeto literal: -- Strings use double quotes. No single quotes or backticks in JSON. So `'John'` becomes `"John"`. -- Object property names are double-quoted also. That's obligatory. So `age:30` becomes `"age":30`. +- Los strings utilizan comillas dobles. No hay comillas simples o acentos abiertos en JSON. Por lo tanto `'John'` pasa a ser `"John"`. +- Los nombres de propiedades de objeto también llevan comillas dobles. Eso es obligatorio. Por lo tanto `age:30` pasa a ser `"age":30`. -`JSON.stringify` can be applied to primitives as well. +`JSON.stringify` puede ser aplicado a los tipos de datos primitivos también. -JSON supports following data types: +JSON admite los siguientes tipos de datos: - Objects `{ ... }` - Arrays `[ ... ]` @@ -86,13 +86,13 @@ JSON supports following data types: - boolean values `true/false`, - `null`. -For instance: +Por ejemplo: ```js run -// a number in JSON is just a number +// un número en JSON es sólo un número alert( JSON.stringify(1) ) // 1 -// a string in JSON is still a string, but double-quoted +// un string en JSON sigue siendo una cadena de caracteres, pero con comillas dobles alert( JSON.stringify('test') ) // "test" alert( JSON.stringify(true) ); // true @@ -100,31 +100,31 @@ alert( JSON.stringify(true) ); // true alert( JSON.stringify([1, 2, 3]) ); // [1,2,3] ``` -JSON is data-only language-independent specification, so some JavaScript-specific object properties are skipped by `JSON.stringify`. +JSON es una especificación de sólo datos independiente del lenguaje, por lo tanto algunas propiedades de objeto específicas de Javascript son omitidas por `JSON.stringify`. -Namely: +A saber: -- Function properties (methods). -- Symbolic keys and values. -- Properties that store `undefined`. +- Propiedades de funciones (métodos). +- Propiedades simbólicas. +- Propiedades que almacenan `undefined`. ```js run let user = { - sayHi() { // ignored + sayHi() { // ignorado alert("Hello"); }, - [Symbol("id")]: 123, // ignored - something: undefined // ignored + [Symbol("id")]: 123, // ignorado + something: undefined // ignorado }; -alert( JSON.stringify(user) ); // {} (empty object) +alert( JSON.stringify(user) ); // {} (objeto vacío) ``` -Usually that's fine. If that's not what we want, then soon we'll see how to customize the process. +Normalmente esto está bien. Si esto no es lo que queremos, pronto veremos cómo personalizar el proceso. -The great thing is that nested objects are supported and converted automatically. +Lo mejor es que se permiten objetos anidados y se convierten automáticamente. -For instance: +Por ejemplo: ```js run let meetup = { @@ -138,7 +138,7 @@ let meetup = { }; alert( JSON.stringify(meetup) ); -/* The whole structure is stringified: +/* La estructura completa es convertida a String: { "title":"Conference", "room":{"number":23,"participants":["john","ann"]}, @@ -146,9 +146,9 @@ alert( JSON.stringify(meetup) ); */ ``` -The important limitation: there must be no circular references. +La limitación importante: no deben existir referencias circulares. -For instance: +Por ejemplo: ```js run let room = { @@ -160,41 +160,41 @@ let meetup = { participants: ["john", "ann"] }; -meetup.place = room; // meetup references room -room.occupiedBy = meetup; // room references meetup +meetup.place = room; // meetup tiene referencia a room +room.occupiedBy = meetup; // room hace referencia a meetup *!* -JSON.stringify(meetup); // Error: Converting circular structure to JSON +JSON.stringify(meetup); // Error: Convirtiendo estructura circular a JSON */!* ``` -Here, the conversion fails, because of circular reference: `room.occupiedBy` references `meetup`, and `meetup.place` references `room`: +Aquí, la conversión falla debido a una referencia circular: `room.occupiedBy` hace referencia a `meetup`, y `meetup.place` hace referencia a `room`: ![](json-meetup.svg) -## Excluding and transforming: replacer +## Excluyendo y transformando: sustituto -The full syntax of `JSON.stringify` is: +La sintaxis completa de `JSON.stringify` es: ```js let json = JSON.stringify(value[, replacer, space]) ``` value -: A value to encode. +: Un valor para codificar. replacer -: Array of properties to encode or a mapping function `function(key, value)`. +: Array de propiedades para codificar o una función de mapeo `function(propiedad, valor)`. space -: Amount of space to use for formatting +: Cantidad de espacio para usar para el formateo -Most of the time, `JSON.stringify` is used with the first argument only. But if we need to fine-tune the replacement process, like to filter out circular references, we can use the second argument of `JSON.stringify`. +La mayor parte del tiempo, `JSON.stringify` es utilizado con el primer argumento unicamente. Pero si necesitamos ajustar el proceso de sustitución, como para filtrar las referencias circulares, podemos utilizar el segundo argumento de `JSON.stringify`. -If we pass an array of properties to it, only these properties will be encoded. +Si pasamos un array de propiedades a él, solamente éstas propiedades serán codificadas. -For instance: +Por ejemplo: ```js run let room = { @@ -204,18 +204,18 @@ let room = { let meetup = { title: "Conference", participants: [{name: "John"}, {name: "Alice"}], - place: room // meetup references room + place: room // meetup hace referencia a room }; -room.occupiedBy = meetup; // room references meetup +room.occupiedBy = meetup; // room hace referencia a meetup alert( JSON.stringify(meetup, *!*['title', 'participants']*/!*) ); // {"title":"Conference","participants":[{},{}]} ``` -Here we are probably too strict. The property list is applied to the whole object structure. So the objects in `participants` are empty, because `name` is not in the list. +Aquí probablemente seamos demasiado estrictos. La lista de propiedades se aplica a toda la estructura de objeto. Por lo tanto los objetos en `participants` están vacíos, porque `name` no está en la lista. -Let's include in the list every property except `room.occupiedBy` that would cause the circular reference: +Incluyamos en la lista todas las propiedades excepto `room.occupiedBy` esto causaría la referencia circular: ```js run let room = { @@ -225,10 +225,10 @@ let room = { let meetup = { title: "Conference", participants: [{name: "John"}, {name: "Alice"}], - place: room // meetup references room + place: room // meetup hace referencia a room }; -room.occupiedBy = meetup; // room references meetup +room.occupiedBy = meetup; // room hace referencia a meetup alert( JSON.stringify(meetup, *!*['title', 'participants', 'place', 'name', 'number']*/!*) ); /* @@ -240,13 +240,13 @@ alert( JSON.stringify(meetup, *!*['title', 'participants', 'place', 'name', 'num */ ``` -Now everything except `occupiedBy` is serialized. But the list of properties is quite long. +Ahora todo con excepción de `occupiedBy` está serializado. Pero la lista de propiedades es bastante larga. -Fortunately, we can use a function instead of an array as the `replacer`. +Por suerte podemos utilizar una función en lugar de un array como el `sustituto`. -The function will be called for every `(key, value)` pair and should return the "replaced" value, which will be used instead of the original one. Or `undefined` if the value is to be skipped. +La función se llamará para cada par de `(propiedad, valor)` y debe devolver el valor "sustituido", el cual será utilizado en lugar del original. O `undefined` si el valor va a ser omitido. -In our case, we can return `value` "as is" for everything except `occupiedBy`. To ignore `occupiedBy`, the code below returns `undefined`: +En nuestro caso, podemos devolver `value` "tal cual" para todo excepto `occupiedBy`. Para ignorar `occupiedBy`, el código de abajo devuelve `undefined`: ```js run let room = { @@ -256,17 +256,17 @@ let room = { let meetup = { title: "Conference", participants: [{name: "John"}, {name: "Alice"}], - place: room // meetup references room + place: room // meetup hace referencia a room }; -room.occupiedBy = meetup; // room references meetup +room.occupiedBy = meetup; // room hace referencia a meetup alert( JSON.stringify(meetup, function replacer(key, value) { alert(`${key}: ${value}`); return (key == 'occupiedBy') ? undefined : value; })); -/* key:value pairs that come to replacer: +/* pares de propiedad:valor que llegan a replacer: : [object Object] title: Conference participants: [object Object],[object Object] @@ -280,20 +280,20 @@ occupiedBy: [object Object] */ ``` -Please note that `replacer` function gets every key/value pair including nested objects and array items. It is applied recursively. The value of `this` inside `replacer` is the object that contains the current property. +Por favor tenga en cuenta que la función `replacer` recibe todos los pares de propiedad/valor incluyendo objetos anidados y elementos de array. Se aplica recursivamente. El valor de `this` dentro de `replacer` es el objeto que contiene la propiedad actual. -The first call is special. It is made using a special "wrapper object": `{"": meetup}`. In other words, the first `(key, value)` pair has an empty key, and the value is the target object as a whole. That's why the first line is `":[object Object]"` in the example above. +El primer llamado es especial. Se realiza utilizando un "Objeto contenedor" especial: `{"": meetup}`. En otras palabras, el primer par `(propiedad, valor)` tiene una propiedad vacía, y el valor es el objeto objetivo como un todo. Es por esto que la primer línea es `":[object Object]"` en el ejemplo de arriba. -The idea is to provide as much power for `replacer` as possible: it has a chance to analyze and replace/skip even the whole object if necessary. +La idea es proporcionar tanta capacidad para `replacer` como sea posible: tiene una oportunidad de analizar y reemplazar/omitir incluso el objeto entero si es necesario. -## Formatting: space +## Formato: espacio -The third argument of `JSON.stringify(value, replacer, space)` is the number of spaces to use for pretty formatting. +El tercer argumento de `JSON.stringify(value, replacer, space)` es el número de espacios a utilizar para un formato agradable. -Previously, all stringified objects had no indents and extra spaces. That's fine if we want to send an object over a network. The `space` argument is used exclusively for a nice output. +Anteriormente todos los objetos convertidos a String no tenían sangría ni espacios adicionales. Eso está bien si queremos enviar un objeto por la red. El argumento `space` es utilizado exclusivamente para una salida agradable. -Here `space = 2` tells JavaScript to show nested objects on multiple lines, with indentation of 2 spaces inside an object: +Aquí `space = 2` le dice a JavaScript que muestre objetos anidados en varias líneas, con sangría de 2 espacios dentro de un objeto: ```js run let user = { @@ -306,7 +306,7 @@ let user = { }; alert(JSON.stringify(user, null, 2)); -/* two-space indents: +/* sangría de dos espacios: { "name": "John", "age": 25, @@ -317,7 +317,7 @@ alert(JSON.stringify(user, null, 2)); } */ -/* for JSON.stringify(user, null, 4) the result would be more indented: +/* para JSON.stringify(user, null, 4) el resultado sería más indentado: { "name": "John", "age": 25, @@ -329,15 +329,15 @@ alert(JSON.stringify(user, null, 2)); */ ``` -The third argument can also be a string. In this case, the string is used for indentation instead of a number of spaces. +El tercer argumento puede ser también string. En ese caso el string será usado como indentación en lugar de un número de espacios. -The `space` parameter is used solely for logging and nice-output purposes. +El argumento `space` es utilizado únicamente para propósitos de registro y agradable impresión. -## Custom "toJSON" +## "toJSON" Personalizado -Like `toString` for string conversion, an object may provide method `toJSON` for to-JSON conversion. `JSON.stringify` automatically calls it if available. +Tal como `toString` para conversión de String, un objeto puede proporcionar el método `toJSON` para conversión a JSON. `JSON.stringify` automáticamente la llama si está disponible. -For instance: +Por ejemplo: ```js run let room = { @@ -362,9 +362,9 @@ alert( JSON.stringify(meetup) ); */ ``` -Here we can see that `date` `(1)` became a string. That's because all dates have a built-in `toJSON` method which returns such kind of string. +Aquí podemos ver que `date` `(1)` se convirtió en un string. Esto es debido a que todas las fechas tienen un método `toJSON` incorporado que devuelve este tipo de string. -Now let's add a custom `toJSON` for our object `room` `(2)`: +Ahora incluyamos un `toJSON` personalizado para nuestro objeto `room` `(2)`: ```js run let room = { @@ -396,28 +396,28 @@ alert( JSON.stringify(meetup) ); */ ``` -As we can see, `toJSON` is used both for the direct call `JSON.stringify(room)` and when `room` is nested in another encoded object. +Como podemos ver, `toJSON` es utilizado para ambos el llamado directo `JSON.stringify(room)` y cuando `room` está anidado en otro objeto codificado. ## JSON.parse -To decode a JSON-string, we need another method named [JSON.parse](mdn:js/JSON/parse). +Para decodificar un string JSON, necesitamos otro método llamado [JSON.parse](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/JSON/parse). -The syntax: +La sintaxis: ```js let value = JSON.parse(str[, reviver]); ``` str -: JSON-string to parse. +: string JSON para analizar. reviver -: Optional function(key,value) that will be called for each `(key, value)` pair and can transform the value. +: function(key,value) opcional que será llamado para cada par `(propiedad, valor)` y puede transformar el valor. -For instance: +Por ejemplo: ```js run -// stringified array +// array convertido en String let numbers = "[0, 1, 2, 3]"; numbers = JSON.parse(numbers); @@ -425,7 +425,7 @@ numbers = JSON.parse(numbers); alert( numbers[1] ); // 1 ``` -Or for nested objects: +O para objetos anidados: ```js run let userData = '{ "name": "John", "age": 35, "isAdmin": false, "friends": [0,1,2,3] }'; @@ -435,40 +435,40 @@ let user = JSON.parse(userData); alert( user.friends[1] ); // 1 ``` -The JSON may be as complex as necessary, objects and arrays can include other objects and arrays. But they must obey the same JSON format. +El JSON puede ser tan complejo como sea necesario, los objetos y arrays pueden incluir otros objetos y arrays. Pero deben cumplir el mismo formato JSON. -Here are typical mistakes in hand-written JSON (sometimes we have to write it for debugging purposes): +Aquí algunos de los errores más comunes al escribir JSON a mano (a veces tenemos que escribirlo por debugging): ```js let json = `{ - *!*name*/!*: "John", // mistake: property name without quotes - "surname": *!*'Smith'*/!*, // mistake: single quotes in value (must be double) - *!*'isAdmin'*/!*: false // mistake: single quotes in key (must be double) - "birthday": *!*new Date(2000, 2, 3)*/!*, // mistake: no "new" is allowed, only bare values - "friends": [0,1,2,3] // here all fine + *!*name*/!*: "John", // error: nombre de propiedad sin comillas + "surname": *!*'Smith'*/!*, // error: comillas simples en valor (debe ser doble) + *!*'isAdmin'*/!*: false // error: comillas simples en propiedad (debe ser doble) + "birthday": *!*new Date(2000, 2, 3)*/!*, // error: no se permite "new", únicamente valores simples + "friends": [0,1,2,3] // aquí todo bien }`; ``` -Besides, JSON does not support comments. Adding a comment to JSON makes it invalid. +Además, JSON no admite comentarios. Agregar un comentario a JSON lo hace inválido. -There's another format named [JSON5](https://json5.org/), which allows unquoted keys, comments etc. But this is a standalone library, not in the specification of the language. +Existe otro formato llamado [JSON5](https://json5.org/), que permite claves sin comillas, comentarios, etcétera. Pero es una librería independiente, no una especificación del lenguaje. -The regular JSON is that strict not because its developers are lazy, but to allow easy, reliable and very fast implementations of the parsing algorithm. +El JSON normal es tan estricto no porque sus desarrolladores sean flojos, sino para permitir la implementación fácil, confiable y muy rápida del algoritmo analizador. -## Using reviver +## Utilizando reactivador -Imagine, we got a stringified `meetup` object from the server. +Imagina esto, obtenemos un objeto `meetup` convertido en String desde el servidor. -It looks like this: +Se ve así: ```js // title: (meetup title), date: (meetup date) let str = '{"title":"Conference","date":"2017-11-30T12:00:00.000Z"}'; ``` -...And now we need to *deserialize* it, to turn back into JavaScript object. +...Y ahora necesitamos *deserializarlo*, para convertirlo de vuelta a un objeto JavaScript. -Let's do it by calling `JSON.parse`: +Hagámoslo llamando a `JSON.parse`: ```js run let str = '{"title":"Conference","date":"2017-11-30T12:00:00.000Z"}'; @@ -480,11 +480,11 @@ alert( meetup.date.getDate() ); // Error! */!* ``` -Whoops! An error! +¡Upss! ¡Un error! -The value of `meetup.date` is a string, not a `Date` object. How could `JSON.parse` know that it should transform that string into a `Date`? +El valor de `meetup.date` es un string, no un objeto `Date`. Cómo puede saber `JSON.parse` que debe transformar ese string a una `Date`? -Let's pass to `JSON.parse` the reviving function as the second argument, that returns all values "as is", but `date` will become a `Date`: +Le pasemos a `JSON.parse` la función reactivadora como el segundo argumento, esto devuelve todos los valores "tal cual", pero `date` se convertirá en una `Date`: ```js run let str = '{"title":"Conference","date":"2017-11-30T12:00:00.000Z"}'; @@ -496,10 +496,10 @@ let meetup = JSON.parse(str, function(key, value) { }); */!* -alert( meetup.date.getDate() ); // now works! +alert( meetup.date.getDate() ); // ¡Ahora funciona! ``` -By the way, that works for nested objects as well: +Por cierto, esto funciona también para objetos anidados: ```js run let schedule = `{ @@ -515,16 +515,16 @@ schedule = JSON.parse(schedule, function(key, value) { }); *!* -alert( schedule.meetups[1].date.getDate() ); // works! +alert( schedule.meetups[1].date.getDate() ); // ¡Funciona! */!* ``` -## Summary +## Resumen -- JSON is a data format that has its own independent standard and libraries for most programming languages. -- JSON supports plain objects, arrays, strings, numbers, booleans, and `null`. -- JavaScript provides methods [JSON.stringify](mdn:js/JSON/stringify) to serialize into JSON and [JSON.parse](mdn:js/JSON/parse) to read from JSON. -- Both methods support transformer functions for smart reading/writing. -- If an object has `toJSON`, then it is called by `JSON.stringify`. +- JSON es un formato de datos que tiene su propio estándar independiente y librerías para la mayoría de los lenguajes de programación. +- JSON admite objetos simples, arrays, strings, números, booleanos y `null`. +- JavaScript proporciona los métodos [JSON.stringify](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/JSON/stringify) para serializar en JSON y [JSON.parse](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) para leer desde JSON. +- Ambos métodos admiten funciones transformadoras para lectura/escritura inteligente. +- Si un objeto tiene `toJSON`, entonces es llamado por` JSON.stringify`. diff --git a/1-js/05-data-types/index.md b/1-js/05-data-types/index.md index 246e2bc91..e2936062b 100644 --- a/1-js/05-data-types/index.md +++ b/1-js/05-data-types/index.md @@ -1,3 +1,3 @@ -# Data types +# Tipos de datos -More data structures and more in-depth study of the types. +Más estructuras de datos y un estudio más profundo de los tipos. diff --git a/1-js/06-advanced-functions/01-recursion/01-sum-to/solution.md b/1-js/06-advanced-functions/01-recursion/01-sum-to/solution.md index 11667f940..c7391337b 100644 --- a/1-js/06-advanced-functions/01-recursion/01-sum-to/solution.md +++ b/1-js/06-advanced-functions/01-recursion/01-sum-to/solution.md @@ -1,4 +1,4 @@ -The solution using a loop: +La solución usando un bucle: ```js run function sumTo(n) { @@ -12,7 +12,7 @@ function sumTo(n) { alert( sumTo(100) ); ``` -The solution using recursion: +La solución usando recursividad: ```js run function sumTo(n) { @@ -23,7 +23,7 @@ function sumTo(n) { alert( sumTo(100) ); ``` -The solution using the formula: `sumTo(n) = n*(n+1)/2`: +La solución usando la fórmula: `sumTo(n) = n*(n+1)/2`: ```js run function sumTo(n) { @@ -33,8 +33,8 @@ function sumTo(n) { alert( sumTo(100) ); ``` -P.S. Naturally, the formula is the fastest solution. It uses only 3 operations for any number `n`. The math helps! +P.D. Naturalmente, la fórmula es la solución más rápida. Utiliza solo 3 operaciones para cualquier número `n` ¡Las matemáticas ayudan! -The loop variant is the second in terms of speed. In both the recursive and the loop variant we sum the same numbers. But the recursion involves nested calls and execution stack management. That also takes resources, so it's slower. +La variación con el bucle es la segunda en términos de velocidad. Tanto en la variante recursiva como en el bucle sumamos los mismos números. Pero la recursión implica llamadas anidadas y gestión de la pila de ejecución. Eso también requiere recursos, por lo que es más lento. -P.P.S. Some engines support the "tail call" optimization: if a recursive call is the very last one in the function, with no other calculations performed, then the outer function will not need to resume the execution, so the engine doesn't need to remember its execution context. That removes the burden on memory. But if the JavaScript engine does not support tail call optimization (most of them don't), there will be an error: maximum stack size exceeded, because there's usually a limitation on the total stack size. +P.P.D. Algunos motores admiten la optimización de "tail call": si una llamada recursiva es la última en la función, sin cálculo extra, entonces la función externa no necesitará reanudar la ejecución, por lo que el motor no necesita recordar su contexto de ejecución. Eso elimina la carga en la memoria. Pero si el motor de JavaScript no soporta la optimización "tail call" (la mayoría no lo hace), entonces habrá un error: tamaño máximo de la pila excedido, porque generalmente hay una limitación en el tamaño total de la pila. diff --git a/1-js/06-advanced-functions/01-recursion/01-sum-to/task.md b/1-js/06-advanced-functions/01-recursion/01-sum-to/task.md index cabc13290..244ca0e9e 100644 --- a/1-js/06-advanced-functions/01-recursion/01-sum-to/task.md +++ b/1-js/06-advanced-functions/01-recursion/01-sum-to/task.md @@ -2,11 +2,11 @@ importance: 5 --- -# Sum all numbers till the given one +# Suma todos los números hasta el elegido -Write a function `sumTo(n)` that calculates the sum of numbers `1 + 2 + ... + n`. +Escribe una función `sumTo(n)` que calcule la suma de los números `1 + 2 + ... + n`. -For instance: +Por ejemplo: ```js no-beautify sumTo(1) = 1 @@ -17,20 +17,20 @@ sumTo(4) = 4 + 3 + 2 + 1 = 10 sumTo(100) = 100 + 99 + ... + 2 + 1 = 5050 ``` -Make 3 solution variants: +Escribe 3 soluciones diferentes: -1. Using a for loop. -2. Using a recursion, cause `sumTo(n) = n + sumTo(n-1)` for `n > 1`. -3. Using the [arithmetic progression](https://en.wikipedia.org/wiki/Arithmetic_progression) formula. +1. Utilizando un bucle `for`. +2. Usando la recursividad, pues `sumTo(n) = n + sumTo(n-1)` para `n > 1`. +3. Utilizando la fórmula de [progresión aritmética](https://es.wikipedia.org/wiki/Progresi%C3%B3n_aritm%C3%A9tica). -An example of the result: +Un ejemplo del resultado: ```js -function sumTo(n) { /*... your code ... */ } +function sumTo(n) { /*... tu código ... */ } alert( sumTo(100) ); // 5050 ``` -P.S. Which solution variant is the fastest? The slowest? Why? +P.D. ¿Qué variante de la solución es la más rápida? ¿Y la más lenta? ¿Por qué? -P.P.S. Can we use recursion to count `sumTo(100000)`? +P.P.D. ¿Podemos usar la recursión para contar `sumTo(100000)`? diff --git a/1-js/06-advanced-functions/01-recursion/02-factorial/solution.md b/1-js/06-advanced-functions/01-recursion/02-factorial/solution.md index 09e511db5..fdaa690f3 100644 --- a/1-js/06-advanced-functions/01-recursion/02-factorial/solution.md +++ b/1-js/06-advanced-functions/01-recursion/02-factorial/solution.md @@ -1,6 +1,6 @@ -By definition, a factorial `n!` can be written as `n * (n-1)!`. +Por definición, un factorial de `n!` puede ser escrito como `n * (n-1)!`. -In other words, the result of `factorial(n)` can be calculated as `n` multiplied by the result of `factorial(n-1)`. And the call for `n-1` can recursively descend lower, and lower, till `1`. +En otras palabras, el resultado de `factorial(n)` se puede calcular como `n` multiplicado por el resultado de `factorial(n-1)`. Y la llamada de `n-1` puede descender recursivamente más y más hasta `1`. ```js run function factorial(n) { @@ -10,7 +10,7 @@ function factorial(n) { alert( factorial(5) ); // 120 ``` -The basis of recursion is the value `1`. We can also make `0` the basis here, doesn't matter much, but gives one more recursive step: +La base de la recursividad es el valor `1`. También podemos hacer `0` la base aquí, no tiene mucha importancia, pero da un paso recursivo más: ```js run function factorial(n) { diff --git a/1-js/06-advanced-functions/01-recursion/02-factorial/task.md b/1-js/06-advanced-functions/01-recursion/02-factorial/task.md index d2aef2d90..71200e3ef 100644 --- a/1-js/06-advanced-functions/01-recursion/02-factorial/task.md +++ b/1-js/06-advanced-functions/01-recursion/02-factorial/task.md @@ -2,17 +2,17 @@ importance: 4 --- -# Calculate factorial +# Calcula el factorial -The [factorial](https://en.wikipedia.org/wiki/Factorial) of a natural number is a number multiplied by `"number minus one"`, then by `"number minus two"`, and so on till `1`. The factorial of `n` is denoted as `n!` +El [factorial](https://es.wikipedia.org/wiki/Factorial) de un número natural es un número multiplicado por `"número menos uno"`, luego por `"número menos dos"`, y así sucesivamente hasta `1`. El factorial de `n` se denota como `n!` -We can write a definition of factorial like this: +Podemos escribir la definición de factorial así: ```js n! = n * (n - 1) * (n - 2) * ...*1 ``` -Values of factorials for different `n`: +Valores de factoriales para diferentes `n`: ```js 1! = 1 @@ -22,10 +22,10 @@ Values of factorials for different `n`: 5! = 5 * 4 * 3 * 2 * 1 = 120 ``` -The task is to write a function `factorial(n)` that calculates `n!` using recursive calls. +La tarea es escribir una función `factorial(n)` que calcule `n!` usando llamadas recursivas. ```js alert( factorial(5) ); // 120 ``` -P.S. Hint: `n!` can be written as `n * (n-1)!` For instance: `3! = 3*2! = 3*2*1! = 6` +P.D. Pista: `n!` puede ser escrito como `n * (n-1)!` Por ejemplo: `3! = 3*2! = 3*2*1! = 6` diff --git a/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/solution.md b/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/solution.md index 36524a45a..e81a1d4e0 100644 --- a/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/solution.md +++ b/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/solution.md @@ -1,6 +1,6 @@ -The first solution we could try here is the recursive one. +La primera solución que podemos probar aquí es la recursiva. -Fibonacci numbers are recursive by definition: +La secuencia de Fibonacci es recursiva por definición: ```js run function fib(n) { @@ -9,14 +9,14 @@ function fib(n) { alert( fib(3) ); // 2 alert( fib(7) ); // 13 -// fib(77); // will be extremely slow! +// fib(77); // ¡Será extremadamente lento! ``` -...But for big values of `n` it's very slow. For instance, `fib(77)` may hang up the engine for some time eating all CPU resources. +...Pero para valores grandes de `n` es muy lenta. Por ejemplo, `fib(77)` puede colgar el motor durante un tiempo consumiendo todos los recursos de la CPU. -That's because the function makes too many subcalls. The same values are re-evaluated again and again. +Eso es porque la función realiza demasiadas sub llamadas. Los mismos valores son evaluados una y otra vez. -For instance, let's see a piece of calculations for `fib(5)`: +Por ejemplo, veamos algunos cálculos para `fib(5)`: ```js no-beautify ... @@ -25,68 +25,68 @@ fib(4) = fib(3) + fib(2) ... ``` -Here we can see that the value of `fib(3)` is needed for both `fib(5)` and `fib(4)`. So `fib(3)` will be called and evaluated two times completely independently. +Aquí podemos ver que el valor de `fib(3)` es necesario tanto para `fib(5)` y `fib(4)`. Entonces `fib(3)` será calculado y evaluado dos veces de forma completamente independiente. -Here's the full recursion tree: +Aquí está el árbol de recursividad completo: ![fibonacci recursion tree](fibonacci-recursion-tree.svg) -We can clearly notice that `fib(3)` is evaluated two times and `fib(2)` is evaluated three times. The total amount of computations grows much faster than `n`, making it enormous even for `n=77`. +Podemos ver claramente que `fib(3)` es evaluado dos veces y `fib(2)` es evaluado tres veces. La cantidad total de cálculos crece mucho más rápido que `n`, lo que lo hace enorme incluso para `n=77`. -We can optimize that by remembering already-evaluated values: if a value of say `fib(3)` is calculated once, then we can just reuse it in future computations. +Podemos optimizarlo recordando los valores ya evaluados: si un valor de por ejemplo `fib(3)` es calculado una vez, entonces podemos reutilizarlo en cálculos futuros. -Another variant would be to give up recursion and use a totally different loop-based algorithm. +Otra variante sería renunciar a la recursión y utilizar un algoritmo basado en bucles totalmente diferente. -Instead of going from `n` down to lower values, we can make a loop that starts from `1` and `2`, then gets `fib(3)` as their sum, then `fib(4)` as the sum of two previous values, then `fib(5)` and goes up and up, till it gets to the needed value. On each step we only need to remember two previous values. +En lugar de ir de `n` a valores más bajos, podemos hacer un bucle que empiece desde `1` y `2`, que obtenga `fib(3)` como su suma, luego `fib(4)` como la suma de los dos valores anteriores, luego `fib(5)` y va subiendo hasta llegar al valor necesario. En cada paso solo necesitamos recordar los dos valores anteriores. -Here are the steps of the new algorithm in details. +Estos son los pasos del nuevo algoritmo en detalle. -The start: +El inicio: ```js -// a = fib(1), b = fib(2), these values are by definition 1 +// a = fib(1), b = fib(2), estos valores son por definición 1 let a = 1, b = 1; -// get c = fib(3) as their sum +// obtener c = fib(3) como su suma let c = a + b; -/* we now have fib(1), fib(2), fib(3) +/* ahora tenemos fib(1), fib(2), fib(3) a b c 1, 1, 2 */ ``` -Now we want to get `fib(4) = fib(2) + fib(3)`. +Ahora queremos obtener `fib(4) = fib(2) + fib(3)`. -Let's shift the variables: `a,b` will get `fib(2),fib(3)`, and `c` will get their sum: +Cambiemos las variables: `a, b` obtendrán `fib(2),fib(3)`, y `c` obtendrá su suma: ```js no-beautify a = b; // now a = fib(2) b = c; // now b = fib(3) c = a + b; // c = fib(4) -/* now we have the sequence: +/* ahora tenemos la secuencia: a b c 1, 1, 2, 3 */ ``` -The next step gives another sequence number: +El siguiente paso obtiene otro número de la secuencia: ```js no-beautify a = b; // now a = fib(3) b = c; // now b = fib(4) c = a + b; // c = fib(5) -/* now the sequence is (one more number): +/* ahora la secuencia es (otro número más): a b c 1, 1, 2, 3, 5 */ ``` -...And so on until we get the needed value. That's much faster than recursion and involves no duplicate computations. +...Y así sucesivamente hasta obtener el valor necesario. Eso es mucho más rápido que la recursión y no implica cálculos duplicados. -The full code: +El código completo: ```js run function fib(n) { @@ -105,6 +105,6 @@ alert( fib(7) ); // 13 alert( fib(77) ); // 5527939700884757 ``` -The loop starts with `i=3`, because the first and the second sequence values are hard-coded into variables `a=1`, `b=1`. +El bucle comienza con `i=3`, porque el primer y segundo valor de la secuencia están codificados en las variables `a=1` y `b=1`. -The approach is called [dynamic programming bottom-up](https://en.wikipedia.org/wiki/Dynamic_programming). +Este enfoque se llama [programación dinámica](https://es.wikipedia.org/wiki/Programaci%C3%B3n_din%C3%A1mica). diff --git a/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/task.md b/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/task.md index 3cdadd219..5d47ab90c 100644 --- a/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/task.md +++ b/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/task.md @@ -2,17 +2,17 @@ importance: 5 --- -# Fibonacci numbers +# Sucesión de Fibonacci -The sequence of [Fibonacci numbers](https://en.wikipedia.org/wiki/Fibonacci_number) has the formula Fn = Fn-1 + Fn-2. In other words, the next number is a sum of the two preceding ones. +La secuencia de [sucesión de Fibonacci](https://es.wikipedia.org/wiki/Sucesi%C3%B3n_de_Fibonacci) tiene la fórmula Fn = Fn-1 + Fn-2. En otras palabras, el siguiente número es una suma de los dos anteriores. -First two numbers are `1`, then `2(1+1)`, then `3(1+2)`, `5(2+3)` and so on: `1, 1, 2, 3, 5, 8, 13, 21...`. +Los dos primeros números son `1`, luego `2(1+1)`, luego `3(1+2)`, `5(2+3)` y así sucesivamente: `1, 1, 2, 3, 5, 8, 13, 21...`. -Fibonacci numbers are related to the [Golden ratio](https://en.wikipedia.org/wiki/Golden_ratio) and many natural phenomena around us. +La sucesión de Fibonacci está relacionada la [proporción áurea](https://es.wikipedia.org/wiki/N%C3%BAmero_%C3%A1ureo) y muchos fenómenos naturales alrededor nuestro. -Write a function `fib(n)` that returns the `n-th` Fibonacci number. +Escribe una función `fib(n)` que devuelve la secuencia `n-th` de Fibonacci. -An example of work: +Un ejemplo de trabajo: ```js function fib(n) { /* your code */ } @@ -22,4 +22,4 @@ alert(fib(7)); // 13 alert(fib(77)); // 5527939700884757 ``` -P.S. The function should be fast. The call to `fib(77)` should take no more than a fraction of a second. +P.D. La función debería ser rápida. La llamada a `fib(77)` no debería tardar más de una fracción de segundo. diff --git a/1-js/06-advanced-functions/01-recursion/04-output-single-linked-list/solution.md b/1-js/06-advanced-functions/01-recursion/04-output-single-linked-list/solution.md index cfcbffea5..a024019a6 100644 --- a/1-js/06-advanced-functions/01-recursion/04-output-single-linked-list/solution.md +++ b/1-js/06-advanced-functions/01-recursion/04-output-single-linked-list/solution.md @@ -1,6 +1,6 @@ -# Loop-based solution +# Solución basada en el bucle -The loop-based variant of the solution: +La solución basada en el bucle: ```js run let list = { @@ -30,7 +30,7 @@ function printList(list) { printList(list); ``` -Please note that we use a temporary variable `tmp` to walk over the list. Technically, we could use a function parameter `list` instead: +Ten en cuenta que utilizamos una variable temporal `tmp` para recorrer la lista. Técnicamente, podríamos usar una función con una `list` de parámetros en su lugar: ```js function printList(list) { @@ -43,15 +43,15 @@ function printList(list) { } ``` -...But that would be unwise. In the future we may need to extend a function, do something else with the list. If we change `list`, then we lose such ability. +...Pero eso no sería prudente. En el futuro, es posible que necesitemos extender la función, hacer algo distinto con la lista. Si cambiamos `list`, entonces perdemos la habilidad. -Talking about good variable names, `list` here is the list itself. The first element of it. And it should remain like that. That's clear and reliable. +Hablando sobre buenos nombres de variables, `list` aquí es la lista en sí. El primer elemento de la misma. Y debería permanecer así. Eso queda claro y fiable. -From the other side, the role of `tmp` is exclusively a list traversal, like `i` in the `for` loop. +Desde el otro lado, el papel de `tmp` es exclusivamente para recorrer la lista, como `i` en el bucle `for`. -# Recursive solution +# Solución recursiva -The recursive variant of `printList(list)` follows a simple logic: to output a list we should output the current element `list`, then do the same for `list.next`: +La solución recursiva de `printList(list)` sigue una lógica simple: para generar una lista debemos generar el elemento actual `list`, luego hacer lo mismo con `list.next`: ```js run let list = { @@ -70,10 +70,10 @@ let list = { function printList(list) { - alert(list.value); // output the current item + alert(list.value); // genera el elemento actual if (list.next) { - printList(list.next); // do the same for the rest of the list + printList(list.next); // hace lo mismo para el resto de la lista } } @@ -81,8 +81,8 @@ function printList(list) { printList(list); ``` -Now what's better? +Ahora, ¿Qué es mejor? -Technically, the loop is more effective. These two variants do the same, but the loop does not spend resources for nested function calls. +Técnicamente, el bucle es más efectivo. Estas dos variantes hacen lo mismo, pero el bucle no gasta recursos en llamadas a funciones anidadas. -From the other side, the recursive variant is shorter and sometimes easier to understand. +Por otro lado, la variante recursiva es más corta y a veces más sencilla de entender. diff --git a/1-js/06-advanced-functions/01-recursion/04-output-single-linked-list/task.md b/1-js/06-advanced-functions/01-recursion/04-output-single-linked-list/task.md index 1076b952a..f3021b737 100644 --- a/1-js/06-advanced-functions/01-recursion/04-output-single-linked-list/task.md +++ b/1-js/06-advanced-functions/01-recursion/04-output-single-linked-list/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# Output a single-linked list +# Generar una lista de un solo enlace -Let's say we have a single-linked list (as described in the chapter ): +Digamos que tenemos una lista de un solo enlace (como se describe en el capítulo ): ```js let list = { @@ -22,8 +22,8 @@ let list = { }; ``` -Write a function `printList(list)` that outputs list items one-by-one. +Escribe una función `printList(list)` que genere los elementos de la lista uno por uno. -Make two variants of the solution: using a loop and using recursion. +Haz dos variantes de la solución: utilizando un bucle y utilizando recursividad. -What's better: with recursion or without it? +¿Qué es mejor: con recursividad o sin ella? diff --git a/1-js/06-advanced-functions/01-recursion/05-output-single-linked-list-reverse/solution.md b/1-js/06-advanced-functions/01-recursion/05-output-single-linked-list-reverse/solution.md index 0eb76ea1c..9d0282ee2 100644 --- a/1-js/06-advanced-functions/01-recursion/05-output-single-linked-list-reverse/solution.md +++ b/1-js/06-advanced-functions/01-recursion/05-output-single-linked-list-reverse/solution.md @@ -1,8 +1,8 @@ -# Using a recursion +# Usando recursividad -The recursive logic is a little bit tricky here. +La lógica recursiva es un poco complicada aquí. -We need to first output the rest of the list and *then* output the current one: +Primero necesitamos generar el resto de la lista y *entonces* generar la lista actual: ```js run let list = { @@ -31,13 +31,13 @@ function printReverseList(list) { printReverseList(list); ``` -# Using a loop +# Usando un bucle -The loop variant is also a little bit more complicated than the direct output. +La variante con bucle también es un poco más complicada que la salida directa. -There is no way to get the last value in our `list`. We also can't "go back". +No hay manera de obtener el último valor en nuestra `list`. Tampoco podemos ir "hacia atrás". -So what we can do is to first go through the items in the direct order and remember them in an array, and then output what we remembered in the reverse order: +Entonces, lo que podemos hacer primero es recorrer los elementos en el orden directo guardándolos en un array, y entonces generar los elementos guardados en el orden inverso: ```js run let list = { @@ -71,4 +71,4 @@ function printReverseList(list) { printReverseList(list); ``` -Please note that the recursive solution actually does exactly the same: it follows the list, remembers the items in the chain of nested calls (in the execution context stack), and then outputs them. +Ten en cuenta que la solución recursiva en realidad hace exactamente lo mismo: recorre la lista, guarda los elementos en la cadena de llamadas anidadas (en la pila de contexto de ejecución), y luego los genera. diff --git a/1-js/06-advanced-functions/01-recursion/05-output-single-linked-list-reverse/task.md b/1-js/06-advanced-functions/01-recursion/05-output-single-linked-list-reverse/task.md index 81b1f3e33..364b3095f 100644 --- a/1-js/06-advanced-functions/01-recursion/05-output-single-linked-list-reverse/task.md +++ b/1-js/06-advanced-functions/01-recursion/05-output-single-linked-list-reverse/task.md @@ -2,8 +2,8 @@ importance: 5 --- -# Output a single-linked list in the reverse order +# Genere una lista de un solo enlace en orden inverso -Output a single-linked list from the previous task in the reverse order. +Genere una lista de un solo enlace a partir de la tarea anterior en orden inverso. -Make two solutions: using a loop and using a recursion. +Escribe dos soluciones: utilizando un bucle y utilizando recursividad. diff --git a/1-js/06-advanced-functions/01-recursion/article.md b/1-js/06-advanced-functions/01-recursion/article.md index 5ae894474..39beb8119 100644 --- a/1-js/06-advanced-functions/01-recursion/article.md +++ b/1-js/06-advanced-functions/01-recursion/article.md @@ -1,18 +1,18 @@ -# Recursion and stack +# Recursión y pila -Let's return to functions and study them more in-depth. +Volvamos a las funciones y estudiémoslas más en profundidad. -Our first topic will be *recursion*. +Nuestro primer tema será la *recursividad*. -If you are not new to programming, then it is probably familiar and you could skip this chapter. +Si no eres nuevo en la programación, probablemente te resulte familiar y puedes saltarte este capítulo. -Recursion is a programming pattern that is useful in situations when a task can be naturally split into several tasks of the same kind, but simpler. Or when a task can be simplified into an easy action plus a simpler variant of the same task. Or, as we'll see soon, to deal with certain data structures. +La recursión es un patrón de programación que es útil en situaciones en las que una tarea puede dividirse naturalmente en varias tareas del mismo tipo, pero más simples. O cuando una tarea se puede simplificar en una acción fácil más una variante más simple de la misma tarea. O, como veremos pronto, tratar con ciertas estructuras de datos. -When a function solves a task, in the process it can call many other functions. A partial case of this is when a function calls *itself*. That's called *recursion*. +Sabemos que cuando una función resuelve una tarea, en el proceso puede llamar a muchas otras funciones. Un caso particular de esto se da cuando una función *se llama a sí misma*. Esto es lo que se llama *recursividad*. -## Two ways of thinking +## Dos formas de pensar -For something simple to start with -- let's write a function `pow(x, n)` that raises `x` to a natural power of `n`. In other words, multiplies `x` by itself `n` times. +Para comenzar con algo simple, escribamos una función `pow(x, n)` que eleve `x` a una potencia natural de`n`. En otras palabras, multiplica `x` por sí mismo `n` veces. ```js pow(2, 2) = 4 @@ -20,15 +20,15 @@ pow(2, 3) = 8 pow(2, 4) = 16 ``` -There are two ways to implement it. +Hay dos formas de implementarlo. -1. Iterative thinking: the `for` loop: +1. Pensamiento iterativo: el bucle `for`: ```js run function pow(x, n) { let result = 1; - // multiply result by x n times in the loop + // multiplicar el resultado por x n veces en el ciclo for (let i = 0; i < n; i++) { result *= x; } @@ -39,7 +39,7 @@ There are two ways to implement it. alert( pow(2, 3) ); // 8 ``` -2. Recursive thinking: simplify the task and call self: +2. Pensamiento recursivo: simplifica la tarea y se llama a sí mismo: ```js run function pow(x, n) { @@ -53,9 +53,9 @@ There are two ways to implement it. alert( pow(2, 3) ); // 8 ``` -Please note how the recursive variant is fundamentally different. +Note cómo la variante recursiva es fundamentalmente diferente. -When `pow(x, n)` is called, the execution splits into two branches: +Cuando se llama a `pow(x, n)`, la ejecución se divide en dos ramas: ```js if n==1 = x @@ -65,65 +65,65 @@ pow(x, n) = else = x * pow(x, n - 1) ``` -1. If `n == 1`, then everything is trivial. It is called *the base* of recursion, because it immediately produces the obvious result: `pow(x, 1)` equals `x`. -2. Otherwise, we can represent `pow(x, n)` as `x * pow(x, n - 1)`. In maths, one would write xn = x * xn-1. This is called *a recursive step*: we transform the task into a simpler action (multiplication by `x`) and a simpler call of the same task (`pow` with lower `n`). Next steps simplify it further and further until `n` reaches `1`. +1. Si `n == 1`, entonces todo es trivial. Esto se llama *base* de la recursividad, porque produce inmediatamente el resultado obvio: `pow (x, 1)` es igual a `x`. +2. De lo contrario, podemos representar `pow (x, n)` como `x * pow (x, n - 1)`. En matemáticas, uno escribiría xn = x * x n-1. Esto se llama *paso recursivo*: transformamos la tarea en una acción más simple (multiplicación por `x`) y una llamada más simple de la misma tarea (`pow` con menor `n`). Los siguientes pasos lo simplifican más y más hasta que `n` llegue a` 1`. -We can also say that `pow` *recursively calls itself* till `n == 1`. +También podemos decir que `pow` *se llama a sí mismo recursivamente* hasta que` n == 1`. -![recursive diagram of pow](recursion-pow.svg) +![diagrama recursivo de pow](recursion-pow.svg) -For example, to calculate `pow(2, 4)` the recursive variant does these steps: +Por ejemplo, para calcular `pow (2, 4)` la variante recursiva realiza estos pasos: 1. `pow(2, 4) = 2 * pow(2, 3)` 2. `pow(2, 3) = 2 * pow(2, 2)` 3. `pow(2, 2) = 2 * pow(2, 1)` 4. `pow(2, 1) = 2` -So, the recursion reduces a function call to a simpler one, and then -- to even more simpler, and so on, until the result becomes obvious. +Por lo tanto, la recursión reduce una llamada de función a una más simple y luego... a una más simple, y así sucesivamente, hasta que el resultado se vuelve obvio. -````smart header="Recursion is usually shorter" -A recursive solution is usually shorter than an iterative one. +````smart header="La recursión suele ser más corta" +Una solución recursiva suele ser más corta que una iterativa. -Here we can rewrite the same using the conditional operator `?` instead of `if` to make `pow(x, n)` more terse and still very readable: +Aquí podemos reescribir lo mismo usando el operador condicional `?` En lugar de `if` para hacer que `pow (x, n)` sea más conciso y aún bastante legible: ```js run -function pow(x, n) { - return (n == 1) ? x : (x * pow(x, n - 1)); +function pow (x, n) { +   return (n == 1)? x: (x * pow (x, n - 1)); } ``` ```` -The maximal number of nested calls (including the first one) is called *recursion depth*. In our case, it will be exactly `n`. +El número máximo de llamadas anidadas (incluida la primera) se llama *profundidad de recursión*. En nuestro caso, será exactamente `n`. -The maximal recursion depth is limited by JavaScript engine. We can rely on it being 10000, some engines allow more, but 100000 is probably out of limit for the majority of them. There are automatic optimizations that help alleviate this ("tail calls optimizations"), but they are not yet supported everywhere and work only in simple cases. +La profundidad máxima de recursión está limitada por el motor de JavaScript. Podemos confiar en que sea 10 000; algunos motores permiten más, pero 100 000 probablemente esté fuera del límite para la mayoría de ellos. Hay optimizaciones automáticas que ayudan a aliviar esto ("optimizaciones de llamadas de cola"), pero aún no tienen soporte en todas partes y funcionan solo en casos simples. -That limits the application of recursion, but it still remains very wide. There are many tasks where recursive way of thinking gives simpler code, easier to maintain. +Eso limita la aplicación de la recursividad, pero sigue siendo muy amplia. Hay muchas tareas donde la forma recursiva de pensar proporciona un código más simple y fácil de mantener. -## The execution context and stack +## El contexto de ejecución y pila -Now let's examine how recursive calls work. For that we'll look under the hood of functions. +Ahora examinemos cómo funcionan las llamadas recursivas. Para eso espiemos lo que sucede bajo la capa en las funciones. -The information about the process of execution of a running function is stored in its *execution context*. +La información sobre el proceso de ejecución de una función en ejecución se almacena en su *contexto de ejecución*. -The [execution context](https://tc39.github.io/ecma262/#sec-execution-contexts) is an internal data structure that contains details about the execution of a function: where the control flow is now, the current variables, the value of `this` (we don't use it here) and few other internal details. +El [contexto de ejecución](https://tc39.github.io/ecma262/#sec-execution-contexts) es una estructura de datos interna que contiene detalles sobre la ejecución de una función: dónde está el flujo de control ahora, las variables actuales, el valor de `this` (que no usamos aquí) y algunos otros detalles internos. -One function call has exactly one execution context associated with it. +Una llamada de función tiene exactamente un contexto de ejecución asociado. -When a function makes a nested call, the following happens: +Cuando una función realiza una llamada anidada, sucede lo siguiente: -- The current function is paused. -- The execution context associated with it is remembered in a special data structure called *execution context stack*. -- The nested call executes. -- After it ends, the old execution context is retrieved from the stack, and the outer function is resumed from where it stopped. +- La función actual se pausa. +- El contexto de ejecución asociado con él se recuerda en una estructura de datos especial llamada *pila de contexto de ejecución*. +- La llamada anidada se ejecuta. +- Una vez que finaliza, el antiguo contexto de ejecución se recupera de la pila y la función externa se reanuda desde donde se pausó. -Let's see what happens during the `pow(2, 3)` call. +Veamos qué sucede durante la llamada de `pow (2, 3)`. -### pow(2, 3) +### pow (2, 3) -In the beginning of the call `pow(2, 3)` the execution context will store variables: `x = 2, n = 3`, the execution flow is at line `1` of the function. +Al comienzo de la llamada `pow (2, 3)` el contexto de ejecución almacenará variables: `x = 2, n = 3`, el flujo de ejecución está en la línea `1` de la función. -We can sketch it as: +Podemos esbozarlo como:
  • @@ -132,7 +132,7 @@ We can sketch it as:
-That's when the function starts to execute. The condition `n == 1` is falsy, so the flow continues into the second branch of `if`: +Ahí es cuando la función comienza a ejecutarse. La condición `n == 1` es falsa, por lo que el flujo continúa en la segunda rama de `if`: ```js run function pow(x, n) { @@ -149,7 +149,7 @@ alert( pow(2, 3) ); ``` -The variables are same, but the line changes, so the context is now: +Las variables son las mismas, pero la línea cambia, por lo que el contexto es ahora:
  • @@ -158,19 +158,19 @@ The variables are same, but the line changes, so the context is now:
-To calculate `x * pow(x, n - 1)`, we need to make a subcall of `pow` with new arguments `pow(2, 2)`. +Para calcular `x * pow (x, n - 1)`, necesitamos hacer una sub-llamada de `pow` con nuevos argumentos` pow (2, 2) `. -### pow(2, 2) +### pow (2, 2) -To do a nested call, JavaScript remembers the current execution context in the *execution context stack*. +Para hacer una llamada anidada, JavaScript recuerda el contexto de ejecución actual en la *pila de contexto de ejecución*. -Here we call the same function `pow`, but it absolutely doesn't matter. The process is the same for all functions: +Aquí llamamos a la misma función `pow`, pero no importa en absoluto. El proceso es el mismo para todas las funciones: -1. The current context is "remembered" on top of the stack. -2. The new context is created for the subcall. -3. When the subcall is finished -- the previous context is popped from the stack, and its execution continues. +1. El contexto actual se "recuerda" en la parte superior de la pila. +2. El nuevo contexto se crea para la subllamada. +3. Cuando finaliza la subllamada, el contexto anterior se extrae de la pila y su ejecución continúa. -Here's the context stack when we entered the subcall `pow(2, 2)`: +Aquí está la pila de contexto cuando ingresamos la subllamada `pow (2, 2)`:
  • @@ -183,21 +183,21 @@ Here's the context stack when we entered the subcall `pow(2, 2)`:
-The new current execution context is on top (and bold), and previous remembered contexts are below. +El nuevo contexto de ejecución actual está en la parte superior (y en negrita), y los contextos recordados anteriores están debajo. -When we finish the subcall -- it is easy to resume the previous context, because it keeps both variables and the exact place of the code where it stopped. +Cuando terminamos la subllamada: es fácil reanudar el contexto anterior, ya que mantiene ambas variables y el lugar exacto del código donde se detuvo. ```smart -Here in the picture we use the word "line", as in our example there's only one subcall in line, but generally a single line of code may contain multiple subcalls, like `pow(…) + pow(…) + somethingElse(…)`. +En la figura usamos la palabra línea "line" porque en nuestro ejemplo hay solo una subllamada en línea, pero generalmente una simple línea de código puede contener múltiples subllamadas, como `pow(…) + pow(…) + otraCosa(…)`. -So it would be more precise to say that the execution resumes "immediately after the subcall". +Entonces sería más preciso decir que la ejecución se reanuda "inmediatamente después de la subllamada". ``` ### pow(2, 1) -The process repeats: a new subcall is made at line `5`, now with arguments `x=2`, `n=1`. +El proceso se repite: se realiza una nueva subllamada en la línea `5`, ahora con argumentos` x = 2`, `n = 1`. -A new execution context is created, the previous one is pushed on top of the stack: +Se crea un nuevo contexto de ejecución, el anterior se coloca en la parte superior de la pila:
  • @@ -214,11 +214,11 @@ A new execution context is created, the previous one is pushed on top of the sta
-There are 2 old contexts now and 1 currently running for `pow(2, 1)`. +Hay 2 contextos antiguos ahora y 1 actualmente en ejecución para `pow (2, 1)`. -### The exit +### La salida -During the execution of `pow(2, 1)`, unlike before, the condition `n == 1` is truthy, so the first branch of `if` works: +Durante la ejecución de `pow (2, 1)`, a diferencia de antes, la condición `n == 1` es verdadera, por lo que funciona la primera rama de `if` : ```js function pow(x, n) { @@ -232,9 +232,9 @@ function pow(x, n) { } ``` -There are no more nested calls, so the function finishes, returning `2`. +No hay más llamadas anidadas, por lo que la función finaliza y devuelve `2`. -As the function finishes, its execution context is not needed anymore, so it's removed from the memory. The previous one is restored off the top of the stack: +Cuando finaliza la función, su contexto de ejecución ya no es necesario y se elimina de la memoria. El anterior se restaura desde la parte superior de la pila:
    @@ -248,9 +248,9 @@ As the function finishes, its execution context is not needed anymore, so it's r
-The execution of `pow(2, 2)` is resumed. It has the result of the subcall `pow(2, 1)`, so it also can finish the evaluation of `x * pow(x, n - 1)`, returning `4`. +Se reanuda la ejecución de `pow (2, 2)`. Tiene el resultado de la subllamada `pow (2, 1)`, por lo que también puede finalizar la evaluación de `x * pow (x, n - 1)`, devolviendo `4`. -Then the previous context is restored: +Luego se restaura el contexto anterior:
  • @@ -259,15 +259,15 @@ Then the previous context is restored:
-When it finishes, we have a result of `pow(2, 3) = 8`. +Cuando termina, tenemos un resultado de `pow (2, 3) = 8`. -The recursion depth in this case was: **3**. +La profundidad de recursión en este caso fue: **3**. -As we can see from the illustrations above, recursion depth equals the maximal number of context in the stack. +Como podemos ver en las ilustraciones anteriores, la profundidad de recursión es igual al número máximo de contexto en la pila. -Note the memory requirements. Contexts take memory. In our case, raising to the power of `n` actually requires the memory for `n` contexts, for all lower values of `n`. +Tenga en cuenta los requisitos de memoria. Los contextos toman memoria. En nuestro caso, elevar a la potencia de `n` realmente requiere la memoria para `n` contextos, para todos los valores más bajos de `n`. -A loop-based algorithm is more memory-saving: +Un algoritmo basado en bucles ahorra más memoria: ```js function pow(x, n) { @@ -281,19 +281,19 @@ function pow(x, n) { } ``` -The iterative `pow` uses a single context changing `i` and `result` in the process. Its memory requirements are small, fixed and do not depend on `n`. +El `pow` iterativo utiliza un solo contexto, cambiando `i` y `result` en el proceso. Sus requisitos de memoria son pequeños, fijos y no dependen de `n`. -**Any recursion can be rewritten as a loop. The loop variant usually can be made more effective.** +**Cualquier recursión puede reescribirse como un bucle. La variante de bucle generalmente se puede hacer más eficaz.** -...But sometimes the rewrite is non-trivial, especially when a function uses different recursive subcalls depending on conditions and merges their results or when the branching is more intricate. And the optimization may be unneeded and totally not worth the efforts. +... Pero a veces la reescritura no es trivial, especialmente cuando la función utiliza sub-llamadas recursivas diferentes según las condiciones y combina sus resultados, o cuando la ramificación es más intrincada. Y la optimización podría ser innecesaria y no merecer la pena el esfuerzo en absoluto. -Recursion can give a shorter code, easier to understand and support. Optimizations are not required in every place, mostly we need a good code, that's why it's used. +La recursión puede dar un código más corto y fácil de entender y mantener. No se requiere optimización en todo lugar, principalmente lo que nos interesa es un buen código y por eso se usa. -## Recursive traversals +## Recorridos recursivos -Another great application of the recursion is a recursive traversal. +Otra gran aplicación de la recursión es un recorrido recursivo. -Imagine, we have a company. The staff structure can be presented as an object: +Imagina que tenemos una empresa. La estructura del personal se puede presentar como un objeto: ```js let company = { @@ -322,34 +322,34 @@ let company = { }; ``` -In other words, a company has departments. +Vemos que esta empresa tiene departamentos. -- A department may have an array of staff. For instance, `sales` department has 2 employees: John and Alice. -- Or a department may split into subdepartments, like `development` has two branches: `sites` and `internals`. Each of them has their own staff. -- It is also possible that when a subdepartment grows, it divides into subsubdepartments (or teams). +- Un departamento puede tener una gran variedad de personal. Por ejemplo, el departamento de ventas `sales` tiene 2 empleados: John y Alice. +- O un departamento puede dividirse en subdepartamentos, como `development` que tiene dos ramas: `sites` e `internals`: cada uno de ellos tiene su propio personal. +- También es posible que cuando un subdepartamento crece, se divida en subdepartamentos (o equipos). - For instance, the `sites` department in the future may be split into teams for `siteA` and `siteB`. And they, potentially, can split even more. That's not on the picture, just something to have in mind. + Por ejemplo, el departamento `sites` en el futuro puede dividirse en equipos para `siteA` y `siteB`. Y ellos, potencialmente, pueden dividirse aún más. Eso no está en la imagen, es solo algo a tener en cuenta. -Now let's say we want a function to get the sum of all salaries. How can we do that? +Ahora digamos que queremos una función para obtener la suma de todos los salarios. ¿Cómo podemos hacer eso? -An iterative approach is not easy, because the structure is not simple. The first idea may be to make a `for` loop over `company` with nested subloop over 1st level departments. But then we need more nested subloops to iterate over the staff in 2nd level departments like `sites`... And then another subloop inside those for 3rd level departments that might appear in the future? If we put 3-4 nested subloops in the code to traverse a single object, it becomes rather ugly. +Un enfoque iterativo no es fácil, porque la estructura no es simple. La primera idea puede ser hacer un bucle `for` sobre `company` con un sub-bucle anidado sobre departamentos de primer nivel. Pero luego necesitamos más sub-bucles anidados para iterar sobre el personal en los departamentos de segundo nivel como `sites`. ...¿Y luego otro sub-bucle dentro de los de los departamentos de tercer nivel que podrían aparecer en el futuro? ¿Deberíamos parar en el nivel 3 o hacer 4 niveles de bucles? Si ponemos 3-4 bucles anidados en el código para atravesar un solo objeto, se vuelve bastante feo. -Let's try recursion. +Probemos la recursividad. -As we can see, when our function gets a department to sum, there are two possible cases: +Como podemos ver, cuando nuestra función hace que un departamento sume, hay dos casos posibles: -1. Either it's a "simple" department with an *array* of people -- then we can sum the salaries in a simple loop. -2. Or it's *an object* with `N` subdepartments -- then we can make `N` recursive calls to get the sum for each of the subdeps and combine the results. +1. O bien es un departamento "simple" con una *array* de personas: entonces podemos sumar los salarios en un bucle simple. +2. O es *un objeto* con `N` subdepartamentos: entonces podemos hacer `N` llamadas recursivas para obtener la suma de cada uno de los subdepartamentos y combinar los resultados. -The 1st case is the base of recursion, the trivial case, when we get an array. +El primer caso es la *base* de la recursividad, el caso trivial, cuando obtenemos un array. -The 2nd case when we get an object is the recursive step. A complex task is split into subtasks for smaller departments. They may in turn split again, but sooner or later the split will finish at (1). +El segundo caso, cuando obtenemos un objeto, es el paso recursivo. Una tarea compleja se divide en subtareas para departamentos más pequeños. A su vez, pueden dividirse nuevamente, pero tarde o temprano la división terminará en (1). -The algorithm is probably even easier to read from the code: +El algoritmo es probablemente aún más fácil de leer desde el código: ```js run -let company = { // the same object, compressed for brevity +let company = { // el mismo objeto, comprimido por brevedad sales: [{name: 'John', salary: 1000}, {name: 'Alice', salary: 1600 }], development: { sites: [{name: 'Peter', salary: 2000}, {name: 'Alex', salary: 1800 }], @@ -357,15 +357,15 @@ let company = { // the same object, compressed for brevity } }; -// The function to do the job +// La función para hacer el trabajo *!* function sumSalaries(department) { - if (Array.isArray(department)) { // case (1) - return department.reduce((prev, current) => prev + current.salary, 0); // sum the array - } else { // case (2) + if (Array.isArray(department)) { // caso (1) + return department.reduce((prev, current) => prev + current.salary, 0); // suma del Array + } else { // caso (2) let sum = 0; for (let subdep of Object.values(department)) { - sum += sumSalaries(subdep); // recursively call for subdepartments, sum the results + sum += sumSalaries(subdep); // llama recursivamente a subdepartamentos, suma los resultados } return sum; } @@ -375,62 +375,62 @@ function sumSalaries(department) { alert(sumSalaries(company)); // 7700 ``` -The code is short and easy to understand (hopefully?). That's the power of recursion. It also works for any level of subdepartment nesting. +El código es corto y fácil de entender (¿Quizás?). Ese es el poder de la recursividad. También funciona para cualquier nivel de anidamiento de subdepartamentos. -Here's the diagram of calls: +Aquí está el diagrama de llamadas: -![recursive salaries](recursive-salaries.svg) +![salarios recursivos](recursive-salaries.svg) -We can easily see the principle: for an object `{...}` subcalls are made, while arrays `[...]` are the "leaves" of the recursion tree, they give immediate result. +Podemos ver fácilmente el principio: para un objeto `{...}` se realizan subllamadas, mientras que los Arrays `[...]` son las "hojas" del árbol recursivo y dan un resultado inmediato. -Note that the code uses smart features that we've covered before: +Tenga en cuenta que el código utiliza funciones inteligentes que hemos cubierto antes: -- Method `arr.reduce` explained in the chapter to get the sum of the array. -- Loop `for(val of Object.values(obj))` to iterate over object values: `Object.values` returns an array of them. +- Método `arr.reduce` explicado en el capítulo para obtener la suma del Array. +- Bucle `for (val of Object.values (obj))` para iterar sobre los valores del objeto: `Object.values` devuelve una matriz de ellos. -## Recursive structures +## Estructuras recursivas -A recursive (recursively-defined) data structure is a structure that replicates itself in parts. +Una estructura de datos recursiva (definida recursivamente) es una estructura que se replica en partes. -We've just seen it in the example of a company structure above. +Lo acabamos de ver en el ejemplo de la estructura de la empresa anterior. -A company *department* is: -- Either an array of people. -- Or an object with *departments*. +Un *departamento* de la empresa es: +- O un array de personas. +- O un objeto con *departamentos*. -For web-developers there are much better-known examples: HTML and XML documents. +Para los desarrolladores web hay ejemplos mucho más conocidos: documentos HTML y XML. -In the HTML document, an *HTML-tag* may contain a list of: -- Text pieces. -- HTML-comments. -- Other *HTML-tags* (that in turn may contain text pieces/comments or other tags etc). +En el documento HTML, una *etiqueta HTML* puede contener una lista de: +- Piezas de texto. +- Comentarios HTML. +- Otras *etiquetas HTML* (que a su vez pueden contener textos/comentarios, otras etiquetas, etc...). -That's once again a recursive definition. +Esa es, una vez más, una definición recursiva. -For better understanding, we'll cover one more recursive structure named "Linked list" that might be a better alternative for arrays in some cases. +Para una mejor comprensión, cubriremos una estructura recursiva más llamada "Lista enlazada" que podría ser una mejor alternativa para las matrices en algunos casos. -### Linked list +### Lista enlazada -Imagine, we want to store an ordered list of objects. +Imagina que queremos almacenar una lista ordenada de objetos. -The natural choice would be an array: +La elección natural sería un array: ```js let arr = [obj1, obj2, obj3]; ``` -...But there's a problem with arrays. The "delete element" and "insert element" operations are expensive. For instance, `arr.unshift(obj)` operation has to renumber all elements to make room for a new `obj`, and if the array is big, it takes time. Same with `arr.shift()`. +...Pero hay un problema con los Arrays. Las operaciones "eliminar elemento" e "insertar elemento" son costosas. Por ejemplo, la operación `arr.unshift(obj)` debe renumerar todos los elementos para dejar espacio para un nuevo `obj`, y si la matriz es grande, lleva tiempo. Lo mismo con `arr.shift ()`. -The only structural modifications that do not require mass-renumbering are those that operate with the end of array: `arr.push/pop`. So an array can be quite slow for big queues, when we have to work with the beginning. +Las únicas modificaciones estructurales que no requieren renumeración masiva son aquellas que operan con el final del array: `arr.push/pop`. Por lo tanto, un array puede ser bastante lento para grandes colas si tenemos que trabajar con el principio del mismo. -Alternatively, if we really need fast insertion/deletion, we can choose another data structure called a [linked list](https://en.wikipedia.org/wiki/Linked_list). +Como alternativa, si realmente necesitamos una inserción/eliminación rápida, podemos elegir otra estructura de datos llamada [lista enlazada](https://es.wikipedia.org/wiki/Lista_enlazada). -The *linked list element* is recursively defined as an object with: +El *elemento de lista enlazada* se define de forma recursiva como un objeto con: - `value`. -- `next` property referencing the next *linked list element* or `null` if that's the end. +- propiedad `next` que hace referencia al siguiente *elemento de lista enlazado* o `null` si ese es el final. -For instance: +Por ejemplo: ```js let list = { @@ -448,11 +448,11 @@ let list = { }; ``` -Graphical representation of the list: +Representación gráfica de la lista: -![linked list](linked-list.svg) +![lista enlazada](linked-list.svg) -An alternative code for creation: +Un código alternativo para la creación: ```js no-beautify let list = { value: 1 }; @@ -462,9 +462,9 @@ list.next.next.next = { value: 4 }; list.next.next.next.next = null; ``` -Here we can even more clearly see that there are multiple objects, each one has the `value` and `next` pointing to the neighbour. The `list` variable is the first object in the chain, so following `next` pointers from it we can reach any element. +Aquí podemos ver aún más claramente que hay varios objetos, cada uno tiene su `value` y un `next` apuntando al vecino. La variable `list` es el primer objeto en la cadena, por lo que siguiendo los punteros` next` de ella podemos alcanzar cualquier elemento. -The list can be easily split into multiple parts and later joined back: +La lista se puede dividir fácilmente en varias partes y luego volver a unir: ```js let secondList = list.next.next; @@ -473,15 +473,15 @@ list.next.next = null; ![linked list split](linked-list-split.svg) -To join: +Para unir: ```js list.next.next = secondList; ``` -And surely we can insert or remove items in any place. +Y seguro, podemos insertar o eliminar elementos en cualquier lugar. -For instance, to prepend a new value, we need to update the head of the list: +Por ejemplo, para anteponer un nuevo valor, necesitamos actualizar el encabezado de la lista: ```js let list = { value: 1 }; @@ -490,14 +490,14 @@ list.next.next = { value: 3 }; list.next.next.next = { value: 4 }; *!* -// prepend the new value to the list +// anteponer el nuevo valor a la lista list = { value: "new item", next: list }; */!* ``` ![linked list](linked-list-0.svg) -To remove a value from the middle, change `next` of the previous one: +Para eliminar un valor del medio, cambie el `next` del anterior: ```js list.next = list.next.next; @@ -505,38 +505,39 @@ list.next = list.next.next; ![linked list](linked-list-remove-1.svg) -We made `list.next` jump over `1` to value `2`. The value `1` is now excluded from the chain. If it's not stored anywhere else, it will be automatically removed from the memory. +Hicimos que `list.next` salte sobre `1` al valor `2`. El valor `1` ahora está excluido de la cadena. Si no se almacena en ningún otro lugar, se eliminará automáticamente de la memoria. + +A diferencia de los arrays, no hay renumeración en masa, podemos reorganizar fácilmente los elementos. -Unlike arrays, there's no mass-renumbering, we can easily rearrange elements. +Naturalmente, las listas no siempre son mejores que los Arrays. De lo contrario, todos usarían solo listas. -Naturally, lists are not always better than arrays. Otherwise everyone would use only lists. +El principal inconveniente es que no podemos acceder fácilmente a un elemento por su número. En un Array eso es fácil: `arr[n]` es una referencia directa. Pero en la lista tenemos que comenzar desde el primer elemento e ir `siguiente` `N` veces para obtener el enésimo elemento. -The main drawback is that we can't easily access an element by its number. In an array that's easy: `arr[n]` is a direct reference. But in the list we need to start from the first item and go `next` `N` times to get the Nth element. +... Pero no siempre necesitamos tales operaciones. Por ejemplo, cuando necesitamos una cola o incluso un [deque](https://es.wikipedia.org/wiki/Cola_doblemente_terminada): la estructura ordenada que debe permitir agregar/eliminar elementos muy rápidamente desde ambos extremos. -...But we don't always need such operations. For instance, when we need a queue or even a [deque](https://en.wikipedia.org/wiki/Double-ended_queue) -- the ordered structure that must allow very fast adding/removing elements from both ends, but access to its middle is not needed. +Las "listas" pueden ser mejoradas: +- Podemos agregar la propiedad `prev` (previo) junto a `next` (siguiente) para referenciar el elemento previo para mover hacia atrás fácilmente. +- Podemos también agregar una variable llamada `tail` (cola) referenciando el último elemento de la lista (y actualizarla cuando se agregan/remueven elementos del final). +- ...La estructura de datos puede variar de acuerdo a nuestras necesidades. -Lists can be enhanced: -- We can add property `prev` in addition to `next` to reference the previous element, to move back easily. -- We can also add a variable named `tail` referencing the last element of the list (and update it when adding/removing elements from the end). -- ...The data structure may vary according to our needs. -## Summary +## Resumen -Terms: -- *Recursion* is a programming term that means calling a function from itself. Recursive functions can be used to solve tasks in elegant ways. +Glosario: +- *Recursion* es concepto de programación que significa que una función se llama a sí misma. Las funciones recursivas se pueden utilizar para resolver ciertas tareas de manera elegante. - When a function calls itself, that's called a *recursion step*. The *basis* of recursion is function arguments that make the task so simple that the function does not make further calls. + Cada vez que una función se llama a sí misma ocurre un *paso de recursión*. La *base* de la recursividad se da cuando los argumentos de la función hacen que la tarea sea tan básica que la función no realiza más llamadas. -- A [recursively-defined](https://en.wikipedia.org/wiki/Recursive_data_type) data structure is a data structure that can be defined using itself. +- Una estructura de datos [definida recursivamente](https://en.wikipedia.org/wiki/Recursive_data_type) es una estructura de datos que se puede definir utilizándose a sí misma. - For instance, the linked list can be defined as a data structure consisting of an object referencing a list (or null). + Por ejemplo, la lista enlazada se puede definir como una estructura de datos que consiste en un objeto que hace referencia a una lista (o nulo). ```js list = { value, next -> list } ``` - Trees like HTML elements tree or the department tree from this chapter are also naturally recursive: they have branches and every branch can have other branches. + Los árboles como el árbol de elementos HTML o el árbol de departamentos de este capítulo también son naturalmente recursivos: se ramifican y cada rama puede tener otras ramas. - Recursive functions can be used to walk them as we've seen in the `sumSalary` example. + Las funciones recursivas se pueden usar para recorrerlas como hemos visto en el ejemplo `sumSalary`. -Any recursive function can be rewritten into an iterative one. And that's sometimes required to optimize stuff. But for many tasks a recursive solution is fast enough and easier to write and support. +Cualquier función recursiva puede reescribirse en una iterativa. Y eso a veces es necesario para optimizar las cosas. Pero para muchas tareas, una solución recursiva es lo suficientemente rápida y fácil de escribir y mantener. diff --git a/1-js/06-advanced-functions/01-recursion/recursion-pow.svg b/1-js/06-advanced-functions/01-recursion/recursion-pow.svg index 2b970a04a..61becce18 100644 --- a/1-js/06-advanced-functions/01-recursion/recursion-pow.svg +++ b/1-js/06-advanced-functions/01-recursion/recursion-pow.svg @@ -1 +1 @@ -pow(x,n)xx * pow(x, n-1)n == 1 ?YesNorecursive call until n==1 \ No newline at end of file +pow(x,n)xx * pow(x, n-1)n == 1 ?SiNollamada recursiva hasta n==1 \ No newline at end of file diff --git a/1-js/06-advanced-functions/02-rest-parameters-spread/article.md b/1-js/06-advanced-functions/02-rest-parameters-spread/article.md index dbdfbd6c0..38f3e5df3 100644 --- a/1-js/06-advanced-functions/02-rest-parameters-spread/article.md +++ b/1-js/06-advanced-functions/02-rest-parameters-spread/article.md @@ -1,20 +1,20 @@ -# Rest parameters and spread syntax +# Parámetros Rest y operador Spread -Many JavaScript built-in functions support an arbitrary number of arguments. +Muchas funciones nativas de JavaScript soportan un número arbitrario de argumentos. -For instance: +Por ejemplo: -- `Math.max(arg1, arg2, ..., argN)` -- returns the greatest of the arguments. -- `Object.assign(dest, src1, ..., srcN)` -- copies properties from `src1..N` into `dest`. -- ...and so on. +- `Math.max(arg1, arg2, ..., argN)` -- devuelve el argumento más grande. +- `Object.assign(dest, src1, ..., srcN)` -- copia las propiedades de `src1..N` en `dest`. +- ...y otros más -In this chapter we'll learn how to do the same. And also, how to pass arrays to such functions as parameters. +En este capítulo aprenderemos como hacer lo mismo. Y, además, cómo trabajar cómodamente con dichas funciones y arrays. -## Rest parameters `...` +## Parámetros Rest `...` -A function can be called with any number of arguments, no matter how it is defined. +Una función puede ser llamada con cualquier número de argumentos sin importar cómo sea definida. -Like here: +Por ejemplo:: ```js run function sum(a, b) { return a + b; @@ -23,14 +23,14 @@ function sum(a, b) { alert( sum(1, 2, 3, 4, 5) ); ``` -There will be no error because of "excessive" arguments. But of course in the result only the first two will be counted, so the result in the code above is `3`. +No habrá ningún error por "exceso" de argumentos. Pero, por supuesto, en el resultado solo los dos primeros serán tomados en cuenta, entonces el resultado del código anterior es `3`. -The rest of the parameters can be included in the function definition by using three dots `...` followed by the name of the array that will contain them. The dots literally mean "gather the remaining parameters into an array". +El resto de los parámetros pueden ser referenciados en la definición de una función con 3 puntos `...` seguidos por el nombre del array que los contendrá. Literalmente significan "Reunir los parámetros restantes en un array". -For instance, to gather all arguments into array `args`: +Por ejemplo, para reunir todos los parámetros en un array `args`: ```js run -function sumAll(...args) { // args is the name for the array +function sumAll(...args) { // args es el nombre del array let sum = 0; for (let arg of args) sum += arg; @@ -43,41 +43,41 @@ alert( sumAll(1, 2) ); // 3 alert( sumAll(1, 2, 3) ); // 6 ``` -We can choose to get the first parameters as variables, and gather only the rest. +Podemos elegir obtener los primeros parámetros como variables, y juntar solo el resto. -Here the first two arguments go into variables and the rest go into `titles` array: +Aquí los primeros dos argumentos van a variables y el resto va al array `titles`: ```js run function showName(firstName, lastName, ...titles) { - alert( firstName + ' ' + lastName ); // Julius Caesar + alert( firstName + ' ' + lastName ); // Julio Cesar - // the rest go into titles array - // i.e. titles = ["Consul", "Imperator"] - alert( titles[0] ); // Consul - alert( titles[1] ); // Imperator + // el resto va en el array titles + // por ejemplo titles = ["Cónsul", "Emperador"] + alert( titles[0] ); // Cónsul + alert( titles[1] ); // Emperador alert( titles.length ); // 2 } -showName("Julius", "Caesar", "Consul", "Imperator"); +showName("Julio", "Cesar", "Cónsul", "Emperador"); ``` -````warn header="The rest parameters must be at the end" -The rest parameters gather all remaining arguments, so the following does not make sense and causes an error: +````warn header="Los parámetros rest deben ir al final" +Los parámetros rest recogen todos los argumentos sobrantes, por lo que el siguiente código no tiene sentido y causa un error: ```js -function f(arg1, ...rest, arg2) { // arg2 after ...rest ?! +function f(arg1, ...rest, arg2) { // arg2 después de ...rest ?! // error } ``` -The `...rest` must always be last. +`...rest` debe ir siempre último. ```` -## The "arguments" variable +## La variable "arguments" -There is also a special array-like object named `arguments` that contains all arguments by their index. +También existe un objeto símil-array especial llamado `arguments` que contiene todos los argumentos indexados. -For instance: +Por ejemplo: ```js run function showName() { @@ -85,29 +85,29 @@ function showName() { alert( arguments[0] ); alert( arguments[1] ); - // it's iterable + // arguments es iterable // for(let arg of arguments) alert(arg); } -// shows: 2, Julius, Caesar -showName("Julius", "Caesar"); +// muestra: 2, Julio, Cesar +showName("Julio", "Cesar"); -// shows: 1, Ilya, undefined (no second argument) +// muestra: 1, Ilya, undefined (no hay segundo argumento) showName("Ilya"); ``` -In old times, rest parameters did not exist in the language, and using `arguments` was the only way to get all arguments of the function. And it still works, we can find it in the old code. +Antiguamente, los parámetros rest no existían en el lenguaje, y usar `arguments` era la única manera de obtener todos los argumentos de una función. Y aún funciona, podemos encontrarlo en código antiguo. -But the downside is that although `arguments` is both array-like and iterable, it's not an array. It does not support array methods, so we can't call `arguments.map(...)` for example. +Pero la desventaja es que a pesar de que `arguments` es símil-array e iterable, no es un array. No soporta los métodos de array, no podemos ejecutar `arguments.map(...)` por ejemplo. -Also, it always contains all arguments. We can't capture them partially, like we did with rest parameters. +Además, siempre contiene todos los argumentos. No podemos capturarlos parcialmente como hicimos con los parámetros rest. -So when we need these features, then rest parameters are preferred. +Por lo tanto, cuando necesitemos estas funcionalidades, los parámetros rest son preferidos. -````smart header="Arrow functions do not have `\"arguments\"`" -If we access the `arguments` object from an arrow function, it takes them from the outer "normal" function. +````smart header="Las funciones flecha no poseen `\"arguments\"`" +Si accedemos el objeto `arguments` desde una función flecha, toma su valor dela función "normal" externa. -Here's an example: +Aquí hay un ejemplo: ```js run function f() { @@ -118,25 +118,25 @@ function f() { f(1); // 1 ``` -As we remember, arrow functions don't have their own `this`. Now we know they don't have the special `arguments` object either. +Como recordamos, las funciones de flecha no tienen su propio `this`. Ahora sabemos que tampoco tienen el objeto especial `arguments`. ```` -## Spread syntax [#spread-syntax] +## Sintaxis Spread [#spread-syntax] -We've just seen how to get an array from the list of parameters. +Acabamos de ver cómo obtener un array de la lista de parámetros. -But sometimes we need to do exactly the reverse. +Pero a veces necesitamos hacer exactamente lo opuesto. -For instance, there's a built-in function [Math.max](mdn:js/Math/max) that returns the greatest number from a list: +Por ejemplo, existe una función nativa [Math.max](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Math/max) que devuelve el número más grande de una lista: ```js run alert( Math.max(3, 5, 1) ); // 5 ``` -Now let's say we have an array `[3, 5, 1]`. How do we call `Math.max` with it? +Ahora bien, supongamos que tenemos un array `[3, 5, 1]`. ¿Cómo ejecutamos `Math.max` con él? -Passing it "as is" won't work, because `Math.max` expects a list of numeric arguments, not a single array: +Pasando la variable no funcionará, porque `Math.max` espera una lista de argumentos numéricos, no un único array: ```js run let arr = [3, 5, 1]; @@ -146,21 +146,21 @@ alert( Math.max(arr) ); // NaN */!* ``` -And surely we can't manually list items in the code `Math.max(arr[0], arr[1], arr[2])`, because we may be unsure how many there are. As our script executes, there could be a lot, or there could be none. And that would get ugly. +Y seguramente no podremos listar manualmente los ítems en el código `Math.max(arr[0], arr[1], arr[2])`, porque tal vez no sepamos cuántos son. A medida que nuestro script se ejecuta, podría haber muchos elementos, o podría no haber ninguno. Y eso podría ponerse feo. -*Spread syntax* to the rescue! It looks similar to rest parameters, also using `...`, but does quite the opposite. +¡*Operador Spread* al rescate! Es similar a los parámetros rest, también usa `...`, pero hace exactamente lo opuesto. -When `...arr` is used in the function call, it "expands" an iterable object `arr` into the list of arguments. +Cuando `...arr` es usado en el llamado de una función, "expande" el objeto iterable `arr` en una lista de argumentos. -For `Math.max`: +Para `Math.max`: ```js run let arr = [3, 5, 1]; -alert( Math.max(...arr) ); // 5 (spread turns array into a list of arguments) +alert( Math.max(...arr) ); // 5 (spread convierte el array en una lista de argumentos) ``` -We also can pass multiple iterables this way: +También podemos pasar múltiples iterables de esta manera: ```js run let arr1 = [1, -2, 3, 4]; @@ -169,7 +169,7 @@ let arr2 = [8, 3, -8, 1]; alert( Math.max(...arr1, ...arr2) ); // 8 ``` -We can even combine the spread syntax with normal values: +Incluso podemos combinar el operador spread con valores normales: ```js run @@ -179,7 +179,7 @@ let arr2 = [8, 3, -8, 1]; alert( Math.max(1, ...arr1, 2, ...arr2, 25) ); // 25 ``` -Also, the spread syntax can be used to merge arrays: +Además, el operador spread puede ser usado para combinar arrays: ```js run let arr = [3, 5, 1]; @@ -189,107 +189,107 @@ let arr2 = [8, 9, 15]; let merged = [0, ...arr, 2, ...arr2]; */!* -alert(merged); // 0,3,5,1,2,8,9,15 (0, then arr, then 2, then arr2) +alert(merged); // 0,3,5,1,2,8,9,15 (0, luego arr, después 2, después arr2) ``` -In the examples above we used an array to demonstrate the spread syntax, but any iterable will do. +En los ejemplos de arriba utilizamos un array para demostrar el operador spread, pero cualquier iterable funcionará también. -For instance, here we use the spread syntax to turn the string into array of characters: +Por ejemplo, aquí usamos el operador spread para convertir la cadena en un array de caracteres: ```js run -let str = "Hello"; +let str = "Hola"; -alert( [...str] ); // H,e,l,l,o +alert( [...str] ); // H,o,l,a ``` -The spread syntax internally uses iterators to gather elements, the same way as `for..of` does. +El operador spread utiliza internamente iteradores para iterar los elementos, de la misma manera que `for..of` hace. -So, for a string, `for..of` returns characters and `...str` becomes `"H","e","l","l","o"`. The list of characters is passed to array initializer `[...str]`. +Entonces, para una cadena `for..of` retorna caracteres y `...str` se convierte en `"H","o","l","a"`. La lista de caracteres es pasada a la inicialización del array `[...str]`. -For this particular task we could also use `Array.from`, because it converts an iterable (like a string) into an array: +Para esta tarea en particular también podríamos haber usado `Array.from`, ya que convierte un iterable (como una cadena de caracteres) en un array: ```js run -let str = "Hello"; +let str = "Hola"; -// Array.from converts an iterable into an array -alert( Array.from(str) ); // H,e,l,l,o +// Array.from convierte un iterable en un array +alert( Array.from(str) ); // H,o,l,a ``` -The result is the same as `[...str]`. +El resultado es el mismo que `[...str]`. -But there's a subtle difference between `Array.from(obj)` and `[...obj]`: +Pero hay una sutil diferencia entre `Array.from(obj)` y `[...obj]`: -- `Array.from` operates on both array-likes and iterables. -- The spread syntax works only with iterables. +- `Array.from` opera con símil-arrays e iterables. +- El operador spread solo opera con iterables. -So, for the task of turning something into an array, `Array.from` tends to be more universal. +Por lo tanto, para la tarea de convertir algo en un array, `Array.from` tiende a ser más universal. -## Copy an array/object +## Copia de un objeto array -Remember when we talked about `Object.assign()` [in the past](info:object-copy#cloning-and-merging-object-assign)? +¿Recuerdas cuando hablamos acerca de `Object.assign()` [anteriormente](info:object-copy#cloning-and-merging-object-assign)? -It is possible to do the same thing with the spread syntax. +Es posible hacer lo mismo con la sintaxis de spread ```js run let arr = [1, 2, 3]; *!* -let arrCopy = [...arr]; // spread the array into a list of parameters - // then put the result into a new array +let arrCopy = [...arr]; // separa el array en una lista de parameters + // luego pone el resultado en un nuevo array */!* -// do the arrays have the same contents? +// ¿los arrays tienen el mismo contenido? alert(JSON.stringify(arr) === JSON.stringify(arrCopy)); // true -// are the arrays equal? -alert(arr === arrCopy); // false (not same reference) +// ¿los arrays son iguales? +alert(arr === arrCopy); // false (no es la misma referencia) -// modifying our initial array does not modify the copy: +// modificando nuestro array inicial no modifica la copia: arr.push(4); alert(arr); // 1, 2, 3, 4 alert(arrCopy); // 1, 2, 3 ``` -Note that it is possible to do the same thing to make a copy of an object: +Nota que es posible hacer lo mismo para hacer una copia de un objeto: ```js run let obj = { a: 1, b: 2, c: 3 }; *!* -let objCopy = { ...obj }; // spread the object into a list of parameters - // then return the result in a new object +let objCopy = { ...obj }; // separa el objeto en una lista de parámetros + // luego devuelve el resultado en un nuevo objeto */!* -// do the objects have the same contents? +// ¿tienen los objetos el mismo contenido? alert(JSON.stringify(obj) === JSON.stringify(objCopy)); // true -// are the objects equal? -alert(obj === objCopy); // false (not same reference) +// ¿son iguales los objetos? +alert(obj === objCopy); // false (no es la misma referencia) -// modifying our initial object does not modify the copy: +// modificando el objeto inicial no modifica la copia: obj.d = 4; alert(JSON.stringify(obj)); // {"a":1,"b":2,"c":3,"d":4} alert(JSON.stringify(objCopy)); // {"a":1,"b":2,"c":3} ``` -This way of copying an object is much shorter than `let objCopy = Object.assign({}, obj)` or for an array `let arrCopy = Object.assign([], arr)` so we prefer to use it whenever we can. +Esta manera de copiar un objeto es mucho más corta que `let objCopy = Object.assign({}, obj);` o para un array `let arrCopy = Object.assign([], arr);` por lo que preferimos usarla siempre que podemos. -## Summary +## Resumen -When we see `"..."` in the code, it is either rest parameters or the spread syntax. +Cuando veamos `"..."` en el código, son los parámetros rest o el operador spread. -There's an easy way to distinguish between them: +Hay una manera fácil de distinguir entre ellos: -- When `...` is at the end of function parameters, it's "rest parameters" and gathers the rest of the list of arguments into an array. -- When `...` occurs in a function call or alike, it's called a "spread syntax" and expands an array into a list. +- Cuando `...` se encuentra al final de los parámetros de una función, son los "parámetros rest" y recogen el resto de la lista de argumentos en un array. +- Cuando `...` está en el llamado de una función o similar, se llama "operador spread" y expande un array en una lista. -Use patterns: +Patrones de uso: -- Rest parameters are used to create functions that accept any number of arguments. -- The spread syntax is used to pass an array to functions that normally require a list of many arguments. +- Los parámetros rest son usados para crear funciones que acepten cualquier número de argumentos. +- El operador spread es usado para pasar un array a funciones que normalmente requieren una lista de muchos argumentos. -Together they help to travel between a list and an array of parameters with ease. +Ambos ayudan a ir entre una lista y un array de parámetros con facilidad. -All arguments of a function call are also available in "old-style" `arguments`: array-like iterable object. +Todos los argumentos de un llamado a una función están también disponibles en el "viejo" `arguments`: un objeto símil-array iterable. diff --git a/1-js/06-advanced-functions/03-closure/1-closure-latest-changes/solution.md b/1-js/06-advanced-functions/03-closure/1-closure-latest-changes/solution.md index 7cbd85ab7..0afef63ab 100644 --- a/1-js/06-advanced-functions/03-closure/1-closure-latest-changes/solution.md +++ b/1-js/06-advanced-functions/03-closure/1-closure-latest-changes/solution.md @@ -1,5 +1,6 @@ -The answer is: **Pete**. +La respuesta es: **Pete**. -A function gets outer variables as they are now, it uses the most recent values. +Una función obtiene variables externas con su estado actual, y utiliza los valores más recientes. + +Los valores de variables anteriores no se guardan en ningún lado. Cuando una función quiere una variable, toma el valor actual de su propio entorno léxico o el externo. -Old variable values are not saved anywhere. When a function wants a variable, it takes the current value from its own Lexical Environment or the outer one. diff --git a/1-js/06-advanced-functions/03-closure/1-closure-latest-changes/task.md b/1-js/06-advanced-functions/03-closure/1-closure-latest-changes/task.md index 819189773..89b8c6c61 100644 --- a/1-js/06-advanced-functions/03-closure/1-closure-latest-changes/task.md +++ b/1-js/06-advanced-functions/03-closure/1-closure-latest-changes/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# Does a function pickup latest changes? +# Esta función: ¿recoge los últimos cambios? -The function sayHi uses an external variable name. When the function runs, which value is it going to use? +La función sayHi usa un nombre de variable externo. Cuando se ejecuta la función, ¿qué valor va a utilizar? ```js let name = "John"; @@ -15,9 +15,10 @@ function sayHi() { name = "Pete"; -sayHi(); // what will it show: "John" or "Pete"? +sayHi(); // ¿qué mostrará: "John" o "Pete"? + ``` +Tales situaciones son comunes tanto en el desarrollo del navegador como del lado del servidor. Se puede programar que una función se ejecute más tarde de lo que se creó, por ejemplo, después de una acción del usuario o una solicitud de red. -Such situations are common both in browser and server-side development. A function may be scheduled to execute later than it is created, for instance after a user action or a network request. +Entonces, la pregunta es: ¿recoge los últimos cambios? -So, the question is: does it pick up the latest changes? diff --git a/1-js/06-advanced-functions/03-closure/10-make-army/lexenv-makearmy.svg b/1-js/06-advanced-functions/03-closure/10-make-army/lexenv-makearmy.svg new file mode 100644 index 000000000..5e759de0b --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/10-make-army/lexenv-makearmy.svg @@ -0,0 +1 @@ +outeri: 0i: 1i: 2i: 10...makeArmy() LexicalEnvironmentbloque for LexicalEnvironment \ No newline at end of file diff --git a/1-js/06-advanced-functions/03-closure/10-make-army/solution.md b/1-js/06-advanced-functions/03-closure/10-make-army/solution.md index 9d99aa717..99642c53c 100644 --- a/1-js/06-advanced-functions/03-closure/10-make-army/solution.md +++ b/1-js/06-advanced-functions/03-closure/10-make-army/solution.md @@ -1,14 +1,14 @@ -Let's examine what exactly happens inside `makeArmy`, and the solution will become obvious. +Examinemos lo que sucede dentro de `makeArmy`, y la solución será obvia. -1. It creates an empty array `shooters`: +1. Esta crea un array vacío de tiradores, `shooters`: ```js let shooters = []; ``` -2. Fills it with functions via `shooters.push(function)` in the loop. +2. Lo llena en el bucle a través de `shooters.push(function...)`. - Every element is a function, so the resulting array looks like this: + Cada elemento es una función, por lo que el array resultante se ve así: ```js no-beautify shooters = [ @@ -24,106 +24,106 @@ Let's examine what exactly happens inside `makeArmy`, and the solution will beco function () { alert(i); } ]; ``` - -3. The array is returned from the function. - - Then, later, the call to any member, e.g. `army[5]()` will get the element `army[5]` from the array (which is a function) and calls it. - - Now why do all such functions show the same value, `10`? - - That's because there's no local variable `i` inside `shooter` functions. When such a function is called, it takes `i` from its outer lexical environment. - - Then, what will be the value of `i`? - - If we look at the source: +3. El array se devuelve desde la función. + + Más tarde la llamada a cualquier miembro, por ejemplo `army[5]()`, obtendrá el elemento `army[5]` del array (será una función) y lo llamará. + + Ahora, ¿por qué todas esas funciones muestran el mismo valor, `10`? + + Esto se debe a que no hay una variable local `i` dentro de las funciones `shooter`. Cuando se llama a tal función, toma `i` de su entorno léxico externo. + + Entonces ¿cuál será el valor de `i`? + + Si miramos la fuente: + ```js function makeArmy() { ... let i = 0; while (i < 10) { let shooter = function() { // shooter function - alert( i ); // should show its number + alert( i ); // debería mostrar su número }; - shooters.push(shooter); // add function to the array + shooters.push(shooter); // agrega la función al array i++; } ... } ``` - - We can see that all `shooter` functions are created in the lexical environment of `makeArmy()` function. But when `army[5]()` is called, `makeArmy` has already finished its job, and the final value of `i` is `10` (`while` stops at `i=10`). - - As the result, all `shooter` functions get the same value from the outer lexical environment and that is, the last value, `i=10`. - + + Podemos ver que todas las funciones `shooter` están creadas en el ambiente léxico asociado a la ejecución de `makeArmy()`. Pero cuando se llama a `army[5]()`, `makeArmy` ya ha terminado su trabajo, y el valor final de `i` es `10` (`while` finaliza en `i=10`). + + Como resultado, todas las funciones `shooter` obtienen el mismo valor del mismo entorno léxico externo, que es el último valor `i=10`. + ![](lexenv-makearmy-empty.svg) - - As you can see above, on each iteration of a `while {...}` block, a new lexical environment is created. So, to fix this, we can copy the value of `i` into a variable within the `while {...}` block, like this: - + + Como puedes ver arriba, con cada iteración del bloque `while {...}` un nuevo ambiente léxico es creado. Entonces, para corregir el problema podemos copiar el valor de `i` en una variable dentro del bloque `while {...}` como aquí: + ```js run function makeArmy() { let shooters = []; - + let i = 0; while (i < 10) { *!* - let j = i; + let j = i; */!* - let shooter = function() { // shooter function - alert( *!*j*/!* ); // should show its number - }; + let shooter = function() { // shooter function + alert( *!*j*/!* ); // debería mostrar su número + }; shooters.push(shooter); i++; } - + return shooters; } - + let army = makeArmy(); - - // Now the code works correctly + + // Ahora el código funciona correctamente army[0](); // 0 army[5](); // 5 ``` - - Here `let j = i` declares an "iteration-local" variable `j` and copies `i` into it. Primitives are copied "by value", so we actually get an independent copy of `i`, belonging to the current loop iteration. - - The shooters work correctly, because the value of `i` now lives a little bit closer. Not in `makeArmy()` Lexical Environment, but in the Lexical Environment that corresponds to the current loop iteration: - + + Aquí `let j = i` declara una variable de iteración local `j` y copia `i` en ella. Las primitivas son copiadas por valor, así que realmente obtenemos una copia independiente de `i`, perteneciente a la iteración del bucle actual. + + Los shooters funcionan correctamente, porque el valor de `i` ahora vive más cerca. No en el ambiente léxico de `makeArmy()` sino en el que corresponde a la iteración del bucle actual: + ![](lexenv-makearmy-while-fixed.svg) - - Such a problem could also be avoided if we used `for` in the beginning, like this: - + + Tal problema habría sido evitado si hubiéramos usado `for` desde el principio: + ```js run demo function makeArmy() { - + let shooters = []; - + *!* for(let i = 0; i < 10; i++) { */!* let shooter = function() { // shooter function - alert( i ); // should show its number + alert( i ); // debería mostrar su número }; shooters.push(shooter); } - + return shooters; } - + let army = makeArmy(); - + army[0](); // 0 army[5](); // 5 ``` - - That's essentially the same, because `for` on each iteration generates a new lexical environment, with its own variable `i`. So `shooter` generated in every iteration references its own `i`, from that very iteration. - + + Esto es esencialmente lo mismo, ya que cada iteración de `for` genera un nuevo ambiente léxico con su propia variable `i`. Así el `shooter` generado en cada iteración hace referencia a su propio `i`, de esa misma iteración. + ![](lexenv-makearmy-for-fixed.svg) -Now, as you've put so much effort into reading this, and the final recipe is so simple - just use `for`, you may wonder -- was it worth that? +Ahora, como has puesto mucho esfuerzo leyendo esto, y la receta final es tan simple: simplemente usa `for`, puede que te preguntes: ¿valió la pena? -Well, if you could easily answer the question, you wouldn't read the solution. So, hopefully this task must have helped you to understand things a bit better. +Bien, si pudiste resolver el problema fácilmente probablemente no habrías necesitado leer la solución, así que esperamos que esta tarea te haya ayudado a entender las cosas mejor. -Besides, there are indeed cases when one prefers `while` to `for`, and other scenarios, where such problems are real. +Además, efectivamente hay casos donde uno prefiere `while` a `for`, y otros escenarios donde tales problemas son reales. diff --git a/1-js/06-advanced-functions/03-closure/10-make-army/task.md b/1-js/06-advanced-functions/03-closure/10-make-army/task.md index f50c7dc20..fa356b3bb 100644 --- a/1-js/06-advanced-functions/03-closure/10-make-army/task.md +++ b/1-js/06-advanced-functions/03-closure/10-make-army/task.md @@ -2,11 +2,11 @@ importance: 5 --- -# Army of functions +# Ejército de funciones -The following code creates an array of `shooters`. +El siguiente código crea una serie de `shooters`. -Every function is meant to output its number. But something is wrong... +Cada función está destinada a generar su número. Pero algo anda mal ... ```js run function makeArmy() { @@ -14,28 +14,28 @@ function makeArmy() { let i = 0; while (i < 10) { - let shooter = function() { // create a shooter function, - alert( i ); // that should show its number + let shooter = function() { // crea la función shooter + alert( i ); // debería mostrar su número }; - shooters.push(shooter); // and add it to the array + shooters.push(shooter); // y agregarlo al array i++; } - // ...and return the array of shooters + // ...y devolver el array de tiradores return shooters; } let army = makeArmy(); *!* -// all shooters show 10 instead of their numbers 0, 1, 2, 3... -army[0](); // 10 from the shooter number 0 -army[1](); // 10 from the shooter number 1 -army[2](); // 10 ...and so on. +// ... todos los tiradores muestran 10 en lugar de sus 0, 1, 2, 3 ... +army[0](); // 10 del tirador número 0 +army[1](); // 10 del tirador número 1 +army[2](); // 10 ...y así sucesivamente. */!* ``` -Why do all of the shooters show the same value? +¿Por qué todos los tiradores muestran el mismo valor? -Fix the code so that they work as intended. +Arregle el código para que funcionen según lo previsto. diff --git a/1-js/06-advanced-functions/03-closure/2-closure-variable-access/solution.md b/1-js/06-advanced-functions/03-closure/2-closure-variable-access/solution.md index 0a522132f..b7e7bc9cc 100644 --- a/1-js/06-advanced-functions/03-closure/2-closure-variable-access/solution.md +++ b/1-js/06-advanced-functions/03-closure/2-closure-variable-access/solution.md @@ -1,9 +1,11 @@ -The answer is: **Pete**. -The `work()` function in the code below gets `name` from the place of its origin through the outer lexical environment reference: +La respuesta es: **Pete**. + +La función `work()` en el código a continuación obtiene `name` del lugar de su origen a través de la referencia del entorno léxico externo: ![](lexenv-nested-work.svg) -So, the result is `"Pete"` here. +Entonces, el resultado es "Pete". + +Pero si no hubiera `let name` en` makeWorker () `, entonces la búsqueda saldría y tomaría la variable global como podemos ver en la cadena de arriba. En ese caso, el resultado sería `John`. -But if there were no `let name` in `makeWorker()`, then the search would go outside and take the global variable as we can see from the chain above. In that case the result would be `"John"`. diff --git a/1-js/06-advanced-functions/03-closure/2-closure-variable-access/task.md b/1-js/06-advanced-functions/03-closure/2-closure-variable-access/task.md index d12a385c8..0e3ca3428 100644 --- a/1-js/06-advanced-functions/03-closure/2-closure-variable-access/task.md +++ b/1-js/06-advanced-functions/03-closure/2-closure-variable-access/task.md @@ -2,11 +2,11 @@ importance: 5 --- -# Which variables are available? +# ¿Qué variables están disponibles? -The function `makeWorker` below makes another function and returns it. That new function can be called from somewhere else. +La función `makeWorker` a continuación crea otra función y la devuelve. Esa nueva función se puede llamar desde otro lugar. -Will it have access to the outer variables from its creation place, or the invocation place, or both? +¿Tendrá acceso a las variables externas desde su lugar de creación, o desde el lugar de invocación, o ambos? ```js function makeWorker() { @@ -19,11 +19,11 @@ function makeWorker() { let name = "John"; -// create a function +// crea una función let work = makeWorker(); -// call it -work(); // what will it show? +// la llama +work(); // ¿qué mostrará? ``` -Which value it will show? "Pete" or "John"? +¿Qué valor mostrará? "Pete" o "John"? diff --git a/1-js/06-advanced-functions/03-closure/3-counter-independent/solution.md b/1-js/06-advanced-functions/03-closure/3-counter-independent/solution.md index 25ecbea4c..6c306c2c9 100644 --- a/1-js/06-advanced-functions/03-closure/3-counter-independent/solution.md +++ b/1-js/06-advanced-functions/03-closure/3-counter-independent/solution.md @@ -1,5 +1,7 @@ -The answer: **0,1.** -Functions `counter` and `counter2` are created by different invocations of `makeCounter`. +La respuesta: **0,1.** + +Las funciones `counter` y` counter2` son creadas por diferentes invocaciones de `makeCounter`. + +Por lo tanto, tienen entornos léxicos externos independientes, cada uno tiene su propio `count`. -So they have independent outer Lexical Environments, each one has its own `count`. diff --git a/1-js/06-advanced-functions/03-closure/3-counter-independent/task.md b/1-js/06-advanced-functions/03-closure/3-counter-independent/task.md index e8c17dd31..39deda7be 100644 --- a/1-js/06-advanced-functions/03-closure/3-counter-independent/task.md +++ b/1-js/06-advanced-functions/03-closure/3-counter-independent/task.md @@ -2,11 +2,12 @@ importance: 5 --- -# Are counters independent? +# ¿Son independientes los contadores? -Here we make two counters: `counter` and `counter2` using the same `makeCounter` function. +Aquí hacemos dos contadores: `counter` y `counter2` usando la misma función `makeCounter`. + +¿Son independientes? ¿Qué va a mostrar el segundo contador? `0,1` o `2,3` o algo más? -Are they independent? What is the second counter going to show? `0,1` or `2,3` or something else? ```js function makeCounter() { diff --git a/1-js/06-advanced-functions/03-closure/4-counter-object-independent/solution.md b/1-js/06-advanced-functions/03-closure/4-counter-object-independent/solution.md index cd4e641e4..e18f7b151 100644 --- a/1-js/06-advanced-functions/03-closure/4-counter-object-independent/solution.md +++ b/1-js/06-advanced-functions/03-closure/4-counter-object-independent/solution.md @@ -1,7 +1,8 @@ -Surely it will work just fine. +Seguramente funcionará bien. + +Ambas funciones anidadas se crean dentro del mismo entorno léxico externo, por lo que comparten acceso a la misma variable `count`: -Both nested functions are created within the same outer Lexical Environment, so they share access to the same `count` variable: ```js run function Counter() { @@ -10,7 +11,6 @@ function Counter() { this.up = function() { return ++count; }; - this.down = function() { return --count; }; diff --git a/1-js/06-advanced-functions/03-closure/4-counter-object-independent/task.md b/1-js/06-advanced-functions/03-closure/4-counter-object-independent/task.md index d770b0ffc..046ec0ba5 100644 --- a/1-js/06-advanced-functions/03-closure/4-counter-object-independent/task.md +++ b/1-js/06-advanced-functions/03-closure/4-counter-object-independent/task.md @@ -2,11 +2,12 @@ importance: 5 --- -# Counter object +# Objeto contador -Here a counter object is made with the help of the constructor function. +Aquí se crea un objeto contador con la ayuda de la función constructora. + +¿Funcionará? ¿Qué mostrará? -Will it work? What will it show? ```js function Counter() { @@ -26,4 +27,3 @@ alert( counter.up() ); // ? alert( counter.up() ); // ? alert( counter.down() ); // ? ``` - diff --git a/1-js/06-advanced-functions/03-closure/5-function-in-if/solution.md b/1-js/06-advanced-functions/03-closure/5-function-in-if/solution.md index e2e7a91b3..01c7c21c1 100644 --- a/1-js/06-advanced-functions/03-closure/5-function-in-if/solution.md +++ b/1-js/06-advanced-functions/03-closure/5-function-in-if/solution.md @@ -1,3 +1,5 @@ -The result is **an error**. -The function `sayHi` is declared inside the `if`, so it only lives inside it. There is no `sayHi` outside. \ No newline at end of file +El resultado es **un error**. + +La función `sayHi` se declara dentro de `if`, por lo que solo vive dentro de ella. No hay `sayHi` afuera. + diff --git a/1-js/06-advanced-functions/03-closure/5-function-in-if/task.md b/1-js/06-advanced-functions/03-closure/5-function-in-if/task.md index 4e386eec5..71eeb5c6f 100644 --- a/1-js/06-advanced-functions/03-closure/5-function-in-if/task.md +++ b/1-js/06-advanced-functions/03-closure/5-function-in-if/task.md @@ -1,9 +1,9 @@ importance: 5 --- -# Function in if +# Función en if -Look at the code. What will be the result of the call at the last line? +Mira el código ¿Cuál será el resultado de la llamada en la última línea? ```js run let phrase = "Hello"; diff --git a/1-js/06-advanced-functions/03-closure/6-closure-sum/solution.md b/1-js/06-advanced-functions/03-closure/6-closure-sum/solution.md index a6679cd20..baaca18e9 100644 --- a/1-js/06-advanced-functions/03-closure/6-closure-sum/solution.md +++ b/1-js/06-advanced-functions/03-closure/6-closure-sum/solution.md @@ -1,14 +1,13 @@ -For the second parentheses to work, the first ones must return a function. +Para que funcionen los segundos paréntesis, los primeros deben devolver una función. -Like this: +Como esto: ```js run function sum(a) { return function(b) { - return a + b; // takes "a" from the outer lexical environment + return a + b; // toma "a" del entorno léxico externo }; - } alert( sum(1)(2) ); // 3 diff --git a/1-js/06-advanced-functions/03-closure/6-closure-sum/task.md b/1-js/06-advanced-functions/03-closure/6-closure-sum/task.md index b45758562..e4ebd8de9 100644 --- a/1-js/06-advanced-functions/03-closure/6-closure-sum/task.md +++ b/1-js/06-advanced-functions/03-closure/6-closure-sum/task.md @@ -2,16 +2,16 @@ importance: 4 --- -# Sum with closures +# Suma con clausuras -Write function `sum` that works like this: `sum(a)(b) = a+b`. +Escriba la función `sum` que funcione así: `sum(a)(b) = a+b`. -Yes, exactly this way, using double parentheses (not a mistype). +Sí, exactamente de esta manera, usando paréntesis dobles (no es un error de tipeo). + +Por ejemplo: -For instance: ```js sum(1)(2) = 3 sum(5)(-1) = 4 ``` - diff --git a/1-js/06-advanced-functions/03-closure/7-let-scope/solution.md b/1-js/06-advanced-functions/03-closure/7-let-scope/solution.md index b16b35290..1c8e4ff0e 100644 --- a/1-js/06-advanced-functions/03-closure/7-let-scope/solution.md +++ b/1-js/06-advanced-functions/03-closure/7-let-scope/solution.md @@ -1,40 +1,42 @@ -The result is: **error**. -Try running it: +El resultado es: **error**. + +Intenta correr esto: ```js run let x = 1; function func() { *!* - console.log(x); // ReferenceError: Cannot access 'x' before initialization + console.log(x); // ReferenceError: No se puede acceder a 'x' antes de la inicialización */!* let x = 2; } - func(); ``` -In this example we can observe the peculiar difference between a "non-existing" and "uninitialized" variable. +En este ejemplo podemos observar la diferencia peculiar entre una variable "no existente" y una variable "no inicializada". -As you may have read in the article [](info:closure), a variable starts in the "uninitialized" state from the moment when the execution enters a code block (or a function). And it stays uninitalized until the corresponding `let` statement. +Como habrás leído en el artículo [](info:closure), una variable comienza en el estado "no inicializado" desde el momento en que la ejecución entra en un bloque de código (o una función). Y permanece sin inicializar hasta la correspondiente declaración `let`. -In other words, a variable technically exists, but can't be used before `let`. +En otras palabras, una variable técnicamente existe, pero no se puede usar antes de `let`. -The code above demonstrates it. +El código anterior lo demuestra. ```js function func() { *!* - // the local variable x is known to the engine from the beginning of the function, - // but "uninitialized" (unusable) until let ("dead zone") - // hence the error + +// la variable local x es conocida por el motor desde el comienzo de la función, +// pero "unitialized" (inutilizable) hasta let ("zona muerta") +// de ahí el error */!* - console.log(x); // ReferenceError: Cannot access 'x' before initialization + console.log(x); // ReferenceError: No se puede acceder a 'x' antes de la inicialización let x = 2; } ``` -This zone of temporary unusability of a variable (from the beginning of the code block till `let`) is sometimes called the "dead zone". +Esta zona de inutilización temporal de una variable (desde el comienzo del bloque de código hasta `let`) a veces se denomina" zona muerta ". + diff --git a/1-js/06-advanced-functions/03-closure/7-let-scope/task.md b/1-js/06-advanced-functions/03-closure/7-let-scope/task.md index fb7445e66..ea764bc0f 100644 --- a/1-js/06-advanced-functions/03-closure/7-let-scope/task.md +++ b/1-js/06-advanced-functions/03-closure/7-let-scope/task.md @@ -2,9 +2,9 @@ importance: 4 --- -# Is variable visible? +# ¿Es visible la variable? -What will be the result of this code? +¿Cuál será el resultado de este código? ```js let x = 1; @@ -18,4 +18,5 @@ function func() { func(); ``` -P.S. There's a pitfall in this task. The solution is not obvious. +P.D Hay una trampa en esta tarea. La solución no es obvia. + diff --git a/1-js/06-advanced-functions/03-closure/8-filter-through-function/solution.md b/1-js/06-advanced-functions/03-closure/8-filter-through-function/solution.md index 46c5514a8..d522bd1f1 100644 --- a/1-js/06-advanced-functions/03-closure/8-filter-through-function/solution.md +++ b/1-js/06-advanced-functions/03-closure/8-filter-through-function/solution.md @@ -1,5 +1,6 @@ -# Filter inBetween +# Filtrar inBetween + ```js run function inBetween(a, b) { @@ -12,7 +13,8 @@ let arr = [1, 2, 3, 4, 5, 6, 7]; alert( arr.filter(inBetween(3, 6)) ); // 3,4,5,6 ``` -# Filter inArray +# Filtrar inArray + ```js run demo function inArray(arr) { diff --git a/1-js/06-advanced-functions/03-closure/8-filter-through-function/task.md b/1-js/06-advanced-functions/03-closure/8-filter-through-function/task.md index d1c39f949..aeccdacca 100644 --- a/1-js/06-advanced-functions/03-closure/8-filter-through-function/task.md +++ b/1-js/06-advanced-functions/03-closure/8-filter-through-function/task.md @@ -2,28 +2,28 @@ importance: 5 --- -# Filter through function +# Filtrar a través de una función -We have a built-in method `arr.filter(f)` for arrays. It filters all elements through the function `f`. If it returns `true`, then that element is returned in the resulting array. +Tenemos un método incorporado `arr.filter(f)` para arrays. Filtra todos los elementos a través de la función `f`. Si devuelve `true`, entonces ese elemento se devuelve en el array resultante. -Make a set of "ready to use" filters: +Haga un conjunto de filtros "listos para usar": -- `inBetween(a, b)` -- between `a` and `b` or equal to them (inclusively). -- `inArray([...])` -- in the given array. +- `inBetween(a, b)` -- entre `a` y `b` o igual a ellos (inclusive). +- `inArray([...])` -- en el array dado -The usage must be like this: +El uso debe ser así: -- `arr.filter(inBetween(3,6))` -- selects only values between 3 and 6. -- `arr.filter(inArray([1,2,3]))` -- selects only elements matching with one of the members of `[1,2,3]`. +- `arr.filter(inBetween(3,6))` -- selecciona solo valores entre 3 y 6. +- `arr.filter(inArray([1,2,3]))` -- selecciona solo elementos que coinciden con uno de los miembros de `[1,2,3]`. -For instance: +Por ejemplo: ```js -/* .. your code for inBetween and inArray */ +/* .. tu código para inBetween y inArray */ + let arr = [1, 2, 3, 4, 5, 6, 7]; alert( arr.filter(inBetween(3, 6)) ); // 3,4,5,6 alert( arr.filter(inArray([1, 2, 10])) ); // 1,2 ``` - diff --git a/1-js/06-advanced-functions/03-closure/9-sort-by-field/task.md b/1-js/06-advanced-functions/03-closure/9-sort-by-field/task.md index 08fb5cc34..8832f4df9 100644 --- a/1-js/06-advanced-functions/03-closure/9-sort-by-field/task.md +++ b/1-js/06-advanced-functions/03-closure/9-sort-by-field/task.md @@ -2,9 +2,10 @@ importance: 5 --- -# Sort by field +# Ordenar por campo + +Tenemos una variedad de objetos para ordenar: -We've got an array of objects to sort: ```js let users = [ @@ -14,23 +15,25 @@ let users = [ ]; ``` -The usual way to do that would be: +La forma habitual de hacerlo sería: ```js -// by name (Ann, John, Pete) +// por nombre(Ann, John, Pete) users.sort((a, b) => a.name > b.name ? 1 : -1); -// by age (Pete, Ann, John) +// por edad (Pete, Ann, John) users.sort((a, b) => a.age > b.age ? 1 : -1); ``` -Can we make it even less verbose, like this? +¿Podemos hacerlo aún menos detallado, como este? + ```js users.sort(byField('name')); users.sort(byField('age')); ``` -So, instead of writing a function, just put `byField(fieldName)`. +Entonces, en lugar de escribir una función, simplemente ponga `byField (fieldName)`. + +Escriba la función `byField` que se pueda usar para eso. -Write the function `byField` that can be used for that. diff --git a/1-js/06-advanced-functions/03-closure/article.md b/1-js/06-advanced-functions/03-closure/article.md index cb43a7968..132f3e1d3 100644 --- a/1-js/06-advanced-functions/03-closure/article.md +++ b/1-js/06-advanced-functions/03-closure/article.md @@ -1,75 +1,74 @@ -# Variable scope, closure +# Ámbito de Variable y el concepto "closure" -JavaScript is a very function-oriented language. It gives us a lot of freedom. A function can be created at any moment, passed as an argument to another function, and then called from a totally different place of code later. +JavaScript es un lenguaje muy orientado a funciones. Nos da mucha libertad. Una función se puede crear en cualquier momento, pasar como argumento a otra función y luego llamar desde un lugar de código totalmente diferente más tarde. -We already know that a function can access variables outside of it ("outer" variables). +Ya sabemos que una función puede acceder a variables fuera de ella. -But what happens if outer variables change since a function is created? Will the function get newer values or the old ones? +Pero, ¿qué sucede si estas variables "externas" cambian desde que se crea una función? ¿La función verá los valores nuevos o los antiguos? -And what if a function is passed along as an argument and called from another place of code, will it get access to outer variables at the new place? +Y si una función se pasa como parámetro y se llama desde otro lugar del código, ¿tendrá acceso a las variables externas en el nuevo lugar? -Let's expand our knowledge to understand these scenarios and more complex ones. +Ampliemos nuestro conocimiento para comprender estos escenarios y otros más complejos. -```smart header="We'll talk about `let/const` variables here" -In JavaScript, there are 3 ways to declare a variable: `let`, `const` (the modern ones), and `var` (the remnant of the past). +```smart header="Aquí hablaremos de variables let/const" +En JavaScript, hay 3 formas de declarar una variable: `let`, `const` (las modernas) y `var` (más antigua). -- In this article we'll use `let` variables in examples. -- Variables, declared with `const`, behave the same, so this article is about `const` too. -- The old `var` has some notable differences, they will be covered in the article . +- En este artículo usaremos las variables `let` en los ejemplos. +- Las variables declaradas con `const` se comportan igual, por lo que este artículo también trata sobre `const`. +- El antiguo `var` tiene algunas diferencias notables que se tratarán en el artículo . ``` -## Code blocks +## Bloques de código -If a variable is declared inside a code block `{...}`, it's only visible inside that block. +Si una variable se declara dentro de un bloque de código `{...}`, solo es visible dentro de ese bloque. -For example: +Por ejemplo: ```js run { - // do some job with local variables that should not be seen outside - - let message = "Hello"; // only visible in this block - + // hacer un trabajo con variables locales que no deberían verse fuera + let message = "Hello"; // solo visible en este bloque alert(message); // Hello } -alert(message); // Error: message is not defined +alert(message); // Error: el mensaje no se ha definido (undefined) ``` -We can use this to isolate a piece of code that does its own task, with variables that only belong to it: +Podemos usar esto para aislar un fragmento de código que realiza su propia tarea, con variables que solo le pertenecen a él: ```js run { - // show message + // ver mensaje let message = "Hello"; alert(message); } { - // show another message + // ver otro mensaje let message = "Goodbye"; alert(message); } ``` -````smart header="There'd be an error without blocks" -Please note, without separate blocks there would be an error, if we use `let` with the existing variable name: +````smart header="Sin bloques, habría un error" +Tenga en cuenta que, sin bloques separados, habría un error si usáramos 'let' con el nombre de la variable existente: ```js run -// show message +// ver mensaje let message = "Hello"; alert(message); -// show another message +// ver otro mensaje *!* -let message = "Goodbye"; // Error: variable already declared +let message = "Goodbye"; // Error: la variable ya ha sido declarada */!* alert(message); ``` ```` -For `if`, `for`, `while` and so on, variables declared in `{...}` are also only visible inside: +Para `if`, `for`, `while` y otros, las variables declaradas dentro de `{...}` también son solo visibles en su interior: + ```js run if (true) { @@ -78,38 +77,39 @@ if (true) { alert(phrase); // Hello! } -alert(phrase); // Error, no such variable! +alert(phrase); // ¡Error, no hay tal variable! ``` -Here, after `if` finishes, the `alert` below won't see the `phrase`, hence the error. +Aquí, después de que `if` termine, la `alerta` a continuación no verá la `phrase`, de ahí el error. -That's great, as it allows us to create block-local variables, specific to an `if` branch. +Eso es genial, ya que nos permite crear variables locales de bloque, específicas de una rama `if`. -The similar thing holds true for `for` and `while` loops: +De la misma manera que para los bucles `for` y `while`: ```js run for (let i = 0; i < 3; i++) { - // the variable i is only visible inside this for + // la variable i solo es visible dentro de este for alert(i); // 0, then 1, then 2 } -alert(i); // Error, no such variable +alert(i); // ¡Error, no hay tal variable! ``` -Visually, `let i` is outside of `{...}`. But the `for` construct is special here: the variable, declared inside it, is considered a part of the block. +Visualmente, `let i` está fuera de `{...} `; pero la construcción `for` es especial aquí: la variable declarada dentro de ella se considera parte del bloque. + -## Nested functions +## Funciones anidadas -A function is called "nested" when it is created inside another function. +Una función se llama "anidada" cuando se crea dentro de otra función. -It is easily possible to do this with JavaScript. +Es fácilmente posible hacer esto con JavaScript. -We can use it to organize our code, like this: +Podemos usarlo para organizar nuestro código: ```js function sayHiBye(firstName, lastName) { - // helper nested function to use below + // función anidada auxiliar para usar a continuación function getFullName() { return firstName + " " + lastName; } @@ -120,11 +120,11 @@ function sayHiBye(firstName, lastName) { } ``` -Here the *nested* function `getFullName()` is made for convenience. It can access the outer variables and so can return the full name. Nested functions are quite common in JavaScript. +Aquí la función *anidada* `getFullName()` se hace por conveniencia. Puede acceder a las variables externas y, por lo tanto, puede devolver el nombre completo. Las funciones anidadas son bastante comunes en JavaScript. -What's much more interesting, a nested function can be returned: either as a property of a new object or as a result by itself. It can then be used somewhere else. No matter where, it still has access to the same outer variables. +Lo que es mucho más interesante, es que puede devolverse una función anidada: ya sea como propiedad de un nuevo objeto o como resultado en sí mismo. Luego se puede usar en otro lugar. No importa dónde, todavía tiene acceso a las mismas variables externas. -Below, `makeCounter` creates the "counter" function that returns the next number on each invocation: +A continuación, `makeCounter` crea la función "contador" que devuelve el siguiente número en cada invocación: ```js run function makeCounter() { @@ -142,87 +142,87 @@ alert( counter() ); // 1 alert( counter() ); // 2 ``` -Despite being simple, slightly modified variants of that code have practical uses, for instance, as a [random number generator](https://en.wikipedia.org/wiki/Pseudorandom_number_generator) to generate random values for automated tests. +A pesar de ser simples, variantes ligeramente modificadas de ese código tienen usos prácticos, como por ejemplo un [generador de números aleatorios](https://es.wikipedia.org/wiki/Generador_de_n%C3%BAmeros_pseudoaleatorios) para pruebas automatizadas. -How does this work? If we create multiple counters, will they be independent? What's going on with the variables here? +¿Cómo funciona esto? Si creamos múltiples contadores, ¿serán independientes? ¿Qué está pasando con las variables aquí? -Understanding such things is great for the overall knowledge of JavaScript and beneficial for more complex scenarios. So let's go a bit in-depth. +Entender tales cosas es excelente para el conocimiento general de JavaScript y beneficioso para escenarios más complejos. Así que vamos a profundizar un poco. -## Lexical Environment +## Ámbito o alcance léxico -```warn header="Here be dragons!" -The in-depth technical explanation lies ahead. +```warn header="¡Aquí hay dragones!" +La explicación técnica en profundidad está por venir. -As far as I'd like to avoid low-level language details, any understanding without them would be lacking and incomplete, so get ready. +Me gustaría evitar los detalles de lenguaje de bajo nivel, pero cualquier comprensión sin ellos sería insuficiente e incompleta, así que prepárate. ``` -For clarity, the explanation is split into multiple steps. +Para mayor claridad, la explicación se divide en múltiples pasos. -### Step 1. Variables +### Paso 1. Variables -In JavaScript, every running function, code block `{...}`, and the script as a whole have an internal (hidden) associated object known as the *Lexical Environment*. +En JavaScript, todas las funciones en ejecución, el bloque de código `{...}` y el script en su conjunto tienen un objeto interno (oculto) asociado, conocido como *Alcance léxico*. -The Lexical Environment object consists of two parts: +El objeto del alcance léxico consta de dos partes: -1. *Environment Record* -- an object that stores all local variables as its properties (and some other information like the value of `this`). -2. A reference to the *outer lexical environment*, the one associated with the outer code. +1. *Registro de entorno*: es un objeto que almacena en sus propiedades todas las variables locales (y alguna otra información, como el valor de `this`). +2. Una referencia al *entorno léxico externo*, asociado con el código externo. -**A "variable" is just a property of the special internal object, `Environment Record`. "To get or change a variable" means "to get or change a property of that object".** +**Una "variable" es solo una propiedad del objeto interno especial, el `Registro de entorno`. "Obtener o cambiar una variable" significa "obtener o cambiar una propiedad de ese objeto".** -In this simple code without functions, there is only one Lexical Environment: +En este código simple y sin funciones, solo hay un entorno léxico: ![lexical environment](lexical-environment-global.svg) -This is the so-called *global* Lexical Environment, associated with the whole script. +Este es el denominado entorno léxico *global*, asociado con todo el script. -On the picture above, the rectangle means Environment Record (variable store) and the arrow means the outer reference. The global Lexical Environment has no outer reference, that's why the arrow points to `null`. +En la imagen de arriba, el rectángulo significa Registro de entornos (almacén de variables) y la flecha significa la referencia externa. El entorno léxico global no tiene referencia externa, por eso la flecha apunta a `nulo`. -As the code starts executing and goes on, the Lexical Environment changes. +A medida que el código comienza a ejecutarse y continúa, el entorno léxico cambia. -Here's a little bit longer code: +Aquí hay un código un poco más largo: ![lexical environment](closure-variable-phrase.svg) -Rectangles on the right-hand side demonstrate how the global Lexical Environment changes during the execution: +Los rectángulos en el lado derecho demuestran cómo cambia el entorno léxico global durante la ejecución: -1. When the script starts, the Lexical Environment is pre-populated with all declared variables. - - Initially, they are in the "Uninitialized" state. That's a special internal state, it means that the engine knows about the variable, but it cannot be referenced until it has been declared with `let`. It's almost the same as if the variable didn't exist. -2. Then `let phrase` definition appears. There's no assignment yet, so its value is `undefined`. We can use the variable from this point forward. -3. `phrase` is assigned a value. -4. `phrase` changes the value. +1. Cuando se inicia el script, el entorno léxico se rellena previamente con todas las variables declaradas. +     - Inicialmente, están en el estado "No inicializado". Ese es un estado interno especial, significa que el motor conoce la variable, pero no se puede hacer referencia a ella hasta que se haya declarado con `let`. Es casi lo mismo que si la variable no existiera. +2. Luego aparece la definición `let phrase`.Todavía no hay una asignación, por lo que su valor es `undefined`. Podemos usar la variable desde este punto en adelante. +3. `phrase` se le asigna un valor. +4. `phrase` cambia el valor. -Everything looks simple for now, right? +Todo parece simple por ahora, ¿verdad? -- A variable is a property of a special internal object, associated with the currently executing block/function/script. -- Working with variables is actually working with the properties of that object. +- Una variable es una propiedad de un objeto interno especial que está asociado con el bloque/función/script actualmente en ejecución. +- Trabajar con variables es realmente trabajar con las propiedades de ese objeto. -```smart header="Lexical Environment is a specification object" -"Lexical Environment" is a specification object: it only exists "theoretically" in the [language specification](https://tc39.es/ecma262/#sec-lexical-environments) to describe how things work. We can't get this object in our code and manipulate it directly. +```smart header="El entorno léxico es un objeto de especificación" +El "entorno léxico" es un objeto de especificación: solo existe "teóricamente" en la [especificación del lenguaje](https://tc39.es/ecma262/#sec-lexical-environments) para describir cómo funcionan las cosas. No podemos obtener este objeto en nuestro código y manipularlo directamente. -JavaScript engines also may optimize it, discard variables that are unused to save memory and perform other internal tricks, as long as the visible behavior remains as described. +Los motores de JavaScript también pueden optimizarlo, descartar variables que no se utilizan para ahorrar memoria y realizar otros trucos internos, siempre que el comportamiento visible permanezca como se describe. ``` -### Step 2. Function Declarations +### Paso 2. Declaración de funciones -A function is also a value, like a variable. +Una función también es un valor, como una variable. -**The difference is that a Function Declaration is instantly fully initialized.** +**La diferencia es que una `declaración de función` se inicializa completamente al instante.** -When a Lexical Environment is created, a Function Declaration immediately becomes a ready-to-use function (unlike `let`, that is unusable till the declaration). +Cuando se crea un entorno léxico, una declaración de función se convierte inmediatamente en una función lista para usar (a diferencia de `let`, que no se puede usar hasta la declaración). -That's why we can use a function, declared as Function Declaration, even before the declaration itself. +Es por eso que podemos usar una función, declarada como `declaración de función`, incluso antes de la declaración misma. -For example, here's the initial state of the global Lexical Environment when we add a function: +Por ejemplo, aquí está el estado inicial del entorno léxico global cuando agregamos una función: ![](closure-function-declaration.svg) -Naturally, this behavior only applies to Function Declarations, not Function Expressions where we assign a function to a variable, such as `let say = function(name)...`. +Naturalmente, este comportamiento solo se aplica a las `declaraciones de funciones`, no a las `expresiones de funciones`, donde asignamos una función a una variable, como `let say = function (name) ...`. -### Step 3. Inner and outer Lexical Environment +### Paso 3. Entorno léxico interno y externo -When a function runs, at the beginning of the call, a new Lexical Environment is created automatically to store local variables and parameters of the call. +Cuando se ejecuta una función, al comienzo de la llamada se crea automáticamente un nuevo entorno léxico para almacenar variables y parámetros locales de la llamada. -For instance, for `say("John")`, it looks like this (the execution is at the line, labelled with an arrow): +Por ejemplo, para `say(" John ")`, se ve así (la ejecución está en la línea etiquetada con una flecha): ![](callback-hell.svg) -The "pyramid" of nested calls grows to the right with every asynchronous action. Soon it spirals out of control. +La "pirámide" de llamadas anidadas crece hacia la derecha con cada acción asincrónica. Pronto se sale de control. -So this way of coding isn't very good. +Entonces esta forma de codificación no es tan buena. -We can try to alleviate the problem by making every action a standalone function, like this: +Podemos tratar de aliviar el problema haciendo, para cada acción, una función independiente: ```js loadScript('1.js', step1); @@ -296,17 +296,17 @@ function step3(error, script) { if (error) { handleError(error); } else { - // ...continue after all scripts are loaded (*) + // ...continua después de que se han cargado todos los scripts (*) } } ``` -See? It does the same thing, and there's no deep nesting now because we made every action a separate top-level function. +¿Lo Ves? Hace lo mismo, y ahora no hay anidamiento profundo porque convertimos cada acción en una función de nivel superior separada. -It works, but the code looks like a torn apart spreadsheet. It's difficult to read, and you probably noticed that one needs to eye-jump between pieces while reading it. That's inconvenient, especially if the reader is not familiar with the code and doesn't know where to eye-jump. +Funciona, pero el código parece una hoja de cálculo desgarrada. Es difícil de leer, y habrás notado que hay que saltar de un lado a otro mientras lees. Es un inconveniente, especialmente si el lector no está familiarizado con el código y no sabe dónde dirigir la mirada. -Also, the functions named `step*` are all of single use, they are created only to avoid the "pyramid of doom." No one is going to reuse them outside of the action chain. So there's a bit of namespace cluttering here. +Además, las funciones llamadas `step*` son de un solo uso, existen únicamente para evitar la "Pirámide de callbacks". Nadie los reutilizará fuera de la cadena de acción. Así que hay muchos nombres abarrotados aquí. -We'd like to have something better. +Nos gustaría tener algo mejor. -Luckily, there are other ways to avoid such pyramids. One of the best ways is to use "promises", described in the next chapter. +Afortunadamente, hay otras formas de evitar tales pirámides. Una de las mejores formas es usando "promesas", descritas en el próximo capítulo. diff --git a/1-js/11-async/02-promise-basics/01-re-resolve/solution.md b/1-js/11-async/02-promise-basics/01-re-resolve/solution.md index 57046a257..af6c3f6af 100644 --- a/1-js/11-async/02-promise-basics/01-re-resolve/solution.md +++ b/1-js/11-async/02-promise-basics/01-re-resolve/solution.md @@ -1,3 +1,3 @@ -The output is: `1`. +La salida es: `1`. -The second call to `resolve` is ignored, because only the first call of `reject/resolve` is taken into account. Further calls are ignored. +La segunda llamada a 'resolve' se ignora, porque solo se tiene en cuenta la primera llamada de 'reject/resolve'. Otras llamadas son ignoradas. diff --git a/1-js/11-async/02-promise-basics/01-re-resolve/task.md b/1-js/11-async/02-promise-basics/01-re-resolve/task.md index 185806ed6..8d22678f4 100644 --- a/1-js/11-async/02-promise-basics/01-re-resolve/task.md +++ b/1-js/11-async/02-promise-basics/01-re-resolve/task.md @@ -1,8 +1,8 @@ -# Re-resolve a promise? +# ¿Volver a resolver una promesa? -What's the output of the code below? +¿Cuál es el resultado del código a continuación? ```js let promise = new Promise(function(resolve, reject) { diff --git a/1-js/11-async/02-promise-basics/02-delay-promise/task.md b/1-js/11-async/02-promise-basics/02-delay-promise/task.md index 0d74cf45d..ef33f71a6 100644 --- a/1-js/11-async/02-promise-basics/02-delay-promise/task.md +++ b/1-js/11-async/02-promise-basics/02-delay-promise/task.md @@ -1,14 +1,14 @@ -# Delay with a promise +# Demora con una promesa -The built-in function `setTimeout` uses callbacks. Create a promise-based alternative. +La función incorporada `setTimeout` utiliza callbacks. Crea una alternativa basada en promesas. -The function `delay(ms)` should return a promise. That promise should resolve after `ms` milliseconds, so that we can add `.then` to it, like this: +La función `delay(ms)` debería devolver una promesa. Esa promesa debería resolverse después de `ms` milisegundos, para que podamos agregarle `.then`, así: ```js function delay(ms) { - // your code + // tu código } -delay(3000).then(() => alert('runs after 3 seconds')); +delay(3000).then(() => alert('se ejecuta después de 3 segundos')); ``` diff --git a/1-js/11-async/02-promise-basics/03-animate-circle-promise/solution.view/index.html b/1-js/11-async/02-promise-basics/03-animate-circle-promise/solution.view/index.html index 6052f009e..1f7e57fd1 100644 --- a/1-js/11-async/02-promise-basics/03-animate-circle-promise/solution.view/index.html +++ b/1-js/11-async/02-promise-basics/03-animate-circle-promise/solution.view/index.html @@ -22,14 +22,14 @@ - + ``` -### Module-level scope +### Alcance a nivel de módulo -Each module has its own top-level scope. In other words, top-level variables and functions from a module are not seen in other scripts. +Cada módulo tiene su propio alcance de nivel superior. En otras palabras, las variables y funciones de nivel superior de un módulo no se ven en otros scripts. -In the example below, two scripts are imported, and `hello.js` tries to use `user` variable declared in `user.js`. It fails, because it's a separate module (you'll see the error in the console): +En el siguiente ejemplo, se importan dos scripts y `hello.js` intenta usar la variable `user` declarada en `user.js`. Falla, porque es un módulo separado (puedes ver el error en la consola): [codetabs src="scopes" height="140" current="index.html"] -Modules should `export` what they want to be accessible from outside and `import` what they need. +Los módulos deben hacer `export` a lo que ellos quieren que esté accesible desde afuera, y hacer `import` de lo que necesiten. -- `user.js` should export the `user` variable. -- `hello.js` should import it from `user.js` module. +- `user.js` debe exportar la variable `user` . +- `hello.js` debe importarla desde el módulo `user.js`. -In other words, with modules we use import/export instead of relying on global variables. +En otra palabras, con módulos usamos import/export en lugar de depender de variables globales. -This is the correct variant: +Esta es la variante correcta: [codetabs src="scopes-working" height="140" current="hello.js"] -In the browser, if we talk about HTML pages, independent top-level scope also exists for each ` ``` ```smart -In the browser, we can make a variable window-level global by explicitly assigning it to a `window` property, e.g. `window.user = "John"`. +En el navegador, podemos hacer que una variable sea global a nivel window si explícitamente la asignamos a la propiedad `window`, por ejemplo `window.user = "John"`. -Then all scripts will see it, both with `type="module"` and without it. +Así todos los scripts la verán, con o sin `type="module"`. -That said, making such global variables is frowned upon. Please try to avoid them. +Dicho esto, hacer este tipo de variables globales está muy mal visto. Por favor evítalas. ``` -### A module code is evaluated only the first time when imported +### Un código de módulo se evalúa solo la primera vez cuando se importa -If the same module is imported into multiple other modules, its code is executed only once, upon the first import. Then its exports are given to all further importers. +Si el mismo módulo se importa en varios otros módulos, su código se ejecuta solo una vez: en el primer import. Luego, sus exportaciones se otorgan a todos los importadores que siguen. -The one-time evaluation has important consequences, that we should be aware of. +Eso tiene consecuencias importantes para las que debemos estar prevenidos. -Let's see a couple of examples. +Echemos un vistazo usando ejemplos: -First, if executing a module code brings side-effects, like showing a message, then importing it multiple times will trigger it only once -- the first time: +Primero, si ejecutar un código de módulo trae efectos secundarios, como mostrar un mensaje, importarlo varias veces lo activará solamente una vez, la primera: ```js // 📁 alert.js -alert("Module is evaluated!"); +alert("Módulo es evaluado!"); ``` ```js -// Import the same module from different files +// Importar el mismo módulo desde archivos distintos // 📁 1.js -import `./alert.js`; // Module is evaluated! +import `./alert.js`; // Módulo es evaluado! // 📁 2.js -import `./alert.js`; // (shows nothing) +import `./alert.js`; // (no muestra nada) ``` -The second import shows nothing, because the module has already been evaluated. +El segundo import no muestra nada, porque el módulo ya fue evaluado. -There's a rule: top-level module code should be used for initialization, creation of module-specific internal data structures. If we need to make something callable multiple times - we should export it as a function, like we did with `sayHi` above. +Existe una regla: el código de módulos del nivel superior debe ser usado para la inicialización y creación de estructuras de datos internas específicas del módulo. Si necesitamos algo que pueda ser llamado varias veces debemos exportarlo como una función, como hicimos con el `sayHi` de arriba. -Now, let's consider a deeper example. +Consideremos un ejemplo más avanzado. -Let's say, a module exports an object: +Digamos que un módulo exporta un objeto: ```js // 📁 admin.js @@ -161,9 +161,9 @@ export let admin = { }; ``` -If this module is imported from multiple files, the module is only evaluated the first time, `admin` object is created, and then passed to all further importers. +Si este módulo se importa desde varios archivos, el módulo solo se evalúa la primera vez, se crea el objeto `admin` y luego se pasa a todos los importadores adicionales. -All importers get exactly the one and only `admin` object: +Todos los importadores obtienen exactamente este mismo y único objeto `admin`: ```js // 📁 1.js @@ -175,25 +175,26 @@ import {admin} from './admin.js'; alert(admin.name); // Pete *!* -// Both 1.js and 2.js reference the same admin object -// Changes made in 1.js are visible in 2.js +// Ambos, 1.js y 2.js, hacen referencia al mismo objeto admin +// Los cambios realizados en 1.js son visibles en 2.js */!* ``` -As you can see, when `1.js` changes the `name` property in the imported `admin`, then `2.js` can see the new `admin.name`. +Como puedes ver, cuando `1.js` cambia la propiedad `name` en el `admin` importado, entonces `2.js` puede ver el nuevo `admin.name`. + +Esto es porque el módulo se ejecuta solo una vez. Los exports son generados y luego compartidos entre importadores, entonces si algo cambia en el objeto `admin`, otros importadores lo verán. -That's exactly because the module is executed only once. Exports are generated, and then they are shared between importers, so if something changes the `admin` object, other importers will see that. +**Tal comportamiento es en verdad muy conveniente, porque nos permite *configurar* módulos.** -**Such behavior is actually very convenient, because it allows us to *configure* modules.** +En otras palabras, un módulo puede brindar una funcionalidad genérica que necesite ser configurada. Por ejemplo, la autenticación necesita credenciales. Entonces se puede exportar un objeto de configuración esperando que el código externo se lo asigne. -In other words, a module can provide a generic functionality that needs a setup. E.g. authentication needs credentials. Then it can export a configuration object expecting the outer code to assign to it. +Aquí está el patrón clásico: +1. Un módulo exporta algún medio de configuración, por ejemplo un objeto configuración. +2. En el primer import lo inicializamos, escribimos en sus propiedades. Los scripts de la aplicación de nivel superior pueden hacerlo. +3. Importaciones posteriores usan el módulo. -Here's the classical pattern: -1. A module exports some means of configuration, e.g. a configuration object. -2. On the first import we initialize it, write to its properties. The top-level application script may do that. -3. Further imports use the module. +Por ejemplo, el módulo `admin.js` puede proporcionar cierta funcionalidad (ej. autenticación), pero espera que las credenciales entren al objeto `admin` desde afuera: -For instance, the `admin.js` module may provide certain functionality (e.g. authentication), but expect the credentials to come into the `config` object from outside: ```js // 📁 admin.js @@ -204,9 +205,9 @@ export function sayHi() { } ``` -Here, `admin.js` exports the `config` object (initially empty, but may have default properties too). +Aquí `admin.js` exporta el objeto `config` (inicialmente vacío, pero podemos tener propiedades por defecto también). -Then in `init.js`, the first script of our app, we import `config` from it and set `config.user`: +Entonces en `init.js`, el primer script de nuestra app, importamos `config` de él y establecemos `config.user`: ```js // 📁 init.js @@ -214,9 +215,9 @@ import {config} from './admin.js'; config.user = "Pete"; ``` -...Now the module `admin.js` is configured. +...Ahora el módulo `admin.js` está configurado. -Further importers can call it, and it correctly shows the current user: +Importadores posteriores pueden llamarlo, y él muestra correctamente el usuario actual: ```js // 📁 another.js @@ -225,27 +226,26 @@ import {sayHi} from './admin.js'; sayHi(); // Ready to serve, *!*Pete*/!*! ``` - ### import.meta -The object `import.meta` contains the information about the current module. +El objeto `import.meta` contiene la información sobre el módulo actual. -Its content depends on the environment. In the browser, it contains the URL of the script, or a current webpage URL if inside HTML: +Su contenido depende del entorno. En el navegador, contiene la URL del script, o la URL de la página web actual si está dentro de HTML: ```html run height=0 ``` -### In a module, "this" is undefined +### En un módulo, "this" es indefinido (undefined). -That's kind of a minor feature, but for completeness we should mention it. +Esa es una característica menor, pero para ser exhaustivos debemos mencionarla. -In a module, top-level `this` is undefined. +En un módulo, el nivel superior `this` no está definido. -Compare it to non-module scripts, where `this` is a global object: +Compárelo con scripts que no sean módulos, donde `this` es un objeto global: ```html run height=0 ``` -## Browser-specific features +## Funciones específicas del navegador -There are also several browser-specific differences of scripts with `type="module"` compared to regular ones. +También hay varias diferencias específicas de los scripts del navegador con `type =" module "` en comparación con los normales. -You may want to skip this section for now if you're reading for the first time, or if you don't use JavaScript in a browser. +Es posible que desee omitir esta sección por ahora si está leyendo por primera vez o si no usa JavaScript en un navegador. -### Module scripts are deferred +### Los módulos son diferidos -Module scripts are *always* deferred, same effect as `defer` attribute (described in the chapter [](info:script-async-defer)), for both external and inline scripts. +Los módulos están *siempre* diferidos, el mismo efecto que el atributo `defer` (descrito en el capítulo [](info:script-async-defer)), para ambos scripts, externos y en línea. -In other words: -- downloading external module scripts ` -Compare to regular script below: +Abajo compare con un script normal: ``` -Please note: the second script actually runs before the first! So we'll see `undefined` first, and then `object`. +Note que: ¡el segundo script se ejecuta antes que el primero! Entonces vemos primero `undefined`, y después `object`. -That's because modules are deferred, so we wait for the document to be processed. The regular script runs immediately, so we see its output first. +Esto se debe a que los módulos están diferidos, por lo que esperamos a que se procese el documento. El script normal se ejecuta inmediatamente, por lo que vemos su salida primero. -When using modules, we should be aware that the HTML page shows up as it loads, and JavaScript modules run after that, so the user may see the page before the JavaScript application is ready. Some functionality may not work yet. We should put "loading indicators", or otherwise ensure that the visitor won't be confused by that. +Al usar módulos, debemos tener en cuenta que la página HTML se muestra a medida que se carga, y los módulos JavaScript se ejecutan después de eso, por lo que el usuario puede ver la página antes de que la aplicación JavaScript esté lista. Es posible que algunas funciones aún no funcionen. Deberíamos poner "indicadores de carga", o asegurarnos de que el visitante no se confunda con eso. -### Async works on inline scripts +### Async funciona en scripts en línea -For non-module scripts, the `async` attribute only works on external scripts. Async scripts run immediately when ready, independently of other scripts or the HTML document. +Para los scripts que no son módulos, el atributo `async` solo funciona en scripts externos. Los scripts asíncronos se ejecutan inmediatamente cuando están listos, independientemente de otros scripts o del documento HTML. -For module scripts, it works on inline scripts as well. +Para los scripts de módulo, esto también funciona en scripts en línea. -For example, the inline script below has `async`, so it doesn't wait for anything. +Por ejemplo, el siguiente script en línea tiene `async`, por lo que no espera nada. -It performs the import (fetches `./analytics.js`) and runs when ready, even if the HTML document is not finished yet, or if other scripts are still pending. +Realiza la importación (extrae `./Analytics.js`) y se ejecuta cuando está listo, incluso si el documento HTML aún no está terminado o si aún hay otros scripts pendientes. -That's good for functionality that doesn't depend on anything, like counters, ads, document-level event listeners. +Eso es bueno para la funcionalidad que no depende de nada, como contadores, anuncios, detectores de eventos a nivel de documento. ```html - - + + ``` -### External scripts +### Scripts externos -External scripts that have `type="module"` are different in two aspects: +Los scripts externos que tienen `type="module"` son diferentes en dos aspectos: -1. External scripts with the same `src` run only once: +1. Los scripts externos con el mismo `src` sólo se ejecutan una vez: ```html - + ``` -2. External scripts that are fetched from another origin (e.g. another site) require [CORS](mdn:Web/HTTP/CORS) headers, as described in the chapter . In other words, if a module script is fetched from another origin, the remote server must supply a header `Access-Control-Allow-Origin` allowing the fetch. +2. Los scripts externos que se buscan desde otro origen (p.ej. otra sitio web) requieren encabezados [CORS](https://developer.mozilla.org/es/docs/Web/HTTP/Access_control_CORS), como se describe en el capítulo . En otras palabras, si un script de módulo es extraído desde otro origen, el servidor remoto debe proporcionar un encabezado `Access-Control-Allow-Origin` permitiendo la búsqueda. ```html - - - + + + ``` - That ensures better security by default. + Esto asegura mejor seguridad de forma predeterminada. + +### No se permiten módulos sueltos -### No "bare" modules allowed +En el navegador, `import` debe obtener una URL, sea relativa o absoluta. Los módulos sin ninguna ruta se denominan módulos sueltos. Dichos módulos no están permitidos en `import`. -In the browser, `import` must get either a relative or absolute URL. Modules without any path are called "bare" modules. Such modules are not allowed in `import`. +Por ejemplo, este `import` no es válido: -For instance, this `import` is invalid: ```js -import {sayHi} from 'sayHi'; // Error, "bare" module -// the module must have a path, e.g. './sayHi.js' or wherever the module is +import {sayHi} from 'sayHi'; // Error, módulo suelto +// el módulo debe tener una ruta, por ejemplo './sayHi.js' o dondequiera que el módulo esté ``` -Certain environments, like Node.js or bundle tools allow bare modules, without any path, as they have their own ways for finding modules and hooks to fine-tune them. But browsers do not support bare modules yet. +Ciertos entornos, como Node.js o herramientas de empaquetado permiten módulos simples sin ninguna ruta, ya que tienen sus propias formas de encontrar módulos y engancharlos. Pero los navegadores aún no admiten módulos sueltos. -### Compatibility, "nomodule" +### Compatibilidad, "nomodule" -Old browsers do not understand `type="module"`. Scripts of an unknown type are just ignored. For them, it's possible to provide a fallback using the `nomodule` attribute: +Los navegadores antiguos no entienden `type = "module"`. Los scripts de un tipo desconocido simplemente se ignoran. Para ellos es posible proporcionar una alternativa, utilizando el atributo `nomodule`: ```html run ``` -## Build tools +## Herramientas de Ensamblaje -In real-life, browser modules are rarely used in their "raw" form. Usually, we bundle them together with a special tool such as [Webpack](https://webpack.js.org/) and deploy to the production server. +En la vida real, los módulos de navegador rara vez se usan en su forma "pura". Por lo general, los agrupamos con una herramienta especial como [Webpack](https://webpack.js.org/) y los implementamos en el servidor de producción. -One of the benefits of using bundlers -- they give more control over how modules are resolved, allowing bare modules and much more, like CSS/HTML modules. +Uno de los beneficios de usar empaquetadores es que dan más control sobre cómo se resuelven los módulos, permitiendo módulos simples y mucho más, como los módulos CSS/HTML. -Build tools do the following: +Las herramientas de compilación hacen lo siguiente: -1. Take a "main" module, the one intended to be put in ` ``` -That said, native modules are also usable. So we won't be using Webpack here: you can configure it later. +Dicho esto, los módulos nativos también se pueden utilizar. Por lo tanto no estaremos utilizando Webpack aquí: tú lo podrás configurar más adelante. -## Summary +## Resumen -To summarize, the core concepts are: +Para resumir, los conceptos centrales son: -1. A module is a file. To make `import/export` work, browsers need ` diff --git a/1-js/13-modules/03-modules-dynamic-imports/say.view/say.js b/1-js/13-modules/03-modules-dynamic-imports/say.view/say.js index cff234b7c..24010b827 100644 --- a/1-js/13-modules/03-modules-dynamic-imports/say.view/say.js +++ b/1-js/13-modules/03-modules-dynamic-imports/say.view/say.js @@ -1,11 +1,11 @@ export function hi() { - alert(`Hello`); + alert(`Hola`); } export function bye() { - alert(`Bye`); + alert(`Adiós`); } export default function() { - alert("Module loaded (export default)!"); + alert("Módulo cargado (export default)!"); } diff --git a/1-js/13-modules/index.md b/1-js/13-modules/index.md index 78fb060e8..889630704 100644 --- a/1-js/13-modules/index.md +++ b/1-js/13-modules/index.md @@ -1,2 +1,2 @@ -# Modules +# Módulos diff --git a/1-js/99-js-misc/01-proxy/01-error-nonexisting/solution.md b/1-js/99-js-misc/01-proxy/01-error-nonexisting/solution.md index 9db69cb2f..f94c4942a 100644 --- a/1-js/99-js-misc/01-proxy/01-error-nonexisting/solution.md +++ b/1-js/99-js-misc/01-proxy/01-error-nonexisting/solution.md @@ -10,7 +10,7 @@ function wrap(target) { if (prop in target) { return Reflect.get(target, prop, receiver); } else { - throw new ReferenceError(`Property doesn't exist: "${prop}"`) + throw new ReferenceError(`La propiedad no existe: "${prop}"`) } } }); @@ -19,5 +19,5 @@ function wrap(target) { user = wrap(user); alert(user.name); // John -alert(user.age); // ReferenceError: Property doesn't exist: "age" +alert(user.age); // ReferenceError: La propiedad no existe: "age" ``` diff --git a/1-js/99-js-misc/01-proxy/01-error-nonexisting/task.md b/1-js/99-js-misc/01-proxy/01-error-nonexisting/task.md index 47985e1a7..febb1a2c6 100644 --- a/1-js/99-js-misc/01-proxy/01-error-nonexisting/task.md +++ b/1-js/99-js-misc/01-proxy/01-error-nonexisting/task.md @@ -1,14 +1,14 @@ -# Error on reading non-existent property +# Error al leer una propiedad no existente -Usually, an attempt to read a non-existent property returns `undefined`. +Usualmente, el intento de leer una propiedad que no existe devuelve `undefined`. -Create a proxy that throws an error for an attempt to read of a non-existent property instead. +Crea en su lugar un proxy que arroje un error por intentar leer una propiedad no existente. -That can help to detect programming mistakes early. +Esto puede ayudar a detectar equivocaciones en la programación en forma temprana. -Write a function `wrap(target)` that takes an object `target` and return a proxy that adds this functionality aspect. +Escribe una función `wrap(target)` que tome un objeto `target` y devuelva un proxy que agregue este aspecto de funcionalidad. -That's how it should work: +Así es como debe funcionar: ```js let user = { @@ -18,7 +18,7 @@ let user = { function wrap(target) { return new Proxy(target, { *!* - /* your code */ + /* tu código */ */!* }); } @@ -27,6 +27,6 @@ user = wrap(user); alert(user.name); // John *!* -alert(user.age); // ReferenceError: Property doesn't exist: "age" +alert(user.age); // ReferenceError: La propiedad no existe: "age" */!* ``` diff --git a/1-js/99-js-misc/01-proxy/02-array-negative/solution.md b/1-js/99-js-misc/01-proxy/02-array-negative/solution.md index 207205501..e5320594c 100644 --- a/1-js/99-js-misc/01-proxy/02-array-negative/solution.md +++ b/1-js/99-js-misc/01-proxy/02-array-negative/solution.md @@ -5,8 +5,8 @@ let array = [1, 2, 3]; array = new Proxy(array, { get(target, prop, receiver) { if (prop < 0) { - // even if we access it like arr[1] - // prop is a string, so need to convert it to number + // incluso aunque la accedamos como arr[1] + // prop es un string, así que necesitamos convertirla a number prop = +prop + target.length; } return Reflect.get(target, prop, receiver); diff --git a/1-js/99-js-misc/01-proxy/02-array-negative/task.md b/1-js/99-js-misc/01-proxy/02-array-negative/task.md index 9b0b13f58..2a0bac281 100644 --- a/1-js/99-js-misc/01-proxy/02-array-negative/task.md +++ b/1-js/99-js-misc/01-proxy/02-array-negative/task.md @@ -1,33 +1,33 @@ -# Accessing array[-1] +# Accediendo a array[-1] -In some programming languages, we can access array elements using negative indexes, counted from the end. +En algunos lenguajes de programación podemos acceder a los arrays usando índices negativos, contando desde el final. -Like this: +Como esto: ```js let array = [1, 2, 3]; -array[-1]; // 3, the last element -array[-2]; // 2, one step from the end -array[-3]; // 1, two steps from the end +array[-1]; // 3, el último elemento +array[-2]; // 2, el penúltimo elemento, uno antes del final +array[-3]; // 1, el antepenúltimo elemento, dos antes el final ``` -In other words, `array[-N]` is the same as `array[array.length - N]`. +En otras palabras, `array[-N]` es lo mismo que `array[array.length - N]`. -Create a proxy to implement that behavior. +Crea un proxy para implementar tal comportamiento. -That's how it should work: +Así es como debe funcionar: ```js let array = [1, 2, 3]; array = new Proxy(array, { - /* your code */ + /* tu código */ }); alert( array[-1] ); // 3 alert( array[-2] ); // 2 -// Other array functionality should be kept "as is" +// el resto de la funcionalidad debe mantenerse igual. ``` diff --git a/1-js/99-js-misc/01-proxy/03-observable/solution.md b/1-js/99-js-misc/01-proxy/03-observable/solution.md index c0797a856..037b2a8fc 100644 --- a/1-js/99-js-misc/01-proxy/03-observable/solution.md +++ b/1-js/99-js-misc/01-proxy/03-observable/solution.md @@ -1,26 +1,26 @@ -The solution consists of two parts: +La solución consiste de dos partes: -1. Whenever `.observe(handler)` is called, we need to remember the handler somewhere, to be able to call it later. We can store handlers right in the object, using our symbol as the property key. -2. We need a proxy with `set` trap to call handlers in case of any change. +1. Cuando `.observe(handler)` es llamado, necesitamos recordar el manejador 'handler' en algún lugar para poder llamarlo después. Podemos almacenar los manejadores directamente en el objeto, usando nuestro symbol como clave de la propiedad. +2. Necesitamos un proxy con la trampa `set` que llame a los manejadores en caso de cualquier cambio. ```js run let handlers = Symbol('handlers'); function makeObservable(target) { - // 1. Initialize handlers store + // 1. Inicializa el almacén de manejadores target[handlers] = []; - // Store the handler function in array for future calls + // Almacena la función manejadora en el array para llamadas futuras target.observe = function(handler) { this[handlers].push(handler); }; - // 2. Create a proxy to handle changes + // 2. Crea un proxy para manejar cambios return new Proxy(target, { set(target, property, value, receiver) { - let success = Reflect.set(...arguments); // forward the operation to object - if (success) { // if there were no error while setting the property - // call all handlers + let success = Reflect.set(...arguments); // reenvía la operación al objeto + if (success) { // si no hay errores al establecer la propiedad + // llama a todos los manejadores target[handlers].forEach(handler => handler(property, value)); } return success; diff --git a/1-js/99-js-misc/01-proxy/03-observable/task.md b/1-js/99-js-misc/01-proxy/03-observable/task.md index 754d9f3bd..21f5d7229 100644 --- a/1-js/99-js-misc/01-proxy/03-observable/task.md +++ b/1-js/99-js-misc/01-proxy/03-observable/task.md @@ -1,13 +1,13 @@ # Observable -Create a function `makeObservable(target)` that "makes the object observable" by returning a proxy. +Crea una función `makeObservable(target)` que "haga el objeto observable" devolviendo un proxy. -Here's how it should work: +Así es como debe funcionar: ```js run function makeObservable(target) { - /* your code */ + /* tu código */ } let user = {}; @@ -17,11 +17,11 @@ user.observe((key, value) => { alert(`SET ${key}=${value}`); }); -user.name = "John"; // alerts: SET name=John +user.name = "John"; // alerta: SET name=John ``` -In other words, an object returned by `makeObservable` is just like the original one, but also has the method `observe(handler)` that sets `handler` function to be called on any property change. +En otras palabras: un objeto devuelto por `makeObservable` es como el original, pero que también tiene el método `observe(handler)` que establece una función `handler`, la que será llamada en cualquier cambio de propiedad. -Whenever a property changes, `handler(key, value)` is called with the name and value of the property. +Cada vez que una propiedad cambie, `handler(key, value)` es llamada con el nombre y el valor de la propiedad. -P.S. In this task, please only take care about writing to a property. Other operations can be implemented in a similar way. +P.D. En esta tarea, solo toma en cuenta la escritura de una propiedad. Otras operaciones pueden ser implementadas de manera similar. diff --git a/1-js/99-js-misc/01-proxy/article.md b/1-js/99-js-misc/01-proxy/article.md index 1f84912e5..1c0c9ba56 100644 --- a/1-js/99-js-misc/01-proxy/article.md +++ b/1-js/99-js-misc/01-proxy/article.md @@ -1,109 +1,109 @@ -# Proxy and Reflect +# Proxy y Reflect -A `Proxy` object wraps another object and intercepts operations, like reading/writing properties and others, optionally handling them on its own, or transparently allowing the object to handle them. +Un objeto `Proxy` envuelve (es un "wrapper": envoltura, contenedor) a otro objeto e intercepta sus operaciones (como leer y escribir propiedades, entre otras). El proxy puede manejar estas operaciones él mismo o, en forma transparente permitirle manejarlas al objeto envuelto. -Proxies are used in many libraries and some browser frameworks. We'll see many practical applications in this article. +Los proxys son usados en muchas librerías y en algunos frameworks de navegador. En este artículo veremos muchas aplicaciones prácticas. ## Proxy -The syntax: +La sintaxis: ```js let proxy = new Proxy(target, handler) ``` -- `target` -- is an object to wrap, can be anything, including functions. -- `handler` -- proxy configuration: an object with "traps", methods that intercept operations. - e.g. `get` trap for reading a property of `target`, `set` trap for writing a property into `target`, and so on. +- `target` -- es el objeto a envolver, puede ser cualquier cosa, incluso funciones. +- `handler` -- configuración de proxy: un objeto que "atrapa", métodos que interceptan operaciones. Ejemplos, la trampa `get` para leer una propiedad de `target`, la trampa `set` para escribir una propiedad en `target`, entre otras. -For operations on `proxy`, if there's a corresponding trap in `handler`, then it runs, and the proxy has a chance to handle it, otherwise the operation is performed on `target`. +Cuando hay una operación sobre `proxy`, este verifica si hay una trampa correspondiente en `handler`. Si la trampa existe se ejecuta y el proxy tiene la oportunidad de manejarla, de otro modo la operación es ejecutada por `target`. -As a starting example, let's create a proxy without any traps: +Como ejemplo para comenzar, creemos un proxy sin ninguna trampa: ```js run let target = {}; -let proxy = new Proxy(target, {}); // empty handler +let proxy = new Proxy(target, {}); // manejador vacío -proxy.test = 5; // writing to proxy (1) -alert(target.test); // 5, the property appeared in target! +proxy.test = 5; // escribiendo en el proxy (1) +alert(target.test); // 5, ¡la propiedad apareció en target! -alert(proxy.test); // 5, we can read it from proxy too (2) +alert(proxy.test); // 5, también podemos leerla en el proxy (2) -for(let key in proxy) alert(key); // test, iteration works (3) +for(let key in proxy) alert(key); // test, la iteración funciona (3) ``` -As there are no traps, all operations on `proxy` are forwarded to `target`. +Como no hay trampas, todas las operaciones sobre `proxy` son redirigidas a `target`. -1. A writing operation `proxy.test=` sets the value on `target`. -2. A reading operation `proxy.test` returns the value from `target`. -3. Iteration over `proxy` returns values from `target`. +1. Una operación de escritura `proxy.test=` establece el valor en `target`. +2. Una operación de lectura `proxy.test` devuelve el valor desde `target`. +3. La iteración sobre `proxy` devuelve valores de `target`. -As we can see, without any traps, `proxy` is a transparent wrapper around `target`. +Como podemos ver, sin ninguna trampa, `proxy` es un envoltorio transparente alrededor de `target`. ![](proxy.svg) -`Proxy` is a special "exotic object". It doesn't have own properties. With an empty `handler` it transparently forwards operations to `target`. +`Proxy` es un "objeto exótico" especial. No tiene propiedades propias. Con un manejador transparente redirige todas las operaciones hacia `target`. -To activate more capabilities, let's add traps. +Para activar más habilidades, agreguemos trampas. -What can we intercept with them? +¿Qué podemos interceptar con ellas? -For most operations on objects, there's a so-called "internal method" in the JavaScript specification that describes how it works at the lowest level. For instance `[[Get]]`, the internal method to read a property, `[[Set]]`, the internal method to write a property, and so on. These methods are only used in the specification, we can't call them directly by name. +Para la mayoría de las operaciones en objetos existe el denominado "método interno" en la especificación Javascript que describe cómo este trabaja en el más bajo nivel. Por ejemplo `[[Get]]`: es el método interno para leer una propiedad, `[[Set]]`: el método interno para escribirla, etcétera. Estos métodos solamente son usados en la especificación, no podemos llamarlos directamente por nombre. -Proxy traps intercept invocations of these methods. They are listed in the [Proxy specification](https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots) and in the table below. +Las trampas del proxy interceptan la invocación a estos métodos. Están listadas en la [Especificación del proxy](https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots) y en la tabla debajo. -For every internal method, there's a trap in this table: the name of the method that we can add to the `handler` parameter of `new Proxy` to intercept the operation: +Para cada método interno, existe una "trampa" en esta tabla: es el nombre del método que podemos agregar al parámetro `handler` de `new Proxy` para interceptar la operación: -| Internal Method | Handler Method | Triggers when... | +| Método interno | Método manejador | Cuándo se dispara | |-----------------|----------------|-------------| -| `[[Get]]` | `get` | reading a property | -| `[[Set]]` | `set` | writing to a property | -| `[[HasProperty]]` | `has` | `in` operator | -| `[[Delete]]` | `deleteProperty` | `delete` operator | -| `[[Call]]` | `apply` | function call | -| `[[Construct]]` | `construct` | `new` operator | -| `[[GetPrototypeOf]]` | `getPrototypeOf` | [Object.getPrototypeOf](mdn:/JavaScript/Reference/Global_Objects/Object/getPrototypeOf) | -| `[[SetPrototypeOf]]` | `setPrototypeOf` | [Object.setPrototypeOf](mdn:/JavaScript/Reference/Global_Objects/Object/setPrototypeOf) | -| `[[IsExtensible]]` | `isExtensible` | [Object.isExtensible](mdn:/JavaScript/Reference/Global_Objects/Object/isExtensible) | -| `[[PreventExtensions]]` | `preventExtensions` | [Object.preventExtensions](mdn:/JavaScript/Reference/Global_Objects/Object/preventExtensions) | -| `[[DefineOwnProperty]]` | `defineProperty` | [Object.defineProperty](mdn:/JavaScript/Reference/Global_Objects/Object/defineProperty), [Object.defineProperties](mdn:/JavaScript/Reference/Global_Objects/Object/defineProperties) | -| `[[GetOwnProperty]]` | `getOwnPropertyDescriptor` | [Object.getOwnPropertyDescriptor](mdn:/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor), `for..in`, `Object.keys/values/entries` | -| `[[OwnPropertyKeys]]` | `ownKeys` | [Object.getOwnPropertyNames](mdn:/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames), [Object.getOwnPropertySymbols](mdn:/JavaScript/Reference/Global_Objects/Object/getOwnPropertySymbols), `for..in`, `Object.keys/values/entries` | - -```warn header="Invariants" -JavaScript enforces some invariants -- conditions that must be fulfilled by internal methods and traps. - -Most of them are for return values: -- `[[Set]]` must return `true` if the value was written successfully, otherwise `false`. -- `[[Delete]]` must return `true` if the value was deleted successfully, otherwise `false`. -- ...and so on, we'll see more in examples below. - -There are some other invariants, like: -- `[[GetPrototypeOf]]`, applied to the proxy object must return the same value as `[[GetPrototypeOf]]` applied to the proxy object's target object. In other words, reading prototype of a proxy must always return the prototype of the target object. - -Traps can intercept these operations, but they must follow these rules. - -Invariants ensure correct and consistent behavior of language features. The full invariants list is in [the specification](https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots). You probably won't violate them if you're not doing something weird. +| `[[Get]]` | `get` | leyendo una propiedad | +| `[[Set]]` | `set` | escribiendo una propiedad | +| `[[HasProperty]]` | `has` | operador `in` | +| `[[Delete]]` | `deleteProperty` | operador `delete`| +| `[[Call]]` | `apply` | llamado a función | +| `[[Construct]]` | `construct` | operador `new` | +| `[[GetPrototypeOf]]` | `getPrototypeOf` | [Object.getPrototypeOf](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/getPrototypeOf) | +| `[[SetPrototypeOf]]` | `setPrototypeOf` | [Object.setPrototypeOf](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/setPrototypeOf) | +| `[[IsExtensible]]` | `isExtensible` | [Object.isExtensible](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/isExtensible) | +| `[[PreventExtensions]]` | `preventExtensions` | [Object.preventExtensions](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/preventExtensions) | +| `[[DefineOwnProperty]]` | `defineProperty` | [Object.defineProperty](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/defineProperty), [Object.defineProperties](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/defineProperties) | +| `[[GetOwnProperty]]` | `getOwnPropertyDescriptor` | [Object.getOwnPropertyDescriptor](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/getOwnPropertyDescriptor), `for..in`, `Object.keys/values/entries` | +| `[[OwnPropertyKeys]]` | `ownKeys` | [Object.getOwnPropertyNames](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/getOwnPropertyNames), [Object.getOwnPropertySymbols](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/getOwnPropertySymbols), `for..in`, `Object.keys/values/entries` | + +```warn header="Invariantes" +JavaScript impone algunas invariantes: condiciones que deben ser satisfechas por métodos internos y trampas. + +La mayor parte de ellos son para devolver valores: +- `[[Set]]` debe devolver `true` si el valor fue escrito correctamente, de otro modo `false`. +- `[[Delete]]` debe devolver `true` si el valor fue borrado correctamente, de otro modo `false`. +- ...y otros, veremos más ejemplos abajo. + +Existen algunas otras invariantes, como: +- `[[GetPrototypeOf]]`, aplicado al proxy, debe devolver el mismo valor que `[[GetPrototypeOf]]` aplicado al "target" del proxy. En otras palabras, leer el prototipo de un proxy debe devolver siempre el prototipo de su objeto target. + +Las trampas pueden interceptar estas operaciones, pero deben seguir estas reglas. + +Las invariantes aseguran un comportamiento correcto y consistente de características de lenguaje. La lista completa de invariantes está en [la especificación](https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots). Probablemente no las infringirás si no estás haciendo algo retorcido. ``` -Let's see how that works in practical examples. +Veamos cómo funciona en ejemplos prácticos. -## Default value with "get" trap +## Valores "por defecto" con la trampa "get" -The most common traps are for reading/writing properties. +Las trampas más comunes son para leer y escribir propiedades. -To intercept reading, the `handler` should have a method `get(target, property, receiver)`. +Para interceptar una lectura, el `handler` debe tener un método `get(target, property, receiver)`. -It triggers when a property is read, with following arguments: +Se dispara cuando una propiedad es leída, con los siguientes argumentos: -- `target` -- is the target object, the one passed as the first argument to `new Proxy`, -- `property` -- property name, -- `receiver` -- if the target property is a getter, then `receiver` is the object that's going to be used as `this` in its call. Usually that's the `proxy` object itself (or an object that inherits from it, if we inherit from proxy). Right now we don't need this argument, so it will be explained in more detail later. +- `target` -- "objetivo", es el objeto pasado como primer argumento a `new Proxy`, +- `property` -- nombre de la propiedad, +- `receiver` -- si la propiedad objetivo es un getter, el `receiver` es el objeto que va a ser usado como `this` en su llamado. Usualmente es el objeto `proxy` mismo (o un objeto que hereda de él, si heredamos desde proxy). No necesitamos este argumento ahora mismo, así que se verá en más detalle luego. -Let's use `get` to implement default values for an object. +Usemos `get` para implementar valores por defecto a un objeto. -We'll make a numeric array that returns `0` for nonexistent values. +Crearemos un arreglo numérico que devuelve `0` para valores no existentes. -Usually when one tries to get a non-existing array item, they get `undefined`, but we'll wrap a regular array into the proxy that traps reading and returns `0` if there's no such property: +Lo usual al tratar de obtener un ítem inexistente de un array es obtener `undefined`, pero envolveremos un array normal en un proxy que atrape lecturas y devuelva `0` si no existe tal propiedad: ```js run let numbers = [0, 1, 2]; @@ -113,22 +113,22 @@ numbers = new Proxy(numbers, { if (prop in target) { return target[prop]; } else { - return 0; // default value + return 0; // valor por defecto } } }); *!* alert( numbers[1] ); // 1 -alert( numbers[123] ); // 0 (no such item) +alert( numbers[123] ); // 0 (porque no existe tal ítem) */!* ``` -As we can see, it's quite easy to do with a `get` trap. +Como podemos ver, es muy fácil de hacer con una trampa `get`. -We can use `Proxy` to implement any logic for "default" values. +Podemos usar `Proxy` para implementar cualquier lógica para valores "por defecto". -Imagine we have a dictionary, with phrases and their translations: +Supongamos que tenemos un diccionario con frases y sus traducciones: ```js run let dictionary = { @@ -140,9 +140,9 @@ alert( dictionary['Hello'] ); // Hola alert( dictionary['Welcome'] ); // undefined ``` -Right now, if there's no phrase, reading from `dictionary` returns `undefined`. But in practice, leaving a phrase untranslated is usually better than `undefined`. So let's make it return an untranslated phrase in that case instead of `undefined`. +Por ahora, si no existe la frase, la lectura de `dictionary` devuelve `undefined`. Pero en la práctica dejar la frase sin traducir es mejor que `undefined`. Así que hagamos que devuelva la frase sin traducir en vez de `undefined`. -To achieve that, we'll wrap `dictionary` in a proxy that intercepts reading operations: +Para lograr esto envolvemos `dictionary` en un proxy que intercepta las operaciones de lectura: ```js run let dictionary = { @@ -152,58 +152,58 @@ let dictionary = { dictionary = new Proxy(dictionary, { *!* - get(target, phrase) { // intercept reading a property from dictionary + get(target, phrase) { // intercepta la lectura de una propiedad en dictionary */!* - if (phrase in target) { // if we have it in the dictionary - return target[phrase]; // return the translation + if (phrase in target) { // si existe en el diccionario + return target[phrase]; // devuelve la traducción } else { - // otherwise, return the non-translated phrase + // caso contrario devuelve la frase sin traducir return phrase; } } }); -// Look up arbitrary phrases in the dictionary! -// At worst, they're not translated. +// ¡Busque frases en el diccionario! +// En el peor caso, no serán traducidas. alert( dictionary['Hello'] ); // Hola *!* -alert( dictionary['Welcome to Proxy']); // Welcome to Proxy (no translation) +alert( dictionary['Welcome to Proxy']); // Welcome to Proxy (sin traducir) */!* ``` ````smart -Please note how the proxy overwrites the variable: +Nota cómo el proxy sobrescribe la variable: ```js dictionary = new Proxy(dictionary, ...); ``` -The proxy should totally replace the target object everywhere. No one should ever reference the target object after it got proxied. Otherwise it's easy to mess up. +El proxy debe reemplazar completamente al objeto "target" que envolvió: nadie debe jamás hacer referencia al objeto target saltando tal envoltura. De otro modo sería fácil desbaratarlo. ```` -## Validation with "set" trap +## Validación con la trampa "set" -Let's say we want an array exclusively for numbers. If a value of another type is added, there should be an error. +Digamos que queremos un array exclusivamente para números. Si se agrega un valor de otro tipo, debería dar un error. -The `set` trap triggers when a property is written. +La trampa `set` se dispara cuando una propiedad es escrita. `set(target, property, value, receiver)`: -- `target` -- is the target object, the one passed as the first argument to `new Proxy`, -- `property` -- property name, -- `value` -- property value, -- `receiver` -- similar to `get` trap, matters only for setter properties. +- `target` -- objetivo, el objeto pasado como primer argumento a `new Proxy`, +- `property` -- nombre de la propiedad, +- `value` -- valor de la propiedad, +- `receiver` -- similar para la trampa `get`, de importancia solamente en propiedades setter. -The `set` trap should return `true` if setting is successful, and `false` otherwise (triggers `TypeError`). +La trampa `set` debe devolver `true` si la escritura fue exitosa, y `false` en caso contrario (dispara `TypeError`). -Let's use it to validate new values: +Usémoslo para validar valores nuevos: ```js run let numbers = []; numbers = new Proxy(numbers, { // (*) *!* - set(target, prop, val) { // to intercept property writing + set(target, prop, val) { // para interceptar la escritura de propiedad */!* if (typeof val == 'number') { target[prop] = val; @@ -214,44 +214,44 @@ numbers = new Proxy(numbers, { // (*) } }); -numbers.push(1); // added successfully -numbers.push(2); // added successfully +numbers.push(1); // añadido correctamente +numbers.push(2); // añadido correctamente alert("Length is: " + numbers.length); // 2 *!* -numbers.push("test"); // TypeError ('set' on proxy returned false) +numbers.push("test"); // TypeError ('set' en el proxy devolvió false) */!* -alert("This line is never reached (error in the line above)"); +alert("Esta linea nunca es alcanzada (error en la línea de arriba)"); ``` -Please note: the built-in functionality of arrays is still working! Values are added by `push`. The `length` property auto-increases when values are added. Our proxy doesn't break anything. +Ten en cuenta: ¡la funcionalidad integrada de los arrays aún funciona! Los valores son añadidos por `push`. La propiedad `length` se autoincrementa cuando son agregados valores. Nuestro proxy no rompe nada. -We don't have to override value-adding array methods like `push` and `unshift`, and so on, to add checks in there, because internally they use the `[[Set]]` operation that's intercepted by the proxy. +No necesitamos sobrescribir métodos de valor añadido como `push`, `unshift` y demás para agregar los chequeos allí, porque internamente ellos usan la operación `[[Set]]` que es interceptada por el proxy. -So the code is clean and concise. +Entonces el código es limpio y conciso. -```warn header="Don't forget to return `true`" -As said above, there are invariants to be held. +```warn header="No olvides devolver `true`" +Como dijimos antes, hay invariantes que se deben mantener. -For `set`, it must return `true` for a successful write. +Para `set`, debe devolver `true` si la escritura fue correcta. -If we forget to do it or return any falsy value, the operation triggers `TypeError`. +Si olvidamos hacerlo o si devolvemos false, la operación dispara `TypeError`. ``` -## Iteration with "ownKeys" and "getOwnPropertyDescriptor" +## Iteración con "ownKeys" y "getOwnPropertyDescriptor" -`Object.keys`, `for..in` loop and most other methods that iterate over object properties use `[[OwnPropertyKeys]]` internal method (intercepted by `ownKeys` trap) to get a list of properties. +`Object.keys`, el bucle `for..in`, y la mayoría de los demás métodos que iteran sobre las propiedades de objeto usan el método interno `[[OwnPropertyKeys]]` (interceptado por la trampa `ownKeys`) para obtener una lista de propiedades . -Such methods differ in details: -- `Object.getOwnPropertyNames(obj)` returns non-symbol keys. -- `Object.getOwnPropertySymbols(obj)` returns symbol keys. -- `Object.keys/values()` returns non-symbol keys/values with `enumerable` flag (property flags were explained in the article ). -- `for..in` loops over non-symbol keys with `enumerable` flag, and also prototype keys. +Tales métodos difieren en detalles: +- `Object.getOwnPropertyNames(obj)` devuelve claves no symbol. +- `Object.getOwnPropertySymbols(obj)` devuelve claves symbol. +- `Object.keys/values()` devuelve claves/valores no symbol con indicador `enumerable` (los indicadores de propiedad fueron explicados en el artículo ). +- `for..in` itera sobre claves no symbol con el indicador `enumerable`, y también claves prototípicas. -...But all of them start with that list. +...Pero todos ellos comienzan con aquella lista. -In the example below we use `ownKeys` trap to make `for..in` loop over `user`, and also `Object.keys` and `Object.values`, to skip properties starting with an underscore `_`: +En el ejemplo abajo usamos la trampa `ownKeys` para hacer el bucle `for..in` sobre `user`. También usamos `Object.keys` y `Object.values` para pasar por alto las propiedades que comienzan con un guion bajo `_`: ```js run let user = { @@ -268,17 +268,17 @@ user = new Proxy(user, { } }); -// "ownKeys" filters out _password +// el filtro en "ownKeys" descarta _password for(let key in user) alert(key); // name, then: age -// same effect on these methods: +// el mismo efecto con estos métodos: alert( Object.keys(user) ); // name,age alert( Object.values(user) ); // John,30 ``` -So far, it works. +Hasta ahora, funciona. -Although, if we return a key that doesn't exist in the object, `Object.keys` won't list it: +Aunque si devolvemos una clave que no existe en el objeto, `Object.keys` no la listará: ```js run let user = { }; @@ -291,28 +291,28 @@ user = new Proxy(user, { } }); -alert( Object.keys(user) ); // +alert( Object.keys(user) ); // ``` -Why? The reason is simple: `Object.keys` returns only properties with the `enumerable` flag. To check for it, it calls the internal method `[[GetOwnProperty]]` for every property to get [its descriptor](info:property-descriptors). And here, as there's no property, its descriptor is empty, no `enumerable` flag, so it's skipped. +¿Por qué? La razón es simple: `Object.keys` devuelve solamente propiedades con el indicador `enumerable`. Para verificarlo, llama el método interno `[[GetOwnProperty]]` en cada propiedad para obtener [su descriptor](info:property-descriptors). Y aquí, como no hay propiedad, su descriptor está vacío, no existe el indicador `enumerable`, entonces lo salta. -For `Object.keys` to return a property, we need it to either exist in the object, with the `enumerable` flag, or we can intercept calls to `[[GetOwnProperty]]` (the trap `getOwnPropertyDescriptor` does it), and return a descriptor with `enumerable: true`. +Para que `Object.keys` devuelva una propiedad, necesitamos que, o bien exista en el objeto, con el indicador `enumerable`, o interceptamos llamadas a `[[GetOwnProperty]]` (la trampa `getOwnPropertyDescriptor` lo hace), y devolver un descriptor con `enumerable: true`. -Here's an example of that: +Aquí un ejemplo de ello: ```js run let user = { }; user = new Proxy(user, { - ownKeys(target) { // called once to get a list of properties + ownKeys(target) { // llamado una vez para obtener la lista de propiedades return ['a', 'b', 'c']; }, - getOwnPropertyDescriptor(target, prop) { // called for every property + getOwnPropertyDescriptor(target, prop) { // llamada para cada propiedad return { enumerable: true, configurable: true - /* ...other flags, probable "value:..." */ + /* ...otros indicadores, probablemente "value:..." */ }; } @@ -321,32 +321,32 @@ user = new Proxy(user, { alert( Object.keys(user) ); // a, b, c ``` -Let's note once again: we only need to intercept `[[GetOwnProperty]]` if the property is absent in the object. +Tomemos nota de nuevo: solamente necesitamos interceptar `[[GetOwnProperty]]` si la propiedad está ausente en el objeto. -## Protected properties with "deleteProperty" and other traps +## Propiedades protegidas con "deleteProperty" y otras trampas -There's a widespread convention that properties and methods prefixed by an underscore `_` are internal. They shouldn't be accessed from outside the object. +Hay una convención extendida: las propiedades y los métodos que comienzan con guion bajo `_` son de uso interno. Ellos no deberían ser accedidos desde fuera del objeto. -Technically that's possible though: +Aunque es técnicamente posible: ```js run let user = { name: "John", - _password: "secret" + _password: "secreto" }; -alert(user._password); // secret +alert(user._password); // secreto ``` -Let's use proxies to prevent any access to properties starting with `_`. +Usemos proxy para prevenir cualquier acceso a propiedades que comienzan con `_`. -We'll need the traps: -- `get` to throw an error when reading such property, -- `set` to throw an error when writing, -- `deleteProperty` to throw an error when deleting, -- `ownKeys` to exclude properties starting with `_` from `for..in` and methods like `Object.keys`. +Necesitaremos las trampas: +- `get` para arrojar un error al leer tal propiedad, +- `set` para arrojar un error al escribirla, +- `deleteProperty` para arrojar un error al eliminar, +- `ownKeys` para excluir propiedades que comienzan con `_` de `for..in` y métodos como `Object.keys`. -Here's the code: +Aquí el código: ```js run let user = { @@ -359,58 +359,58 @@ user = new Proxy(user, { get(target, prop) { */!* if (prop.startsWith('_')) { - throw new Error("Access denied"); + throw new Error("Acceso denegado"); } let value = target[prop]; return (typeof value === 'function') ? value.bind(target) : value; // (*) }, *!* - set(target, prop, val) { // to intercept property writing + set(target, prop, val) { // para interceptar la escritura de la propiedad */!* if (prop.startsWith('_')) { - throw new Error("Access denied"); + throw new Error("Acceso denegado"); } else { target[prop] = val; return true; } }, *!* - deleteProperty(target, prop) { // to intercept property deletion + deleteProperty(target, prop) { // para interceptar la eliminación de la propiedad */!* if (prop.startsWith('_')) { - throw new Error("Access denied"); + throw new Error("Acceso denegado"); } else { delete target[prop]; return true; } }, *!* - ownKeys(target) { // to intercept property list + ownKeys(target) { // para interceptar su listado */!* return Object.keys(target).filter(key => !key.startsWith('_')); } }); -// "get" doesn't allow to read _password +// "get" no permite leer _password try { - alert(user._password); // Error: Access denied + alert(user._password); // Error: Acceso denegado } catch(e) { alert(e.message); } -// "set" doesn't allow to write _password +// "set" no permite escribir _password try { - user._password = "test"; // Error: Access denied + user._password = "test"; // Error: Acceso denegado } catch(e) { alert(e.message); } -// "deleteProperty" doesn't allow to delete _password +// "deleteProperty" no permite eliminar _password try { - delete user._password; // Error: Access denied + delete user._password; // Error: Acceso denegado } catch(e) { alert(e.message); } -// "ownKeys" filters out _password +// "ownKeys" filtra descartando _password for(let key in user) alert(key); // name ``` -Please note the important detail in the `get` trap, in the line `(*)`: +Nota el importante detalle en la trampa `get`, en la línea `(*)`: ```js get(target, prop) { @@ -422,42 +422,42 @@ get(target, prop) { } ``` -Why do we need a function to call `value.bind(target)`? +¿Por qué necesitamos una función para llamar `value.bind(target)`? -The reason is that object methods, such as `user.checkPassword()`, must be able to access `_password`: +La razón es que los métodos de objeto, como `user.checkPassword()`, deben ser capaces de acceder a `_password`: ```js user = { // ... checkPassword(value) { - // object method must be able to read _password + // método de objeto debe poder leer _password return value === this._password; } } ``` -A call to `user.checkPassword()` gets proxied `user` as `this` (the object before dot becomes `this`), so when it tries to access `this._password`, the `get` trap activates (it triggers on any property read) and throws an error. +Un llamado a `user.checkPassword()` hace que el objeto target `user` sea `this` (el objeto antes del punto se vuelve `this`), entonces cuando trata de acceder a `this._password`, la trampa `get` se activa (se dispara en cualquier lectura de propiedad) y arroja un error. -So we bind the context of object methods to the original object, `target`, in the line `(*)`. Then their future calls will use `target` as `this`, without any traps. +Entonces vinculamos (bind) el contexto de los métodos al objeto original, `target`, en la línea `(*)`. Así futuros llamados usarán `target` como `this`, sin trampas. -That solution usually works, but isn't ideal, as a method may pass the unproxied object somewhere else, and then we'll get messed up: where's the original object, and where's the proxied one? +Esta solución usualmente funciona, pero no es ideal, porque un método podría pasar el objeto original hacia algún otro lado y lo habremos arruinado: ¿dónde está el objeto original, y dónde el apoderado? -Besides, an object may be proxied multiple times (multiple proxies may add different "tweaks" to the object), and if we pass an unwrapped object to a method, there may be unexpected consequences. +Además, un objeto puede ser envuelto por proxys muchas veces (proxys múltiples pueden agregar diferentes ajustes al objeto), y si pasamos un objeto no envuelto por proxy a un método, puede haber consecuencias inesperadas. -So, such a proxy shouldn't be used everywhere. +Por lo tanto, tal proxy no debería usarse en todos lados. -```smart header="Private properties of a class" -Modern JavaScript engines natively support private properties in classes, prefixed with `#`. They are described in the article . No proxies required. +```smart header="Propiedades privadas de una clase" +Los motores de JavaScript moderno soportan en las clases las propiedades privadas, aquellas con el prefijo `#`. Estas son descritas en el artículo . No requieren proxys. -Such properties have their own issues though. In particular, they are not inherited. +Pero tales propiedades tienen sus propios problemas. En particular, ellas no se heredan. ``` -## "In range" with "has" trap +## "In range" con la trampa "has" -Let's see more examples. +Veamos más ejemplos. -We have a range object: +Tenemos un objeto range: ```js let range = { @@ -466,16 +466,16 @@ let range = { }; ``` -We'd like to use the `in` operator to check that a number is in `range`. +Queremos usar el operador `in` para verificar que un número está en el rango, `range`. -The `has` trap intercepts `in` calls. +La trampa `has` intercepta la llamada `in`. `has(target, property)` -- `target` -- is the target object, passed as the first argument to `new Proxy`, -- `property` -- property name +- `target` -- objetivo, el objeto pasado como primer argumento a `new Proxy`, +- `property` -- nombre de propiedad -Here's the demo: +Aquí el demo: ```js run let range = { @@ -497,27 +497,27 @@ alert(50 in range); // false */!* ``` -Nice syntactic sugar, isn't it? And very simple to implement. +Bonita azúcar sintáctica, ¿no es cierto? Y muy simple de implementar. -## Wrapping functions: "apply" [#proxy-apply] +## Envolviendo funciones: "apply" [#proxy-apply] -We can wrap a proxy around a function as well. +Podemos envolver un proxy a una función también. -The `apply(target, thisArg, args)` trap handles calling a proxy as function: +La trampa `apply(target, thisArg, args)` maneja llamados a proxy como función: -- `target` is the target object (function is an object in JavaScript), -- `thisArg` is the value of `this`. -- `args` is a list of arguments. +- `target` es el objeto/objetivo (en JavaScript, la función es un objeto), +- `thisArg` es el valor de `this`. +- `args` es una lista de argumentos. -For example, let's recall `delay(f, ms)` decorator, that we did in the article . +Por ejemplo, recordemos el decorador `delay(f, ms)` que hicimos en el artículo . -In that article we did it without proxies. A call to `delay(f, ms)` returned a function that forwards all calls to `f` after `ms` milliseconds. +En ese artículo lo hicimos sin proxy. Un llamado a `delay(f, ms)` devolvía una función que redirigía todos los llamados a `f` después de `ms` milisegundos. -Here's the previous, function-based implementation: +Aquí la version previa, implementación basada en función: ```js run function delay(f, ms) { - // return a wrapper that passes the call to f after the timeout + // devuelve un envoltorio que pasa el llamado a f después del timeout return function() { // (*) setTimeout(() => f.apply(this, arguments), ms); }; @@ -527,15 +527,15 @@ function sayHi(user) { alert(`Hello, ${user}!`); } -// after this wrapping, calls to sayHi will be delayed for 3 seconds +// después de esta envoltura, los llamados a sayHi serán demorados por 3 segundos sayHi = delay(sayHi, 3000); -sayHi("John"); // Hello, John! (after 3 seconds) +sayHi("John"); // Hello, John! (después de 3 segundos) ``` -As we've seen already, that mostly works. The wrapper function `(*)` performs the call after the timeout. +Como ya hemos visto, esto mayormente funciona. La función envoltorio `(*)` ejecuta el llamado después del lapso. -But a wrapper function does not forward property read/write operations or anything else. After the wrapping, the access is lost to properties of the original functions, such as `name`, `length` and others: +Pero una simple función envoltura (wrapper) no redirige operaciones de lectura y escritura ni ninguna otra cosa. Una vez envuelta, el acceso a las propiedades de la función original (`name`, `length`) se pierde: ```js run function delay(f, ms) { @@ -549,19 +549,19 @@ function sayHi(user) { } *!* -alert(sayHi.length); // 1 (function length is the arguments count in its declaration) +alert(sayHi.length); // 1 (length, longitud, en una función es la cantidad de argumentos en su declaración) */!* sayHi = delay(sayHi, 3000); *!* -alert(sayHi.length); // 0 (in the wrapper declaration, there are zero arguments) +alert(sayHi.length); // 0 (en la declaración de envoltorio hay cero argumentos) */!* ``` -`Proxy` is much more powerful, as it forwards everything to the target object. +El `Proxy` es mucho más poderoso, porque redirige todo lo que no maneja al objeto envuelto "target". -Let's use `Proxy` instead of a wrapping function: +Usemos `Proxy` en lugar de una función envoltura: ```js run function delay(f, ms) { @@ -579,29 +579,29 @@ function sayHi(user) { sayHi = delay(sayHi, 3000); *!* -alert(sayHi.length); // 1 (*) proxy forwards "get length" operation to the target +alert(sayHi.length); // 1 (*) el proxy redirige la operación "get length" al objeto target */!* -sayHi("John"); // Hello, John! (after 3 seconds) +sayHi("John"); // Hello, John! (después de 3 segundos) ``` -The result is the same, but now not only calls, but all operations on the proxy are forwarded to the original function. So `sayHi.length` is returned correctly after the wrapping in the line `(*)`. +El resultado es el mismo, pero ahora no solo las llamadas sino todas las operaciones son redirigidas a la función original. Así `sayHi.length` se devuelve correctamente luego de la envoltura en la línea `(*)`. -We've got a "richer" wrapper. +Obtuvimos una envoltura "enriquecida". -Other traps exist: the full list is in the beginning of this article. Their usage pattern is similar to the above. +Existen otras trampas. La lista completa está en el principio de este artículo. Su patrón de uso es similar al de arriba. ## Reflect -`Reflect` is a built-in object that simplifies creation of `Proxy`. +`Reflect` es un objeto nativo que simplifica la creación de `Proxy`. -It was said previously that internal methods, such as `[[Get]]`, `[[Set]]` and others are specification-only, they can't be called directly. +Se dijo previamente que los métodos internos como `[[Get]]`, `[[Set]]` son únicamente para la especificación, que no pueden ser llamados directamente. -The `Reflect` object makes that somewhat possible. Its methods are minimal wrappers around the internal methods. +El objeto `Reflect` hace de alguna manera esto posible. Sus métodos son envoltorios mínimos alrededor del método interno. -Here are examples of operations and `Reflect` calls that do the same: +Aquí hay ejemplos de operaciones y llamados a `Reflect` que hacen lo mismo: -| Operation | `Reflect` call | Internal method | +| Operación | Llamada `Reflect` | Método interno | |-----------------|----------------|-------------| | `obj[prop]` | `Reflect.get(obj, prop)` | `[[Get]]` | | `obj[prop] = value` | `Reflect.set(obj, prop, value)` | `[[Set]]` | @@ -609,7 +609,7 @@ Here are examples of operations and `Reflect` calls that do the same: | `new F(value)` | `Reflect.construct(F, value)` | `[[Construct]]` | | ... | ... | ... | -For example: +Por ejemplo: ```js run let user = {}; @@ -619,13 +619,13 @@ Reflect.set(user, 'name', 'John'); alert(user.name); // John ``` -In particular, `Reflect` allows us to call operators (`new`, `delete`...) as functions (`Reflect.construct`, `Reflect.deleteProperty`, ...). That's an interesting capability, but here another thing is important. +En particular, `Reflect` nos permite llamar a los operadores (`new`, `delete`, ...) como funciones (`Reflect.construct`, `Reflect.deleteProperty`, ...). Esta es una capacidad interesante, pero hay otra cosa importante. -**For every internal method, trappable by `Proxy`, there's a corresponding method in `Reflect`, with the same name and arguments as the `Proxy` trap.** +**Para cada método interno atrapable por `Proxy`, hay un método correspondiente en `Reflect` con el mismo nombre y argumentos que la trampa `Proxy`.** -So we can use `Reflect` to forward an operation to the original object. +Entonces podemos usar `Reflect` para redirigir una operación al objeto original. -In this example, both traps `get` and `set` transparently (as if they didn't exist) forward reading/writing operations to the object, showing a message: +En este ejemplo, ambas trampas `get` y `set` transparentemente (como si no existieran) reenvían las operaciones de lectura y escritura al objeto, mostrando un mensaje: ```js run let user = { @@ -647,26 +647,26 @@ user = new Proxy(user, { } }); -let name = user.name; // shows "GET name" -user.name = "Pete"; // shows "SET name=Pete" +let name = user.name; // muestra "GET name" +user.name = "Pete"; // muestra "SET name=Pete" ``` -Here: +Aquí: -- `Reflect.get` reads an object property. -- `Reflect.set` writes an object property and returns `true` if successful, `false` otherwise. +- `Reflect.get` lee una propiedad de objeto. +- `Reflect.set` escribe una propiedad de objeto y devuelve `true` si fue exitosa, `false` si no lo fue. -That is, everything's simple: if a trap wants to forward the call to the object, it's enough to call `Reflect.` with the same arguments. +Eso es todo, así de simple: si una trampa quiere dirigir el llamado al objeto, es suficiente con el llamado a `Reflect.` con los mismos argumentos. -In most cases we can do the same without `Reflect`, for instance, reading a property `Reflect.get(target, prop, receiver)` can be replaced by `target[prop]`. There are important nuances though. +En la mayoría de los casos podemos hacerlo sin `Reflect`, por ejemplo, leer una propiedad `Reflect.get(target, prop, receiver)` puede ser reemplazado por `target[prop]`. Aunque hay importantes distinciones. -### Proxying a getter +### Proxy en un getter -Let's see an example that demonstrates why `Reflect.get` is better. And we'll also see why `get/set` have the third argument `receiver`, that we didn't use before. +Veamos un ejemplo que demuestra por qué `Reflect.get` es mejor. Y veremos también por qué `get/set` tiene el tercer argumento `receiver` que no usamos antes. -We have an object `user` with `_name` property and a getter for it. +Tenemos un objeto `user` con la propiedad `_name` y un getter para ella. -Here's a proxy around it: +Aquí hay un proxy alrededor de él: ```js run let user = { @@ -687,11 +687,11 @@ let userProxy = new Proxy(user, { alert(userProxy.name); // Guest ``` -The `get` trap is "transparent" here, it returns the original property, and doesn't do anything else. That's enough for our example. +La trampa `get` es "transparente" aquí, devuelve la propiedad original, y no hace nada más. Esto es suficiente para nuestro ejemplo. -Everything seems to be all right. But let's make the example a little bit more complex. +Todo se ve bien. Pero hagamos el ejemplo un poco más complejo. -After inheriting another object `admin` from `user`, we can observe the incorrect behavior: +Después de heredar otro objeto `admin` desde `user`, podemos observar el comportamiento incorrecto: ```js run let user = { @@ -713,32 +713,32 @@ let admin = { _name: "Admin" }; -// Expected: Admin -alert(admin.name); // outputs: Guest (?!?) +// Esperado: Admin +alert(admin.name); // salida: Guest (?!?) */!* ``` -Reading `admin.name` should return `"Admin"`, not `"Guest"`! +¡Leer `admin.name` debería devolver `"Admin"`, no `"Guest"`! -What's the matter? Maybe we did something wrong with the inheritance? +¿Qué es lo que pasa? ¿Acaso hicimos algo mal con la herencia? -But if we remove the proxy, then everything will work as expected. +Pero si quitamos el proxy, todo funciona como se espera. -The problem is actually in the proxy, in the line `(*)`. +En realidad el problema está en el proxy, en la línea `(*)`. -1. When we read `admin.name`, as `admin` object doesn't have such own property, the search goes to its prototype. -2. The prototype is `userProxy`. -3. When reading `name` property from the proxy, its `get` trap triggers and returns it from the original object as `target[prop]` in the line `(*)`. +1. Cuando leemos `admin.name`, como el objeto`admin` no tiene su propia propiedad, la búsqueda va a su prototipo. +2. El prototipo es `userProxy`. +3. Cuando se lee la propiedad `name` del proxy, se dispara su trampa `get` y devuelve desde el objeto original como `target[prop]` en la línea `(*)`. - A call to `target[prop]`, when `prop` is a getter, runs its code in the context `this=target`. So the result is `this._name` from the original object `target`, that is: from `user`. + Un llamado a `target[prop]`, cuando `prop` es un getter, ejecuta su código en el contexto `this=target`. Entonces el resultado es `this._name` desde el objeto original `target`, que es: desde `user`. -To fix such situations, we need `receiver`, the third argument of `get` trap. It keeps the correct `this` to be passed to a getter. In our case that's `admin`. +Para arreglar estas situaciones, necesitamos `receiver`, el tercer argumento de la trampa `get`. Este mantiene el `this` correcto para pasarlo al getter. Que en nuestro caso es `admin`. -How to pass the context for a getter? For a regular function we could use `call/apply`, but that's a getter, it's not "called", just accessed. +¿Cómo pasar el contexto para un getter? Para una función regular podemos usar `call/apply`, pero es un getter, no es "llamado", solamente accedido. -`Reflect.get` can do that. Everything will work right if we use it. +`Reflect.get` hace eso. Todo funcionará bien si lo usamos. -Here's the corrected variant: +Aquí la variante corregida: ```js run let user = { @@ -749,7 +749,7 @@ let user = { }; let userProxy = new Proxy(user, { - get(target, prop, receiver) { // receiver = admin + get(target, prop, receiver) { // receiver = admin *!* return Reflect.get(target, prop, receiver); // (*) */!* @@ -767,9 +767,9 @@ alert(admin.name); // Admin */!* ``` -Now `receiver` that keeps a reference to the correct `this` (that is `admin`), is passed to the getter using `Reflect.get` in the line `(*)`. +Ahora `receiver`, que mantiene una referencia al `this` correcto (que es `admin`), es pasado al getter usando `Reflect.get` en la línea `(*)`. -We can rewrite the trap even shorter: +Podemos reescribir la trampa aún más corta: ```js get(target, prop, receiver) { @@ -778,25 +778,25 @@ get(target, prop, receiver) { ``` -`Reflect` calls are named exactly the same way as traps and accept the same arguments. They were specifically designed this way. +Los llamados de `Reflect` fueron nombrados exactamente igual a las trampas y aceptan los mismos argumentos. Fueron específicamente diseñados así. -So, `return Reflect...` provides a safe no-brainer to forward the operation and make sure we don't forget anything related to that. +Entonces, `return Reflect...` brinda una forma segura y "no cerebral" de redirigir la operación y asegurarse de que no olvidamos nada relacionado a ello. -## Proxy limitations +## Limitaciones del proxy -Proxies provide a unique way to alter or tweak the behavior of the existing objects at the lowest level. Still, it's not perfect. There are limitations. +Proxy brinda una manera única de alterar o ajustar el comportamiento de objetos existentes al más bajo nivel. Pero no es perfecto. Hay limitaciones. -### Built-in objects: Internal slots +### Objetos nativos: slots internos -Many built-in objects, for example `Map`, `Set`, `Date`, `Promise` and others make use of so-called "internal slots". +Muchos objetos nativos, por ejemplo `Map`, `Set`, `Date`, `Promise`, etc, hacen uso de los llamados "slots internos". -These are like properties, but reserved for internal, specification-only purposes. For instance, `Map` stores items in the internal slot `[[MapData]]`. Built-in methods access them directly, not via `[[Get]]/[[Set]]` internal methods. So `Proxy` can't intercept that. +Los slots (hueco, celda) son como propiedades; pero están reservados para uso interno, con propósito de especificación únicamente. Por ejemplo, `Map` almacena items en el slot interno `[[MapData]]`. Los métodos nativos los acceden directamente, sin usar los métodos internos `[[Get]]/[[Set]]`. Entonces `Proxy` no puede interceptar eso. -Why care? They're internal anyway! +¿Qué importa? ¡De cualquier manera son internos! -Well, here's the issue. After a built-in object like that gets proxied, the proxy doesn't have these internal slots, so built-in methods will fail. +Bueno, hay un problema. Cuando se envuelve un objeto nativo el proxy no tiene acceso a estos slots internos, entonces los métodos nativos fallan. -For example: +Por ejemplo: ```js run let map = new Map(); @@ -808,9 +808,9 @@ proxy.set('test', 1); // Error */!* ``` -Internally, a `Map` stores all data in its `[[MapData]]` internal slot. The proxy doesn't have such a slot. The [built-in method `Map.prototype.set`](https://tc39.es/ecma262/#sec-map.prototype.set) method tries to access the internal property `this.[[MapData]]`, but because `this=proxy`, can't find it in `proxy` and just fails. +Internamente, un `Map` almacena todos los datos en su slot interno `[[MapData]]`. El proxy no tiene tal slot. El [método nativo `Map.prototype.set`](https://tc39.es/ecma262/#sec-map.prototype.set) trata de acceder a la propiedad interna `this.[[MapData]]`, pero como `this=proxy`, no puede encontrarlo en `proxy` y simplemente falla. -Fortunately, there's a way to fix it: +Afortunadamente, hay una forma de arreglarlo: ```js run let map = new Map(); @@ -825,24 +825,24 @@ let proxy = new Proxy(map, { }); proxy.set('test', 1); -alert(proxy.get('test')); // 1 (works!) +alert(proxy.get('test')); // 1 (¡Funciona!) ``` -Now it works fine, because `get` trap binds function properties, such as `map.set`, to the target object (`map`) itself. +Ahora funciona bien porque la trampa `get` vincula las propiedades de la función, tales como `map.set`, al objeto target mismo (`map`). -Unlike the previous example, the value of `this` inside `proxy.set(...)` will be not `proxy`, but the original `map`. So when the internal implementation of `set` tries to access `this.[[MapData]]` internal slot, it succeeds. +A diferencia del ejemplo previo, el valor de `this` dentro de `proxy.set(...)` no será `proxy` sino el `map` original. Entonces, cuando la implementación interna de `set` trata de acceder al slot interno `this.[[MapData]]`, lo logra. -```smart header="`Array` has no internal slots" -A notable exception: built-in `Array` doesn't use internal slots. That's for historical reasons, as it appeared so long ago. +```smart header="`Array` no tiene slots internos" +Una excepción notable: El objeto nativo `Array` no tiene slots internos. Esto es por razones históricas, ya que apareció hace tanto tiempo. -So there's no such problem when proxying an array. +Así que no hay problema en usar proxy con un array. ``` -### Private fields +### Campos privados -A similar thing happens with private class fields. +Algo similar ocurre con los "campos privados" usados en las clases. -For example, `getName()` method accesses the private `#name` property and breaks after proxying: +Por ejemplo, el método `getName()` accede a la propiedad privada `#name` y falla cuando lo proxificamos: ```js run class User { @@ -862,11 +862,11 @@ alert(user.getName()); // Error */!* ``` -The reason is that private fields are implemented using internal slots. JavaScript does not use `[[Get]]/[[Set]]` when accessing them. +La razón es que los campos privados son implementados usando slots internos. JavaScript no usa `[[Get]]/[[Set]]` cuando accede a ellos. -In the call `getName()` the value of `this` is the proxied `user`, and it doesn't have the slot with private fields. +En la llamada a `getName()`, el valor de `this` es el proxy `user`que no tiene el slot con campos privados. -Once again, the solution with binding the method makes it work: +De nuevo, la solución de vincular el método hace que funcione: ```js run class User { @@ -889,13 +889,13 @@ user = new Proxy(user, { alert(user.getName()); // Guest ``` -That said, the solution has drawbacks, as explained previously: it exposes the original object to the method, potentially allowing it to be passed further and breaking other proxied functionality. +Dicho esto, la solución tiene su contra, explicada previamente: expone el objeto original al método, potencialmente permite ser pasado más allá y dañar otra funcionalidad del proxy. ### Proxy != target -The proxy and the original object are different objects. That's natural, right? +El proxy y el objeto original son objetos diferentes. Es natural, ¿cierto? -So if we use the original object as a key, and then proxy it, then the proxy can't be found: +Así que si usamos el objeto original como clave y luego lo hacemos proxy, entonces el proxy no puede ser hallado: ```js run let allUsers = new Set(); @@ -918,58 +918,58 @@ alert(allUsers.has(user)); // false */!* ``` -As we can see, after proxying we can't find `user` in the set `allUsers`, because the proxy is a different object. +Como podemos ver, después del proxy no podemos hallar `user` en el set `allUsers` porque el proxy es un objeto diferente. -```warn header="Proxies can't intercept a strict equality test `===`" -Proxies can intercept many operators, such as `new` (with `construct`), `in` (with `has`), `delete` (with `deleteProperty`) and so on. +```warn header="El proxy no puede interceptar un test de igualdad estricta `===`" +Los proxys pueden interceptar muchos operadores; tales como `new` (con `construct`), `in` (con `has`), `delete` (con `deleteProperty`) y otros. -But there's no way to intercept a strict equality test for objects. An object is strictly equal to itself only, and no other value. +Pero no hay forma de interceptar un test de igualdad estricta entre objetos. Un objeto es estrictamente igual únicamente a sí mismo y a ningún otro valor. -So all operations and built-in classes that compare objects for equality will differentiate between the object and the proxy. No transparent replacement here. +Por lo tanto todas las operaciones y clases nativas que hacen una comparación estricta de objetos diferenciarán entre el objeto original y su proxy. No hay reemplazo transparente aquí.. ``` -## Revocable proxies +## Proxy revocable -A *revocable* proxy is a proxy that can be disabled. +Un proxy *revocable* es uno que puede ser deshabilitado. -Let's say we have a resource, and would like to close access to it any moment. +Digamos que tenemos un recurso al que quisiéramos poder cerrar en cualquier momento. -What we can do is to wrap it into a revocable proxy, without any traps. Such a proxy will forward operations to object, and we can disable it at any moment. +Podemos envolverlo en un proxy revocable sin trampas. Tal proxy dirigirá todas las operaciones al objeto, y podemos deshabilitarlo en cualquier momento. -The syntax is: +La sintaxis es: ```js let {proxy, revoke} = Proxy.revocable(target, handler) ``` -The call returns an object with the `proxy` and `revoke` function to disable it. +La llamada devuelve un objeto con el `proxy` y la función `revoke` para deshabilitarlo. -Here's an example: +Aquí hay un ejemplo: ```js run let object = { - data: "Valuable data" + data: "datos valiosos" }; let {proxy, revoke} = Proxy.revocable(object, {}); -// pass the proxy somewhere instead of object... -alert(proxy.data); // Valuable data +// pasamos el proxy en lugar del objeto... +alert(proxy.data); // datos valiosos -// later in our code +// luego en nuestro código revoke(); -// the proxy isn't working any more (revoked) +// el proxy no funciona más (revocado) alert(proxy.data); // Error ``` -A call to `revoke()` removes all internal references to the target object from the proxy, so they are no longer connected. +La llamada a `revoke()` quita al proxy todas las referencias internas hacia el objeto target, ya no estarán conectados. -Initially, `revoke` is separate from `proxy`, so that we can pass `proxy` around while leaving `revoke` in the current scope. +En principio `revoke` está separado de `proxy`, así que podemos pasar `proxy` alrededor mientras mantenemos `revoke` en la vista actual. -We can also bind `revoke` method to proxy by setting `proxy.revoke = revoke`. +También podemos vincular el método `revoke` al proxy asignándolo como propiedad: `proxy.revoke = revoke`. -Another option is to create a `WeakMap` that has `proxy` as the key and the corresponding `revoke` as the value, that allows to easily find `revoke` for a proxy: +Otra opción es crear un `WeakMap` que tenga a `proxy` como clave y su correspondiente `revoke` como valor, esto permite fácilmente encontrar el `revoke` para un proxy: ```js run *!* @@ -984,51 +984,51 @@ let {proxy, revoke} = Proxy.revocable(object, {}); revokes.set(proxy, revoke); -// ..somewhere else in our code.. +// ...en algún otro lado de nuestro código... revoke = revokes.get(proxy); revoke(); -alert(proxy.data); // Error (revoked) +alert(proxy.data); // Error (revocado) ``` -We use `WeakMap` instead of `Map` here because it won't block garbage collection. If a proxy object becomes "unreachable" (e.g. no variable references it any more), `WeakMap` allows it to be wiped from memory together with its `revoke` that we won't need any more. +Usamos `WeakMap` en lugar de `Map` aquí porque no bloqueará la recolección de basura. Si el objeto proxy se vuelve inalcanzable (es decir, ya ninguna variable hace referencia a él), `WeakMap` permite eliminarlo junto con su `revoke` que no necesitaremos más. ## References - Specification: [Proxy](https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots). -- MDN: [Proxy](mdn:/JavaScript/Reference/Global_Objects/Proxy). +- MDN: [Proxy](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Proxy). -## Summary +## Resumen -`Proxy` is a wrapper around an object, that forwards operations on it to the object, optionally trapping some of them. +`Proxy` es un envoltorio (wrapper) alrededor de un objeto que redirige las operaciones en el hacia el objeto, opcionalmente atrapando algunas de ellas para manejarlas por su cuenta. -It can wrap any kind of object, including classes and functions. +Puede envolver cualquier tipo de objeto, incluyendo clases y funciones. -The syntax is: +La sintaxis es: ```js let proxy = new Proxy(target, { - /* traps */ + /* trampas */ }); ``` -...Then we should use `proxy` everywhere instead of `target`. A proxy doesn't have its own properties or methods. It traps an operation if the trap is provided, otherwise forwards it to `target` object. +...Entonces deberíamos usar `proxy` en todos lados en lugar de `target`. Un proxy no tiene sus propias propiedades o métodos. Atrapa una operación si la trampa correspondiente le es provista, de otro modo la reenvía al objeto `target`. -We can trap: -- Reading (`get`), writing (`set`), deleting (`deleteProperty`) a property (even a non-existing one). -- Calling a function (`apply` trap). -- The `new` operator (`construct` trap). -- Many other operations (the full list is at the beginning of the article and in the [docs](mdn:/JavaScript/Reference/Global_Objects/Proxy)). +Podemos atrapar: +- Lectura (`get`), escritura (`set`), eliminación de propiedad (`deleteProperty`) (incluso si no existe). +- Llamadas a función (trampa `apply`). +- El operador `new` (trampa `construct`). +- Muchas otras operaciones (la lista completa al principio del artículo y en [docs](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Proxy)). -That allows us to create "virtual" properties and methods, implement default values, observable objects, function decorators and so much more. +Esto nos permite crear propiedades y métodos "virtuales", implementar valores por defecto, objetos observables, decoradores de función y mucho más. -We can also wrap an object multiple times in different proxies, decorating it with various aspects of functionality. +También podemos atrapar un objeto múltiples veces en proxys diferentes, decorándolos con varios aspectos de funcionalidad. -The [Reflect](mdn:/JavaScript/Reference/Global_Objects/Reflect) API is designed to complement [Proxy](mdn:/JavaScript/Reference/Global_Objects/Proxy). For any `Proxy` trap, there's a `Reflect` call with same arguments. We should use those to forward calls to target objects. +La API de [Reflect](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Reflect) está diseñada para complementar [Proxy](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Proxy). Para cada trampa de `Proxy` hay una llamada `Reflect` con los mismos argumentos. Deberíamos usarlas para redirigir llamadas hacia los objetos target. -Proxies have some limitations: +Los proxys tienen algunas limitaciones: -- Built-in objects have "internal slots", access to those can't be proxied. See the workaround above. -- The same holds true for private class fields, as they are internally implemented using slots. So proxied method calls must have the target object as `this` to access them. -- Object equality tests `===` can't be intercepted. -- Performance: benchmarks depend on an engine, but generally accessing a property using a simplest proxy takes a few times longer. In practice that only matters for some "bottleneck" objects though. +- Los objetos nativos tienen "slots internos" a los que el proxy no tiene acceso. Ver la forma de sortear el problema más arriba. +- Lo mismo cuenta para los campos privados en las clases porque están implementados internamente usando slots. Entonces las llamadas a métodos atrapados deben tener en `this` al objeto target para poder accederlos. +- El test de igualdad de objeto `===` no puede ser interceptado. +- Performance: los tests de velocidad dependen del motor, pero generalmente acceder a una propiedad usando el proxy más simple el tiempo se multiplica unas veces. Aunque en la práctica esto solo es importante para los objetos que son los "cuello de botella" de una aplicación. diff --git a/1-js/99-js-misc/01-proxy/proxy-inherit-admin.svg b/1-js/99-js-misc/01-proxy/proxy-inherit-admin.svg index 3fba64606..deb3d24bc 100644 --- a/1-js/99-js-misc/01-proxy/proxy-inherit-admin.svg +++ b/1-js/99-js-misc/01-proxy/proxy-inherit-admin.svg @@ -1 +1 @@ -_name: "Guest" name: getter_name: "Admin"user (proxied)original useradmin[[Prototype]] \ No newline at end of file +_name: "Guest" name: getter_name: "Admin"user (con proxy)user originaladmin[[Prototype]] \ No newline at end of file diff --git a/1-js/99-js-misc/01-proxy/proxy-inherit.svg b/1-js/99-js-misc/01-proxy/proxy-inherit.svg index 6c34c0f4e..7ba12d8b2 100644 --- a/1-js/99-js-misc/01-proxy/proxy-inherit.svg +++ b/1-js/99-js-misc/01-proxy/proxy-inherit.svg @@ -1 +1 @@ -_name: "Guest" name: getteruser (proxied)original user \ No newline at end of file +_name: "Guest" name: getteruser (con proxy)user original \ No newline at end of file diff --git a/1-js/99-js-misc/02-eval/1-eval-calculator/solution.md b/1-js/99-js-misc/02-eval/1-eval-calculator/solution.md index 24d40c9b8..16e05075d 100644 --- a/1-js/99-js-misc/02-eval/1-eval-calculator/solution.md +++ b/1-js/99-js-misc/02-eval/1-eval-calculator/solution.md @@ -1,11 +1,11 @@ -Let's use `eval` to calculate the maths expression: +Usemos `eval` para calcular la expresión matemática: ```js demo run -let expr = prompt("Type an arithmetic expression?", '2*3+2'); +let expr = prompt("Escribe una expresión matemática:", '2*3+2'); alert( eval(expr) ); ``` -The user can input any text or code though. +Aunque el usuario puede ingresar cualquier texto o código. -To make things safe, and limit it to arithmetics only, we can check the `expr` using a [regular expression](info:regular-expressions), so that it only may contain digits and operators. +Para hacer las cosas seguras, y limitarlo a aritmética solamente, podemos verificar `expr` usando una [expresión regular](info:regular-expressions) que solo pueda contener dígitos y operadores. diff --git a/1-js/99-js-misc/02-eval/1-eval-calculator/task.md b/1-js/99-js-misc/02-eval/1-eval-calculator/task.md index ece43ec9e..d26c7e712 100644 --- a/1-js/99-js-misc/02-eval/1-eval-calculator/task.md +++ b/1-js/99-js-misc/02-eval/1-eval-calculator/task.md @@ -2,10 +2,10 @@ importance: 4 --- -# Eval-calculator +# Calculadora-eval -Create a calculator that prompts for an arithmetic expression and returns its result. +Crea una calculadora que pida una expresión aritmética y devuelva su resultado. -There's no need to check the expression for correctness in this task. Just evaluate and return the result. +No es necesario verificar que la expresión sea correcta en esta tarea. Simplemente que evalue y devuelva el resultado. [demo] diff --git a/1-js/99-js-misc/02-eval/article.md b/1-js/99-js-misc/02-eval/article.md index 1fdafeeec..4add4b9b5 100644 --- a/1-js/99-js-misc/02-eval/article.md +++ b/1-js/99-js-misc/02-eval/article.md @@ -1,25 +1,25 @@ -# Eval: run a code string +# Eval: ejecutando una cadena de código -The built-in `eval` function allows to execute a string of code. +La función incorporada `eval` permite ejecutar una cadena de código. -The syntax is: +La sintaxis es: ```js let result = eval(code); ``` -For example: +Por ejemplo: ```js run let code = 'alert("Hello")'; eval(code); // Hello ``` -A string of code may be long, contain line breaks, function declarations, variables and so on. +Una cadena de código puede ser larga, contener cortes de línea, declaración de funciones, variables y así. -The result of `eval` is the result of the last statement. +El resultado de `eval` es el resultado de la última sentencia. -For example: +Por ejemplo: ```js run let value = eval('1+1'); alert(value); // 2 @@ -30,7 +30,7 @@ let value = eval('let i = 0; ++i'); alert(value); // 1 ``` -The eval'ed code is executed in the current lexical environment, so it can see outer variables: +El código evaluado es ejecutado en el entorno léxico presente, entonces podemos ver sus variables externas: ```js run no-beautify let a = 1; @@ -46,56 +46,56 @@ function f() { f(); ``` -It can change outer variables as well: +También puede modificar variables externas: ```js untrusted refresh run let x = 5; eval("x = 10"); -alert(x); // 10, value modified +alert(x); // 10, valor modificado ``` -In strict mode, `eval` has its own lexical environment. So functions and variables, declared inside eval, are not visible outside: +En modo estricto, `eval` tiene su propio entorno léxico. Entonces funciones y variables declaradas dentro de eval no son visibles fuera: ```js untrusted refresh run -// reminder: 'use strict' is enabled in runnable examples by default +// recordatorio: 'use strict' está habilitado en los ejemplos ejecutables por defecto eval("let x = 5; function f() {}"); -alert(typeof x); // undefined (no such variable) -// function f is also not visible +alert(typeof x); // undefined (no existe tal variable) +// la función f tampoco es visible ``` -Without `use strict`, `eval` doesn't have its own lexical environment, so we would see `x` and `f` outside. +Sin `use strict`, `eval` no tiene su propio entorno léxico, entonces podemos ver `x` y `f` afuera. -## Using "eval" +## Usando "eval" -In modern programming `eval` is used very sparingly. It's often said that "eval is evil". +En programación moderna `eval` es usado muy ocasionalmente. Se suele decir que "eval is evil" - juego de palabras en inglés que significa en español: "eval es malvado". -The reason is simple: long, long time ago JavaScript was a much weaker language, many things could only be done with `eval`. But that time passed a decade ago. +La razón es simple: largo, largo tiempo atrás JavaScript era un lenguaje mucho más débil, muchas cosas podían ser concretadas solamente con `eval`. Pero aquel tiempo pasó hace una década. -Right now, there's almost no reason to use `eval`. If someone is using it, there's a good chance they can replace it with a modern language construct or a [JavaScript Module](info:modules). +Ahora casi no hay razones para usar `eval`. Si alguien lo está usando, hay buena chance de que pueda ser reemplazado con una construcción moderna del lenguaje o un [Módulo JavaScript](info:modules). -Please note that its ability to access outer variables has side-effects. +Por favor ten en cuenta que su habilidad para acceder a variables externas tiene efectos colaterales. -Code minifiers (tools used before JS gets to production, to compress it) rename local variables into shorter ones (like `a`, `b` etc) to make the code smaller. That's usually safe, but not if `eval` is used, as local variables may be accessed from eval'ed code string. So minifiers don't do that renaming for all variables potentially visible from `eval`. That negatively affects code compression ratio. +Los Code minifiers (minimizadores de código, herramientas usadas antes de poner JS en producción para comprimirlo) renombran las variables locales acortándolas (como `a`, `b` etc) para achicar el código. Usualmente esto es seguro, pero no si `eval` es usado porque las variables locales pueden ser accedidas desde la cadena de código evaluada. Por ello los minimizadores no hacen tal renombrado en todas las variables potencialmente visibles por `eval`. Esto afecta negativamente en el índice de compresión. -Using outer local variables inside `eval` is also considered a bad programming practice, as it makes maintaining the code more difficult. +El uso de variables locales dentro de `eval` es también considerado una mala práctica de programación, porque hace el mantenimiento de código más difícil. -There are two ways how to be totally safe from such problems. +Hay dos maneras de estar asegurado frente a tales problemas. -**If eval'ed code doesn't use outer variables, please call `eval` as `window.eval(...)`:** +**Si el código evaluado no usa variables externas, por favor llama `eval` como `window.eval(...)`:** -This way the code is executed in the global scope: +De esta manera el código es ejecutado en el entorno global: ```js untrusted refresh run let x = 1; { let x = 5; - window.eval('alert(x)'); // 1 (global variable) + window.eval('alert(x)'); // 1 (variable global) } ``` -**If eval'ed code needs local variables, change `eval` to `new Function` and pass them as arguments:** +**Si el código evaluado necesita variables locales, cambia `eval` por `new Function` y pásalas como argumentos:** ```js run let f = new Function('a', 'alert(a)'); @@ -103,12 +103,12 @@ let f = new Function('a', 'alert(a)'); f(5); // 5 ``` -The `new Function` construct is explained in the chapter . It creates a function from a string, also in the global scope. So it can't see local variables. But it's so much clearer to pass them explicitly as arguments, like in the example above. +La construcción `new Function` es explicada en el capítulo . Esta crea una función desde una cadena, también en el entorno global, y así no puede ver las variables locales. Pero es mucho más claro pasarlas explícitamente como argumentos como en el ejemplo de arriba. -## Summary +## Resumen -A call to `eval(code)` runs the string of code and returns the result of the last statement. -- Rarely used in modern JavaScript, as there's usually no need. -- Can access outer local variables. That's considered bad practice. -- Instead, to `eval` the code in the global scope, use `window.eval(code)`. -- Or, if your code needs some data from the outer scope, use `new Function` and pass it as arguments. +Un llamado a `eval(code)` ejecuta la cadena de código y devuelve el resultado de la última sentencia. +- Es raramente usado en JavaScript moderno, y usualmente no es necesario. +- Puede acceder a variables locales externas. Esto es considerado una mala práctica. +- En su lugar, para evaluar el código en el entorno global, usa `window.eval(code)`. +- O, si tu código necesita algunos datos de el entorno externo, usa `new Function` y pásalos como argumentos. diff --git a/1-js/99-js-misc/03-currying-partials/article.md b/1-js/99-js-misc/03-currying-partials/article.md index d71ac23f8..de70dd820 100644 --- a/1-js/99-js-misc/03-currying-partials/article.md +++ b/1-js/99-js-misc/03-currying-partials/article.md @@ -3,21 +3,21 @@ libs: --- -# Currying +# Currificación -[Currying](https://en.wikipedia.org/wiki/Currying) is an advanced technique of working with functions. It's used not only in JavaScript, but in other languages as well. +La [Currificación](https://es.wikipedia.org/wiki/Currificación) es una técnica avanzada de trabajo con funciones. No solo se usa en JavaScript, sino también en otros lenguajes. -Currying is a transformation of functions that translates a function from callable as `f(a, b, c)` into callable as `f(a)(b)(c)`. +La currificación es una transformación de funciones que traduce una función invocable como `f(a, b, c)` a invocable como `f(a)(b)(c)`. -Currying doesn't call a function. It just transforms it. +La currificación no llama a una función. Simplemente la transforma. -Let's see an example first, to better understand what we're talking about, and then practical applications. +Veamos primero un ejemplo, para comprender mejor de qué estamos hablando, y luego sus aplicaciones prácticas. -We'll create a helper function `curry(f)` that performs currying for a two-argument `f`. In other words, `curry(f)` for two-argument `f(a, b)` translates it into a function that runs as `f(a)(b)`: +Crearemos una función auxiliar `curry(f)` que realice el curry para una `f` de dos argumentos. En otras palabras, `curry(f)` para dos argumentos `f(a, b)` lo traduce en una función que se ejecuta como `f(a)(b)`: ```js run *!* -function curry(f) { // curry(f) does the currying transform +function curry(f) { // curry (f) realiza la transformación curry return function(a) { return function(b) { return f(a, b); @@ -26,7 +26,7 @@ function curry(f) { // curry(f) does the currying transform } */!* -// usage +// uso function sum(a, b) { return a + b; } @@ -36,30 +36,30 @@ let curriedSum = curry(sum); alert( curriedSum(1)(2) ); // 3 ``` -As you can see, the implementation is straightforward: it's just two wrappers. +Como se puede ver, la implementación es sencilla: son solo dos contenedores. -- The result of `curry(func)` is a wrapper `function(a)`. -- When it is called like `curriedSum(1)`, the argument is saved in the Lexical Environment, and a new wrapper is returned `function(b)`. -- Then this wrapper is called with `2` as an argument, and it passes the call to the original `sum`. +- El resultado de `curry(func)` es un contenedor `function(a)`. +- Cuando se llama como `curriedSum(1)`, el argumento se guarda en el entorno léxico y se devuelve un nuevo contenedor `function(b)`. +- Luego se llama a este contenedor con `2` como argumento, y pasa la llamada a la función `sum` original. -More advanced implementations of currying, such as [_.curry](https://lodash.com/docs#curry) from lodash library, return a wrapper that allows a function to be called both normally and partially: +Las implementaciones más avanzadas de currificación, como [_.curry](https://lodash.com/docs#curry) de la librería lodash, devuelven un contenedor que permite llamar a una función de manera normal y parcial: ```js run function sum(a, b) { return a + b; } -let curriedSum = _.curry(sum); // using _.curry from lodash library +let curriedSum = _.curry(sum); // usando _.curry desde la librería lodash -alert( curriedSum(1, 2) ); // 3, still callable normally -alert( curriedSum(1)(2) ); // 3, called partially +alert( curriedSum(1, 2) ); // 3, todavía se puede llamar normalmente +alert( curriedSum(1)(2) ); // 3, llamada parcial ``` -## Currying? What for? +## ¿Curry? ¿Para qué? -To understand the benefits we need a worthy real-life example. +Para comprender los beneficios, necesitamos un ejemplo digno, de la vida real. -For instance, we have the logging function `log(date, importance, message)` that formats and outputs the information. In real projects such functions have many useful features like sending logs over the network, here we'll just use `alert`: +Por ejemplo, tenemos la función de registro `log(date, importance, message)` que formatea y genera la información. En proyectos reales, tales funciones tienen muchas características útiles, como enviar registros a través de la red, aquí solo usaremos `alert`: ```js function log(date, importance, message) { @@ -67,53 +67,53 @@ function log(date, importance, message) { } ``` -Let's curry it! +¡Pongámosle curry! ```js -log = _.curry(log); +log = _.curry (log); ``` -After that `log` works normally: +Después de eso, `log` funciona normalmente: ```js log(new Date(), "DEBUG", "some debug"); // log(a, b, c) ``` -...But also works in the curried form: +...Pero también funciona en forma de curry: ```js log(new Date())("DEBUG")("some debug"); // log(a)(b)(c) ``` -Now we can easily make a convenience function for current logs: +Ahora podemos hacer fácilmente una función conveniente para los registros actuales: ```js -// logNow will be the partial of log with fixed first argument +// logNow será el parcial del registro con el primer argumento fijo let logNow = log(new Date()); -// use it -logNow("INFO", "message"); // [HH:mm] INFO message +// uso +logNow("INFO", "message"); // [HH: mm] mensaje INFO ``` -Now `logNow` is `log` with fixed first argument, in other words "partially applied function" or "partial" for short. +Ahora `logNow` es `log` con un primer argumento fijo, en otras palabras, "función parcialmente aplicada" o "parcial" para abreviar. -We can go further and make a convenience function for current debug logs: +Podemos ir más allá y hacer una función conveniente para los registros de depuración actuales: ```js let debugNow = logNow("DEBUG"); -debugNow("message"); // [HH:mm] DEBUG message +debugNow("message"); // [HH:mm] mensaje DEBUG ``` -So: -1. We didn't lose anything after currying: `log` is still callable normally. -2. We can easily generate partial functions such as for today's logs. +Entonces: +1. No perdimos nada después del curry: `log` todavía se puede llamar normalmente. +2. Podemos generar fácilmente funciones parciales, como los registros de hoy. -## Advanced curry implementation +## Implementación avanzada de curry -In case you'd like to get in to the details, here's the "advanced" curry implementation for multi-argument functions that we could use above. +En caso de que quiera entrar en detalles, aquí está la implementación de curry "avanzado" para funciones de múltiples argumentos que podríamos usar arriba. -It's pretty short: +Es bastante corto: ```js function curry(func) { @@ -131,7 +131,7 @@ function curry(func) { } ``` -Usage examples: +Ejemplos de uso: ```js function sum(a, b, c) { @@ -140,17 +140,17 @@ function sum(a, b, c) { let curriedSum = curry(sum); -alert( curriedSum(1, 2, 3) ); // 6, still callable normally -alert( curriedSum(1)(2,3) ); // 6, currying of 1st arg -alert( curriedSum(1)(2)(3) ); // 6, full currying +alert( curriedSum(1, 2, 3) ); // 6, todavía se puede llamar con normalidad +alert( curriedSum(1)(2,3) ); // 6, curry en el primer argumento +alert( curriedSum(1)(2)(3) ); // 6, curry completo ``` -The new `curry` may look complicated, but it's actually easy to understand. +El nuevo `curry` puede parecer complicado, pero en realidad es fácil de entender. -The result of `curry(func)` call is the wrapper `curried` that looks like this: +El resultado de la llamada `curry(func)` es el contenedor `curried` que se ve así: ```js -// func is the function to transform +// func es la función a transformar function curried(...args) { if (args.length >= func.length) { // (1) return func.apply(this, args); @@ -162,27 +162,27 @@ function curried(...args) { }; ``` -When we run it, there are two `if` execution branches: +Cuando lo ejecutamos, hay dos ramas de ejecución `if`: -1. If passed `args` count is the same or more than the original function has in its definition (`func.length`) , then just pass the call to it using `func.apply`. -2. Otherwise, get a partial: we don't call `func` just yet. Instead, another wrapper is returned, that will re-apply `curried` providing previous arguments together with the new ones. +1. Si el recuento de `args` pasado es el mismo que tiene la función original en su definición (`func.length`), entonces simplemente páselo usando `func.apply`. +2. De lo contrario, obtenga un parcial: No llamamos a `func` aún. En cambio, se devuelve otro contenedor que volverá a aplicar `curried` proporcionando los argumentos anteriores junto con los nuevos. -Then, if we call it, again, we'll get either a new partial (if not enough arguments) or, finally, the result. +Luego, en una nueva llamada, nuevamente obtendremos un nuevo parcial (si no hay suficientes argumentos) o, finalmente, el resultado. -```smart header="Fixed-length functions only" -The currying requires the function to have a fixed number of arguments. +```smart header="Solo funciones de longitud fija " +El currying requiere que la función tenga un número fijo de argumentos. -A function that uses rest parameters, such as `f(...args)`, can't be curried this way. +Una función que utiliza múltiples parámetros, como `f(...args)`, no se puede currificar. ``` -```smart header="A little more than currying" -By definition, currying should convert `sum(a, b, c)` into `sum(a)(b)(c)`. +```smart header="Un poco más que curry " +Por definición, el curry debería convertir `sum(a, b, c)` en `sum(a)(b)(c)`. -But most implementations of currying in JavaScript are advanced, as described: they also keep the function callable in the multi-argument variant. +Pero la mayoría de las implementaciones de curry en JavaScript son avanzadas, como se describe: también mantienen la función invocable en la variante de múltiples argumentos. ``` -## Summary +## Resumen -*Currying* is a transform that makes `f(a,b,c)` callable as `f(a)(b)(c)`. JavaScript implementations usually both keep the function callable normally and return the partial if the arguments count is not enough. +*Currificación* es una transformación que hace que `f(a, b, c)` sea invocable como `f(a)(b)(c)`. Las implementaciones de JavaScript generalmente mantienen la función invocable normalmente y devuelven el parcial si el conteo de argumentos no es suficiente. -Currying allows us to easily get partials. As we've seen in the logging example, after currying the three argument universal function `log(date, importance, message)` gives us partials when called with one argument (like `log(date)`) or two arguments (like `log(date, importance)`). +La currificación nos permite obtener parciales fácilmente. Como hemos visto en el ejemplo de registro, después de aplicar currificación a la función universal de tres argumentos `log(fecha, importancia, mensaje)` nos da parciales cuando se llama con un argumento (como `log(fecha)`) o dos argumentos (como ` log(fecha, importancia) `). diff --git a/1-js/99-js-misc/04-reference-type/2-check-syntax/solution.md b/1-js/99-js-misc/04-reference-type/2-check-syntax/solution.md index ba5d3bf04..1f2e0bd94 100644 --- a/1-js/99-js-misc/04-reference-type/2-check-syntax/solution.md +++ b/1-js/99-js-misc/04-reference-type/2-check-syntax/solution.md @@ -1,6 +1,6 @@ -**Error**! +¡**Error**! -Try it: +Inténtalo: ```js run let user = { @@ -8,22 +8,22 @@ let user = { go: function() { alert(this.name) } } -(user.go)() // error! +(user.go)() // ¡Error! ``` -The error message in most browsers does not give us much of a clue about what went wrong. +El mensaje de error en la mayoría de los navegadores no nos da una pista sobre lo que salió mal. -**The error appears because a semicolon is missing after `user = {...}`.** +**El error aparece porque falta un punto y coma después de `user = {...}`.** -JavaScript does not auto-insert a semicolon before a bracket `(user.go)()`, so it reads the code like: +JavaScript no inserta automáticamente un punto y coma antes de un paréntesis `(user.go)()`, por lo que lee el código así: ```js no-beautify let user = { go:... }(user.go)() ``` -Then we can also see that such a joint expression is syntactically a call of the object `{ go: ... }` as a function with the argument `(user.go)`. And that also happens on the same line with `let user`, so the `user` object has not yet even been defined, hence the error. +Entonces también podemos ver que tal expresión conjunta es sintácticamente una llamada del objeto `{ go: ... }` como una función con el argumento `(user.go)`. Y eso también ocurre en la misma línea con `let user`, por lo que el objeto `user` aún no se ha definido y de ahí el error. -If we insert the semicolon, all is fine: +Si insertamos el punto y coma todo está bien: ```js run let user = { @@ -34,4 +34,4 @@ let user = { (user.go)() // John ``` -Please note that parentheses around `(user.go)` do nothing here. Usually they setup the order of operations, but here the dot `.` works first anyway, so there's no effect. Only the semicolon thing matters. +Tenga en cuenta que los paréntesis alrededor de `(user.go)` no hacen nada aquí. Usualmente son configurados para ordenar las operaciones, pero aquí el punto `.` funciona primero de todas formas, por lo que no tienen ningún efecto en él. Solamente el punto y coma importa. diff --git a/1-js/99-js-misc/04-reference-type/2-check-syntax/task.md b/1-js/99-js-misc/04-reference-type/2-check-syntax/task.md index f40d68735..cde3298dd 100644 --- a/1-js/99-js-misc/04-reference-type/2-check-syntax/task.md +++ b/1-js/99-js-misc/04-reference-type/2-check-syntax/task.md @@ -2,9 +2,9 @@ importance: 2 --- -# Syntax check +# Verificación de sintaxis -What is the result of this code? +¿Cuál es el resultado de este código? ```js no-beautify @@ -16,4 +16,4 @@ let user = { (user.go)() ``` -P.S. There's a pitfall :) +P.D. Hay una trampa :) diff --git a/1-js/99-js-misc/04-reference-type/3-why-this/solution.md b/1-js/99-js-misc/04-reference-type/3-why-this/solution.md index e4ee78748..5453c6686 100644 --- a/1-js/99-js-misc/04-reference-type/3-why-this/solution.md +++ b/1-js/99-js-misc/04-reference-type/3-why-this/solution.md @@ -1,22 +1,21 @@ -Here's the explanations. +Aquí está la explicación. -1. That's a regular object method call. +1. Esta es una llamada común al método del objeto -2. The same, parentheses do not change the order of operations here, the dot is first anyway. +2. Lo mismo, aquí los paréntesis no cambian el orden de las operaciones, el punto es el primero de todos modos. -3. Here we have a more complex call `(expression)()`. The call works as if it were split into two lines: +3. Aquí tenemos una llamada más compleja `(expression)()`. La llamada funciona como si se dividiera en dos líneas: ```js no-beautify - f = obj.go; // calculate the expression - f(); // call what we have + f = obj.go; // Calcula la expresión + f(); // Llama a lo que tenemos ``` - Here `f()` is executed as a function, without `this`. + Aquí `f()` se ejecuta como una función, sin `this`. -4. The similar thing as `(3)`, to the left of the parentheses `()` we have an expression. +4. Lo mismo que `(3)`, a la izquierda de los paréntesis `()` tenemos una expresión. -To explain the behavior of `(3)` and `(4)` we need to recall that property accessors (dot or square brackets) return a value of the Reference Type. - -Any operation on it except a method call (like assignment `=` or `||`) turns it into an ordinary value, which does not carry the information allowing to set `this`. +Para explicar el funcionamiento de `(3)` y `(4)` necesitamos recordar que los accesores de propiedad (punto o corchetes) devuelven un valor del Tipo de Referencia. +Cualquier operación en él excepto una llamada al método (como asignación `=` o `||`) lo convierte en un valor ordinario que no transporta la información que permite establecer `this`. diff --git a/1-js/99-js-misc/04-reference-type/3-why-this/task.md b/1-js/99-js-misc/04-reference-type/3-why-this/task.md index e2c073f62..4039d8c6a 100644 --- a/1-js/99-js-misc/04-reference-type/3-why-this/task.md +++ b/1-js/99-js-misc/04-reference-type/3-why-this/task.md @@ -2,11 +2,11 @@ importance: 3 --- -# Explain the value of "this" +# Explica el valor de "this" -In the code below we intend to call `obj.go()` method 4 times in a row. +En el código siguiente intentamos llamar al método `obj.go()` 4 veces seguidas. -But calls `(1)` and `(2)` works differently from `(3)` and `(4)`. Why? +Pero las llamadas `(1)` y `(2)` funcionan diferente a `(3)` y `(4)`. ¿Por qué? ```js run no-beautify let obj, method; diff --git a/1-js/99-js-misc/04-reference-type/article.md b/1-js/99-js-misc/04-reference-type/article.md index 894db8fc6..440d1f6e1 100644 --- a/1-js/99-js-misc/04-reference-type/article.md +++ b/1-js/99-js-misc/04-reference-type/article.md @@ -1,15 +1,15 @@ -# Reference Type +# Tipo de Referencia -```warn header="In-depth language feature" -This article covers an advanced topic, to understand certain edge-cases better. +```warn header="Característica del lenguaje en profundidad" +Este artículo cubre un tema avanzado para comprender mejor ciertos casos límite. -It's not important. Many experienced developers live fine without knowing it. Read on if you want to know how things work under the hood. +Esto no es importante. Muchos desarrolladores experimentados viven bien sin saberlo. Sigue leyendo si quieres saber cómo funcionan las cosas por debajo de la tapa. ``` -A dynamically evaluated method call can lose `this`. +Una llamada al método evaluado dinámicamente puede perder `this`. -For instance: +Por ejemplo: ```js run let user = { @@ -18,42 +18,42 @@ let user = { bye() { alert("Bye"); } }; -user.hi(); // works +user.hi(); // Funciona -// now let's call user.hi or user.bye depending on the name +// Ahora llamemos a user.hi o user.bye dependiendo del nombre ingresado *!* -(user.name == "John" ? user.hi : user.bye)(); // Error! +(user.name == "John" ? user.hi : user.bye)(); // ¡Error! */!* ``` -On the last line there is a conditional operator that chooses either `user.hi` or `user.bye`. In this case the result is `user.hi`. +En la última linea hay un operador condicional que elije entre `user.hi` o `user.bye`. En este caso el resultado es `user.hi`. -Then the method is immediately called with parentheses `()`. But it doesn't work correctly! +Entonces el método es llamado con paréntesis `()`. ¡Pero esto no funciona correctamente! -As you can see, the call results in an error, because the value of `"this"` inside the call becomes `undefined`. +Como puedes ver, la llamada resulta en un error porque el valor de `"this"` dentro de la llamada se convierte en `undefined`. -This works (object dot method): +Esto funciona (objeto, punto, método): ```js user.hi(); ``` -This doesn't (evaluated method): +Esto no funciona (método evaluado): ```js -(user.name == "John" ? user.hi : user.bye)(); // Error! +(user.name == "John" ? user.hi : user.bye)(); // ¡Error! ``` -Why? If we want to understand why it happens, let's get under the hood of how `obj.method()` call works. +¿Por qué? Si queremos entender por qué pasa esto vayamos bajo la tapa de cómo funciona la llamada `obj.method()`. -## Reference type explained +## Tipo de Referencia explicado -Looking closely, we may notice two operations in `obj.method()` statement: +Mirando de cerca podemos notar dos operaciones en la declaración `obj.method()`: -1. First, the dot `'.'` retrieves the property `obj.method`. -2. Then parentheses `()` execute it. +1. Primero, el punto '.' recupera la propiedad de `obj.method`. +2. Luego el paréntesis `()` lo ejecuta. -So, how does the information about `this` get passed from the first part to the second one? +Entonces ¿cómo es trasladada la información de `this` de la primera parte a la segunda? -If we put these operations on separate lines, then `this` will be lost for sure: +Si ponemos estas operaciones en líneas separadas, entonces `this` se perderá con seguridad: ```js run let user = { @@ -62,47 +62,47 @@ let user = { }; *!* -// split getting and calling the method in two lines +// Se divide la obtención y se llama al método en dos lineas let hi = user.hi; -hi(); // Error, because this is undefined +hi(); // Error porque this es indefinido */!* ``` -Here `hi = user.hi` puts the function into the variable, and then on the last line it is completely standalone, and so there's no `this`. +Aquí `hi = user.hi` coloca la función dentro de una variable y luego la última linea es completamente independiente, por lo tanto no hay `this`. -**To make `user.hi()` calls work, JavaScript uses a trick -- the dot `'.'` returns not a function, but a value of the special [Reference Type](https://tc39.github.io/ecma262/#sec-reference-specification-type).** +**Para hacer que la llamada `user.hi()` funcione, JavaScript usa un truco: el punto `'.'` no devuelve una función, sino un valor especial del [Tipo de referencia](https://tc39.github.io/ecma262/#sec-reference-specification-type).** -The Reference Type is a "specification type". We can't explicitly use it, but it is used internally by the language. +El Tipo de Referencia es un "tipo de especificación". No podemos usarla explícitamente, pero es usada internamente por el lenguaje. -The value of Reference Type is a three-value combination `(base, name, strict)`, where: +El valor del Tipo de Referencia es una combinación de triple valor `(base, name, strict)`, donde: -- `base` is the object. -- `name` is the property name. -- `strict` is true if `use strict` is in effect. +- `base` es el objeto. +- `name` es el nombre de la propiedad. +- `strict` es verdadero si `use strict` está en efecto. -The result of a property access `user.hi` is not a function, but a value of Reference Type. For `user.hi` in strict mode it is: +El resultado de un acceso a la propiedad `user.hi` no es una función, sino un valor de Tipo de Referencia. Para `user.hi` en modo estricto esto es: ```js -// Reference Type value +// Valor de Tipo de Referencia (user, "hi", true) ``` -When parentheses `()` are called on the Reference Type, they receive the full information about the object and its method, and can set the right `this` (`user` in this case). +Cuando son llamados los paréntesis `()` en el tipo de referencia, reciben la información completa sobre el objeto y su método, y pueden establecer el `this` correcto (`user` en este caso). -Reference type is a special "intermediary" internal type, with the purpose to pass information from dot `.` to calling parentheses `()`. +Tipo de Referencia es un tipo interno de "intermediario", con el propósito de pasar información desde el punto `.` hacia los paréntesis de la llamada `()`. -Any other operation like assignment `hi = user.hi` discards the reference type as a whole, takes the value of `user.hi` (a function) and passes it on. So any further operation "loses" `this`. +Cualquier otra operación como la asignación `hi = user.hi` descarta el tipo de referencia como un todo, toma el valor de `user.hi` (una función) y lo pasa. Entonces cualquier operación "pierde" `this`. -So, as the result, the value of `this` is only passed the right way if the function is called directly using a dot `obj.method()` or square brackets `obj['method']()` syntax (they do the same here). There are various ways to solve this problem such as [func.bind()](/bind#solution-2-bind). +Entonces, como resultado, el valor de `this` solo se pasa de la manera correcta si la función se llama directamente usando una sintaxis de punto `obj.method()` o corchetes `obj['method']()` (aquí hacen lo mismo). Hay varias formas de resolver este problema, como [func.bind()](/bind#solution-2-bind). -## Summary +## Resumen -Reference Type is an internal type of the language. +El Tipo de Referencia es un tipo interno del lenguaje. -Reading a property, such as with dot `.` in `obj.method()` returns not exactly the property value, but a special "reference type" value that stores both the property value and the object it was taken from. +Leer una propiedad como las que tienen un punto `.` en `obj.method()` no devuelve exactamente el valor de la propiedad, sino un valor especial de "tipo de referencia" que almacena tanto el valor de la propiedad como el objeto del que se tomó. -That's for the subsequent method call `()` to get the object and set `this` to it. +Eso se hace para la llamada `()` al siguiente método para obtener el objeto y establecer `this` en él. -For all other operations, the reference type automatically becomes the property value (a function in our case). +Para todas las demás operaciones, el tipo de referencia se convierte automáticamente en el valor de la propiedad (una función en nuestro caso). -The whole mechanics is hidden from our eyes. It only matters in subtle cases, such as when a method is obtained dynamically from the object, using an expression. +Toda la mecánica está oculta a nuestros ojos. Solo importa en casos sutiles, como cuando un método se obtiene dinámicamente del objeto, usando una expresión. diff --git a/1-js/99-js-misc/05-bigint/article.md b/1-js/99-js-misc/05-bigint/article.md index 2a1cfc843..c2a9e20f5 100644 --- a/1-js/99-js-misc/05-bigint/article.md +++ b/1-js/99-js-misc/05-bigint/article.md @@ -2,21 +2,21 @@ [recent caniuse="bigint"] -`BigInt` is a special numeric type that provides support for integers of arbitrary length. +`BigInt` es un tipo numérico especial que provee soporte a enteros de tamaño arbitrario. -A bigint is created by appending `n` to the end of an integer literal or by calling the function `BigInt` that creates bigints from strings, numbers etc. +Un bigint se crea agregando `n` al final del literal entero o llamando a la función `BigInt` que crea bigints desde cadenas, números, etc. ```js const bigint = 1234567890123456789012345678901234567890n; const sameBigint = BigInt("1234567890123456789012345678901234567890"); -const bigintFromNumber = BigInt(10); // same as 10n +const bigintFromNumber = BigInt(10); // lo mismo que 10n ``` -## Math operators +## Operadores matemáticos -`BigInt` can mostly be used like a regular number, for example: +`BigInt` puede ser usado mayormente como un número regular, por ejemplo: ```js run alert(1n + 2n); // 3 @@ -24,44 +24,44 @@ alert(1n + 2n); // 3 alert(5n / 2n); // 2 ``` -Please note: the division `5/2` returns the result rounded towards zero, without the decimal part. All operations on bigints return bigints. +Por favor, ten en cuenta: la división `5/2` devuelve el resultado redondeado a cero, sin la parte decimal. Todas las operaciones sobre bigints devuelven bigints. -We can't mix bigints and regular numbers: +No podemos mezclar bigints con números regulares: ```js run -alert(1n + 2); // Error: Cannot mix BigInt and other types +alert(1n + 2); // Error: No se puede mezclar BigInt y otros tipos. ``` -We should explicitly convert them if needed: using either `BigInt()` or `Number()`, like this: +Podemos convertirlos explícitamente cuando es necesario: usando `BigInt()` o `Number()` como aquí: ```js run let bigint = 1n; let number = 2; -// number to bigint +// De number a bigint alert(bigint + BigInt(number)); // 3 -// bigint to number +// De bigint a number alert(Number(bigint) + number); // 3 ``` -The conversion operations are always silent, never give errors, but if the bigint is too huge and won't fit the number type, then extra bits will be cut off, so we should be careful doing such conversion. +Las operaciones de conversión siempre son silenciosas, nunca dan error, pero si el bigint es tan gigante que no podrá ajustarse al tipo numérico, los bits extra serán recortados, entonces deberíamos ser cuidadosos al hacer tal conversión. -````smart header="The unary plus is not supported on bigints" -The unary plus operator `+value` is a well-known way to convert `value` to a number. +````smart header="El unario más no tiene soporte en bigints" +El operador unario más `+value` es una manera bien conocida de convertir `value` a number. -In order to avoid confusion, it's not supported on bigints: +Para evitar las confusiones, con bigints eso no es soportado: ```js run let bigint = 1n; alert( +bigint ); // error ``` -So we should use `Number()` to convert a bigint to a number. +Entonces debemos usar `Number()` para convertir un bigint a number. ```` -## Comparisons +## Comparaciones -Comparisons, such as `<`, `>` work with bigints and numbers just fine: +Comparaciones tales como `<`, `>` funcionan bien entre bigints y numbers: ```js run alert( 2n > 1n ); // true @@ -69,7 +69,7 @@ alert( 2n > 1n ); // true alert( 2n > 1 ); // true ``` -Please note though, as numbers and bigints belong to different types, they can be equal `==`, but not strictly equal `===`: +Por favor, nota que como number y bigint pertenecen a diferentes tipos, ellos pueden ser iguales `==`, pero no estrictamente iguales `===`: ```js run alert( 1 == 1n ); // true @@ -77,54 +77,54 @@ alert( 1 == 1n ); // true alert( 1 === 1n ); // false ``` -## Boolean operations +## Operaciones booleanas -When inside `if` or other boolean operations, bigints behave like numbers. +Cuando están dentro de un `if` u otra operación booleana, los bigints se comportan como numbers. -For instance, in `if`, bigint `0n` is falsy, other values are truthy: +Por ejemplo, en `if`, el bigint `0n` es falso, los otros valores son verdaderos: ```js run if (0n) { - // never executes + // nunca se ejecuta } ``` -Boolean operators, such as `||`, `&&` and others also work with bigints similar to numbers: +Los operadores booleanos, tales como `||`, `&&` y otros, también trabajan con bigints en forma similar a los number: ```js run -alert( 1n || 2 ); // 1 (1n is considered truthy) +alert( 1n || 2 ); // 1 (1n es considerado verdadero) -alert( 0n || 2 ); // 2 (0n is considered falsy) +alert( 0n || 2 ); // 2 (0n es considerado falso) ``` ## Polyfills -Polyfilling bigints is tricky. The reason is that many JavaScript operators, such as `+`, `-` and so on behave differently with bigints compared to regular numbers. +Hacer Polyfill con bigints es trabajoso. La razón es que muchos operadores JavaScript como `+`, `-` y otros se comportan de diferente manera comparados con los números regulares. -For example, division of bigints always returns a bigint (rounded if necessary). +Por ejemplo, la división de bigints siempre devuelve un bigint (redondeado cuando es necesario). -To emulate such behavior, a polyfill would need to analyze the code and replace all such operators with its functions. But doing so is cumbersome and would cost a lot of performance. +Para emular tal comportamiento, un polyfill necesitaría analizar el código y reemplazar todos los operadores con sus funciones. Pero hacerlo es engorroso y tendría mucho costo en performance. -So, there's no well-known good polyfill. +Por lo que no se conoce un buen polyfill. -Although, the other way around is proposed by the developers of [JSBI](https://github.com/GoogleChromeLabs/jsbi) library. +Aunque hay otra manera, la propuesta por los desarrolladores de la librería [JSBI](https://github.com/GoogleChromeLabs/jsbi). -This library implements big numbers using its own methods. We can use them instead of native bigints: +Esta librería implementa bigint usando sus propios métodos. Podemos usarlos en lugar de bigints nativos: -| Operation | native `BigInt` | JSBI | +| Operación | `BigInt` nativo | JSBI | |-----------|-----------------|------| -| Creation from Number | `a = BigInt(789)` | `a = JSBI.BigInt(789)` | -| Addition | `c = a + b` | `c = JSBI.add(a, b)` | -| Subtraction | `c = a - b` | `c = JSBI.subtract(a, b)` | +| Creación desde Number | `a = BigInt(789)` | `a = JSBI.BigInt(789)` | +| Suma | `c = a + b` | `c = JSBI.add(a, b)` | +| Resta | `c = a - b` | `c = JSBI.subtract(a, b)` | | ... | ... | ... | -...And then use the polyfill (Babel plugin) to convert JSBI calls to native bigints for those browsers that support them. +...Y entonces usar polyfill (plugin Babel) para convertir las llamadas de JSBI en bigints nativos para aquellos navegadores que los soporten. -In other words, this approach suggests that we write code in JSBI instead of native bigints. But JSBI works with numbers as with bigints internally, emulates them closely following the specification, so the code will be "bigint-ready". +En otras palabras, este enfoque sugiere que escribamos código en JSBI en lugar de bigints nativos. Pero JSBI trabaja internamente tanto con numbers como con bigints, los emula siguiendo de cerca la especificación, entonces el código será "bigint-ready" (preparado para bigint). -We can use such JSBI code "as is" for engines that don't support bigints and for those that do support - the polyfill will convert the calls to native bigints. +Podemos usar tal código JSBI "tal como está" en motores que no soportan bigints, y para aquellos que sí lo soportan - el polyfill convertirá las llamadas en bigints nativos. -## References +## Referencias -- [MDN docs on BigInt](mdn:/JavaScript/Reference/Global_Objects/BigInt). -- [Specification](https://tc39.es/ecma262/#sec-bigint-objects). +- [MDN documentación BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt). +- [Especificación](https://tc39.es/ecma262/#sec-bigint-objects). diff --git a/1-js/99-js-misc/06-unicode/article.md b/1-js/99-js-misc/06-unicode/article.md index 4f144f824..807cfe1d4 100644 --- a/1-js/99-js-misc/06-unicode/article.md +++ b/1-js/99-js-misc/06-unicode/article.md @@ -1,165 +1,165 @@ # Unicode, String internals -```warn header="Advanced knowledge" -The section goes deeper into string internals. This knowledge will be useful for you if you plan to deal with emoji, rare mathematical or hieroglyphic characters, or other rare symbols. +```warn header="Conocimiento avanzado" +Esta sección ahonda en los interioridades de los string. Este conocimiento será útil para ti si planeas lidiar con emojis, raros caracteres matemáticos, jeroglíficos, u otros símbolos extraños. ``` -As we already know, JavaScript strings are based on [Unicode](https://en.wikipedia.org/wiki/Unicode): each character is represented by a byte sequence of 1-4 bytes. +Como ya mencionamos, los strings de JavaScript están basados en [Unicode](https://es.wikipedia.org/wiki/Unicode): cada carácter está representado por una secuencia de entre 1 y 4 bytes. -JavaScript allows us to insert a character into a string by specifying its hexadecimal Unicode code with one of these three notations: +JavaScript nos permite insertar un carácter en un string por medio de su código hexadecimal Unicode, usando estas tres notaciones: - `\xXX` - `XX` must be two hexadecimal digits with a value between `00` and `FF`, then `\xXX` is the character whose Unicode code is `XX`. + `XX` deben ser dos dígitos hexadecimales con un valor entre `00` y `FF`. Entonces, `\xXX` es el carácter cuyo código Unicode es `XX`. - Because the `\xXX` notation supports only two hexadecimal digits, it can be used only for the first 256 Unicode characters. + Como la notación `\xXX` admite solo dos dígitos hexadecimales, puede representar solamente los primeros 256 caracteres Unicode. - These first 256 characters include the Latin alphabet, most basic syntax characters, and some others. For example, `"\x7A"` is the same as `"z"` (Unicode `U+007A`). + Estos primeros 256 caracteres incluyen el alfabeto latino, la mayoría de caracteres de sintaxis básicos, y algunos otros. Por ejemplo, `"\x7A"` es lo mismo que `"z"` (Unicode `U+007A`). ```js run alert( "\x7A" ); // z - alert( "\xA9" ); // ©, the copyright symbol + alert( "\xA9" ); // ©, el símbolo de copyright ``` - `\uXXXX` - `XXXX` must be exactly 4 hex digits with the value between `0000` and `FFFF`, then `\uXXXX` is the character whose Unicode code is `XXXX`. + `XXXX` deben ser exactamente 4 dígitos hexadecimales con un valor entre `0000` y `FFFF`. Entonces, `\uXXXX` es el carácter cuyo código Unicode es `XXXX`. - Characters with Unicode values greater than `U+FFFF` can also be represented with this notation, but in this case, we will need to use a so called surrogate pair (we will talk about surrogate pairs later in this chapter). + Caracteres con un valor Unicode mayor que `U+FFFF` también pueden ser representados con esta notación, pero en ese caso necesitamos usar los llamados "pares sustitutos", descritos más adelante. ```js run - alert( "\u00A9" ); // ©, the same as \xA9, using the 4-digit hex notation - alert( "\u044F" ); // я, the Cyrillic alphabet letter - alert( "\u2191" ); // ↑, the arrow up symbol + alert( "\u00A9" ); // ©, lo mismo que \xA9, usando la notación de 4 dígitos hexa + alert( "\u044F" ); // я, letra del alfabeto cirílico + alert( "\u2191" ); // ↑, símbolo flecha ``` - `\u{X…XXXXXX}` - `X…XXXXXX` must be a hexadecimal value of 1 to 6 bytes between `0` and `10FFFF` (the highest code point defined by Unicode). This notation allows us to easily represent all existing Unicode characters. + `X…XXXXXX` debe ser un valor hexadecimal de 1 a 6 bytes entre `0` y `10FFFF` (el mayor punto de código definido por Unicode). Esta notación nos permite fácilmente representar todos los caracteres Unicode existentes. ```js run - alert( "\u{20331}" ); // 佫, a rare Chinese character (long Unicode) - alert( "\u{1F60D}" ); // 😍, a smiling face symbol (another long Unicode) + alert( "\u{20331}" ); // 佫, un raro carácter chino + alert( "\u{1F60D}" ); // 😍, un símbolo de cara sonriente ``` -## Surrogate pairs +## Pares sustitutos [#surrogate-pairs] -All frequently used characters have 2-byte codes (4 hex digits). Letters in most European languages, numbers, and the basic unified CJK ideographic sets (CJK -- from Chinese, Japanese, and Korean writing systems), have a 2-byte representation. +Todos los caracteres frecuentes tienen códigos de 2 bytes (4 dígitos hexa). Las letras de la mayoría de los lenguajes europeos, números, los conjuntos básicos de caracteres ideográficos CJK unificados (CJK: de los sistemas chino, japonés y coreano), tienen un representación de 2 bytes. -Initially, JavaScript was based on UTF-16 encoding that only allowed 2 bytes per character. But 2 bytes only allow 65536 combinations and that's not enough for every possible symbol of Unicode. +Inicialmente, JavaScript estaba basado en la codificación UTF-16 que solo permite 2 bytes por carácter. Pero 2 bytes solo permiten 65536 combinaciones y eso no es suficiente para cada símbolo Unicode posible. -So rare symbols that require more than 2 bytes are encoded with a pair of 2-byte characters called "a surrogate pair". +Entonces, los símbolos raros que requieren más de 2 bytes son codificados con un par de caracteres de 2 bytes llamado "par sustituto". -As a side effect, the length of such symbols is `2`: +Como efecto secundario, el largo de tales símbolos es `2`: ```js run -alert( '𝒳'.length ); // 2, MATHEMATICAL SCRIPT CAPITAL X -alert( '😂'.length ); // 2, FACE WITH TEARS OF JOY -alert( '𩷶'.length ); // 2, a rare Chinese character +alert( '𝒳'.length ); // 2, carácter matemático X capitalizado +alert( '😂'.length ); // 2, cara con lágrimas de risa +alert( '𩷶'.length ); // 2, un raro carácter chino ``` -That's because surrogate pairs did not exist at the time when JavaScript was created, and thus are not correctly processed by the language! +Esto es porque los pares sustitutos no existían cuando JavaScript fue creado, por ello no es procesado correctamente por el lenguaje. -We actually have a single symbol in each of the strings above, but the `length` property shows a length of `2`. +En realidad tenemos un solo símbolo en cada línea de los string de arriba, pero la propiedad `length` los muestra con un largo de `2`. -Getting a symbol can also be tricky, because most language features treat surrogate pairs as two characters. +Obtener un símbolo puede ser intrincado, porque la mayoría de las características del lenguaje trata a los pares sustitutos como de 2 caracteres. -For example, here we can see two odd characters in the output: +Por ejemplo, aquí vemos dos caracteres extraños en la salida: ```js run -alert( '𝒳'[0] ); // shows strange symbols... -alert( '𝒳'[1] ); // ...pieces of the surrogate pair +alert( '𝒳'[0] ); // muestra símbolos extraños... +alert( '𝒳'[1] ); // ...partes del par sustituto ``` -Pieces of a surrogate pair have no meaning without each other. So the alerts in the example above actually display garbage. +Las 2 partes del par sustituto no tienen significado el uno sin el otro. Entonces las alertas del ejemplo en realidad muestran basura. -Technically, surrogate pairs are also detectable by their codes: if a character has the code in the interval of `0xd800..0xdbff`, then it is the first part of the surrogate pair. The next character (second part) must have the code in interval `0xdc00..0xdfff`. These intervals are reserved exclusively for surrogate pairs by the standard. +Técnicamente, los pares sustitutos son también detectables por su propio código: si un carácter tiene código en el intervalo de `0xd800..0xdbff`, entonces es la primera parte de un par sustituto. El siguiente carácter (segunda parte) debe tener el código en el intervalo `0xdc00..0xdfff`. Estos intervalos son reservados exclusivamente para pares sustitutos por el estándar. -So the methods [String.fromCodePoint](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint) and [str.codePointAt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/codePointAt) were added in JavaScript to deal with surrogate pairs. +Los métodos [String.fromCodePoint](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint) y [str.codePointAt](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/String/codePointAt) fueron añadidos en JavaScript para manejar los pares sustitutos. -They are essentially the same as [String.fromCharCode](mdn:js/String/fromCharCode) and [str.charCodeAt](mdn:js/String/charCodeAt), but they treat surrogate pairs correctly. +Esencialmente, son lo mismo que [String.fromCharCode](mdn:js/String/fromCharCode) y [str.charCodeAt](mdn:js/String/charCodeAt), pero tratan a los pares sustitutos correctamente. -One can see the difference here: +Se puede ver la diferencia aquí: ```js run -// charCodeAt is not surrogate-pair aware, so it gives codes for the 1st part of 𝒳: +// charCodeAt no percibe los pares sustitutos, entonces da el código de la primera parte de 𝒳: alert( '𝒳'.charCodeAt(0).toString(16) ); // d835 -// codePointAt is surrogate-pair aware -alert( '𝒳'.codePointAt(0).toString(16) ); // 1d4b3, reads both parts of the surrogate pair +// codePointAt reconoce los pares sustitutos +alert( '𝒳'.codePointAt(0).toString(16) ); // 1d4b3, lee ambas partes del par sustituto ``` -That said, if we take from position 1 (and that's rather incorrect here), then they both return only the 2nd part of the pair: +Dicho esto, si tomamos desde la posición 1 (y hacerlo es incorrecto aquí), ambas funciones devolverán solo la segunda parte del par: ```js run alert( '𝒳'.charCodeAt(1).toString(16) ); // dcb3 alert( '𝒳'.codePointAt(1).toString(16) ); // dcb3 -// meaningless 2nd half of the pair +// segunda parte del par, sin sentido ``` -You will find more ways to deal with surrogate pairs later in the chapter . There are probably special libraries for that too, but nothing famous enough to suggest here. +Encontrarás más formas de trabajar con pares sustitutos más adelante en el capítulo . Probablemente hay bibliotecas especiales para eso también, pero nada lo suficientemente famoso como para sugerirlo aquí. -````warn header="Takeaway: splitting strings at an arbitrary point is dangerous" -We can't just split a string at an arbitrary position, e.g. take `str.slice(0, 4)` and expect it to be a valid string, e.g.: +````warn header="En conclusión: partir strings en un punto arbitrario es peligroso" +No podemos simplemente separar un string en una posición arbitraria, por ejemplo tomar `str.slice(0, 4)`, y confiar en que sea un string válido: ```js run alert( 'hi 😂'.slice(0, 4) ); // hi [?] ``` -Here we can see a garbage character (first half of the smile surrogate pair) in the output. +Aquí podemos ver basura (la primera mitad del par sustituto de la sonrisa) en la salida. -Just be aware of it if you intend to reliably work with surrogate pairs. May not be a big problem, but at least you should understand what happens. +Simplemente sé consciente de esto si quieres trabajar con confianza con los pares sustitutos. Puede que no sea un gran problema, pero al menos deberías entender lo que pasa. ```` -## Diacritical marks and normalization +### Marcas diacríticas y normalización -In many languages, there are symbols that are composed of the base character with a mark above/under it. +En muchos idiomas hay símbolos compuestos, con un carácter de base y una marca arriba o debajo. -For instance, the letter `a` can be the base character for these characters: `àáâäãåā`. +Por ejemplo, la letra `a` puede ser el carácter base para estos caracteres: `àáâäãåā`. -Most common "composite" characters have their own code in the Unicode table. But not all of them, because there are too many possible combinations. +Los caracteres "compuestos" más comunes tienen su propio código en la tabla UTF-16. Pero no todos ellos, porque hay demasiadas combinaciones posibles. -To support arbitrary compositions, the Unicode standard allows us to use several Unicode characters: the base character followed by one or many "mark" characters that "decorate" it. +Para soportar composiciones arbitrarias, el estándar Unicode permite usar varios caracteres Unicode: el carácter base y uno o varios caracteres de "marca" que lo "decoran". -For instance, if we have `S` followed by the special "dot above" character (code `\u0307`), it is shown as Ṡ. +Por ejemplo, si tenemos `S` seguido del carácter especial "punto arriba" (código `\u0307`), se muestra como Ṡ. ```js run -alert( 'S\u0307' ); // Ṡ +alert('S\u0307'); // Ṡ ``` -If we need an additional mark above the letter (or below it) -- no problem, just add the necessary mark character. +Si necesitamos una marca adicional sobre la letra (o debajo de ella), no hay problema, simplemente se agrega el carácter de marca necesario. -For instance, if we append a character "dot below" (code `\u0323`), then we'll have "S with dots above and below": `Ṩ`. +Por ejemplo, si agregamos un carácter "punto debajo" (código `\u0323`), entonces tendremos" S con puntos arriba y abajo ": `Ṩ`. -For example: +Ejemplo: ```js run alert( 'S\u0307\u0323' ); // Ṩ ``` -This provides great flexibility, but also an interesting problem: two characters may visually look the same, but be represented with different Unicode compositions. +Esto proporciona una gran flexibilidad, pero también un problema interesante: dos caracteres pueden ser visualmente iguales, pero estar representados con diferentes composiciones Unicode. -For instance: +Por ejemplo: ```js run -let s1 = 'S\u0307\u0323'; // Ṩ, S + dot above + dot below -let s2 = 'S\u0323\u0307'; // Ṩ, S + dot below + dot above +let s1 = 'S\u0307\u0323'; // Ṩ, S + punto arriba + punto debajo +let s2 = 'S\u0323\u0307'; // Ṩ, S + punto debajo + punto arriba alert( `s1: ${s1}, s2: ${s2}` ); -alert( s1 == s2 ); // false though the characters look identical (?!) +alert( s1 == s2 ); // false aunque los caracteres se ven idénticos (?!) ``` -To solve this, there exists a "Unicode normalization" algorithm that brings each string to the single "normal" form. +Para resolver esto, existe un algoritmo de "normalización Unicode" que lleva cada cadena a la forma "normal". -It is implemented by [str.normalize()](mdn:js/String/normalize). +Este es implementado por [str.normalize()](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/String/normalize). ```js run alert( "S\u0307\u0323".normalize() == "S\u0323\u0307".normalize() ); // true ``` -It's funny that in our situation `normalize()` actually brings together a sequence of 3 characters to one: `\u1e68` (S with two dots). +Lo curioso de esta situación particular es que `normalize ()` reúne una secuencia de 3 caracteres en uno: `\u1e68` (S con dos puntos). ```js run alert( "S\u0307\u0323".normalize().length ); // 1 @@ -167,6 +167,6 @@ alert( "S\u0307\u0323".normalize().length ); // 1 alert( "S\u0307\u0323".normalize() == "\u1e68" ); // true ``` -In reality, this is not always the case. The reason is that the symbol `Ṩ` is "common enough", so Unicode creators included it in the main table and gave it the code. +En realidad, este no es siempre el caso. La razón es que el símbolo `Ṩ` es "bastante común", por lo que los creadores de Unicode lo incluyeron en la tabla principal y le dieron el código. -If you want to learn more about normalization rules and variants -- they are described in the appendix of the Unicode standard: [Unicode Normalization Forms](https://www.unicode.org/reports/tr15/), but for most practical purposes the information from this section is enough. +Si desea obtener más información sobre las reglas y variantes de normalización, se describen en el apéndice del estándar: [Unicode](https://www.unicode.org/reports/tr15/), pero para la mayoría de los propósitos prácticos, la información de esta sección es suficiente. diff --git a/1-js/99-js-misc/07-weakref-finalizationregistry/article.md b/1-js/99-js-misc/07-weakref-finalizationregistry/article.md index 777bf703c..492923908 100644 --- a/1-js/99-js-misc/07-weakref-finalizationregistry/article.md +++ b/1-js/99-js-misc/07-weakref-finalizationregistry/article.md @@ -1,158 +1,158 @@ -# WeakRef and FinalizationRegistry +# WeakRef y FinalizationRegistry -```warn header="\"Hidden\" features of the language" -This article covers a very narrowly focused topic, that most developers extremely rarely encounter in practice (and may not even be aware of its existence). +```warn header="Características \"ocultas\" del lenguaje" +Este artículo trata un tema muy específico que la mayoría de los desarrolladores rara vez encuentran en la práctica, e incluso pueden desconocer su existencia. -We recommend skipping this chapter if you have just started learning JavaScript. +Si recién estás aprendiendo JavaScript, te recomendamos saltar este capítulo. ``` -Recalling the basic concept of the *reachability principle* from the chapter, -we can note that the JavaScript engine is guaranteed to keep values in memory that are accessible or in use. +El *principio de alcance*, explicado en el capítulo , +establece que el motor de JavaScript garantiza que se mantengan en memoria los valores mientras sean accesibles o estén en uso. -For example: +Por ejemplo: ```js -// the user variable holds a strong reference to the object +// la variable user mantiene una referencia fuerte al objeto let user = { name: "John" }; -// let's overwrite the value of the user variable +// sobrescribimos el valor de la variable user user = null; -// the reference is lost and the object will be deleted from memory +// la referencia se pierde y el objeto será eliminado de la memoria ``` -Or a similar, but slightly more complicated code with two strong references: +Compliquemos un poco el código con dos referencias fuertes: ```js -// the user variable holds a strong reference to the object +// la variable user mantiene una referencia fuerte al objeto let user = { name: "John" }; -// copied the strong reference to the object into the admin variable +// copiamos la referencia fuerte al objeto en la variable admin *!* let admin = user; */!* -// let's overwrite the value of the user variable +// sobrescribimos el valor de la variable user user = null; -// the object is still reachable through the admin variable +// el objeto sigue siendo accesible a través de la variable admin ``` -The object `{ name: "John" }` would only be deleted from memory if there were no strong references to it (if we also overwrote the value of the `admin` variable). +El objeto `{ name: "John" }` solo se eliminará de la memoria si no quedan referencias fuertes a él; es decir, si también sobrescribimos la variable `admin`. -In JavaScript, there is a concept called `WeakRef`, which behaves slightly differently in this case. +En JavaScript existe un concepto llamado `WeakRef`, o referencia débil, que se comporta de manera diferente. -````smart header="Terms: \"Strong reference\", \"Weak reference\"" -**Strong reference** - is a reference to an object or value, that prevents them from being deleted by the garbage collector. Thereby, keeping the object or value in memory, to which it points. +````smart header="Términos: \"Referencia fuerte\", \"Referencia débil\"" +**Referencia fuerte** -- es una referencia a un objeto o valor que impide que el recolector de basura lo elimine, manteniéndolo en memoria. -This means, that the object or value remains in memory and is not collected by the garbage collector as long, as there are active strong references to it. +Esto significa que el objeto o valor permanecerá en memoria y no será eliminado por el recolector de basura mientras haya referencias fuertes activas a él. -In JavaScript, ordinary references to objects are strong references. For example: +En JavaScript, las referencias típicas son fuertes. Por ejemplo: ```js -// the user variable holds a strong reference to this object +// la variable user mantiene una referencia fuerte al objeto let user = { name: "John" }; ``` -**Weak reference** - is a reference to an object or value, that does *not* prevent them from being deleted by the garbage collector. -An object or value can be deleted by the garbage collector if, the only remaining references to them are weak references. +**Referencia débil** -- es una referencia a un objeto o valor que *no* impide que sean eliminados por el recolector de basura. +Un objeto o valor puede ser borrado por el recolector de basura si las únicas referencias a él son débiles. ```` ## WeakRef -````warn header="Note of caution" -Before we dive into it, it is worth noting that the correct use of the structures discussed in this article requires very careful thought, and they are best avoided if possible. +````warn header="Advertencia" +Antes de profundizar en el tema, es importante señalar que su uso requiere una planificación cuidadosa y que, en general, es mejor evitarlas si no son estrictamente necesarias. ```` -`WeakRef` - is an object, that contains a weak reference to another object, called `target` or `referent`. +`WeakRef` -- es un objeto que mantiene referencias débiles a otros objetos, llamados objetivo (`target`) o referente (`referent`). -The peculiarity of `WeakRef` is that it does not prevent the garbage collector from deleting its referent-object. In other words, a `WeakRef` object does not keep the `referent` object alive. +La particularidad de `WeakRef` es que no impide que el recolector de basura elimine a su "referente". En otras palabras, un objeto `WeakRef` no mantiene vivo al objeto referido. -Now let's take the `user` variable as the "referent" and create a weak reference from it to the `admin` variable. -To create a weak reference, you need to use the `WeakRef` constructor, passing in the target object (the object you want a weak reference to). +Ahora tomemos la variable `user` como "referente" y establezcamos una referencia débil hacia ella en la variable `admin`. +Para crear una referencia débil se debe usar el constructor `WeakRef`, pasando como argumento el objetivo (el objeto al que queremos hacer la referencia débil). -In our case — this is the `user` variable: +En nuestro caso, ese objeto es la variable `user`: ```js -// the user variable holds a strong reference to the object +// la variable user mantiene una referencia fuerte al objeto let user = { name: "John" }; -// the admin variable holds a weak reference to the object +// la variable admin mantiene una referencia débil al objeto *!* let admin = new WeakRef(user); */!* ``` -The diagram below depicts two types of references: a strong reference using the `user` variable and a weak reference using the `admin` variable: +El siguiente diagrama muestra los dos tipos de referencia: una referencia fuerte con la variable `user` y una referencia débil con la variable `admin`: -![](weakref-finalizationregistry-01.svg) +![](weakref-finalizationregistry-01.svg) -Then, at some point, we stop using the `user` variable - it gets overwritten, goes out of scope, etc., while keeping the `WeakRef` instance in the `admin` variable: +Luego, en algún momento, dejamos de usar la variable `user` (su valor se sobrescribe, sale de alcance, etc.), pero la instancia de `WeakRef` sigue almacenada en la variable `admin`: ```js -// let's overwrite the value of the user variable +// sobrescribimos el valor de la variable user user = null; ``` -A weak reference to an object is not enough to keep it "alive". When the only remaining references to a referent-object are weak references, the garbage collector is free to destroy this object and use its memory for something else. +Una referencia débil a un objeto no es suficiente para mantenerlo "vivo". Si las únicas referencias restantes a un objeto referido son débiles, el recolector de basura puede destruirlo y reutilizar su memoria para otra cosa. -However, until the object is actually destroyed, the weak reference may return it, even if there are no more strong references to this object. -That is, our object becomes a kind of "[Schrödinger's cat](https://en.wikipedia.org/wiki/Schr%C3%B6dinger%27s_cat)" – we cannot know for sure whether it's "alive" or "dead": +Sin embargo, hasta que el objeto sea realmente eliminado, la referencia débil puede seguir devolviéndolo, incluso si ya no existen referencias fuertes hacia él. +Es decir, nuestro objeto se convierte en una especie de "gato de Schrödinger": no podemos saber con certeza si está "vivo" o "muerto". ![](weakref-finalizationregistry-02.svg) -At this point, to get the object from the `WeakRef` instance, we will use its `deref()` method. +En este punto, para obtener el objeto desde la instancia de `WeakRef`, usamos su método `deref()`. -The `deref()` method returns the referent-object that the `WeakRef` points to, if the object is still in memory. If the object has been deleted by the garbage collector, then the `deref()` method will return `undefined`: +El método `deref()` devuelve el objeto referido al que apunta el `WeakRef`, si aún está en memoria. Si el objeto ha sido eliminado por el recolector de basura,`deref()` devolverá `undefined`: ```js let ref = admin.deref(); if (ref) { - // the object is still accessible: we can perform any manipulations with it + // el objeto sigue accesible: podemos manipularlo libremente } else { - // the object has been collected by the garbage collector + // el objeto ha sido eliminado por el recolector de basura } ``` -## WeakRef use cases +## WeakRef, casos de uso -`WeakRef` is typically used to create caches or [associative arrays](https://en.wikipedia.org/wiki/Associative_array) that store resource-intensive objects. -This allows one to avoid preventing these objects from being collected by the garbage collector solely based on their presence in the cache or associative array. +`WeakRef` se usa típicamente para crear cachés o [arrays asociativos](https://es.wikipedia.org/wiki/Tabla_hash) que almacenan objetos con un alto consumo de recursos. +Esto permite evitar que dichos objetos permanezcan en memoria solo por estar en la caché o en un array asociativo.. -One of the primary examples - is a situation when we have numerous binary image objects (for instance, represented as `ArrayBuffer` or `Blob`), and we want to associate a name or path with each image. -Existing data structures are not quite suitable for these purposes: +Uno de los principales ejemplos es cuando manejamos múltiples objetos de imagen binaria (por ejemplo, representados como `ArrayBuffer` o `Blob`), y queremos asociarles un nombre o una ruta. +Las estructuras de datos existentes no son del todo adecuadas para esto: -- Using `Map` to create associations between names and images, or vice versa, will keep the image objects in memory since they are present in the `Map` as keys or values. -- `WeakMap` is ineligible for this goal either: because the objects represented as `WeakMap` keys use weak references, and are not protected from deletion by the garbage collector. +- Usar `Map` para asociar nombres con imágenes (o viceversa) mantiene las imágenes en memoria, ya que siguen presentes en el `Map` como claves o valores. +- `WeakMap` tampoco es una opción válida: los objetos usados como claves en `WeakMap` tienen referencias débiles, por lo que el recolector de basura puede eliminarlos. -But, in this situation, we need a data structure that would use weak references in its values. +En este caso, necesitamos una estructura de datos que use referencias débiles en sus valores. -For this purpose, we can use a `Map` collection, whose values are `WeakRef` instances referring to the large objects we need. -Consequently, we will not keep these large and unnecessary objects in memory longer than they should be. +Para ello, podemos usar una colección `Map`, donde los valores sean instancias de `WeakRef` apuntando a los objetos grandes que queremos manejar. +Así, evitamos mantener estos objetos grandes e innecesarios en memoria más tiempo del necesario. -Otherwise, this is a way to get the image object from the cache if it is still reachable. -If it has been garbage collected, we will re-generate or re-download it again. +De este modo, si el objeto aún es accesible, podemos obtenerlo desde la caché. +Si ha sido eliminado por el recolector de basura, lo regeneramos o lo descargamos nuevamente. -This way, less memory is used in some situations. +Esto permite reducir el uso de memoria en ciertas situaciones. -## Example №1: using WeakRef for caching +## Ejemplo №1: Uso de WeakRef para caché -Below is a code snippet that demonstrates the technique of using `WeakRef`. +A continuación, se muestra un fragmento de código que demuestra el uso de `WeakRef`. -In short, we use a `Map` with string keys and `WeakRef` objects as their values. -If the `WeakRef` object has not been collected by the garbage collector, we get it from the cache. -Otherwise, we re-download it again and put it in the cache for further possible reuse: +En resumen, utilizamos un `Map` con claves de tipo string y objetos `WeakRef` como valores. +Si el objeto referenciado por `WeakRef` no ha sido eliminado por el recolector de basura, lo recuperamos de la caché. +Caso contrario, lo descargamos nuevamente y lo almacenamos en la caché para su posible reutilización: ```js function fetchImg() { - // abstract function for downloading images... + // función abstracta para descargar imágenes... } function weakRefCache(fetchImg) { // (1) @@ -173,157 +173,157 @@ function weakRefCache(fetchImg) { // (1) } const getCachedImg = weakRefCache(fetchImg); -``` +``` -Let's delve into the details of what happened here: -1. `weakRefCache` - is a higher-order function that takes another function, `fetchImg`, as an argument. In this example, we can neglect a detailed description of the `fetchImg` function, since it can be any logic for downloading images. -2. `imgCache` - is a cache of images, that stores cached results of the `fetchImg` function, in the form of string keys (image name) and `WeakRef` objects as their values. -3. Return an anonymous function that takes the image name as an argument. This argument will be used as a key for the cached image. -4. Trying to get the cached result from the cache, using the provided key (image name). -5. If the cache contains a value for the specified key, and the `WeakRef` object has not been deleted by the garbage collector, return the cached result. -6. If there is no entry in the cache with the requested key, or `deref()` method returns `undefined` (meaning that the `WeakRef` object has been garbage collected), the `fetchImg` function downloads the image again. -7. Put the downloaded image into the cache as a `WeakRef` object. +Analicemos en detalle lo que ocurre aquí: +1. `weakRefCache` -- es una función de orden superior que recibe otra función, `fetchImg`, como argumento. En este ejemplo, no es necesario describir `fetchImg` en detalle, ya que puede ser cualquier lógica para descargar imágenes. +2. `imgCache` -- es una caché de imágenes que almacena los resultados de `fetchImg` en un `map`, con claves de tipo `string` para los nombres de las imágenes y objetos `WeakRef` como valores. +3. Se devuelve una función anónima que toma el nombre de la imagen como argumento. Este argumento se usa como clave en la caché. +4. Se intenta obtener el resultado almacenado en caché usando la clave proporcionada. +5. Si la caché contiene un valor para la clave especificada y el objeto referenciado por `WeakRef` aún existe, se devuelve el resultado en caché. +6. Si no hay una entrada en la caché con la clave solicitada, o si `deref()` devuelve `undefined` (lo que significa que el objeto ha sido eliminado por el recolector de basura), se vuelve a descargar la imagen con `fetchImg`. +7. La imagen descargada se almacena en la caché como un objeto `WeakRef`. -Now we have a `Map` collection, where the keys - are image names as strings, and values - are `WeakRef` objects containing the images themselves. +Ahora tenemos una colección `Map`donde las claves son string con los nombres de imágenes y los valores son objetos `WeakRef` que contienen las imágenes. -This technique helps to avoid allocating a large amount of memory for resource-intensive objects, that nobody uses anymore. -It also saves memory and time in case of reusing cached objects. +Esta técnica ayuda a evitar la asignación innecesaria de grandes cantidades de memoria a objetos que ya no están en uso. +Pero también ahorra memoria y tiempo cuando se reutilizan objetos en caché. -Here is a visual representation of what this code looks like: +Aquí hay una representación visual de este código: -![](weakref-finalizationregistry-03.svg) +![](weakref-finalizationregistry-03.svg) -But, this implementation has its drawbacks: over time, `Map` will be filled with strings as keys, that point to a `WeakRef`, whose referent-object has already been garbage collected: +Sin embargo, esta implementación tiene una desventaja: con el tiempo, `Map` se llenará de claves `string` que apuntan a `WeakRef` cuyos objetos referenciados ya han sido eliminados por el recolector de basura: ![](weakref-finalizationregistry-04.svg) -One way to handle this problem - is to periodically scavenge the cache and clear out "dead" entries. -Another way - is to use finalizers, which we will explore next. +Una forma de manejar este problema es limpiar periódicamente la caché para eliminar las entradas "muertas". +Otra opción es usar finalizadores, que exploraremos a continuación. -## Example №2: Using WeakRef to track DOM objects +## Ejemplo №2: Usando WeakRef para rastrear objetos del DOM -Another use case for `WeakRef` - is tracking DOM objects. +Otro caso de uso de `WeakRef` es rastrear objetos del DOM. -Let's imagine a scenario where some third-party code or library interacts with elements on our page as long as they exist in the DOM. -For example, it could be an external utility for monitoring and notifying about the system's state (commonly so-called "logger" – a program that sends informational messages called "logs"). +Imaginemos un escenario en el que un código o biblioteca de terceros interactúa con elementos de nuestra página mientras existan en el DOM. +Por ejemplo, podría ser una utilidad externa que monitorea y notifica el estado del sistema (comúnmente llamada "logger", un programa que envía mensajes informativos llamados "logs"). -Interactive example: +Ejemplo interactivo: -[codetabs height=420 src="weakref-dom"] +[codetabs height=420 src="weakref-dom"] -When the "Start sending messages" button is clicked, in the so-called "logs display window" (an element with the `.window__body` class), messages (logs) start to appear. +Cuando se hace clic en el botón "Iniciar envío de mensajes", comienzan a aparecer mensajes (logs) en la llamada "ventana de visualización de logs" (un elemento con la clase `.window__body`). -But, as soon as this element is deleted from the DOM, the logger should stop sending messages. -To reproduce the removal of this element, just click the "Close" button in the top right corner. +Sin embargo, tan pronto como este elemento es eliminado del DOM, el logger debería dejar de enviar mensajes. +Para simular la eliminación de este elemento, simplemente haz clic en el botón "Cerrar" en la esquina superior derecha. -In order not to complicate our work, and not to notify third-party code every time our DOM-element is available, and when it is not, it will be enough to create a weak reference to it using `WeakRef`. +Para evitar la necesidad de notificar al código externo cada vez que nuestro elemento DOM está disponible o no, podemos simplemente crear una referencia débil con `WeakRef`. -Once the element is removed from the DOM, the logger will notice it and stop sending messages. +Una vez que el elemento es eliminado del DOM, el logger lo detectará y dejará de enviar mensajes. -Now let's take a closer look at the source code (*tab `index.js`*): +Ahora veamos en detalle el código fuente (*pestaña `index.js`*): -1. Get the DOM-element of the "Start sending messages" button. -2. Get the DOM-element of the "Close" button. -3. Get the DOM-element of the logs display window using the `new WeakRef()` constructor. This way, the `windowElementRef` variable holds a weak reference to the DOM-element. -4. Add an event listener on the "Start sending messages" button, responsible for starting the logger when clicked. -5. Add an event listener on the "Close" button, responsible for closing the logs display window when clicked. -6. Use `setInterval` to start displaying a new message every second. -7. If the DOM-element of the logs display window is still accessible and kept in memory, create and send a new message. -8. If the `deref()` method returns `undefined`, it means that the DOM-element has been deleted from memory. In this case, the logger stops displaying messages and clears the timer. -9. `alert`, which will be called, after the DOM-element of the logs display window is deleted from memory (i.e. after clicking the "Close" button). **Note, that deletion from memory may not happen immediately, as it depends only on the internal mechanisms of the garbage collector.** +1. Obtener el elemento DOM del botón "Iniciar envío de mensajes". +2. Obtener el elemento DOM del botón "Cerrar". +3. Obtener el elemento DOM de la ventana de logs usando el constructor `new WeakRef()`. De esta manera, la variable `windowElementRef` mantiene una referencia débil al elemento del DOM. +4. Agregar un event listener al botón "Iniciar envío de mensajes", que inicia el logger cuando se hace clic. +5. Agregar un event listener al botón "Cerrar", que elimina la ventana de logs del DOM cuando se hace clic. +6. Usar `setInterval` para mostrar un nuevo mensaje cada segundo. +7. Si el elemento DOM de la ventana de logs sigue disponible en memoria, crear y enviar un nuevo mensaje. +8. Si el método `deref()` devuelve `undefined`, significa que el elemento DOM ha sido eliminado de la memoria. En este caso, el logger deja de mostrar mensajes y se limpia el temporizador. +9. Mostrar un`alert`, cuando el elemento DOM de la ventana de logs haya sido eliminado de la memoria (es decir, después de hacer clic en el botón "Cerrar"). **Nota: La eliminación de la memoria puede no ocurrir de inmediato, ya que depende únicamente de los mecanismos internos del recolector de basura.** - We cannot control this process directly from the code. However, despite this, we still have the option to force garbage collection from the browser. + No podemos controlar este proceso directamente desde el código. Sin embargo, aún es posible forzar la recolección de basura en el navegador. - In Google Chrome, for example, to do this, you need to open the developer tools (`key:Ctrl` + `key:Shift` + `key:J` on Windows/Linux or `key:Option` + `key:⌘` + `key:J` on macOS), go to the "Performance" tab, and click on the bin icon button – "Collect garbage": + En Google Chrome, por ejemplo, para hacer esto, abre las herramientas para desarrolladores (`key:Ctrl` + `key:Shift` + `key:J` en Windows/Linux o `key:Option` + `key:⌘` + `key:J` en macOS), ve a la pestaña "Performance" y haz clic en el ícono de la papelera – "Collect garbage": ![](google-chrome-developer-tools.png)
- This functionality is supported in most modern browsers. After the actions are taken, the alert will trigger immediately. + Esta funcionalidad está disponible en la mayoría de los navegadores modernos. Después de realizar estos pasos, el alert se activará inmediatamente. ## FinalizationRegistry -Now it is time to talk about finalizers. Before we move on, let's clarify the terminology: +Es momento de hablar sobre los finalizadores. Aclaremos la terminología antes de continuar: -**Cleanup callback (finalizer)** - is a function that is executed, when an object, registered in the `FinalizationRegistry`, is deleted from memory by the garbage collector. +**Callback de limpieza (finalizador)** -- es una función que se ejecuta cuando un objeto registrado en `FinalizationRegistry` es eliminado de la memoria por el recolector de basura. -Its purpose - is to provide the ability to perform additional operations, related to the object, after it has been finally deleted from memory. +Su propósito es permitir realizar operaciones adicionales relacionadas con el objeto después de que haya sido eliminado de la memoria. -**Registry** (or `FinalizationRegistry`) - is a special object in JavaScript that manages the registration and unregistration of objects and their cleanup callbacks. +**Registro** (o `FinalizationRegistry`) -- es un objeto especial en JavaScript que gestiona el alta y la eliminación de objetos referent junto con sus callbacks de limpieza. -This mechanism allows registering an object to track and associate a cleanup callback with it. -Essentially it is a structure that stores information about registered objects and their cleanup callbacks, and then automatically invokes those callbacks when the objects are deleted from memory. +Este mecanismo permite registrar un objeto para rastrearlo y asociarle un callback de limpieza. +Básicamente, es una estructura que almacena información sobre los objetos registrados y sus callbacks de limpieza, y luego los invoca automáticamente cuando los objetos son eliminados de la memoria. -To create an instance of the `FinalizationRegistry`, it needs to call its constructor, which takes a single argument - the cleanup callback (finalizer). +Para crear una instancia de `FinalizationRegistry`, se debe llamar a su constructor, el que recibe un solo argumento: el callback de limpieza (el finalizador). -Syntax: +Sintaxis: ```js function cleanupCallback(heldValue) { - // cleanup callback code + // código del callback de limpieza } const registry = new FinalizationRegistry(cleanupCallback); ``` -Here: +Donde: -- `cleanupCallback` - a cleanup callback that will be automatically called when a registered object is deleted from memory. -- `heldValue` - the value that is passed as an argument to the cleanup callback. If `heldValue` is an object, the registry keeps a strong reference to it. -- `registry` - an instance of `FinalizationRegistry`. +- `cleanupCallback` -- es la función que se ejecutará automáticamente cuando un objeto registrado sea eliminado de la memoria. +- `heldValue` -- es el valor que se pasará como argumento al callback de limpieza. Si `heldValue` es un objeto, el registro mantiene una referencia fuerte a él. +- `registry` -- es la instancia de `FinalizationRegistry`. -`FinalizationRegistry` methods: +Métodos de `FinalizationRegistry`: -- `register(target, heldValue [, unregisterToken])` - used to register objects in the registry. +- `register(target, heldValue [, unregisterToken])` -- registra un objeto en el registro. - `target` - the object being registered for tracking. If the `target` is garbage collected, the cleanup callback will be called with `heldValue` as its argument. + `target` -- el objeto a registrar. Si `target` es recolectado por el recolector de basura, el callback de limpieza se ejecutará con `heldValue` como argumento. - Optional `unregisterToken` – an unregistration token. It can be passed to unregister an object before the garbage collector deletes it. Typically, the `target` object is used as `unregisterToken`, which is the standard practice. -- `unregister(unregisterToken)` - the `unregister` method is used to unregister an object from the registry. It takes one argument - `unregisterToken` (the unregister token that was obtained when registering the object). + Opcional `unregisterToken` -- un token de desregistro. Se puede usar para anular el registro de un objeto antes de que el recolector de basura lo elimine. Por convención, suele ser el mismo `target`. +- `unregister(unregisterToken)` -- elimina un objeto del registro. Recibe un argumento: `unregisterToken` (el token usado al registrar el objeto). -Now let's move on to a simple example. Let's use the already-known `user` object and create an instance of `FinalizationRegistry`: +Veamos un ejemplo. Usemos el ya conocido objeto `user` y creemos una instancia de `FinalizationRegistry`: ```js let user = { name: "John" }; const registry = new FinalizationRegistry((heldValue) => { - console.log(`${heldValue} has been collected by the garbage collector.`); + console.log(`${heldValue} ha sido recolectado por el recolector de basura.`); }); ``` -Then, we will register the object, that requires a cleanup callback by calling the `register` method: +Luego, registramos el objeto que requiere limpieza llamando al método `register`: ```js registry.register(user, user.name); ``` -The registry does not keep a strong reference to the object being registered, as this would defeat its purpose. If the registry kept a strong reference, then the object would never be garbage collected. +El registro no mantiene una referencia fuerte al objeto, ya que eso impediría que el recolector de basura lo eliminara. -If the object is deleted by the garbage collector, our cleanup callback may be called at some point in the future, with the `heldValue` passed to it: +Si el objeto es eliminado por el recolector de basura, el callback de limpieza puede ejecutarse en algún momento futuro con el `heldValue` pasado como argumento: ```js -// When the user object is deleted by the garbage collector, the following message will be printed in the console: -"John has been collected by the garbage collector." +// Cuando el objeto user sea eliminado, se imprimirá en la consola: +"John ha sido recolectado por el recolector de basura." ``` -There are also situations where, even in implementations that use a cleanup callback, there is a chance that it will not be called. +Hay casos donde el callback tiene posibilidades de no ejecutarse. -For example: -- When the program fully terminates its operation (for example, when closing a tab in a browser). -- When the `FinalizationRegistry` instance itself is no longer reachable to JavaScript code. - If the object that creates the `FinalizationRegistry` instance goes out of scope or is deleted, the cleanup callbacks registered in that registry might also not be invoked. +Por ejemplo: +- Cuando el programa termina por completo su operación (por ejemplo, al cerrar una pestaña en el navegador). +- Cuando la instancia de `FinalizationRegistry` mismo deja de estar accesible en el código. + Si el objeto que creó la instancia de `FinalizationRegistry` sale del ámbito o es eliminado, los callbacks de limpieza registrados en tal registro podrían no ser invocados. -## Caching with FinalizationRegistry +## Caché con FinalizationRegistry -Returning to our *weak* cache example, we can notice the following: -- Even though the values wrapped in the `WeakRef` have been collected by the garbage collector, there is still an issue of "memory leakage" in the form of the remaining keys, whose values have been collected by the garbage collector. +Volviendo a nuestro ejemplo de caché *débil*, podemos notar lo siguiente: +- Aunque los valores envueltos en `WeakRef` hayan sido recolectados por el recolector de basura, sigue existiendo un problema de "fuga de memoria" debido a las claves restantes cuyos valores han sido eliminados. -Here is an improved caching example using `FinalizationRegistry`: +Esta es una versión mejorada de la caché usando `FinalizationRegistry`: ```js function fetchImg() { - // abstract function for downloading images... + // función abstracta para descargar imágenes... } function weakRefCache(fetchImg) { @@ -356,128 +356,128 @@ function weakRefCache(fetchImg) { const getCachedImg = weakRefCache(fetchImg); ``` -1. To manage the cleanup of "dead" cache entries, when the associated `WeakRef` objects are collected by the garbage collector, we create a `FinalizationRegistry` cleanup registry. +1. Para gestionar la limpieza de las entradas "muertas" de la caché cuando los objetos `WeakRef` son eliminados por el recolector de basura, creamos un registro de limpieza con `FinalizationRegistry`. - The important point here is, that in the cleanup callback, it should be checked, if the entry was deleted by the garbage collector and not re-added, in order not to delete a "live" entry. -2. Once the new value (image) is downloaded and put into the cache, we register it in the finalizer registry to track the `WeakRef` object. + Es importante comprobar en el callback de limpieza si la entrada fue eliminada por el recolector y no ha sido reinsertada, para evitar borrar una entrada "viva". +2. Una vez que se descarga una nueva imagen y se almacena en la caché, la registramos en el `FinalizationRegistry` para rastrear el objeto `WeakRef`. -This implementation contains only actual or "live" key/value pairs. -In this case, each `WeakRef` object is registered in the `FinalizationRegistry`. -And after the objects are cleaned up by the garbage collector, the cleanup callback will delete all `undefined` values. +Esta implementación solo mantiene pares clave/valor realmente "vivos". +En este caso, cada objeto `WeakRef` se registra en `FinalizationRegistry`. +Y después de que los objetos sean eliminados por el recolector de basura, el callback de limpieza eliminará todas las entradas `undefined`. -Here is a visual representation of the updated code: +Aquí la representación visual del código actualizado: ![](weakref-finalizationregistry-05.svg) -A key aspect of the updated implementation is that finalizers allow parallel processes to be created between the "main" program and cleanup callbacks. -In the context of JavaScript, the "main" program - is our JavaScript-code, that runs and executes in our application or web page. +Un aspecto clave de esta implementación es que los finalizadores permiten la ejecución en paralelo entre el programa principal y los callbacks de limpieza. +En el contexto de JavaScript, el "programa principal" es el código JavaScript que se ejecuta en nuestra aplicación o página web. -Hence, from the moment an object is marked for deletion by the garbage collector, and to the actual execution of the cleanup callback, there may be a certain time gap. -It is important to understand that during this time gap, the main program can make any changes to the object or even bring it back to memory. +Por lo tanto, desde el momento en que un objeto es marcado para eliminación por el recolector de basura hasta que el callback de limpieza se ejecuta, puede haber un cierto lapso. +Es importante entender que, durante este tiempo, el programa principal puede modificar el objeto o incluso restaurarlo en la memoria. -That's why, in the cleanup callback, we must check to see if an entry has been added back to the cache by the main program to avoid deleting "live" entries. -Similarly, when searching for a key in the cache, there is a chance that the value has been deleted by the garbage collector, but the cleanup callback has not been executed yet. +Por eso, en el callback de limpieza debemos verificar si la entrada ha sido agregada nuevamente a la caché para evitar eliminar valores "vivos". +Del mismo modo, al buscar una clave en la caché, existe la posibilidad de que su valor haya sido eliminado por el recolector de basura, pero el callback de limpieza aún no se haya ejecutado. -Such situations require special attention if you are working with `FinalizationRegistry`. +Estas situaciones requieren especial atención si trabajas con `FinalizationRegistry`. -## Using WeakRef and FinalizationRegistry in practice +## Uso de WeakRef y FinalizationRegistry en la práctica -Moving from theory to practice, imagine a real-life scenario, where a user synchronizes their photos on a mobile device with some cloud service -(such as [iCloud](https://en.wikipedia.org/wiki/ICloud) or [Google Photos](https://en.wikipedia.org/wiki/Google_Photos)), -and wants to view them from other devices. In addition to the basic functionality of viewing photos, such services offer a lot of additional features, for example: +Pasando de la teoría a la práctica, imaginemos un escenario real en el que un usuario sincroniza sus fotos en un dispositivo móvil con un servicio en la nube +(como [iCloud](https://en.wikipedia.org/wiki/ICloud) o [Google Photos](https://en.wikipedia.org/wiki/Google_Photos)), y quiere verlas desde otros dispositivos. +Además de la funcionalidad básica de visualización, estos servicios ofrecen características adicionales como: -- Photo editing and video effects. -- Creating "memories" and albums. -- Video montage from a series of photos. -- ...and much more. +- Edición de fotos y efectos de video. +- Creación de "recuerdos" y álbumes. +- Montajes de video a partir de una serie de fotos. +- ... y mucho más. -Here, as an example, we will use a fairly primitive implementation of such a service. -The main point - is to show a possible scenario of using `WeakRef` and `FinalizationRegistry` together in real life. +Aquí, como ejemplo, usaremos una implementación bastante primitiva de un servicio similar. +El objetivo principal es mostrar un posible escenario en el que `WeakRef` y `FinalizationRegistry` se utilicen juntos en una aplicación real. -Here is what it looks like: +Así es como se ve: ![](weakref-finalizationregistry-demo-01.png)
-On the left side, there is a cloud library of photos (they are displayed as thumbnails). -We can select the images we need and create a collage, by clicking the "Create collage" button on the right side of the page. -Then, the resulting collage can be downloaded as an image. +A la izquierda, hay una biblioteca de fotos en la nube (mostradas como miniaturas). +Podemos seleccionar imágenes y crear un collage haciendo clic en el botón "Crear collage" en la derecha. +Luego, el collage resultante se puede descargar como una imagen.

-To increase page loading speed, it would be reasonable to download and display photo thumbnails in *compressed* quality. -But, to create a collage from selected photos, download and use them in *full-size* quality. +Para acelerar la carga de la página, lo lógico es descargar y mostrar las miniaturas en calidad *comprimida*, +pero al crear el collage, descargar y usar las imágenes en *calidad completa*. -Below, we can see, that the intrinsic size of the thumbnails is 240x240 pixels. -The size was chosen on purpose to increase loading speed. -Moreover, we do not need full-size photos in preview mode. +Las miniaturas tienen un tamaño de 240x240 píxeles. +Este tamaño se eligió intencionalmente para optimizar la velocidad de carga. +En el modo de vista previa, tampoco necesitamos imágenes en tamaño completo. ![](weakref-finalizationregistry-demo-02.png)
-Let's assume, that we need to create a collage of 4 photos: we select them, and then click the "Create collage" button. -At this stage, the already known to us weakRefCache function checks whether the required image is in the cache. -If not, it downloads it from the cloud and puts it in the cache for further use. -This happens for each selected image: +Supongamos que queremos crear un collage con 4 fotos. Las seleccionamos y hacemos clic en "Crear collage". +Aquí, la ya conocida función weakRefCache verifica si la imagen requerida está en caché. +Si no lo está, la descarga desde la nube y la almacena para futuros usos. +Esto ocurre para cada imagen seleccionada:

![](weakref-finalizationregistry-demo-03.gif)
-Paying attention to the output in the console, you can see, which of the photos were downloaded from the cloud - this is indicated by FETCHED_IMAGE. -Since this is the first attempt to create a collage, this means, that at this stage the "weak cache" was still empty, and all the photos were downloaded from the cloud and put in it. +Al observar la consola podemos ver qué fotos fueron descargadas desde la nube, indicadas por FETCHED_IMAGE. +Como es la primera vez que se crea el collage, la "caché débil" estaba vacía, así que todas las imágenes se descargaron de la nube. -But, along with the process of downloading images, there is also a process of memory cleanup by the garbage collector. -This means, that the object stored in the cache, which we refer to, using a weak reference, is deleted by the garbage collector. -And our finalizer executes successfully, thereby deleting the key, by which the image was stored in the cache. -CLEANED_IMAGE notifies us about it: +Pero al mismo tiempo el recolector de basura está limpiando la memoria. +Esto significa que las imágenes almacenadas con referencias débiles pueden ser eliminadas. +Cuando esto sucede, nuestro finalizador borra la clave correspondiente de la caché. +CLEANED_IMAGE nos lo notifica: ![](weakref-finalizationregistry-demo-04.jpg)
-Next, we realize that we do not like the resulting collage, and decide to change one of the images and create a new one. -To do this, just deselect the unnecessary image, select another one, and click the "Create collage" button again: +Después, nos damos cuenta de que no nos gusta el collage y decidimos cambiar una de las imágenes. +Para esto, desmarcamos una, seleccionamos otra y hacemos clic en "Crear collage" de nuevo:

![](weakref-finalizationregistry-demo-05.gif)
-But this time not all images were downloaded from the network, and one of them was taken from the weak cache: the CACHED_IMAGE message tells us about it. -This means that at the time of collage creation, the garbage collector had not yet deleted our image, and we boldly took it from the cache, -thereby reducing the number of network requests and speeding up the overall time of the collage creation process: +Pero esta vez, no todas las imágenes se descargaron de la red, algunas se obtuvieron de la caché débil, como indica el mensaje CACHED_IMAGE. +Esto significa que el recolector de basura aún no eliminó algunas imágenes, +lo que reduce el número de descargas y acelera el proceso de creación del collage.

![](weakref-finalizationregistry-demo-06.jpg)
-Let's "play around" a little more, by replacing one of the images again and creating a new collage: +Juguemos un poco más. Volvamos a cambiar otra imagen y creemos un nuevo collage:

![](weakref-finalizationregistry-demo-07.gif)
-This time the result is even more impressive. Of the 4 images selected, 3 of them were taken from the weak cache, and only one had to be downloaded from the network. -The reduction in network load was about 75%. Impressive, isn't it? +Esta vez, el resultado es aún mejor. De las 4 imágenes seleccionadas, 3 fueron recuperadas de la caché débil y solo una tuvo que descargarse. +La reducción en el uso de la red fue del 75%. Nada mal.

![](weakref-finalizationregistry-demo-08.jpg)
-Of course, it is important to remember, that such behavior is not guaranteed, and depends on the specific implementation and operation of the garbage collector. +Es importante recordar que este comportamiento no está garantizado y depende de la implementación específica del recolector de basura. -Based on this, a completely logical question immediately arises: why do not we use an ordinary cache, where we can manage its entities ourselves, instead of relying on the garbage collector? -That's right, in the vast majority of cases there is no need to use `WeakRef` and `FinalizationRegistry`. +Dicho esto, surge una pregunta lógica: ¿por qué no usar una caché normal, que podamos gestionar manualmente en lugar de depender del recolector de basura? +En la mayoría de los casos, no hay necesidad de usar `WeakRef` y `FinalizationRegistry`. -Here, we simply demonstrated an alternative implementation of similar functionality, using a non-trivial approach with interesting language features. -Still, we cannot rely on this example, if we need a constant and predictable result. +Aquí simplemente demostramos una alternativa con un enfoque diferente y características interesantes del lenguaje. +Sin embargo, este ejemplo no es fiable si necesitamos resultados constantes y predecibles. -You can [open this example in the sandbox](sandbox:weakref-finalizationregistry). +Puedes [abrir este ejemplo en el sandbox.](sandbox:weakref-finalizationregistry). -## Summary +## Resumen -`WeakRef` - designed to create weak references to objects, allowing them to be deleted from memory by the garbage collector if there are no longer strong references to them. -This is beneficial for addressing excessive memory usage and optimizing the utilization of system resources in applications. +`WeakRef` -- está diseñado para crear referencias débiles a objetos, lo que permite que el recolector de basura los elimine si no hay referencias fuertes a ellos. +Esto habilita al motor de JavaScript optimizar el uso de memoria y recursos del sistema. -`FinalizationRegistry` - is a tool for registering callbacks, that are executed when objects that are no longer strongly referenced, are destroyed. -This allows releasing resources associated with the object or performing other necessary operations before deleting the object from memory. \ No newline at end of file +`FinalizationRegistry` -- permite registrar callbacks que se ejecutan cuando un objeto sin referencias fuertes es eliminado. +Esto permite liberar manualmente recursos asociados al objeto o realizar tareas de limpieza adicionales antes de que el objeto desaparezca de la memoria. diff --git a/1-js/99-js-misc/index.md b/1-js/99-js-misc/index.md index 79cd72fe7..e9e145883 100644 --- a/1-js/99-js-misc/index.md +++ b/1-js/99-js-misc/index.md @@ -1,2 +1,2 @@ +# Temas diversos -# Miscellaneous diff --git a/1-js/index.md b/1-js/index.md index c313cb85c..0bcd38d01 100644 --- a/1-js/index.md +++ b/1-js/index.md @@ -1,6 +1,6 @@ -# The JavaScript language +# El lenguaje JavaScript -Here we learn JavaScript, starting from scratch and go on to advanced concepts like OOP. +Aquí aprenderemos JavaScript, empezando desde cero y llegaremos hasta conceptos avanzados como POO. -We concentrate on the language itself here, with the minimum of environment-specific notes. +Nos concentraremos en el lenguaje mismo con el mínimo de notas específicas del entorno. diff --git a/2-ui/1-document/01-browser-environment/article.md b/2-ui/1-document/01-browser-environment/article.md index eedc28fb3..553692ad8 100644 --- a/2-ui/1-document/01-browser-environment/article.md +++ b/2-ui/1-document/01-browser-environment/article.md @@ -1,113 +1,113 @@ -# Browser environment, specs +# Entorno del navegador, especificaciones -The JavaScript language was initially created for web browsers. Since then, it has evolved into a language with many uses and platforms. +El lenguaje JavaScript fue creado inicialmente para los navegadores web. Desde entonces, ha evolucionado en un lenguaje con muchos usos y plataformas. -A platform may be a browser, or a web-server or another *host*, or even a "smart" coffee machine if it can run JavaScript. Each of these provides platform-specific functionality. The JavaScript specification calls that a *host environment*. +Una plataforma puede ser un navegador, un servidor web u otro *host* ("anfitrión"); incluso una máquina de café "inteligente", si puede ejecutar JavaScript. Cada uno de ellos proporciona una funcionalidad específica de la plataforma. La especificación de JavaScript llama a esto *entorno de host*. -A host environment provides its own objects and functions in addition to the language core. Web browsers give a means to control web pages. Node.js provides server-side features, and so on. +Un entorno host proporciona sus propios objetos y funciones adicionales al núcleo del lenguaje. Los navegadores web proporcionan un medio para controlar las páginas web. Node.js proporciona características del lado del servidor, etc. -Here's a bird's-eye view of what we have when JavaScript runs in a web browser: +Aquí tienes una vista general de lo que tenemos cuando JavaScript se ejecuta en un navegador web: ![](windowObjects.svg) -There's a "root" object called `window`. It has two roles: +Hay un objeto "raíz" llamado `window`. Tiene dos roles: -1. First, it is a global object for JavaScript code, as described in the chapter . -2. Second, it represents the "browser window" and provides methods to control it. +1. Primero, es un objeto global para el código JavaScript, como se describe en el capítulo . +2. Segundo, representa la "ventana del navegador" y proporciona métodos para controlarla. -For instance, we can use it as a global object: +Por ejemplo, podemos usarlo como objeto global: ```js run global function sayHi() { - alert("Hello"); + alert("Hola"); } -// global functions are methods of the global object: +// Las funciones globales son métodos del objeto global: window.sayHi(); ``` -And we can use it as a browser window, to show the window height: +Y podemos usarlo como una ventana del navegador. Para ver la altura de la ventana: ```js run -alert(window.innerHeight); // inner window height +alert(window.innerHeight); // altura interior de la ventana ``` -There are more window-specific methods and properties, which we'll cover later. +Hay más métodos y propiedades específicos de `window`, los que cubriremos más adelante. -## DOM (Document Object Model) +## DOM (Modelo de Objetos del Documento) -The Document Object Model, or DOM for short, represents all page content as objects that can be modified. +Document Object Model, o DOM, representa todo el contenido de la página como objetos que pueden ser modificados. -The `document` object is the main "entry point" to the page. We can change or create anything on the page using it. +El objeto `document` es el punto de entrada a la página. Con él podemos cambiar o crear cualquier cosa en la página. -For instance: +Por ejemplo: ```js run -// change the background color to red +// cambiar el color de fondo a rojo document.body.style.background = "red"; -// change it back after 1 second +// deshacer el cambio después de 1 segundo setTimeout(() => document.body.style.background = "", 1000); ``` -Here, we used `document.body.style`, but there's much, much more. Properties and methods are described in the specification: [DOM Living Standard](https://dom.spec.whatwg.org). +Aquí usamos `document.body.style`, pero hay muchos, muchos más. Las propiedades y métodos se describen en la especificación: [DOM Living Standard](https://dom.spec.whatwg.org). -```smart header="DOM is not only for browsers" -The DOM specification explains the structure of a document and provides objects to manipulate it. There are non-browser instruments that use DOM too. +```smart header="DOM no es solo para navegadores" +La especificación DOM explica la estructura de un documento y proporciona objetos para manipularlo. Hay instrumentos que no son del navegador que también usan DOM. -For instance, server-side scripts that download HTML pages and process them can also use the DOM. They may support only a part of the specification though. +Por ejemplo, los scripts del lado del servidor que descargan páginas HTML y las procesan, también pueden usar DOM. Sin embargo, podrían admitir solamente parte de la especificación. ``` -```smart header="CSSOM for styling" -There's also a separate specification, [CSS Object Model (CSSOM)](https://www.w3.org/TR/cssom-1/) for CSS rules and stylesheets, that explains how they are represented as objects, and how to read and write them. +```smart header="CSSOM para los estilos" +También hay una especificación separada, [CSS Object Model (CSSOM)](https://www.w3.org/TR/cssom-1/) para las reglas y hojas de estilo CSS, que explica cómo se representan como objetos y cómo leerlos y escribirlos. -The CSSOM is used together with the DOM when we modify style rules for the document. In practice though, the CSSOM is rarely required, because we rarely need to modify CSS rules from JavaScript (usually we just add/remove CSS classes, not modify their CSS rules), but that's also possible. +CSSOM se usa junto con DOM cuando modificamos las reglas de estilo para el documento. Sin embargo, en la práctica rara vez se requiere CSSOM, porque rara vez necesitamos modificar las reglas CSS desde JavaScript (generalmente solo agregamos y eliminamos clases CSS, no modificamos sus reglas CSS), pero eso también es posible. ``` -## BOM (Browser Object Model) +## BOM (Modelo de Objetos del Navegador) -The Browser Object Model (BOM) represents additional objects provided by the browser (host environment) for working with everything except the document. +El Modelo de Objetos del Navegador (Browser Object Model, BOM) son objetos adicionales proporcionados por el navegador (entorno host) para trabajar con todo excepto el documento. -For instance: +Por ejemplo: -- The [navigator](mdn:api/Window/navigator) object provides background information about the browser and the operating system. There are many properties, but the two most widely known are: `navigator.userAgent` -- about the current browser, and `navigator.platform` -- about the platform (can help to differentiate between Windows/Linux/Mac etc). -- The [location](mdn:api/Window/location) object allows us to read the current URL and can redirect the browser to a new one. +- El objeto [navigator](https://developer.mozilla.org/es/docs/Web/API/Window/navigator) proporciona información sobre el navegador y el sistema operativo. Hay muchas propiedades, pero las dos más conocidas son: `navigator.userAgent`: acerca del navegador actual, y `navigator.platform`: acerca de la plataforma (ayuda a distinguir Windows/Linux/Mac, etc.). +- El objeto [location](https://developer.mozilla.org/es/docs/Web/API/Window/location) nos permite leer la URL actual y puede redirigir el navegador a una nueva. -Here's how we can use the `location` object: +Aquí vemos cómo podemos usar el objeto `location`: ```js run -alert(location.href); // shows current URL -if (confirm("Go to Wikipedia?")) { - location.href = "https://wikipedia.org"; // redirect the browser to another URL +alert(location.href); // muestra la URL actual +if (confirm("Ir a wikipedia?")) { + location.href = "https://wikipedia.org"; // redirigir el navegador a otra URL } ``` -The functions `alert/confirm/prompt` are also a part of the BOM: they are not directly related to the document, but represent pure browser methods for communicating with the user. +Las funciones `alert/confirm/prompt` también forman parte de BOM: no están directamente relacionadas con el documento, sino que representan métodos puros de comunicación del navegador con el usuario. -```smart header="Specifications" -The BOM is a part of the general [HTML specification](https://html.spec.whatwg.org). +```smart header="Especificaciones" +BOM es la parte general de la especificación de [HTML specification](https://html.spec.whatwg.org). -Yes, you heard that right. The HTML spec at is not only about the "HTML language" (tags, attributes), but also covers a bunch of objects, methods, and browser-specific DOM extensions. That's "HTML in broad terms". Also, some parts have additional specs listed at . +Sí, oíste bien. La especificación HTML en no solo trata sobre el "lenguaje HTML" (etiquetas, atributos), sino que también cubre un montón de objetos, métodos y extensiones DOM específicas del navegador. Eso es "HTML en términos generales". Además, algunas partes tienen especificaciones adicionales listadas en . ``` -## Summary +## Resumen -Talking about standards, we have: +En términos de estándares, tenemos: -DOM specification -: Describes the document structure, manipulations, and events, see . +La especificación del DOM +: Describe la estructura del documento, las manipulaciones y los eventos; consulte . -CSSOM specification -: Describes stylesheets and style rules, manipulations with them, and their binding to documents, see . +La especificación del CSSOM +: Describe las hojas de estilo y las reglas de estilo, las manipulaciones con ellas y su vínculo a los documentos. Consulte . -HTML specification -: Describes the HTML language (e.g. tags) and also the BOM (browser object model) -- various browser functions: `setTimeout`, `alert`, `location` and so on, see . It takes the DOM specification and extends it with many additional properties and methods. +La especificación del HTML +: Describe el lenguaje HTML (por ejemplo, etiquetas), y también el BOM (modelo de objeto del navegador) que describe varias funciones del navegador como `setTimeout`, `alert`, `location`, etc. Esta toma la especificación DOM y la extiende con muchas propiedades y métodos adicionales. Consulta . -Additionally, some classes are described separately at . +Adicionalmente, algunas clases son descritas separadamente en . -Please note these links, as there's so much to learn that it's impossible to cover everything and remember it all. +Ten en cuenta los enlaces anteriores, ya que hay tantas cosas que es imposible cubrir y recordar todo. -When you'd like to read about a property or a method, the Mozilla manual at is also a nice resource, but the corresponding spec may be better: it's more complex and longer to read, but will make your fundamental knowledge sound and complete. +Cuando desees leer sobre una propiedad o un método, el manual de Mozilla en es un buen recurso, pero leer las especificaciones correspondientes puede ser mejor: es más complejo y hay más para leer, pero hará que su conocimiento de los fundamentos sea sólido y completo. -To find something, it's often convenient to use an internet search "WHATWG [term]" or "MDN [term]", e.g , . +Para encontrar algo, a menudo es conveniente usar una búsqueda como "WHATWG [término]" o "MDN [término]". Por ejemplo , . -Now, we'll get down to learning the DOM, because the document plays the central role in the UI. +Ahora nos concentraremos en aprender el DOM, porque `document` juega el papel central en la interfaz de usuario. diff --git a/2-ui/1-document/02-dom-nodes/article.md b/2-ui/1-document/02-dom-nodes/article.md index f7f2be91d..226e118a2 100644 --- a/2-ui/1-document/02-dom-nodes/article.md +++ b/2-ui/1-document/02-dom-nodes/article.md @@ -4,35 +4,35 @@ libs: --- -# DOM tree +# Árbol del Modelo de Objetos del Documento (DOM) -The backbone of an HTML document is tags. +La estructura de un documento HTML son las etiquetas. -According to the Document Object Model (DOM), every HTML tag is an object. Nested tags are "children" of the enclosing one. The text inside a tag is an object as well. +Según el *Modelo de Objetos del Documento* (DOM), cada etiqueta HTML es un objeto. Las etiquetas anidadas son llamadas "hijas" de la etiqueta que las contiene. El texto dentro de una etiqueta también es un objeto. -All these objects are accessible using JavaScript, and we can use them to modify the page. +Todos estos objetos son accesibles empleando JavaScript, y podemos usarlos para modificar la página. -For example, `document.body` is the object representing the `` tag. +Por ejemplo, `document.body` es el objeto que representa la etiqueta ``. -Running this code will make the `` red for 3 seconds: +Ejecutar el siguiente código hará que el `` sea de color rojo durante 3 segundos: ```js run -document.body.style.background = 'red'; // make the background red +document.body.style.background = 'red'; // establece un color de fondo rojo -setTimeout(() => document.body.style.background = '', 3000); // return back +setTimeout(() => document.body.style.background = '', 3000); // volver atrás ``` -Here we used `style.background` to change the background color of `document.body`, but there are many other properties, such as: +En el caso anterior usamos `style.background` para cambiar el color de fondo del `document.body`, pero existen muchas otras propiedades, tales como: -- `innerHTML` -- HTML contents of the node. -- `offsetWidth` -- the node width (in pixels) -- ...and so on. +- `innerHTML` -- contenido HTML del nodo. +- `offsetWidth` -- ancho del nodo (en píxeles). +- ..., etc. -Soon we'll learn more ways to manipulate the DOM, but first we need to know about its structure. +Más adelante, aprenderemos otras formas de manipular el DOM, pero primero necesitamos conocer su estructura. -## An example of the DOM +## Un ejemplo del DOM -Let's start with the following simple document: +Comencemos con un documento simple: ```html run no-beautify @@ -46,7 +46,7 @@ Let's start with the following simple document: ``` -The DOM represents HTML as a tree structure of tags. Here's how it looks: +El DOM representa el HTML como una estructura de árbol de etiquetas. A continuación podemos ver cómo se muestra:
@@ -57,31 +57,31 @@ drawHtmlTree(node1, 'div.domtree', 690, 320); ```online -On the picture above, you can click on element nodes and their children will open/collapse. +En la imagen de arriba, puedes hacer clic sobre los nodos del elemento y como resultado se expanden/colapsan sus nodos hijos. ``` -Every tree node is an object. +Cada nodo del árbol es un objeto. -Tags are *element nodes* (or just elements) and form the tree structure: `` is at the root, then `` and `` are its children, etc. +Las etiquetas son *nodos de elementos* (o simplemente "elementos") y forman la estructura del árbol. `` está ubicado en la raíz del documento, por lo tanto, `` y `` son sus hijos, etc. -The text inside elements forms *text nodes*, labelled as `#text`. A text node contains only a string. It may not have children and is always a leaf of the tree. +El texto dentro de los elementos forma *nodos de texto*, y son etiquetados como `#text`. Un nodo de texto puede contener únicamente una cadena y no puede tener hijos, siempre es una hoja del árbol. -For instance, the `` tag has the text `"About elk"`. +Por ejemplo, la etiqueta `<title>` tiene el texto `"About elk"`. -Please note the special characters in text nodes: +Hay que tener en cuenta los caracteres especiales en nodos de texto: -- a newline: `↵` (in JavaScript known as `\n`) -- a space: `␣` +- una línea nueva: `↵` (en JavaScript se emplea `\n` para obtener este resultado) +- un espacio: `␣` -Spaces and newlines are totally valid characters, like letters and digits. They form text nodes and become a part of the DOM. So, for instance, in the example above the `<head>` tag contains some spaces before `<title>`, and that text becomes a `#text` node (it contains a newline and some spaces only). +Los espacios y líneas nuevas son caracteres totalmente válidos, al igual que letras y dígitos. Ellos forman nodos de texto y se convierten en parte del DOM. Así, por ejemplo, en el caso de arriba la etiqueta `<head>` contiene algunos espacios antes de la etiqueta `<title>`, entonces ese texto se convierte en el nodo `#text`, que contiene una nueva línea y solo algunos espacios. -There are only two top-level exclusions: -1. Spaces and newlines before `<head>` are ignored for historical reasons. -2. If we put something after `</body>`, then that is automatically moved inside the `body`, at the end, as the HTML spec requires that all content must be inside `<body>`. So there can't be any spaces after `</body>`. +Hay solo dos excepciones de nivel superior: +1. Los espacios y líneas nuevas ubicados antes de la etiqueta `<head>` son ignorados por razones históricas. +2. Si colocamos algo después de la etiqueta `</body>`, automáticamente se situará dentro de `body`, en el final, ya que la especificación HTML necesita que todo el contenido esté dentro de la etiqueta `<body>`. No puede haber espacios después de esta. -In other cases everything's straightforward -- if there are spaces (just like any character) in the document, then they become text nodes in the DOM, and if we remove them, then there won't be any. +En otros casos todo es sencillo: si hay espacios (como cualquier carácter) en el documento, se convierten en nodos de texto en el DOM; y si los eliminamos, entonces no habrá nodo. -Here are no space-only text nodes: +En el siguiente ejemplo, no hay nodos de texto con espacios en blanco: ```html no-beautify <!DOCTYPE HTML> @@ -96,21 +96,21 @@ let node2 = {"name":"HTML","nodeType":1,"children":[{"name":"HEAD","nodeType":1, drawHtmlTree(node2, 'div.domtree', 690, 210); </script> -```smart header="Spaces at string start/end and space-only text nodes are usually hidden in tools" -Browser tools (to be covered soon) that work with DOM usually do not show spaces at the start/end of the text and empty text nodes (line-breaks) between tags. +```smart header="Las herramientas, por lo general, ocultan los espacios al inicio/final de la cadena y los nodos de texto que solo contienen espacios en blanco" +Las herramientas del navegador (las veremos más adelante) que trabajan con el DOM usualmente no muestran espacios al inicio/final del texto ni los nodos de texto vacíos (saltos de línea) entre etiquetas. -Developer tools save screen space this way. +De esta manera ahorran espacio en la pantalla. -On further DOM pictures we'll sometimes omit them when they are irrelevant. Such spaces usually do not affect how the document is displayed. +En las próximas representaciones del DOM, las omitiremos cuando sean irrelevantes. Tales espacios generalmente no afectan la forma en la cual el documento es mostrado. ``` -## Autocorrection +## Autocorrección -If the browser encounters malformed HTML, it automatically corrects it when making the DOM. +Si el navegador encuentra HTML mal escrito, lo corrige automáticamente al construir el DOM. -For instance, the top tag is always `<html>`. Even if it doesn't exist in the document, it will exist in the DOM, because the browser will create it. The same goes for `<body>`. +Por ejemplo, la etiqueta superior siempre será `<html>`. Incluso si no existe en el documento, ésta existirá en el DOM, puesto que el navegador la creará. Sucede lo mismo con la etiqueta `<body>`. -As an example, if the HTML file is the single word `"Hello"`, the browser will wrap it into `<html>` and `<body>`, and add the required `<head>`, and the DOM will be: +Como ejemplo de esto, si el archivo HTML es la palabra `"Hello"`, el navegador lo envolverá con las etiquetas `<html>` y `<body>`, y añadirá la etiqueta `<head>` la cual es requerida. Basado en esto, el DOM resultante será: <div class="domtree"></div> @@ -121,9 +121,9 @@ let node3 = {"name":"HTML","nodeType":1,"children":[{"name":"HEAD","nodeType":1, drawHtmlTree(node3, 'div.domtree', 690, 150); </script> -While generating the DOM, browsers automatically process errors in the document, close tags and so on. +Al generar el DOM, los navegadores procesan automáticamente los errores en el documento, cierran etiquetas, etc. -A document with unclosed tags: +Un documento sin etiquetas de cierre: ```html no-beautify <p>Hello @@ -132,7 +132,7 @@ A document with unclosed tags: <li>Dad ``` -...will become a normal DOM as the browser reads tags and restores the missing parts: +...se convertirá en un DOM normal a medida que el navegador lee las etiquetas y compone las partes faltantes: <div class="domtree"></div> @@ -142,16 +142,16 @@ let node4 = {"name":"HTML","nodeType":1,"children":[{"name":"HEAD","nodeType":1, drawHtmlTree(node4, 'div.domtree', 690, 360); </script> -````warn header="Tables always have `<tbody>`" -An interesting "special case" is tables. By DOM specification they must have `<tbody>` tag, but HTML text may omit it. Then the browser creates `<tbody>` in the DOM automatically. +````warn header="Las tablas siempre tienen la etiqueta `<tbody>`" +Un caso especial interesante son las tablas. De acuerdo a la especificación DOM deben tener la etiqueta `<tbody>`, sin embargo el texto HTML puede omitirla: el navegador crea automáticamente la etiqueta `<tbody>` en el DOM. -For the HTML: +Para el HTML: ```html no-beautify <table id="table"><tr><td>1</td></tr></table> ``` -DOM-structure will be: +La estructura del DOM será: <div class="domtree"></div> <script> @@ -160,14 +160,14 @@ let node5 = {"name":"TABLE","nodeType":1,"children":[{"name":"TBODY","nodeType": drawHtmlTree(node5, 'div.domtree', 600, 200); </script> -You see? The `<tbody>` appeared out of nowhere. We should keep this in mind while working with tables to avoid surprises. +¿Lo ves? La etiqueta `<tbody>` apareció de la nada. Debemos tener esto en cuenta al trabajar con tablas para evitar sorpresas. ```` -## Other node types +## Otros tipos de nodos -There are some other node types besides elements and text nodes. +Existen otros tipos de nodos además de elementos y nodos de texto. -For example, comments: +Por ejemplo, los comentarios: ```html <!DOCTYPE HTML> @@ -177,9 +177,9 @@ For example, comments: <ol> <li>An elk is a smart</li> *!* - <!-- comment --> + <!-- comentario --> */!* - <li>...and cunning animal!</li> + <li>...y el astuto animal!</li> </ol> </body> </html> @@ -193,90 +193,90 @@ let node6 = {"name":"HTML","nodeType":1,"children":[{"name":"HEAD","nodeType":1, drawHtmlTree(node6, 'div.domtree', 690, 500); </script> -We can see here a new tree node type -- *comment node*, labeled as `#comment`, between two text nodes. +Aquí podemos ver un nuevo tipo de nodo: *nodo de comentario*, etiquetado como `#comment`, entre dos nodos de texto. -We may think -- why is a comment added to the DOM? It doesn't affect the visual representation in any way. But there's a rule -- if something's in HTML, then it also must be in the DOM tree. +Podemos pensar: ¿Por qué se agrega un comentario al DOM? Esto no afecta la representación de ninguna manera. Pero hay una regla: si algo está en el código HTML, entonces también debe estar en el árbol DOM. -**Everything in HTML, even comments, becomes a part of the DOM.** +**Todo en HTML, incluso los comentarios, se convierte en parte del DOM.** -Even the `<!DOCTYPE...>` directive at the very beginning of HTML is also a DOM node. It's in the DOM tree right before `<html>`. Few people know about that. We are not going to touch that node, we even don't draw it on diagrams, but it's there. +Hasta la declaración `<!DOCTYPE...>` al principio del HTML es un nodo del DOM. Su ubicación en el DOM es justo antes de la etiqueta `<html>`. No vamos a tocar ese nodo, por esa razón ni siquiera lo dibujamos en diagramas, pero esta ahí. -The `document` object that represents the whole document is, formally, a DOM node as well. +El objeto `document` que representa todo el documento es también, formalmente, un nodo DOM. -There are [12 node types](https://dom.spec.whatwg.org/#node). In practice we usually work with 4 of them: +Hay [12 tipos de nodos](https://dom.spec.whatwg.org/#node). En la práctica generalmente trabajamos con 4 de ellos: -1. `document` -- the "entry point" into DOM. -2. element nodes -- HTML-tags, the tree building blocks. -3. text nodes -- contain text. -4. comments -- sometimes we can put information there, it won't be shown, but JS can read it from the DOM. +1. `document` -- el "punto de entrada" en el DOM. +2. nodos de elementos -- Etiquetas-HTML, los bloques de construcción del árbol. +3. nodos de texto -- contienen texto. +4. comentarios -- Podríamos colocar información allí. No se mostrará, pero JS puede leerla desde el DOM. -## See it for yourself +## Véalo usted mismo -To see the DOM structure in real-time, try [Live DOM Viewer](https://software.hixie.ch/utilities/js/live-dom-viewer/). Just type in the document, and it will show up as a DOM at an instant. +Para ver la estructura del DOM en tiempo real, intente [Live DOM Viewer](https://software.hixie.ch/utilities/js/live-dom-viewer/). Simplemente escriba el documento, y se mostrará como un DOM al instante. -Another way to explore the DOM is to use the browser developer tools. Actually, that's what we use when developing. +Otra forma de explorar el DOM es usando la herramienta para desarrolladores del navegador. En realidad, eso es lo que usamos cuando estamos desarrollando. -To do so, open the web page [elk.html](elk.html), turn on the browser developer tools and switch to the Elements tab. +Para hacerlo, abra la página web [elk.html](elk.html), active las herramientas para desarroladores del navegador y cambie la pestaña a elementos. -It should look like this: +Debe verse así: ![](elk.svg) -You can see the DOM, click on elements, see their details and so on. +Puedes ver el DOM, hacer clic sobre los elementos, ver sus detalles, etc. -Please note that the DOM structure in developer tools is simplified. Text nodes are shown just as text. And there are no "blank" (space only) text nodes at all. That's fine, because most of the time we are interested in element nodes. +Tenga en cuenta que la estructura DOM en la herramienta para desarrolladores está simplificada. Los nodos de texto se muestran como texto. Y no hay nodos de texto con espacios en blanco en absoluto. Esto es aceptable, porque la mayoría de las veces nos interesan los nodos de elementos. -Clicking the <span class="devtools" style="background-position:-328px -124px"></span> button in the left-upper corner allows us to choose a node from the webpage using a mouse (or other pointer devices) and "inspect" it (scroll to it in the Elements tab). This works great when we have a huge HTML page (and corresponding huge DOM) and would like to see the place of a particular element in it. +Hacer clic en el botón <span class="devtools" style="background-position:-328px -124px"></span> ubicado en la esquina superior izquierda nos permite elegir un nodo desde la página web utilizando un "mouse" (u otros dispositivos de puntero) e "inspeccionar" (desplazarse hasta él en la pestaña elementos). Esto funciona muy bien cuando tenemos una página HTML enorme (y el DOM correspondiente es enorme) y nos gustaría ver la posición de un elemento en particular. -Another way to do it would be just right-clicking on a webpage and selecting "Inspect" in the context menu. +Otra forma de realizarlo sería hacer clic derecho en la página web y en el menú contextual elegir la opción "Inspeccionar Elemento". ![](inspect.svg) -At the right part of the tools there are the following subtabs: -- **Styles** -- we can see CSS applied to the current element rule by rule, including built-in rules (gray). Almost everything can be edited in-place, including the dimensions/margins/paddings of the box below. -- **Computed** -- to see CSS applied to the element by property: for each property we can see a rule that gives it (including CSS inheritance and such). -- **Event Listeners** -- to see event listeners attached to DOM elements (we'll cover them in the next part of the tutorial). -- ...and so on. +En la parte derecha de las herramientas encontramos las siguientes sub-pestañas: +- **Styles** -- podemos ver CSS aplicado al elemento actual regla por regla, incluidas las reglas integradas (gris). Casi todo puede ser editado en el lugar, incluyendo las dimensiones/márgenes/relleno de la siguiente caja. +- **Computed** -- nos permite ver cada propiedad CSS aplicada al elemento: para cada propiedad podemos ver la regla que la provee (incluida la herencia CSS y demás). +- **Event Listeners** -- nos ayuda a ver los "escuchadores de eventos" adosados a elementos del DOM (los cubriremos en la siguiente parte del tutorial). +- ...,etc. -The best way to study them is to click around. Most values are editable in-place. +La mejor manera de estudiarlos es haciendo clic en ellos. Casi todos los valores son editables en el lugar. -## Interaction with console +## Interacción con la consola -As we work the DOM, we also may want to apply JavaScript to it. Like: get a node and run some code to modify it, to see the result. Here are few tips to travel between the Elements tab and the console. +A medida que trabajamos con el DOM, también podemos querer aplicarle JavaScript. Por ejemplo, obtener un nodo y ejecutar algún código para modificarlo y ver el resultado. Aquí hay algunos consejos para desplazarse entre la pestaña elementos y la consola. -For the start: +Para empezar: -1. Select the first `<li>` in the Elements tab. -2. Press `key:Esc` -- it will open console right below the Elements tab. +1. Seleccione el primer elemento `<li>` en la pestaña elementos. +2. Presiona `key:Esc`. Esto abrirá la consola justo debajo de la pestaña de elementos. -Now the last selected element is available as `$0`, the previously selected is `$1` etc. +Ahora el último elemento seleccionado esta disponible como `$0`, el seleccionado previamente es `$1`, etc. -We can run commands on them. For instance, `$0.style.background = 'red'` makes the selected list item red, like this: +Podemos ejecutar comandos en ellos. Por ejemplo, `$0.style.background = 'red'` hace que el elemento de la lista seleccionado sea rojo, algo así: ![](domconsole0.svg) -That's how to get a node from Elements in Console. +Así es como se obtiene un nodo de los elementos en la consola. -There's also a road back. If there's a variable referencing a DOM node, then we can use the command `inspect(node)` in Console to see it in the Elements pane. +También está el camino inverso. Si hay una variable que hace referencia a un nodo del DOM, usamos el comando `inspect(node)` en la consola para verlo en el panel de elementos. -Or we can just output the DOM node in the console and explore "in-place", like `document.body` below: +O simplemente podemos imprimir el nodo del DOM en la consola y explorarlo en el lugar, tal como `document.body` a continuación: ![](domconsole1.svg) -That's for debugging purposes of course. From the next chapter on we'll access and modify DOM using JavaScript. +Desde luego, eso es para propósitos de depuración. A partir del siguiente capítulo accederemos y modificaremos el DOM usando JavaScript. -The browser developer tools are a great help in development: we can explore the DOM, try things and see what goes wrong. +Las herramientas para desarrolladores del navegador son de mucha ayuda en el desarrollo: podemos explorar el DOM, probar cosas y ver qué sale mal. -## Summary +## Resumen -An HTML/XML document is represented inside the browser as the DOM tree. +Un documento HTML/XML esta representado dentro del navegador como un árbol de nodos (DOM). -- Tags become element nodes and form the structure. -- Text becomes text nodes. -- ...etc, everything in HTML has its place in DOM, even comments. +- Las etiquetas se convierten en nodos de elemento y forman la estructura. +- El texto se convierte en nodos de texto. +- ...etc, todos los elementos de HTML tienen su lugar en el DOM, incluso los comentarios. -We can use developer tools to inspect DOM and modify it manually. +Podemos utilizar las herramientas para desarrolladores para inspeccionar el DOM y modificarlo manualmente. -Here we covered the basics, the most used and important actions to start with. There's an extensive documentation about Chrome Developer Tools at <https://developers.google.com/web/tools/chrome-devtools>. The best way to learn the tools is to click here and there, read menus: most options are obvious. Later, when you know them in general, read the docs and pick up the rest. +Aquí hemos cubierto los conceptos básicos, las acciones más importantes y utilizadas para comenzar. Hay una extensa documentación acerca de las herramientas para desarrolladores de Chrome en <https://developers.google.com/web/tools/chrome-devtools>. La mejor forma de aprender a usar las herramientas es hacer clic en ellas, leer los menús: la mayoría de las opciones son obvias. Más adelante, cuando tenga conocimiento general sobre ellas, lea la documentación y elija el resto. -DOM nodes have properties and methods that allow us to travel between them, modify them, move around the page, and more. We'll get down to them in the next chapters. +Los nodos del DOM tienen propiedades y métodos que nos permiten desplazarnos entre ellos, modificarlos, movernos por la página, y más. Empezaremos a realizar todo esto en los siguientes capítulos. diff --git a/2-ui/1-document/03-dom-navigation/1-dom-children/solution.md b/2-ui/1-document/03-dom-navigation/1-dom-children/solution.md index decfa62c7..7f62da711 100644 --- a/2-ui/1-document/03-dom-navigation/1-dom-children/solution.md +++ b/2-ui/1-document/03-dom-navigation/1-dom-children/solution.md @@ -1,27 +1,27 @@ -There are many ways, for instance: +Hay muchas maneras, por ejemplo: -The `<div>` DOM node: +El nodo `<div>` del DOM: ```js document.body.firstElementChild -// or +// o document.body.children[0] -// or (the first node is space, so we take 2nd) +// o (el primer nodo es un espacio, así que tomamos el segundo) document.body.childNodes[1] ``` -The `<ul>` DOM node: +El nodo `<ul>` del DOM: ```js document.body.lastElementChild -// or +// o document.body.children[1] ``` -The second `<li>` (with Pete): +El segundo `<li>` (con Pete): ```js -// get <ul>, and then get its last element child +// obtener <ul>, y luego obtener su último elemento hijo document.body.lastElementChild.lastElementChild ``` diff --git a/2-ui/1-document/03-dom-navigation/1-dom-children/task.md b/2-ui/1-document/03-dom-navigation/1-dom-children/task.md index d97f2748a..c512b2b68 100644 --- a/2-ui/1-document/03-dom-navigation/1-dom-children/task.md +++ b/2-ui/1-document/03-dom-navigation/1-dom-children/task.md @@ -4,7 +4,7 @@ importance: 5 # DOM children -Look at this page: +Mira esta página: ```html <html> @@ -18,7 +18,7 @@ Look at this page: </html> ``` -For each of the following, give at least one way of how to access them: -- The `<div>` DOM node? -- The `<ul>` DOM node? -- The second `<li>` (with Pete)? +Para cada una de las siguientes preguntas, da al menos una forma de cómo acceder a ellos: +- ¿El nodo `<div>` del DOM? +- ¿El nodo `<ul>` del DOM? +- El segundo `<li>` (con Pete)? diff --git a/2-ui/1-document/03-dom-navigation/3-navigation-links-which-null/solution.md b/2-ui/1-document/03-dom-navigation/3-navigation-links-which-null/solution.md index d76936320..e8fb616aa 100644 --- a/2-ui/1-document/03-dom-navigation/3-navigation-links-which-null/solution.md +++ b/2-ui/1-document/03-dom-navigation/3-navigation-links-which-null/solution.md @@ -1,6 +1,6 @@ -1. Yes, true. The element `elem.lastChild` is always the last one, it has no `nextSibling`. -2. No, wrong, because `elem.children[0]` is the first child *among elements*. But there may exist non-element nodes before it. So `previousSibling` may be a text node. +1. Sí, verdadero. El elemento `elem.lastChild` siempre es el último, no tiene `nextSibling`. +2. No, falso. `elem.children[0]` es el primer hijo *entre elementos*, pero pueden existir nodos que no son elementos antes que él. `previousSibling` puede ser un nodo texto. -Please note: for both cases if there are no children, then there will be an error. +Ten en cuenta: para ambos casos, si no hay hijos habrá un error. -If there are no children, `elem.lastChild` is `null`, so we can't access `elem.lastChild.nextSibling`. And the collection `elem.children` is empty (like an empty array `[]`). +Si no hay hijos, `elem.lastChild` es `null`, entonces no podemos acceder a `elem.lastChild.nextSibling`. Y la colección `elem.children` es vacía (como un array vacío `[]`). diff --git a/2-ui/1-document/03-dom-navigation/3-navigation-links-which-null/task.md b/2-ui/1-document/03-dom-navigation/3-navigation-links-which-null/task.md index 235e83a0c..950398a7e 100644 --- a/2-ui/1-document/03-dom-navigation/3-navigation-links-which-null/task.md +++ b/2-ui/1-document/03-dom-navigation/3-navigation-links-which-null/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# The sibling question +# La pregunta de los hermanos -If `elem` -- is an arbitrary DOM element node... +Si `elem` -- es un elemento nodo arbitrario del DOM... -- Is it true that `elem.lastChild.nextSibling` is always `null`? -- Is it true that `elem.children[0].previousSibling` is always `null` ? +- ¿Es cierto que `elem.lastChild.nextSibling` siempre es `null`? +- ¿Es cierto que `elem.children[0].previousSibling` siempre es `null` ? diff --git a/2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/solution.md b/2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/solution.md index f2aa86302..94359d1a2 100644 --- a/2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/solution.md +++ b/2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/solution.md @@ -1 +1 @@ -We'll be using `rows` and `cells` properties to access diagonal table cells. +Usaremos las propiedades de las `filas` y las `celdas` para acceder a las celdas de la tabla diagonal diff --git a/2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/source.view/index.html b/2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/source.view/index.html index ad835f9f6..90441bf05 100644 --- a/2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/source.view/index.html +++ b/2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/source.view/index.html @@ -54,7 +54,7 @@ <script> let table = document.body.firstElementChild; - // your code + // ... tu código... </script> </body> </html> diff --git a/2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/task.md b/2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/task.md index 23be59fc1..dab6493c6 100644 --- a/2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/task.md +++ b/2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/task.md @@ -2,17 +2,17 @@ importance: 5 --- -# Select all diagonal cells +# Seleccionar todas las celdas diagonales -Write the code to paint all diagonal table cells in red. +Escribe el código para pintar todas las celdas diagonales de rojo. -You'll need to get all diagonal `<td>` from the `<table>` and paint them using the code: +Necesitarás obtener todas las `<td>` de la `<table>` y pintarlas usando el código: ```js -// td should be the reference to the table cell +// td debe ser la referencia a la celda de la tabla td.style.backgroundColor = 'red'; ``` -The result should be: +El resultado debe ser: [iframe src="solution" height=180] diff --git a/2-ui/1-document/03-dom-navigation/article.md b/2-ui/1-document/03-dom-navigation/article.md index b5f03098c..5cec620b2 100644 --- a/2-ui/1-document/03-dom-navigation/article.md +++ b/2-ui/1-document/03-dom-navigation/article.md @@ -5,37 +5,37 @@ libs: --- -# Walking the DOM +# Recorriendo el DOM -The DOM allows us to do anything with elements and their contents, but first we need to reach the corresponding DOM object. +El DOM nos permite hacer cualquier cosa con sus elementos y contenidos, pero lo primero que tenemos que hacer es llegar al objeto correspondiente del DOM. -All operations on the DOM start with the `document` object. That's the main "entry point" to DOM. From it we can access any node. +Todas las operaciones en el DOM comienzan con el objeto `document`. Este es el principal "punto de entrada" al DOM. Desde ahí podremos acceder a cualquier nodo. -Here's a picture of links that allow for travel between DOM nodes: +Esta imagen representa los enlaces que nos permiten viajar a través de los nodos del DOM: ![](dom-links.svg) -Let's discuss them in more detail. +Vamos a analizarlos con más detalle. -## On top: documentElement and body +## En la parte superior: documentElement y body -The topmost tree nodes are available directly as `document` properties: +Los tres nodos superiores están disponibles como propiedades de `document`: `<html>` = `document.documentElement` -: The topmost document node is `document.documentElement`. That's the DOM node of the `<html>` tag. +: El nodo superior del documento es `document.documentElement`. Este es el nodo del DOM para la etiqueta `<html>`. `<body>` = `document.body` -: Another widely used DOM node is the `<body>` element -- `document.body`. +: Otro nodo muy utilizado es el elemento `<body>` -- `document.body`. `<head>` = `document.head` -: The `<head>` tag is available as `document.head`. +: La etiqueta `<head>` está disponible como `document.head`. -````warn header="There's a catch: `document.body` can be `null`" -A script cannot access an element that doesn't exist at the moment of running. +````warn header="Hay una trampa: `document.body` puede ser `null`" +Un script no puede acceder a un elemento que no existe en el momento de su ejecución. -In particular, if a script is inside `<head>`, then `document.body` is unavailable, because the browser did not read it yet. +Por ejemplo, si un script está dentro de `<head>`, entonces `document.body` no está disponible, porque el navegador no lo ha leído aún. -So, in the example below the first `alert` shows `null`: +Entonces, en el siguiente ejemplo `alert` muestra `null`: ```html run <html> @@ -43,7 +43,7 @@ So, in the example below the first `alert` shows `null`: <head> <script> *!* - alert( "From HEAD: " + document.body ); // null, there's no <body> yet + alert( "From HEAD: " + document.body ); // null, no hay <body> aún */!* </script> </head> @@ -51,7 +51,7 @@ So, in the example below the first `alert` shows `null`: <body> <script> - alert( "From BODY: " + document.body ); // HTMLBodyElement, now it exists + alert( "From BODY: " + document.body ); // HTMLBodyElement, ahora existe </script> </body> @@ -59,18 +59,18 @@ So, in the example below the first `alert` shows `null`: ``` ```` -```smart header="In the DOM world `null` means \"doesn't exist\"" -In the DOM, the `null` value means "doesn't exist" or "no such node". +```smart header="En el mundo del DOM `null` significa \"no existe\"" +En el DOM, el valor `null` significa que "no existe" o "no hay tal nodo". ``` -## Children: childNodes, firstChild, lastChild +## Hijos: childNodes, firstChild, lastChild -There are two terms that we'll use from now on: +Existen dos términos que vamos a utilizar de ahora en adelante: -- **Child nodes (or children)** -- elements that are direct children. In other words, they are nested exactly in the given one. For instance, `<head>` and `<body>` are children of `<html>` element. -- **Descendants** -- all elements that are nested in the given one, including children, their children and so on. +- **Nodos hijos (childNodes)** -- elementos que son hijos directos, es decir sus descendientes inmediatos. Por ejemplo, `<head>` y `<body>` son hijos del elemento `<html>`. +- **Descendientes** -- todos los elementos anidados de un elemento dado, incluyendo los hijos, sus hijos y así sucesivamente. -For instance, here `<body>` has children `<div>` and `<ul>` (and few blank text nodes): +Por ejemplo, aquí `<body>` tiene de hijos `<div>` y `<ul>` (y unos pocos nodos de texto en blanco): ```html run <html> @@ -86,11 +86,11 @@ For instance, here `<body>` has children `<div>` and `<ul>` (and few blank text </html> ``` -...And descendants of `<body>` are not only direct children `<div>`, `<ul>` but also more deeply nested elements, such as `<li>` (a child of `<ul>`) and `<b>` (a child of `<li>`) -- the entire subtree. +...Y los descendientes de `<body>` no son solo los hijos `<div>`, `<ul>` sino también elementos anidados más profundamente, como `<li>` (un hijo de `<ul>`) o `<b>` (un hijo de `<li>`) -- el subárbol entero. -**The `childNodes` collection lists all child nodes, including text nodes.** +**La colección `childNodes` enumera todos los nodos hijos, incluidos los nodos de texto.** -The example below shows children of `document.body`: +El ejemplo inferior muestra todos los hijos de `document.body`: ```html run <html> @@ -106,85 +106,85 @@ The example below shows children of `document.body`: <script> *!* for (let i = 0; i < document.body.childNodes.length; i++) { - alert( document.body.childNodes[i] ); // Text, DIV, Text, UL, ..., SCRIPT + alert( document.body.childNodes[i] ); // Texto, DIV, Texto, UL, ..., SCRIPT } */!* </script> - ...more stuff... + ...más cosas... </body> </html> ``` -Please note an interesting detail here. If we run the example above, the last element shown is `<script>`. In fact, the document has more stuff below, but at the moment of the script execution the browser did not read it yet, so the script doesn't see it. +Por favor observa un interesante detalle aquí. Si ejecutamos el ejemplo anterior, el último elemento que se muestra es `<script>`. De hecho, el documento tiene más cosas debajo, pero en el momento de ejecución del script el navegador todavía no lo ha leído, por lo que el script no lo ve. -**Properties `firstChild` and `lastChild` give fast access to the first and last children.** +**Las propiedades `firstChild` y `lastChild` dan acceso rápido al primer y al último hijo.** -They are just shorthands. If there exist child nodes, then the following is always true: +Son solo atajos. Si existieran nodos hijos, la respuesta siguiente sería siempre verdadera: ```js elem.childNodes[0] === elem.firstChild elem.childNodes[elem.childNodes.length - 1] === elem.lastChild ``` -There's also a special function `elem.hasChildNodes()` to check whether there are any child nodes. +También hay una función especial `elem.hasChildNodes()` para comprobar si hay algunos nodos hijos. -### DOM collections +### Colecciones del DOM -As we can see, `childNodes` looks like an array. But actually it's not an array, but rather a *collection* -- a special array-like iterable object. +Como podemos ver, `childNodes` parece un array. Pero realmente no es un array, sino más bien una *colección* -- un objeto especial iterable, simil-array. -There are two important consequences: +Hay dos importantes consecuencias de esto: -1. We can use `for..of` to iterate over it: +1. Podemos usar `for..of` para iterar sobre él: ```js for (let node of document.body.childNodes) { - alert(node); // shows all nodes from the collection + alert(node); // enseña todos los nodos de la colección } ``` - That's because it's iterable (provides the `Symbol.iterator` property, as required). + Eso es porque es iterable (proporciona la propiedad `Symbol.iterator`, como se requiere). -2. Array methods won't work, because it's not an array: +2. Los métodos de Array no funcionan, porque no es un array: ```js run - alert(document.body.childNodes.filter); // undefined (there's no filter method!) + alert(document.body.childNodes.filter); // undefined (¡No hay método filter!) ``` -The first thing is nice. The second is tolerable, because we can use `Array.from` to create a "real" array from the collection, if we want array methods: +La primera consecuencia es agradable. La segunda es tolerable, porque podemos usar `Array.from` para crear un array "real" desde la colección si es que queremos usar métodos del array: ```js run - alert( Array.from(document.body.childNodes).filter ); // function + alert( Array.from(document.body.childNodes).filter ); // función ``` -```warn header="DOM collections are read-only" -DOM collections, and even more -- *all* navigation properties listed in this chapter are read-only. +```warn header="Las colecciones DOM son solo de lectura" +Las colecciones DOM, incluso más-- *todas* las propiedades de navegación enumeradas en este capítulo son sólo de lectura. -We can't replace a child by something else by assigning `childNodes[i] = ...`. +No podemos reemplazar a un hijo por otro elemento asignándolo así `childNodes[i] = ...`. -Changing DOM needs other methods. We will see them in the next chapter. +Cambiar el DOM necesita otros métodos. Los veremos en el siguiente capítulo. ``` -```warn header="DOM collections are live" -Almost all DOM collections with minor exceptions are *live*. In other words, they reflect the current state of DOM. +```warn header="Las colecciones del DOM están vivas" +Casi todas las colecciones del DOM, salvo algunas excepciones, están *vivas*. En otras palabras, reflejan el estado actual del DOM. -If we keep a reference to `elem.childNodes`, and add/remove nodes into DOM, then they appear in the collection automatically. +Si mantenemos una referencia a `elem.childNodes`, y añadimos o quitamos nodos del DOM, entonces estos nodos aparecen en la colección automáticamente. ``` -````warn header="Don't use `for..in` to loop over collections" -Collections are iterable using `for..of`. Sometimes people try to use `for..in` for that. +````warn header="No uses `for..in` para recorrer colecciones" +Las colecciones son iterables usando `for..of`. Hay quienes tratan de utilizar `for..in` para eso. -Please, don't. The `for..in` loop iterates over all enumerable properties. And collections have some "extra" rarely used properties that we usually do not want to get: +Por favor, no lo hagas. El bucle `for..in` itera sobre todas las propiedades enumerables. Y las colecciones tienen unas propiedades "extra" raramente usadas que normalmente no queremos obtener: ```html run <body> <script> - // shows 0, 1, length, item, values and more. + // enseña 0, 1, longitud, item, valores y más cosas. for (let prop in document.body.childNodes) alert(prop); </script> </body> ```` -## Siblings and the parent +## Hermanos y el padre -*Siblings* are nodes that are children of the same parent. +*Los hermanos* son nodos que son hijos del mismo padre. -For instance, here `<head>` and `<body>` are siblings: +Por ejemplo, aquí `<head>` y `<body>` son hermanos: ```html <html> @@ -192,64 +192,64 @@ For instance, here `<head>` and `<body>` are siblings: </html> ``` -- `<body>` is said to be the "next" or "right" sibling of `<head>`, -- `<head>` is said to be the "previous" or "left" sibling of `<body>`. +- `<body>` se dice que es el hermano "siguiente" o a la "derecha" de `<head>`, +- `<head>` se dice que es el hermano "anterior" o a la "izquierda" de `<body>`. -The next sibling is in `nextSibling` property, and the previous one - in `previousSibling`. +El hermano siguiente está en la propiedad `nextSibling` y el anterior - en `previousSibling`. -The parent is available as `parentNode`. +El padre está disponible en `parentNode`. -For example: +Por ejemplo: ```js run -// parent of <body> is <html> -alert( document.body.parentNode === document.documentElement ); // true +// el padre de <body> es <html> +alert( document.body.parentNode === document.documentElement ); // verdadero -// after <head> goes <body> +// después de <head> va <body> alert( document.head.nextSibling ); // HTMLBodyElement -// before <body> goes <head> +// antes de <body> va <head> alert( document.body.previousSibling ); // HTMLHeadElement ``` -## Element-only navigation +## Navegación solo por elementos -Navigation properties listed above refer to *all* nodes. For instance, in `childNodes` we can see both text nodes, element nodes, and even comment nodes if they exist. +Las propiedades de navegación enumeradas abajo se refieren a *todos* los nodos. Por ejemplo, en `childNodes` podemos ver nodos de texto, nodos elementos; y si existen, incluso los nodos de comentarios. -But for many tasks we don't want text or comment nodes. We want to manipulate element nodes that represent tags and form the structure of the page. +Pero para muchas tareas no queremos los nodos de texto o comentarios. Queremos manipular el nodo que representa las etiquetas y formularios de la estructura de la página. -So let's see more navigation links that only take *element nodes* into account: +Así que vamos a ver más enlaces de navegación que solo tienen en cuenta los *elementos nodos*: ![](dom-links-elements.svg) -The links are similar to those given above, just with `Element` word inside: +Los enlaces son similares a los de arriba, solo que tienen dentro la palabra `Element`: -- `children` -- only those children that are element nodes. -- `firstElementChild`, `lastElementChild` -- first and last element children. -- `previousElementSibling`, `nextElementSibling` -- neighbor elements. -- `parentElement` -- parent element. +- `children` -- solo esos hijos que tienen el elemento nodo. +- `firstElementChild`, `lastElementChild` -- el primer y el último elemento hijo. +- `previousElementSibling`, `nextElementSibling` -- elementos vecinos. +- `parentElement` -- elemento padre. -````smart header="Why `parentElement`? Can the parent be *not* an element?" -The `parentElement` property returns the "element" parent, while `parentNode` returns "any node" parent. These properties are usually the same: they both get the parent. +````smart header="¿Por qué `parentElement`? ¿Puede el padre *no* ser un elemento?" +La propiedad `parentElement` devuelve el "elemento" padre, mientras `parentNode` devuelve "cualquier nodo" padre. Estas propiedades son normalmente las mismas: ambas seleccionan el padre. -With the one exception of `document.documentElement`: +Con la excepción de `document.documentElement`: ```js run alert( document.documentElement.parentNode ); // document alert( document.documentElement.parentElement ); // null ``` -The reason is that the root node `document.documentElement` (`<html>`) has `document` as its parent. But `document` is not an element node, so `parentNode` returns it and `parentElement` does not. +La razón es que el nodo raíz `document.documentElement` (`<html>`) tiene a `document` como su padre. Pero `document` no es un elemento nodo, por lo que `parentNode` lo devuelve y `parentElement` no lo hace. -This detail may be useful when we want to travel up from an arbitrary element `elem` to `<html>`, but not to the `document`: +Este detalle puede ser útil cuando queramos navegar hacia arriba desde cualquier elemento `elem` al `<html>`, pero no hacia el `document`: ```js -while(elem = elem.parentElement) { // go up till <html> +while(elem = elem.parentElement) { // sube hasta <html> alert( elem ); } ``` ```` -Let's modify one of the examples above: replace `childNodes` with `children`. Now it shows only elements: +Vamos a modificar uno de los ejemplos de arriba: reemplaza `childNodes` por `children`. Ahora enseña solo elementos: ```html run <html> @@ -274,31 +274,31 @@ Let's modify one of the examples above: replace `childNodes` with `children`. No </html> ``` -## More links: tables [#dom-navigation-tables] +## Más enlaces: tablas [#dom-navigation-tables] -Till now we described the basic navigation properties. +Hasta ahora hemos descrito las propiedades de navegación básicas. -Certain types of DOM elements may provide additional properties, specific to their type, for convenience. +Ciertos tipos de elementos del DOM pueden tener propiedades adicionales, específicas de su tipo, por conveniencia. -Tables are a great example of that, and represent a particularly important case: +Las tablas son un gran ejemplo de ello, y representan un particular caso importante: -**The `<table>`** element supports (in addition to the given above) these properties: -- `table.rows` -- the collection of `<tr>` elements of the table. -- `table.caption/tHead/tFoot` -- references to elements `<caption>`, `<thead>`, `<tfoot>`. -- `table.tBodies` -- the collection of `<tbody>` elements (can be many according to the standard, but there will always be at least one -- even if it is not in the source HTML, the browser will put it in the DOM). +**El elemento `<table>`** soporta estas propiedades (añadidas a las que hemos dado anteriormente): +- `table.rows` -- la colección de elementos`<tr>` de la tabla. +- `table.caption/tHead/tFoot` -- referencias a los elementos `<caption>`, `<thead>`, `<tfoot>`. +- `table.tBodies` -- la colección de elementos `<tbody>` (pueden ser muchos según el estándar, pero siempre habrá al menos uno, aunque no esté en el HTML el navegador lo pondrá en el DOM). -**`<thead>`, `<tfoot>`, `<tbody>`** elements provide the `rows` property: -- `tbody.rows` -- the collection of `<tr>` inside. +**`<thead>`, `<tfoot>`, `<tbody>`** estos elementos proporcionan las propiedades de las `filas`. +- `tbody.rows` -- la colección dentro de `<tr>`. **`<tr>`:** -- `tr.cells` -- the collection of `<td>` and `<th>` cells inside the given `<tr>`. -- `tr.sectionRowIndex` -- the position (index) of the given `<tr>` inside the enclosing `<thead>/<tbody>/<tfoot>`. -- `tr.rowIndex` -- the number of the `<tr>` in the table as a whole (including all table rows). +- `tr.cells` -- la colección de celdas `<td>` y `<th>` dentro del `<tr>` dado. +- `tr.sectionRowIndex` -- la posición (índice) del `<tr>` dado dentro del `<thead>/<tbody>/<tfoot>` adjunto. +- `tr.rowIndex` -- el número de `<tr>` en la tabla en su conjunto (incluyendo todas las filas de una tabla). **`<td>` and `<th>`:** -- `td.cellIndex` -- the number of the cell inside the enclosing `<tr>`. +- `td.cellIndex` -- el número de celdas dentro del adjunto `<tr>`. -An example of usage: +Un ejemplo de uso: ```html run height=100 <table id="table"> @@ -311,23 +311,23 @@ An example of usage: </table> <script> - // get td with "two" (first row, second column) + // seleccionar td con "dos" (primera fila, segunda columna) let td = table.*!*rows[0].cells[1]*/!*; - td.style.backgroundColor = "red"; // highlight it + td.style.backgroundColor = "red"; // destacarlo </script> ``` -The specification: [tabular data](https://html.spec.whatwg.org/multipage/tables.html). +La especificación: [tabular data](https://html.spec.whatwg.org/multipage/tables.html). -There are also additional navigation properties for HTML forms. We'll look at them later when we start working with forms. +También hay propiedades de navegación adicionales para los formularios HTML. Las veremos más adelante cuando empecemos a trabajar con los formularios. -## Summary +## Resumen -Given a DOM node, we can go to its immediate neighbors using navigation properties. +Dado un nodo del DOM, podemos ir a sus inmediatos vecinos utilizando las propiedades de navegación. -There are two main sets of them: +Hay dos conjuntos principales de ellas: -- For all nodes: `parentNode`, `childNodes`, `firstChild`, `lastChild`, `previousSibling`, `nextSibling`. -- For element nodes only: `parentElement`, `children`, `firstElementChild`, `lastElementChild`, `previousElementSibling`, `nextElementSibling`. +- Para todos los nodos: `parentNode`, `childNodes`, `firstChild`, `lastChild`, `previousSibling`, `nextSibling`. +- Para los nodos elementos: `parentElement`, `children`, `firstElementChild`, `lastElementChild`, `previousElementSibling`, `nextElementSibling`. -Some types of DOM elements, e.g. tables, provide additional properties and collections to access their content. +Algunos tipos de elementos del DOM, por ejemplo las tablas, proveen propiedades adicionales y colecciones para acceder a su contenido. diff --git a/2-ui/1-document/04-searching-elements-dom/1-find-elements/solution.md b/2-ui/1-document/04-searching-elements-dom/1-find-elements/solution.md index c73aecd99..95d25ac99 100644 --- a/2-ui/1-document/04-searching-elements-dom/1-find-elements/solution.md +++ b/2-ui/1-document/04-searching-elements-dom/1-find-elements/solution.md @@ -1,35 +1,35 @@ -There are many ways to do it. +Hay muchas maneras de resolverlo. -Here are some of them: +Aquí hay algunas de ellas: ```js -// 1. The table with `id="age-table"`. +// 1. La tabla con `id="age-table"`. let table = document.getElementById('age-table') -// 2. All label elements inside that table +// 2. Todos los elementos `label` dentro de esa tabla table.getElementsByTagName('label') // or document.querySelectorAll('#age-table label') -// 3. The first td in that table (with the word "Age") +// 3. El primer `td` en la tabla (con la palabra "Age") table.rows[0].cells[0] // or table.getElementsByTagName('td')[0] // or table.querySelector('td') -// 4. The form with the name "search" -// assuming there's only one element with name="search" in the document +// 4. El `form` con name="search" +// suponiendo que sólo hay un elemento con name="search" en el documento let form = document.getElementsByName('search')[0] -// or, form specifically +//o, utilizando el form específicamente document.querySelector('form[name="search"]') -// 5. The first input in that form. +// 5. El primer input en el form. form.getElementsByTagName('input')[0] -// or +// o form.querySelector('input') -// 6. The last input in that form -let inputs = form.querySelectorAll('input') // find all inputs -inputs[inputs.length-1] // take the last one +// 6. El último input en el form. +let inputs = form.querySelectorAll('input') // encontrar todos los inputs +inputs[inputs.length-1] // obtener el último ``` diff --git a/2-ui/1-document/04-searching-elements-dom/1-find-elements/table.html b/2-ui/1-document/04-searching-elements-dom/1-find-elements/table.html index 5b92c34b9..6ab10829c 100644 --- a/2-ui/1-document/04-searching-elements-dom/1-find-elements/table.html +++ b/2-ui/1-document/04-searching-elements-dom/1-find-elements/table.html @@ -2,7 +2,7 @@ <html> <body> <form name="search"> - <label>Search the site: + <label>Buscar en la página: <input type="text" name="search"> </label> <input type="submit" value="Search!"> @@ -11,22 +11,22 @@ <hr> <form name="search-person"> - Search the visitors: + Buscar a los visitantes: <table id="age-table"> <tr> - <td>Age:</td> + <td>Edad:</td> <td id="age-list"> <label> - <input type="radio" name="age" value="young">less than 18</label> + <input type="radio" name="age" value="young">menor de 18</label> <label> <input type="radio" name="age" value="mature">18-50</label> <label> - <input type="radio" name="age" value="senior">more than 50</label> + <input type="radio" name="age" value="senior">mayor de 50</label> </td> </tr> <tr> - <td>Additionally:</td> + <td>Más:</td> <td> <input type="text" name="info[0]"> <input type="text" name="info[1]"> diff --git a/2-ui/1-document/04-searching-elements-dom/1-find-elements/task.md b/2-ui/1-document/04-searching-elements-dom/1-find-elements/task.md index f0b54beac..d93a27392 100644 --- a/2-ui/1-document/04-searching-elements-dom/1-find-elements/task.md +++ b/2-ui/1-document/04-searching-elements-dom/1-find-elements/task.md @@ -2,17 +2,16 @@ importance: 4 --- -# Search for elements +# Buscar elementos +Aquí está el documento con la tabla y el formulario. -Here's the document with the table and form. +¿Cómo encontrar?... -How to find?... +1. La tabla con `id="age-table"`. +2. Todos los elementos `label`dentro de la tabla (debería haber 3). +3. El primer `td` en la tabla (con la palabra "Age"). +4. El `form` con `name="search"`. +5. El primer `input` en ese formulario. +6. El último `input` en ese formulario. -1. The table with `id="age-table"`. -2. All `label` elements inside that table (there should be 3 of them). -3. The first `td` in that table (with the word "Age"). -4. The `form` with `name="search"`. -5. The first `input` in that form. -6. The last `input` in that form. - -Open the page [table.html](table.html) in a separate window and make use of browser tools for that. +Abra la página [table.html](table.html) en una ventana separada y haga uso de las herramientas del navegador. diff --git a/2-ui/1-document/04-searching-elements-dom/article.md b/2-ui/1-document/04-searching-elements-dom/article.md index 405129694..54fe7cb9a 100644 --- a/2-ui/1-document/04-searching-elements-dom/article.md +++ b/2-ui/1-document/04-searching-elements-dom/article.md @@ -1,93 +1,93 @@ -# Searching: getElement*, querySelector* +# Buscar: getElement*, querySelector* -DOM navigation properties are great when elements are close to each other. What if they are not? How to get an arbitrary element of the page? +Las propiedades de navegación del DOM son ideales cuando los elementos están cerca unos de otros. Pero, ¿y si no lo están? ¿Cómo obtener un elemento arbitrario de la página? -There are additional searching methods for that. +Para estos casos existen métodos de búsqueda adicionales. -## document.getElementById or just id +## document.getElementById o sólo id -If an element has the `id` attribute, we can get the element using the method `document.getElementById(id)`, no matter where it is. +Si un elemento tiene el atributo `id`, podemos obtener el elemento usando el método `document.getElementById(id)`, sin importar dónde se encuentre. -For instance: +Por ejemplo: ```html run <div id="elem"> - <div id="elem-content">Element</div> + <div id="elem-content">Elemento</div> </div> <script> - // get the element + // obtener el elemento *!* let elem = document.getElementById('elem'); */!* - // make its background red + // hacer que su fondo sea rojo elem.style.background = 'red'; </script> ``` -Also, there's a global variable named by `id` that references the element: +Existe además una variable global nombrada por el `id` que hace referencia al elemento: ```html run <div id="*!*elem*/!*"> - <div id="*!*elem-content*/!*">Element</div> + <div id="*!*elem-content*/!*">Elemento</div> </div> <script> - // elem is a reference to DOM-element with id="elem" + // elem es una referencia al elemento del DOM con id="elem" elem.style.background = 'red'; - // id="elem-content" has a hyphen inside, so it can't be a variable name - // ...but we can access it using square brackets: window['elem-content'] + // id="elem-content" tiene un guion en su interior, por lo que no puede ser un nombre de variable + // ...pero podemos acceder a él usando corchetes: window['elem-content'] </script> ``` -...That's unless we declare a JavaScript variable with the same name, then it takes precedence: +...Esto es a menos que declaremos una variable de JavaScript con el mismo nombre, entonces ésta tiene prioridad: ```html run untrusted height=0 <div id="elem"></div> <script> - let elem = 5; // now elem is 5, not a reference to <div id="elem"> + let elem = 5; // ahora elem es 5, no una referencia a <div id="elem"> alert(elem); // 5 </script> ``` -```warn header="Please don't use id-named global variables to access elements" -This behavior is described [in the specification](https://html.spec.whatwg.org/multipage/window-object.html#named-access-on-the-window-object), but it is supported mainly for compatibility. +```warn header="Por favor, no utilice variables globales nombradas por id para acceder a los elementos" +Este comportamiento se encuentra descrito [en la especificación](https://html.spec.whatwg.org/multipage/window-object.html#named-access-on-the-window-object), pero está soportado principalmente para compatibilidad. -The browser tries to help us by mixing namespaces of JS and DOM. That's fine for simple scripts, inlined into HTML, but generally isn't a good thing. There may be naming conflicts. Also, when one reads JS code and doesn't have HTML in view, it's not obvious where the variable comes from. +El navegador intenta ayudarnos mezclando espacios de nombres (*namespaces*) de JS y DOM. Esto está bien para los scripts simples, incrustados en HTML, pero generalmente no es una buena práctica. Puede haber conflictos de nombres. Además, cuando uno lee el código de JS y no tiene el HTML a la vista, no es obvio de dónde viene la variable. -Here in the tutorial we use `id` to directly reference an element for brevity, when it's obvious where the element comes from. +Aquí en el tutorial usamos `id` para referirnos directamente a un elemento por brevedad, cuando es obvio de dónde viene el elemento. -In real life `document.getElementById` is the preferred method. +En la vida real `document.getElementById` es el método preferente. ``` -```smart header="The `id` must be unique" -The `id` must be unique. There can be only one element in the document with the given `id`. +```smart header="El `id` debe ser único" +El `id` debe ser único. Sólo puede haber en todo el documento un elemento con un `id` determinado. -If there are multiple elements with the same `id`, then the behavior of methods that use it is unpredictable, e.g. `document.getElementById` may return any of such elements at random. So please stick to the rule and keep `id` unique. +Si hay múltiples elementos con el mismo id, entonces el comportamiento de los métodos que lo usan es impredecible, por ejemplo `document.getElementById` puede devolver cualquiera de esos elementos al azar. Así que, por favor, sigan la regla y mantengan el `id` único. ``` -```warn header="Only `document.getElementById`, not `anyElem.getElementById`" -The method `getElementById` can be called only on `document` object. It looks for the given `id` in the whole document. +```warn header="Sólo `document.getElementById`, no `anyElem.getElementById`" +El método `getElementById` sólo puede ser llamado en el objeto `document`. Busca el `id` dado en todo el documento. ``` ## querySelectorAll [#querySelectorAll] -By far, the most versatile method, `elem.querySelectorAll(css)` returns all elements inside `elem` matching the given CSS selector. +Sin duda el método más versátil, `elem.querySelectorAll(css)` devuelve todos los elementos dentro de `elem` que coinciden con el selector CSS dado. -Here we look for all `<li>` elements that are last children: +Aquí buscamos todos los elementos `<li>` que son los últimos hijos: ```html run <ul> - <li>The</li> - <li>test</li> + <li>La</li> + <li>prueba</li> </ul> <ul> - <li>has</li> - <li>passed</li> + <li>ha</li> + <li>pasado/li> </ul> <script> *!* @@ -95,44 +95,44 @@ Here we look for all `<li>` elements that are last children: */!* for (let elem of elements) { - alert(elem.innerHTML); // "test", "passed" + alert(elem.innerHTML); // "prueba", "pasado" } </script> ``` -This method is indeed powerful, because any CSS selector can be used. +Este método es muy poderoso, porque se puede utilizar cualquier selector de CSS. -```smart header="Can use pseudo-classes as well" -Pseudo-classes in the CSS selector like `:hover` and `:active` are also supported. For instance, `document.querySelectorAll(':hover')` will return the collection with elements that the pointer is over now (in nesting order: from the outermost `<html>` to the most nested one). +```smart header="También se pueden usar pseudoclases" +Las pseudoclases como `:hover` (cuando el cursor sobrevuela el elemento) y `:active` (cuando hace clic con el botón principal) también son soportadas. Por ejemplo, `document.querySelectorAll(':hover')` devolverá una colección de elementos sobre los que el puntero hace hover en ese momento (en orden de anidación: desde el más exterior `<html>` hasta el más anidado). ``` ## querySelector [#querySelector] -The call to `elem.querySelector(css)` returns the first element for the given CSS selector. +La llamada a `elem.querySelector(css)` devuelve el primer elemento para el selector CSS dado. -In other words, the result is the same as `elem.querySelectorAll(css)[0]`, but the latter is looking for *all* elements and picking one, while `elem.querySelector` just looks for one. So it's faster and also shorter to write. +En otras palabras, el resultado es el mismo que `elem.querySelectorAll(css)[0]`, pero este último busca *todos* los elementos y elige uno, mientras que `elem.querySelector` sólo busca uno. Así que es más rápido y también más corto de escribir. ## matches -Previous methods were searching the DOM. +Los métodos anteriores consistían en buscar en el DOM. -The [elem.matches(css)](https://dom.spec.whatwg.org/#dom-element-matches) does not look for anything, it merely checks if `elem` matches the given CSS-selector. It returns `true` or `false`. +El [elem.matches(css)](https://dom.spec.whatwg.org/#dom-element-matches) no busca nada, sólo comprueba si el `elem` coincide con el selector CSS dado. Devuelve `true` o `false`. -The method comes in handy when we are iterating over elements (like in an array or something) and trying to filter out those that interest us. +Este método es útil cuando estamos iterando sobre elementos (como en un array) y tratando de filtrar los que nos interesan. -For instance: +Por ejemplo: ```html run <a href="http://example.com/file.zip">...</a> <a href="http://ya.ru">...</a> <script> - // can be any collection instead of document.body.children + // puede ser cualquier colección en lugar de document.body.children for (let elem of document.body.children) { *!* if (elem.matches('a[href$="zip"]')) { */!* - alert("The archive reference: " + elem.href ); + alert("La referencia del archivo: " + elem.href ); } } </script> @@ -140,21 +140,21 @@ For instance: ## closest -*Ancestors* of an element are: parent, the parent of parent, its parent and so on. The ancestors together form the chain of parents from the element to the top. +Los *ancestros* de un elemento son: el padre, el padre del padre, su padre y así sucesivamente. Todos los ancestros juntos forman la cadena de padres desde el elemento hasta la cima. -The method `elem.closest(css)` looks for the nearest ancestor that matches the CSS-selector. The `elem` itself is also included in the search. +El método `elem.closest(css)` busca el ancestro más cercano que coincide con el selector CSS. El propio `elem` también se incluye en la búsqueda. -In other words, the method `closest` goes up from the element and checks each of parents. If it matches the selector, then the search stops, and the ancestor is returned. +En otras palabras, el método `closest` sube del elemento y comprueba cada uno de los padres. Si coincide con el selector, entonces la búsqueda se detiene y devuelve dicho ancestro. -For instance: +Por ejemplo: ```html run -<h1>Contents</h1> +<h1>Contenido</h1> <div class="contents"> <ul class="book"> - <li class="chapter">Chapter 1</li> - <li class="chapter">Chapter 2</li> + <li class="chapter">Capítulo 1</li> + <li class="chapter">Capítulo 2</li> </ul> </div> @@ -164,44 +164,44 @@ For instance: alert(chapter.closest('.book')); // UL alert(chapter.closest('.contents')); // DIV - alert(chapter.closest('h1')); // null (because h1 is not an ancestor) + alert(chapter.closest('h1')); // null (porque h1 no es un ancestro) </script> ``` ## getElementsBy* -There are also other methods to look for nodes by a tag, class, etc. +También hay otros métodos que permiten buscar nodos por una etiqueta, una clase, etc. -Today, they are mostly history, as `querySelector` is more powerful and shorter to write. +Hoy en día, son en su mayoría historia, ya que `querySelector` es más poderoso y corto de escribir. -So here we cover them mainly for completeness, while you can still find them in the old scripts. +Aquí los cubrimos principalmente por completar el temario, aunque todavía se pueden encontrar en scripts antiguos. -- `elem.getElementsByTagName(tag)` looks for elements with the given tag and returns the collection of them. The `tag` parameter can also be a star `"*"` for "any tags". -- `elem.getElementsByClassName(className)` returns elements that have the given CSS class. -- `document.getElementsByName(name)` returns elements with the given `name` attribute, document-wide. Very rarely used. +- `elem.getElementsByTagName(tag)` busca elementos con la etiqueta dada y devuelve una colección con ellos. El parámetro `tag` también puede ser un asterisco `"*"` para "cualquier etiqueta". +- `elem.getElementsByClassName(className)` devuelve elementos con la clase dada. +- `document.getElementsByName(name)` devuelve elementos con el atributo `name` dado, en todo el documento. Muy raramente usado. -For instance: +Por ejemplo: ```js -// get all divs in the document +// obtener todos los divs del documento let divs = document.getElementsByTagName('div'); ``` -Let's find all `input` tags inside the table: +Para encontrar todas las etiquetas `input` dentro de una tabla: ```html run height=50 <table id="table"> <tr> - <td>Your age:</td> + <td>Su edad:</td> <td> <label> - <input type="radio" name="age" value="young" checked> less than 18 + <input type="radio" name="age" value="young" checked> menos de 18 </label> <label> - <input type="radio" name="age" value="mature"> from 18 to 50 + <input type="radio" name="age" value="mature"> de 18 a 50 </label> <label> - <input type="radio" name="age" value="senior"> more than 60 + <input type="radio" name="age" value="senior"> más de 60 </label> </td> </tr> @@ -218,66 +218,66 @@ Let's find all `input` tags inside the table: </script> ``` -```warn header="Don't forget the `\"s\"` letter!" -Novice developers sometimes forget the letter `"s"`. That is, they try to call `getElementByTagName` instead of <code>getElement<b>s</b>ByTagName</code>. +```warn header="¡No olvides la letra `\"s\"`!" +Los desarrolladores novatos a veces olvidan la letra `"s"`. Esto es, intentan llamar a `getElementByTagName` en vez de a <code>getElement<b>s</b>ByTagName</code>. -The `"s"` letter is absent in `getElementById`, because it returns a single element. But `getElementsByTagName` returns a collection of elements, so there's `"s"` inside. +La letra `"s"` no se encuentra en `getElementById` porque devuelve sólo un elemento. But `getElementsByTagName` devuelve una colección de elementos, de ahí que tenga la `"s"`. ``` -````warn header="It returns a collection, not an element!" -Another widespread novice mistake is to write: +````warn header="¡Devuelve una colección, no un elemento!" +Otro error muy extendido entre los desarrolladores novatos es escribir: ```js -// doesn't work +// no funciona document.getElementsByTagName('input').value = 5; ``` -That won't work, because it takes a *collection* of inputs and assigns the value to it rather than to elements inside it. +Esto no funcionará, porque toma una *colección* de inputs y le asigna el valor a ella en lugar de a los elementos dentro de ella. -We should either iterate over the collection or get an element by its index, and then assign, like this: +En dicho caso, deberíamos iterar sobre la colección o conseguir un elemento por su índice y luego asignarlo así: ```js -// should work (if there's an input) +// debería funcionar (si hay un input) document.getElementsByTagName('input')[0].value = 5; ``` ```` -Looking for `.article` elements: +Buscando elementos `.article`: ```html run height=50 <form name="my-form"> - <div class="article">Article</div> - <div class="long article">Long article</div> + <div class="article">Artículo</div> + <div class="long article">Artículo largo</div> </form> <script> - // find by name attribute + // encontrar por atributo de nombre let form = document.getElementsByName('my-form')[0]; - // find by class inside the form + // encontrar por clase dentro del formulario let articles = form.getElementsByClassName('article'); - alert(articles.length); // 2, found two elements with class "article" + alert(articles.length); // 2, encontró dos elementos con la clase "article" </script> ``` -## Live collections +## Colecciones vivas -All methods `"getElementsBy*"` return a *live* collection. Such collections always reflect the current state of the document and "auto-update" when it changes. +Todos los métodos `"getElementsBy*"` devuelven una colección *viva* (*live collection*). Tales colecciones siempre reflejan el estado actual del documento y se "auto-actualizan" cuando cambia. -In the example below, there are two scripts. +En el siguiente ejemplo, hay dos scripts. -1. The first one creates a reference to the collection of `<div>`. As of now, its length is `1`. -2. The second scripts runs after the browser meets one more `<div>`, so its length is `2`. +1. El primero crea una referencia a la colección de `<div>`. Por ahora, su longitud es `1`. +2. El segundo script se ejecuta después de que el navegador se encuentre con otro `<div>`, por lo que su longitud es de `2`. ```html run -<div>First div</div> +<div>Primer div</div> <script> let divs = document.getElementsByTagName('div'); alert(divs.length); // 1 </script> -<div>Second div</div> +<div>Segundo div</div> <script> *!* @@ -286,20 +286,20 @@ In the example below, there are two scripts. </script> ``` -In contrast, `querySelectorAll` returns a *static* collection. It's like a fixed array of elements. +Por el contrario, `querySelectorAll` devuelve una colección *estática*. Es como un array de elementos fijos. -If we use it instead, then both scripts output `1`: +Si lo utilizamos en lugar de `getElementsByTagName`, entonces ambos scripts dan como resultado `1`: ```html run -<div>First div</div> +<div>Primer div</div> <script> let divs = document.querySelectorAll('div'); alert(divs.length); // 1 </script> -<div>Second div</div> +<div>Segundo div</div> <script> *!* @@ -308,31 +308,31 @@ If we use it instead, then both scripts output `1`: </script> ``` -Now we can easily see the difference. The static collection did not increase after the appearance of a new `div` in the document. +Ahora podemos ver fácilmente la diferencia. La colección estática no aumentó después de la aparición de un nuevo `div` en el documento. -## Summary +## Resumen -There are 6 main methods to search for nodes in DOM: +Hay 6 métodos principales para buscar nodos en el DOM: <table> <thead> <tr> -<td>Method</td> -<td>Searches by...</td> -<td>Can call on an element?</td> -<td>Live?</td> +<td>Método</td> +<td>Busca por...</td> +<td>¿Puede llamar a un elemento?</td> +<td>¿Vivo?</td> </tr> </thead> <tbody> <tr> <td><code>querySelector</code></td> -<td>CSS-selector</td> +<td>selector CSS</td> <td>✔</td> <td>-</td> </tr> <tr> <td><code>querySelectorAll</code></td> -<td>CSS-selector</td> +<td>selector CSS</td> <td>✔</td> <td>-</td> </tr> @@ -350,7 +350,7 @@ There are 6 main methods to search for nodes in DOM: </tr> <tr> <td><code>getElementsByTagName</code></td> -<td>tag or <code>'*'</code></td> +<td>etiqueta o <code>'*'</code></td> <td>✔</td> <td>✔</td> </tr> @@ -363,12 +363,12 @@ There are 6 main methods to search for nodes in DOM: </tbody> </table> -By far the most used are `querySelector` and `querySelectorAll`, but `getElement(s)By*` can be sporadically helpful or found in the old scripts. +Los más utilizados son `querySelector` y `querySelectorAll`, pero `getElementBy*` puede ser de ayuda esporádicamente o encontrarse en scripts antiguos. -Besides that: +Aparte de eso: -- There is `elem.matches(css)` to check if `elem` matches the given CSS selector. -- There is `elem.closest(css)` to look for the nearest ancestor that matches the given CSS-selector. The `elem` itself is also checked. +- Existe `elem.matches(css)` para comprobar si `elem` coincide con el selector CSS dado. +- Existe `elem.closest(css)` para buscar el ancestro más cercano que coincida con el selector CSS dado. El propio `elem` también se comprueba. -And let's mention one more method here to check for the child-parent relationship, as it's sometimes useful: -- `elemA.contains(elemB)` returns true if `elemB` is inside `elemA` (a descendant of `elemA`) or when `elemA==elemB`. +Y mencionemos un método más para comprobar la relación hijo-padre, ya que a veces es útil: +- `elemA.contains(elemB)` devuelve true si `elemB` está dentro de `elemA` (un descendiente de `elemA`) o cuando `elemA==elemB`. diff --git a/2-ui/1-document/05-basic-dom-node-properties/2-lastchild-nodetype-inline/solution.md b/2-ui/1-document/05-basic-dom-node-properties/2-lastchild-nodetype-inline/solution.md index 52c34640a..46e2f6588 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/2-lastchild-nodetype-inline/solution.md +++ b/2-ui/1-document/05-basic-dom-node-properties/2-lastchild-nodetype-inline/solution.md @@ -1,8 +1,8 @@ -There's a catch here. +Aquí hay una trampa. -At the time of `<script>` execution the last DOM node is exactly `<script>`, because the browser did not process the rest of the page yet. +En el momento de la ejecución de `<script>`, el último nodo DOM es exactamente `<script>`, porque el navegador aún no procesó el resto de la página. -So the result is `1` (element node). +Entonces el resultado es `1` (nodo de elemento). ```html run height=60 <html> diff --git a/2-ui/1-document/05-basic-dom-node-properties/2-lastchild-nodetype-inline/task.md b/2-ui/1-document/05-basic-dom-node-properties/2-lastchild-nodetype-inline/task.md index 0ed407cae..4d2731714 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/2-lastchild-nodetype-inline/task.md +++ b/2-ui/1-document/05-basic-dom-node-properties/2-lastchild-nodetype-inline/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# What's in the nodeType? +# ¿Qué hay en nodeType? -What does the script show? +¿Qué muestra el script? ```html <html> diff --git a/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/solution.md b/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/solution.md index 0088882c2..f480a3f3d 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/solution.md +++ b/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/solution.md @@ -1,4 +1,4 @@ -Let's make a loop over `<li>`: +Hagamos un ciclo sobre `<li>`: ```js for (let li of document.querySelectorAll('li')) { @@ -6,16 +6,16 @@ for (let li of document.querySelectorAll('li')) { } ``` -In the loop we need to get the text inside every `li`. +En el ciclo, necesitamos introducir el texto dentro de cada `li`. -We can read the text from the first child node of `li`, that is the text node: +Podemos leer el texto del primer nodo hijo de `li`, que es el nodo de texto: ```js for (let li of document.querySelectorAll('li')) { let title = li.firstChild.data; - // title is the text in <li> before any other nodes + // el título es el texto en <li> antes de cualquier otro nodo } ``` -Then we can get the number of descendants as `li.getElementsByTagName('li').length`. +Entonces podemos obtener el número de descendientes como `li.getElementsByTagName('li').length`. diff --git a/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/solution.view/index.html b/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/solution.view/index.html index 5947ec097..ed4ec1280 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/solution.view/index.html +++ b/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/solution.view/index.html @@ -3,36 +3,36 @@ <body> <ul> - <li>Animals + <li>Animales <ul> - <li>Mammals + <li>Mamíferos <ul> - <li>Cows</li> - <li>Donkeys</li> - <li>Dogs</li> - <li>Tigers</li> + <li>Vacas</li> + <li>Burros</li> + <li>Perros</li> + <li>Tigres</li> </ul> </li> - <li>Other + <li>Otros <ul> - <li>Snakes</li> - <li>Birds</li> - <li>Lizards</li> + <li>Serpientes</li> + <li>Aves</li> + <li>Lagartos</li> </ul> </li> </ul> </li> - <li>Fishes + <li>Peces <ul> - <li>Aquarium + <li>Acuario <ul> <li>Guppy</li> - <li>Angelfish</li> + <li>Angelote</li> </ul> </li> - <li>Sea + <li>Mar <ul> - <li>Sea trout</li> + <li>Trucha de mar</li> </ul> </li> </ul> @@ -41,12 +41,12 @@ <script> for (let li of document.querySelectorAll('li')) { - // get the title from the text node + // obtener el título del nodo de texto let title = li.firstChild.data; - title = title.trim(); // remove extra spaces from ends + title = title.trim(); // eliminar los espacios adicionales de los extremos - // get the descendants count + // obtener el recuento de descendientes let count = li.getElementsByTagName('li').length; alert(title + ': ' + count); diff --git a/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/source.view/index.html b/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/source.view/index.html index fbfacaa88..35624ba4c 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/source.view/index.html +++ b/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/source.view/index.html @@ -3,36 +3,36 @@ <body> <ul> - <li>Animals + <li>Animales <ul> - <li>Mammals + <li>Mamíferos <ul> - <li>Cows</li> - <li>Donkeys</li> - <li>Dogs</li> - <li>Tigers</li> + <li>Vacas</li> + <li>Burros</li> + <li>Perros</li> + <li>Tigres</li> </ul> </li> - <li>Other + <li>Otros <ul> - <li>Snakes</li> - <li>Birds</li> - <li>Lizards</li> + <li>Serpientes</li> + <li>Aves</li> + <li>Lagartos</li> </ul> </li> </ul> </li> - <li>Fishes + <li>Peces <ul> - <li>Aquarium + <li>Acuario <ul> <li>Guppy</li> - <li>Angelfish</li> + <li>Angelote</li> </ul> </li> - <li>Sea + <li>Mar <ul> - <li>Sea trout</li> + <li>Trucha de mar</li> </ul> </li> </ul> @@ -40,7 +40,7 @@ </ul> <script> - // ... your code... + // ... tu código... </script> </body> diff --git a/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/task.md b/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/task.md index f2d9edc67..066ac9b13 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/task.md +++ b/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/task.md @@ -2,13 +2,13 @@ importance: 5 --- -# Count descendants +# Contar los descendientes -There's a tree structured as nested `ul/li`. +Hay un árbol estructurado como `ul/li` anidado. -Write the code that for each `<li>` shows: +Escribe el código que para cada `<li>` muestra: -1. What's the text inside it (without the subtree) -2. The number of nested `<li>` -- all descendants, including the deeply nested ones. +1. ¿Cuál es el texto dentro de él (sin el subárbol)? +2. El número de `<li>` anidados: todos los descendientes, incluidos los profundamente anidados. [demo src="solution"] diff --git a/2-ui/1-document/05-basic-dom-node-properties/3-tag-in-comment/solution.md b/2-ui/1-document/05-basic-dom-node-properties/3-tag-in-comment/solution.md index 32900a789..a2e548367 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/3-tag-in-comment/solution.md +++ b/2-ui/1-document/05-basic-dom-node-properties/3-tag-in-comment/solution.md @@ -1,4 +1,4 @@ -The answer: **`BODY`**. +La respuesta: **`BODY`**. ```html run <script> @@ -10,8 +10,8 @@ The answer: **`BODY`**. </script> ``` -What's going on step by step: +¿Qué está pasando paso a paso? -1. The content of `<body>` is replaced with the comment. The comment is `<!--BODY-->`, because `body.tagName == "BODY"`. As we remember, `tagName` is always uppercase in HTML. -2. The comment is now the only child node, so we get it in `body.firstChild`. -3. The `data` property of the comment is its contents (inside `<!--...-->`): `"BODY"`. +1. El contenido de `<body>` se reemplaza con el comentario. El comentario es `<!--BODY-->`, porque `body.tagName == "BODY"`. Como recordamos, `tagName` siempre está en mayúsculas en HTML. +2. El comentario es ahora el único nodo hijo, así que lo obtenemos en `body.firstChild`. +3. La propiedad `data` del comentario es su contenido (dentro de `<!--...-->`): `"BODY"`. diff --git a/2-ui/1-document/05-basic-dom-node-properties/3-tag-in-comment/task.md b/2-ui/1-document/05-basic-dom-node-properties/3-tag-in-comment/task.md index efe50b48f..e55beb21f 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/3-tag-in-comment/task.md +++ b/2-ui/1-document/05-basic-dom-node-properties/3-tag-in-comment/task.md @@ -2,9 +2,9 @@ importance: 3 --- -# Tag in comment +# Etiqueta en comentario -What does this code show? +¿Qué muestra este código? ```html <script> @@ -12,6 +12,6 @@ What does this code show? body.innerHTML = "<!--" + body.tagName + "-->"; - alert( body.firstChild.data ); // what's here? + alert( body.firstChild.data ); // ¿qué hay aquí? </script> ``` diff --git a/2-ui/1-document/05-basic-dom-node-properties/4-where-document-in-hierarchy/solution.md b/2-ui/1-document/05-basic-dom-node-properties/4-where-document-in-hierarchy/solution.md index cb9456717..6b7d529b2 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/4-where-document-in-hierarchy/solution.md +++ b/2-ui/1-document/05-basic-dom-node-properties/4-where-document-in-hierarchy/solution.md @@ -1,33 +1,33 @@ -We can see which class it belongs by outputting it, like: +Podemos ver a qué clase pertenece, imprimiéndola, así: ```js run alert(document); // [object HTMLDocument] ``` -Or: +O: ```js run alert(document.constructor.name); // HTMLDocument ``` -So, `document` is an instance of `HTMLDocument` class. +Entonces, `document` es una instancia de la clase `HTMLDocument`. -What's its place in the hierarchy? +¿Cuál es su lugar en la jerarquía? -Yeah, we could browse the specification, but it would be faster to figure out manually. +Sí, podríamos examinar las especificaciones, pero sería más rápido averiguarlo manualmente. -Let's traverse the prototype chain via `__proto__`. +Recorramos la cadena de prototype través de `__proto__`. -As we know, methods of a class are in the `prototype` of the constructor. For instance, `HTMLDocument.prototype` has methods for documents. +Como sabemos, los métodos de una clase están en el `prototype` del constructor. Por ejemplo, `HTMLDocument.prototype` tiene métodos para documentos. -Also, there's a reference to the constructor function inside the `prototype`: +Además, hay una referencia a la función constructor dentro de `prototype`: ```js run alert(HTMLDocument.prototype.constructor === HTMLDocument); // true ``` -To get a name of the class as a string, we can use `constructor.name`. Let's do it for the whole `document` prototype chain, till class `Node`: +Para obtener un nombre de la clase como string, podemos usar `constructor.name`. Hagámoslo para toda la cadena prototype de `document`, hasta la clase `Node`: ```js run alert(HTMLDocument.prototype.constructor.name); // HTMLDocument @@ -35,6 +35,6 @@ alert(HTMLDocument.prototype.__proto__.constructor.name); // Document alert(HTMLDocument.prototype.__proto__.__proto__.constructor.name); // Node ``` -That's the hierarchy. +Esa es la jerarquía. -We also could examine the object using `console.dir(document)` and see these names by opening `__proto__`. The console takes them from `constructor` internally. +También podríamos examinar el objeto usando `console.dir(document)` y ver estos nombres abriendo `__proto__`. La consola los toma del `constructor` internamente. diff --git a/2-ui/1-document/05-basic-dom-node-properties/4-where-document-in-hierarchy/task.md b/2-ui/1-document/05-basic-dom-node-properties/4-where-document-in-hierarchy/task.md index de266c6ae..2f63f606f 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/4-where-document-in-hierarchy/task.md +++ b/2-ui/1-document/05-basic-dom-node-properties/4-where-document-in-hierarchy/task.md @@ -2,10 +2,10 @@ importance: 4 --- -# Where's the "document" in the hierarchy? +# ¿Dónde está el "document" en la jerarquía? -Which class does the `document` belong to? +¿A qué clase pertenece el `document`? -What's its place in the DOM hierarchy? +¿Cuál es su lugar en la jerarquía DOM? -Does it inherit from `Node` or `Element`, or maybe `HTMLElement`? +¿Hereda de `Node` o `Element`, o tal vez `HTMLElement`? diff --git a/2-ui/1-document/05-basic-dom-node-properties/article.md b/2-ui/1-document/05-basic-dom-node-properties/article.md index 99dde5bcd..28ea65556 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/article.md +++ b/2-ui/1-document/05-basic-dom-node-properties/article.md @@ -1,81 +1,81 @@ -# Node properties: type, tag and contents +# Propiedades del nodo: tipo, etiqueta y contenido -Let's get a more in-depth look at DOM nodes. +Echemos un mirada más en profundidad a los nodos DOM. -In this chapter we'll see more into what they are and learn their most used properties. +En este capítulo veremos más sobre cuáles son y aprenderemos sus propiedades más utilizadas. -## DOM node classes +## Clases de nodo DOM -Different DOM nodes may have different properties. For instance, an element node corresponding to tag `<a>` has link-related properties, and the one corresponding to `<input>` has input-related properties and so on. Text nodes are not the same as element nodes. But there are also common properties and methods between all of them, because all classes of DOM nodes form a single hierarchy. +Los diferentes nodos DOM pueden tener diferentes propiedades. Por ejemplo, un nodo de elemento correspondiente a la etiqueta `<a>` tiene propiedades relacionadas con el enlace, y el correspondiente a `<input>` tiene propiedades relacionadas con la entrada y así sucesivamente. Los nodos de texto no son lo mismo que los nodos de elementos. Pero también hay propiedades y métodos comunes entre todos ellos, porque todas las clases de nodos DOM forman una única jerarquía. -Each DOM node belongs to the corresponding built-in class. +Cada nodo DOM pertenece a la clase nativa correspondiente. -The root of the hierarchy is [EventTarget](https://dom.spec.whatwg.org/#eventtarget), that is inherited by [Node](https://dom.spec.whatwg.org/#interface-node), and other DOM nodes inherit from it. +La raíz de la jerarquía es [EventTarget](https://dom.spec.whatwg.org/#eventtarget), que es heredada por [Node](https://dom.spec.whatwg.org/#interface-node), y otros nodos DOM heredan de él. -Here's the picture, explanations to follow: +Aquí está la imagen, con las explicaciones a continuación: ![](dom-class-hierarchy.svg) -The classes are: +Las clases son: -- [EventTarget](https://dom.spec.whatwg.org/#eventtarget) -- is the root "abstract" class for everything. +- [EventTarget](https://dom.spec.whatwg.org/#eventtarget) -- es la clase raíz "abstracta". - Objects of that class are never created. It serves as a base, so that all DOM nodes support so-called "events", we'll study them later. + Los objetos de esta clase nunca se crean. Sirve como base, es por la que todos los nodos DOM soportan los llamados "eventos" que estudiaremos más adelante. -- [Node](https://dom.spec.whatwg.org/#interface-node) -- is also an "abstract" class, serving as a base for DOM nodes. +- [Node](http://dom.spec.whatwg.org/#interface-node) -- también es una clase "abstracta", sirve como base para los nodos DOM. - It provides the core tree functionality: `parentNode`, `nextSibling`, `childNodes` and so on (they are getters). Objects of `Node` class are never created. But there are other classes that inherit from it (and so inherit the `Node` functionality). + Proporciona la funcionalidad del árbol principal: `parentNode`, `nextSibling`, `childNodes` y demás (son getters). Los objetos de la clase `Node` nunca se crean. Pero hay clases de nodos concretas que heredan de ella (y también heredan la funcionalidad de `Node`). -- [Document](https://dom.spec.whatwg.org/#interface-document), for historical reasons often inherited by `HTMLDocument` (though the latest spec doesn't dictate it) -- is a document as a whole. +- [Document](https://dom.spec.whatwg.org/#interface-document), por razones históricas, heredado a menudo por `HTMLDocument` (aunque la última especificación no lo exige) -- es el documento como un todo. - The `document` global object belongs exactly to this class. It serves as an entry point to the DOM. + El objeto global `document` pertenece exactamente a esta clase. Sirve como punto de entrada al DOM. -- [CharacterData](https://dom.spec.whatwg.org/#interface-characterdata) -- an "abstract" class, inherited by: - - [Text](https://dom.spec.whatwg.org/#interface-text) -- the class corresponding to a text inside elements, e.g. `Hello` in `<p>Hello</p>`. - - [Comment](https://dom.spec.whatwg.org/#interface-comment) -- the class for comments. They are not shown, but each comment becomes a member of DOM. +- [CharacterData](https://dom.spec.whatwg.org/#interface-characterdata) -- una clase "abstract" heredada por: + - [Text](https://dom.spec.whatwg.org/#interface-text) -- la clase correspondiente a texto dentro de los elementos, por ejemplo `Hello` en `<p>Hello</p>`. + - [Comment](https://dom.spec.whatwg.org/#interface-comment) -- la clase para los "comentarios". No se muestran, pero cada comentario se vuelve un miembro del DOM. -- [Element](https://dom.spec.whatwg.org/#interface-element) -- is the base class for DOM elements. +- [Element](http://dom.spec.whatwg.org/#interface-element) -- es una clase base para elementos DOM. - It provides element-level navigation like `nextElementSibling`, `children` and searching methods like `getElementsByTagName`, `querySelector`. + Proporciona navegación a nivel de elemento como `nextElementSibling`, `children` y métodos de búsqueda como `getElementsByTagName`, `querySelector`. + + Un navegador admite no solo HTML, sino también XML y SVG. La clase `Element` sirve como base para clases más específicas: `SVGElement`, `XMLElement` (no las necesitamos aquí) y `HTMLElement`. - A browser supports not only HTML, but also XML and SVG. So the `Element` class serves as a base for more specific classes: `SVGElement`, `XMLElement` (we don't need them here) and `HTMLElement`. +- Finalmente, [HTMLElement](https://html.spec.whatwg.org/multipage/dom.html#htmlelement) -- es la clase básica para todos los elementos HTML. Trabajaremos con ella la mayor parte del tiempo. + + Es heredado por elementos HTML concretos: + - [HTMLInputElement](https://html.spec.whatwg.org/multipage/forms.html#htmlinputelement) -- la clase para elementos `<input>`, + - [HTMLBodyElement](https://html.spec.whatwg.org/multipage/semantics.html#htmlbodyelement) -- la clase para los elementos `<body>`, + - [HTMLAnchorElement](https://html.spec.whatwg.org/multipage/semantics.html#htmlanchorelement) -- la clase para elementos `<a>`, + - ...y así sucesivamente. -- Finally, [HTMLElement](https://html.spec.whatwg.org/multipage/dom.html#htmlelement) is the basic class for all HTML elements. We'll work with it most of the time. +Hay muchas otras etiquetas con sus propias clases que pueden tener propiedades y métodos específicos, mientras que algunos elementos, tales como `<span>`, `<section>`, `<article>`, no tienen ninguna propiedad específica entonces derivan de la clase `HTMLElement`. - It is inherited by concrete HTML elements: - - [HTMLInputElement](https://html.spec.whatwg.org/multipage/forms.html#htmlinputelement) -- the class for `<input>` elements, - - [HTMLBodyElement](https://html.spec.whatwg.org/multipage/semantics.html#htmlbodyelement) -- the class for `<body>` elements, - - [HTMLAnchorElement](https://html.spec.whatwg.org/multipage/semantics.html#htmlanchorelement) -- the class for `<a>` elements, - - ...and so on. +Entonces, el conjunto completo de propiedades y métodos de un nodo dado viene como resultado de la cadena de herencia. -There are many other tags with their own classes that may have specific properties and methods, while some elements, such as `<span>`, `<section>`, `<article>` do not have any specific properties, so they are instances of `HTMLElement` class. +Por ejemplo, consideremos el objeto DOM para un elemento `<input>`. Pertenece a la clase [HTMLInputElement](https://html.spec.whatwg.org/multipage/forms.html#htmlinputelement). -So, the full set of properties and methods of a given node comes as the result of the chain of inheritance. +Obtiene propiedades y métodos como una superposición de (enumerados en orden de herencia): -For example, let's consider the DOM object for an `<input>` element. It belongs to [HTMLInputElement](https://html.spec.whatwg.org/multipage/forms.html#htmlinputelement) class. +- `HTMLInputElement` -- esta clase proporciona propiedades específicas de entrada, +- `HTMLElement` -- proporciona métodos de elementos HTML comunes (y getters/setters), +- `Element` -- proporciona métodos de elementos genéricos, +- `Node` -- proporciona propiedades comunes del nodo DOM, +- `EventTarget` -- da el apoyo para eventos (a cubrir), +- ...y finalmente hereda de `Object`, por lo que también están disponibles métodos de "objeto simple" como `hasOwnProperty`. -It gets properties and methods as a superposition of (listed in inheritance order): - -- `HTMLInputElement` -- this class provides input-specific properties, -- `HTMLElement` -- it provides common HTML element methods (and getters/setters), -- `Element` -- provides generic element methods, -- `Node` -- provides common DOM node properties, -- `EventTarget` -- gives the support for events (to be covered), -- ...and finally it inherits from `Object`, so "plain object" methods like `hasOwnProperty` are also available. - -To see the DOM node class name, we can recall that an object usually has the `constructor` property. It references the class constructor, and `constructor.name` is its name: +Para ver el nombre de la clase del nodo DOM, podemos recordar que un objeto generalmente tiene la propiedad `constructor`. Hace referencia al constructor de la clase, y `constructor.name` es su nombre: ```js run alert( document.body.constructor.name ); // HTMLBodyElement ``` -...Or we can just `toString` it: +...O podemos simplemente usar `toString`: ```js run alert( document.body ); // [object HTMLBodyElement] ``` -We also can use `instanceof` to check the inheritance: +También podemos usar `instanceof` para verificar la herencia: ```js run alert( document.body instanceof HTMLBodyElement ); // true @@ -85,38 +85,38 @@ alert( document.body instanceof Node ); // true alert( document.body instanceof EventTarget ); // true ``` -As we can see, DOM nodes are regular JavaScript objects. They use prototype-based classes for inheritance. +Como podemos ver, los nodos DOM son objetos regulares de JavaScript. Usan clases basadas en prototipos para la herencia. -That's also easy to see by outputting an element with `console.dir(elem)` in a browser. There in the console you can see `HTMLElement.prototype`, `Element.prototype` and so on. +Eso también es fácil de ver al generar un elemento con `console.dir(elem)` en un navegador. Allí, en la consola, puede ver `HTMLElement.prototype`, `Element.prototype` y así sucesivamente. ```smart header="`console.dir(elem)` versus `console.log(elem)`" -Most browsers support two commands in their developer tools: `console.log` and `console.dir`. They output their arguments to the console. For JavaScript objects these commands usually do the same. +La mayoría de los navegadores admiten dos comandos en sus herramientas de desarrollo: `console.log` y `console.dir`. Envían sus argumentos a la consola. Para los objetos JavaScript, estos comandos suelen hacer lo mismo. -But for DOM elements they are different: +Pero para los elementos DOM son diferentes: -- `console.log(elem)` shows the element DOM tree. -- `console.dir(elem)` shows the element as a DOM object, good to explore its properties. +- `console.log(elem)` muestra el árbol DOM del elemento. +- `console.dir(elem)` muestra el elemento como un objeto DOM, es bueno para explorar sus propiedades. -Try it on `document.body`. +Inténtalo en `document.body`. ``` -````smart header="IDL in the spec" -In the specification, DOM classes aren't described by using JavaScript, but a special [Interface description language](https://en.wikipedia.org/wiki/Interface_description_language) (IDL), that is usually easy to understand. +````smart header="IDL en la especificación" +En la especificación, las clases DOM no se describen mediante JavaScript, sino con un [Lenguaje de descripción de interfaz](https://es.wikipedia.org/wiki/Lenguaje_de_descripci%C3%B3n_de_interfaz) (IDL) especial, que suele ser fácil de entender. -In IDL all properties are prepended with their types. For instance, `DOMString`, `boolean` and so on. +En IDL, todas las propiedades están precedidas por sus tipos. Por ejemplo, `DOMString`, `boolean` y así sucesivamente. -Here's an excerpt from it, with comments: +Aquí hay un extracto, con comentarios: ```js -// Define HTMLInputElement +// Definir HTMLInputElement *!* -// The colon ":" means that HTMLInputElement inherits from HTMLElement +// Los dos puntos ":" significan que HTMLInputElement hereda de HTMLElement */!* interface HTMLInputElement: HTMLElement { - // here go properties and methods of <input> elements + // aquí van las propiedades y métodos de los elementos <input> *!* - // "DOMString" means that the value of a property is a string + // "DOMString" significa que el valor de una propiedad es un string */!* attribute DOMString accept; attribute DOMString alt; @@ -124,12 +124,12 @@ interface HTMLInputElement: HTMLElement { attribute DOMString value; *!* - // boolean value property (true/false) + // Propiedad de valor booleano (true/false) attribute boolean autofocus; */!* ... *!* - // now the method: "void" means that the method returns no value + // ahora el método: "void" significa que el método no devuelve ningún valor */!* void select(); ... @@ -137,342 +137,342 @@ interface HTMLInputElement: HTMLElement { ``` ```` -## The "nodeType" property +## La propiedad "nodeType" -The `nodeType` property provides one more, "old-fashioned" way to get the "type" of a DOM node. +La propiedad `nodeType` proporciona una forma "anticuada" más de obtener el "tipo" de un nodo DOM. -It has a numeric value: -- `elem.nodeType == 1` for element nodes, -- `elem.nodeType == 3` for text nodes, -- `elem.nodeType == 9` for the document object, -- there are few other values in [the specification](https://dom.spec.whatwg.org/#node). +Tiene un valor numérico: +- `elem.nodeType == 1` para nodos de elementos, +- `elem.nodeType == 3` para nodos de texto, +- `elem.nodeType == 9` para el objeto de documento, +- hay algunos otros valores en [la especificación](https://dom.spec.whatwg.org/#node). -For instance: +Por ejemplo: ```html run <body> <script> let elem = document.body; - // let's examine: what type of node is in elem? - alert(elem.nodeType); // 1 => element + // vamos a examinar: ¿qué tipo de nodo es elem? + alert(elem.nodeType); // 1 => elemento - // and its first child is... - alert(elem.firstChild.nodeType); // 3 => text + // Y el primer hijo es... + alert(elem.firstChild.nodeType); // 3 => texto - // for the document object, the type is 9 + // para el objeto de tipo documento, el tipo es 9 alert( document.nodeType ); // 9 </script> </body> ``` -In modern scripts, we can use `instanceof` and other class-based tests to see the node type, but sometimes `nodeType` may be simpler. We can only read `nodeType`, not change it. +En los scripts modernos, podemos usar `instanceof` y otras pruebas basadas en clases para ver el tipo de nodo, pero a veces `nodeType` puede ser más simple. Solo podemos leer `nodeType`, no cambiarlo. -## Tag: nodeName and tagName +## Tag: nodeName y tagName -Given a DOM node, we can read its tag name from `nodeName` or `tagName` properties: +Dado un nodo DOM, podemos leer su nombre de etiqueta en las propiedades de `nodeName` o `tagName`: -For instance: +Por ejemplo: ```js run alert( document.body.nodeName ); // BODY alert( document.body.tagName ); // BODY ``` -Is there any difference between `tagName` and `nodeName`? +¿Hay alguna diferencia entre `tagName` y `nodeName`? -Sure, the difference is reflected in their names, but is indeed a bit subtle. +Claro, la diferencia se refleja en sus nombres, pero de hecho es un poco sutil. -- The `tagName` property exists only for `Element` nodes. -- The `nodeName` is defined for any `Node`: - - for elements it means the same as `tagName`. - - for other node types (text, comment, etc.) it has a string with the node type. +- La propiedad `tagName` existe solo para los nodos `Element`. +- El `nodeName` se define para cualquier `Node`: + - para los elementos, significa lo mismo que `tagName`. + - para otros tipos de nodo (texto, comentario, etc.) tiene una cadena con el tipo de nodo. -In other words, `tagName` is only supported by element nodes (as it originates from `Element` class), while `nodeName` can say something about other node types. +En otras palabras, `tagName` solo es compatible con los nodos de elementos (ya que se origina en la clase `Element`), mientras que `nodeName` puede decir algo sobre otros tipos de nodos. -For instance, let's compare `tagName` and `nodeName` for the `document` and a comment node: +Por ejemplo, comparemos `tagName` y `nodeName` para `document` y un nodo de comentario: ```html run -<body><!-- comment --> +<body><!-- comentario --> <script> - // for comment - alert( document.body.firstChild.tagName ); // undefined (not an element) + // para comentarios + alert( document.body.firstChild.tagName ); // undefined (no es un elemento) alert( document.body.firstChild.nodeName ); // #comment - // for document - alert( document.tagName ); // undefined (not an element) + // para documentos + alert( document.tagName ); // undefined (no es un elemento) alert( document.nodeName ); // #document </script> </body> ``` -If we only deal with elements, then we can use both `tagName` and `nodeName` - there's no difference. +Si solo tratamos con elementos, entonces podemos usar tanto `tagName` como `nodeName` - no hay diferencia. -```smart header="The tag name is always uppercase except in XML mode" -The browser has two modes of processing documents: HTML and XML. Usually the HTML-mode is used for webpages. XML-mode is enabled when the browser receives an XML-document with the header: `Content-Type: application/xml+xhtml`. +```smart header="El nombre de la etiqueta siempre está en mayúsculas, excepto en el modo XML" +El navegador tiene dos modos de procesar documentos: HTML y XML. Por lo general, el modo HTML se usa para páginas web. El modo XML está habilitado cuando el navegador recibe un documento XML con el encabezado: `Content-Type: application/xml+xhtml`. -In HTML mode `tagName/nodeName` is always uppercased: it's `BODY` either for `<body>` or `<BoDy>`. +En el modo HTML, `tagName/nodeName` siempre está en mayúsculas: es `BODY` ya sea para `<body>` o `<BoDy>`. -In XML mode the case is kept "as is". Nowadays XML mode is rarely used. +En el modo XML, el caso se mantiene "tal cual". Hoy en día, el modo XML rara vez se usa. ``` -## innerHTML: the contents +## innerHTML: los contenidos -The [innerHTML](https://w3c.github.io/DOM-Parsing/#the-innerhtml-mixin) property allows to get the HTML inside the element as a string. +La propiedad [innerHTML](https://w3c.github.io/DOM-Parsing/#the-innerhtml-mixin) permite obtener el HTML dentro del elemento como un string. -We can also modify it. So it's one of the most powerful ways to change the page. +También podemos modificarlo. Así que es una de las formas más poderosas de cambiar la página. -The example shows the contents of `document.body` and then replaces it completely: +El ejemplo muestra el contenido de `document.body` y luego lo reemplaza por completo: ```html run <body> - <p>A paragraph</p> - <div>A div</div> + <p>Un párrafo</p> + <div>Un div</div> <script> - alert( document.body.innerHTML ); // read the current contents - document.body.innerHTML = 'The new BODY!'; // replace it + alert( document.body.innerHTML ); // leer el contenido actual + document.body.innerHTML = 'El nuevo BODY!'; // reemplazar </script> </body> ``` -We can try to insert invalid HTML, the browser will fix our errors: +Podemos intentar insertar HTML no válido, el navegador corregirá nuestros errores: ```html run <body> <script> - document.body.innerHTML = '<b>test'; // forgot to close the tag - alert( document.body.innerHTML ); // <b>test</b> (fixed) + document.body.innerHTML = '<b>prueba'; // olvidé cerrar la etiqueta + alert( document.body.innerHTML ); // <b>prueba</b> (arreglado) </script> </body> ``` -```smart header="Scripts don't execute" -If `innerHTML` inserts a `<script>` tag into the document -- it becomes a part of HTML, but doesn't execute. +```smart header="Los scripts no se ejecutan" +Si `innerHTML` inserta una etiqueta `<script>` en el documento, se convierte en parte de HTML, pero no se ejecuta. ``` -### Beware: "innerHTML+=" does a full overwrite +### Cuidado: "innerHTML+=" hace una sobrescritura completa -We can append HTML to an element by using `elem.innerHTML+="more html"`. +Podemos agregar HTML a un elemento usando `elem.innerHTML+="more html"`. -Like this: +Así: ```js -chatDiv.innerHTML += "<div>Hello<img src='smile.gif'/> !</div>"; -chatDiv.innerHTML += "How goes?"; +chatDiv.innerHTML += "<div>Hola<img src='smile.gif'/> !</div>"; +chatDiv.innerHTML += "¿Cómo vas?"; ``` -But we should be very careful about doing it, because what's going on is *not* an addition, but a full overwrite. +Pero debemos tener mucho cuidado al hacerlo, porque lo que está sucediendo *no* es una adición, sino una sobrescritura completa. -Technically, these two lines do the same: +Técnicamente, estas dos líneas hacen lo mismo: ```js elem.innerHTML += "..."; -// is a shorter way to write: +// es una forma más corta de escribir: *!* elem.innerHTML = elem.innerHTML + "..." */!* ``` -In other words, `innerHTML+=` does this: +En otras palabras, `innerHTML+=` hace esto: -1. The old contents is removed. -2. The new `innerHTML` is written instead (a concatenation of the old and the new one). +1. Se elimina el contenido antiguo. +2. En su lugar, se escribe el nuevo `innerHTML` (una concatenación del antiguo y el nuevo). -**As the content is "zeroed-out" and rewritten from the scratch, all images and other resources will be reloaded**. +**Como el contenido se "pone a cero" y se reescribe desde cero, todas las imágenes y otros recursos se volverán a cargar.**. -In the `chatDiv` example above the line `chatDiv.innerHTML+="How goes?"` re-creates the HTML content and reloads `smile.gif` (hope it's cached). If `chatDiv` has a lot of other text and images, then the reload becomes clearly visible. +En el ejemplo de `chatDiv` arriba, la línea `chatDiv.innerHTML+="¿Cómo va?"` recrea el contenido HTML y recarga `smile.gif` (con la esperanza de que esté en caché). Si `chatDiv` tiene muchos otros textos e imágenes, entonces la recarga se vuelve claramente visible. -There are other side-effects as well. For instance, if the existing text was selected with the mouse, then most browsers will remove the selection upon rewriting `innerHTML`. And if there was an `<input>` with a text entered by the visitor, then the text will be removed. And so on. +También hay otros efectos secundarios. Por ejemplo, si el texto existente se seleccionó con el mouse, la mayoría de los navegadores eliminarán la selección al reescribir `innerHTML`. Y si había un `<input>` con un texto ingresado por el visitante, entonces el texto será eliminado. Y así. -Luckily, there are other ways to add HTML besides `innerHTML`, and we'll study them soon. +Afortunadamente, hay otras formas de agregar HTML además de `innerHTML`, y las estudiaremos pronto. -## outerHTML: full HTML of the element +## outerHTML: HTML completo del elemento -The `outerHTML` property contains the full HTML of the element. That's like `innerHTML` plus the element itself. +La propiedad `outerHTML` contiene el HTML completo del elemento. Eso es como `innerHTML` más el elemento en sí. -Here's an example: +He aquí un ejemplo: ```html run -<div id="elem">Hello <b>World</b></div> +<div id="elem">Hola <b>Mundo</b></div> <script> - alert(elem.outerHTML); // <div id="elem">Hello <b>World</b></div> + alert(elem.outerHTML); // <div id="elem">Hola <b>Mundo</b></div> </script> ``` -**Beware: unlike `innerHTML`, writing to `outerHTML` does not change the element. Instead, it replaces it in the DOM.** +**Cuidado: a diferencia de `innerHTML`, escribir en `outerHTML` no cambia el elemento. En cambio, lo reemplaza en el DOM.** -Yeah, sounds strange, and strange it is, that's why we make a separate note about it here. Take a look. +Sí, suena extraño, y es extraño, por eso hacemos una nota aparte al respecto aquí. Echa un vistazo. -Consider the example: +Considera el ejemplo: ```html run -<div>Hello, world!</div> +<div>¡Hola, mundo!</div> <script> let div = document.querySelector('div'); *!* - // replace div.outerHTML with <p>...</p> + // reemplaza div.outerHTML con <p>...</p> */!* - div.outerHTML = '<p>A new element</p>'; // (*) + div.outerHTML = '<p>Un nuevo elemento</p>'; // (*) *!* - // Wow! 'div' is still the same! + // ¡Guauu! ¡'div' sigue siendo el mismo! */!* - alert(div.outerHTML); // <div>Hello, world!</div> (**) + alert(div.outerHTML); // <div>¡Hola, mundo!</div> (**) </script> ``` -Looks really odd, right? +Parece realmente extraño, ¿verdad? -In the line `(*)` we replaced `div` with `<p>A new element</p>`. In the outer document (the DOM) we can see the new content instead of the `<div>`. But, as we can see in line `(**)`, the value of the old `div` variable hasn't changed! +En la línea `(*)` reemplazamos `div` con `<p>Un nuevo elemento</p>`. En el documento externo (el DOM) podemos ver el nuevo contenido en lugar del `<div>`. Pero, como podemos ver en la línea `(**)`, ¡el valor de la antigua variable `div` no ha cambiado! -The `outerHTML` assignment does not modify the DOM element (the object referenced by, in this case, the variable 'div'), but removes it from the DOM and inserts the new HTML in its place. +La asignación `outerHTML` no modifica el elemento DOM (el objeto al que hace referencia, en este caso, la variable 'div'), pero lo elimina del DOM e inserta el nuevo HTML en su lugar. -So what happened in `div.outerHTML=...` is: -- `div` was removed from the document. -- Another piece of HTML `<p>A new element</p>` was inserted in its place. -- `div` still has its old value. The new HTML wasn't saved to any variable. +Entonces, lo que sucedió en `div.outerHTML=...` es: +- `div` fue eliminado del documento. +- Otro fragmento de HTML `<p>Un nuevo elemento</p>` se insertó en su lugar. +- `div` todavía tiene su antiguo valor. El nuevo HTML no se guardó en ninguna variable. -It's so easy to make an error here: modify `div.outerHTML` and then continue to work with `div` as if it had the new content in it. But it doesn't. Such thing is correct for `innerHTML`, but not for `outerHTML`. +Es muy fácil cometer un error aquí: modificar `div.outerHTML` y luego continuar trabajando con `div` como si tuviera el nuevo contenido. Pero no es así. Esto es correcto para `innerHTML`, pero no para `outerHTML`. -We can write to `elem.outerHTML`, but should keep in mind that it doesn't change the element we're writing to ('elem'). It puts the new HTML in its place instead. We can get references to the new elements by querying the DOM. +Podemos escribir en `elem.outerHTML`, pero debemos tener en cuenta que no cambia el elemento en el que estamos escribiendo ('elem'). En su lugar, coloca el nuevo HTML en su lugar. Podemos obtener referencias a los nuevos elementos consultando el DOM. -## nodeValue/data: text node content +## nodeValue/data: contenido del nodo de texto -The `innerHTML` property is only valid for element nodes. +La propiedad `innerHTML` solo es válida para los nodos de elementos. -Other node types, such as text nodes, have their counterpart: `nodeValue` and `data` properties. These two are almost the same for practical use, there are only minor specification differences. So we'll use `data`, because it's shorter. +Otros tipos de nodos, como los nodos de texto, tienen su contraparte: propiedades `nodeValue` y `data`. Estas dos son casi iguales para uso práctico, solo hay pequeñas diferencias de especificación. Entonces usaremos `data`, porque es más corto. -An example of reading the content of a text node and a comment: +Un ejemplo de lectura del contenido de un nodo de texto y un comentario: ```html run height="50" <body> - Hello - <!-- Comment --> + Hola + <!-- Comentario --> <script> let text = document.body.firstChild; *!* - alert(text.data); // Hello + alert(text.data); // Hola */!* let comment = text.nextSibling; *!* - alert(comment.data); // Comment + alert(comment.data); // Comentario */!* </script> </body> ``` -For text nodes we can imagine a reason to read or modify them, but why comments? +Para los nodos de texto podemos imaginar una razón para leerlos o modificarlos, pero ¿por qué comentarios? -Sometimes developers embed information or template instructions into HTML in them, like this: +A veces, los desarrolladores incorporan información o instrucciones de plantilla en HTML, así: ```html <!-- if isAdmin --> - <div>Welcome, Admin!</div> + <div>¡Bienvenido, administrador!</div> <!-- /if --> ``` -...Then JavaScript can read it from `data` property and process embedded instructions. +...Entonces JavaScript puede leerlo desde la propiedad `data` y procesar las instrucciones integradas. -## textContent: pure text +## textContent: texto puro -The `textContent` provides access to the *text* inside the element: only text, minus all `<tags>`. +El `textContent` proporciona acceso al *texto* dentro del elemento: solo texto, menos todas las `<tags>`. -For instance: +Por ejemplo: ```html run <div id="news"> - <h1>Headline!</h1> - <p>Martians attack people!</p> + <h1>¡Titular!</h1> + <p>¡Los marcianos atacan a la gente!</p> </div> <script> - // Headline! Martians attack people! + // ¡Titular! ¡Los marcianos atacan a la gente! alert(news.textContent); </script> ``` -As we can see, only text is returned, as if all `<tags>` were cut out, but the text in them remained. +Como podemos ver, solo se devuelve texto, como si todas las `<etiquetas>` fueran recortadas, pero el texto en ellas permaneció. -In practice, reading such text is rarely needed. +En la práctica, rara vez se necesita leer este tipo de texto. -**Writing to `textContent` is much more useful, because it allows to write text the "safe way".** +**Escribir en `textContent` es mucho más útil, porque permite escribir texto de "forma segura".** -Let's say we have an arbitrary string, for instance entered by a user, and want to show it. +Digamos que tenemos un string arbitrario, por ejemplo, ingresado por un usuario, y queremos mostrarlo. -- With `innerHTML` we'll have it inserted "as HTML", with all HTML tags. -- With `textContent` we'll have it inserted "as text", all symbols are treated literally. +- Con `innerHTML` lo tendremos insertado "como HTML", con todas las etiquetas HTML. +- Con `textContent` lo tendremos insertado "como texto", todos los símbolos se tratan literalmente. -Compare the two: +Compara los dos: ```html run <div id="elem1"></div> <div id="elem2"></div> <script> - let name = prompt("What's your name?", "<b>Winnie-the-Pooh!</b>"); + let name = prompt("¿Cuál es tu nombre?", "<b>¡Winnie-Pooh!</b>"); elem1.innerHTML = name; elem2.textContent = name; </script> ``` -1. The first `<div>` gets the name "as HTML": all tags become tags, so we see the bold name. -2. The second `<div>` gets the name "as text", so we literally see `<b>Winnie-the-Pooh!</b>`. +1. El primer `<div>` obtiene el nombre "como HTML": todas las etiquetas se convierten en etiquetas, por lo que vemos el nombre en negrita. +2. El segundo `<div>` obtiene el nombre "como texto", así que literalmente vemos `<b>¡Winnie-Pooh!</b>`. -In most cases, we expect the text from a user, and want to treat it as text. We don't want unexpected HTML in our site. An assignment to `textContent` does exactly that. +En la mayoría de los casos, esperamos el texto de un usuario y queremos tratarlo como texto. No queremos HTML inesperado en nuestro sitio. Una asignación a `textContent` hace exactamente eso. -## The "hidden" property +## La propiedad "hidden" -The "hidden" attribute and the DOM property specifies whether the element is visible or not. +El atributo "hidden" y la propiedad DOM especifican si el elemento es visible o no. -We can use it in HTML or assign it using JavaScript, like this: +Podemos usarlo en HTML o asignarlo usando JavaScript, así: ```html run height="80" -<div>Both divs below are hidden</div> +<div>Ambos divs a continuación están ocultos</div> -<div hidden>With the attribute "hidden"</div> +<div hidden>Con el atributo "hidden"</div> -<div id="elem">JavaScript assigned the property "hidden"</div> +<div id="elem">JavaScript asignó la propiedad "hidden"</div> <script> elem.hidden = true; </script> ``` -Technically, `hidden` works the same as `style="display:none"`. But it's shorter to write. +Técnicamente, `hidden` funciona igual que `style="display:none"`. Pero es más corto de escribir. -Here's a blinking element: +Aquí hay un elemento parpadeante: ```html run height=50 -<div id="elem">A blinking element</div> +<div id="elem">Un elemento parpadeante</div> <script> setInterval(() => elem.hidden = !elem.hidden, 1000); </script> ``` -## More properties +## Más propiedades -DOM elements also have additional properties, in particular those that depend on the class: +Los elementos DOM también tienen propiedades adicionales, en particular aquellas que dependen de la clase: -- `value` -- the value for `<input>`, `<select>` and `<textarea>` (`HTMLInputElement`, `HTMLSelectElement`...). -- `href` -- the "href" for `<a href="...">` (`HTMLAnchorElement`). -- `id` -- the value of "id" attribute, for all elements (`HTMLElement`). -- ...and much more... +- `value` -- el valor para `<input>`, `<select>` y `<textarea>` (`HTMLInputElement`, `HTMLSelectElement`...). +- `href` -- el "href" para `<a href="...">` (`HTMLAnchorElement`). +- `id` -- el valor del atributo "id", para todos los elementos (`HTMLElement`). +- ...y mucho más... -For instance: +Por ejemplo: ```html run height="80" <input type="text" id="elem" value="value"> @@ -484,39 +484,39 @@ For instance: </script> ``` -Most standard HTML attributes have the corresponding DOM property, and we can access it like that. +La mayoría de los atributos HTML estándar tienen la propiedad DOM correspondiente, y podemos acceder a ella así. -If we want to know the full list of supported properties for a given class, we can find them in the specification. For instance, `HTMLInputElement` is documented at <https://html.spec.whatwg.org/#htmlinputelement>. +Si queremos conocer la lista completa de propiedades admitidas para una clase determinada, podemos encontrarlas en la especificación. Por ejemplo, `HTMLInputElement` está documentado en <https://html.spec.whatwg.org/#htmlinputelement>. -Or if we'd like to get them fast or are interested in a concrete browser specification -- we can always output the element using `console.dir(elem)` and read the properties. Or explore "DOM properties" in the Elements tab of the browser developer tools. +O si nos gustaría obtenerlos rápidamente o estamos interesados en una especificación concreta del navegador, siempre podemos generar el elemento usando `console.dir(elem)` y leer las propiedades. O explora las "propiedades DOM" en la pestaña Elements de las herramientas de desarrollo del navegador. -## Summary +## Resumen -Each DOM node belongs to a certain class. The classes form a hierarchy. The full set of properties and methods come as the result of inheritance. +Cada nodo DOM pertenece a una determinada clase. Las clases forman una jerarquía. El conjunto completo de propiedades y métodos proviene de la herencia. -Main DOM node properties are: +Las propiedades principales del nodo DOM son: `nodeType` -: We can use it to see if a node is a text or an element node. It has a numeric value: `1` for elements,`3` for text nodes, and a few others for other node types. Read-only. +: Podemos usarla para ver si un nodo es un texto o un elemento. Tiene un valor numérico: `1` para elementos, `3` para nodos de texto y algunos otros para otros tipos de nodos. Solo lectura. `nodeName/tagName` -: For elements, tag name (uppercased unless XML-mode). For non-element nodes `nodeName` describes what it is. Read-only. +: Para los elementos, nombre de la etiqueta (en mayúsculas a menos que esté en modo XML). Para los nodos que no son elementos, `nodeName` describe lo que es. Solo lectura. `innerHTML` -: The HTML content of the element. Can be modified. +: El contenido HTML del elemento. Puede modificarse. `outerHTML` -: The full HTML of the element. A write operation into `elem.outerHTML` does not touch `elem` itself. Instead it gets replaced with the new HTML in the outer context. +: El HTML completo del elemento. Una operación de escritura en `elem.outerHTML` no toca a `elem` en sí. En su lugar, se reemplaza con el nuevo HTML en el contexto externo. `nodeValue/data` -: The content of a non-element node (text, comment). These two are almost the same, usually we use `data`. Can be modified. +: El contenido de un nodo que no es un elemento (text, comment). Estos dos son casi iguales, usualmente usamos `data`. Puede modificarse. `textContent` -: The text inside the element: HTML minus all `<tags>`. Writing into it puts the text inside the element, with all special characters and tags treated exactly as text. Can safely insert user-generated text and protect from unwanted HTML insertions. +: El texto dentro del elemento: HTML menos todas las `<tags>`. Escribir en él coloca el texto dentro del elemento, con todos los caracteres especiales y etiquetas tratados exactamente como texto. Puede insertar de forma segura texto generado por el usuario y protegerse de inserciones HTML no deseadas. `hidden` -: When set to `true`, does the same as CSS `display:none`. +: Cuando se establece en `true`, hace lo mismo que CSS `display:none`. -DOM nodes also have other properties depending on their class. For instance, `<input>` elements (`HTMLInputElement`) support `value`, `type`, while `<a>` elements (`HTMLAnchorElement`) support `href` etc. Most standard HTML attributes have a corresponding DOM property. +Los nodos DOM también tienen otras propiedades dependiendo de su clase. Por ejemplo, los elementos `<input>` (`HTMLInputElement`) admiten `value`, `type`, mientras que los elementos `<a>` (`HTMLAnchorElement`) admiten `href`, etc. La mayoría de los atributos HTML estándar tienen una propiedad DOM correspondiente. -However, HTML attributes and DOM properties are not always the same, as we'll see in the next chapter. +Sin embargo, los atributos HTML y las propiedades DOM no siempre son iguales, como veremos en el próximo capítulo. diff --git a/2-ui/1-document/06-dom-attributes-and-properties/1-get-user-attribute/solution.md b/2-ui/1-document/06-dom-attributes-and-properties/1-get-user-attribute/solution.md index 0507832f3..aa91c52ef 100644 --- a/2-ui/1-document/06-dom-attributes-and-properties/1-get-user-attribute/solution.md +++ b/2-ui/1-document/06-dom-attributes-and-properties/1-get-user-attribute/solution.md @@ -4,15 +4,15 @@ <html> <body> - <div data-widget-name="menu">Choose the genre</div> + <div data-widget-name="menu">Elige el género</div> <script> - // getting it + // obteniéndolo let elem = document.querySelector('[data-widget-name]'); - // reading the value + // leyendo el valor alert(elem.dataset.widgetName); - // or + // o alert(elem.getAttribute('data-widget-name')); </script> </body> diff --git a/2-ui/1-document/06-dom-attributes-and-properties/1-get-user-attribute/task.md b/2-ui/1-document/06-dom-attributes-and-properties/1-get-user-attribute/task.md index 4cdf231b0..d73a65200 100644 --- a/2-ui/1-document/06-dom-attributes-and-properties/1-get-user-attribute/task.md +++ b/2-ui/1-document/06-dom-attributes-and-properties/1-get-user-attribute/task.md @@ -2,19 +2,19 @@ importance: 5 --- -# Get the attribute +# Obtén en atributo -Write the code to select the element with `data-widget-name` attribute from the document and to read its value. +Escribe el código para obtener el atributo `data-widget-name` del documento y leer su valor. ```html run <!DOCTYPE html> <html> <body> - <div data-widget-name="menu">Choose the genre</div> + <div data-widget-name="menu">Elige el genero</div> <script> - /* your code */ + /* Tu código */ </script> </body> </html> diff --git a/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/solution.md b/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/solution.md index 726be4c8f..6ef9444a0 100644 --- a/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/solution.md +++ b/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/solution.md @@ -1,9 +1,9 @@ -First, we need to find all external references. +Primero, necesitamos encontrar todos los enlaces externos. -There are two ways. +Hay dos. -The first is to find all links using `document.querySelectorAll('a')` and then filter out what we need: +El primero es encontrar todos los enlaces usando `document.querySelectorAll('a')` y luego filtrar lo que necesitamos: ```js let links = document.querySelectorAll('a'); @@ -12,23 +12,23 @@ for (let link of links) { *!* let href = link.getAttribute('href'); */!* - if (!href) continue; // no attribute + if (!href) continue; // no atributo - if (!href.includes('://')) continue; // no protocol + if (!href.includes('://')) continue; // no protocolo - if (href.startsWith('http://internal.com')) continue; // internal + if (href.startsWith('http://internal.com')) continue; // interno link.style.color = 'orange'; } ``` -Please note: we use `link.getAttribute('href')`. Not `link.href`, because we need the value from HTML. +Tenga en cuenta: nosotros usamos `link.getAttribute('href')`. No `link.href`, porque necesitamos el valor del HTML. -...Another, simpler way would be to add the checks to CSS selector: +...Otra forma más simple sería agregar las comprobaciones al selector CSS: ```js -// look for all links that have :// in href -// but href doesn't start with http://internal.com +// busque todos los enlaces que tengan: // en href +//pero href no comienza con http://internal.com let selector = 'a[href*="://"]:not([href^="http://internal.com"])'; let links = document.querySelectorAll(selector); diff --git a/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/solution.view/index.html b/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/solution.view/index.html index 4209a5f34..d2e6afaaa 100644 --- a/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/solution.view/index.html +++ b/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/solution.view/index.html @@ -2,7 +2,7 @@ <html> <body> - <a name="list">The list:</a> + <a name="list">La lista:</a> <ul> <li><a href="http://google.com">http://google.com</a></li> <li><a href="/tutorial">/tutorial.html</a></li> diff --git a/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/source.view/index.html b/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/source.view/index.html index e12048323..381f49def 100644 --- a/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/source.view/index.html +++ b/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/source.view/index.html @@ -2,7 +2,7 @@ <html> <body> - <a name="list">The list:</a> + <a name="list">La lista</a> <ul> <li><a href="http://google.com">http://google.com</a></li> <li><a href="/tutorial">/tutorial.html</a></li> @@ -13,7 +13,7 @@ </ul> <script> - // ...your code... + // ...Tu código... </script> </body> diff --git a/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/task.md b/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/task.md index b0a8ab7b1..f326bbe10 100644 --- a/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/task.md +++ b/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/task.md @@ -2,15 +2,15 @@ importance: 3 --- -# Make external links orange +# Haz los enlaces externos naranjas -Make all external links orange by altering their `style` property. +Haz todos los enlaces externos de color orange alterando su propiedad `style`. -A link is external if: -- Its `href` has `://` in it -- But doesn't start with `http://internal.com`. +Un link es externo si: +- Su `href` tiene `://` +- Pero no comienza con `http://internal.com`. -Example: +Ejemplo: ```html run <a name="list">the list</a> @@ -24,12 +24,12 @@ Example: </ul> <script> - // setting style for a single link + // establecer un estilo para un enlace let link = document.querySelector('a'); link.style.color = 'orange'; </script> ``` -The result should be: +El resultado podría ser: [iframe border=1 height=180 src="solution"] diff --git a/2-ui/1-document/06-dom-attributes-and-properties/article.md b/2-ui/1-document/06-dom-attributes-and-properties/article.md index b02f626dc..9bc8c4477 100644 --- a/2-ui/1-document/06-dom-attributes-and-properties/article.md +++ b/2-ui/1-document/06-dom-attributes-and-properties/article.md @@ -1,150 +1,150 @@ -# Attributes and properties +# Atributos y propiedades -When the browser loads the page, it "reads" (another word: "parses") the HTML and generates DOM objects from it. For element nodes, most standard HTML attributes automatically become properties of DOM objects. +Cuando el navegador carga la página, "lee" (o "parser"(analiza en inglés")) el HTML y genera objetos DOM a partir de él. Para los nodos de elementos, la mayoría de los atributos HTML estándar se convierten automáticamente en propiedades de los objetos DOM. -For instance, if the tag is `<body id="page">`, then the DOM object has `body.id="page"`. +Por ejemplo, si la etiqueta es `<body id="page">`, entonces el objeto DOM tiene `body.id="page"`. -But the attribute-property mapping is not one-to-one! In this chapter we'll pay attention to separate these two notions, to see how to work with them, when they are the same, and when they are different. +¡Pero el mapeo de propiedades y atributos no es uno a uno! En este capítulo, prestaremos atención para separar estas dos nociones, para ver cómo trabajar con ellos, cuándo son iguales y cuándo son diferentes. -## DOM properties +## Propiedades DOM -We've already seen built-in DOM properties. There are a lot. But technically no one limits us, and if there aren't enough, we can add our own. +Ya hemos visto propiedades DOM integradas. Hay muchas. Pero técnicamente nadie nos limita, y si no hay suficientes, podemos agregar las nuestras. -DOM nodes are regular JavaScript objects. We can alter them. +Los nodos DOM son objetos JavaScript normales. Podemos alterarlos. -For instance, let's create a new property in `document.body`: +Por ejemplo, creemos una nueva propiedad en `document.body`: ```js run document.body.myData = { - name: 'Caesar', - title: 'Imperator' + name: 'Cesar', + title: 'Emperador' }; -alert(document.body.myData.title); // Imperator +alert(document.body.myData.title); // Emperador ``` -We can add a method as well: +También podemos agregar un método: ```js run document.body.sayTagName = function() { alert(this.tagName); }; -document.body.sayTagName(); // BODY (the value of "this" in the method is document.body) +document.body.sayTagName(); // BODY (el valor de 'this' en el método es document.body) ``` -We can also modify built-in prototypes like `Element.prototype` and add new methods to all elements: +También podemos modificar prototipos incorporados como `Element.prototype` y agregar nuevos métodos a todos los elementos: ```js run Element.prototype.sayHi = function() { - alert(`Hello, I'm ${this.tagName}`); + alert(`Hola, yo soy ${this.tagName}`); }; -document.documentElement.sayHi(); // Hello, I'm HTML -document.body.sayHi(); // Hello, I'm BODY +document.documentElement.sayHi(); // Hola, yo soy HTML +document.body.sayHi(); // Hola, yo soy BODY ``` -So, DOM properties and methods behave just like those of regular JavaScript objects: +Por lo tanto, las propiedades y métodos DOM se comportan igual que los objetos JavaScript normales: -- They can have any value. -- They are case-sensitive (write `elem.nodeType`, not `elem.NoDeTyPe`). +- Pueden tener cualquier valor. +- Distingue entre mayúsculas y minúsculas (escribir `elem.nodeType`, no es lo mismo que `elem.NoDeTyPe`). -## HTML attributes +## Atributos HTML -In HTML, tags may have attributes. When the browser parses the HTML to create DOM objects for tags, it recognizes *standard* attributes and creates DOM properties from them. +En HTML, las etiquetas pueden tener atributos. Cuando el navegador analiza el HTML para crear objetos DOM para etiquetas, reconoce los atributos *estándar* y crea propiedades DOM a partir de ellos. -So when an element has `id` or another *standard* attribute, the corresponding property gets created. But that doesn't happen if the attribute is non-standard. +Entonces, cuando un elemento tiene `id` u otro atributo *estándar*, se crea la propiedad correspondiente. Pero eso no sucede si el atributo no es estándar. -For instance: +Por ejemplo: ```html run <body id="test" something="non-standard"> <script> - alert(document.body.id); // test + alert(document.body.id); // prueba *!* - // non-standard attribute does not yield a property + // el atributo no estándar no produce una propiedad alert(document.body.something); // undefined */!* </script> </body> ``` -Please note that a standard attribute for one element can be unknown for another one. For instance, `"type"` is standard for `<input>` ([HTMLInputElement](https://html.spec.whatwg.org/#htmlinputelement)), but not for `<body>` ([HTMLBodyElement](https://html.spec.whatwg.org/#htmlbodyelement)). Standard attributes are described in the specification for the corresponding element class. +Tenga en cuenta que un atributo estándar para un elemento puede ser desconocido para otro. Por ejemplo, `"type"` es estándar para `<input>` ([HTMLInputElement](https://html.spec.whatwg.org/#htmlinputelement)), pero no para `<body>` ([HTMLBodyElement](https://html.spec.whatwg.org/#htmlbodyelement)). Los atributos estándar se describen en la especificación para la clase del elemento correspondiente. -Here we can see it: +Aquí podemos ver esto: ```html run <body id="body" type="..."> <input id="input" type="text"> <script> alert(input.type); // text *!* - alert(body.type); // undefined: DOM property not created, because it's non-standard + alert(body.type); // undefined: Propiedad DOM no creada, porque no es estándar */!* </script> </body> ``` -So, if an attribute is non-standard, there won't be a DOM-property for it. Is there a way to access such attributes? +Entonces, si un atributo no es estándar, no habrá una propiedad DOM para él. ¿Hay alguna manera de acceder a tales atributos? -Sure. All attributes are accessible by using the following methods: +Claro. Todos los atributos son accesibles usando los siguientes métodos: -- `elem.hasAttribute(name)` -- checks for existence. -- `elem.getAttribute(name)` -- gets the value. -- `elem.setAttribute(name, value)` -- sets the value. -- `elem.removeAttribute(name)` -- removes the attribute. +- `elem.hasAttribute(nombre)` -- comprueba si existe. +- `elem.getAttribute(nombre)` -- obtiene el valor. +- `elem.setAttribute(nombre, valor)` -- establece el valor. +- `elem.removeAttribute(nombre)` -- elimina el atributo. -These methods operate exactly with what's written in HTML. +Estos métodos funcionan exactamente con lo que está escrito en HTML. -Also one can read all attributes using `elem.attributes`: a collection of objects that belong to a built-in [Attr](https://dom.spec.whatwg.org/#attr) class, with `name` and `value` properties. +También se pueden leer todos los atributos usando `elem.attributes`: una colección de objetos que pertenecen a una clase integrada [Attr](https://dom.spec.whatwg.org/#attr), con propiedades `nombre` y `valor` . -Here's a demo of reading a non-standard property: +Aquí hay una demostración de la lectura de una propiedad no estándar: ```html run <body something="non-standard"> <script> *!* - alert(document.body.getAttribute('something')); // non-standard -*/!* + alert(document.body.getAttribute('something')); // no estándar +*/!* </script> </body> ``` -HTML attributes have the following features: +Los atributos HTML tienen las siguientes características: -- Their name is case-insensitive (`id` is same as `ID`). -- Their values are always strings. +- Su nombre no distingue entre mayúsculas y minúsculas (`id` es igual a` ID`). +- Sus valores son siempre strings. -Here's an extended demo of working with attributes: +Aquí hay una demostración extendida de cómo trabajar con atributos: ```html run <body> <div id="elem" about="Elephant"></div> <script> - alert( elem.getAttribute('About') ); // (1) 'Elephant', reading + alert( elem.getAttribute('About') ); // (1) 'Elephant', leyendo - elem.setAttribute('Test', 123); // (2), writing + elem.setAttribute('Test', 123); // (2), escribiendo - alert( elem.outerHTML ); // (3), see if the attribute is in HTML (yes) + alert( elem.outerHTML ); // (3), ver si el atributo está en HTML (sí) - for (let attr of elem.attributes) { // (4) list all + for (let attr of elem.attributes) { // (4) listar todo alert( `${attr.name} = ${attr.value}` ); } </script> </body> ``` -Please note: +Tenga en cuenta: -1. `getAttribute('About')` -- the first letter is uppercase here, and in HTML it's all lowercase. But that doesn't matter: attribute names are case-insensitive. -2. We can assign anything to an attribute, but it becomes a string. So here we have `"123"` as the value. -3. All attributes including ones that we set are visible in `outerHTML`. -4. The `attributes` collection is iterable and has all the attributes of the element (standard and non-standard) as objects with `name` and `value` properties. +1. `getAttribute ('About)` - la primera letra está en mayúscula aquí, y en HTML todo está en minúscula. Pero eso no importa: los nombres de los atributos no distinguen entre mayúsculas y minúsculas. +2. Podemos asignar cualquier cosa a un atributo, pero se convierte en un string. Así que aquí tenemos `"123"` como valor. +3. Todos los atributos, incluidos los que configuramos, son visibles en `outerHTML`. +4. La colección `attributes` es iterable y tiene todos los atributos del elemento (estándar y no estándar) como objetos con propiedades `name` y `value`. -## Property-attribute synchronization +## Sincronización de propiedad y atributo -When a standard attribute changes, the corresponding property is auto-updated, and (with some exceptions) vice versa. +Cuando cambia un atributo estándar, la propiedad correspondiente se actualiza automáticamente, y (con algunas excepciones) viceversa. -In the example below `id` is modified as an attribute, and we can see the property changed too. And then the same backwards: +En el ejemplo a continuación, `id` se modifica como un atributo, y podemos ver que la propiedad también es cambiada. Y luego lo mismo al revés: ```html run <input> @@ -152,17 +152,17 @@ In the example below `id` is modified as an attribute, and we can see the proper <script> let input = document.querySelector('input'); - // attribute => property + // atributo -> propiedad input.setAttribute('id', 'id'); - alert(input.id); // id (updated) + alert(input.id); // id (actualizado) - // property => attribute + // propiedad -> atributo input.id = 'newId'; - alert(input.getAttribute('id')); // newId (updated) + alert(input.getAttribute('id')); // newId (actualizado) </script> ``` -But there are exclusions, for instance `input.value` synchronizes only from attribute -> property, but not back: +Pero hay exclusiones, por ejemplo, `input.value` se sincroniza solo del atributo a la propiedad (atributo -> propiedad), pero no de regreso: ```html run <input> @@ -170,41 +170,41 @@ But there are exclusions, for instance `input.value` synchronizes only from attr <script> let input = document.querySelector('input'); - // attribute => property + // atributo -> propiedad input.setAttribute('value', 'text'); alert(input.value); // text *!* - // NOT property => attribute + // NO propiedad -> atributo input.value = 'newValue'; - alert(input.getAttribute('value')); // text (not updated!) + alert(input.getAttribute('value')); // text (¡no actualizado!) */!* </script> ``` -In the example above: -- Changing the attribute `value` updates the property. -- But the property change does not affect the attribute. +En el ejemplo anterior: +- Cambiar el atributo `value` actualiza la propiedad. +- Pero el cambio de propiedad no afecta al atributo. -That "feature" may actually come in handy, because the user actions may lead to `value` changes, and then after them, if we want to recover the "original" value from HTML, it's in the attribute. +Esa "característica" en realidad puede ser útil, porque las acciones del usuario pueden conducir a cambios de `value`, y luego, si queremos recuperar el valor "original" de HTML, está en el atributo. -## DOM properties are typed +## Las propiedades DOM tienen tipo -DOM properties are not always strings. For instance, the `input.checked` property (for checkboxes) is a boolean: +Las propiedades DOM no siempre son strings. Por ejemplo, la propiedad `input.checked` (para casillas de verificación) es un booleano: ```html run <input id="input" type="checkbox" checked> checkbox <script> - alert(input.getAttribute('checked')); // the attribute value is: empty string - alert(input.checked); // the property value is: true + alert(input.getAttribute('checked')); // el valor del atributo es: string vacía + alert(input.checked); // el valor de la propiedad es: true </script> ``` -There are other examples. The `style` attribute is a string, but the `style` property is an object: +Hay otros ejemplos. El atributo `style` es un string, pero la propiedad `style` es un objeto: ```html run -<div id="div" style="color:red;font-size:120%">Hello</div> +<div id="div" style="color:red;font-size:120%">Hola</div> <script> // string @@ -216,171 +216,171 @@ There are other examples. The `style` attribute is a string, but the `style` pro </script> ``` -Most properties are strings though. +La mayoría de las propiedades son strings. -Quite rarely, even if a DOM property type is a string, it may differ from the attribute. For instance, the `href` DOM property is always a *full* URL, even if the attribute contains a relative URL or just a `#hash`. +Muy raramente, incluso si un tipo de propiedad DOM es un string, puede diferir del atributo. Por ejemplo, la propiedad DOM `href` siempre es una URL *completa*, incluso si el atributo contiene una URL relativa o solo un `#hash`. -Here's an example: +Aquí hay un ejemplo: ```html height=30 run -<a id="a" href="#hello">link</a> +<a id="a" href="#hola">link</a> <script> - // attribute - alert(a.getAttribute('href')); // #hello + // atributo + alert(a.getAttribute('href')); // #hola - // property - alert(a.href ); // full URL in the form http://site.com/page#hello + // propiedad + alert(a.href ); // URL completa de http://site.com/page#hola </script> ``` -If we need the value of `href` or any other attribute exactly as written in the HTML, we can use `getAttribute`. +Si necesitamos el valor de `href` o cualquier otro atributo exactamente como está escrito en el HTML, podemos usar `getAttribute`. -## Non-standard attributes, dataset +## Atributos no estándar, dataset -When writing HTML, we use a lot of standard attributes. But what about non-standard, custom ones? First, let's see whether they are useful or not? What for? +Cuando escribimos HTML, usamos muchos atributos estándar. Pero, ¿qué pasa con los no personalizados y personalizados? Primero, veamos si son útiles o no. ¿Para qué? -Sometimes non-standard attributes are used to pass custom data from HTML to JavaScript, or to "mark" HTML-elements for JavaScript. +A veces, los atributos no estándar se utilizan para pasar datos personalizados de HTML a JavaScript, o para "marcar" elementos HTML para JavaScript. -Like this: +Como esto: ```html run -<!-- mark the div to show "name" here --> -<div *!*show-info="name"*/!*></div> -<!-- and age here --> -<div *!*show-info="age"*/!*></div> +<!-- marque el div para mostrar "nombre" aquí --> +<div *!*show-info="nombre"*/!*></div> +<!-- y "edad" aquí --> +<div *!*show-info="edad"*/!*></div> <script> - // the code finds an element with the mark and shows what's requested + // el código encuentra un elemento con la marca y muestra lo que se solicita let user = { - name: "Pete", - age: 25 + nombre: "Pete", + edad: 25 }; for(let div of document.querySelectorAll('[show-info]')) { - // insert the corresponding info into the field + // inserta la información correspondiente en el campo let field = div.getAttribute('show-info'); - div.innerHTML = user[field]; // first Pete into "name", then 25 into "age" + div.innerHTML = user[field]; // primero Pete en "nombre", luego 25 en "edad" } </script> ``` -Also they can be used to style an element. +También se pueden usar para diseñar un elemento. -For instance, here for the order state the attribute `order-state` is used: +Por ejemplo, aquí para el estado del pedido se usa el atributo `order-state`: ```html run <style> - /* styles rely on the custom attribute "order-state" */ - .order[order-state="new"] { + /* los estilos se basan en el atributo personalizado "order-state" */ + .order[order-state="nuevo"] { color: green; } - .order[order-state="pending"] { + .order[order-state="pendiente"] { color: blue; } - .order[order-state="canceled"] { + .order[order-state="cancelado"] { color: red; } </style> -<div class="order" order-state="new"> - A new order. +<div class="order" order-state="nuevo"> + Un nuevo pedido. </div> -<div class="order" order-state="pending"> - A pending order. +<div class="order" order-state="pendiente"> + Un pedido pendiente. </div> -<div class="order" order-state="canceled"> - A canceled order. +<div class="order" order-state="cancelado"> + Un pedido cancelado </div> ``` -Why would using an attribute be preferable to having classes like `.order-state-new`, `.order-state-pending`, `.order-state-canceled`? +¿Por qué sería preferible usar un atributo a tener clases como `.order-state-new`, `.order-state-pending`, `.order-state-canceled`? -Because an attribute is more convenient to manage. The state can be changed as easy as: +Porque un atributo es más conveniente de administrar. El estado se puede cambiar tan fácil como: ```js -// a bit simpler than removing old/adding a new class +// un poco más simple que eliminar/agregar clases div.setAttribute('order-state', 'canceled'); ``` -But there may be a possible problem with custom attributes. What if we use a non-standard attribute for our purposes and later the standard introduces it and makes it do something? The HTML language is alive, it grows, and more attributes appear to suit the needs of developers. There may be unexpected effects in such case. +Pero puede haber un posible problema con los atributos personalizados. ¿Qué sucede si usamos un atributo no estándar para nuestros propósitos y luego el estándar lo introduce y hace que haga algo? El lenguaje HTML está vivo, crece y cada vez hay más atributos que aparecen para satisfacer las necesidades de los desarrolladores. Puede haber efectos inesperados en tal caso. -To avoid conflicts, there exist [data-*](https://html.spec.whatwg.org/#embedding-custom-non-visible-data-with-the-data-*-attributes) attributes. +Para evitar conflictos, existen atributos [data-*](https://html.spec.whatwg.org/#embedding-custom-non-visible-data-with-the-data-*-attributes). -**All attributes starting with "data-" are reserved for programmers' use. They are available in the `dataset` property.** +**Todos los atributos que comienzan con "data-" están reservados para el uso de los programadores. Están disponibles en la propiedad `dataset`.** -For instance, if an `elem` has an attribute named `"data-about"`, it's available as `elem.dataset.about`. +Por ejemplo, si un `elem` tiene un atributo llamado `"data-about"`, está disponible como `elem.dataset.about`. -Like this: +Como esto: ```html run -<body data-about="Elephants"> +<body data-about="Elefante"> <script> - alert(document.body.dataset.about); // Elephants + alert(document.body.dataset.about); // Elefante </script> ``` -Multiword attributes like `data-order-state` become camel-cased: `dataset.orderState`. +Los atributos de varias palabras como `data-order-state` se convierten en camel-case: `dataset.orderState` -Here's a rewritten "order state" example: +Aquí hay un ejemplo reescrito de "estado del pedido": ```html run <style> - .order[data-order-state="new"] { + .order[data-order-state="nuevo"] { color: green; } - .order[data-order-state="pending"] { + .order[data-order-state="pendiente"] { color: blue; } - .order[data-order-state="canceled"] { + .order[data-order-state="cancelado"] { color: red; } </style> -<div id="order" class="order" data-order-state="new"> - A new order. +<div id="order" class="order" data-order-state="nuevo"> + Una nueva orden. </div> <script> - // read - alert(order.dataset.orderState); // new + // leer + alert(order.dataset.orderState); // nuevo - // modify - order.dataset.orderState = "pending"; // (*) + // modificar + order.dataset.orderState = "pendiente"; // (*) </script> ``` -Using `data-*` attributes is a valid, safe way to pass custom data. +El uso de los atributos `data- *` es una forma válida y segura de pasar datos personalizados. -Please note that we can not only read, but also modify data-attributes. Then CSS updates the view accordingly: in the example above the last line `(*)` changes the color to blue. +Tenga en cuenta que no solo podemos leer, sino también modificar los atributos de datos. Luego, CSS actualiza la vista en consecuencia: en el ejemplo anterior, la última línea `(*)` cambia el color a azul. -## Summary +## Resumen -- Attributes -- is what's written in HTML. -- Properties -- is what's in DOM objects. +- Atributos: es lo que está escrito en HTML. +- Propiedades: es lo que hay en los objetos DOM. -A small comparison: +Una pequeña comparación: -| | Properties | Attributes | +| | Propiedades | Atributos | |------------|------------|------------| -|Type|Any value, standard properties have types described in the spec|A string| -|Name|Name is case-sensitive|Name is not case-sensitive| +|Tipo|Cualquier valor, las propiedades estándar tienen tipos descritos en la especificación|Un string| +|Nombre|El nombre distingue entre mayúsculas y minúsculas|El nombre no distingue entre mayúsculas y minúsculas| -Methods to work with attributes are: +Los métodos para trabajar con atributos son: -- `elem.hasAttribute(name)` -- to check for existence. -- `elem.getAttribute(name)` -- to get the value. -- `elem.setAttribute(name, value)` -- to set the value. -- `elem.removeAttribute(name)` -- to remove the attribute. -- `elem.attributes` is a collection of all attributes. +- `elem.hasAttribute(nombre)` -- para comprobar si existe. +- `elem.getAttribute(nombre)` -- para obtener el valor. +- `elem.setAttribute(nombre, valor)` -- para dar un valor. +- `elem.removeAttribute(nombre)` -- para eliminar el atributo. +- `elem.attributes` es una colección de todos los atributos. -For most situations using DOM properties is preferable. We should refer to attributes only when DOM properties do not suit us, when we need exactly attributes, for instance: +Para la mayoría de las situaciones, es preferible usar las propiedades DOM. Deberíamos referirnos a los atributos solo cuando las propiedades DOM no nos convienen, cuando necesitamos exactamente atributos, por ejemplo: -- We need a non-standard attribute. But if it starts with `data-`, then we should use `dataset`. -- We want to read the value "as written" in HTML. The value of the DOM property may be different, for instance the `href` property is always a full URL, and we may want to get the "original" value. +- Necesitamos un atributo no estándar. Pero si comienza con `data-`, entonces deberíamos usar `dataset`. +- Queremos leer el valor "como está escrito" en HTML. El valor de la propiedad DOM puede ser diferente, por ejemplo, la propiedad `href` siempre es una URL completa, y es posible que queramos obtener el valor "original ". diff --git a/2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/solution.md b/2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/solution.md index a38f01645..a4dbf05a7 100644 --- a/2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/solution.md +++ b/2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/solution.md @@ -1,8 +1,8 @@ -Answer: **1 and 3**. +Respuesta: **1 y 3**. -Both commands result in adding the `text` "as text" into the `elem`. +Ambos comandos agregan `text` "como texto" dentro de `elem`. -Here's an example: +Aquí el ejemplo: ```html run height=80 <div id="elem1"></div> diff --git a/2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/task.md b/2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/task.md index 40c75dff3..f8855eed8 100644 --- a/2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/task.md +++ b/2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/task.md @@ -4,9 +4,9 @@ importance: 5 # createTextNode vs innerHTML vs textContent -We have an empty DOM element `elem` and a string `text`. +Tenemos un elemento DOM vacio `elem` y un string `text`. -Which of these 3 commands will do exactly the same? +¿Cuáles de estos 3 comandos harán exactamente lo mismo? 1. `elem.append(document.createTextNode(text))` 2. `elem.innerHTML = text` diff --git a/2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.md b/2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.md index 1414e90c1..09655fad1 100644 --- a/2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.md +++ b/2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.md @@ -1,6 +1,6 @@ -First, let's make HTML/CSS. +Primero escribamos HTML/CSS. -Each component of the time would look great in its own `<span>`: +Cada componente de la hora se verá muy bien dentro de su propio `<span>`: ```html <div id="clock"> @@ -8,9 +8,9 @@ Each component of the time would look great in its own `<span>`: </div> ``` -Also we'll need CSS to color them. +También necesitamos CSS para colorearlos. -The `update` function will refresh the clock, to be called by `setInterval` every second: +La función `update` que refrescará el reloj será llamada por `setInterval` una vez por segundo: ```js function update() { @@ -32,15 +32,15 @@ function update() { } ``` -In the line `(*)` we every time check the current date. The calls to `setInterval` are not reliable: they may happen with delays. +En la línea `(*)` verificamos la hora cada vez. Las llamadas a `setInterval` no son confiables: pueden ocurrir con demoras. -The clock-managing functions: +Las funciones que manejan el reloj: ```js let timerId; -function clockStart() { // run the clock - if (!timerId) { // only set a new interval if the clock is not running +function clockStart() { // ejecuta el reloj + if (!timerId) { // solo establece un nuevo intervalo si el reloj no está corriendo timerId = setInterval(update, 1000); } update(); // (*) @@ -52,6 +52,6 @@ function clockStop() { } ``` -Please note that the call to `update()` is not only scheduled in `clockStart()`, but immediately run in the line `(*)`. Otherwise the visitor would have to wait till the first execution of `setInterval`. And the clock would be empty till then. +Nota que la llamada a `update()` no solo está agendada en `clockStart()`, también la ejecuta inmediatamente en la línea `(*)`. De otro modo el visitante tendría que esperar hasta la primera ejecución de `setInterval`. Y el reloj estaría vacío hasta entonces. -Also it is important to set a new interval in `clockStart()` only when the clock is not running. Otherways clicking the start button several times would set multiple concurrent intervals. Even worse - we would only keep the `timerID` of the last interval, losing references to all others. Then we wouldn't be able to stop the clock ever again! Note that we need to clear the `timerID` when the clock is stopped in the line `(**)`, so that it can be started again by running `clockStart()`. +También es importante establecer un nuevo intervalo en `clockStart()` solamente cuando el reloj no está corriendo. De otra forma al cliquear el botón de inicio varias veces se establecerían múltiples intervalos concurrentes. Peor aún, solo mantendríamos el `timerID` del último intervalo, perdiendo referencia a todos los demás. ¡No podríamos detener el reloj nunca más! Nota que necesitamos limpiar `timerID` cuando el reloj es detenido en la línea `(**)`, así puede ser reiniciado corriendo `clockStart()`. diff --git a/2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.view/index.html b/2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.view/index.html index 84ee26f19..c79d4011a 100644 --- a/2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.view/index.html +++ b/2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.view/index.html @@ -43,25 +43,25 @@ } function clockStart() { - // set a new interval only if the clock is stopped - // otherwise we would rewrite the timerID reference to the running interval and wouldn't be able to stop the clock ever again - if (!timerId) { + // establece un nuevo intervalo solo si el reloj está detenido + // de otro modo sobreescribiríamos la referencia timerID del intervalo en ejecución y no podríamos detener el reloj nunca más + if (!timerId) { timerId = setInterval(update, 1000); } - update(); // <-- start right now, don't wait 1 second till the first setInterval works + update(); // <-- inicia ahora mismo, no espera 1 second hasta el primer intervalo } function clockStop() { clearInterval(timerId); - timerId = null; // <-- clear timerID to indicate that the clock has been stopped, so that it is possible to start it again in clockStart() + timerId = null; // <-- borra timerID para indicar que el reloj fue detenido, haciendo posible iniciarlo de nuevo en clockStart() } </script> - <!-- click on this button calls clockStart() --> + <!-- cliquear este botón llama a clockStart() --> <input type="button" onclick="clockStart()" value="Start"> - <!-- click on this button calls clockStop() --> + <!-- cliquear este botón llama a clockStop() --> <input type="button" onclick="clockStop()" value="Stop"> </body> diff --git a/2-ui/1-document/07-modifying-document/10-clock-setinterval/source.view/index.html b/2-ui/1-document/07-modifying-document/10-clock-setinterval/source.view/index.html index ecf5df99a..0782b58d1 100644 --- a/2-ui/1-document/07-modifying-document/10-clock-setinterval/source.view/index.html +++ b/2-ui/1-document/07-modifying-document/10-clock-setinterval/source.view/index.html @@ -2,10 +2,10 @@ <html> <body> - <!-- click on this button calls clockStart() --> + <!-- cliquear en este botón llama a clockStart() --> <input type="button" onclick="clockStart()" value="Start"> - <!-- click on this button calls clockStop() --> + <!-- cliquear en este botón llama a clockStop() --> <input type="button" onclick="clockStop()" value="Stop"> </body> diff --git a/2-ui/1-document/07-modifying-document/10-clock-setinterval/task.md b/2-ui/1-document/07-modifying-document/10-clock-setinterval/task.md index a1b53e337..e90cbdc23 100644 --- a/2-ui/1-document/07-modifying-document/10-clock-setinterval/task.md +++ b/2-ui/1-document/07-modifying-document/10-clock-setinterval/task.md @@ -2,10 +2,10 @@ importance: 4 --- -# Colored clock with setInterval +# Reloj coloreado con setInterval -Create a colored clock like here: +Crea un reloj coloreado como aquí: [iframe src="solution" height=60] -Use HTML/CSS for the styling, JavaScript only updates time in elements. +Usa HTML/CSS para el estilo, JavaScript solamente actualiza la hora en elements. diff --git a/2-ui/1-document/07-modifying-document/11-append-to-list/solution.md b/2-ui/1-document/07-modifying-document/11-append-to-list/solution.md index 4e77fb5cb..3df90497f 100644 --- a/2-ui/1-document/07-modifying-document/11-append-to-list/solution.md +++ b/2-ui/1-document/07-modifying-document/11-append-to-list/solution.md @@ -1,7 +1,7 @@ -When we need to insert a piece of HTML somewhere, `insertAdjacentHTML` is the best fit. +Cuando necesitamos insertar una pieza de HTML en algún lugar, `insertAdjacentHTML` es lo más adecuado. -The solution: +La solución: ```js one.insertAdjacentHTML('afterend', '<li>2</li><li>3</li>'); diff --git a/2-ui/1-document/07-modifying-document/11-append-to-list/task.md b/2-ui/1-document/07-modifying-document/11-append-to-list/task.md index 543cd3e46..a3ec08db3 100644 --- a/2-ui/1-document/07-modifying-document/11-append-to-list/task.md +++ b/2-ui/1-document/07-modifying-document/11-append-to-list/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# Insert the HTML in the list +# Inserta el HTML en la lista -Write the code to insert `<li>2</li><li>3</li>` between two `<li>` here: +Escribe el código para insertar `<li>2</li><li>3</li>` entre dos `<li>` aquí: ```html <ul id="ul"> diff --git a/2-ui/1-document/07-modifying-document/12-sort-table/solution.md b/2-ui/1-document/07-modifying-document/12-sort-table/solution.md index 49243e8e3..8f3811c09 100644 --- a/2-ui/1-document/07-modifying-document/12-sort-table/solution.md +++ b/2-ui/1-document/07-modifying-document/12-sort-table/solution.md @@ -1,4 +1,4 @@ -The solution is short, yet may look a bit tricky, so here I provide it with extensive comments: +La solución es corta, pero puede verse algo dificultosa así que brindamos comentarios extendidos: ```js let sortedRows = Array.from(table.tBodies[0].rows) // 1 @@ -7,12 +7,12 @@ let sortedRows = Array.from(table.tBodies[0].rows) // 1 table.tBodies[0].append(...sortedRows); // (3) ``` -The step-by-step algorthm: +El algoritmo paso a paso: -1. Get all `<tr>`, from `<tbody>`. -2. Then sort them comparing by the content of the first `<td>` (the name field). -3. Now insert nodes in the right order by `.append(...sortedRows)`. +1. Obtener todos los `<tr>` de `<tbody>`. +2. Entonces ordenarlos comparando por el contenido de su primer `<td>` (el campo nombre). +3. Ahora insertar nodos en el orden correcto con `.append(...sortedRows)`. -We don't have to remove row elements, just "re-insert", they leave the old place automatically. +No necesitamos quitar los elementos row, simplemente "reinsertarlos", ellos dejan el viejo lugar automáticamente. -P.S. In our case, there's an explicit `<tbody>` in the table, but even if HTML table doesn't have `<tbody>`, the DOM structure always has it. +P.S. En nuestro caso, hay un `<tbody>` explícito en la tabla, pero incluso si la tabla HTML no tiene `<tbody>`, la estructura DOM siempre lo tiene. diff --git a/2-ui/1-document/07-modifying-document/12-sort-table/source.view/index.html b/2-ui/1-document/07-modifying-document/12-sort-table/source.view/index.html index 9071c88ee..724e9d1cb 100644 --- a/2-ui/1-document/07-modifying-document/12-sort-table/source.view/index.html +++ b/2-ui/1-document/07-modifying-document/12-sort-table/source.view/index.html @@ -23,5 +23,5 @@ </table> <script> - // ... your code ... + // ... tu código ... </script> diff --git a/2-ui/1-document/07-modifying-document/12-sort-table/task.md b/2-ui/1-document/07-modifying-document/12-sort-table/task.md index 7cdba35bc..01053c22b 100644 --- a/2-ui/1-document/07-modifying-document/12-sort-table/task.md +++ b/2-ui/1-document/07-modifying-document/12-sort-table/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# Sort the table +# Ordena la tabla -There's a table: +Tenemos una tabla: ```html run <table> @@ -30,6 +30,6 @@ There's a table: </table> ``` -There may be more rows in it. +Puede haber más filas en ella. -Write the code to sort it by the `"name"` column. +Escribe el código para ordenarla por la columna `"name"`. diff --git a/2-ui/1-document/07-modifying-document/4-clear-elem/solution.md b/2-ui/1-document/07-modifying-document/4-clear-elem/solution.md index 62c3386d8..c2e4317a1 100644 --- a/2-ui/1-document/07-modifying-document/4-clear-elem/solution.md +++ b/2-ui/1-document/07-modifying-document/4-clear-elem/solution.md @@ -1,5 +1,5 @@ -First, let's see how *not* to do it: +Primero veamos cómo *no* hacerlo: ```js function clear(elem) { @@ -9,11 +9,11 @@ function clear(elem) { } ``` -That won't work, because the call to `remove()` shifts the collection `elem.childNodes`, so elements start from the index `0` every time. But `i` increases, and some elements will be skipped. +Eso no funciona, porque la llamada a `remove()` desplaza la colección `elem.childNodes`, entonces los elementos comienzan desde el índice `0` cada vez. Pero `i` se incrementa y algunos elementos serán saltados. -The `for..of` loop also does the same. +El bucle `for..of` también hace lo mismo. -The right variant could be: +Una variante correcta puede ser: ```js function clear(elem) { @@ -23,7 +23,7 @@ function clear(elem) { } ``` -And also there's a simpler way to do the same: +Y también una manera más simple de hacer lo mismo: ```js function clear(elem) { diff --git a/2-ui/1-document/07-modifying-document/4-clear-elem/task.md b/2-ui/1-document/07-modifying-document/4-clear-elem/task.md index 938d53470..5f308f6a4 100644 --- a/2-ui/1-document/07-modifying-document/4-clear-elem/task.md +++ b/2-ui/1-document/07-modifying-document/4-clear-elem/task.md @@ -2,19 +2,19 @@ importance: 5 --- -# Clear the element +# Limpiar el elemento -Create a function `clear(elem)` that removes everything from the element. +Crea una función `clear(elem)` que remueva todo del elemento. ```html run height=60 <ol id="elem"> - <li>Hello</li> - <li>World</li> + <li>Hola</li> + <li>mundo</li> </ol> <script> - function clear(elem) { /* your code */ } + function clear(elem) { /* tu código */ } - clear(elem); // clears the list + clear(elem); // borra la lista </script> ``` diff --git a/2-ui/1-document/07-modifying-document/5-why-aaa/solution.md b/2-ui/1-document/07-modifying-document/5-why-aaa/solution.md index 3d1f6698f..84bb42dde 100644 --- a/2-ui/1-document/07-modifying-document/5-why-aaa/solution.md +++ b/2-ui/1-document/07-modifying-document/5-why-aaa/solution.md @@ -1,9 +1,9 @@ -The HTML in the task is incorrect. That's the reason of the odd thing. +El HTML de la tarea es incorrecto. Esa es la razón del comportamiento extraño. -The browser has to fix it automatically. But there may be no text inside the `<table>`: according to the spec only table-specific tags are allowed. So the browser shows `"aaa"` *before* the `<table>`. +El navegador tiene que corregirlo automáticamente. No debe haber texto dentro de `<table>`: de acuerdo con la especificación solo son permitidas las etiquetas específicas de tabla. Entonces el navegador ubica `"aaa"` *antes* de `<table>`. -Now it's obvious that when we remove the table, it remains. +Ahora resulta obvio que cuando quitamos la tabla, ese texto permanece. -The question can be easily answered by exploring the DOM using the browser tools. You'll see `"aaa"` before the `<table>`. +La pregunta puede ser respondida fácilmente explorando el DOM usando la herramientas del navegador. Estas muestran `"aaa"` antes que `<table>`. -The HTML standard specifies in detail how to process bad HTML, and such behavior of the browser is correct. +El estándar HTML especifica en detalle cómo procesar HTML incorrecto, y tal comportamiento del navegador es el correcto. diff --git a/2-ui/1-document/07-modifying-document/5-why-aaa/task.md b/2-ui/1-document/07-modifying-document/5-why-aaa/task.md index 861f70503..619c832d0 100644 --- a/2-ui/1-document/07-modifying-document/5-why-aaa/task.md +++ b/2-ui/1-document/07-modifying-document/5-why-aaa/task.md @@ -2,13 +2,13 @@ importance: 1 --- -# Why does "aaa" remain? +# Por qué "aaa" permanece? -In the example below, the call `table.remove()` removes the table from the document. +En el ejemplo de abajo, la llamada `table.remove()` quita la tabla del documento. -But if you run it, you can see that the text `"aaa"` is still visible. +Pero si la ejecutas, puedes ver que el texto "aaa"` es aún visible. -Why does that happen? +¿Por qué ocurre esto? ```html height=100 run <table id="table"> @@ -19,9 +19,9 @@ Why does that happen? </table> <script> - alert(table); // the table, as it should be + alert(table); // la tabla, tal como debería ser table.remove(); - // why there's still "aaa" in the document? + // ¿Por qué aún está "aaa" en el documento? </script> ``` diff --git a/2-ui/1-document/07-modifying-document/6-create-list/solution.md b/2-ui/1-document/07-modifying-document/6-create-list/solution.md index 1669be18f..6e83786b4 100644 --- a/2-ui/1-document/07-modifying-document/6-create-list/solution.md +++ b/2-ui/1-document/07-modifying-document/6-create-list/solution.md @@ -1 +1 @@ -Please note the usage of `textContent` to assign the `<li>` content. +Observa el uso de `textContent` para asignar el contenido de `<li>`. diff --git a/2-ui/1-document/07-modifying-document/6-create-list/solution.view/index.html b/2-ui/1-document/07-modifying-document/6-create-list/solution.view/index.html index 071645e8d..4dbe27e36 100755 --- a/2-ui/1-document/07-modifying-document/6-create-list/solution.view/index.html +++ b/2-ui/1-document/07-modifying-document/6-create-list/solution.view/index.html @@ -1,14 +1,14 @@ <!DOCTYPE HTML> <html> <body> - <h1>Create a list</h1> + <h1>Crear un lista</h1> <script> let ul = document.createElement('ul'); document.body.append(ul); while (true) { - let data = prompt("Enter the text for the list item", ""); + let data = prompt("Ingresa el texto para el ítem de la lista", ""); if (!data) { break; diff --git a/2-ui/1-document/07-modifying-document/6-create-list/task.md b/2-ui/1-document/07-modifying-document/6-create-list/task.md index a57e7e2d9..2bbc5665c 100644 --- a/2-ui/1-document/07-modifying-document/6-create-list/task.md +++ b/2-ui/1-document/07-modifying-document/6-create-list/task.md @@ -2,18 +2,18 @@ importance: 4 --- -# Create a list +# Crear una lista -Write an interface to create a list from user input. +Escribir una interfaz para crear una lista de lo que ingresa un usuario. -For every list item: +Para cada item de la lista: -1. Ask a user about its content using `prompt`. -2. Create the `<li>` with it and add it to `<ul>`. -3. Continue until the user cancels the input (by pressing `key:Esc` or via an empty entry). +1. Preguntar al usuario acerca del contenido usando `prompt`. +2. Crear el `<li>` con ello y agregarlo a `<ul>`. +3. Continuar hasta que el usuario cancela el ingreso (presionando `key:Esc` o con un ingreso vacío). -All elements should be created dynamically. +Todos los elementos deben ser creados dinámicamente. -If a user types HTML-tags, they should be treated like a text. +Si el usuario ingresa etiquetas HTML, deben ser tratadas como texto. [demo src="solution"] diff --git a/2-ui/1-document/07-modifying-document/7-create-object-tree/build-tree-dom.view/index.html b/2-ui/1-document/07-modifying-document/7-create-object-tree/build-tree-dom.view/index.html index 06d9c01b1..45b31ee3f 100755 --- a/2-ui/1-document/07-modifying-document/7-create-object-tree/build-tree-dom.view/index.html +++ b/2-ui/1-document/07-modifying-document/7-create-object-tree/build-tree-dom.view/index.html @@ -28,8 +28,8 @@ } function createTreeDom(obj) { - // if there's no children, then the call returns undefined - // and the <ul> won't be created + // si no tiene hijos, el llamado devuelve undefined + // y el <ul> no será creado if (!Object.keys(obj).length) return; let ul = document.createElement('ul'); diff --git a/2-ui/1-document/07-modifying-document/7-create-object-tree/innerhtml.view/index.html b/2-ui/1-document/07-modifying-document/7-create-object-tree/innerhtml.view/index.html index 0f5f6b037..72a36ef53 100644 --- a/2-ui/1-document/07-modifying-document/7-create-object-tree/innerhtml.view/index.html +++ b/2-ui/1-document/07-modifying-document/7-create-object-tree/innerhtml.view/index.html @@ -27,7 +27,7 @@ container.innerHTML = createTreeText(obj); } - function createTreeText(obj) { // standalone recursive function + function createTreeText(obj) { // función recursiva aislada let li = ''; let ul; for (let key in obj) { diff --git a/2-ui/1-document/07-modifying-document/7-create-object-tree/solution.md b/2-ui/1-document/07-modifying-document/7-create-object-tree/solution.md index d29636ee2..daa72bd7e 100644 --- a/2-ui/1-document/07-modifying-document/7-create-object-tree/solution.md +++ b/2-ui/1-document/07-modifying-document/7-create-object-tree/solution.md @@ -1,4 +1,4 @@ -The easiest way to walk the object is to use recursion. +La forma más fácil de recorrer el objeto es usando recursividad. -1. [The solution with innerHTML](sandbox:innerhtml). -2. [The solution with DOM](sandbox:build-tree-dom). +1. [La solución con innerHTML](sandbox:innerhtml). +2. [La solución con DOM](sandbox:build-tree-dom). diff --git a/2-ui/1-document/07-modifying-document/7-create-object-tree/source.view/index.html b/2-ui/1-document/07-modifying-document/7-create-object-tree/source.view/index.html index 8586f6b24..89dd69b7f 100755 --- a/2-ui/1-document/07-modifying-document/7-create-object-tree/source.view/index.html +++ b/2-ui/1-document/07-modifying-document/7-create-object-tree/source.view/index.html @@ -9,7 +9,7 @@ <div id="tree"></div> - <!-- The result should be: + <!-- El resultado debe ser: <div id="tree"> <ul> <li>Fish @@ -58,7 +58,7 @@ }; function createTree(container, data) { - /* your code */ + /* tu código */ } createTree(document.getElementById('tree'), data); diff --git a/2-ui/1-document/07-modifying-document/7-create-object-tree/task.md b/2-ui/1-document/07-modifying-document/7-create-object-tree/task.md index 5ec1a01bc..ea0aa9801 100644 --- a/2-ui/1-document/07-modifying-document/7-create-object-tree/task.md +++ b/2-ui/1-document/07-modifying-document/7-create-object-tree/task.md @@ -2,11 +2,11 @@ importance: 5 --- -# Create a tree from the object +# Crea un árbol desde el objeto -Write a function `createTree` that creates a nested `ul/li` list from the nested object. +Escribe una función `createTree` que crea una lista ramificada `ul/li` desde un objeto ramificado. -For instance: +Por ejemplo: ```js let data = { @@ -28,24 +28,24 @@ let data = { }; ``` -The syntax: +La sintaxis: ```js let container = document.getElementById('container'); *!* -createTree(container, data); // creates the tree in the container +createTree(container, data); // crea el árbol en el contenedor */!* ``` -The result (tree) should look like this: +El árbol resultante debe verse así: [iframe border=1 src="build-tree-dom"] -Choose one of two ways of solving this task: +Elige una de estas dos formas para resolver esta tarea: -1. Create the HTML for the tree and then assign to `container.innerHTML`. -2. Create tree nodes and append with DOM methods. +1. Crear el HTML para el árbol y entonces asignarlo a `container.innerHTML`. +2. Crear los nodos del árbol y añadirlos con métodos DOM. -Would be great if you could do both. +Sería muy bueno que hicieras ambas soluciones. -P.S. The tree should not have "extra" elements like empty `<ul></ul>` for the leaves. +P.S. El árbol no debe tener elementos "extras" como `<ul></ul>` vacíos para las hojas. diff --git a/2-ui/1-document/07-modifying-document/8-tree-count/solution.md b/2-ui/1-document/07-modifying-document/8-tree-count/solution.md index 43b9a362c..03dc50768 100644 --- a/2-ui/1-document/07-modifying-document/8-tree-count/solution.md +++ b/2-ui/1-document/07-modifying-document/8-tree-count/solution.md @@ -1 +1 @@ -To append text to each `<li>` we can alter the text node `data`. +Para añadir texto a cada `<li>` podemos alterar el nodo texto `data`. diff --git a/2-ui/1-document/07-modifying-document/8-tree-count/solution.view/index.html b/2-ui/1-document/07-modifying-document/8-tree-count/solution.view/index.html index ec44bfda1..922e8f4cf 100644 --- a/2-ui/1-document/07-modifying-document/8-tree-count/solution.view/index.html +++ b/2-ui/1-document/07-modifying-document/8-tree-count/solution.view/index.html @@ -43,11 +43,11 @@ let lis = document.getElementsByTagName('li'); for (let li of lis) { - // get the count of all <li> below this <li> + // obtener la cuenta de todo <li> bajo su <li> let descendantsCount = li.getElementsByTagName('li').length; if (!descendantsCount) continue; - // add directly to the text node (append to the text) + // sumarlo directamente al nodo texto (añadirlo al texto) li.firstChild.data += ' [' + descendantsCount + ']'; } </script> diff --git a/2-ui/1-document/07-modifying-document/8-tree-count/source.view/index.html b/2-ui/1-document/07-modifying-document/8-tree-count/source.view/index.html index 542bd9376..9b7e66178 100644 --- a/2-ui/1-document/07-modifying-document/8-tree-count/source.view/index.html +++ b/2-ui/1-document/07-modifying-document/8-tree-count/source.view/index.html @@ -40,7 +40,7 @@ </ul> <script> - // ... your code ... + // ... tu código ... </script> </body> diff --git a/2-ui/1-document/07-modifying-document/8-tree-count/task.md b/2-ui/1-document/07-modifying-document/8-tree-count/task.md index d6343bf3b..b7875bbcd 100644 --- a/2-ui/1-document/07-modifying-document/8-tree-count/task.md +++ b/2-ui/1-document/07-modifying-document/8-tree-count/task.md @@ -2,12 +2,12 @@ importance: 5 --- -# Show descendants in a tree +# Mostrar descendientes en un árbol -There's a tree organized as nested `ul/li`. +Hay un árbol organizado como ramas `ul/li`. -Write the code that adds to each `<li>` the number of its descendants. Skip leaves (nodes without children). +Escribe el código que agrega a cada `<li>` el número de su descendientes. No cuentes las hojas (nodos sin hijos). -The result: +El resultado: [iframe border=1 src="solution"] diff --git a/2-ui/1-document/07-modifying-document/9-calendar-table/solution.md b/2-ui/1-document/07-modifying-document/9-calendar-table/solution.md index de8be56e9..d8cc452c2 100644 --- a/2-ui/1-document/07-modifying-document/9-calendar-table/solution.md +++ b/2-ui/1-document/07-modifying-document/9-calendar-table/solution.md @@ -1,9 +1,9 @@ -We'll create the table as a string: `"<table>...</table>"`, and then assign it to `innerHTML`. +Crearemos la tabla como un string: `"<table>...</table>"`, y entonces lo asignamos a `innerHTML`. -The algorithm: +El algoritmo: -1. Create the table header with `<th>` and weekday names. -2. Create the date object `d = new Date(year, month-1)`. That's the first day of `month` (taking into account that months in JavaScript start from `0`, not `1`). -3. First few cells till the first day of the month `d.getDay()` may be empty. Let's fill them in with `<td></td>`. -4. Increase the day in `d`: `d.setDate(d.getDate()+1)`. If `d.getMonth()` is not yet the next month, then add the new cell `<td>` to the calendar. If that's a Sunday, then add a newline <code>"</tr><tr>"</code>. -5. If the month has finished, but the table row is not yet full, add empty `<td>` into it, to make it square. +1. Crea el encabezado de la tabla con `<th>` y los nombres de los días de la semana. +2. Crea el objeto date `d = new Date(year, month-1)`. Este es el primer día del mes `month` (tomando en cuenta que los meses en JavaScript comienzan en `0`, no `1`). +3. Las primeras celdas hasta el primer día del mes `d.getDay()` podrían estar vacías. Las completamos con `<td></td>`. +4. Incrementa el día en `d`: `d.setDate(d.getDate()+1)`. Si `d.getMonth()` no es aún del mes siguiente, agregamos una nueva celda `<td>` al calendario. Si es domingo, agregamos un nueva línea<code>"</tr><tr>"</code>. +5. Si el mes terminó, pero la fila no está completa, le agregamos `<td>` vacíos para hacerlo rectangular. diff --git a/2-ui/1-document/07-modifying-document/9-calendar-table/solution.view/index.html b/2-ui/1-document/07-modifying-document/9-calendar-table/solution.view/index.html index 7e211abc6..8a287d3c7 100644 --- a/2-ui/1-document/07-modifying-document/9-calendar-table/solution.view/index.html +++ b/2-ui/1-document/07-modifying-document/9-calendar-table/solution.view/index.html @@ -29,30 +29,30 @@ <script> function createCalendar(elem, year, month) { - let mon = month - 1; // months in JS are 0..11, not 1..12 + let mon = month - 1; // los meses en JS son 0..11, no 1..12 let d = new Date(year, mon); let table = '<table><tr><th>MO</th><th>TU</th><th>WE</th><th>TH</th><th>FR</th><th>SA</th><th>SU</th></tr><tr>'; - // spaces for the first row - // from Monday till the first day of the month + // espacios en la primera línea + // desde lunes hasta el primer día del mes // * * * 1 2 3 4 for (let i = 0; i < getDay(d); i++) { table += '<td></td>'; } - // <td> with actual dates + // <td> con el día (1 - 31) while (d.getMonth() == mon) { table += '<td>' + d.getDate() + '</td>'; - if (getDay(d) % 7 == 6) { // sunday, last day of week - newline + if (getDay(d) % 7 == 6) { // domingo, último dia de la semana --> nueva línea table += '</tr><tr>'; } d.setDate(d.getDate() + 1); } - // add spaces after last days of month for the last row + // espacios después del último día del mes hasta completar la última línea // 29 30 31 * * * * if (getDay(d) != 0) { for (let i = getDay(d); i < 7; i++) { @@ -60,15 +60,15 @@ } } - // close the table + // cerrar la tabla table += '</tr></table>'; elem.innerHTML = table; } - function getDay(date) { // get day number from 0 (monday) to 6 (sunday) + function getDay(date) { // obtiene el número de día desde 0 (lunes) a 6 (domingo) let day = date.getDay(); - if (day == 0) day = 7; // make Sunday (0) the last day + if (day == 0) day = 7; // hacer domingo (0) el último día return day - 1; } diff --git a/2-ui/1-document/07-modifying-document/9-calendar-table/source.view/index.html b/2-ui/1-document/07-modifying-document/9-calendar-table/source.view/index.html index e1f4cd6bd..d00a9868c 100644 --- a/2-ui/1-document/07-modifying-document/9-calendar-table/source.view/index.html +++ b/2-ui/1-document/07-modifying-document/9-calendar-table/source.view/index.html @@ -28,7 +28,7 @@ <script> function createCalendar(elem, year, month) { - // ...your code that generates the calndar in elem... + // ...tu código que genera el calendario en elem ... } createCalendar(calendar, 2012, 9); diff --git a/2-ui/1-document/07-modifying-document/9-calendar-table/task.md b/2-ui/1-document/07-modifying-document/9-calendar-table/task.md index 37b1a60d2..202bdae20 100644 --- a/2-ui/1-document/07-modifying-document/9-calendar-table/task.md +++ b/2-ui/1-document/07-modifying-document/9-calendar-table/task.md @@ -2,16 +2,16 @@ importance: 4 --- -# Create a calendar +# Crea un calendario -Write a function `createCalendar(elem, year, month)`. +Escribe una función `createCalendar(elem, year, month)`. -The call should create a calendar for the given year/month and put it inside `elem`. +Su llamado debe crear un calendario para el año y mes dados y ponerlo dentro de `elem`. -The calendar should be a table, where a week is `<tr>`, and a day is `<td>`. The table top should be `<th>` with weekday names: the first day should be Monday, and so on till Sunday. +El calendario debe ser una tabla, donde una semana es `<tr>`, y un día es`<td>`. Los encabezados de la tabla deben ser `<th>` con los nombres de los días de la semana: el primer día debe ser "lunes" y así hasta "domingo". -For instance, `createCalendar(cal, 2012, 9)` should generate in element `cal` the following calendar: +Por ejemplo, `createCalendar(cal, 2012, 9)` debe generar en el elemento `cal` el siguiente calendario: [iframe height=210 src="solution"] -P.S. For this task it's enough to generate the calendar, should not yet be clickable. +P.S. Para esta tarea es suficiente generar el calendario, no necesita aún ser cliqueable. diff --git a/2-ui/1-document/07-modifying-document/article.md b/2-ui/1-document/07-modifying-document/article.md index 75ce1fbb0..fbe489551 100644 --- a/2-ui/1-document/07-modifying-document/article.md +++ b/2-ui/1-document/07-modifying-document/article.md @@ -1,14 +1,14 @@ -# Modifying the document +# Modificando el documento -DOM modification is the key to creating "live" pages. +La modificación del DOM es la clave para crear páginas "vivas", dinámicas. -Here we'll see how to create new elements "on the fly" and modify the existing page content. +Aquí veremos cómo crear nuevos elementos "al vuelo" y modificar el contenido existente de la página. -## Example: show a message +## Ejemplo: mostrar un mensaje -Let's demonstrate using an example. We'll add a message on the page that looks nicer than `alert`. +Hagamos una demostración usando un ejemplo. Añadiremos un mensaje que se vea más agradable que un `alert`. -Here's how it will look: +Así es como se verá: ```html autorun height="80" <style> @@ -23,57 +23,57 @@ Here's how it will look: *!* <div class="alert"> - <strong>Hi there!</strong> You've read an important message. + <strong>¡Hola!</strong> Usted ha leído un importante mensaje. </div> */!* ``` -That was the HTML example. Now let's create the same `div` with JavaScript (assuming that the styles are in the HTML/CSS already). +Eso fue el ejemplo HTML. Ahora creemos el mismo `div` con JavaScript (asumiendo que los estilos ya están en HTML/CSS). -## Creating an element +## Creando un elemento -To create DOM nodes, there are two methods: +Para crear nodos DOM, hay dos métodos: `document.createElement(tag)` -: Creates a new *element node* with the given tag: +: Crea un nuevo *nodo elemento* con la etiqueta HTML dada: ```js let div = document.createElement('div'); ``` `document.createTextNode(text)` -: Creates a new *text node* with the given text: +: Crea un nuevo *nodo texto* con el texto dado: ```js - let textNode = document.createTextNode('Here I am'); + let textNode = document.createTextNode('Aquí estoy'); ``` -Most of the time we need to create element nodes, such as the `div` for the message. +La mayor parte del tiempo necesitamos crear nodos de elemento, como el `div` para el mensaje. -### Creating the message +### Creando el mensaje -Creating the message div takes 3 steps: +Crear el div de mensaje toma 3 pasos: ```js -// 1. Create <div> element +// 1. Crear elemento <div> let div = document.createElement('div'); -// 2. Set its class to "alert" +// 2. Establecer su clase a "alert" div.className = "alert"; -// 3. Fill it with the content -div.innerHTML = "<strong>Hi there!</strong> You've read an important message."; +// 3. Agregar el contenido +div.innerHTML = "<strong>¡Hola!</strong> Usted ha leído un importante mensaje."; ``` -We've created the element. But as of now it's only in a variable named `div`, not in the page yet. So we can't see it. +Hemos creado el elemento. Pero hasta ahora solamente está en una variable llamada `div`, no aún en la página, y no la podemos ver. -## Insertion methods +## Métodos de inserción -To make the `div` show up, we need to insert it somewhere into `document`. For instance, into `<body>` element, referenced by `document.body`. +Para hacer que el `div` aparezca, necesitamos insertarlo en algún lado dentro de `document`. Por ejemplo, en el elemento `<body>`, referenciado por `document.body`. -There's a special method `append` for that: `document.body.append(div)`. +Hay un método especial `append` para ello: `document.body.append(div)`. -Here's the full code: +El código completo: ```html run height="80" <style> @@ -89,7 +89,7 @@ Here's the full code: <script> let div = document.createElement('div'); div.className = "alert"; - div.innerHTML = "<strong>Hi there!</strong> You've read an important message."; + div.innerHTML = "<strong>¡Hola!</strong> Usted ha leído un importante mensaje."; *!* document.body.append(div); @@ -97,21 +97,21 @@ Here's the full code: </script> ``` -Here we called `append` on `document.body`, but we can call `append` method on any other element, to put another element into it. For instance, we can append something to `<div>` by calling `div.append(anotherElement)`. +Aquí usamos el método `append` sobre `document.body`, pero podemos llamar `append` sobre cualquier elemento para poner otro elemento dentro de él. Por ejemplo, podemos añadir algo a `<div>` llamando `div.append(anotherElement)`. -Here are more insertion methods, they specify different places where to insert: +Aquí hay más métodos de inserción, ellos especifican diferentes lugares donde insertar: -- `node.append(...nodes or strings)` -- append nodes or strings *at the end* of `node`, -- `node.prepend(...nodes or strings)` -- insert nodes or strings *at the beginning* of `node`, -- `node.before(...nodes or strings)` –- insert nodes or strings *before* `node`, -- `node.after(...nodes or strings)` –- insert nodes or strings *after* `node`, -- `node.replaceWith(...nodes or strings)` –- replaces `node` with the given nodes or strings. +- `node.append(...nodos o strings)` -- agrega nodos o strings *al final* de `node`, +- `node.prepend(...nodos o strings)` -- insert nodos o strings *al principio* de `node`, +- `node.before(...nodos o strings)` –- inserta nodos o strings *antes* de `node`, +- `node.after(...nodos o strings)` –- inserta nodos o strings *después* de `node`, +- `node.replaceWith(...nodos o strings)` –- reemplaza `node` con los nodos o strings dados. -Arguments of these methods are an arbitrary list of DOM nodes to insert, or text strings (that become text nodes automatically). +Los argumentos de estos métodos son una lista arbitraria de lo que se va a insertar: nodos DOM o strings de texto (estos se vuelven nodos de texto automáticamente). -Let's see them in action. +Veámoslo en acción. -Here's an example of using these methods to add items to a list and the text before/after it: +Aquí tenemos un ejemplo del uso de estos métodos para agregar items a una lista y el texto antes/después de él: ```html autorun <ol id="ol"> @@ -121,24 +121,24 @@ Here's an example of using these methods to add items to a list and the text bef </ol> <script> - ol.before('before'); // insert string "before" before <ol> - ol.after('after'); // insert string "after" after <ol> + ol.before('before'); // inserta el string "before" antes de <ol> + ol.after('after'); // inserta el string "after" después de <ol> let liFirst = document.createElement('li'); liFirst.innerHTML = 'prepend'; - ol.prepend(liFirst); // insert liFirst at the beginning of <ol> + ol.prepend(liFirst); // inserta liFirst al principio de <ol> let liLast = document.createElement('li'); liLast.innerHTML = 'append'; - ol.append(liLast); // insert liLast at the end of <ol> + ol.append(liLast); // inserta liLast al final de <ol> </script> ``` -Here's a visual picture of what the methods do: +Aquí la representación visual de lo que hacen los métodos: ![](before-prepend-append-after.svg) -So the final list will be: +Entonces la lista final será: ```html before @@ -152,82 +152,82 @@ before after ``` -As said, these methods can insert multiple nodes and text pieces in a single call. +Como dijimos antes, estos métodos pueden insertar múltiples nodos y piezas de texto en un simple llamado. -For instance, here a string and an element are inserted: +Por ejemplo, aquí se insertan un string y un elemento: ```html run <div id="div"></div> <script> - div.before('<p>Hello</p>', document.createElement('hr')); + div.before('<p>Hola</p>', document.createElement('hr')); </script> ``` -Please note: the text is inserted "as text", not "as HTML", with proper escaping of characters such as `<`, `>`. +Nota que el texto es insertado "como texto" y no "como HTML", escapando apropiadamente los caracteres como `<`, `>`. -So the final HTML is: +Entonces el HTML final es: ```html run *!* -<p>Hello</p> +<p>Hola</p> */!* <hr> <div id="div"></div> ``` -In other words, strings are inserted in a safe way, like `elem.textContent` does it. +En otras palabras, los strings son insertados en una manera segura, tal como lo hace `elem.textContent`. -So, these methods can only be used to insert DOM nodes or text pieces. +Entonces, estos métodos solo pueden usarse para insertar nodos DOM como piezas de texto. -But what if we'd like to insert an HTML string "as html", with all tags and stuff working, in the same manner as `elem.innerHTML` does it? +Pero ¿y si queremos insertar un string HTML "como html", con todas las etiquetas y demás funcionando, de la misma manera que lo hace `elem.innerHTML`? ## insertAdjacentHTML/Text/Element -For that we can use another, pretty versatile method: `elem.insertAdjacentHTML(where, html)`. +Para ello podemos usar otro métodos, muy versátil: `elem.insertAdjacentHTML(where, html)`. -The first parameter is a code word, specifying where to insert relative to `elem`. Must be one of the following: +El primer parámetro es un palabra código que especifica dónde insertar relativo a `elem`. Debe ser uno de los siguientes: -- `"beforebegin"` -- insert `html` immediately before `elem`, -- `"afterbegin"` -- insert `html` into `elem`, at the beginning, -- `"beforeend"` -- insert `html` into `elem`, at the end, -- `"afterend"` -- insert `html` immediately after `elem`. +- `"beforebegin"` -- inserta `html` inmediatamente antes de `elem` +- `"afterbegin"` -- inserta `html` en `elem`, al principio +- `"beforeend"` -- inserta `html` en `elem`, al final +- `"afterend"` -- inserta `html` inmediatamente después de `elem` -The second parameter is an HTML string, that is inserted "as HTML". +El segundo parámetro es un string HTML, que es insertado "como HTML". -For instance: +Por ejemplo: ```html run <div id="div"></div> <script> - div.insertAdjacentHTML('beforebegin', '<p>Hello</p>'); - div.insertAdjacentHTML('afterend', '<p>Bye</p>'); + div.insertAdjacentHTML('beforebegin', '<p>Hola</p>'); + div.insertAdjacentHTML('afterend', '<p>Adiós</p>'); </script> ``` -...Would lead to: +...resulta en: ```html run -<p>Hello</p> +<p>Hola</p> <div id="div"></div> -<p>Bye</p> +<p>Adiós</p> ``` -That's how we can append arbitrary HTML to the page. +Así es como podemos añadir HTML arbitrario a la página. -Here's the picture of insertion variants: +Aquí abajo, la imagen de las variantes de inserción: ![](insert-adjacent.svg) -We can easily notice similarities between this and the previous picture. The insertion points are actually the same, but this method inserts HTML. +Fácilmente podemos notar similitudes entre esta imagen y la anterior. Los puntos de inserción son los mismos, pero este método inserta HTML. -The method has two brothers: +El método tiene dos hermanos: -- `elem.insertAdjacentText(where, text)` -- the same syntax, but a string of `text` is inserted "as text" instead of HTML, -- `elem.insertAdjacentElement(where, elem)` -- the same syntax, but inserts an element. +- `elem.insertAdjacentText(where, text)` -- la misma sintaxis, pero un string de `texto` es insertado "como texto" en vez de HTML, +- `elem.insertAdjacentElement(where, elem)` -- la misma sintaxis, pero inserta un elemento. -They exist mainly to make the syntax "uniform". In practice, only `insertAdjacentHTML` is used most of the time. Because for elements and text, we have methods `append/prepend/before/after` -- they are shorter to write and can insert nodes/text pieces. +Ellos existen principalmente para hacer la sintaxis "uniforme". En la práctica, solo `insertAdjacentHTML` es usado la mayor parte del tiempo. Porque para elementos y texto, tenemos los métodos `append/prepend/before/after`: son más cortos para escribir y pueden insertar piezas de texto y nodos. -So here's an alternative variant of showing a message: +Entonces tenemos una alternativa para mostrar un mensaje: ```html run <style> @@ -242,16 +242,16 @@ So here's an alternative variant of showing a message: <script> document.body.insertAdjacentHTML("afterbegin", `<div class="alert"> - <strong>Hi there!</strong> You've read an important message. + <strong>¡Hola!</strong> Usted ha leído un importante mensaje. </div>`); </script> ``` -## Node removal +## Eliminación de nodos -To remove a node, there's a method `node.remove()`. +Para quitar un nodo, tenemos el método `node.remove()`. -Let's make our message disappear after a second: +Hagamos que nuestro mensaje desaparezca después de un segundo: ```html run untrusted <style> @@ -267,7 +267,7 @@ Let's make our message disappear after a second: <script> let div = document.createElement('div'); div.className = "alert"; - div.innerHTML = "<strong>Hi there!</strong> You've read an important message."; + div.innerHTML = "<strong>¡Hola!</strong> Usted ha leído un importante mensaje."; document.body.append(div); *!* @@ -276,32 +276,32 @@ Let's make our message disappear after a second: </script> ``` -Please note: if we want to *move* an element to another place -- there's no need to remove it from the old one. +Nota que si queremos *mover* un elemento a un nuevo lugar, no hay necesidad de quitarlo del viejo. -**All insertion methods automatically remove the node from the old place.** +**Todos los métodos de inserción automáticamente quitan el nodo del lugar viejo.** -For instance, let's swap elements: +Por ejemplo, intercambiemos elementos: ```html run height=50 -<div id="first">First</div> -<div id="second">Second</div> +<div id="first">Primero</div> +<div id="second">Segundo</div> <script> - // no need to call remove - second.after(first); // take #second and after it insert #first + // no hay necesidad de llamar "remove" + second.after(first); // toma #second y después inserta #first </script> ``` -## Cloning nodes: cloneNode +## Clonando nodos: cloneNode -How to insert one more similar message? +¿Cómo insertar un mensaje similar más? -We could make a function and put the code there. But the alternative way would be to *clone* the existing `div` and modify the text inside it (if needed). +Podríamos hacer una función y poner el código allí. Pero la alternativa es *clonar* el `div` existente, y modificar el texto dentro si es necesario. -Sometimes when we have a big element, that may be faster and simpler. +A veces, cuando tenemos un elemento grande, esto es más simple y rápido. -- The call `elem.cloneNode(true)` creates a "deep" clone of the element -- with all attributes and subelements. If we call `elem.cloneNode(false)`, then the clone is made without child elements. +- La llamada `elem.cloneNode(true)` crea una clonación "profunda" del elemento, con todos los atributos y subelementos. Si llamamos `elem.cloneNode(false)`, la clonación se hace sin sus elementos hijos. -An example of copying the message: +Un ejemplo de copia del mensaje: ```html run height="120" <style> @@ -315,26 +315,26 @@ An example of copying the message: </style> <div class="alert" id="div"> - <strong>Hi there!</strong> You've read an important message. + <strong>¡Hola!</strong> Usted ha leído un importante mensaje. </div> <script> *!* - let div2 = div.cloneNode(true); // clone the message - div2.querySelector('strong').innerHTML = 'Bye there!'; // change the clone + let div2 = div.cloneNode(true); // clona el mensaje + div2.querySelector('strong').innerHTML = '¡Adiós!'; // altera el clon - div.after(div2); // show the clone after the existing div + div.after(div2); // muestra el clon después del div existente */!* </script> ``` ## DocumentFragment [#document-fragment] -`DocumentFragment` is a special DOM node that serves as a wrapper to pass around lists of nodes. +`DocumentFragment` es un nodo DOM especial que sirve como contenedor para trasladar listas de nodos. -We can append other nodes to it, but when we insert it somewhere, then its content is inserted instead. +Podemos agregarle nodos, pero cuando lo insertamos en algún lugar, lo que se inserta es su contenido. -For example, `getListContent` below generates a fragment with `<li>` items, that are later inserted into `<ul>`: +Por ejemplo, `getListContent` de abajo genera un fragmento con items `<li>`, que luego son insertados en `<ul>`: ```html run <ul id="ul"></ul> @@ -358,7 +358,7 @@ ul.append(getListContent()); // (*) </script> ``` -Please note, at the last line `(*)` we append `DocumentFragment`, but it "blends in", so the resulting structure will be: +Nota que a la última línea `(*)` añadimos `DocumentFragment`, pero este despliega su contenido. Entonces la estructura resultante será: ```html <ul> @@ -368,7 +368,7 @@ Please note, at the last line `(*)` we append `DocumentFragment`, but it "blends </ul> ``` -`DocumentFragment` is rarely used explicitly. Why append to a special kind of node, if we can return an array of nodes instead? Rewritten example: +Es raro que `DocumentFragment` se use explícitamente. ¿Por qué añadir un tipo especial de nodo si en su lugar podemos devolver un array de nodos? El ejemplo reescrito: ```html run <ul id="ul"></ul> @@ -387,27 +387,27 @@ function getListContent() { } *!* -ul.append(...getListContent()); // append + "..." operator = friends! +ul.append(...getListContent()); // append + el operador "..." = ¡amigos! */!* </script> ``` -We mention `DocumentFragment` mainly because there are some concepts on top of it, like [template](info:template-element) element, that we'll cover much later. +Mencionamos `DocumentFragment` principalmente porque hay algunos conceptos asociados a él, como el elemento [template](info:template-element), que cubriremos mucho después. -## Old-school insert/remove methods +## Métodos de la vieja escuela para insertar/quitar [old] -There are also "old school" DOM manipulation methods, existing for historical reasons. +Hay también métodos de manipulación de DOM de "vieja escuela", existentes por razones históricas. -These methods come from really ancient times. Nowadays, there's no reason to use them, as modern methods, such as `append`, `prepend`, `before`, `after`, `remove`, `replaceWith`, are more flexible. +Estos métodos vienen de realmente viejos tiempos. No hay razón para usarlos estos días, ya que los métodos modernos como `append`, `prepend`, `before`, `after`, `remove`, `replaceWith`, son más flexibles. -The only reason we list these methods here is that you can find them in many old scripts: +La única razón por la que los listamos aquí es porque podrías encontrarlos en viejos scripts: `parentElem.appendChild(node)` -: Appends `node` as the last child of `parentElem`. +: Añade `node` como último hijo de `parentElem`. - The following example adds a new `<li>` to the end of `<ol>`: + El siguiente ejemplo agrega un nuevo `<li>` al final de `<ol>`: ```html run height=100 <ol id="list"> @@ -418,16 +418,16 @@ The only reason we list these methods here is that you can find them in many old <script> let newLi = document.createElement('li'); - newLi.innerHTML = 'Hello, world!'; + newLi.innerHTML = '¡Hola, mundo!'; list.appendChild(newLi); </script> ``` `parentElem.insertBefore(node, nextSibling)` -: Inserts `node` before `nextSibling` into `parentElem`. +: Inserta `node` antes de `nextSibling` dentro de `parentElem`. - The following code inserts a new list item before the second `<li>`: + El siguiente código inserta un nuevo ítem de lista antes del segundo `<li>`: ```html run height=100 <ol id="list"> @@ -437,26 +437,26 @@ The only reason we list these methods here is that you can find them in many old </ol> <script> let newLi = document.createElement('li'); - newLi.innerHTML = 'Hello, world!'; + newLi.innerHTML = '¡Hola, mundo!'; *!* list.insertBefore(newLi, list.children[1]); */!* </script> ``` - To insert `newLi` as the first element, we can do it like this: + Para insertar `newLi` como primer elemento, podemos hacerlo así: ```js list.insertBefore(newLi, list.firstChild); ``` `parentElem.replaceChild(node, oldChild)` -: Replaces `oldChild` with `node` among children of `parentElem`. +: Reemplaza `oldChild` con `node` entre los hijos de `parentElem`. `parentElem.removeChild(node)` -: Removes `node` from `parentElem` (assuming `node` is its child). +: Quita `node` de `parentElem` (asumiendo que `node` es su hijo). - The following example removes first `<li>` from `<ol>`: + El siguiente ejemplo quita el primer `<li>` de `<ol>`: ```html run height=100 <ol id="list"> @@ -471,91 +471,91 @@ The only reason we list these methods here is that you can find them in many old </script> ``` -All these methods return the inserted/removed node. In other words, `parentElem.appendChild(node)` returns `node`. But usually the returned value is not used, we just run the method. +Todos estos métodos devuelven el nodo insertado/quitado. En otras palabras, `parentElem.appendChild(node)` devuelve `node`. Pero lo usual es que el valor no se use y solo ejecutemos el método. -## A word about "document.write" +## Una palabra acerca de "document.write" -There's one more, very ancient method of adding something to a web-page: `document.write`. +Hay uno más, un método muy antiguo para agregar algo a una página web: `document.write`. -The syntax: +La sintaxis: ```html run -<p>Somewhere in the page...</p> +<p>En algún lugar de la página...</p> *!* <script> - document.write('<b>Hello from JS</b>'); + document.write('<b>Saludos de JS</b>'); </script> */!* -<p>The end</p> +<p>Fin</p> ``` -The call to `document.write(html)` writes the `html` into page "right here and now". The `html` string can be dynamically generated, so it's kind of flexible. We can use JavaScript to create a full-fledged webpage and write it. +El llamado a `document.write(html)` escribe el `html` en la página "aquí y ahora". El string `html` puede ser generado dinámicamente, así que es muy flexible. Podemos usar JavaScript para crear una página completa al vuelo y escribirla. -The method comes from times when there was no DOM, no standards... Really old times. It still lives, because there are scripts using it. +El método viene de tiempos en que no había DOM ni estándares... Realmente viejos tiempos. Todavía vive, porque hay scripts que lo usan. -In modern scripts we can rarely see it, because of the following important limitation: +En scripts modernos rara vez lo vemos, por una importante limitación: -**The call to `document.write` only works while the page is loading.** +**El llamado a `document.write` solo funciona mientras la página está cargando.** -If we call it afterwards, the existing document content is erased. +Si la llamamos después, el contenido existente del documento es borrado. -For instance: +Por ejemplo: ```html run -<p>After one second the contents of this page will be replaced...</p> +<p>Después de un segundo el contenido de esta página será reemplazado...</p> *!* <script> - // document.write after 1 second - // that's after the page loaded, so it erases the existing content - setTimeout(() => document.write('<b>...By this.</b>'), 1000); + // document.write después de 1 segundo + // eso es después de que la página cargó, entonces borra el contenido existente + setTimeout(() => document.write('<b>...Por esto.</b>'), 1000); </script> */!* ``` -So it's kind of unusable at "after loaded" stage, unlike other DOM methods we covered above. +Así que es bastante inusable en el estado "after loaded" (después de cargado), al contrario de los otros métodos DOM que cubrimos antes. -That's the downside. +Ese es el punto en contra. -There's an upside also. Technically, when `document.write` is called while the browser is reading ("parsing") incoming HTML, and it writes something, the browser consumes it just as if it were initially there, in the HTML text. +También tiene un punto a favor. Técnicamente, cuando es llamado `document.write` mientras el navegador está leyendo el HTML entrante ("parsing"), y escribe algo, el navegador lo consume como si hubiera estado inicialmente allí, en el texto HTML. -So it works blazingly fast, because there's *no DOM modification* involved. It writes directly into the page text, while the DOM is not yet built. +Así que funciona muy rápido, porque no hay una "modificación de DOM" involucrada. Escribe directamente en el texto de la página mientras el DOM ni siquiera está construido. -So if we need to add a lot of text into HTML dynamically, and we're at page loading phase, and the speed matters, it may help. But in practice these requirements rarely come together. And usually we can see this method in scripts just because they are old. +Entonces: si necesitamos agregar un montón de texto en HTML dinámicamente, estamos en la fase de carga de página, y la velocidad es importante, esto puede ayudar. Pero en la práctica estos requerimientos raramente vienen juntos. Así que si vemos este método en scripts, probablemente sea solo porque son viejos. -## Summary +## Resumen -- Methods to create new nodes: - - `document.createElement(tag)` -- creates an element with the given tag, - - `document.createTextNode(value)` -- creates a text node (rarely used), - - `elem.cloneNode(deep)` -- clones the element, if `deep==true` then with all descendants. +- Métodos para crear nuevos nodos: + - `document.createElement(tag)` -- crea un elemento con la etiqueta HTML dada + - `document.createTextNode(value)` -- crea un nodo de texto (raramente usado) + - `elem.cloneNode(deep)` -- clona el elemento. Si `deep==true`, lo clona con todos sus descendientes. -- Insertion and removal: - - `node.append(...nodes or strings)` -- insert into `node`, at the end, - - `node.prepend(...nodes or strings)` -- insert into `node`, at the beginning, - - `node.before(...nodes or strings)` –- insert right before `node`, - - `node.after(...nodes or strings)` –- insert right after `node`, - - `node.replaceWith(...nodes or strings)` –- replace `node`. - - `node.remove()` –- remove the `node`. +- Inserción y eliminación: + - `node.append(...nodes or strings)` -- inserta en `node`, al final + - `node.prepend(...nodes or strings)` -- inserta en `node`, al principio + - `node.before(...nodes or strings)` –- inserta inmediatamente antes de `node` + - `node.after(...nodes or strings)` –- inserta inmediatamente después de `node` + - `node.replaceWith(...nodes or strings)` –- reemplaza `node` + - `node.remove()` –- quita el `node`. - Text strings are inserted "as text". + Los strings de texto son insertados "como texto". -- There are also "old school" methods: +- También hay métodos "de vieja escuela": - `parent.appendChild(node)` - `parent.insertBefore(node, nextSibling)` - `parent.removeChild(node)` - `parent.replaceChild(newElem, node)` - All these methods return `node`. + Todos estos métodos devuelven `node`. -- Given some HTML in `html`, `elem.insertAdjacentHTML(where, html)` inserts it depending on the value of `where`: - - `"beforebegin"` -- insert `html` right before `elem`, - - `"afterbegin"` -- insert `html` into `elem`, at the beginning, - - `"beforeend"` -- insert `html` into `elem`, at the end, - - `"afterend"` -- insert `html` right after `elem`. +- Dado cierto HTML en `html`, `elem.insertAdjacentHTML(where, html)` lo inserta dependiendo del valor `where`: + - `"beforebegin"` -- inserta `html` inmediatamente antes de `elem` + - `"afterbegin"` -- inserta `html` en `elem`, al principio + - `"beforeend"` -- inserta `html` en `elem`, al final + - `"afterend"` -- inserta `html` inmediatamente después de`elem` - Also there are similar methods, `elem.insertAdjacentText` and `elem.insertAdjacentElement`, that insert text strings and elements, but they are rarely used. + También hay métodos similares, `elem.insertAdjacentText` y `elem.insertAdjacentElement`, que insertan strings de texto y elementos, pero son raramente usados. -- To append HTML to the page before it has finished loading: +- Para agregar HTML a la página antes de que haya terminado de cargar: - `document.write(html)` - After the page is loaded such a call erases the document. Mostly seen in old scripts. + Después de que la página fue cargada tal llamada borra el documento. Puede verse principalmente en scripts viejos. diff --git a/2-ui/1-document/08-styles-and-classes/2-create-notification/solution.view/index.html b/2-ui/1-document/08-styles-and-classes/2-create-notification/solution.view/index.html index dc4eeec9f..40da537be 100755 --- a/2-ui/1-document/08-styles-and-classes/2-create-notification/solution.view/index.html +++ b/2-ui/1-document/08-styles-and-classes/2-create-notification/solution.view/index.html @@ -6,7 +6,7 @@ <body> - <h2>Notification is on the right</h2> + <h2>La notificación está a la derecha</h2> <p> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum aspernatur quam ex eaque inventore quod voluptatem adipisci omnis nemo nulla fugit iste numquam ducimus cumque minima porro ea quidem maxime necessitatibus beatae labore soluta voluptatum @@ -32,7 +32,7 @@ <h2>Notification is on the right</h2> setTimeout(() => notification.remove(), 1500); } - // test it + // probando... let i = 1; setInterval(() => { showNotification({ diff --git a/2-ui/1-document/08-styles-and-classes/2-create-notification/source.view/index.html b/2-ui/1-document/08-styles-and-classes/2-create-notification/source.view/index.html index b5e2f83f7..5c546e7d7 100755 --- a/2-ui/1-document/08-styles-and-classes/2-create-notification/source.view/index.html +++ b/2-ui/1-document/08-styles-and-classes/2-create-notification/source.view/index.html @@ -6,7 +6,7 @@ <body> - <h2>Notification is on the right</h2> + <h2>La notificación está a la derecha</h2> <p> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum aspernatur quam ex eaque inventore quod voluptatem adipisci omnis nemo nulla fugit iste numquam ducimus cumque minima porro ea quidem maxime necessitatibus beatae labore soluta voluptatum @@ -16,10 +16,10 @@ <h2>Notification is on the right</h2> <script> function showNotification({top = 0, right = 0, className, html}) { - /* your code */ + /* tu código */ } - // test it + // prueba let i = 1; setInterval(() => { showNotification({ diff --git a/2-ui/1-document/08-styles-and-classes/2-create-notification/task.md b/2-ui/1-document/08-styles-and-classes/2-create-notification/task.md index 60930cb6c..1c0e3d938 100644 --- a/2-ui/1-document/08-styles-and-classes/2-create-notification/task.md +++ b/2-ui/1-document/08-styles-and-classes/2-create-notification/task.md @@ -2,23 +2,23 @@ importance: 5 --- -# Create a notification +# Crear una notificación -Write a function `showNotification(options)` that creates a notification: `<div class="notification">` with the given content. The notification should automatically disappear after 1.5 seconds. +Escribir una función `showNotification(options)` que cree una notificación: `<div class="notification">` con el contenido dado. La notificación debería desaparecer automáticamente despues de 1.5 segundos. -The options are: +Las opciones son: ```js -// shows an element with the text "Hello" near the right-top of the window +// muestra un elemento con el texto "Hello" cerca de la parte superior de la ventana showNotification({ - top: 10, // 10px from the top of the window (by default 0px) - right: 10, // 10px from the right edge of the window (by default 0px) - html: "Hello!", // the HTML of notification - className: "welcome" // an additional class for the div (optional) + top: 10, // 10px desde la parte superior de la ventana (por defecto es 0px) + right: 10, // 10px desde el borde derecho de la ventana (por defecto es 0px) + html: "Hello!", // el HTML de la notificación + className: "welcome" // una clase adicional para el "div" (opcional) }); ``` [demo src="solution"] -Use CSS positioning to show the element at given top/right coordinates. The source document has the necessary styles. +Usar posicionamiento CSS para mostrar el elemento en las coordenadas (top/right) dadas. El documento tiene los estilos necesarios. diff --git a/2-ui/1-document/08-styles-and-classes/article.md b/2-ui/1-document/08-styles-and-classes/article.md index 46aaa3b00..45da7205d 100644 --- a/2-ui/1-document/08-styles-and-classes/article.md +++ b/2-ui/1-document/08-styles-and-classes/article.md @@ -1,92 +1,92 @@ -# Styles and classes +# Estilos y clases -Before we get into JavaScript's ways of dealing with styles and classes -- here's an important rule. Hopefully it's obvious enough, but we still have to mention it. +Antes de profundizar en cómo JavaScript maneja las clases y los estilos, hay una regla importante. Aunque es lo suficientemente obvio, aún tenemos que mencionarlo. -There are generally two ways to style an element: +Por lo general, hay dos formas de dar estilo a un elemento: -1. Create a class in CSS and add it: `<div class="...">` -2. Write properties directly into `style`: `<div style="...">`. +1. Crear una clase `css` y agregarla: `<div class="...">` +2. Escribir las propiedades directamente en `style`: `<div style="...">`. -JavaScript can modify both classes and `style` properties. +JavaScript puede modificar ambos, clases y las propiedades de `style`. -We should always prefer CSS classes to `style`. The latter should only be used if classes "can't handle it". +Nosotros deberíamos preferir las clases `css` en lugar de `style`. Este último solo debe usarse si las clases "no pueden manejarlo". -For example, `style` is acceptable if we calculate coordinates of an element dynamically and want to set them from JavaScript, like this: +Por ejemplo, `style` es aceptable si nosotros calculamos las coordenadas de un elemento dinámicamente y queremos establecer estas desde JavaScript, así: ```js -let top = /* complex calculations */; -let left = /* complex calculations */; +let top = /* cálculos complejos */; +let left = /* cálculos complejos */; -elem.style.left = left; // e.g '123px', calculated at run-time -elem.style.top = top; // e.g '456px' +elem.style.left = left; // ej. '123px', calculado en tiempo de ejecución +elem.style.top = top; // ej. '456px' ``` -For other cases, like making the text red, adding a background icon -- describe that in CSS and then add the class (JavaScript can do that). That's more flexible and easier to support. +Para otros casos como convertir un texto en rojo, agregar un icono de fondo. Escribir eso en CSS y luego agregar la clase (JavaScript puede hacer eso), es más flexible y más fácil de mantener. -## className and classList +## className y classList -Changing a class is one of the most often used actions in scripts. +Cambiar una clase es una de las acciones más utilizadas. -In the ancient time, there was a limitation in JavaScript: a reserved word like `"class"` could not be an object property. That limitation does not exist now, but at that time it was impossible to have a `"class"` property, like `elem.class`. +En la antigüedad, había una limitación en JavaScript: una palabra reservada como `"class"` no podía ser una propiedad de un objeto. Esa limitación no existe ahora, pero en ese momento era imposible tener una propiedad `"class"`, como `elem.class`. -So for classes the similar-looking property `"className"` was introduced: the `elem.className` corresponds to the `"class"` attribute. +Entonces para clases de similares propiedades, `"className"` fue introducido: el `elem.className` corresponde al atributo `"class"`. -For instance: +Por ejemplo: ```html run <body class="main page"> <script> - alert(document.body.className); // main page + alert(document.body.className); // página principal </script> </body> ``` -If we assign something to `elem.className`, it replaces the whole string of classes. Sometimes that's what we need, but often we want to add/remove a single class. +Si asignamos algo a `elem.className`, reemplaza toda la cadena de clases. A veces es lo que necesitamos, pero a menudo queremos agregar o eliminar una sola clase. -There's another property for that: `elem.classList`. +Hay otra propiedad para eso: `elem.classList`. -The `elem.classList` is a special object with methods to `add/remove/toggle` a single class. +El `elem.classList` es un objeto especial con métodos para agregar, eliminar y alternar (`add/remove/toggle`) una sola clase. -For instance: +Por ejemplo: ```html run <body class="main page"> <script> *!* - // add a class + // agregar una clase document.body.classList.add('article'); */!* - alert(document.body.className); // main page article + alert(document.body.className); // clase "article" de la página principal </script> </body> ``` -So we can operate both on the full class string using `className` or on individual classes using `classList`. What we choose depends on our needs. +Entonces podemos trabajar con ambos: todas las clases como una cadena usando `className` o con clases individuales usando `classList`. Lo que elijamos depende de nuestras necesidades. -Methods of `classList`: +Métodos de `classList`: -- `elem.classList.add/remove("class")` -- adds/removes the class. -- `elem.classList.toggle("class")` -- adds the class if it doesn't exist, otherwise removes it. -- `elem.classList.contains("class")` -- checks for the given class, returns `true/false`. +- `elem.classList.add/remove("class")` -- agrega o remueve la clase. +- `elem.classList.toggle("class")` -- agrega la clase si no existe, si no la remueve. +- `elem.classList.contains("class")` -- verifica si tiene la clase dada, devuelve `true/false`. -Besides, `classList` is iterable, so we can list all classes with `for..of`, like this: +Además, `classList` es iterable, entonces podemos listar todas las clases con `for..of`, así: ```html run <body class="main page"> <script> for (let name of document.body.classList) { - alert(name); // main, and then page + alert(name); // main y luego page } </script> </body> ``` -## Element style +## `style` de un elemento -The property `elem.style` is an object that corresponds to what's written in the `"style"` attribute. Setting `elem.style.width="100px"` works the same as if we had in the attribute `style` a string `width:100px`. +La propiedad `elem.style` es un objeto que corresponde a lo escrito en el atributo `"style"`. Establecer `elem.style.width="100px"` funciona igual que sí tuviéramos en el atributo `style` una cadena con `width:100px`. -For multi-word property the camelCase is used: +Para propiedades de varias palabras se usa `camelCase`: ```js no-beautify background-color => elem.style.backgroundColor @@ -94,16 +94,16 @@ z-index => elem.style.zIndex border-left-width => elem.style.borderLeftWidth ``` -For instance: +Por ejemplo: ```js run document.body.style.backgroundColor = prompt('background color?', 'green'); ``` -````smart header="Prefixed properties" -Browser-prefixed properties like `-moz-border-radius`, `-webkit-border-radius` also follow the same rule: a dash means upper case. +````smart header="Propiedades prefijadas" +Propiedades con prefijos del navegador como `-moz-border-radius`, `-webkit-border-radius` también siguen la misma regla: un guion significa mayúscula. -For instance: +Por ejemplo: ```js button.style.MozBorderRadius = '5px'; @@ -111,41 +111,41 @@ button.style.WebkitBorderRadius = '5px'; ``` ```` -## Resetting the style property +## Reseteando la propiedad `style` -Sometimes we want to assign a style property, and later remove it. +A veces queremos asignar una propiedad de estilo y luego removerla. -For instance, to hide an element, we can set `elem.style.display = "none"`. +Por ejemplo, para ocultar un elemento, podemos establecer `elem.style.display = "none"`. -Then later we may want to remove the `style.display` as if it were not set. Instead of `delete elem.style.display` we should assign an empty string to it: `elem.style.display = ""`. +Luego, más tarde, es posible que queramos remover `style.display` como si no estuviera establecido. En lugar de `delete elem.style.display` deberíamos asignarle una cadena vacía: `elem.style.display = ""`. ```js run -// if we run this code, the <body> will blink -document.body.style.display = "none"; // hide +// si ejecutamos este código, el <body> parpadeará +document.body.style.display = "none"; // ocultar -setTimeout(() => document.body.style.display = "", 1000); // back to normal +setTimeout(() => document.body.style.display = "", 1000); // volverá a lo normal ``` -If we set `style.display` to an empty string, then the browser applies CSS classes and its built-in styles normally, as if there were no such `style.display` property at all. +Si establecemos `style.display` como una cadena vacía, entonces el navegador aplica clases y estilos CSS incorporados normalmente por el navegador, como si no existiera tal `style.display`. -Also there is a special method for that, `elem.style.removeProperty('style property')`. So, We can remove a property like this: +También hay un método especial para eso, `elem.style.removeProperty('style property')`. Así, podemos quitar una propiedad: ```js run -document.body.style.background = 'red'; //set background to red +document.body.style.background = 'red'; //establece background a rojo -setTimeout(() => document.body.style.removeProperty('background'), 1000); // remove background after 1 second +setTimeout(() => document.body.style.removeProperty('background'), 1000); // quitar background después de 1 segundo ``` -````smart header="Full rewrite with `style.cssText`" -Normally, we use `style.*` to assign individual style properties. We can't set the full style like `div.style="color: red; width: 100px"`, because `div.style` is an object, and it's read-only. +````smart header="Reescribir todo usando `style.cssText`" +Normalmente, podemos usar `style.*` para asignar propiedades de estilo individuales. No podemos establecer todo el estilo como `div.style="color: red; width: 100px"`, porque `div.style` es un objeto y es solo de lectura. -To set the full style as a string, there's a special property `style.cssText`: +Para establecer todo el estilo como una cadena, hay una propiedad especial: `style.cssText`: ```html run <div id="div">Button</div> <script> - // we can set special style flags like "important" here + // podemos establecer estilos especiales con banderas como "important" div.style.cssText=`color: red !important; background-color: yellow; width: 100px; @@ -156,27 +156,27 @@ To set the full style as a string, there's a special property `style.cssText`: </script> ``` -This property is rarely used, because such assignment removes all existing styles: it does not add, but replaces them. May occasionally delete something needed. But we can safely use it for new elements, when we know we won't delete an existing style. +Esta propiedad es rara vez usada, porque tal asignación remueve todo los estilos: no agrega estilos sino que los reemplaza en su totalidad. Ocasionalmente podría eliminar algo necesario. Pero podemos usarlo de manera segura para nuevos elementos, cuando sabemos que no vamos a eliminar un estilo existente. -The same can be accomplished by setting an attribute: `div.setAttribute('style', 'color: red...')`. +Lo mismo se puede lograr estableciendo un atributo: `div.setAttribute('style', 'color: red...')`. ```` -## Mind the units +## Cuidado con las unidades CSS -Don't forget to add CSS units to values. +No olvidar agregar las unidades CSS a los valores. -For instance, we should not set `elem.style.top` to `10`, but rather to `10px`. Otherwise it wouldn't work: +Por ejemplo, nosotros no debemos establecer `elem.style.top` a `10`, sino más bien a `10px`. De lo contrario no funcionaría: ```html run height=100 <body> <script> *!* - // doesn't work! + // ¡no funciona! document.body.style.margin = 20; - alert(document.body.style.margin); // '' (empty string, the assignment is ignored) + alert(document.body.style.margin); // '' (cadena vacía, la asignación es ignorada) */!* - // now add the CSS unit (px) - and it works + // ahora agregamos la unidad CSS (px) y esta sí funciona document.body.style.margin = '20px'; alert(document.body.style.margin); // 20px @@ -186,19 +186,19 @@ For instance, we should not set `elem.style.top` to `10`, but rather to `10px`. </body> ``` -Please note: the browser "unpacks" the property `style.margin` in the last lines and infers `style.marginLeft` and `style.marginTop` from it. +Tenga en cuenta: el navegador "desempaqueta" la propiedad `style.margin` en las últimas lineas e infiere `style.marginLeft` y `style.marginTop` de eso. -## Computed styles: getComputedStyle +## Estilos calculados: getComputedStyle -So, modifying a style is easy. But how to *read* it? +Entonces, modificar un estilo es fácil. ¿Pero cómo *leerlo*? -For instance, we want to know the size, margins, the color of an element. How to do it? +Por ejemplo, queremos saber el tamaño, los márgenes, el color de un elemento. ¿Cómo hacerlo? -**The `style` property operates only on the value of the `"style"` attribute, without any CSS cascade.** +**La propiedad `style` solo opera en el valor del atributo `"style"`, sin ninguna cascada de `css`.** -So we can't read anything that comes from CSS classes using `elem.style`. +Entonces no podemos leer ninguna clase CSS usando `elem.style`. -For instance, here `style` doesn't see the margin: +Por ejemplo, aquí `style` no ve el margen: ```html run height=60 no-beautify <head> @@ -206,35 +206,35 @@ For instance, here `style` doesn't see the margin: </head> <body> - The red text + El texto en rojo <script> *!* - alert(document.body.style.color); // empty - alert(document.body.style.marginTop); // empty + alert(document.body.style.color); // vacío + alert(document.body.style.marginTop); // vacío */!* </script> </body> ``` -...But what if we need, say, to increase the margin by `20px`? We would want the current value of it. +Pero si necesitamos incrementar el margen a `20px`? vamos el querer el valor de la misma. -There's another method for that: `getComputedStyle`. +Hay otro método para eso: `getComputedStyle`. -The syntax is: +La sintaxis es: ```js getComputedStyle(element, [pseudo]) ``` element -: Element to read the value for. +: Elemento del cual se va a leer el valor. pseudo -: A pseudo-element if required, for instance `::before`. An empty string or no argument means the element itself. +: Un pseudo-elemento es requerido, por ejemplo `::before`. Una cadena vacía o sin argumento significa el elemento mismo. -The result is an object with styles, like `elem.style`, but now with respect to all CSS classes. +El resultado es un objeto con estilos, como `elem.style`, pero ahora con respecto a todas las clases CSS. -For instance: +Por ejemplo: ```html run height=100 <head> @@ -245,7 +245,7 @@ For instance: <script> let computedStyle = getComputedStyle(document.body); - // now we can read the margin and the color from it + // ahora podemos leer los márgenes y el color de ahí alert( computedStyle.marginTop ); // 5px alert( computedStyle.color ); // rgb(255, 0, 0) @@ -254,44 +254,45 @@ For instance: </body> ``` -```smart header="Computed and resolved values" -There are two concepts in [CSS](https://drafts.csswg.org/cssom/#resolved-values): +```smart header="Valores calculado y resueltos" +Hay dos conceptos en [CSS](https://drafts.csswg.org/cssom/#resolved-values): -1. A *computed* style value is the value after all CSS rules and CSS inheritance is applied, as the result of the CSS cascade. It can look like `height:1em` or `font-size:125%`. -2. A *resolved* style value is the one finally applied to the element. Values like `1em` or `125%` are relative. The browser takes the computed value and makes all units fixed and absolute, for instance: `height:20px` or `font-size:16px`. For geometry properties resolved values may have a floating point, like `width:50.5px`. +1. Un estilo *calculado* es el valor final de aplicar todas las reglas y herencias CSS, como resultado de la cascada CSS. Puede parecer `height:1em` o `font-size:125%`. +2. Un estilo *resuelto* es la que finalmente se aplica al elemento. Valores como `1em` o `125%` son relativos. El navegador toma el valor calculado y hace que todas las unidades sean fijas y absolutas, por ejemplo: `height:20px` o `font-size:16px`. Para las propiedades de geometría los valores resueltos pueden tener un punto flotante, como `width:50.5px`. -A long time ago `getComputedStyle` was created to get computed values, but it turned out that resolved values are much more convenient, and the standard changed. +Hace mucho tiempo `getComputedStyle` fue creado para obtener los valores calculados, pero los valores resueltos son muchos más convenientes, y el estándar cambió. -So nowadays `getComputedStyle` actually returns the resolved value of the property, usually in `px` for geometry. +Así que hoy en día `getComputedStyle` en realidad devuelve el valor resuelto de la propiedad, usualmente en `px` para geometría. ``` -````warn header="`getComputedStyle` requires the full property name" -We should always ask for the exact property that we want, like `paddingLeft` or `marginTop` or `borderTopWidth`. Otherwise the correct result is not guaranteed. +````warn header="El método `getComputedStyle` requiere el nombre completo de la propiedad" +Siempre deberíamos preguntar por la propiedad exacta que queremos, como `paddingLeft` o `marginTop` o `borderTopWidth`. De lo contrario, no se garantiza el resultado correcto. -For instance, if there are properties `paddingLeft/paddingTop`, then what should we get for `getComputedStyle(elem).padding`? Nothing, or maybe a "generated" value from known paddings? There's no standard rule here. +Por ejemplo, si hay propiedades `paddingLeft/paddingTop`, entonces ¿qué deberíamos obtener de `getComputedStyle(elem).padding`? ¿Nada, o tal vez un valor "generado" de los paddings? No hay una regla estándar aquí. ```` -```smart header="Styles applied to `:visited` links are hidden!" -Visited links may be colored using `:visited` CSS pseudoclass. +```smart header="¡Los estilos aplicados a los enlaces `:visited` están ocultos!" +Los enlaces visitados pueden ser coloreados usando la pseudo-clase `:visited` de CSS. -But `getComputedStyle` does not give access to that color, because otherwise an arbitrary page could find out whether the user visited a link by creating it on the page and checking the styles. +Pero `getComputedStyle` no da acceso a ese color, porque de lo contrario una página cualquiera podría averiguar si el usuario visitó un enlace creándolo en la página y verificar los estilos. -JavaScript may not see the styles applied by `:visited`. And also, there's a limitation in CSS that forbids applying geometry-changing styles in `:visited`. That's to guarantee that there's no side way for an evil page to test if a link was visited and hence to break the privacy. +JavaScript no puede ver los estilos aplicados por `:visited`. También hay una limitación en CSS que prohíbe la aplicación de estilos de cambio de geometría en `:visited`. Eso es para garantizar que no haya forma para que una página maligna pruebe si un enlace fue visitado y vulnere la privacidad. ``` -## Summary +## Resumen -To manage classes, there are two DOM properties: +Para manejar clases, hay dos propiedades del DOM: -- `className` -- the string value, good to manage the whole set of classes. -- `classList` -- the object with methods `add/remove/toggle/contains`, good for individual classes. +- `className` -- el valor de la cadena, perfecto para manejar todo el conjunto de clases. +- `classList` -- el objeto con los métodos: `add/remove/toggle/contains`, perfecto para clases individuales. -To change the styles: +Para cambiar los estilos: -- The `style` property is an object with camelCased styles. Reading and writing to it has the same meaning as modifying individual properties in the `"style"` attribute. To see how to apply `important` and other rare stuff -- there's a list of methods at [MDN](mdn:api/CSSStyleDeclaration). +- La propiedad `style` es un objeto con los estilos en `camelcase`. +Leer y escribir tiene el mismo significado que modificar propiedades individuales en el atributo `"style"`. Para ver cómo aplicar `important` y otras cosas raras, hay una lista de métodos en [MDN](https://developer.mozilla.org/es/docs/Web/API/CSSStyleDeclaration). -- The `style.cssText` property corresponds to the whole `"style"` attribute, the full string of styles. +- La propiedad `style.cssText` corresponde a todo el atributo `"style"`, la cadena completa de estilos. -To read the resolved styles (with respect to all classes, after all CSS is applied and final values are calculated): +Para leer los estilos resueltos (con respecto a todas las clases, después de que se aplica todo el `css` y se calculan los valores finales): -- The `getComputedStyle(elem, [pseudo])` returns the style-like object with them. Read-only. +- El método `getComputedStyle(elem, [pseudo])` retorna el objeto de estilo con ellos (solo lectura). diff --git a/2-ui/1-document/09-size-and-scroll/1-get-scroll-height-bottom/solution.md b/2-ui/1-document/09-size-and-scroll/1-get-scroll-height-bottom/solution.md index 01e073655..af026c3ea 100644 --- a/2-ui/1-document/09-size-and-scroll/1-get-scroll-height-bottom/solution.md +++ b/2-ui/1-document/09-size-and-scroll/1-get-scroll-height-bottom/solution.md @@ -1,7 +1,7 @@ -The solution is: +La solución es: ```js let scrollBottom = elem.scrollHeight - elem.scrollTop - elem.clientHeight; ``` -In other words: (full height) minus (scrolled out top part) minus (visible part) -- that's exactly the scrolled out bottom part. +En otras palabras: (altura total) menos (parte superior desplazada) menos (parte visible) -- esa es exactamente la parte inferior desplazada. diff --git a/2-ui/1-document/09-size-and-scroll/1-get-scroll-height-bottom/task.md b/2-ui/1-document/09-size-and-scroll/1-get-scroll-height-bottom/task.md index 796039c2a..cc54da223 100644 --- a/2-ui/1-document/09-size-and-scroll/1-get-scroll-height-bottom/task.md +++ b/2-ui/1-document/09-size-and-scroll/1-get-scroll-height-bottom/task.md @@ -2,10 +2,10 @@ importance: 5 --- -# What's the scroll from the bottom? +# ¿Qué es el desplazamiento desde la parte inferior? -The `elem.scrollTop` property is the size of the scrolled out part from the top. How to get the size of the bottom scroll (let's call it `scrollBottom`)? +La propiedad `elem.scrollTop` es el tamaño desplazado desde la parte superior. ¿Cómo obtener el tamaño de la parte inferior desplazada (vamos a llamarlo `scrollBottom`)? -Write the code that works for an arbitrary `elem`. +Escribe el código que funcione para un `elem` arbitrario. -P.S. Please check your code: if there's no scroll or the element is fully scrolled down, then it should return `0`. +P.S. Por favor revisa tu código: si no hay desplazamiento o el elemento está complemamente desplazado, debería retornar `0`. diff --git a/2-ui/1-document/09-size-and-scroll/2-scrollbar-width/solution.md b/2-ui/1-document/09-size-and-scroll/2-scrollbar-width/solution.md index 1ba1e5e57..61e5b79d1 100644 --- a/2-ui/1-document/09-size-and-scroll/2-scrollbar-width/solution.md +++ b/2-ui/1-document/09-size-and-scroll/2-scrollbar-width/solution.md @@ -1,16 +1,16 @@ -To get the scrollbar width, we can create an element with the scroll, but without borders and paddings. +Para obtener el ancho de la barra de desplazamiento, podemos crear un elemento con el scroll, pero sin bordes ni rellenos. -Then the difference between its full width `offsetWidth` and the inner content area width `clientWidth` will be exactly the scrollbar: +Entonces la diferencia entre su ancho completo `offsetWidth` y el ancho del area interior `clientWidth` será exactamente la barra de desplazamiento: ```js run -// create a div with the scroll +// crea un div con el scroll let div = document.createElement('div'); div.style.overflowY = 'scroll'; div.style.width = '50px'; div.style.height = '50px'; -// must put it in the document, otherwise sizes will be 0 +// debe ponerlo en el documento, de lo contrario los tamaños serán 0 document.body.append(div); let scrollWidth = div.offsetWidth - div.clientWidth; diff --git a/2-ui/1-document/09-size-and-scroll/2-scrollbar-width/task.md b/2-ui/1-document/09-size-and-scroll/2-scrollbar-width/task.md index b39004cbf..31de6a310 100644 --- a/2-ui/1-document/09-size-and-scroll/2-scrollbar-width/task.md +++ b/2-ui/1-document/09-size-and-scroll/2-scrollbar-width/task.md @@ -2,10 +2,10 @@ importance: 3 --- -# What is the scrollbar width? +# ¿Qué es el ancho de la barra de desplazamiento? -Write the code that returns the width of a standard scrollbar. +Escribe el código que retorna el tamaño de una barra de desplazamiento estándar. -For Windows it usually varies between `12px` and `20px`. If the browser doesn't reserve any space for it (the scrollbar is half-translucent over the text, also happens), then it may be `0px`. +Para Windows esto usualmente varía entre `12px` y `20px`. Si el navegador no reserva algún espacio para esto (la barra de desplazamiento es medio traslúcida sobre el texto, también pasa), entonces puede ser `0px`. -P.S. The code should work for any HTML document, do not depend on its content. +P.S. El código debería funcionar con cualquier documento HTML, no depende de su contenido. diff --git a/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/ball-half/index.html b/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/ball-half/index.html index 8f855ecfa..289311f3b 100755 --- a/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/ball-half/index.html +++ b/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/ball-half/index.html @@ -29,8 +29,8 @@ let ball = document.getElementById('ball') let field = document.getElementById('field') - // ball.offsetWidth=0 before image loaded! - // to fix: set width + // ball.offsetWidth=0 ¡antes de la carga de la imagen! + // para corregirlo: establecer el ancho "width" ball.style.left = Math.round(field.clientWidth / 2) + 'px' ball.style.top = Math.round(field.clientHeight / 2) + 'px' </script> diff --git a/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/solution.md b/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/solution.md index afa1d8f50..c8c4c6970 100644 --- a/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/solution.md +++ b/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/solution.md @@ -1,53 +1,53 @@ -The ball has `position:absolute`. It means that its `left/top` coordinates are measured from the nearest positioned element, that is `#field` (because it has `position:relative`). +La pelota tiene `position:absolute`. Significa que sus coordenadas `left/top` se miden desde el elemento posicionado más cercano, es decir `#field` (porque tiene `position:relative`). -The coordinates start from the inner left-upper corner of the field: +Las coordenadas inician desde el interior de la esquina superior izquierda del campo: ![](field.svg) -The inner field width/height is `clientWidth/clientHeight`. So the field center has coordinates `(clientWidth/2, clientHeight/2)`. +El interior del campo ancho/alto es `clientWidth/clientHeight`. Entonces el centro del campo tiene coordenadas `(clientWidth/2, clientHeight/2)`. -...But if we set `ball.style.left/top` to such values, then not the ball as a whole, but the left-upper edge of the ball would be in the center: +...Pero si configuramos `ball.style.left/top` a tales valores, entonces no la pelota en su conjunto, sino el borde superior izquierdo de la pelota estaría en el centro: ```js ball.style.left = Math.round(field.clientWidth / 2) + 'px'; ball.style.top = Math.round(field.clientHeight / 2) + 'px'; ``` -Here's how it looks: +Así es como se ve: [iframe height=180 src="ball-half"] -To align the ball center with the center of the field, we should move the ball to the half of its width to the left and to the half of its height to the top: +Para alinear la pelota al centro con el centro del campo, deberíamos mover la pelota a la mitad de su ancho a la izquierda y a la mitad de su altura hacia arriba: ```js ball.style.left = Math.round(field.clientWidth / 2 - ball.offsetWidth / 2) + 'px'; ball.style.top = Math.round(field.clientHeight / 2 - ball.offsetHeight / 2) + 'px'; ``` -Now the ball is finally centered. +Ahora la pelota está finalmente centrada. -````warn header="Attention: the pitfall!" +````warn header="Atención: ¡la trampa!" -The code won't work reliably while `<img>` has no width/height: +El código no funcionará seguramente mientras `<img>` no tenga width/height: ```html <img src="ball.png" id="ball"> ``` ```` -When the browser does not know the width/height of an image (from tag attributes or CSS), then it assumes them to equal `0` until the image finishes loading. +Cuando el navegador no conoce el ancho/alto de una imagen (de un atributo o CSS), entonces este asume que es igual a `0` hasta que la imagen termine de cargarse. -So the value of `ball.offsetWidth` will be `0` until the image loads. That leads to wrong coordinates in the code above. +Entonces el valor de `ball.offsetWidth` deberá ser `0` hasta que la imagen cargue. Eso conduce a coordinadas incorrectas en el código anterior. -After the first load, the browser usually caches the image, and on reloads it will have the size immediately. But on the first load the value of `ball.offsetWidth` is `0`. +Después de la primera carga, el navegador usualmente almacena en caché la imagen, y cuando se vuelva a cargar esta tendrá el tamaño inmediatamente. Pero en la primera carga el valor de `ball.offsetWidth` es `0`. -We should fix that by adding `width/height` to `<img>`: +Deberíamos arreglar eso agregando `width/height` en `<img>`: ```html <img src="ball.png" *!*width="40" height="40"*/!* id="ball"> ``` -...Or provide the size in CSS: +...O indicar el tamaño en CSS: ```css #ball { diff --git a/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/solution.view/index.html b/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/solution.view/index.html index 9f21e5421..5974040ed 100755 --- a/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/solution.view/index.html +++ b/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/solution.view/index.html @@ -26,8 +26,8 @@ <script> - // ball.offsetWidth=0 before image loaded! - // to fix: set width + // ball.offsetWidth=0 ¡antes de la carga de la imagen! + // para corregirlo: establecer el ancho "width" ball.style.left = Math.round(field.clientWidth / 2 - ball.offsetWidth / 2) + 'px' ball.style.top = Math.round(field.clientHeight / 2 - ball.offsetHeight / 2) + 'px' </script> diff --git a/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/task.md b/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/task.md index f56e0858b..ddcb15003 100644 --- a/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/task.md +++ b/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/task.md @@ -2,19 +2,19 @@ importance: 5 --- -# Place the ball in the field center +# Coloca la pelota en el centro del campo. -Here's how the source document looks: +Así es como se ve el documento de origen: [iframe src="source" edit link height=180] -What are coordinates of the field center? +¿Cuáles son las coordenadas del centro de campo? -Calculate them and use to place the ball into the center of the green field: +Calcúlalos y úsalos para colocar la pelota en el centro del campo verde: [iframe src="solution" height=180] -- The element should be moved by JavaScript, not CSS. -- The code should work with any ball size (`10`, `20`, `30` pixels) and any field size, not be bound to the given values. +- El elemento debe ser movido por JavaScript, no por CSS. +- El código debería funcionar con cualquier una pelota de cualquier tamaño (`10`, `20`, `30` pixels) y cualquier tamaño de campo, no debe estar vinculado a los valores dados. -P.S. Sure, centering could be done with CSS, but here we want exactly JavaScript. Further we'll meet other topics and more complex situations when JavaScript must be used. Here we do a "warm-up". +P.S. Claro, el centrado se podría hacer con CSS, pero aquí lo queremos específicamente con JavaScript. Además, conoceremos otros temas y situaciones más complejas en las que se debe utilizar JavaScript. Aquí hacemos un "calentamiento". diff --git a/2-ui/1-document/09-size-and-scroll/6-width-vs-clientwidth/solution.md b/2-ui/1-document/09-size-and-scroll/6-width-vs-clientwidth/solution.md index 0a18bd86e..abe0bb37d 100644 --- a/2-ui/1-document/09-size-and-scroll/6-width-vs-clientwidth/solution.md +++ b/2-ui/1-document/09-size-and-scroll/6-width-vs-clientwidth/solution.md @@ -1,6 +1,6 @@ -Differences: +Diferencias: -1. `clientWidth` is numeric, while `getComputedStyle(elem).width` returns a string with `px` at the end. -2. `getComputedStyle` may return non-numeric width like `"auto"` for an inline element. -3. `clientWidth` is the inner content area of the element plus paddings, while CSS width (with standard `box-sizing`) is the inner content area *without paddings*. -4. If there's a scrollbar and the browser reserves the space for it, some browser substract that space from CSS width (cause it's not available for content any more), and some do not. The `clientWidth` property is always the same: scrollbar size is substracted if reserved. +1. `clientWidth` es numérico, mientras `getComputedStyle(elem).width` retorna una cadena con `px` en el final. +2. `getComputedStyle` puede devolver un ancho no numérico como `"auto"` para un elemento en linea. +3. `clientWidth` es el contenido interior del área del elemento más los rellenos, mientras el ancho de CSS (con el estándar `box-sizing`) es el contenido interior del área *sin rellenos*. +4. Si hay una barra de desplazamiento y el navegador reserva espacio para esta, algunos navegadores restan ese espacio del ancho de CSS (por que no está disponible para el contenido), y otros no. La propiedad `clientWidth` es siempre la misma: el tamaño de la barra de desplazamiento se resta si está reservado. diff --git a/2-ui/1-document/09-size-and-scroll/6-width-vs-clientwidth/task.md b/2-ui/1-document/09-size-and-scroll/6-width-vs-clientwidth/task.md index 5378c5915..ad67d4f5f 100644 --- a/2-ui/1-document/09-size-and-scroll/6-width-vs-clientwidth/task.md +++ b/2-ui/1-document/09-size-and-scroll/6-width-vs-clientwidth/task.md @@ -2,8 +2,8 @@ importance: 5 --- -# The difference: CSS width versus clientWidth +# La diferencia: CSS width versus clientWidth -What's the difference between `getComputedStyle(elem).width` and `elem.clientWidth`? +¿Cuál es la diferencia entre `getComputedStyle(elem).width` y `elem.clientWidth`? -Give at least 3 differences. The more the better. +Dar al menos 3 diferencias. Mientras más, mejor. diff --git a/2-ui/1-document/09-size-and-scroll/article.md b/2-ui/1-document/09-size-and-scroll/article.md index 66f28115f..3fe93bac5 100644 --- a/2-ui/1-document/09-size-and-scroll/article.md +++ b/2-ui/1-document/09-size-and-scroll/article.md @@ -1,16 +1,16 @@ -# Element size and scrolling +# Tamaño de elementos y desplazamiento -There are many JavaScript properties that allow us to read information about element width, height and other geometry features. +Hay muchas propiedades en JavaScript que nos permiten leer información sobre el ancho, alto y otras características geométricas de los elementos. -We often need them when moving or positioning elements in JavaScript. +A menudo necesitamos de ellas cuando movemos o posicionamos un elemento en JavaScript. -## Sample element +## Elemento de muestra -As a sample element to demonstrate properties we'll use the one given below: +Como un elemento de muestra para demostrar las propiedades, usaremos el que se indica a continuación: ```html no-beautify <div id="example"> - ...Text... + ...Texto... </div> <style> #example { @@ -23,49 +23,49 @@ As a sample element to demonstrate properties we'll use the one given below: </style> ``` -It has the border, padding and scrolling. The full set of features. There are no margins, as they are not the part of the element itself, and there are no special properties for them. +Este tiene borde, relleno y desplazamiento. El conjunto completo de funciones. No hay márgenes porque no son parte del elemento en sí, y no tienen propiedades especiales. -The element looks like this: +El elemento tiene este aspecto: ![](metric-css.svg) -You can [open the document in the sandbox](sandbox:metric). +Puedes [abrir el documento en la zona de pruebas](sandbox:metric). -```smart header="Mind the scrollbar" -The picture above demonstrates the most complex case when the element has a scrollbar. Some browsers (not all) reserve the space for it by taking it from the content (labeled as "content width" above). +```smart header="Atento a la barra de desplazamiento (scrollbar)" +La imagen de arriba muestra el caso más complejo cuando el elemento tiene una barra de desplazamiento. Algunos navegadores (no todos) reservan espacio para tomarlo del contenido (el etiquetado como "content width" arriba). -So, without scrollbar the content width would be `300px`, but if the scrollbar is `16px` wide (the width may vary between devices and browsers) then only `300 - 16 = 284px` remains, and we should take it into account. That's why examples from this chapter assume that there's a scrollbar. Without it, some calculations are simpler. +Entonces, sin la barra de desplazamiento el ancho del contenido sería `300px`, pero si la barra de desplazamiento tiene un ancho de `16px` (el ancho puede variar entre dispositivos y navegadores) entonces solo queda `300 - 16 = 284px`, y deberíamos tenerlo en cuenta. Es por eso que los ejemplos de este capítulo asumen que hay una barra de desplazamiento. Sin ella, algunos cálculos son más sencillos. ``` -```smart header="The `padding-bottom` area may be filled with text" -Usually paddings are shown empty on our illustrations, but if there's a lot of text in the element and it overflows, then browsers show the "overflowing" text at `padding-bottom`, that's normal. +```smart header="El área `padding-bottom` puede estar lleno de texto" +Por lo general, los rellenos se muestran vacíos en nuestras ilustraciones, pero si hay mucho texto en el elemento y se desborda, los navegadores muestran el texto "desbordado" en `padding-bottom`, eso es normal. ``` -## Geometry +## Geometría -Here's the overall picture with geometry properties: +Aquí está la imagen general con propiedades geométricas: ![](metric-all.svg) -Values of these properties are technically numbers, but these numbers are "of pixels", so these are pixel measurements. +Los valores de estas propiedades son técnicamente números, pero estos números son "de píxeles", así que estas son medidas de píxeles. -Let's start exploring the properties starting from the outside of the element. +Comencemos a explotar las propiedades, iniciando desde el exterior del elemento. ## offsetParent, offsetLeft/Top -These properties are rarely needed, but still they are the "most outer" geometry properties, so we'll start with them. +Estas propiedades son raramente necesarias, pero aún son las propiedades de geometría "más externas" así que comenzaremos con ellas. -The `offsetParent` is the nearest ancestor that the browser uses for calculating coordinates during rendering. +El `offsetParent` es el antepasado más cercano que usa el navegador para calcular las coordenadas durante el renderizado. -That's the nearest ancestor that is one of the following: +Ese es el antepasado más cercano que es uno de los siguientes: -1. CSS-positioned (`position` is `absolute`, `relative`, `fixed` or `sticky`), or -2. `<td>`, `<th>`, or `<table>`, or +1. Posicionado por CSS (`position` es `absolute`, `relative`, `fixed` o `sticky`), o... +2. `<td>`, `<th>`, or `<table>`, o... 3. `<body>`. -Properties `offsetLeft/offsetTop` provide x/y coordinates relative to `offsetParent` upper-left corner. +Las propiedades `offsetLeft/offsetTop` proporcionan coordenadas x/y relativas a la esquina superior izquierda de `offsetParent`. -In the example below the inner `<div>` has `<main>` as `offsetParent` and `offsetLeft/offsetTop` shifts from its upper-left corner (`180`): +En el siguiente ejemplo el `<div>` más interno tiene `<main>` como `offsetParent`, y `offsetLeft/offsetTop` lo desplaza desde su esquina superior izquierda (`180`): ```html run height=10 <main style="position: relative" id="main"> @@ -75,40 +75,40 @@ In the example below the inner `<div>` has `<main>` as `offsetParent` and `offse </main> <script> alert(example.offsetParent.id); // main - alert(example.offsetLeft); // 180 (note: a number, not a string "180px") + alert(example.offsetLeft); // 180 (nota: es un número, no un string "180px") alert(example.offsetTop); // 180 </script> ``` ![](metric-offset-parent.svg) -There are several occasions when `offsetParent` is `null`: +Hay varias ocasiones en la que `offsetParent` es `null`: -1. For not shown elements (`display:none` or not in the document). -2. For `<body>` and `<html>`. -3. For elements with `position:fixed`. +1. Para elementos no mostrados (`display:none` o no en el documento). +2. Para `<body>` y `<html>`. +3. Para elementos con `position:fixed`. ## offsetWidth/Height -Now let's move on to the element itself. +Ahora pasemos al elemento en sí. -These two properties are the simplest ones. They provide the "outer" width/height of the element. Or, in other words, its full size including borders. +Estas dos propiedades son las más simples. Proporcionan el ancho y alto "exterior" del elemento. O, en otras palabras, su tamaño completo, incluidos los bordes. ![](metric-offset-width-height.svg) -For our sample element: +Para nuestro elemento de muestra: -- `offsetWidth = 390` -- the outer width, can be calculated as inner CSS-width (`300px`) plus paddings (`2 * 20px`) and borders (`2 * 25px`). -- `offsetHeight = 290` -- the outer height. +- `offsetWidth = 390` -- el ancho exterior, puede ser calculado como CSS-width interno (`300px`) más acolchonados (`2 * 20px`) y bordes (`2 * 25px`). +- `offsetHeight = 290` -- el alto exterior. -````smart header="Geometry properties are zero/null for elements that are not displayed" -Geometry properties are calculated only for displayed elements. +````smart header="Las propiedades geométricas para elementos no mostrados son cero o null" +Las propiedades geométricas son calculadas solo para elementos mostrados. -If an element (or any of its ancestors) has `display:none` or is not in the document, then all geometry properties are zero (or `null` for `offsetParent`). +En un elemento (o cualquiera de sus antepasados) tiene `display:none` o no está en el documento, entonces las propiedades geométricas son cero (o `null` para `offsetParent`). -For example, `offsetParent` is `null`, and `offsetWidth`, `offsetHeight` are `0` when we created an element, but haven't inserted it into the document yet, or it (or its ancestor) has `display:none`. +Por ejemplo, `offsetParent` es `null`, y `offsetWidth`, `offsetHeight` son `0` cuando creamos un elemento pero aún no lo han insertado en el documento, o cuando éste (o su ancestro) tiene `display:none`. -We can use this to check if an element is hidden, like this: +Nosotros podemos usar esto para verificar si un elemento está oculto, así: ```js function isHidden(elem) { @@ -116,76 +116,76 @@ function isHidden(elem) { } ``` -Please note that such `isHidden` returns `true` for elements that are on-screen, but have zero sizes. +Observa que tal `isHidden` devuelve `true` para elementos que están en pantalla pero tienen tamaño cero. ```` ## clientTop/Left -Inside the element we have the borders. +Dentro del elemento, tenemos los bordes. -To measure them, there are properties `clientTop` and `clientLeft`. +Para medirlos, están las propiedades `clientTop` y `clientLeft`. -In our example: +En nuestro ejemplo: -- `clientLeft = 25` -- left border width -- `clientTop = 25` -- top border width +- `clientLeft = 25` -- ancho del borde izquierdo +- `clientTop = 25` -- ancho del borde superior ![](metric-client-left-top.svg) -...But to be precise -- these properties are not border width/height, but rather relative coordinates of the inner side from the outer side. +... pero para ser precisos: estas propiedades no son el ancho/alto del borde sino las coordenadas relativas del lado interior respecto al lado exterior. -What's the difference? +¿Cuál es la diferencia? -It becomes visible when the document is right-to-left (the operating system is in Arabic or Hebrew languages). The scrollbar is then not on the right, but on the left, and then `clientLeft` also includes the scrollbar width. +Esto se vuelve evidente cuando el documento está de derecha a izquierda (con el sistema operativo en idioma árabe, o hebreo). La barra de desplazamiento no está a la derecha sino a la izquierda, entonces `clientLeft` también incluye el ancho de la barra de desplazamiento. -In that case, `clientLeft` would be not `25`, but with the scrollbar width `25 + 16 = 41`. +En este caso, `clientLeft` no es `25`, sino que se suma el ancho de la barra de desplazamiento `25 + 16 = 41`. -Here's the example in hebrew: +Aquí está el ejemplo en hebreo: ![](metric-client-left-top-rtl.svg) ## clientWidth/Height -These properties provide the size of the area inside the element borders. +Esta propiedad proporciona el tamaño del área dentro de los bordes del elemento. -They include the content width together with paddings, but without the scrollbar: +Incluyen el ancho del contenido junto con los rellenos, pero sin la barra de desplazamiento: ![](metric-client-width-height.svg) -On the picture above let's first consider `clientHeight`. +En la imagen de arriba, consideramos primero `clientHeight`. -There's no horizontal scrollbar, so it's exactly the sum of what's inside the borders: CSS-height `200px` plus top and bottom paddings (`2 * 20px`) total `240px`. +No hay una barra de desplazamiento horizontal, por lo que es exactamente la suma de lo que está dentro de los bordes: CSS-height `200px` más el relleno superior e inferior (`2 * 20px`) totaliza `240px`. -Now `clientWidth` -- here the content width is not `300px`, but `284px`, because `16px` are occupied by the scrollbar. So the sum is `284px` plus left and right paddings, total `324px`. +Ahora `clientWidth`: aquí el ancho del contenido no es `300px`, sino `284px`, porque los `16px` son ocupados por la barra de desplazamiento. Entonces la suma es `284px` más los rellenos de izquierda y derecha, total `324px`. -**If there are no paddings, then `clientWidth/Height` is exactly the content area, inside the borders and the scrollbar (if any).** +**Si no hay rellenos, entonces `clientWidth/Height` es exactamente el área de contenido, dentro de los bordes y la barra de desplazamiento (si la hay).** ![](metric-client-width-nopadding.svg) -So when there's no padding we can use `clientWidth/clientHeight` to get the content area size. +Entonces, cuando no hay relleno, podremos usar `clientWidth/clientHeight` para obtener el tamaño del área de contenido. ## scrollWidth/Height -These properties are like `clientWidth/clientHeight`, but they also include the scrolled out (hidden) parts: +Estas propiedades son como `clientWidth/clientHeight`, pero también incluyen las partes desplazadas (ocultas): ![](metric-scroll-width-height.svg) -On the picture above: +En la imagen de arriba: -- `scrollHeight = 723` -- is the full inner height of the content area including the scrolled out parts. -- `scrollWidth = 324` -- is the full inner width, here we have no horizontal scroll, so it equals `clientWidth`. +- `scrollHeight = 723` -- es la altura interior completa del área de contenido, incluyendo las partes desplazadas. +- `scrollWidth = 324` -- es el ancho interior completo, aquí no tenemos desplazamiento horizontal, por lo que es igual a `clientWidth`. -We can use these properties to expand the element wide to its full width/height. +Podemos usar estas propiedades para expandir el elemento a su ancho/alto completo. -Like this: +Como esto: ```js -// expand the element to the full content height +// expanda el elemento a la altura completa del contenido element.style.height = `${element.scrollHeight}px`; ``` ```online -Click the button to expand the element: +Clic en el botón para expandir el elemento: <div id="element" style="width:300px;height:200px; padding: 0;overflow: auto; border:1px solid black;">text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text</div> @@ -194,47 +194,47 @@ Click the button to expand the element: ## scrollLeft/scrollTop -Properties `scrollLeft/scrollTop` are the width/height of the hidden, scrolled out part of the element. +Las propiedades `scrollLeft/scrollTop` son el ancho/alto de la parte oculta y desplazada del elemento. -On the picture below we can see `scrollHeight` and `scrollTop` for a block with a vertical scroll. +En la imagen abajo podemos ver `scrollHeight` y `scrollTop` para un bloque con un desplazamiento vertical. ![](metric-scroll-top.svg) -In other words, `scrollTop` is "how much is scrolled up". +En otras palabras, `scrollTop` es "cuánto se desplaza hacia arriba". -````smart header="`scrollLeft/scrollTop` can be modified" -Most of the geometry properties here are read-only, but `scrollLeft/scrollTop` can be changed, and the browser will scroll the element. +````smart header="`scrollLeft/scrollTop` puede ser modificado" +La mayoría de las propiedades aquí son solo lectura, pero `scrollLeft/scrollTop` se puede cambiar, y el navegador desplazará el elemento. ```online -If you click the element below, the code `elem.scrollTop += 10` executes. That makes the element content scroll `10px` down. +Si haces clic en el elemento de abajo, se ejecuta el código `elem.scrollTop += 10`. Eso hace que el contenido del elemento se desplace `10px` abajo. <div onclick="this.scrollTop+=10" style="cursor:pointer;border:1px solid black;width:100px;height:80px;overflow:auto">Click<br>Me<br>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9</div> ``` -Setting `scrollTop` to `0` or a big value, such as `1e9` will make the element scroll to the very top/bottom respectively. +Establecer `scrollTop` en `0` o un valor grande, como `1e9` hará que el elemento se desplace hacia arriba/abajo respectivamente. ```` -## Don't take width/height from CSS +## No uses width/height obtenidos de CSS -We've just covered geometry properties of DOM elements, that can be used to get widths, heights and calculate distances. +Acabamos de cubrir las propiedades geométricas de los elementos DOM, que se pueden usar para obtener anchos, alturas y calcular distancias. -But as we know from the chapter <info:styles-and-classes>, we can read CSS-height and width using `getComputedStyle`. +Pero como sabemos por el capítulo <info:styles-and-classes>, podemos leer CSS-height y width usando `getComputedStyle`. -So why not to read the width of an element with `getComputedStyle`, like this? +Entonces, ¿Por qué no leer el ancho de un elemento con `getComputedStyle` como aquí? ```js run let elem = document.body; -alert( getComputedStyle(elem).width ); // show CSS width for elem +alert( getComputedStyle(elem).width ); // muestra CSS width por elemento ``` -Why should we use geometry properties instead? There are two reasons: +¿Por qué deberíamos usar propiedades geométricas en su lugar? Hay dos razones: -1. First, CSS `width/height` depend on another property: `box-sizing` that defines "what is" CSS width and height. A change in `box-sizing` for CSS purposes may break such JavaScript. -2. Second, CSS `width/height` may be `auto`, for instance for an inline element: +1. Primero, CSS `width/height` dependen de otra propiedad: `box-sizing` que define "qué es" CSS width y height. Un cambio en `box-sizing` para propósitos de CSS puede romper dicho JavaScript. +2. Segundo, CSS `width/height` puede ser `auto`, por ejemplo para un elemento en linea: ```html run - <span id="elem">Hello!</span> + <span id="elem">Hola!</span> <script> *!* @@ -243,34 +243,34 @@ Why should we use geometry properties instead? There are two reasons: </script> ``` - From the CSS standpoint, `width:auto` is perfectly normal, but in JavaScript we need an exact size in `px` that we can use in calculations. So here CSS width is useless. + Desde el punto de vista de CSS, `width:auto` es perfectamente normal, pero en JavaScript necesitamos un tamaño exacto en `px` que podríamos usar en los cálculos. Entonces, aquí el ancho de CSS width es inútil. -And there's one more reason: a scrollbar. Sometimes the code that works fine without a scrollbar becomes buggy with it, because a scrollbar takes the space from the content in some browsers. So the real width available for the content is *less* than CSS width. And `clientWidth/clientHeight` take that into account. +Y hay una razón más: una barra de desplazamiento. A veces, el código que funcionaba bien sin una barra de desplazamiento tiene errores, porque una barra de desplazamiento toma el espacio del contenido en algunos navegadores. Entonces, el ancho real disponible para el contenido es *menor* que el ancho de CSS. Y `clientWidth/clientHeight` tiene eso en cuenta. -...But with `getComputedStyle(elem).width` the situation is different. Some browsers (e.g. Chrome) return the real inner width, minus the scrollbar, and some of them (e.g. Firefox) -- CSS width (ignore the scrollbar). Such cross-browser differences is the reason not to use `getComputedStyle`, but rather rely on geometry properties. +...Pero con `getComputedStyle(elem).width` la situación es diferente. Algunos navegadores (p.e. Chrome) devuelven el ancho interno real, menos la barra de desplazamiento, y algunos de ellos (p.e. Firefox) -- CSS width (ignora la barra de desplazamiento). Estas diferencias entre los navegadores son la razón para no usar `getComputedStyle`, sino confiar en las propiedades geométricas. ```online -If your browser reserves the space for a scrollbar (most browsers for Windows do), then you can test it below. +Si tu navegador reserva el espacio para la barra de desplazamiento (la mayoría de navegadores para Windows lo hacen), pueden probarlo a continuación. [iframe src="cssWidthScroll" link border=1] -The element with text has CSS `width:300px`. +El elemento con texto tiene CSS `width:300px`. -On a Desktop Windows OS, Firefox, Chrome, Edge all reserve the space for the scrollbar. But Firefox shows `300px`, while Chrome and Edge show less. That's because Firefox returns the CSS width and other browsers return the "real" width. +En un Windows OS de escritorio, Firefox, Chrome, Edge reservan el espacio para la barra de desplazamiento. Pero Firefox muestra `300px`, mientras que Chrome y Edge muestran menos. Eso es por que Firefox devuelve el ancho del CSS y otros navegadores devuelven el ancho "real". ``` -Please note that the described difference is only about reading `getComputedStyle(...).width` from JavaScript, visually everything is correct. +Tenga en cuenta que la diferencia descrita es solo de leer `getComputedStyle(...).width` de JavaScript, visualmente todo es correcto. -## Summary +## Resumen -Elements have the following geometry properties: +Los elementos tienen las siguientes propiedades geométricas: -- `offsetParent` -- is the nearest positioned ancestor or `td`, `th`, `table`, `body`. -- `offsetLeft/offsetTop` -- coordinates relative to the upper-left edge of `offsetParent`. -- `offsetWidth/offsetHeight` -- "outer" width/height of an element including borders. -- `clientLeft/clientTop` -- the distances from the upper-left outer corner to the upper-left inner (content + padding) corner. For left-to-right OS they are always the widths of left/top borders. For right-to-left OS the vertical scrollbar is on the left so `clientLeft` includes its width too. -- `clientWidth/clientHeight` -- the width/height of the content including paddings, but without the scrollbar. -- `scrollWidth/scrollHeight` -- the width/height of the content, just like `clientWidth/clientHeight`, but also include scrolled-out, invisible part of the element. -- `scrollLeft/scrollTop` -- width/height of the scrolled out upper part of the element, starting from its upper-left corner. +- `offsetParent` -- es el ancestro posicionado más cercano o `td`, `th`, `table`, `body`. +- `offsetLeft/offsetTop` -- coordenadas relativas al borde superior izquierdo de `offsetParent`. +- `offsetWidth/offsetHeight` -- ancho/alto "exterior" de un elemento, incluidos los bordes. +- `clientLeft/clientTop` -- las distancias desde la esquina exterior superior izquierda a la esquina interior superior izquierda (contenido + relleno). Para los Sistemas Operativos de izquierda a derecha, siempre son los anchos de los bordes izquierdo/superior. Para los Sistemas Operativos de derecha a izquierda, la barra de desplazamiento está a la izquierda, por lo que `clientLeft` también incluye su ancho. +- `clientWidth/clientHeight` -- el ancho/alto del contenido incluyendo rellenos, pero sin la barra de desplazamiento. +- `scrollWidth/scrollHeight` -- el ancho/alto del contenido, al igual que `clientWidth/clientHeight`, pero también incluye la parte invisible desplazada del elemento. +- `scrollLeft/scrollTop` -- ancho/alto de la parte superior desplazada del elemento, comenzando desde la esquina superior izquierda. -All properties are read-only except `scrollLeft/scrollTop` that make the browser scroll the element if changed. +Todas las propiedades son solo lectura excepto `scrollLeft/scrollTop` que hacen que el navegador desplace el elemento si se cambia. diff --git a/2-ui/1-document/09-size-and-scroll/cssWidthScroll.view/index.html b/2-ui/1-document/09-size-and-scroll/cssWidthScroll.view/index.html index 2869c7ed9..3a1a4dc25 100755 --- a/2-ui/1-document/09-size-and-scroll/cssWidthScroll.view/index.html +++ b/2-ui/1-document/09-size-and-scroll/cssWidthScroll.view/index.html @@ -7,7 +7,7 @@ text text text text text text text text text text text text text text text text text text text text text text text text text text </div> - The element has <code>style="width:300px"</code> + El elemento tiene <code>style="width:300px"</code> <br> <button onclick="alert( getComputedStyle(elem).width )">alert( getComputedStyle(elem).width )</button> diff --git a/2-ui/1-document/10-size-and-scroll-window/article.md b/2-ui/1-document/10-size-and-scroll-window/article.md index 08a2f6576..2dd337871 100644 --- a/2-ui/1-document/10-size-and-scroll-window/article.md +++ b/2-ui/1-document/10-size-and-scroll-window/article.md @@ -1,50 +1,50 @@ -# Window sizes and scrolling +# Tamaño de ventana y desplazamiento -How do we find the width and height of the browser window? How do we get the full width and height of the document, including the scrolled out part? How do we scroll the page using JavaScript? +¿Cómo encontramos el ancho y el alto de la ventana del navegador? ¿Cómo obtenemos todo el ancho y la altura del documento, incluida la parte desplazada? ¿Cómo desplazamos la página usando JavaScript? -For this type of information, we can use the root document element `document.documentElement`, that corresponds to the `<html>` tag. But there are additional methods and peculiarities to consider. +Para la mayoría de estas cuestiones, podemos usar el elemento de documento raíz `document.documentElement`, que corresponde a la etiqueta `<html>`. Pero hay métodos y peculiaridades adicionales lo suficientemente importantes para considerar. -## Width/height of the window +## Ancho/alto de la ventana -To get window width and height, we can use the `clientWidth/clientHeight` of `document.documentElement`: +Para obtener el ancho y alto de la ventana, podemos usar `clientWidth / clientHeight` de `document.documentElement`: ![](document-client-width-height.svg) ```online -For instance, this button shows the height of your window: +Por ejemplo, este botón muestra la altura de su ventana: <button onclick="alert(document.documentElement.clientHeight)">alert(document.documentElement.clientHeight)</button> ``` -````warn header="Not `window.innerWidth/innerHeight`" -Browsers also support properties like `window.innerWidth/innerHeight`. They look like what we want, so why not to use them instead? +````warn header="No *window.innerWidth/Height*" +Los navegadores también admiten propiedades `window.innerWidth / innerHeight`. Se parecen a lo que queremos. Entonces, ¿por qué no usarlos? -If there exists a scrollbar, and it occupies some space, `clientWidth/clientHeight` provide the width/height without it (subtract it). In other words, they return the width/height of the visible part of the document, available for the content. +Si existe una barra de desplazamiento, y ocupa algo de espacio, `clientWidth / clientHeight` proporciona el ancho/alto sin ella (resta el espacio desplazado). En otras palabras, devuelven ancho/alto de la parte visible del documento, disponible para el contenido. -`window.innerWidth/innerHeight` includes the scrollbar. +... Y `window.innerWidth / innerHeight` incluye la barra de desplazamiento. -If there's a scrollbar, and it occupies some space, then these two lines show different values: +Si hay una barra de desplazamiento y ocupa algo de espacio, estas dos líneas muestran valores diferentes: ```js run -alert( window.innerWidth ); // full window width -alert( document.documentElement.clientWidth ); // window width minus the scrollbar +alert( window.innerWidth ); // ancho de la ventana completa +alert( document.documentElement.clientWidth ); // ancho de ventana menos el desplazamiento. ``` -In most cases, we need the *available* window width in order to draw or position something within scrollbars (if there are any), so we should use `documentElement.clientHeight/clientWidth`. +En la mayoría de los casos, necesitamos el ancho de ventana *disponible*, para dibujar o colocar algo. Es decir: el espacio del desplazamiento si hay alguno. Entonces deberíamos usar `documentElement.clientHeight/Width`. ```` -```warn header="`DOCTYPE` is important" -Please note: top-level geometry properties may work a little bit differently when there's no `<!DOCTYPE HTML>` in HTML. Odd things are possible. +```warn header="*DOCTYPE* es importante" +Tenga en cuenta que las propiedades de geometría de nivel superior pueden funcionar de manera un poco diferente cuando no hay `<!DOCTYPE HTML>` en HTML. Pueden suceder cosas extrañas. -In modern HTML we should always write `DOCTYPE`. +En HTML moderno siempre debemos escribir `DOCTYPE`. ``` -## Width/height of the document +## Ancho/Alto del documento -Theoretically, as the root document element is `document.documentElement`, and it encloses all the content, we could measure the document's full size as `document.documentElement.scrollWidth/scrollHeight`. +Teóricamente, como el elemento del documento raíz es `document.documentElement`, e incluye todo el contenido, podríamos medir el tamaño completo del documento con `document.documentElement.scrollWidth / scrollHeight`. -But on that element, for the whole page, these properties do not work as intended. In Chrome/Safari/Opera, if there's no scroll, then `documentElement.scrollHeight` may be even less than `documentElement.clientHeight`! Weird, right? +Pero en ese elemento, para toda la página, estas propiedades no funcionan según lo previsto. ¡En Chrome/Safari/Opera si no hay desplazamiento, entonces `documentElement.scrollHeight` puede ser incluso menor que `documentElement.clientHeight`! Suena como una tontería, raro, ¿verdad? -To reliably obtain the full document height, we should take the maximum of these properties: +Para obtener de manera confiable la altura completa del documento, debemos tomar el máximo de estas propiedades: ```js run let scrollHeight = Math.max( @@ -53,110 +53,110 @@ let scrollHeight = Math.max( document.body.clientHeight, document.documentElement.clientHeight ); -alert('Full document height, with scrolled out part: ' + scrollHeight); +alert('Altura completa del documento, con parte desplazada: ' + scrollHeight); ``` -Why so? Better don't ask. These inconsistencies come from ancient times, not a "smart" logic. +¿Por qué? Mejor no preguntes. Estas inconsistencias provienen de tiempos antiguos, no una lógica "inteligente". -## Get the current scroll [#page-scroll] +## Obtener el desplazamiento actual [#page-scroll] -DOM elements have their current scroll state in their `scrollLeft/scrollTop` properties. +Los elementos DOM tienen su estado de desplazamiento actual en sus propiedades `elem.scrollLeft/scrollTop`. -For document scroll, `document.documentElement.scrollLeft/scrollTop` works in most browsers, except older WebKit-based ones, like Safari (bug [5991](https://bugs.webkit.org/show_bug.cgi?id=5991)), where we should use `document.body` instead of `document.documentElement`. +El desplazamiento de documentos, `document.documentElement.scrollLeft / Top` funciona en la mayoría de los navegadores, excepto los más antiguos basados en WebKit, como Safari (bug [5991](https://bugs.webkit.org/show_bug.cgi?id=5991)), donde deberíamos usar `document.body` en lugar de `document.documentElement`. -Luckily, we don't have to remember these peculiarities at all, because the scroll is available in the special properties, `window.pageXOffset/pageYOffset`: +Afortunadamente, no tenemos que recordar estas peculiaridades en absoluto, porque el desplazamiento está disponible en las propiedades especiales `window.pageXOffset/pageYOffset`: ```js run -alert('Current scroll from the top: ' + window.pageYOffset); -alert('Current scroll from the left: ' + window.pageXOffset); +alert('Desplazamiento actual desde la parte superior: ' + window.pageYOffset); +alert('Desplazamiento actual desde la parte izquierda: ' + window.pageXOffset); ``` -These properties are read-only. +Estas propiedades son de solo lectura. -```smart header="Also available as `window` properties `scrollX` and `scrollY`" -For historical reasons, both properties exist, but they are the same: -- `window.pageXOffset` is an alias of `window.scrollX`. -- `window.pageYOffset` is an alias of `window.scrollY`. +```smart header="También disponible como propiedades `window`: `scrollX` y `scrollY`" +Por razones históricas existen ambas propiedades, pero ambas son lo mismo: +- `window.pageXOffset` es un alias de `window.scrollX`. +- `window.pageYOffset` es un alias de `window.scrollY`. ``` -## Scrolling: scrollTo, scrollBy, scrollIntoView [#window-scroll] +## Desplazamiento: scrollTo, scrollBy, scrollIntoView [#window-scroll] ```warn -To scroll the page with JavaScript, its DOM must be fully built. +para desplazar la página desde JavaScript, su DOM debe estar completamente construido. -For instance, if we try to scroll the page with a script in `<head>`, it won't work. +Por ejemplo, si intentamos desplazar la página desde el script en `<head>`, no funcionará. ``` -Regular elements can be scrolled by changing `scrollTop/scrollLeft`. +Los elementos regulares se pueden desplazar cambiando `scrollTop/scrollLeft`. -We can do the same for the page using `document.documentElement.scrollTop/scrollLeft` (except Safari, where `document.body.scrollTop/Left` should be used instead). +Nosotros podemos hacer lo mismo para la página usando `document.documentElement.scrollTop/Left` (excepto Safari, donde `document.body.scrollTop/Left` debería usarse en su lugar). -Alternatively, there's a simpler, universal solution: special methods [window.scrollBy(x,y)](mdn:api/Window/scrollBy) and [window.scrollTo(pageX,pageY)](mdn:api/Window/scrollTo). +Alternativamente, hay una solución más simple y universal: métodos especiales [window.scrollBy(x,y)](https://developer.mozilla.org/es/docs/Web/API/Window/scrollBy) y [window.scrollTo(pageX,pageY)](https://developer.mozilla.org/es/docs/Web/API/Window/scrollTo). -- The method `scrollBy(x,y)` scrolls the page *relative to its current position*. For instance, `scrollBy(0,10)` scrolls the page `10px` down. +- El método `scrollBy(x, y)` desplaza la página *en relación con su posición actual*. Por ejemplo, `scrollBy(0,10)` desplaza la página `10px` hacia abajo. ```online - The button below demonstrates this: + El siguiente botón demuestra esto: <button onclick="window.scrollBy(0,10)">window.scrollBy(0,10)</button> ``` -- The method `scrollTo(pageX,pageY)` scrolls the page *to absolute coordinates*, so that the top-left corner of the visible part has coordinates `(pageX, pageY)` relative to the document's top-left corner. It's like setting `scrollLeft/scrollTop`. +- El método `scrollTo(pageX, pageY)` desplaza la página *a coordenadas absolutas*, de modo que la esquina superior izquierda de la parte visible tiene coordenadas `(pageX, pageY)` en relación con la esquina superior izquierda del documento. Es como configurar `scrollLeft / scrollTop`. - To scroll to the very beginning, we can use `scrollTo(0,0)`. + Para desplazarnos hasta el principio, podemos usar `scrollTo(0,0)`. ```online <button onclick="window.scrollTo(0,0)">window.scrollTo(0,0)</button> ``` -These methods work for all browsers the same way. +Estos métodos funcionan para todos los navegadores de la misma manera. ## scrollIntoView -For completeness, let's cover one more method: [elem.scrollIntoView(top)](mdn:api/Element/scrollIntoView). +Para completar, cubramos un método más: [elem.scrollIntoView(top)](https://developer.mozilla.org/es/docs/Web/API/Element/scrollIntoView). -The call to `elem.scrollIntoView(top)` scrolls the page to make `elem` visible. It has one argument: +La llamada a `elem.scrollIntoView(top)` desplaza la página para hacer visible `elem`. Tiene un argumento: -- If `top=true` (that's the default), then the page will be scrolled to make `elem` appear on the top of the window. The upper edge of the element will be aligned with the window top. -- If `top=false`, then the page scrolls to make `elem` appear at the bottom. The bottom edge of the element will be aligned with the window bottom. +- si `top=true` (ese es el valor predeterminado), la página se desplazará para que aparezca `element` en la parte superior de la ventana. El borde superior del elemento está alineado con la parte superior de la ventana. +- si `top=false`, la página se desplaza para hacer que `element` aparezca en la parte inferior. El borde inferior del elemento está alineado con la parte inferior de la ventana. ```online -The button below scrolls the page to position itself at the window top: +El botón a continuación desplaza la página para mostrarse en la parte superior de la ventana: <button onclick="this.scrollIntoView()">this.scrollIntoView()</button> -And this button scrolls the page to position itself at the bottom: +Y este botón desplaza la página para mostrarla en la parte inferior: <button onclick="this.scrollIntoView(false)">this.scrollIntoView(false)</button> ``` -## Forbid the scrolling +## Prohibir el desplazamiento -Sometimes we need to make the document "unscrollable". For instance, when we need to cover the page with a large message requiring immediate attention, and we want the visitor to interact with that message, not with the document. +A veces necesitamos hacer que el documento sea "inescrutable". Por ejemplo, cuando necesitamos cubrirlo con un mensaje grande que requiere atención inmediata, y queremos que el visitante interactúe con ese mensaje, no con el documento. -To make the document unscrollable, it's enough to set `document.body.style.overflow = "hidden"`. The page will "freeze" at its current scroll position. +Para hacer que el documento sea inescrutable, es suficiente establecer `document.body.style.overflow="hidden"`. La página se congelará en su desplazamiento actual. ```online -Try it: +Prueba esto: <button onclick="document.body.style.overflow = 'hidden'">document.body.style.overflow = 'hidden'</button> <button onclick="document.body.style.overflow = ''">document.body.style.overflow = ''</button> -The first button freezes the scroll, while the second one releases it. +El primer botón congela el desplazamiento, el segundo lo reanuda. ``` -We can use the same technique to freeze the scroll for other elements, not just for `document.body`. +Podemos usar la misma técnica para "congelar" el desplazamiento para otros elementos, no solo para `document.body`. -The drawback of the method is that the scrollbar disappears. If it occupied some space, then that space is now free and the content "jumps" to fill it. +El inconveniente del método es que la barra de desplazamiento desaparece. Si ocupaba algo de espacio, entonces ese espacio ahora es libre y el contenido "salta" para llenarlo. -That looks a bit odd, but can be worked around if we compare `clientWidth` before and after the freeze. If it increased (the scrollbar disappeared), then add `padding` to `document.body` in place of the scrollbar to keep the content width the same. +Eso parece un poco extraño, pero puede solucionarse si comparamos `clientWidth` antes y después del congelamiento, y si aumentó (la barra de desplazamiento desapareció) luego agregue `padding` a `document.body` en lugar de la barra de desplazamiento, para que mantenga el ancho del contenido igual. -## Summary +## Resumen -Geometry: +Geometría: -- Width/height of the visible part of the document (content area width/height): `document.documentElement.clientWidth/clientHeight` -- Width/height of the whole document, with the scrolled out part: +- Ancho/alto de la parte visible del documento (área de contenido ancho/alto): `document.documentElement.clientWidth/Height` +- Ancho/alto de todo el documento, con la parte desplazada: ```js let scrollHeight = Math.max( @@ -166,11 +166,11 @@ Geometry: ); ``` -Scrolling: +Desplazamiento: -- Read the current scroll: `window.pageYOffset/pageXOffset`. -- Change the current scroll: +- Lee el desplazamiento actual: `window.pageYOffset/pageXOffset`. +- Cambia el desplazamiento actual: - - `window.scrollTo(pageX,pageY)` -- absolute coordinates, - - `window.scrollBy(x,y)` -- scroll relative the current place, - - `elem.scrollIntoView(top)` -- scroll to make `elem` visible (align with the top/bottom of the window). + - `window.scrollTo(pageX,pageY)` -- coordenadas absolutas + - `window.scrollBy(x,y)` -- desplazamiento relativo al lugar actual, + - `elem.scrollIntoView(top)` -- desplácese para hacer visible el `elem` (alineación con la parte superior/inferior de la ventana). diff --git a/2-ui/1-document/11-coordinates/1-find-point-coordinates/solution.md b/2-ui/1-document/11-coordinates/1-find-point-coordinates/solution.md index 4101d4915..456e390a1 100644 --- a/2-ui/1-document/11-coordinates/1-find-point-coordinates/solution.md +++ b/2-ui/1-document/11-coordinates/1-find-point-coordinates/solution.md @@ -1,8 +1,8 @@ -# Outer corners +# Esquinas externas -Outer corners are basically what we get from [elem.getBoundingClientRect()](https://developer.mozilla.org/en-US/docs/DOM/element.getBoundingClientRect). +Las esquinas externas son básicamente las que obtenemos de [elem.getBoundingClientRect()](https://developer.mozilla.org/es/docs/Web/API/Element/element.getBoundingClientRect). -Coordinates of the upper-left corner `answer1` and the bottom-right corner `answer2`: +Las coordenadas de la esquina superior izquierda `answer1` y la esquina inferior derecha `answer2`: ```js let coords = elem.getBoundingClientRect(); @@ -11,19 +11,19 @@ let answer1 = [coords.left, coords.top]; let answer2 = [coords.right, coords.bottom]; ``` -# Left-upper inner corner +# Esquina interna y superior izquierda -That differs from the outer corner by the border width. A reliable way to get the distance is `clientLeft/clientTop`: +Esta es diferente a la esquina externa por el ancho del borde. Una manera confiable de obtener la distancia es usando `clientLeft/clientTop`: ```js let answer3 = [coords.left + field.clientLeft, coords.top + field.clientTop]; ``` -# Right-bottom inner corner +# Esquina interna e inferior derecha -In our case we need to substract the border size from the outer coordinates. +En nuestro caso necesitamos sustraer la medida del borde de las coordenadas externas. -We could use CSS way: +Podemos usar la forma de CSS: ```js let answer4 = [ @@ -32,7 +32,7 @@ let answer4 = [ ]; ``` -An alternative way would be to add `clientWidth/clientHeight` to coordinates of the left-upper corner. That's probably even better: +Una forma alternativa puede ser agregando `clientWidth/clientHeight` a las coordenadas de la esquina superior izquierda. Probablemente sea incluso mejor: ```js let answer4 = [ diff --git a/2-ui/1-document/11-coordinates/1-find-point-coordinates/solution.view/index.html b/2-ui/1-document/11-coordinates/1-find-point-coordinates/solution.view/index.html index 229c87186..cafb3788d 100755 --- a/2-ui/1-document/11-coordinates/1-find-point-coordinates/solution.view/index.html +++ b/2-ui/1-document/11-coordinates/1-find-point-coordinates/solution.view/index.html @@ -5,7 +5,7 @@ <meta charset="utf-8"> <link rel="stylesheet" href="index.css"> <script> - document.onclick = function(e) { // shows click coordinates + document.onclick = function(e) { // muestra las coordenadas del clic coords.innerHTML = e.clientX + ':' + e.clientY; }; </script> @@ -13,10 +13,10 @@ <body> - Click anywhere to get window coordinates. - <br> That's for testing, to check the result you get by JavaScript. + Haz click en cualquier lugar para obtener las coordenadas de la ventana. + <br> Esto es útil para testear y confirmar el resultado que obtuviste con JavaScript. <br> - <div id="coords">(click coordinates show up here)</div> + <div id="coords">(Las coordenadas del click se mostrarán aquí)</div> <div id="field"> diff --git a/2-ui/1-document/11-coordinates/1-find-point-coordinates/source.view/index.html b/2-ui/1-document/11-coordinates/1-find-point-coordinates/source.view/index.html index dd168f783..a08a6a42e 100755 --- a/2-ui/1-document/11-coordinates/1-find-point-coordinates/source.view/index.html +++ b/2-ui/1-document/11-coordinates/1-find-point-coordinates/source.view/index.html @@ -5,7 +5,7 @@ <meta charset="utf-8"> <link rel="stylesheet" href="index.css"> <script> - document.onclick = function(e) { // shows click coordinates + document.onclick = function(e) { // muestra las coordenadas del clic coords.innerHTML = e.clientX + ':' + e.clientY; }; </script> @@ -13,10 +13,10 @@ <body> - Click anywhere to get window coordinates. - <br> That's for testing, to check the result you get by JavaScript. + Haz click en cualquier lugar para obtener las coordenadas de la ventana. + <br> Esto es útil para testear y confirmar el resultado que obtuviste con JavaScript. <br> - <div id="coords">(click coordinates show up here)</div> + <div id="coords">(Las coordenadas del click se mostrarán aquí)</div> <div id="field"> @@ -32,7 +32,7 @@ <script> - // ...your code... + // ...tu código... </script> </body> diff --git a/2-ui/1-document/11-coordinates/1-find-point-coordinates/task.md b/2-ui/1-document/11-coordinates/1-find-point-coordinates/task.md index 6bbb9fe13..2f39fbd34 100644 --- a/2-ui/1-document/11-coordinates/1-find-point-coordinates/task.md +++ b/2-ui/1-document/11-coordinates/1-find-point-coordinates/task.md @@ -2,23 +2,23 @@ importance: 5 --- -# Find window coordinates of the field +# Encuentra las coordenadas del campo en la ventana -In the iframe below you can see a document with the green "field". +En el siguiente iframe puedes ver un documento con el "campo" verde. -Use JavaScript to find window coordinates of corners pointed by with arrows. +Usa JavaScript para encontrar las coordenadas de las esquinas de la ventana señaladas con las flechas. -There's a small feature implemented in the document for convenience. A click at any place shows coordinates there. +Hay una pequeña característica implementada en el documento para conveniencia. Un click en cualquier lugar mostrará las coordenadas ahí. [iframe border=1 height=360 src="source" link edit] -Your code should use DOM to get window coordinates of: +Tu código debe usar el DOM para obtener las coordenadas en la ventana de: -1. Upper-left, outer corner (that's simple). -2. Bottom-right, outer corner (simple too). -3. Upper-left, inner corner (a bit harder). -4. Bottom-right, inner corner (there are several ways, choose one). +1. La esquina superior izquierda externa (eso es simple). +2. La esquina inferior derecha externa (simple también). +3. La esquina superior izquierda interna (un poco más difícil). +4. La esquina inferior derecha interna (existen muchas maneras, elige una). -The coordinates that you calculate should be the same as those returned by the mouse click. +Las coordenadas que tú calcules deben ser iguales a las devueltas por el click del mouse. -P.S. The code should also work if the element has another size or border, not bound to any fixed values. +P.D. El código también debe funcionar si el elemento tiene otro tamaño o borde, no está ligado a ningún valor fijo. diff --git a/2-ui/1-document/11-coordinates/2-position-at/solution.md b/2-ui/1-document/11-coordinates/2-position-at/solution.md index 353eb65dd..8523ec286 100644 --- a/2-ui/1-document/11-coordinates/2-position-at/solution.md +++ b/2-ui/1-document/11-coordinates/2-position-at/solution.md @@ -1,4 +1,4 @@ -In this task we only need to accurately calculate the coordinates. See the code for details. +En esta tarea sólo necesitamos calcular exactamente las coordenadas. Mira el código para más detalles. -Please note: the elements must be in the document to read `offsetHeight` and other properties. -A hidden (`display:none`) or out of the document element has no size. +Ten en cuenta: los elementos deben estar en el documento para leer `offsetHeight` y otras propiedades. +Un elemento oculto (`display:none`) o fuera del documento no tiene medidas. diff --git a/2-ui/1-document/11-coordinates/2-position-at/solution.view/index.html b/2-ui/1-document/11-coordinates/2-position-at/solution.view/index.html index f931fbeac..e08d9c07b 100755 --- a/2-ui/1-document/11-coordinates/2-position-at/solution.view/index.html +++ b/2-ui/1-document/11-coordinates/2-position-at/solution.view/index.html @@ -12,25 +12,25 @@ esse sequi officia sapiente.</p> <blockquote> - Teacher: Why are you late? - Student: There was a man who lost a hundred dollar bill. - Teacher: That's nice. Were you helping him look for it? - Student: No. I was standing on it. + Maestra: Por qué llegas tarde? + Alumno: Alguien perdió un billete de cien dólares. + Maestra: Que bueno. Lo estábas ayudando a buscarlo? + Alumno: No. Estaba parado encima del billete. </blockquote> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit sint atque dolorum fuga ad incidunt voluptatum error fugiat animi amet! Odio temporibus nulla id unde quaerat dignissimos enim nisi rem provident molestias sit tempore omnis recusandae - esse sequi officia sapiente.</p> + esse sequi officia sapiente.</p> <script> /** - * Positions elem relative to anchor as said in position. + * Posiciona elem en relación a anchor como se indica en position. * - * @param {Node} anchor Anchor element for positioning - * @param {string} position One of: top/right/bottom - * @param {Node} elem Element to position + * @param {Node} anchor Elemento ancla para posicionamiento + * @param {string} position Cualquiera de los siguientes: top/right/bottom + * @param {Node} elem Elemento para position * - * Both elements: elem and anchor must be in the document + * Ambos elementos: elem y anchor deben estar en el documento */ function positionAt(anchor, position, elem) { @@ -56,8 +56,8 @@ } /** - * Shows a note with the given html at the given position - * relative to the anchor element. + * Muestra una nota con el html proporcionado en el lugar indicado por position + * relativa al elemento anchor. */ function showNote(anchor, position, html) { @@ -69,12 +69,12 @@ positionAt(anchor, position, note); } - // test it + // ¡Testéalo! let blockquote = document.querySelector('blockquote'); - showNote(blockquote, "top", "note above"); - showNote(blockquote, "right", "note at the right"); - showNote(blockquote, "bottom", "note below"); + showNote(blockquote, "top", "nota encima"); + showNote(blockquote, "right", "nota a la derecha"); + showNote(blockquote, "bottom", "nota debajo"); </script> diff --git a/2-ui/1-document/11-coordinates/2-position-at/source.view/index.html b/2-ui/1-document/11-coordinates/2-position-at/source.view/index.html index 675573450..c71c98692 100755 --- a/2-ui/1-document/11-coordinates/2-position-at/source.view/index.html +++ b/2-ui/1-document/11-coordinates/2-position-at/source.view/index.html @@ -12,10 +12,10 @@ esse sequi officia sapiente.</p> <blockquote> - Teacher: Why are you late? - Student: There was a man who lost a hundred dollar bill. - Teacher: That's nice. Were you helping him look for it? - Student: No. I was standing on it. + Maestra: Por qué llegas tarde? + Alumno: Alguien perdió un billete de cien dólares. + Maestra: Que bueno. Lo estábas ayudando a buscarlo? + Alumno: No. Estaba parado encima del billete. </blockquote> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit sint atque dolorum fuga ad incidunt voluptatum error fugiat animi amet! Odio temporibus nulla id unde quaerat dignissimos enim nisi rem provident molestias sit tempore omnis recusandae @@ -24,21 +24,21 @@ <script> /** - * Positions elem relative to anchor as said in position. + * Posiciona elem en relación a anchor como se indica en position. * - * @param {Node} anchor Anchor element for positioning - * @param {string} position One of: top/right/bottom - * @param {Node} elem Element to position + * @param {Node} anchor Elemento anchor para posicionamiento + * @param {string} position Cualquiera de los siguientes: top/right/bottom + * @param {Node} elem Elemento para position * - * Both elements: elem and anchor must be in the document + * Ambos elementos: elem y anchor deben estar en el documento */ function positionAt(anchor, position, elem) { - // ... your code ... + // ... tu código ... } /** - * Shows a note with the given html at the given position - * relative to the anchor element. + * Muestra una nota con el html proporcionado en el lugar indicado por position + * relativa al elemento anchor. */ function showNote(anchor, position, html) { @@ -50,12 +50,12 @@ positionAt(anchor, position, note); } - // test it + // ¡Testealo! let blockquote = document.querySelector('blockquote'); - showNote(blockquote, "top", "note above"); - showNote(blockquote, "right", "note at the right"); - showNote(blockquote, "bottom", "note below"); + showNote(blockquote, "top", "nota encima"); + showNote(blockquote, "right", "nota a la derecha"); + showNote(blockquote, "bottom", "nota debajo"); </script> diff --git a/2-ui/1-document/11-coordinates/2-position-at/task.md b/2-ui/1-document/11-coordinates/2-position-at/task.md index 3aaa47f03..2a30ffce7 100644 --- a/2-ui/1-document/11-coordinates/2-position-at/task.md +++ b/2-ui/1-document/11-coordinates/2-position-at/task.md @@ -2,17 +2,17 @@ importance: 5 --- -# Show a note near the element +# Muestra una nota cercana al elemento -Create a function `positionAt(anchor, position, elem)` that positions `elem`, depending on `position` near `anchor` element. +Crea una función `positionAt(anchor, position, elem)` que posicione `elem`, dependiendo de la proximidad de `position` al elemento `anchor`. -The `position` must be a string with any one of 3 values: -- `"top"` - position `elem` right above `anchor` -- `"right"` - position `elem` immediately at the right of `anchor` -- `"bottom"` - position `elem` right below `anchor` +`position` debe ser un string con alguno de estos 3 valores: +- `"top"` - posiciona `elem` encima de `anchor` +- `"right"` - posiciona `elem` inmediatamente a la derecha de `anchor` +- `"bottom"` - posiciona `elem` debajo de `anchor` -It's used inside function `showNote(anchor, position, html)`, provided in the task source code, that creates a "note" element with given `html` and shows it at the given `position` near the `anchor`. +Esto será usado dentro de la función `showNote(anchor, position, html)`, proveída en el código fuente de la tarea, que crea un elemento "note" con el `html` y lo muestra en el lugar proporcionado por `position` cercano a `anchor`. -Here's the demo of notes: +Aquí está el demo de las notas: [iframe src="solution" height="350" border="1" link] diff --git a/2-ui/1-document/11-coordinates/3-position-at-absolute/solution.md b/2-ui/1-document/11-coordinates/3-position-at-absolute/solution.md index 014e505ed..f8574473f 100644 --- a/2-ui/1-document/11-coordinates/3-position-at-absolute/solution.md +++ b/2-ui/1-document/11-coordinates/3-position-at-absolute/solution.md @@ -1,4 +1,4 @@ -The solution is actually pretty simple: +La solución realmente es muy simple: -- Use `position:absolute` in CSS instead of `position:fixed` for `.note`. -- Use the function [getCoords()](info:coordinates#getCoords) from the chapter <info:coordinates> to get document-relative coordinates. +- Usa `position:absolute` con CSS en lugar de `position:fixed` para `.note`. +- Usa la función [getCoords()](info:coordinates#getCoords) del capítulo <info:coordinates> para obtener las coordenadas relativas al documento. diff --git a/2-ui/1-document/11-coordinates/3-position-at-absolute/solution.view/index.html b/2-ui/1-document/11-coordinates/3-position-at-absolute/solution.view/index.html index 56c95d5ec..254f4473d 100644 --- a/2-ui/1-document/11-coordinates/3-position-at-absolute/solution.view/index.html +++ b/2-ui/1-document/11-coordinates/3-position-at-absolute/solution.view/index.html @@ -8,14 +8,14 @@ <body style="height: 2000px"> - <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit sint atque dolorum fuga ad incidunt voluptatum error fugiat animi amet! Odio temporibus nulla id unde quaerat dignissimos enim nisi rem provident molestias sit tempore omnis recusandae + <p>El malvado Lord Vader, obsesionado por encontrar al joven Skywalker esse sequi officia sapiente.</p> <blockquote> - Teacher: Why are you late? - Student: There was a man who lost a hundred dollar bill. - Teacher: That's nice. Were you helping him look for it? - Student: No. I was standing on it. + Maestra: ¿Por qué llegas tarde? + Alumno: Alguien perdió un billete de cien dólares. + Maestra: Qué bueno, ¿lo estabas ayudando a buscarlo? + Alumno: No. Estaba parado encima del billete. </blockquote> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit sint atque dolorum fuga ad incidunt voluptatum error fugiat animi amet! Odio temporibus nulla id unde quaerat dignissimos enim nisi rem provident molestias sit tempore omnis recusandae @@ -66,12 +66,12 @@ positionAt(anchor, position, note); } - // test it + // ¡Testéalo! let blockquote = document.querySelector('blockquote'); - showNote(blockquote, "top", "note above"); - showNote(blockquote, "right", "note at the right"); - showNote(blockquote, "bottom", "note below"); + showNote(blockquote, "top", "nota encima"); + showNote(blockquote, "right", "nota a la derecha"); + showNote(blockquote, "bottom", "nota debajo"); </script> diff --git a/2-ui/1-document/11-coordinates/3-position-at-absolute/task.md b/2-ui/1-document/11-coordinates/3-position-at-absolute/task.md index 0554da862..ebac7f7b2 100644 --- a/2-ui/1-document/11-coordinates/3-position-at-absolute/task.md +++ b/2-ui/1-document/11-coordinates/3-position-at-absolute/task.md @@ -2,10 +2,10 @@ importance: 5 --- -# Show a note near the element (absolute) +# Muestra una nota cercana al elemento (absolute) -Modify the solution of the [previous task](info:task/position-at) so that the note uses `position:absolute` instead of `position:fixed`. +Modifica la solución de la [tarea previa](info:task/position-at) de manera que la nota use `position:absolute` en lugar de `position:fixed`. -That will prevent its "runaway" from the element when the page scrolls. +Esto evitará que se "aleje" del elemento cuando se desplace la página. -Take the solution of that task as a starting point. To test the scroll, add the style `<body style="height: 2000px">`. +Toma la solución de la tarea anterior como punto de partida. Para testear el scroll, agrega el estilo `<body style="height: 2000px">`. diff --git a/2-ui/1-document/11-coordinates/4-position-inside-absolute/solution.view/index.html b/2-ui/1-document/11-coordinates/4-position-inside-absolute/solution.view/index.html index b89db3790..37c5c63b2 100644 --- a/2-ui/1-document/11-coordinates/4-position-inside-absolute/solution.view/index.html +++ b/2-ui/1-document/11-coordinates/4-position-inside-absolute/solution.view/index.html @@ -12,15 +12,16 @@ esse sequi officia sapiente.</p> <blockquote> - Teacher: Why are you late? - Student: There was a man who lost a hundred dollar bill. - Teacher: That's nice. Were you helping him look for it? - Student: No. I was standing on it. + Maestra: Por qué llegas tarde? + Alumno: Alguien perdió un billete de cien dólares. + Maestra: Que bueno. Lo estábas ayudando a buscarlo? + Alumno: No. Estaba parado encima del billete. </blockquote> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit sint atque dolorum fuga ad incidunt voluptatum error fugiat animi amet! Odio temporibus nulla id unde quaerat dignissimos enim nisi rem provident molestias sit tempore omnis recusandae esse sequi officia sapiente.</p> + <script> function getCoords(elem) { let box = elem.getBoundingClientRect(); @@ -83,10 +84,10 @@ let blockquote = document.querySelector('blockquote'); - showNote(blockquote, "top-in", "note top-in"); - showNote(blockquote, "top-out", "note top-out"); - showNote(blockquote, "right-out", "note right-out"); - showNote(blockquote, "bottom-in", "note bottom-in"); + showNote(blockquote, "top-in", "nota superior interna"); + showNote(blockquote, "top-out", "nota superior externa"); + showNote(blockquote, "right-out", "nota a la derecha externa"); + showNote(blockquote, "bottom-in", "nota inferior interna"); </script> diff --git a/2-ui/1-document/11-coordinates/4-position-inside-absolute/task.md b/2-ui/1-document/11-coordinates/4-position-inside-absolute/task.md index de45b5498..373d0f48f 100644 --- a/2-ui/1-document/11-coordinates/4-position-inside-absolute/task.md +++ b/2-ui/1-document/11-coordinates/4-position-inside-absolute/task.md @@ -2,27 +2,27 @@ importance: 5 --- -# Position the note inside (absolute) +# Posiciona la nota adentro (absolute) -Extend the previous task <info:task/position-at-absolute>: teach the function `positionAt(anchor, position, elem)` to insert `elem` inside the `anchor`. +Ampliando a la tarea anterior <info:task/position-at-absolute>: enséñale a la función `positionAt(anchor, position, elem)` a insertar `elem` dentro de `anchor`. -New values for `position`: +Los nuevos valores para posición son `position`: -- `top-out`, `right-out`, `bottom-out` -- work the same as before, they insert the `elem` over/right/under `anchor`. -- `top-in`, `right-in`, `bottom-in` -- insert `elem` inside the `anchor`: stick it to the upper/right/bottom edge. +- `top-out`, `right-out`, `bottom-out` -- funciona ingual que antes, inserta el `elem` encima, a la derecha o debajo de `anchor`. +- `top-in`, `right-in`, `bottom-in` -- inserta el `elem` dentro del `anchor`: lo fija en la parte superior, derecha o inferior del borde. -For instance: +Por ejemplo: ```js -// shows the note above blockquote +// Muestra la nota encima de la cita textual positionAt(blockquote, "top-out", note); -// shows the note inside blockquote, at the top +// Muestra la nota dentro de la cita textual en la parte superior positionAt(blockquote, "top-in", note); ``` -The result: +El resultado: [iframe src="solution" height="310" border="1" link] -As the source code, take the solution of the task <info:task/position-at-absolute>. +Para el código fuente toma la solución de la tarea <info:task/position-at-absolute>. diff --git a/2-ui/1-document/11-coordinates/article.md b/2-ui/1-document/11-coordinates/article.md index fc605c414..952647271 100644 --- a/2-ui/1-document/11-coordinates/article.md +++ b/2-ui/1-document/11-coordinates/article.md @@ -1,42 +1,42 @@ -# Coordinates +# Coordenadas -To move elements around we should be familiar with coordinates. +Para mover elementos debemos estar familiarizados con las coordenadas. -Most JavaScript methods deal with one of two coordinate systems: +La mayoría de los métodos de JavaScript tratan con uno de dos sistemas de coordenadas: -1. **Relative to the window** - similar to `position:fixed`, calculated from the window top/left edge. - - we'll denote these coordinates as `clientX/clientY`, the reasoning for such name will become clear later, when we study event properties. -2. **Relative to the document** - similar to `position:absolute` in the document root, calculated from the document top/left edge. - - we'll denote them `pageX/pageY`. +1. **Relativo a la ventana**: similar a `position:fixed`, calculado desde el borde superior/izquierdo de la ventana. + - Designaremos estas coordenadas como `clientX/clientY`, el razonamiento para tal nombre se aclarará más adelante, cuando estudiemos las propiedades de los eventos. +2. **Relative al documento** - similar a `position:absolute` en la raíz del documento, calculado a partir del borde superior/izquierdo del documento. + - Las designaremos como `pageX/pageY`. -When the page is scrolled to the very beginning, so that the top/left corner of the window is exactly the document top/left corner, these coordinates equal each other. But after the document shifts, window-relative coordinates of elements change, as elements move across the window, while document-relative coordinates remain the same. +Cuando la página se desplaza hasta el comienzo, de modo que la esquina superior/izquierda de la ventana es exactamente la esquina superior/izquierda del documento, estas coordenadas son iguales entre sí. Pero después de que el documento cambia, las coordenadas relativas a la ventana de los elementos cambian, a medida que los elementos se mueven a través de la ventana, mientras que las coordenadas relativas al documento permanecen iguales. -On this picture we take a point in the document and demonstrate its coordinates before the scroll (left) and after it (right): +En esta imagen tomamos un punto en el documento y demostramos sus coordenadas antes del desplazamiento (primera imagen) y después (segunda imagen): ![](document-and-window-coordinates-scrolled.svg) -When the document scrolled: -- `pageY` - document-relative coordinate stayed the same, it's counted from the document top (now scrolled out). -- `clientY` - window-relative coordinate did change (the arrow became shorter), as the same point became closer to window top. +Cuando el documento se desplazó: +- La coordenada `pageY` relativa al documento se mantuvo igual, se cuenta desde la parte superior del documento (ahora desplazada). +- La coordenada `clientY` relativa a la ventana cambió (la flecha se acortó), ya que el mismo punto se acercó a la parte superior de la ventana. -## Element coordinates: getBoundingClientRect +## Coordenadas de elemento: getBoundingClientRect -The method `elem.getBoundingClientRect()` returns window coordinates for a minimal rectangle that encloses `elem` as an object of built-in [DOMRect](https://www.w3.org/TR/geometry-1/#domrect) class. +El método `elem.getBoundingClientRect()` devuelve las coordenadas de la ventana para un rectángulo mínimo que encasilla a `elem` como un objeto de la clase interna [DOMRect](https://www.w3.org/TR/geometry-1/#domrect). -Main `DOMRect` properties: +Propiedades principales de `DOMRect`: -- `x/y` -- X/Y-coordinates of the rectangle origin relative to window, -- `width/height` -- width/height of the rectangle (can be negative). +- `x/y`: coordenadas X/Y del origen del rectángulo con relación a la ventana. +- `width/height`: ancho/alto del rectángulo (pueden ser negativos). -Additionally, there are derived properties: +Adicionalmente existen estas propiedades derivadas: -- `top/bottom` -- Y-coordinate for the top/bottom rectangle edge, -- `left/right` -- X-coordinate for the left/right rectangle edge. +- `top/bottom`: coordenada Y para el borde superior/inferior del rectángulo. +- `left/right`: coordenada X para el borde izquierdo/derecho del rectángulo. ```online -For instance click this button to see its window coordinates: +Por ejemplo, haz click en este botón para ver las coordenadas en relación a la ventana: -<p><input id="brTest" type="button" style="max-width: 90vw;" value="Get coordinates using button.getBoundingClientRect() for this button" onclick='showRect(this)'/></p> +<p><input id="brTest" type="button" style="max-width: 90vw;" value="Recibe las coordenadas para este botón con button.getBoundingClientRect()" onclick='showRect(this)'/></p> <script> function showRect(elem) { @@ -53,66 +53,66 @@ right:${r.right} } </script> -If you scroll the page and repeat, you'll notice that as window-relative button position changes, its window coordinates (`y/top/bottom` if you scroll vertically) change as well. +Si desplazas la página y repites te darás cuenta que así como cambia la posición del botón relativa a la ventada también cambian sus coordenadas en la ventana (`y/top/bottom` si es que haces scroll vertical). ``` -Here's the picture of `elem.getBoundingClientRect()` output: +Aquí hay la imagen con el output de `elem.getBoundingClientRect()`: ![](coordinates.svg) -As you can see, `x/y` and `width/height` fully describe the rectangle. Derived properties can be easily calculated from them: +Como puedes ver `x/y` y `width/height` describen completamente el rectángulo. Las propiedades derivadas pueden ser calculadas a partir de ellas: - `left = x` - `top = y` - `right = x + width` - `bottom = y + height` -Please note: +Toma en cuenta: -- Coordinates may be decimal fractions, such as `10.5`. That's normal, internally browser uses fractions in calculations. We don't have to round them when setting to `style.left/top`. -- Coordinates may be negative. For instance, if the page is scrolled so that `elem` is now above the window, then `elem.getBoundingClientRect().top` is negative. +- Las coordenadas pueden ser fracciones decimales, tales como `10.5`. Esto es normal ya que internamente el navegador usa fracciones en los cálculos. No tenemos que redondearlos para poder asignarlos a `style.left/top`. +- Las coordenadas pueden ser negativas. Por ejemplo, si la página se desplaza hasta que `elem` rebase el borde superior de la ventana, entonces `elem.getBoundingClientRect().top` será negativo. -```smart header="Why derived properties are needed? Why does `top/left` exist if there's `x/y`?" -Mathematically, a rectangle is uniquely defined with its starting point `(x,y)` and the direction vector `(width,height)`. So the additional derived properties are for convenience. +```smart header="¿Por qué se necesitan propiedades derivadas? ¿Por qué `top/left` si existe `x/y`?" +Matemáticamente un rectángulo se define de únicamente con su punto de partida `(x,y)` y el vector de dirección `(width,height)`. Por lo tanto, las propiedades derivadas adicionales son por conveniencia. -Technically it's possible for `width/height` to be negative, that allows for "directed" rectangle, e.g. to represent mouse selection with properly marked start and end. +Técnicamente es posible que `width/height` sean negativos, lo que permite un rectángulo "dirigido". Por ejemplo, para representar la selección del mouse con su inicio y final debidamente marcados. -Negative `width/height` values mean that the rectangle starts at its bottom-right corner and then "grows" left-upwards. +Los valores negativos para `width/height` indican que el rectángulo comienza en su esquina inferior derecha y luego se extiende hacia la izquierda y arriba. -Here's a rectangle with negative `width` and `height` (e.g. `width=-200`, `height=-100`): +Aquí hay un rectángulo con valores `width` y `height` negativos(ejemplo: `width=-200`, `height=-100`): ![](coordinates-negative.svg) -As you can see, `left/top` do not equal `x/y` in such case. +Como puedes ver: `left/top` no es igual a `x/y` en tal caso. -In practice though, `elem.getBoundingClientRect()` always returns positive width/height, here we mention negative `width/height` only for you to understand why these seemingly duplicate properties are not actually duplicates. +Pero en la práctica `elem.getBoundingClientRect()` siempre devuelve el ancho y alto positivos. Aquí hemos mencionado los valores negativos para `width/height` solo para que comprendas por qué estas propiedades aparentemente duplicadas en realidad no lo son. ``` -```warn header="Internet Explorer: no support for `x/y`" -Internet Explorer doesn't support `x/y` properties for historical reasons. +```warn header="En Internet Explorer no hay soporte para `x/y`" +Internet Explorer no tiene soporte para las propiedades `x/y` por razones históricas. -So we can either make a polyfill (add getters in `DomRect.prototype`) or just use `top/left`, as they are always the same as `x/y` for positive `width/height`, in particular in the result of `elem.getBoundingClientRect()`. +De manera que podemos crear un polyfill y (obtenerlo con `DomRect.prototype`) o solo usar `top/left`, ya que son siempre las mismas que `x/y` para `width/height` positivos, en particular en el resultado de `elem.getBoundingClientRect()`. ``` -```warn header="Coordinates right/bottom are different from CSS position properties" -There are obvious similarities between window-relative coordinates and CSS `position:fixed`. +```warn header="Las coordenadas right/bottom son diferentes a las propiedades de posición en CSS" +Existen muchas similitudes obvias entre las coordenadas relativas a la ventana y `position:fixed` en CSS. -But in CSS positioning, `right` property means the distance from the right edge, and `bottom` property means the distance from the bottom edge. +Pero en el posicionamiento con CSS, la propiedad `right` define la distancia entre el borde derecho y el elemento y la propiedad `bottom` supone la distancia entre el borde inferior y el elemento. -If we just look at the picture above, we can see that in JavaScript it is not so. All window coordinates are counted from the top-left corner, including these ones. +Si echamos un vistazo a la imagen anterior veremos que en JavaScript esto no es así. Todas las coordenadas de la ventana se cuentan a partir de la esquina superior izquierda, incluyendo estas. ``` ## elementFromPoint(x, y) [#elementFromPoint] -The call to `document.elementFromPoint(x, y)` returns the most nested element at window coordinates `(x, y)`. +La llamada a `document.elementFromPoint(x, y)` devuelve el elemento más anidado dentro de las coordenadas de la ventana `(x, y)`. -The syntax is: +La sintaxis es: ```js let elem = document.elementFromPoint(x, y); ``` -For instance, the code below highlights and outputs the tag of the element that is now in the middle of the window: +Por ejemplo, el siguiente código resalta y muestra la etiqueta del elemento que ahora se encuentra en medio de la ventana: ```js run let centerX = document.documentElement.clientWidth / 2; @@ -124,43 +124,43 @@ elem.style.background = "red"; alert(elem.tagName); ``` -As it uses window coordinates, the element may be different depending on the current scroll position. +Debido a que utiliza las coordenadas de la ventana, el elemento puede ser diferente dependiendo de la posición actual del scroll. -````warn header="For out-of-window coordinates the `elementFromPoint` returns `null`" -The method `document.elementFromPoint(x,y)` only works if `(x,y)` are inside the visible area. +````warn header="Para coordenadas fuera de la ventana, el `elementFromPoint` devuelve `null`" +El método `document.elementFromPoint(x,y)` solo funciona si `(x,y)` se encuentra dentro del área visible. -If any of the coordinates is negative or exceeds the window width/height, then it returns `null`. +Si alguna de las coordenadas es negativa o excede el ancho o alto de la ventana entonces devolverá `null`. -Here's a typical error that may occur if we don't check for it: +Aquí hay un error típico que podría ocurrir si no nos aseguramos de ello: ```js let elem = document.elementFromPoint(x, y); -// if the coordinates happen to be out of the window, then elem = null +// si las coordenadas sobrepasan la ventana entonces elem = null *!* -elem.style.background = ''; // Error! +elem.style.background = ''; // ¡Error! */!* ``` ```` -## Using for "fixed" positioning +## Usándolas para posicionamiento "fijo" -Most of time we need coordinates in order to position something. +La mayoría del tiempo necesitamos coordenadas para posicionar algo. -To show something near an element, we can use `getBoundingClientRect` to get its coordinates, and then CSS `position` together with `left/top` (or `right/bottom`). +Para mostrar algo cercano a un elemento podemos usar `getBoundingClientRect` para obtener sus coordenadas y entonces CSS `position` junto con `left/top` (o `right/bottom`). -For instance, the function `createMessageUnder(elem, html)` below shows the message under `elem`: +Por ejemplo, la función `createMessageUnder(elem, html)` a continuación nos muestra un mensaje debajo de `elem`: ```js let elem = document.getElementById("coords-show-mark"); function createMessageUnder(elem, html) { - // create message element + // Crea un elemento de mensaje let message = document.createElement('div'); - // better to use a css class for the style here + // Lo mejor es usar una clase css para el estilo aquí message.style.cssText = "position:fixed; color: red"; *!* - // assign coordinates, don't forget "px"! + // Asignando las coordenadas, no olvides "px"! let coords = elem.getBoundingClientRect(); message.style.left = coords.left + "px"; @@ -172,45 +172,45 @@ function createMessageUnder(elem, html) { return message; } -// Usage: -// add it for 5 seconds in the document -let message = createMessageUnder(elem, 'Hello, world!'); +// Uso: +// agregarlo por 5 segundos en el documento +let message = createMessageUnder(elem, '¡Hola, mundo!'); document.body.append(message); setTimeout(() => message.remove(), 5000); ``` ```online -Click the button to run it: +Pulsa el botón para ejecutarlo: -<button id="coords-show-mark">Button with id="coords-show-mark", the message will appear under it</button> +<button id="coords-show-mark">Botón con el id="coords-show-mark", el mensaje aparecerá aquí debajo</button> ``` -The code can be modified to show the message at the left, right, below, apply CSS animations to "fade it in" and so on. That's easy, as we have all the coordinates and sizes of the element. +El código puede ser modificado para mostrar el mensaje a la izquierda, derecha, abajo, aplicando animaciones con CSS para "desvanecerlo" y así. Es fácil una vez que tenemos todas las coordenadas y medidas del elemento. -But note the important detail: when the page is scrolled, the message flows away from the button. +Pero nota un detalle importante: cuando la página se desplaza, el mensaje se aleja del botón. -The reason is obvious: the message element relies on `position:fixed`, so it remains at the same place of the window while the page scrolls away. +La razón es obvia: el elemento del mensaje se basa en `position:fixed`, esto lo reubica al mismo lugar en la ventana mientras se desplaza. -To change that, we need to use document-based coordinates and `position:absolute`. +Para cambiar esto necesitamos usar las coordenadas basadas en el documento y `position:absolute`. -## Document coordinates [#getCoords] +## Coordenadas del documento [#getCoords] -Document-relative coordinates start from the upper-left corner of the document, not the window. +Las coordenadas relativas al documento comienzan en la esquina superior izquierda del documento, no de la ventana. -In CSS, window coordinates correspond to `position:fixed`, while document coordinates are similar to `position:absolute` on top. +En CSS las coordenadas de la ventana corresponden a `position:fixed` mientras que las del documento son similares a `position:absolute` en la parte superior. -We can use `position:absolute` and `top/left` to put something at a certain place of the document, so that it remains there during a page scroll. But we need the right coordinates first. +Podemos usar `position:absolute` y `top/left` para colocar algo en un lugar determinado del documento, esto lo reubicará ahí mismo durante un desplazamiento de página. Pero primero necesitamos las coordenadas correctas. -There's no standard method to get the document coordinates of an element. But it's easy to write it. +No existe un estándar para obtener las coordenadas de un elemento en un documento. Pero es fácil de codificarlo. -The two coordinate systems are connected by the formula: -- `pageY` = `clientY` + height of the scrolled-out vertical part of the document. -- `pageX` = `clientX` + width of the scrolled-out horizontal part of the document. +Los dos sistemas de coordenadas están relacionados mediante la siguiente fórmula: +- `pageY` = `clientY` + el alto de la parte vertical desplazada del documento. +- `pageX` = `clientX` + el ancho de la parte horizontal desplazada del documento. -The function `getCoords(elem)` will take window coordinates from `elem.getBoundingClientRect()` and add the current scroll to them: +La función `getCoords(elem)` toma las coordenadas de la ventana de `elem.getBoundingClientRect()` y agrega el desplazamiento actual a ellas: ```js -// get document coordinates of the element +// obteniendo las coordenadas en el documento del elemento function getCoords(elem) { let box = elem.getBoundingClientRect(); @@ -223,9 +223,9 @@ function getCoords(elem) { } ``` -If in the example above we used it with `position:absolute`, then the message would stay near the element on scroll. +Si el ejemplo anterior se usara con `position:absolute` entonces el mensaje podría permanecer cerca del elemento durante el desplazamiento. -The modified `createMessageUnder` function: +La función modificada `createMessageUnder`: ```js function createMessageUnder(elem, html) { @@ -243,13 +243,13 @@ function createMessageUnder(elem, html) { } ``` -## Summary +## Resumen -Any point on the page has coordinates: +Cualquier punto en la página tiene coordenadas: -1. Relative to the window -- `elem.getBoundingClientRect()`. -2. Relative to the document -- `elem.getBoundingClientRect()` plus the current page scroll. +1. Relativas a la ventana: `elem.getBoundingClientRect()`. +2. Relativas al documento: `elem.getBoundingClientRect()` mas el desplazamiento actual de la página. -Window coordinates are great to use with `position:fixed`, and document coordinates do well with `position:absolute`. +Las coordenadas de la ventana son ideales para usarse con `position:fixed`, y las coordenadas del documento funcionan bien con `position:absolute`. -Both coordinate systems have their pros and cons; there are times we need one or the other one, just like CSS `position` `absolute` and `fixed`. +Ambos sistemas de coordenadas tienen pros y contras; habrá ocasiones en que ocuparemos una u otra, justamente como con los valores `absolute` y `fixed` para `position` en CSS. diff --git a/2-ui/1-document/11-coordinates/head.html b/2-ui/1-document/11-coordinates/head.html index c00340039..548ace164 100644 --- a/2-ui/1-document/11-coordinates/head.html +++ b/2-ui/1-document/11-coordinates/head.html @@ -3,7 +3,7 @@ let elem = document.getElementById('coords-show-mark'); - // no elem in ebook (pdf/epub) mode + // no hay elem en la versión ebook (pdf/epub) if (elem) { elem.onclick = function() { @@ -20,7 +20,7 @@ return message; } - let message = createMessageUnder(elem, 'Hello, world!'); + let message = createMessageUnder(elem, 'Hola, mundo!'); document.body.append(message); setTimeout(() => message.remove(), 5000); } diff --git a/2-ui/1-document/index.md b/2-ui/1-document/index.md index 1b60cdf2c..230c9d942 100644 --- a/2-ui/1-document/index.md +++ b/2-ui/1-document/index.md @@ -1,3 +1,3 @@ -# Document +# Documento -Here we'll learn to manipulate a web-page using JavaScript. +Aquí aprenderemos a manipular una página web usando JavaScript. diff --git a/2-ui/2-events/01-introduction-browser-events/01-hide-other/solution.view/index.html b/2-ui/2-events/01-introduction-browser-events/01-hide-other/solution.view/index.html index 7228a45de..403fedbfe 100644 --- a/2-ui/2-events/01-introduction-browser-events/01-hide-other/solution.view/index.html +++ b/2-ui/2-events/01-introduction-browser-events/01-hide-other/solution.view/index.html @@ -7,13 +7,13 @@ <body> - <input type="button" id="hider" value="Click to hide the text" /> + <input type="button" id="hider" value="Haz click para desaparecer el texto"/> - <div id="text">Text</div> + <div id="text">Texto</div> <script> - // Here it doesn't matter how we hide the text, - // could also use style.display: + // Aquí no importa cómo escondamos el texto + // podríamos usar style.display: document.getElementById('hider').onclick = function() { document.getElementById('text').hidden = true; } diff --git a/2-ui/2-events/01-introduction-browser-events/01-hide-other/source.view/index.html b/2-ui/2-events/01-introduction-browser-events/01-hide-other/source.view/index.html index 17c5be6d1..19dbe60f4 100644 --- a/2-ui/2-events/01-introduction-browser-events/01-hide-other/source.view/index.html +++ b/2-ui/2-events/01-introduction-browser-events/01-hide-other/source.view/index.html @@ -7,12 +7,12 @@ <body> - <input type="button" id="hider" value="Click to hide the text" /> + <input type="button" id="hider" value="Haz click para desaparecer el texto"/> - <div id="text">Text</div> + <div id="text">Texto</div> <script> - /* your code */ + /* Tu código */ </script> </body> diff --git a/2-ui/2-events/01-introduction-browser-events/01-hide-other/task.md b/2-ui/2-events/01-introduction-browser-events/01-hide-other/task.md index 7cb0cb0c9..f0209e6ca 100644 --- a/2-ui/2-events/01-introduction-browser-events/01-hide-other/task.md +++ b/2-ui/2-events/01-introduction-browser-events/01-hide-other/task.md @@ -2,10 +2,10 @@ importance: 5 --- -# Hide on click +# Ocultar con un click -Add JavaScript to the `button` to make `<div id="text">` disappear when we click it. +Agrega JavaScript al `button` para hacer que `<div id="text">` desaparezca al clickearlo. -The demo: +El demo: [iframe border=1 src="solution" height=80] diff --git a/2-ui/2-events/01-introduction-browser-events/02-hide-self-onclick/solution.md b/2-ui/2-events/01-introduction-browser-events/02-hide-self-onclick/solution.md index cded5b622..ff7109a82 100644 --- a/2-ui/2-events/01-introduction-browser-events/02-hide-self-onclick/solution.md +++ b/2-ui/2-events/01-introduction-browser-events/02-hide-self-onclick/solution.md @@ -1,5 +1,5 @@ -Can use `this` in the handler to reference "the element itself" here: +Podemos usar `this` en el handler para referenciar "al propio elemento" aquí: ```html run height=50 -<input type="button" onclick="this.hidden=true" value="Click to hide"> +<input type="button" onclick="this.hidden=true" value="Click para ocultar"> ``` diff --git a/2-ui/2-events/01-introduction-browser-events/02-hide-self-onclick/task.md b/2-ui/2-events/01-introduction-browser-events/02-hide-self-onclick/task.md index 9ee8f18e1..d727b75bc 100644 --- a/2-ui/2-events/01-introduction-browser-events/02-hide-self-onclick/task.md +++ b/2-ui/2-events/01-introduction-browser-events/02-hide-self-onclick/task.md @@ -2,11 +2,11 @@ importance: 5 --- -# Hide self +# Ocultarse -Create a button that hides itself on click. +Crea un botón que se oculte a sí mismo al darle un click. ```online -Like this: -<input type="button" onclick="this.hidden=true" value="Click to hide"> +Así: +<input type="button" onclick="this.hidden=true" value="Click para esconder"> ``` diff --git a/2-ui/2-events/01-introduction-browser-events/03-which-handlers-run/solution.md b/2-ui/2-events/01-introduction-browser-events/03-which-handlers-run/solution.md index d569f0e4d..5962a899a 100644 --- a/2-ui/2-events/01-introduction-browser-events/03-which-handlers-run/solution.md +++ b/2-ui/2-events/01-introduction-browser-events/03-which-handlers-run/solution.md @@ -1,8 +1,8 @@ -The answer: `1` and `2`. +La respuesta: `1` y `2`. -The first handler triggers, because it's not removed by `removeEventListener`. To remove the handler we need to pass exactly the function that was assigned. And in the code a new function is passed, that looks the same, but is still another function. +El primer handler se activa porque no es removido por `removeEventListener`. Para remover el handler necesitamos pasar exactamente la función que fue asignada. Y en el código se pasa una función que se ve igual, pero es otra función. -To remove a function object, we need to store a reference to it, like this: +Para remover un objeto de función necesitamos almacenar una referencia a él, así: ```js function handler() { @@ -13,4 +13,4 @@ button.addEventListener("click", handler); button.removeEventListener("click", handler); ``` -The handler `button.onclick` works independently and in addition to `addEventListener`. +El handler `button.onclick` funciona independientemente y en adición a `addEventListener`. diff --git a/2-ui/2-events/01-introduction-browser-events/03-which-handlers-run/task.md b/2-ui/2-events/01-introduction-browser-events/03-which-handlers-run/task.md index f8cd75d5a..e83f6b55e 100644 --- a/2-ui/2-events/01-introduction-browser-events/03-which-handlers-run/task.md +++ b/2-ui/2-events/01-introduction-browser-events/03-which-handlers-run/task.md @@ -2,11 +2,11 @@ importance: 5 --- -# Which handlers run? +# ¿Qué handlers se ejecutan? -There's a button in the variable. There are no handlers on it. +Hay un botón en la variable. No hay handlers en él. -Which handlers run on click after the following code? Which alerts show up? +¿Qué handlers se ejecutan con el click después del siguiente código? ¿Qué alertas se muestran? ```js no-beautify button.addEventListener("click", () => alert("1")); diff --git a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/solution.md b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/solution.md index b04cb8231..b2f205839 100644 --- a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/solution.md +++ b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/solution.md @@ -1,11 +1,11 @@ -First we need to choose a method of positioning the ball. +Primero necesitamos elegir un método para posicionar el balón. -We can't use `position:fixed` for it, because scrolling the page would move the ball from the field. +No podemos usar `position:fixed` para ello, porque al desplazar la página se movería el balón del campo. -So we should use `position:absolute` and, to make the positioning really solid, make `field` itself positioned. +Así que deberíamos usar `position:absolute` y, para que el posicionamiento sea realmente sólido, hacer que `field` sea posicione a sí mismo. -Then the ball will be positioned relatively to the field: +Entonces el balón se posicionará en relación al campo: ```css #field { @@ -16,36 +16,36 @@ Then the ball will be positioned relatively to the field: #ball { position: absolute; - left: 0; /* relative to the closest positioned ancestor (field) */ + left: 0; /* relativo al predecesor más cercano (field) */ top: 0; - transition: 1s all; /* CSS animation for left/top makes the ball fly */ + transition: 1s all; /* Animación CSS para que left/top hagan al balón volar */ } ``` -Next we need to assign the correct `ball.style.left/top`. They contain field-relative coordinates now. +Lo siguiente es asignar el `ball.style.left/top` correcto. Ahora contienen coordenadas relativas al campo. -Here's the picture: +Aquí está la imagen: ![](move-ball-coords.svg) -We have `event.clientX/clientY` -- window-relative coordinates of the click. +Tenemos `event.clientX/clientY`, las cuales son las coordenadas del click relativas a la ventana. -To get field-relative `left` coordinate of the click, we can substract the field left edge and the border width: +Para obtener la coordenada `left` del click relativa al campo necesitamos restar el limite izquierdo del campo y el ancho del borde: ```js let left = event.clientX - fieldCoords.left - field.clientLeft; ``` -Normally, `ball.style.left` means the "left edge of the element" (the ball). So if we assign that `left`, then the ball edge, not center, would be under the mouse cursor. +Normalmente `ball.style.left` significa el "borde izquierdo del elemento" (el balón). Por lo que si asignamos ese `left`, entonces el borde del balón, no el centro, es el que se encontraría debajo del cursor del mouse. -We need to move the ball half-width left and half-height up to make it center. +Necesitamos mover la mitad del ancho del balón a la izquierda y la mitad del alto hacia arriba para que quede en el centro. -So the final `left` would be: +Por lo que el `left` final debería ser: ```js let left = event.clientX - fieldCoords.left - field.clientLeft - ball.offsetWidth/2; ``` -The vertical coordinate is calculated using the same logic. +La coordenada vertical es calculada usando la misma lógica. -Please note that the ball width/height must be known at the time we access `ball.offsetWidth`. Should be specified in HTML or CSS. +Por favor, nota que el ancho/alto del balón se debe conocer al momento que accedemos a `ball.offsetWidth`. Se debe especificar en HTML o CSS. diff --git a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/solution.view/index.html b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/solution.view/index.html index 3ebe8739e..5b82bff34 100644 --- a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/solution.view/index.html +++ b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/solution.view/index.html @@ -27,7 +27,7 @@ <body style="height:2000px"> - Click on a field to move the ball there. + Haz click en un lugar del campo para mover el balón allí. <br> @@ -39,29 +39,29 @@ <script> field.onclick = function(event) { - // window-relative field coordinates + // Coordenadas del campo relativas a la ventana let fieldCoords = this.getBoundingClientRect(); - // the ball has position:absolute, the field: position:relative - // so ball coordinates are relative to the field inner left-upper corner + // El balón tiene position:absolute, el campo: position:relative + // por lo que las coordenadas de la bola son relativas a la esquina superior izquierda interna del campo let ballCoords = { top: event.clientY - fieldCoords.top - field.clientTop - ball.clientHeight / 2, left: event.clientX - fieldCoords.left - field.clientLeft - ball.clientWidth / 2 }; - // prevent crossing the top field boundary + // previniendo el cruce del limite superior del campo if (ballCoords.top < 0) ballCoords.top = 0; - // prevent crossing the left field boundary + // previniendo el cruce del limite izquierdo del campo if (ballCoords.left < 0) ballCoords.left = 0; - // // prevent crossing the right field boundary + // // previniendo el cruce del limite derecho del campo if (ballCoords.left + ball.clientWidth > field.clientWidth) { ballCoords.left = field.clientWidth - ball.clientWidth; } - // prevent crossing the bottom field boundary + // previniendo el cruce del limite inferior del campo if (ballCoords.top + ball.clientHeight > field.clientHeight) { ballCoords.top = field.clientHeight - ball.clientHeight; } diff --git a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/source.view/index.html b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/source.view/index.html index 34f9c88ae..34c21d9cb 100644 --- a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/source.view/index.html +++ b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/source.view/index.html @@ -16,8 +16,8 @@ <body style="height:2000px"> - Click on a field to move the ball there. - <br> The ball should never leave the field. + Haz click en un lugar del campo para mover el balón allí. + <br> El balón nunca debe abandonar el campo. <div id="field"> diff --git a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/task.md b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/task.md index d5269147a..c604c0ce0 100644 --- a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/task.md +++ b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/task.md @@ -2,20 +2,20 @@ importance: 5 --- -# Move the ball across the field +# Mueve el balón por el campo -Move the ball across the field to a click. Like this: +Mueve el balón por el campo con un click. Así: [iframe src="solution" height="260" link] -Requirements: +Requerimientos: -- The ball center should come exactly under the pointer on click (if possible without crossing the field edge). -- CSS-animation is welcome. -- The ball must not cross field boundaries. -- When the page is scrolled, nothing should break. +- El centro del balón debe quedar exactamente bajo el cursor al hacer click (sin atravesar el borde del campo si es posible). +- Las animaciones CSS son bienvenidas. +- El balón no debe cruzar los límites del campo. +- Cuando la página se desplace nada se debe romper. -Notes: +Notas: -- The code should also work with different ball and field sizes, not be bound to any fixed values. -- Use properties `event.clientX/event.clientY` for click coordinates. +- El código también debe funcionar con medidas diferentes de campo y balón, no debe estar asociado a ningún valor fijo. +- Usa las propiedades `event.clientX/event.clientY` para las coordenadas del click. diff --git a/2-ui/2-events/01-introduction-browser-events/05-sliding-menu/solution.md b/2-ui/2-events/01-introduction-browser-events/05-sliding-menu/solution.md index 7554a2f09..6f7aaf91d 100644 --- a/2-ui/2-events/01-introduction-browser-events/05-sliding-menu/solution.md +++ b/2-ui/2-events/01-introduction-browser-events/05-sliding-menu/solution.md @@ -1,12 +1,12 @@ # HTML/CSS -First let's create HTML/CSS. +Primero hay que crear el HTML y CSS. -A menu is a standalone graphical component on the page, so it's better to put it into a single DOM element. +Un menú es un componente gráfico independiente en la página, por lo que es mejor colocarlo en un solo elemento del DOM. -A list of menu items can be laid out as a list `ul/li`. +Una lista de elementos del menú se puede diseñar como una lista `ul/li`. -Here's the example structure: +Aquí está la estructura de ejemplo: ```html <div class="menu"> @@ -19,29 +19,29 @@ Here's the example structure: </div> ``` -We use `<span>` for the title, because `<div>` has an implicit `display:block` on it, and it will occupy 100% of the horizontal width. +Usamos `<span>` para el título, porque `<div>` tiene un `display:block` implícito en él, y va a ocupar 100% del ancho horizontal. -Like this: +Así: ```html autorun height=50 <div style="border: solid red 1px" onclick="alert(1)">Sweeties (click me)!</div> ``` -So if we set `onclick` on it, then it will catch clicks to the right of the text. +Entonces si establecemos `onclick` en él, detectará los clics a la derecha del texto. -As `<span>` has an implicit `display: inline`, it occupies exactly enough place to fit all the text: +Como `<span>` tiene un `display: inline` implícito, ocupa exactamente el lugar suficiente para que quepa todo el texto: ```html autorun height=50 <span style="border: solid red 1px" onclick="alert(1)">Sweeties (click me)!</span> ``` -# Toggling the menu +# Alternar el menú -Toggling the menu should change the arrow and show/hide the menu list. +Alternar el menú debería cambiar la flecha y mostrar/ocultar la lista del menú. -All these changes are perfectly handled by CSS. In JavaScript we should label the current state of the menu by adding/removing the class `.open`. +Todos estos cambios son perfectamente controlados con CSS. En JavaScript debemos etiquetar el estado actual del menú agregando/eliminando la clase `.open`. -Without it, the menu will be closed: +Sin él, el menú se cerrará: ```css .menu ul { @@ -58,7 +58,7 @@ Without it, the menu will be closed: } ``` -...And with `.open` the arrow changes and the list shows up: +...Y con `.open` la flecha cambia y aparece la lista: ```css .menu.open .title::before { diff --git a/2-ui/2-events/01-introduction-browser-events/05-sliding-menu/task.md b/2-ui/2-events/01-introduction-browser-events/05-sliding-menu/task.md index 34c313710..d572aeee9 100644 --- a/2-ui/2-events/01-introduction-browser-events/05-sliding-menu/task.md +++ b/2-ui/2-events/01-introduction-browser-events/05-sliding-menu/task.md @@ -2,10 +2,10 @@ importance: 5 --- -# Create a sliding menu +# Crear un menú deslizante -Create a menu that opens/collapses on click: +Crea un menú que se abra/colapse al hacer click: [iframe border=1 height=100 src="solution"] -P.S. HTML/CSS of the source document is to be modified. +P.D. El HTML/CSS del documento fuente se debe modificar. diff --git a/2-ui/2-events/01-introduction-browser-events/06-hide-message/solution.md b/2-ui/2-events/01-introduction-browser-events/06-hide-message/solution.md index 022a0d977..b024570a1 100644 --- a/2-ui/2-events/01-introduction-browser-events/06-hide-message/solution.md +++ b/2-ui/2-events/01-introduction-browser-events/06-hide-message/solution.md @@ -1,12 +1,12 @@ -To add the button we can use either `position:absolute` (and make the pane `position:relative`) or `float:right`. The `float:right` has the benefit that the button never overlaps the text, but `position:absolute` gives more freedom. So the choice is yours. +Para agregar el botón podemos usar cualquiera de las opciones `position:absolute` (y hacer el panel `position:relative`) o `float:right`. El `float:right` tiene la ventaja de que el botón no se encima con el texto, pero `position:absolute` da más libertad. Entonces la elección es tuya. -Then for each pane the code can be like: +Luego, para cada panel, el código puede ser así: ```js pane.insertAdjacentHTML("afterbegin", '<button class="remove-button">[x]</button>'); ``` -Then the `<button>` becomes `pane.firstChild`, so we can add a handler to it like this: +Luego el `<button>` se convierte en `pane.firstChild`, por lo que podemos agregarle un controlador como este: ```js pane.firstChild.onclick = () => pane.remove(); diff --git a/2-ui/2-events/01-introduction-browser-events/06-hide-message/solution.view/index.html b/2-ui/2-events/01-introduction-browser-events/06-hide-message/solution.view/index.html index ff9d8cc2b..0fc652310 100644 --- a/2-ui/2-events/01-introduction-browser-events/06-hide-message/solution.view/index.html +++ b/2-ui/2-events/01-introduction-browser-events/06-hide-message/solution.view/index.html @@ -30,7 +30,7 @@ <h3>Cat</h3> for(let pane of panes) { pane.insertAdjacentHTML("afterbegin", '<button class="remove-button">[x]</button>'); - // button becomes the first child of pane + // button debe convertirse en el primer child de pane pane.firstChild.onclick = () => pane.remove(); } </script> diff --git a/2-ui/2-events/01-introduction-browser-events/06-hide-message/task.md b/2-ui/2-events/01-introduction-browser-events/06-hide-message/task.md index 152cf41fe..349d62ace 100644 --- a/2-ui/2-events/01-introduction-browser-events/06-hide-message/task.md +++ b/2-ui/2-events/01-introduction-browser-events/06-hide-message/task.md @@ -2,12 +2,12 @@ importance: 5 --- -# Add a closing button +# Agregar un botón de cierre -There's a list of messages. +Hay una lista de mensajes. -Use JavaScript to add a closing button to the right-upper corner of each message. +Usa JavaScript para agregar un botón de cierre en la esquina superior derecha de cada mensaje. -The result should look like this: +El resultado debería verse algo así: [iframe src="solution" height=450] diff --git a/2-ui/2-events/01-introduction-browser-events/07-carousel/solution.md b/2-ui/2-events/01-introduction-browser-events/07-carousel/solution.md index 1c6b52cea..b41d03cc6 100644 --- a/2-ui/2-events/01-introduction-browser-events/07-carousel/solution.md +++ b/2-ui/2-events/01-introduction-browser-events/07-carousel/solution.md @@ -1,17 +1,17 @@ -The images ribbon can be represented as `ul/li` list of images `<img>`. +La cinta de imágenes se puede representar como una lista `ul/li` de imágenes `<img>`. -Normally, such a ribbon is wide, but we put a fixed-size `<div>` around to "cut" it, so that only a part of the ribbon is visible: +Normalmente dicha cinta es ancha, pero colocamos un tamaño fijo `<div>` alrededor para "cortarla", de modo que solo una parte de la cinta sea visible: ![](carousel1.svg) -To make the list show horizontally we need to apply correct CSS properties for `<li>`, like `display: inline-block`. +Para que la lista se muestre horizontalmente debemos aplicar las propiedades CSS correctas para `<li>`, como `display: inline-block`. -For `<img>` we should also adjust `display`, because by default it's `inline`. There's extra space reserved under `inline` elements for "letter tails", so we can use `display:block` to remove it. +Para `<img>` también deberíamos ajustar `display`, ya que es `inline` por default. Hay espacio adicional reservado debajo de los "letter tails", por lo que podemos usar `display:block` para eliminarlo. -To do the scrolling, we can shift `<ul>`. There are many ways to do it, for instance by changing `margin-left` or (better performance) use `transform: translateX()`: +Para hacer el desplazamiento, podemos cambiar `<ul>`. Hay muchas formas de hacerlo, por ejemplo, cambiando `margin-left` o (para mejor rendimiento) usando `transform: translateX()`: ![](carousel2.svg) -The outer `<div>` has a fixed width, so "extra" images are cut. +El `<div>` exterior tiene un ancho fijo, por lo que se cortan las imágenes "extra". -The whole carousel is a self-contained "graphical component" on the page, so we'd better wrap it into a single `<div class="carousel">` and style things inside it. +Todo el carrusel es un "componente gráfico" autónomo en la página, por lo que será mejor que lo envuelva en un solo elemento `<div class="carousel">` y le apliquemos estilo. diff --git a/2-ui/2-events/01-introduction-browser-events/07-carousel/solution.view/index.html b/2-ui/2-events/01-introduction-browser-events/07-carousel/solution.view/index.html index baf867664..94fa9ae91 100644 --- a/2-ui/2-events/01-introduction-browser-events/07-carousel/solution.view/index.html +++ b/2-ui/2-events/01-introduction-browser-events/07-carousel/solution.view/index.html @@ -27,7 +27,7 @@ </div> <script> - /* label the images, just for convenience, to visually track them */ + /* etiqueta las imágenes pra poder rastrearlas, solo por conveniencia */ let i = 1; for(let li of carousel.querySelectorAll('li')) { li.style.position = 'relative'; @@ -35,27 +35,27 @@ i++; } - /* configuration */ - let width = 130; // image width - let count = 3; // visible images count + /* configuración */ + let width = 130; // ancho de las imágenes + let count = 3; // conteo de las imágenes visibles let list = carousel.querySelector('ul'); let listElems = carousel.querySelectorAll('li'); - let position = 0; // ribbon scroll position + let position = 0; // posición del desplazamiento del carrete carousel.querySelector('.prev').onclick = function() { - // shift left + // desplazamiento izquierdo position += width * count; - // can't move to the left too much, end of images + // no podemos mover demasiado a la izquierda, se acaban las imágenes position = Math.min(position, 0) list.style.marginLeft = position + 'px'; }; carousel.querySelector('.next').onclick = function() { - // shift right + // desplazamiento derecho position -= width * count; - // can only shift the ribbbon for (total ribbon length - visible count) images + // solo se puede desplazar el carrete de imágenes (longitud total de la cinta - conteo visibles) position = Math.max(position, -width * (listElems.length - count)); list.style.marginLeft = position + 'px'; }; diff --git a/2-ui/2-events/01-introduction-browser-events/07-carousel/solution.view/style.css b/2-ui/2-events/01-introduction-browser-events/07-carousel/solution.view/style.css index bbbc6327e..f31acdc66 100644 --- a/2-ui/2-events/01-introduction-browser-events/07-carousel/solution.view/style.css +++ b/2-ui/2-events/01-introduction-browser-events/07-carousel/solution.view/style.css @@ -14,7 +14,7 @@ body { .carousel img { width: 130px; height: 130px; - /* make it block to remove space around images */ + /* establecer block para remover el espacio alrededor de las imágenes */ display: block; } @@ -60,7 +60,7 @@ body { padding: 0; list-style: none; transition: margin-left 250ms; - /* remove white-space between inline-block'ed li */ + /* eliminando los espacios en blanco entre los li inline-block'ed */ /* http://davidwalsh.name/remove-whitespace-inline-block */ font-size: 0; } diff --git a/2-ui/2-events/01-introduction-browser-events/07-carousel/source.view/index.html b/2-ui/2-events/01-introduction-browser-events/07-carousel/source.view/index.html index e9a9bc8d4..36cd64e7c 100644 --- a/2-ui/2-events/01-introduction-browser-events/07-carousel/source.view/index.html +++ b/2-ui/2-events/01-introduction-browser-events/07-carousel/source.view/index.html @@ -7,7 +7,7 @@ <body> - <!-- create your markup and styles --> + <!-- crea tus "markup" y "styles" --> <button class="arrow">⇦</button> <button class="arrow">⇨</button> @@ -28,8 +28,8 @@ <script> - // label the images to visually track them, just for convenience, - // this code can be removed + // etiqueta las imágenes pra poder rastrearlas, solo por conveniencia, + // este código puede ser removido let i = 1; for(let li of carousel.querySelectorAll('li')) { li.style.position = 'relative'; @@ -37,7 +37,7 @@ i++; } - // ...your code to make carousel alive! + // ...¡Tu código para crear un carrusel con vida! </script> </body> diff --git a/2-ui/2-events/01-introduction-browser-events/07-carousel/source.view/style.css b/2-ui/2-events/01-introduction-browser-events/07-carousel/source.view/style.css index 75c68f01a..08b0ec549 100644 --- a/2-ui/2-events/01-introduction-browser-events/07-carousel/source.view/style.css +++ b/2-ui/2-events/01-introduction-browser-events/07-carousel/source.view/style.css @@ -30,9 +30,9 @@ ul { ul img { width: 130px; height: 130px; - display: block; /* removes extra space near images */ + display: block; /* remueve el espacio extra cerca de las imágenes */ } ul li { - display: inline-block; /* removes extra space between list items + display: inline-block; /* remueve el espacio extra entre los elementos li } diff --git a/2-ui/2-events/01-introduction-browser-events/07-carousel/task.md b/2-ui/2-events/01-introduction-browser-events/07-carousel/task.md index a6adbffa5..b39014595 100644 --- a/2-ui/2-events/01-introduction-browser-events/07-carousel/task.md +++ b/2-ui/2-events/01-introduction-browser-events/07-carousel/task.md @@ -2,12 +2,12 @@ importance: 4 --- -# Carousel +# Carrusel -Create a "carousel" -- a ribbon of images that can be scrolled by clicking on arrows. +Crea un "carrusel": una cinta de imágenes que se puede desplazar haciendo clic en las flechas. [iframe height=200 src="solution"] -Later we can add more features to it: infinite scrolling, dynamic loading etc. +Más adelante podemos agregarle más funciones: desplazamiento infinito, carga dinámica, etc. -P.S. For this task HTML/CSS structure is actually 90% of the solution. +P.D. Para esta tarea, la estructura HTML / CSS es en realidad el 90% de la solución. diff --git a/2-ui/2-events/01-introduction-browser-events/article.md b/2-ui/2-events/01-introduction-browser-events/article.md index 4eca222aa..6fab8d8fd 100644 --- a/2-ui/2-events/01-introduction-browser-events/article.md +++ b/2-ui/2-events/01-introduction-browser-events/article.md @@ -1,256 +1,256 @@ -# Introduction to browser events +# Introducción a los eventos en el navegador -*An event* is a signal that something has happened. All DOM nodes generate such signals (but events are not limited to DOM). +*Un evento* es una señal de que algo ocurrió. Todos los nodos del DOM generan dichas señales (pero los eventos no están limitados sólo al DOM). -Here's a list of the most useful DOM events, just to take a look at: +Aquí hay una lista con los eventos del DOM más utilizados, solo para echar un vistazo: -**Mouse events:** -- `click` -- when the mouse clicks on an element (touchscreen devices generate it on a tap). -- `contextmenu` -- when the mouse right-clicks on an element. -- `mouseover` / `mouseout` -- when the mouse cursor comes over / leaves an element. -- `mousedown` / `mouseup` -- when the mouse button is pressed / released over an element. -- `mousemove` -- when the mouse is moved. +**Eventos del mouse:** +- `click` -- cuando el mouse hace click sobre un elemento (los dispositivos touch lo generan con un toque). +- `contextmenu` -- cuando el mouse hace click derecho sobre un elemento. +- `mouseover` / `mouseout` -- cuando el cursor del mouse ingresa/abandona un elemento. +- `mousedown` / `mouseup` -- cuando el botón del mouse es presionado/soltado sobre un elemento. +- `mousemove` -- cuando el mouse se mueve. -**Keyboard events:** -- `keydown` and `keyup` -- when a keyboard key is pressed and released. +**Eventos del teclado:** +- `keydown` / `keyup` -- cuando se presiona/suelta una tecla. -**Form element events:** -- `submit` -- when the visitor submits a `<form>`. -- `focus` -- when the visitor focuses on an element, e.g. on an `<input>`. +**Eventos del elemento form:** +- `submit` -- cuando el visitante envía un `<form>`. +- `focus` -- cuando el visitante hace foco en un elemento, por ejemplo un `<input>`. -**Document events:** -- `DOMContentLoaded` -- when the HTML is loaded and processed, DOM is fully built. +**Eventos del documento:** +- `DOMContentLoaded` --cuando el HTML es cargado y procesado, el DOM está completamente construido -**CSS events:** -- `transitionend` -- when a CSS-animation finishes. +**Eventos del CSS:** +- `transitionend` -- cuando una animación CSS concluye. -There are many other events. We'll get into more details of particular events in upcoming chapters. +Hay muchos más eventos. Entraremos en más detalles con eventos particulares en los siguientes capítulos. -## Event handlers +## Controladores de eventos -To react on events we can assign a *handler* -- a function that runs in case of an event. +Para reaccionar a los eventos podemos asignar un *handler (controlador)* el cual es una función que se ejecuta en caso de un evento. -Handlers are a way to run JavaScript code in case of user actions. +Los handlers son una forma de ejecutar código JavaScript en caso de acciones por parte del usuario. -There are several ways to assign a handler. Let's see them, starting from the simplest one. +Hay muchas maneras de asignar un handler. Vamos a verlas empezando por las más simples. -### HTML-attribute +### Atributo HTML -A handler can be set in HTML with an attribute named `on<event>`. +Un handler puede ser establecido en el HTML con un atributo llamado `on<event>`. -For instance, to assign a `click` handler for an `input`, we can use `onclick`, like here: +Por ejemplo, para asignar un handler `click` a un `input` podemos usar `onclick`, como aquí: ```html run -<input value="Click me" *!*onclick="alert('Click!')"*/!* type="button"> +<input value="Haz click aquí" *!*onclick="alert('¡Click!')"*/!* type="button"> ``` -On mouse click, the code inside `onclick` runs. +Al hacer click, el código dentro de `onclick` se ejecuta. -Please note that inside `onclick` we use single quotes, because the attribute itself is in double quotes. If we forget that the code is inside the attribute and use double quotes inside, like this: `onclick="alert("Click!")"`, then it won't work right. +Toma en cuenta que dentro de `onclick` usamos comillas simples, porque el atributo en sí va entre comillas dobles. Si olvidamos que el código está dentro del atributo y usamos comillas dobles dentro, así: `onclick="alert("Click!")"`, no funcionará correctamente. -An HTML-attribute is not a convenient place to write a lot of code, so we'd better create a JavaScript function and call it there. +Un atributo HTML no es un lugar conveniente para escribir un montón de código, así que mejor creamos una función JavaScript y la llamamos allí. -Here a click runs the function `countRabbits()`: +Aquí un click ejecuta la función `countRabbits()`: ```html autorun height=50 <script> function countRabbits() { for(let i=1; i<=3; i++) { - alert("Rabbit number " + i); + alert("Conejo número " + i); } } </script> -<input type="button" *!*onclick="countRabbits()"*/!* value="Count rabbits!"> +<input type="button" *!*onclick="countRabbits()"*/!* value="¡Cuenta los conejos!"> ``` -As we know, HTML attribute names are not case-sensitive, so `ONCLICK` works as well as `onClick` and `onCLICK`... But usually attributes are lowercased: `onclick`. +Como sabemos, los nombres de los atributos HTML no distinguen entre mayúsculas y minúsculas, entonces `ONCLICK` funciona bien al igual que `onClick` y `onCLICK`... Pero usualmente los atributos van con minúsculas: `onclick`. -### DOM property +### Propiedad del DOM -We can assign a handler using a DOM property `on<event>`. +Podemos asignar un handler usando una propiedad del DOM `on<event>`. -For instance, `elem.onclick`: +Por ejemplo, `elem.onclick`: ```html autorun -<input id="elem" type="button" value="Click me"> +<input id="elem" type="button" value="Haz click en mí"> <script> *!* elem.onclick = function() { - alert('Thank you'); + alert('¡Gracias!'); }; */!* </script> ``` -If the handler is assigned using an HTML-attribute then the browser reads it, creates a new function from the attribute content and writes it to the DOM property. +Si el handler es asignado usando un atributo HTML entonces el navegador lo lee, crea una nueva función desde el contenido del atributo y lo escribe en la propiedad del DOM. -So this way is actually the same as the previous one. +Esta forma en realidad es la misma que ya habíamos visto antes. -These two code pieces work the same: +Estás dos piezas de código funcionan igual: -1. Only HTML: +1. Solo HTML: ```html autorun height=50 - <input type="button" *!*onclick="alert('Click!')"*/!* value="Button"> + <input type="button" *!*onclick="alert('¡Click!')"*/!* value="Botón"> ``` 2. HTML + JS: ```html autorun height=50 - <input type="button" id="button" value="Button"> + <input type="button" id="button" value="Botón"> <script> *!* button.onclick = function() { - alert('Click!'); + alert('¡Click!'); }; */!* </script> ``` -In the first example, the HTML attribute is used to initialize the `button.onclick`, while in the second example -- the script, that's all the difference. +En el primer ejemplo el atributo HTML es usado para inicializar el `button.onclick`, mientras que en el segundo ejemplo se usa el script. Esa es toda la diferencia. -**As there's only one `onclick` property, we can't assign more than one event handler.** +**Como solo hay una propiedad `onclick`, no podemos asignar más de un handler.** -In the example below adding a handler with JavaScript overwrites the existing handler: +En el siguiente ejemplo se agrega un handler con JavaScript que sobrescribe el handler existente: ```html run height=50 autorun -<input type="button" id="elem" onclick="alert('Before')" value="Click me"> +<input type="button" id="elem" onclick="alert('Antes')" value="¡Haz click en mí!"> <script> *!* - elem.onclick = function() { // overwrites the existing handler - alert('After'); // only this will be shown + elem.onclick = function() { // sobrescribe el handler existente + alert('Después'); // solo se mostrará este }; */!* </script> ``` -To remove a handler -- assign `elem.onclick = null`. +Para eliminar un handler, asigna `elem.onclick = null`. -## Accessing the element: this +## Accediendo al elemento: this -The value of `this` inside a handler is the element. The one which has the handler on it. +El valor de `this` dentro de un handler es el elemento, el cual tiene el handler dentro. -In the code below `button` shows its contents using `this.innerHTML`: +En el siguiente código el `button` muestra su contenido usando `this.innerHTML`: ```html height=50 autorun -<button onclick="alert(this.innerHTML)">Click me</button> +<button onclick="alert(this.innerHTML)">Haz click en mí</button> ``` -## Possible mistakes +## Posibles errores -If you're starting to work with events -- please note some subtleties. +Si estás empezando a trabajar con eventos, por favor, nota algunas sutilezas. -We can set an existing function as a handler: +Nosotros podemos establecer una función existente como un handler: ```js function sayThanks() { - alert('Thanks!'); + alert('¡Gracias!'); } elem.onclick = sayThanks; ``` -But be careful: the function should be assigned as `sayThanks`, not `sayThanks()`. +Pero ten cuidado: la función debe ser asignada como `sayThanks`, no `sayThanks()`. ```js -// right +// correcto button.onclick = sayThanks; -// wrong +// incorrecto button.onclick = sayThanks(); ``` -If we add parentheses, then `sayThanks()` becomes a function call. So the last line actually takes the *result* of the function execution, that is `undefined` (as the function returns nothing), and assigns it to `onclick`. That doesn't work. +Si agregamos paréntesis, `sayThanks()` se convierte en una llamada de función. En ese caso la última linea toma el *resultado* de la ejecución de la función, que es `undefined` (ya que la función no devuelve nada), y lo asigna a `onclick`. Esto no funciona. -...On the other hand, in the markup we do need the parentheses: +...Por otro lado, en el markup necesitamos los paréntesis: ```html <input type="button" id="button" onclick="sayThanks()"> ``` -The difference is easy to explain. When the browser reads the attribute, it creates a handler function with body from the attribute content. +La diferencia es fácil de explicar. Cuando el navegador lee el atributo crea una función handler con cuerpo a partir del contenido del atributo. -So the markup generates this property: +Por lo que el markup genera esta propiedad: ```js button.onclick = function() { *!* - sayThanks(); // <-- the attribute content goes here + sayThanks(); // <-- el contenido del atributo va aquí */!* }; ``` -**Don't use `setAttribute` for handlers.** +**No uses `setAttribute` para handlers.** -Such a call won't work: +Tal llamada no funcionará: ```js run no-beautify -// a click on <body> will generate errors, -// because attributes are always strings, function becomes a string +// un click sobre <body> generará errores, +// debido a que los atributos siempre son strings, la función se convierte en un string document.body.setAttribute('onclick', function() { alert(1) }); ``` -**DOM-property case matters.** +**Las mayúsculas en las propiedades DOM importan.** -Assign a handler to `elem.onclick`, not `elem.ONCLICK`, because DOM properties are case-sensitive. +Asignar un handler a `elem.onclick`, en lugar de `elem.ONCLICK`, ya que las propiedades DOM son sensibles a mayúsculas. ## addEventListener -The fundamental problem of the aforementioned ways to assign handlers is that we *can't assign multiple handlers to one event*. +El problema fundamental de las formas ya mencionadas para asignar handlers es que *no podemos asignar multiples handlers a un solo evento*. -Let's say, one part of our code wants to highlight a button on click, and another one wants to show a message on the same click. +Digamos que una parte de nuestro código quiere resaltar un botón al hacer click, y otra quiere mostrar un mensaje en el mismo click. -We'd like to assign two event handlers for that. But a new DOM property will overwrite the existing one: +Nos gustaría asignar dos handlers de eventos para eso. Pero una nueva propiedad DOM sobrescribirá la que ya existe: ```js no-beautify input.onclick = function() { alert(1); } // ... -input.onclick = function() { alert(2); } // replaces the previous handler +input.onclick = function() { alert(2); } // el handler reemplaza el handler anterior ``` -Developers of web standards understood that long ago and suggested an alternative way of managing handlers using the special methods `addEventListener` and `removeEventListener` which aren't bound by such constraint. +Los desarrolladores de estándares de la web entendieron eso hace mucho tiempo y sugirieron una forma alternativa de administrar los handlers utilizando los métodos especiales `addEventListener` y `removeEventListener`, que no tienen este problema. -The syntax to add a handler: +La sintaxis para agregar un handler: ```js element.addEventListener(event, handler, [options]); ``` `event` -: Event name, e.g. `"click"`. +: Nombre del evento, por ejemplo: `"click"`. `handler` -: The handler function. +: La función handler. `options` -: An additional optional object with properties: - - `once`: if `true`, then the listener is automatically removed after it triggers. - - `capture`: the phase where to handle the event, to be covered later in the chapter <info:bubbling-and-capturing>. For historical reasons, `options` can also be `false/true`, that's the same as `{capture: false/true}`. - - `passive`: if `true`, then the handler will not call `preventDefault()`, we'll explain that later in <info:default-browser-action>. +: Un objeto adicional, opcional, con las propiedades: + - `once`: si es `true` entonces el listener se remueve automáticamente después de activarlo. + - `capture`: la fase en la que se controla el evento, que será cubierta en el capítulo <info:bubbling-and-capturing>. Por razones históricas, `options` también puede ser `false/true`, lo que es igual a `{capture: false/true}`. + - `passive`: si es `true` entonces el handler no llamará a `preventDefault()`, esto lo explicaremos más adelante en <info:default-browser-action>. -To remove the handler, use `removeEventListener`: +Para remover el handler, usa `removeEventListener`: ```js element.removeEventListener(event, handler, [options]); ``` -````warn header="Removal requires the same function" -To remove a handler we should pass exactly the same function as was assigned. +````warn header="Remover requiere la misma función" +Para remover un handler deberemos pasar exactamente la misma función que asignamos. -This doesn't work: +Esto no funciona: ```js no-beautify -elem.addEventListener( "click" , () => alert('Thanks!')); +elem.addEventListener( "click" , () => alert('¡Gracias!')); // .... -elem.removeEventListener( "click", () => alert('Thanks!')); +elem.removeEventListener( "click", () => alert('¡Gracias!')); ``` -The handler won't be removed, because `removeEventListener` gets another function -- with the same code, but that doesn't matter, as it's a different function object. +El handler no será removido porque `removeEventListener` obtiene otra función, con el mismo código, pero eso no importa, ya que es un objeto de función diferente. -Here's the right way: +Aquí está la manera correcta: ```js function handler() { - alert( 'Thanks!' ); + alert( '¡Gracias!' ); } input.addEventListener("click", handler); @@ -258,112 +258,112 @@ input.addEventListener("click", handler); input.removeEventListener("click", handler); ``` -Please note -- if we don't store the function in a variable, then we can't remove it. There's no way to "read back" handlers assigned by `addEventListener`. +Por favor nota que si no almacenamos la función en una variable entonces no podremos removerla. No hay forma de "volver a leer" los handlers asignados por `addEventListener`. ```` -Multiple calls to `addEventListener` allow it to add multiple handlers, like this: +Múltiples llamadas a `addEventListener`permiten agregar múltiples handlers: ```html run no-beautify -<input id="elem" type="button" value="Click me"/> +<input id="elem" type="button" value="Haz click en mí"/> <script> function handler1() { - alert('Thanks!'); + alert('¡Gracias!'); }; function handler2() { - alert('Thanks again!'); + alert('¡Gracias de nuevo!'); } *!* - elem.onclick = () => alert("Hello"); - elem.addEventListener("click", handler1); // Thanks! - elem.addEventListener("click", handler2); // Thanks again! + elem.onclick = () => alert("Hola"); + elem.addEventListener("click", handler1); // Gracias! + elem.addEventListener("click", handler2); // Gracias de nuevo! */!* </script> ``` -As we can see in the example above, we can set handlers *both* using a DOM-property and `addEventListener`. But generally we use only one of these ways. +Como podemos ver en el ejemplo anterior, podemos establecer handlers *tanto* usando un propiedad DOM como `addEventListener` juntos. Pero por lo general solo usamos una de esas maneras. -````warn header="For some events, handlers only work with `addEventListener`" -There exist events that can't be assigned via a DOM-property. Only with `addEventListener`. +````warn header="Para algunos eventos, los handlers solo funcionan con `addEventListener`" +Hay eventos que no pueden ser asignados por medio de una propiedad del DOM, sino solamente con `addEventListener`. -For instance, the `DOMContentLoaded` event, that triggers when the document is loaded and the DOM has been built. +Por ejemplo, el evento `DOMContentLoaded`, que se activa cuando el documento está cargado y el DOM está construido. ```js -// will never run +// nunca se ejecutará document.onDOMContentLoaded = function() { - alert("DOM built"); + alert("DOM construido"); }; ``` ```js -// this way it works +// Así sí funciona document.addEventListener("DOMContentLoaded", function() { - alert("DOM built"); + alert("DOM construido"); }); ``` -So `addEventListener` is more universal. Although, such events are an exception rather than the rule. +Por lo que `addEventListener` es más universal. Aún así, tales eventos son una excepción más que la regla. ```` -## Event object +## Objeto del evento -To properly handle an event we'd want to know more about what's happened. Not just a "click" or a "keydown", but what were the pointer coordinates? Which key was pressed? And so on. +Pero para manejar correctamente un evento necesitamos saber todavía más acerca de lo que está pasando. No solo si fue un "click" o un "teclazo", sino ¿cuáles eran coordenadas del cursor, o qué tecla fue oprimida? Y así. -When an event happens, the browser creates an *event object*, puts details into it and passes it as an argument to the handler. +Cuando un evento ocurre, el navegador crea un *objeto del evento*, coloca los detalles dentro y los pasa como un argumento al handler. -Here's an example of getting pointer coordinates from the event object: +Aquí hay un ejemplo para obtener las coordenadas del cursor a partir del objeto del evento: ```html run -<input type="button" value="Click me" id="elem"> +<input type="button" value="¡Haz click en mí!" id="elem"> <script> elem.onclick = function(*!*event*/!*) { - // show event type, element and coordinates of the click - alert(event.type + " at " + event.currentTarget); - alert("Coordinates: " + event.clientX + ":" + event.clientY); + // muestra el tipo de evento, el elemento y las coordenadas del click + alert(event.type + " en el " + event.currentTarget); + alert("Coordenadas: " + event.clientX + ":" + event.clientY); }; </script> ``` -Some properties of `event` object: +Algunas propiedades del objeto `event`: `event.type` -: Event type, here it's `"click"`. +: Tipo de evento, en este caso fue `"click"`. `event.currentTarget` -: Element that handled the event. That's exactly the same as `this`, unless the handler is an arrow function, or its `this` is bound to something else, then we can get the element from `event.currentTarget`. +: Elemento que maneja el evento. Lo que es exactamente igual a `this`, a menos que `this` haya cambiado por ser el handler una función de flecha, o su `this` esté vinculado a otra cosa. event.currentTarget`siempre apunta al elemento. `event.clientX` / `event.clientY` -: Window-relative coordinates of the cursor, for pointer events. +: Coordenadas del cursor relativas a la ventana, para eventos de cursor. -There are more properties. Many of them depend on the event type: keyboard events have one set of properties, pointer events - another one, we'll study them later when as we move on to the details of different events. +Hay más propiedades. Muchas de ellas dependen del tipo de evento: los eventos del teclado tienen un conjunto de propiedades, y las de cursor, otro. Los estudiaremos después, cuando lleguemos a los detalles de diferentes eventos. -````smart header="The event object is also available in HTML handlers" -If we assign a handler in HTML, we can also use the `event` object, like this: +````smart header="El objeto del evento también está disponible para handlers HTML" +Si asignamos un handler en HTML también podemos usar el objeto `event`, así: ```html autorun height=60 <input type="button" onclick="*!*alert(event.type)*/!*" value="Event type"> ``` -That's possible because when the browser reads the attribute, it creates a handler like this: `function(event) { alert(event.type) }`. That is: its first argument is called `"event"`, and the body is taken from the attribute. +Esto es posible porque cuando el navegador lee el atributo, crea un handler como este: `function(event) { alert(event.type) }`. Lo que significa que el primer argumento es llamado `"event"` y el cuerpo es tomado del atributo. ```` -## Object handlers: handleEvent +## Objetos handlers: handleEvent -We can assign not just a function, but an object as an event handler using `addEventListener`. When an event occurs, its `handleEvent` method is called. +Podemos asignar no solo una función, sino un objeto como handler del evento usando `addEventListener`. Cuando el evento ocurre, el método `handleEvent` es llamado. -For instance: +Por ejemplo: ```html run -<button id="elem">Click me</button> +<button id="elem">Haz click en mí</button> <script> let obj = { handleEvent(event) { - alert(event.type + " at " + event.currentTarget); + alert(event.type + " en " + event.currentTarget); } }; @@ -371,23 +371,23 @@ For instance: </script> ``` -As we can see, when `addEventListener` receives an object as the handler, it calls `obj.handleEvent(event)` in case of an event. +Como podemos ver, cuando `addEventListener` recibe como handler a un objeto, llama a `obj.handleEvent(event)` en caso de un evento. -We could also use objects of a custom class, like this: +También podemos usar objetos de una clase personalizada: ```html run -<button id="elem">Click me</button> +<button id="elem">Haz click en mí</button> <script> class Menu { handleEvent(event) { switch(event.type) { case 'mousedown': - elem.innerHTML = "Mouse button pressed"; + elem.innerHTML = "Botón del mouse presionado"; break; case 'mouseup': - elem.innerHTML += "...and released."; + elem.innerHTML += "...y soltado."; break; } } @@ -402,12 +402,12 @@ We could also use objects of a custom class, like this: </script> ``` -Here the same object handles both events. Please note that we need to explicitly setup the events to listen using `addEventListener`. The `menu` object only gets `mousedown` and `mouseup` here, not any other types of events. +Aquí el mismo objeto maneja ambos eventos. Nota que necesitamos configurar explícitamente los eventos a escuchar usando `addEventListener`. El objeto `menu` solo obtiene `mousedown` y `mouseup` aquí, no hay ningún otro tipo de eventos. -The method `handleEvent` does not have to do all the job by itself. It can call other event-specific methods instead, like this: +El método `handleEvent` no tiene que hacer todo el trabajo por sí solo. En su lugar puede llamar a otros métodos específicos de eventos, como este: ```html run -<button id="elem">Click me</button> +<button id="elem">Haz click en mí</button> <script> class Menu { @@ -418,11 +418,11 @@ The method `handleEvent` does not have to do all the job by itself. It can call } onMousedown() { - elem.innerHTML = "Mouse button pressed"; + elem.innerHTML = "Botón del mouse presionado"; } onMouseup() { - elem.innerHTML += "...and released."; + elem.innerHTML += "...y soltado."; } } @@ -432,22 +432,22 @@ The method `handleEvent` does not have to do all the job by itself. It can call </script> ``` -Now event handlers are clearly separated, that may be easier to support. +Ahora los handlers del evento están claramente separados, lo que puede ser más fácil de mantener. -## Summary +## Resumen -There are 3 ways to assign event handlers: +Hay tres formas de asignar handlers: -1. HTML attribute: `onclick="..."`. -2. DOM property: `elem.onclick = function`. -3. Methods: `elem.addEventListener(event, handler[, phase])` to add, `removeEventListener` to remove. +1. Atributos HTML: `onclick="..."`. +2. Propiedades del DOM: `elem.onclick = function`. +3. Métodos: `elem.addEventListener(event, handler[, phase])` para agregarlos ó `removeEventListener` para quitarlos. -HTML attributes are used sparingly, because JavaScript in the middle of an HTML tag looks a little bit odd and alien. Also can't write lots of code in there. +Los atributos HTML se usan con moderación, porque JavaScript en medio de una etiqueta HTML luce un poco extraño y ajeno. Además no podemos escribir montones de código ahí. -DOM properties are ok to use, but we can't assign more than one handler of the particular event. In many cases that limitation is not pressing. +Las propiedades del DOM son buenas para usar, pero no podemos asignar más de un handler a un evento en particular. En la mayoría de casos esta limitación no es apremiante. -The last way is the most flexible, but it is also the longest to write. There are few events that only work with it, for instance `transitionend` and `DOMContentLoaded` (to be covered). Also `addEventListener` supports objects as event handlers. In that case the method `handleEvent` is called in case of the event. +La última forma es la más flexible, pero también es la más larga para escribir. Unos pocos eventos solo funcionan con ésta, por ejemplo `transitionend` y `DOMContentLoaded` (que veremos después). Además `addEventListener` soporta objetos como handlers de eventos. En este caso `handleEvent` es llamado en caso del evento. -No matter how you assign the handler -- it gets an event object as the first argument. That object contains the details about what's happened. +No importa como asignes el handler, este obtiene un objeto como primer argumento. Este objeto contiene los detalles sobre lo que pasó. -We'll learn more about events in general and about different types of events in the next chapters. +Vamos a aprender más sobre eventos en general y sobre diferentes tipos de eventos en los siguientes capítulos. diff --git a/2-ui/2-events/02-bubbling-and-capturing/article.md b/2-ui/2-events/02-bubbling-and-capturing/article.md index 2448cfa5b..0ffc870d5 100644 --- a/2-ui/2-events/02-bubbling-and-capturing/article.md +++ b/2-ui/2-events/02-bubbling-and-capturing/article.md @@ -1,24 +1,24 @@ -# Bubbling and capturing +# Propagación y captura -Let's start with an example. +Vamos a empezar con un ejemplo. -This handler is assigned to `<div>`, but also runs if you click any nested tag like `<em>` or `<code>`: +Este manejador está asignado a `<div>`, pero también se ejecuta si haces clic a cualquier elemento anidado como `<em>` ó `<code>`: ```html autorun height=60 -<div onclick="alert('The handler!')"> - <em>If you click on <code>EM</code>, the handler on <code>DIV</code> runs.</em> +<div onclick="alert('¡El manejador!')"> + <em>Si haces clic en<code>EM</code>, el manejador en <code>DIV</code> es ejecutado.</em> </div> ``` -Isn't it a bit strange? Why does the handler on `<div>` run if the actual click was on `<em>`? +¿No es un poco extraño? ¿Por qué el manejador en `<div>` es ejecutado, si el clic fue hecho en `<em>`? -## Bubbling +# Propagación -The bubbling principle is simple. +El principio de propagación es simple. -**When an event happens on an element, it first runs the handlers on it, then on its parent, then all the way up on other ancestors.** +**Cuando un evento ocurre en un elemento, este primero ejecuta los manejadores que tiene asignados, luego los manejadores de su padre, y así hasta otros ancestros.** -Let's say we have 3 nested elements `FORM > DIV > P` with a handler on each of them: +Digamos que tenemos 3 elementos anidados `FORM > DIV > P` con un manejador en cada uno de ellos: ```html run autorun <style> @@ -35,125 +35,124 @@ Let's say we have 3 nested elements `FORM > DIV > P` with a handler on each of t </form> ``` -A click on the inner `<p>` first runs `onclick`: -1. On that `<p>`. -2. Then on the outer `<div>`. -3. Then on the outer `<form>`. -4. And so on upwards till the `document` object. +Un clic en el elemento del interior `<p>` primero ejecuta `onclick`: +1. En ese `<p>`. +2. Luego en el `<div>` de arriba. +3. Luego en el `<form>` de más arriba. +4. Y así sucesivamente hasta el objeto `document`. ![](event-order-bubbling.svg) -So if we click on `<p>`, then we'll see 3 alerts: `p` -> `div` -> `form`. +Así si hacemos clic en `<p>`, entonces veremos 3 alertas: `p` -> `div` -> `form`. -The process is called "bubbling", because events "bubble" from the inner element up through parents like a bubble in the water. +Este proceso se conoce como "propagación" (bubbling, burbujeo) porque los eventos "se propagan" desde el elemento más al interior, a través de los padres, como una burbuja en el agua. -```warn header="*Almost* all events bubble." -The key word in this phrase is "almost". +```warn header="*Casi* todos los elementos se propagan." +La palabra clave en esta frase es "casi". -For instance, a `focus` event does not bubble. There are other examples too, we'll meet them. But still it's an exception, rather than a rule, most events do bubble. +Por ejemplo, un evento `focus` no se propaga. Hay otros ejemplos que veremos también. Pero aún así, esta es la excepción a la regla, la mayoría de eventos sí se propagan. ``` ## event.target -A handler on a parent element can always get the details about where it actually happened. +Un manejador en un elemento padre siempre puede obtener los detalles sobre dónde realmente ocurrió el evento. -**The most deeply nested element that caused the event is called a *target* element, accessible as `event.target`.** +**El elemento anidado más profundo que causó el evento es llamado elemento *objetivo*, accesible como `event.target`** -Note the differences from `this` (=`event.currentTarget`): +Nota la diferencia de `this` (=`event.currentTarget`): -- `event.target` -- is the "target" element that initiated the event, it doesn't change through the bubbling process. -- `this` -- is the "current" element, the one that has a currently running handler on it. +- `event.target` -- es el elemento "objetivo" que inició el evento, no cambia a través de todo el proceso de propagación. +- `this` -- es el elemento "actual", el que tiene un manejador ejecutándose en el momento. -For instance, if we have a single handler `form.onclick`, then it can "catch" all clicks inside the form. No matter where the click happened, it bubbles up to `<form>` and runs the handler. +Por ejemplo, si tenemos un solo manejador `form.onclick`, este puede `atrapar` todos los clicks dentro del formulario. No importa dónde el clic se hizo, se propaga hasta el `<form>` y ejecuta el manejador. -In `form.onclick` handler: +En el manejador `form.onclick`: -- `this` (=`event.currentTarget`) is the `<form>` element, because the handler runs on it. -- `event.target` is the actual element inside the form that was clicked. +- `this` (=`event.currentTarget`) es el elemento `<form>`, porque el manejador se ejecutó en él. +- `event.target` es el elemento actual dentro de el formulario al que se le hizo clic. -Check it out: +Mira esto: [codetabs height=220 src="bubble-target"] -It's possible that `event.target` could equal `this` -- it happens when the click is made directly on the `<form>` element. +Es posible que `event.target` sea igual a `this`: ocurre cuando el clic se hace directamente en el elemento `<form>`. -## Stopping bubbling +## Detener la propagación -A bubbling event goes from the target element straight up. Normally it goes upwards till `<html>`, and then to `document` object, and some events even reach `window`, calling all handlers on the path. +Una propagación de evento empieza desde el elemento objetivo hacia arriba. Normalmente este continúa hasta `<html>` y luego hacia el objeto `document`, algunos eventos incluso alcanzan `window`, llamando a todos los manejadores en el camino. -But any handler may decide that the event has been fully processed and stop the bubbling. +Pero cualquier manejador podría decidir que el evento se ha procesado por completo y detener su propagación. -The method for it is `event.stopPropagation()`. +El método para esto es `event.stopPropagation()`. -For instance, here `body.onclick` doesn't work if you click on `<button>`: +Por ejemplo, aquí `body.onclick` no funciona si haces clic en `<button>`: ```html run autorun height=60 -<body onclick="alert(`the bubbling doesn't reach here`)"> - <button onclick="event.stopPropagation()">Click me</button> +<body onclick="alert(`No se propagó hasta aquí`)"> + <button onclick="event.stopPropagation()">Haz clic</button> </body> ``` ```smart header="event.stopImmediatePropagation()" -If an element has multiple event handlers on a single event, then even if one of them stops the bubbling, the other ones still execute. +Si un elemento tiene múltiples manejadores para un solo evento, aunque uno de ellos detenga la propagación, los demás aún se ejecutarán. -In other words, `event.stopPropagation()` stops the move upwards, but on the current element all other handlers will run. +En otras palabras, `event.stopPropagation()` detiene la propagación hacia arriba, pero todos los manejadores en el elemento actual se ejecutarán. -To stop the bubbling and prevent handlers on the current element from running, there's a method `event.stopImmediatePropagation()`. After it no other handlers execute. +Para detener la propagación e impedir que los manejadores del elemento actual se ejecuten, hay un método `event.stopImmediatePropagation()`. Después de él, ningún otro manejador será ejecutado. ``` -```warn header="Don't stop bubbling without a need!" -Bubbling is convenient. Don't stop it without a real need: obvious and architecturally well thought out. +```warn header="¡No detengas la propagación si no es necesario!" +La propagación es conveniente. No la detengas sin una necesidad real, obvia y arquitectónicamente bien pensada. -Sometimes `event.stopPropagation()` creates hidden pitfalls that later may become problems. +A veces `event.stopPropagation()` crea trampas ocultas que luego se convierten en problemas. -For instance: +Por ejemplo: -1. We create a nested menu. Each submenu handles clicks on its elements and calls `stopPropagation` so that the outer menu won't trigger. -2. Later we decide to catch clicks on the whole window, to track users' behavior (where people click). Some analytic systems do that. Usually the code uses `document.addEventListener('click'…)` to catch all clicks. -3. Our analytic won't work over the area where clicks are stopped by `stopPropagation`. Sadly, we've got a "dead zone". +1. Creamos un menú anidado. Cada submenú maneja los clics en sus elementos y ejecuta `stopPropagation` para que el menu de arriba no se desencadene. +2. Luego decidimos atrapar los clic en toda la ventana, para seguir el rastro del comportamiento del usuario (donde hacen clic). Algunos sistemas de análisis hacen eso. Usualmente el código usa `document.addEventListener('click'…)` para atrapar todos los clics. +3. Nuestro análisis no funcionará sobre el área dónde los clics son detenidos por `stopPropagation`. Tristemente, tenemos una "zona muerta". -There's usually no real need to prevent the bubbling. A task that seemingly requires that may be solved by other means. One of them is to use custom events, we'll cover them later. Also we can write our data into the `event` object in one handler and read it in another one, so we can pass to handlers on parents information about the processing below. +Usualmente no hay una necesidad real para impedir la propagación. Cuando algo parece necesitarlo, existen otros medios para resolverlo. Uno es usar eventos personalizados, que cubriremos más adelante. Otro es escribir datos en event en un manejador y leerlos en otro, para pasar información a los manejadores padres. ``` +## Captura -## Capturing +Hay otra fase en el procesamiento de eventos llamada "captura". Es raro usarla en código real, pero a veces puede ser útil. -There's another phase of event processing called "capturing". It is rarely used in real code, but sometimes can be useful. +El estándar de [eventos del DOM](https://www.w3.org/TR/DOM-Level-3-Events/) describe 3 fases de la propagación de eventos: -The standard [DOM Events](https://www.w3.org/TR/DOM-Level-3-Events/) describes 3 phases of event propagation: +1. Fase de captura -- el evento desciende al elemento. +2. Fase de objetivo -- el evento alcanza al elemento. +3. Fase de propagación -- el evento se propaga hacia arriba del elemento. -1. Capturing phase -- the event goes down to the element. -2. Target phase -- the event reached the target element. -3. Bubbling phase -- the event bubbles up from the element. - -Here's the picture, taken from the specification, of the capturing `(1)`, target `(2)` and bubbling `(3)` phases for a click event on a `<td>` inside a table: +Aquí (tomada de la especificación), tenemos la imagen de las fases de captura `(1)`, objetivo `(2)`, y propagación `(3)`, de un evento click en un `<td>` dentro de una tabla: ![](eventflow.svg) -That is: for a click on `<td>` the event first goes through the ancestors chain down to the element (capturing phase), then it reaches the target and triggers there (target phase), and then it goes up (bubbling phase), calling handlers on its way. +Se explica así: por un clic en `<td>` el evento va primero a través de la cadena de ancestros hacia el elemento (fase de captura), luego alcanza el objetivo y se desencadena ahí (fase de objetivo), y por último va hacia arriba (fase de propagación), ejecutando los manejadores en su camino. -Until now, we only talked about bubbling, because the capturing phase is rarely used. +Hasta ahora solo hablamos de la propagación, porque la fase de captura es raramente usada. -In fact, the capturing phase was invisible for us, because handlers added using `on<event>`-property or using HTML attributes or using two-argument `addEventListener(event, handler)` don't know anything about capturing, they only run on the 2nd and 3rd phases. +De hecho, la fase de captura es invisible para nosotros, porque los manejadores agregados que usan la propiedad `on<event>`, ó usan atributos HTML, ó `addEventListener(event, handler)` de dos argumentos, no ven la fase de captura, únicamente se ejecutan en la 2da y 3ra fase. -To catch an event on the capturing phase, we need to set the handler `capture` option to `true`: +Para atrapar un evento en la fase de captura, necesitamos preparar la opción `capture` como `true` en el manejador: ```js elem.addEventListener(..., {capture: true}) -// or, just "true" is an alias to {capture: true} +// o solamente "true". Es una forma más corta de {capture: true} elem.addEventListener(..., true) ``` -There are two possible values of the `capture` option: +Hay dos posibles valores para la opción `capture`: -- If it's `false` (default), then the handler is set on the bubbling phase. -- If it's `true`, then the handler is set on the capturing phase. +- Si es `false` (por defecto), entonces el manejador es preparado para la fase de propagación. +- Si es `true`, entonces el manejador es preparado para la fase de captura. -Note that while formally there are 3 phases, the 2nd phase ("target phase": the event reached the element) is not handled separately: handlers on both capturing and bubbling phases trigger at that phase. +Es de notar que mientras formalmente hay 3 fases, la 2da fase ("la fase de objetivo": el evento alcanzó el elemento) no es manejada de forma separada; los manejadores en ambas fases, la de captura y propagación, se disparan en esa fase. -Let's see both capturing and bubbling in action: +Veamos ambas fases, captura y propagación, en acción: ```html run autorun height=140 edit <style> @@ -171,63 +170,63 @@ Let's see both capturing and bubbling in action: <script> for(let elem of document.querySelectorAll('*')) { - elem.addEventListener("click", e => alert(`Capturing: ${elem.tagName}`), true); - elem.addEventListener("click", e => alert(`Bubbling: ${elem.tagName}`)); + elem.addEventListener("click", e => alert(`Captura: ${elem.tagName}`), true); + elem.addEventListener("click", e => alert(`Propagación: ${elem.tagName}`)); } </script> ``` -The code sets click handlers on *every* element in the document to see which ones are working. +El código prepara manejadores de clic en *cada* elemento en el documento para ver cuáles están funcionando. -If you click on `<p>`, then the sequence is: +Si haces clic en `<p>`, verás que la secuencia es: -1. `HTML` -> `BODY` -> `FORM` -> `DIV -> P` (capturing phase, the first listener): -2. `P` -> `DIV` -> `FORM` -> `BODY` -> `HTML` (bubbling phase, the second listener). +1. `HTML` -> `BODY` -> `FORM` -> `DIV` (fase de captura, el primer detector o "listener"): +2. `P` -> `DIV` -> `FORM` -> `BODY` -> `HTML` (fase de propagación, el segundo detector). -Please note, the `P` shows up twice, because we've set two listeners: capturing and bubbling. The target triggers at the end of the first and at the beginning of the second phase. +Nota que `P` aparece dos veces, porque establecimos dos listeners: captura y propagación. Se disparan en el objetivo al final de la primera fase y al principio de la segunda fase. -There's a property `event.eventPhase` that tells us the number of the phase on which the event was caught. But it's rarely used, because we usually know it in the handler. +Hay un propiedad `event.eventPhase` que nos dice el número de fase en la que el evento fue capturado. Pero es raramente usada, ya que usualmente lo sabemos en el manejador. -```smart header="To remove the handler, `removeEventListener` needs the same phase" -If we `addEventListener(..., true)`, then we should mention the same phase in `removeEventListener(..., true)` to correctly remove the handler. +```smart header="Para quitar el manejador, `removeEventListener` necesita la misma fase" +Si agregamos `addEventListener(..., true)`, entonces debemos mencionar la misma fase en `removeEventListener(..., true)` para remover el manejador correctamente. ``` -````smart header="Listeners on the same element and same phase run in their set order" -If we have multiple event handlers on the same phase, assigned to the same element with `addEventListener`, they run in the same order as they are created: +````smart header="Detectores de eventos en el mismo elemento y en la misma fase se ejecutan en el orden de asignación" +Si tenemos múltiples manejadores de eventos en la misma fase, asignados al mismo elemento con `addEventListener`, se ejecutarán en el orden que fueron creados: ```js -elem.addEventListener("click", e => alert(1)); // guaranteed to trigger first +elem.addEventListener("click", e => alert(1)); // garantizado que se ejecutará primero elem.addEventListener("click", e => alert(2)); ``` ```` -```smart header="The `event.stopPropagation()` during the capturing also prevents the bubbling" -The `event.stopPropagation()` method and its sibling `event.stopImmediatePropagation()` can also be called on the capturing phase. Then not only the futher capturing is stopped, but the bubbling as well. +```smart header="`event.stopPropagation()` durante la captura también impide la propagación" +El método `event.stopPropagation()` y su hermano `event.stopImmediatePropagation()` también pueden ser llamados en la fase de captura. En este caso no solo se detienen las capturas sino también la propagación. -In other words, normally the event goes first down ("capturing") and then up ("bubbling"). But if `event.stopPropagation()` is called during the capturing phase, then the event travel stops, no bubbling will occur. +En otras palabras, normalmente el evento primero va hacia abajo ("captura") y luego hacia arriba ("propagación"). Pero si se llama a `event.stopPropagation()` durante la fase de captura, se detiene la travesía del evento, y la propagación no volverá a ocurrir. ``` -## Summary +## Resumen -When an event happens -- the most nested element where it happens gets labeled as the "target element" (`event.target`). +Cuando ocurre un evento, el elemento más anidado dónde ocurrió se reconoce como el "elemento objetivo" (`event.target`). -- Then the event moves down from the document root to `event.target`, calling handlers assigned with `addEventListener(..., true)` on the way (`true` is a shorthand for `{capture: true}`). -- Then handlers are called on the target element itself. -- Then the event bubbles up from `event.target` to the root, calling handlers assigned using `on<event>`, HTML attributes and `addEventListener` without the 3rd argument or with the 3rd argument `false/{capture:false}`. +- Luego el evento se mueve hacia abajo desde el documento raíz hacia `event.target`, llamando a los manejadores en el camino asignados con `addEventListener(..., true)` (`true` es una abreviación para `{capture: true}`). +- Luego los manejadores son llamados en el elemento objetivo mismo. +- Luego el evento se propaga desde `event.target` hacia la raíz, llamando a los manejadores que se asignaron usando `on<event>`, atributos HTML y `addEventListener` sin el 3er argumento o con el 3er argumento `false/{capture:false}`. -Each handler can access `event` object properties: +Cada manejador puede acceder a las propiedades del objeto `event`: -- `event.target` -- the deepest element that originated the event. -- `event.currentTarget` (=`this`) -- the current element that handles the event (the one that has the handler on it) -- `event.eventPhase` -- the current phase (capturing=1, target=2, bubbling=3). +- `event.target` -- el elemento más profundo que originó el evento. +- `event.currentTarget` (=`this`) -- el elemento actual que maneja el evento (el que tiene al manejador en él) +- `event.eventPhase` -- la fase actual (captura=1, objetivo=2, propagación=3). -Any event handler can stop the event by calling `event.stopPropagation()`, but that's not recommended, because we can't really be sure we won't need it above, maybe for completely different things. +Cualquier manejador de evento puede detener el evento al llamar `event.stopPropagation()`, pero no es recomendado porque no podemos realmente asegurar que no lo necesitaremos más adelante, quizá para completar diferentes cosas. -The capturing phase is used very rarely, usually we handle events on bubbling. And there's a logical explanation for that. +La fase de captura raramente es usada, usualmente manejamos los eventos en la propagación. Y hay una explicación lógica para ello. -In real world, when an accident happens, local authorities react first. They know best the area where it happened. Then higher-level authorities if needed. +En el mundo real, cuando un accidente ocurre, las autoridades locales reaccionan primero. Ellos conocen mejor el área dónde ocurrió. Luego, si es necesario, las autoridades de alto nivel. -The same for event handlers. The code that set the handler on a particular element knows maximum details about the element and what it does. A handler on a particular `<td>` may be suited for that exactly `<td>`, it knows everything about it, so it should get the chance first. Then its immediate parent also knows about the context, but a little bit less, and so on till the very top element that handles general concepts and runs the last one. +Lo mismo para los manejadores de eventos. El código que se prepara en el manejador de un elemento en particular conoce el máximo de detalles sobre el elemento y qué hace. Un manejador en un `<td>` particular puede ser adecuado para ese exacto `<td>`, conocer todo sobre él, entonces debe tener su oportunidad primero. Luego su padre inmediato también conoce sobre el contexto, pero un poco menos, y así sucesivamente hasta el elemento de arriba que maneja conceptos generales y se ejecuta al final. -Bubbling and capturing lay the foundation for "event delegation" -- an extremely powerful event handling pattern that we study in the next chapter. +La propagación y captura ponen los cimientos para "delegación de eventos": un extremadamente poderoso patrón de manejo de eventos que se estudia en el siguiente capítulo. diff --git a/2-ui/2-events/02-bubbling-and-capturing/bubble-target.view/index.html b/2-ui/2-events/02-bubbling-and-capturing/bubble-target.view/index.html index 8313ec29f..31e3573e6 100644 --- a/2-ui/2-events/02-bubbling-and-capturing/bubble-target.view/index.html +++ b/2-ui/2-events/02-bubbling-and-capturing/bubble-target.view/index.html @@ -7,7 +7,7 @@ </head> <body> - A click shows both <code>event.target</code> and <code>this</code> to compare: + Un clic muestra ambos, el <code>event.target</code> y <code>this</code> para comparar: <form id="form">FORM <div>DIV diff --git a/2-ui/2-events/02-bubbling-and-capturing/event-order-bubbling.svg b/2-ui/2-events/02-bubbling-and-capturing/event-order-bubbling.svg index 2ea88f081..270a5ac65 100644 --- a/2-ui/2-events/02-bubbling-and-capturing/event-order-bubbling.svg +++ b/2-ui/2-events/02-bubbling-and-capturing/event-order-bubbling.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="353" height="216" viewBox="0 0 353 216"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="event-order-bubbling.svg"><path id="Rectangle-210" fill="#DBAF88" d="M159.488 140L174 186H60l14.512-46z"/><path id="Rectangle-209" stroke="#91C2A3" stroke-width="18" d="M173.634 81l16.09 51H43.276l16.09-51h114.267z"/><path id="Rectangle-208" stroke="#EFA39F" stroke-width="18" d="M193.014 29l22.71 72H17.276l22.71-72h153.028z"/><path id="Fill-46" fill="#166388" d="M121.5 141v13.816a4.5 4.5 0 11-9 0V141h9zm0-31v13h-9v-13h9zM117 20.53a4.471 4.471 0 013.362 1.3l17.64 17.64a4.5 4.5 0 01-6.364 6.364L121.5 35.698V92h-9V35.7l-10.138 10.136a4.5 4.5 0 11-6.363-6.364l17.639-17.64a4.477 4.477 0 013.363-1.3z"/><text id="1" fill="#643B0C" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="210" y="105">1</tspan></text><text id="2" fill="#643B0C" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="185" y="136">2</tspan></text><text id="3" fill="#C06334" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="157" y="181">3</tspan></text><text id="Most-deeply-nested-e" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="233.48" y="175">Most deeply</tspan> <tspan x="221.306" y="194">nested element</tspan></text><path id="Line-30" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M179.5 177.5h30"/></g></g></svg> \ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" width="353" height="216" viewBox="0 0 353 216"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="event-order-bubbling.svg"><path id="Rectangle-210" fill="#DBAF88" d="M159.488 140L174 186H60l14.512-46z"/><path id="Rectangle-209" stroke="#91C2A3" stroke-width="18" d="M173.634 81l16.09 51H43.276l16.09-51h114.267z"/><path id="Rectangle-208" stroke="#EFA39F" stroke-width="18" d="M39.986 29h153.028l22.71 72H17.276l22.71-72z"/><path id="Fill-46" fill="#166388" d="M121.5 141v13.816a4.5 4.5 0 11-9 0V141h9zm0-31v13h-9v-13h9zM117 20.53a4.471 4.471 0 013.362 1.3l17.64 17.64a4.5 4.5 0 01-6.364 6.364L121.5 35.698V92h-9V35.7l-10.138 10.136a4.5 4.5 0 11-6.363-6.364l17.639-17.64a4.477 4.477 0 013.363-1.3z"/><text id="1" fill="#643B0C" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="210" y="105">1</tspan></text><text id="2" fill="#643B0C" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="185" y="136">2</tspan></text><text id="3" fill="#C06334" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="157" y="181">3</tspan></text><text id="Most-deeply-nested-e" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="227.48" y="175">Más profundo</tspan> <tspan x="212.306" y="194">elemento anidado</tspan></text><path id="Line-30" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M179.5 177.5h30"/></g></g></svg> \ No newline at end of file diff --git a/2-ui/2-events/03-event-delegation/1-hide-message-delegate/source.view/index.html b/2-ui/2-events/03-event-delegation/1-hide-message-delegate/source.view/index.html index 8a9ed9a09..218ea1e36 100644 --- a/2-ui/2-events/03-event-delegation/1-hide-message-delegate/source.view/index.html +++ b/2-ui/2-events/03-event-delegation/1-hide-message-delegate/source.view/index.html @@ -28,7 +28,7 @@ <h3>Cat</h3> </div> <script> - // ...your code... + // ... tu código... </script> </body> diff --git a/2-ui/2-events/03-event-delegation/1-hide-message-delegate/task.md b/2-ui/2-events/03-event-delegation/1-hide-message-delegate/task.md index 62c0a8ab0..1b29127e2 100644 --- a/2-ui/2-events/03-event-delegation/1-hide-message-delegate/task.md +++ b/2-ui/2-events/03-event-delegation/1-hide-message-delegate/task.md @@ -2,12 +2,12 @@ importance: 5 --- -# Hide messages with delegation +# Ocultar mensajes con delegación -There's a list of messages with removal buttons `[x]`. Make the buttons work. +Hay una lista de mensajes con botones para borrarlos `[x]`. Haz que funcionen. -Like this: +Como esto: [iframe src="solution" height=420] -P.S. Should be only one event listener on the container, use event delegation. +P.D. Debe haber solamente un _event lintener_ en el contenedor, usa delegación de eventos. diff --git a/2-ui/2-events/03-event-delegation/2-sliding-tree/solution.md b/2-ui/2-events/03-event-delegation/2-sliding-tree/solution.md index 09c14a08c..2ef1d81c5 100644 --- a/2-ui/2-events/03-event-delegation/2-sliding-tree/solution.md +++ b/2-ui/2-events/03-event-delegation/2-sliding-tree/solution.md @@ -1,4 +1,4 @@ -The solution has two parts. +La solución tiene dos partes. -1. Wrap every tree node title into `<span>`. Then we can CSS-style them on `:hover` and handle clicks exactly on text, because `<span>` width is exactly the text width (unlike without it). -2. Set a handler to the `tree` root node and handle clicks on that `<span>` titles. +1. Envuelve cada nodo de título del árbol dentro de `<span>`. Luego podemos aplicarles CSS-style en `:hover` y manejar los clics exactamente sobre el texto, porque el ancho de `<span>` es exactamente el ancho del texto (no lo será si no lo tiene). +2. Establece el manejador al nodo raíz del `tree` y maneja los clics en aquellos títulos `<span>`. diff --git a/2-ui/2-events/03-event-delegation/2-sliding-tree/solution.view/index.html b/2-ui/2-events/03-event-delegation/2-sliding-tree/solution.view/index.html index 0e85e457e..0e6165bfb 100644 --- a/2-ui/2-events/03-event-delegation/2-sliding-tree/solution.view/index.html +++ b/2-ui/2-events/03-event-delegation/2-sliding-tree/solution.view/index.html @@ -54,15 +54,15 @@ </ul> <script> - // move all text into <span> - // they occupy exactly the place necessary for the text, + // mueve todo el texto dentro de <span> + // esto ocupa exactamente lo necesario para el texto, for (let li of tree.querySelectorAll('li')) { let span = document.createElement('span'); li.prepend(span); - span.append(span.nextSibling); // move the text node into span + span.append(span.nextSibling); // mueve el nodo texto dentro de span } - // catch clicks on whole tree + // atrapa clics en el árbol entero tree.onclick = function(event) { if (event.target.tagName != 'SPAN') { @@ -70,7 +70,7 @@ } let childrenContainer = event.target.parentNode.querySelector('ul'); - if (!childrenContainer) return; // no children + if (!childrenContainer) return; // sin hijos childrenContainer.hidden = !childrenContainer.hidden; } diff --git a/2-ui/2-events/03-event-delegation/2-sliding-tree/task.md b/2-ui/2-events/03-event-delegation/2-sliding-tree/task.md index bdcf2a510..42fdffa79 100644 --- a/2-ui/2-events/03-event-delegation/2-sliding-tree/task.md +++ b/2-ui/2-events/03-event-delegation/2-sliding-tree/task.md @@ -2,13 +2,13 @@ importance: 5 --- -# Tree menu +# Menú de árbol -Create a tree that shows/hides node children on click: +Crea un árbol que muestre y oculte nodos hijos con clics: [iframe border=1 src="solution"] -Requirements: +Requerimientos: -- Only one event handler (use delegation) -- A click outside the node title (on an empty space) should not do anything. +- Solamente un manejador de eventos (usa delegación) +- Un clic fuera de los nodos de títulos (en un espacio vacío) no debe hacer nada. diff --git a/2-ui/2-events/03-event-delegation/3-sortable-table/solution.view/index.html b/2-ui/2-events/03-event-delegation/3-sortable-table/solution.view/index.html index d5d2d640b..20c6bfd7b 100644 --- a/2-ui/2-events/03-event-delegation/3-sortable-table/solution.view/index.html +++ b/2-ui/2-events/03-event-delegation/3-sortable-table/solution.view/index.html @@ -59,10 +59,10 @@ if (e.target.tagName != 'TH') return; let th = e.target; - // if TH, then sort - // cellIndex is the number of th: - // 0 for the first column - // 1 for the second column, etc + // si TH, entonces ordena + // cellIndex es el número de th: + // 0 para la primera columna + // 1 para la segunda columna, etc. sortGrid(th.cellIndex, th.dataset.type); }; @@ -71,7 +71,7 @@ let rowsArray = Array.from(tbody.rows); - // compare(a, b) compares two rows, need for sorting + // compare(a, b) compara dos filas, necesario para ordenar let compare; switch (type) { diff --git a/2-ui/2-events/03-event-delegation/3-sortable-table/source.view/index.html b/2-ui/2-events/03-event-delegation/3-sortable-table/source.view/index.html index 8fac06246..3e55e6fe9 100644 --- a/2-ui/2-events/03-event-delegation/3-sortable-table/source.view/index.html +++ b/2-ui/2-events/03-event-delegation/3-sortable-table/source.view/index.html @@ -54,7 +54,7 @@ </table> <script> - // ...your code... + // ... tu código... </script> </body> diff --git a/2-ui/2-events/03-event-delegation/3-sortable-table/task.md b/2-ui/2-events/03-event-delegation/3-sortable-table/task.md index ec85a473c..e0f31505c 100644 --- a/2-ui/2-events/03-event-delegation/3-sortable-table/task.md +++ b/2-ui/2-events/03-event-delegation/3-sortable-table/task.md @@ -2,11 +2,11 @@ importance: 4 --- -# Sortable table +# Tabla ordenable -Make the table sortable: clicks on `<th>` elements should sort it by corresponding column. +Haz que la tabla se pueda ordenar: los clics en elementos `<th>` deberían ordenarla por la columna correspondiente. -Each `<th>` has the type in the attribute, like this: +Cada `<th>` tiene su tipo de datos en el atributo, como esto: ```html <table id="grid"> @@ -32,12 +32,12 @@ Each `<th>` has the type in the attribute, like this: </table> ``` -In the example above the first column has numbers, and the second one -- strings. The sorting function should handle sort according to the type. +En el ejemplo anterior la primera columna tiene números y la segunda cadenas. La función de ordenamiento debe manejar el orden de acuerdo al tipo de dato. -Only `"string"` and `"number"` types should be supported. +Solamente los tipos `"string"` y `"number"` deben ser soportados. -The working example: +Ejemplo en funcionamiento: [iframe border=1 src="solution" height=190] -P.S. The table can be big, with any number of rows and columns. +P.D. La tabla puede ser grande, con cualquier cantidad de filas y columnas. diff --git a/2-ui/2-events/03-event-delegation/4-behavior-tooltip/solution.view/index.html b/2-ui/2-events/03-event-delegation/4-behavior-tooltip/solution.view/index.html index 1ef37cdc9..22d03896d 100644 --- a/2-ui/2-events/03-event-delegation/4-behavior-tooltip/solution.view/index.html +++ b/2-ui/2-events/03-event-delegation/4-behavior-tooltip/solution.view/index.html @@ -6,7 +6,7 @@ <style> body { height: 2000px; - /* make body scrollable, the tooltip should work after the scroll */ + /* hacer body desplazable, el tooltip debe funcionar después del desplazamiento */ } .tooltip { @@ -28,10 +28,10 @@ <p>LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa</p> <p>LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa</p> - <button data-tooltip="the tooltip is longer than the element">Short button</button> - <button data-tooltip="HTML<br>tooltip">One more button</button> + <button data-tooltip="the tooltip is longer than the element">Botón corto</button> + <button data-tooltip="HTML<br>tooltip">Un botón más</button> - <p>Scroll the page to make buttons appear on the top, check if the tooltips show up correctly.</p> + <p>Desplaza la página para que los botones aparezcan arriba de todo, verifica que los tooltips se muestren correctamente.</p> <script> @@ -40,25 +40,25 @@ document.onmouseover = function(event) { let target = event.target; - // if we have tooltip HTML... + // si tenemos HTML de tooltip... let tooltipHtml = target.dataset.tooltip; if (!tooltipHtml) return; - // ...create the tooltip element + // ...crear el elemento tooltip tooltipElem = document.createElement('div'); tooltipElem.className = 'tooltip'; tooltipElem.innerHTML = tooltipHtml; document.body.append(tooltipElem); - // position it above the annotated element (top-center) + // posicionarlo arriba del elemento (top-center) let coords = target.getBoundingClientRect(); let left = coords.left + (target.offsetWidth - tooltipElem.offsetWidth) / 2; - if (left < 0) left = 0; // don't cross the left window edge + if (left < 0) left = 0; // no cruzar el borde izquierdo de la ventana let top = coords.top - tooltipElem.offsetHeight - 5; - if (top < 0) { // if crossing the top window edge, show below instead + if (top < 0) { // en cambio, si cruza el borde superior de la ventana, mostrarlo debajo top = coords.top + target.offsetHeight + 5; } diff --git a/2-ui/2-events/03-event-delegation/4-behavior-tooltip/source.view/index.html b/2-ui/2-events/03-event-delegation/4-behavior-tooltip/source.view/index.html index add3b2144..d1edf845c 100644 --- a/2-ui/2-events/03-event-delegation/4-behavior-tooltip/source.view/index.html +++ b/2-ui/2-events/03-event-delegation/4-behavior-tooltip/source.view/index.html @@ -6,11 +6,11 @@ <style> body { height: 2000px; - /* make body scrollable, the tooltip should work after the scroll */ + /* hacer body desplazable, el tooltip debe funcionar después del desplazamiento */ } .tooltip { - /* some styles for the tooltip, you can use your own instead */ + /* estilos del tooltip, puedes usar uno tuyo en su lugar*/ position: fixed; padding: 10px 20px; border: 1px solid #b3c9ce; @@ -29,14 +29,14 @@ <p>LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa</p> <p>LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa</p> - <button data-tooltip="the tooltip is longer than the element">Short button</button> - <button data-tooltip="HTML<br>tooltip">One more button</button> + <button data-tooltip="the tooltip is longer than the element">Botón corto</button> + <button data-tooltip="HTML<br>tooltip">Un botón más</button> - <p>Scroll the page to make buttons appear on the top, check if the tooltips show up correctly.</p> + <p>Desplaza la página para que los botones aparezcan arriba de todo, verifica que los tooltips se muestren correctamente.</p> <script> - // ...your code... + // ... tu código... </script> </body> diff --git a/2-ui/2-events/03-event-delegation/4-behavior-tooltip/task.md b/2-ui/2-events/03-event-delegation/4-behavior-tooltip/task.md index 3001b9915..ff50e6aa1 100644 --- a/2-ui/2-events/03-event-delegation/4-behavior-tooltip/task.md +++ b/2-ui/2-events/03-event-delegation/4-behavior-tooltip/task.md @@ -2,37 +2,37 @@ importance: 5 --- -# Tooltip behavior +# Comportamiento: Tooltip -Create JS-code for the tooltip behavior. +Crea código JS para el comportamiento "tooltip". -When a mouse comes over an element with `data-tooltip`, the tooltip should appear over it, and when it's gone then hide. +Cuando un mouse pasa sobre un elemento con `data-tooltip`, el tooltip debe aparecer sobre él, y ocultarse cuando se va. -An example of annotated HTML: +Un ejemplo en HTML comentado: ```html <button data-tooltip="the tooltip is longer than the element">Short button</button> <button data-tooltip="HTML<br>tooltip">One more button</button> ``` -Should work like this: +Debe funcionar así: [iframe src="solution" height=200 border=1] -In this task we assume that all elements with `data-tooltip` have only text inside. No nested tags (yet). +En esta tarea suponemos que todos los elementos con `data-tooltip` solo tienen texto dentro. Sin tags anidados (todavía). -Details: +Detalles: -- The distance between the element and the tooltip should be `5px`. -- The tooltip should be centered relative to the element, if possible. -- The tooltip should not cross window edges. Normally it should be above the element, but if the element is at the page top and there's no space for the tooltip, then below it. -- The tooltip content is given in the `data-tooltip` attribute. It can be arbitrary HTML. +- La distancia entre el elemento y el tooltip debe ser `5px`. +- El tooltip debe ser centrado relativo al elemento si es posible. +- El tooltip no debe cruzar los bordes de la ventana. Normalmente debería estar sobre el elemento, pero si el elemento está en la parte superior de la página y no hay espacio para el tooltip, entonces debajo de él. +- El contenido del tooltip está dado en el atributo `data-tooltip`. Este puede ser HTML arbitrario. -You'll need two events here: -- `mouseover` triggers when a pointer comes over an element. -- `mouseout` triggers when a pointer leaves an element. +Necesitarás dos eventos aquí: +- `mouseover` se dispara cuando el puntero pasa sobre el elemento. +- `mouseout` se dispara cuando el puntero deja el elemento. -Please use event delegation: set up two handlers on `document` to track all "overs" and "outs" from elements with `data-tooltip` and manage tooltips from there. +Usa delegación de eventos: prepare dos manejadores en el `document` para rastrear todos los "overs" y "outs" de los elementos con `data-tooltip` y administra los tooltips desde allí. -After the behavior is implemented, even people unfamiliar with JavaScript can add annotated elements. +Después de implementar el comportamiento, incluso gente no familiarizada con JavaScript puede agregar elementos anotados. -P.S. Only one tooltip may show up at a time. +P.D. Solamente un tooltip puede mostrarse a la vez. diff --git a/2-ui/2-events/03-event-delegation/article.md b/2-ui/2-events/03-event-delegation/article.md index 881183740..bd14f0a76 100644 --- a/2-ui/2-events/03-event-delegation/article.md +++ b/2-ui/2-events/03-event-delegation/article.md @@ -1,19 +1,19 @@ -# Event delegation +# Delegación de eventos -Capturing and bubbling allow us to implement one of the most powerful event handling patterns called *event delegation*. +La captura y el propagación nos permiten implementar uno de los más poderosos patrones de manejo de eventos llamado *delegación de eventos*. -The idea is that if we have a lot of elements handled in a similar way, then instead of assigning a handler to each of them -- we put a single handler on their common ancestor. +La idea es que si tenemos muchos elementos manejados de manera similar podemos, en lugar de asignar un manejador a cada uno de ellos, poner un único manejador a su ancestro común. -In the handler we get `event.target` to see where the event actually happened and handle it. +En el manejador obtenemos `event.target` para ver dónde ocurrió realmente el evento y manejarlo. -Let's see an example -- the [Ba-Gua diagram](http://en.wikipedia.org/wiki/Ba_gua) reflecting the ancient Chinese philosophy. +Veamos un ejemplo: El [diagrama Pa kua](https://es.wikipedia.org/wiki/Pa_kua) que refleja la antigua filosofía china. -Here it is: +Aquí está: [iframe height=350 src="bagua" edit link] -The HTML is like this: +El HTML es este: ```html <table> @@ -30,45 +30,45 @@ The HTML is like this: </table> ``` -The table has 9 cells, but there could be 99 or 9999, doesn't matter. +La tabla tiene 9 celdas, pero puede haber 99 o 999, eso no importa. -**Our task is to highlight a cell `<td>` on click.** +**Nuestra tarea es destacar una celda `<td>` al hacer clic en ella.** -Instead of assign an `onclick` handler to each `<td>` (can be many) -- we'll setup the "catch-all" handler on `<table>` element. +En lugar de asignar un manejador `onclick` a cada `<td>` (puede haber muchos), configuramos un manejador "atrapa-todo" en el elemento `<table>`. -It will use `event.target` to get the clicked element and highlight it. +Este usará `event.target` para obtener el elemento del clic y destacarlo. -The code: +El código: ```js let selectedTd; *!* table.onclick = function(event) { - let target = event.target; // where was the click? + let target = event.target; // ¿dónde fue el clic? - if (target.tagName != 'TD') return; // not on TD? Then we're not interested + if (target.tagName != 'TD') return; // ¿no es un TD? No nos interesa - highlight(target); // highlight it + highlight(target); // destacarlo }; */!* function highlight(td) { - if (selectedTd) { // remove the existing highlight if any + if (selectedTd) { // quitar cualquier celda destacada que hubiera antes selectedTd.classList.remove('highlight'); } selectedTd = td; - selectedTd.classList.add('highlight'); // highlight the new td + selectedTd.classList.add('highlight'); // y destacar el nuevo td } ``` -Such a code doesn't care how many cells there are in the table. We can add/remove `<td>` dynamically at any time and the highlighting will still work. +A tal código no le interesa cuántas celdas hay en la tabla. Podemos agregar y quitar `<td>` dinámicamente en cualquier momento y el realzado aún funcionará. -Still, there's a drawback. +Pero hay un inconveniente. -The click may occur not on the `<td>`, but inside it. +El clic puede ocurrir no sobre `<td>`, sino dentro de él. -In our case if we take a look inside the HTML, we can see nested tags inside `<td>`, like `<strong>`: +En nuestro caso, si miramos dentro del HTML, podemos ver tags anidados dentro de `<td>`, como `<strong>`: ```html <td> @@ -79,13 +79,13 @@ In our case if we take a look inside the HTML, we can see nested tags inside `<t </td> ``` -Naturally, if a click happens on that `<strong>` then it becomes the value of `event.target`. +Naturalmente, si el clic ocurre en `<strong>`, este se vuelve el valor de `event.target`. ![](bagua-bubble.svg) -In the handler `table.onclick` we should take such `event.target` and find out whether the click was inside `<td>` or not. +En el manejador `table.onclick` debemos tomar tal `event.target` e indagar si el clic fue dentro de `<td>` o no. -Here's the improved code: +Aquí el código mejorado: ```js table.onclick = function(event) { @@ -99,27 +99,27 @@ table.onclick = function(event) { }; ``` -Explanations: -1. The method `elem.closest(selector)` returns the nearest ancestor that matches the selector. In our case we look for `<td>` on the way up from the source element. -2. If `event.target` is not inside any `<td>`, then the call returns immediately, as there's nothing to do. -3. In case of nested tables, `event.target` may be a `<td>`, but lying outside of the current table. So we check if that's actually *our table's* `<td>`. -4. And, if it's so, then highlight it. +Explicación: +1. El método `elem.closest(selector)` devuelve el ancestro más cercano que coincide con el selector. En nuestro caso buscamos `<td>` hacia arriba desde el elemento de origen. +2. Si `event.target` no ocurrió dentro de algún `<td>`, el llamado retorna inmediatamente pues no hay nada que hacer. +3. En caso de tablas anidadas, `event.target` podría ser un `<td>`, pero fuera de la tabla actual. Entonces verificamos que sea realmente un `<td>` de *nuestra tabla*. +4. Y, si es así, destacarla. -As the result, we have a fast, efficient highlighting code, that doesn't care about the total number of `<td>` in the table. +Como resultado, tenemos un código de realzado rápido y eficiente al que no le afecta la cantidad total de `<td>` en la tabla. -## Delegation example: actions in markup +## Ejemplo de delegación: acciones en markup -There are other uses for event delegation. +Hay otros usos para la delegación de eventos. -Let's say, we want to make a menu with buttons "Save", "Load", "Search" and so on. And there's an object with methods `save`, `load`, `search`... How to match them? +Digamos que queremos hacer un menú con los botones "Save", "Load", "Search" y así. Y hay objetos con los métodos `save`, `load`, `search`... ¿Cómo asociarlos? -The first idea may be to assign a separate handler to each button. But there's a more elegant solution. We can add a handler for the whole menu and `data-action` attributes for buttons that has the method to call: +La primera idea podría ser asignar un controlador separado para cada botón. Pero hay una solución más elegante. Podemos agregar un controlador para el menú completo y un atributo `data-action` a los botones con el método a llamar: ```html <button *!*data-action="save"*/!*>Click to Save</button> ``` -The handler reads the attribute and executes the method. Take a look at the working example: +El manejador lee el atributo y ejecuta el método. Puedes ver el siguiente ejemplo en funcionamiento: ```html autorun height=60 run untrusted <div id="menu"> @@ -161,28 +161,28 @@ The handler reads the attribute and executes the method. Take a look at the work </script> ``` -Please note that `this.onClick` is bound to `this` in `(*)`. That's important, because otherwise `this` inside it would reference the DOM element (`elem`), not the `Menu` object, and `this[action]` would not be what we need. +Ten en cuenta que `this.onClick` está ligado a `this` en `(*)`. Esto es importante, porque de otra manera el `this` que está dentro haría referencia al elemento DOM (`elem`), no al objeto `Menu`, y `this[action]` no sería lo que necesitamos. -So, what advantages does delegation give us here? +Entonces, ¿qué ventajas nos ofrece la delegación aquí? -```compare -+ We don't need to write the code to assign a handler to each button. Just make a method and put it in the markup. -+ The HTML structure is flexible, we can add/remove buttons at any time. +```compare ++ No necesitamos escribir el código para asignar el manejador a cada botón. Simplemente hacer un método y ponerlo en el markup. ++ La estructura HTML es flexible, podemos agregar y quitar botones en cualquier momento. ``` -We could also use classes `.action-save`, `.action-load`, but an attribute `data-action` is better semantically. And we can use it in CSS rules too. +Podríamos usar clases `.action-save`, `.action-load`, pero un atributo `data-action` es mejor semánticamente. Y podemos usarlo con reglas CSS también. -## The "behavior" pattern +## El patrón "comportamiento" -We can also use event delegation to add "behaviors" to elements *declaratively*, with special attributes and classes. +También podemos usar delegación de eventos para agregar "comportamiento" a los elementos de forma *declarativa*, con atributos y clases especiales. -The pattern has two parts: -1. We add a custom attribute to an element that describes its behavior. -2. A document-wide handler tracks events, and if an event happens on an attributed element -- performs the action. +El patrón tiene dos partes: +1. Agregamos un atributo personalizado al elemento que describe su comportamiento. +2. Un manejador rastrea eventos del documento completo, y si un evento ocurre en un elemento con el atributo ejecuta la acción. -### Behavior: Counter +### Comportamiento: Contador -For instance, here the attribute `data-counter` adds a behavior: "increase value on click" to buttons: +Por ejemplo, aquí el atributo `data-counter` agrega un comportamiento: "incrementar el valor con un clic" a los botones: ```html run autorun height=60 Counter: <input type="button" value="1" data-counter> @@ -191,7 +191,7 @@ One more counter: <input type="button" value="2" data-counter> <script> document.addEventListener('click', function(event) { - if (event.target.dataset.counter != undefined) { // if the attribute exists... + if (event.target.dataset.counter != undefined) { // si el atributo existe... event.target.value++; } @@ -199,19 +199,19 @@ One more counter: <input type="button" value="2" data-counter> </script> ``` -If we click a button -- its value is increased. Not buttons, but the general approach is important here. +Si hacemos clic en un botón, su valor se incrementa. Lo importante aquí no son los botones sino el enfoque general. -There can be as many attributes with `data-counter` as we want. We can add new ones to HTML at any moment. Using the event delegation we "extended" HTML, added an attribute that describes a new behavior. +Puede haber tantos atributos `data-counter` como queramos. Podemos agregar nuevos al HTML en cualquier momento. Usando delegación de eventos "extendimos" el HTML, agregando un atributo que describe un nuevo comportamiento. -```warn header="For document-level handlers -- always `addEventListener`" -When we assign an event handler to the `document` object, we should always use `addEventListener`, not `document.on<event>`, because the latter will cause conflicts: new handlers overwrite old ones. +```warn header="Para manejadores de nivel de documento: siempre `addEventListener`" +Cuando asignamos un manejador de evento al objeto `document`, debemos usar siempre `addEventListener`, no `document.on<event>`, porque este último causa conflictos: los manejadores nuevos sobrescribirán los viejos. -For real projects it's normal that there are many handlers on `document` set by different parts of the code. +En proyectos reales es normal que haya muchos manejadores en `document`, asignados en diferentes partes del código. ``` -### Behavior: Toggler +### Comportamiento: Conmutador (toggle) -One more example of behavior. A click on an element with the attribute `data-toggle-id` will show/hide the element with the given `id`: +Un ejemplo más de comportamiento. Un clic en un elemento con el atributo `data-toggle-id` mostrará/ocultará el elemento con el `id` recibido: ```html autorun run height=60 <button *!*data-toggle-id="subscribe-mail"*/!*> @@ -236,37 +236,37 @@ One more example of behavior. A click on an element with the attribute `data-tog </script> ``` -Let's note once again what we did. Now, to add toggling functionality to an element -- there's no need to know JavaScript, just use the attribute `data-toggle-id`. +Veamos una vez más lo que hicimos aquí: ahora, para agregar la funcionalidad de conmutación a un elemento, no hay necesidad de conocer JavaScript, simplemente usamos el atributo `data-toggle-id`. -That may become really convenient -- no need to write JavaScript for every such element. Just use the behavior. The document-level handler makes it work for any element of the page. +Esto puede ser muy conveniente: no hay necesidad de escribir JavaScript para cada elemento. Simplemente usamos el comportamiento. El manejador a nivel de documento hace el trabajo para cualquier elemento de la página. -We can combine multiple behaviors on a single element as well. +Podemos combinar múltiples comportamientos en un único elemento también. -The "behavior" pattern can be an alternative to mini-fragments of JavaScript. +El patrón "comportamiento" puede ser una alternativa a los mini-fragmentos de JavaScript. -## Summary +## Resumen -Event delegation is really cool! It's one of the most helpful patterns for DOM events. +¡La delegación de eventos es verdaderamente fantástica! Es uno de los patrones más útiles entre los eventos DOM. -It's often used to add the same handling for many similar elements, but not only for that. +A menudo es usado para manejar elementos similares, pero no solamente para eso. -The algorithm: +El algoritmo: -1. Put a single handler on the container. -2. In the handler -- check the source element `event.target`. -3. If the event happened inside an element that interests us, then handle the event. +1. Pone un único manejador en el contenedor. +2. Dentro del manejador revisa el elemento de origen `event.target`. +3. Si el evento ocurrió dentro de un elemento que nos interesa, maneja el evento. -Benefits: +Beneficios: -```compare -+ Simplifies initialization and saves memory: no need to add many handlers. -+ Less code: when adding or removing elements, no need to add/remove handlers. -+ DOM modifications: we can mass add/remove elements with `innerHTML` and the like. +```compare ++ Simplifica la inicialización y ahorra memoria: no hay necesidad de agregar muchos controladores. ++ Menos código: cuando agregamos o quitamos elementos, no hay necesidad de agregar y quitar controladores. ++ Modificaciones del DOM: podemos agregar y quitar elementos en masa con `innerHTML` y similares. ``` -The delegation has its limitations of course: +La delegación tiene sus limitaciones por supuesto: -```compare -- First, the event must be bubbling. Some events do not bubble. Also, low-level handlers should not use `event.stopPropagation()`. -- Second, the delegation may add CPU load, because the container-level handler reacts on events in any place of the container, no matter whether they interest us or not. But usually the load is negligible, so we don't take it into account. +```compare +- Primero, el evento debe "propagarse". Algunos eventos no lo hacen. Además manejadores de bajo nivel no deben usar `event.stopPropagation()`. +- Segundo, la delegación puede agregar carga a la CPU, porque el controlador a nivel de contenedor reacciona a eventos en cualquier lugar del mismo, no importa si nos interesan o no. Pero usualmente la carga es imperceptible y no la tomamos en cuenta. ``` diff --git a/2-ui/2-events/04-default-browser-action/1-why-return-false-fails/solution.md b/2-ui/2-events/04-default-browser-action/1-why-return-false-fails/solution.md index 4d175ca01..e570bea4d 100644 --- a/2-ui/2-events/04-default-browser-action/1-why-return-false-fails/solution.md +++ b/2-ui/2-events/04-default-browser-action/1-why-return-false-fails/solution.md @@ -1,16 +1,16 @@ -When the browser reads the `on*` attribute like `onclick`, it creates the handler from its content. +Cuando el navegador lee un atributo `on*` como `onclick`, crea el controlador a partir de su contenido. -For `onclick="handler()"` the function will be: +Para `onclick="handler()"` la función será: ```js function(event) { - handler() // the content of onclick + handler() // el contenido de onclick } ``` -Now we can see that the value returned by `handler()` is not used and does not affect the result. +Ahora podemos ver que el valor devuelto por `handler()` no se usa y no afecta el resultado. -The fix is simple: +La solución es simple: ```html run <script> @@ -23,7 +23,7 @@ The fix is simple: <a href="https://w3.org" onclick="*!*return handler()*/!*">w3.org</a> ``` -Also we can use `event.preventDefault()`, like this: +También podemos usar `event.preventDefault()`, así: ```html run <script> diff --git a/2-ui/2-events/04-default-browser-action/1-why-return-false-fails/task.md b/2-ui/2-events/04-default-browser-action/1-why-return-false-fails/task.md index 9a5bb98ac..9af62afbc 100644 --- a/2-ui/2-events/04-default-browser-action/1-why-return-false-fails/task.md +++ b/2-ui/2-events/04-default-browser-action/1-why-return-false-fails/task.md @@ -2,9 +2,9 @@ importance: 3 --- -# Why "return false" doesn't work? +# ¿Por qué "return false" no funciona? -Why in the code below `return false` doesn't work at all? +¿Por qué en el código de abajo `return false` no funciona en absoluto? ```html autorun run <script> @@ -14,9 +14,9 @@ Why in the code below `return false` doesn't work at all? } </script> -<a href="https://w3.org" onclick="handler()">the browser will go to w3.org</a> +<a href="https://w3.org" onclick="handler()">el navegador irá a w3.org</a> ``` -The browser follows the URL on click, but we don't want it. +El navegador sigue la URL al hacer clic, pero no la queremos. -How to fix? +¿Como se arregla? diff --git a/2-ui/2-events/04-default-browser-action/2-catch-link-navigation/solution.md b/2-ui/2-events/04-default-browser-action/2-catch-link-navigation/solution.md index 25079cb8d..bf3346ff8 100644 --- a/2-ui/2-events/04-default-browser-action/2-catch-link-navigation/solution.md +++ b/2-ui/2-events/04-default-browser-action/2-catch-link-navigation/solution.md @@ -1,5 +1,5 @@ -That's a great use of the event delegation pattern. +Ese es un gran uso para el patrón de delegación de eventos. -In real life instead of asking we can send a "logging" request to the server that saves the information about where the visitor left. Or we can load the content and show it right in the page (if allowable). +En la vida real, en lugar de preguntar, podemos enviar una solicitud de "logging" al servidor que guarda la información sobre dónde se fue el visitante. O podemos cargar el contenido y mostrarlo directamente en la página (si está permitido). -All we need is to catch the `contents.onclick` and use `confirm` to ask the user. A good idea would be to use `link.getAttribute('href')` instead of `link.href` for the URL. See the solution for details. +Todo lo que necesitamos es capturar el `contents.onclick` y usar `confirm` para preguntar al usuario. Una buena idea sería usar `link.getAttribute('href')` en lugar de `link.href` para la URL. Consulte la solución para obtener más detalles. diff --git a/2-ui/2-events/04-default-browser-action/2-catch-link-navigation/solution.view/index.html b/2-ui/2-events/04-default-browser-action/2-catch-link-navigation/solution.view/index.html index 51ac0838b..39b533722 100644 --- a/2-ui/2-events/04-default-browser-action/2-catch-link-navigation/solution.view/index.html +++ b/2-ui/2-events/04-default-browser-action/2-catch-link-navigation/solution.view/index.html @@ -16,7 +16,7 @@ <fieldset id="contents"> <legend>#contents</legend> <p> - How about to read <a href="https://wikipedia.org">Wikipedia</a> or visit <a href="https://w3.org"><i>W3.org</i></a> and learn about modern standards? + ¿Que tal si leemos <a href="https://wikipedia.org">Wikipedia</a> o visitamos <a href="https://w3.org"><i>W3.org</i></a> y aprendemos sobre los estándares modernos? </p> </fieldset> @@ -24,7 +24,7 @@ contents.onclick = function(event) { function handleLink(href) { - let isLeaving = confirm(`Leave for ${href}?`); + let isLeaving = confirm(`¿Irse a ${href}?`); if (!isLeaving) return false; } diff --git a/2-ui/2-events/04-default-browser-action/2-catch-link-navigation/source.view/index.html b/2-ui/2-events/04-default-browser-action/2-catch-link-navigation/source.view/index.html index f0c934391..110a62d1b 100644 --- a/2-ui/2-events/04-default-browser-action/2-catch-link-navigation/source.view/index.html +++ b/2-ui/2-events/04-default-browser-action/2-catch-link-navigation/source.view/index.html @@ -16,7 +16,7 @@ <fieldset id="contents"> <legend>#contents</legend> <p> - How about to read <a href="https://wikipedia.org">Wikipedia</a> or visit <a href="https://w3.org"><i>W3.org</i></a> and learn about modern standards? + ¿Que tal si leemos <a href="https://wikipedia.org">Wikipedia</a> o visitamos <a href="https://w3.org"><i>W3.org</i></a> y aprendemos sobre los estándares modernos? </p> </fieldset> diff --git a/2-ui/2-events/04-default-browser-action/2-catch-link-navigation/task.md b/2-ui/2-events/04-default-browser-action/2-catch-link-navigation/task.md index 6ca456c2c..07e2482f1 100644 --- a/2-ui/2-events/04-default-browser-action/2-catch-link-navigation/task.md +++ b/2-ui/2-events/04-default-browser-action/2-catch-link-navigation/task.md @@ -2,15 +2,15 @@ importance: 5 --- -# Catch links in the element +# Captura enlaces en el elemento -Make all links inside the element with `id="contents"` ask the user if they really want to leave. And if they don't then don't follow. +Haz que todos los enlaces dentro del elemento con `id="contents"` pregunten al usuario si realmente quiere irse. Y si no quiere, no sigas. -Like this: +Así: [iframe height=100 border=1 src="solution"] -Details: +Detalles: -- HTML inside the element may be loaded or regenerated dynamically at any time, so we can't find all links and put handlers on them. Use event delegation. -- The content may have nested tags. Inside links too, like `<a href=".."><i>...</i></a>`. +- El HTML dentro del elemento puede cargarse o regenerarse dinámicamente en cualquier momento, por lo que no podemos encontrar todos los enlaces y ponerles controladores. Utilice la delegación de eventos. +- El contenido puede tener etiquetas anidadas. Dentro de los enlaces también, como `<a href=".."><i>...</i></a>`. diff --git a/2-ui/2-events/04-default-browser-action/3-image-gallery/solution.md b/2-ui/2-events/04-default-browser-action/3-image-gallery/solution.md index 5ff60b5c0..407ad45e5 100644 --- a/2-ui/2-events/04-default-browser-action/3-image-gallery/solution.md +++ b/2-ui/2-events/04-default-browser-action/3-image-gallery/solution.md @@ -1 +1 @@ -The solution is to assign the handler to the container and track clicks. If a click is on the `<a>` link, then change `src` of `#largeImg` to the `href` of the thumbnail. +La solución es asignar el controlador al contenedor y realizar un seguimiento de los clics. Si haces clic en el enlace `<a>`, cambias `src` de `#largeImg` por el `href` de la miniatura. diff --git a/2-ui/2-events/04-default-browser-action/3-image-gallery/solution.view/index.html b/2-ui/2-events/04-default-browser-action/3-image-gallery/solution.view/index.html index 524bc7152..02dc9bc9a 100644 --- a/2-ui/2-events/04-default-browser-action/3-image-gallery/solution.view/index.html +++ b/2-ui/2-events/04-default-browser-action/3-image-gallery/solution.view/index.html @@ -9,24 +9,24 @@ <body> - <p><img id="largeImg" src="https://en.js.cx/gallery/img1-lg.jpg" alt="Large image"></p> + <p><img id="largeImg" src="https://es.js.cx/gallery/img1-lg.jpg" alt="Imagen grande"></p> <ul id="thumbs"> - <!-- the browser shows a small built-in tooltip on hover with the text from "title" attribute --> + <!-- el navegadort tiene un pequeño tooltip incorporado que "on hover" muestra el texto del atributo "title" --> <li> - <a href="https://en.js.cx/gallery/img2-lg.jpg" title="Image 2"><img src="https://en.js.cx/gallery/img2-thumb.jpg"></a> + <a href="https://es.js.cx/gallery/img2-lg.jpg" title="Imagen 2"><img src="https://es.js.cx/gallery/img2-thumb.jpg"></a> </li> <li> - <a href="https://en.js.cx/gallery/img3-lg.jpg" title="Image 3"><img src="https://en.js.cx/gallery/img3-thumb.jpg"></a> + <a href="https://es.js.cx/gallery/img3-lg.jpg" title="Imagen 3"><img src="https://es.js.cx/gallery/img3-thumb.jpg"></a> </li> <li> - <a href="https://en.js.cx/gallery/img4-lg.jpg" title="Image 4"><img src="https://en.js.cx/gallery/img4-thumb.jpg"></a> + <a href="https://es.js.cx/gallery/img4-lg.jpg" title="Imagen 4"><img src="https://es.js.cx/gallery/img4-thumb.jpg"></a> </li> <li> - <a href="https://en.js.cx/gallery/img5-lg.jpg" title="Image 5"><img src="https://en.js.cx/gallery/img5-thumb.jpg"></a> + <a href="https://es.js.cx/gallery/img5-lg.jpg" title="Imagen 5"><img src="https://es.js.cx/gallery/img5-thumb.jpg"></a> </li> <li> - <a href="https://en.js.cx/gallery/img6-lg.jpg" title="Image 6"><img src="https://en.js.cx/gallery/img6-thumb.jpg"></a> + <a href="https://es.js.cx/gallery/img6-lg.jpg" title="Imagen 6"><img src="https://es.js.cx/gallery/img6-thumb.jpg"></a> </li> </ul> diff --git a/2-ui/2-events/04-default-browser-action/3-image-gallery/source.view/index.html b/2-ui/2-events/04-default-browser-action/3-image-gallery/source.view/index.html index c488ddcc2..83866900f 100644 --- a/2-ui/2-events/04-default-browser-action/3-image-gallery/source.view/index.html +++ b/2-ui/2-events/04-default-browser-action/3-image-gallery/source.view/index.html @@ -9,24 +9,24 @@ <body> - <p><img id="largeImg" src="https://en.js.cx/gallery/img1-lg.jpg" alt="Large image"></p> + <p><img id="largeImg" src="https://es.js.cx/gallery/img1-lg.jpg" alt="Large image"></p> <ul id="thumbs"> - <!-- the browser shows a small built-in tooltip on hover with the text from "title" attribute --> + <!-- el navegador muestra un tooltip (ya viene integrado) con el texto del atributo "title" al pasar el ratón sobre él --> <li> - <a href="https://en.js.cx/gallery/img2-lg.jpg" title="Image 2"><img src="https://en.js.cx/gallery/img2-thumb.jpg"></a> + <a href="https://es.js.cx/gallery/img2-lg.jpg" title="Image 2"><img src="https://es.js.cx/gallery/img2-thumb.jpg"></a> </li> <li> - <a href="https://en.js.cx/gallery/img3-lg.jpg" title="Image 3"><img src="https://en.js.cx/gallery/img3-thumb.jpg"></a> + <a href="https://es.js.cx/gallery/img3-lg.jpg" title="Image 3"><img src="https://es.js.cx/gallery/img3-thumb.jpg"></a> </li> <li> - <a href="https://en.js.cx/gallery/img4-lg.jpg" title="Image 4"><img src="https://en.js.cx/gallery/img4-thumb.jpg"></a> + <a href="https://es.js.cx/gallery/img4-lg.jpg" title="Image 4"><img src="https://es.js.cx/gallery/img4-thumb.jpg"></a> </li> <li> - <a href="https://en.js.cx/gallery/img5-lg.jpg" title="Image 5"><img src="https://en.js.cx/gallery/img5-thumb.jpg"></a> + <a href="https://es.js.cx/gallery/img5-lg.jpg" title="Image 5"><img src="https://es.js.cx/gallery/img5-thumb.jpg"></a> </li> <li> - <a href="https://en.js.cx/gallery/img6-lg.jpg" title="Image 6"><img src="https://en.js.cx/gallery/img6-thumb.jpg"></a> + <a href="https://es.js.cx/gallery/img6-lg.jpg" title="Image 6"><img src="https://es.js.cx/gallery/img6-thumb.jpg"></a> </li> </ul> diff --git a/2-ui/2-events/04-default-browser-action/3-image-gallery/task.md b/2-ui/2-events/04-default-browser-action/3-image-gallery/task.md index f7571cc80..443a3f3f6 100644 --- a/2-ui/2-events/04-default-browser-action/3-image-gallery/task.md +++ b/2-ui/2-events/04-default-browser-action/3-image-gallery/task.md @@ -2,12 +2,12 @@ importance: 5 --- -# Image gallery +# Galería de imágenes -Create an image gallery where the main image changes by the click on a thumbnail. +Crea una galería de imágenes donde la imagen principal cambia al hacer clic en una miniatura. -Like this: +Así: [iframe src="solution" height=600] -P.S. Use event delegation. +P.D. Utiliza la delegación de eventos. diff --git a/2-ui/2-events/04-default-browser-action/article.md b/2-ui/2-events/04-default-browser-action/article.md index cd815654f..e1b3ec396 100644 --- a/2-ui/2-events/04-default-browser-action/article.md +++ b/2-ui/2-events/04-default-browser-action/article.md @@ -1,43 +1,43 @@ -# Browser default actions +# Acciones predeterminadas del navegador -Many events automatically lead to certain actions performed by the browser. +Muchos eventos conducen automáticamente a determinadas acciones realizadas por el navegador. -For instance: +Por ejemplo: -- A click on a link - initiates navigation to its URL. -- A click on a form submit button - initiates its submission to the server. -- Pressing a mouse button over a text and moving it - selects the text. +- Un clic en un enlace: inicia la navegación a su URL. +- Un clic en el botón de envío de un formulario inicia su envío al servidor. +- Al presionar un botón del ratón sobre un texto y moverlo, se selecciona el texto. -If we handle an event in JavaScript, we may not want the corresponding browser action to happen, and want to implement another behavior instead. +Si manejamos un evento en JavaScript, es posible que no queramos que suceda la acción correspondiente del navegador e implementar en cambio otro comportamiento. -## Preventing browser actions +## Evitar las acciones del navegador -There are two ways to tell the browser we don't want it to act: +Hay dos formas de decirle al navegador que no queremos que actúe: -- The main way is to use the `event` object. There's a method `event.preventDefault()`. -- If the handler is assigned using `on<event>` (not by `addEventListener`), then returning `false` also works the same. +- La forma principal es utilizar el objeto `event`. Hay un método `event.preventDefault()`. +- Si el controlador se asigna usando `on<event>` (no por `addEventListener`), entonces devolver `false` también funciona igual. -In this HTML, a click on a link doesn't lead to navigation; the browser doesn't do anything: +En este HTML, un clic en un enlace no conduce a la navegación. El navegador no hace nada: ```html autorun height=60 no-beautify -<a href="/" onclick="return false">Click here</a> -or -<a href="/" onclick="event.preventDefault()">here</a> +<a href="/" onclick="return false">Haz clic aquí</a> +o +<a href="/" onclick="event.preventDefault()">aquí</a> ``` -In the next example we'll use this technique to create a JavaScript-powered menu. +En el siguiente ejemplo usaremos esta técnica para crear un menú basado en JavaScript. -```warn header="Returning `false` from a handler is an exception" -The value returned by an event handler is usually ignored. +```warn header="Regresar `false` desde un controlador es una excepción" +El valor devuelto por un controlador de eventos generalmente se ignora. -The only exception is `return false` from a handler assigned using `on<event>`. +La única excepción es `return false` de un controlador asignado usando `on<event>`. -In all other cases, `return` value is ignored. In particular, there's no sense in returning `true`. +En todos los demás casos, se ignora el valor `return`. En particular, no tiene sentido devolver `true`. ``` -### Example: the menu +### Ejemplo: el menú -Consider a site menu, like this: +Considere un menú de sitio, como este: ```html <ul id="menu" class="menu"> @@ -47,116 +47,116 @@ Consider a site menu, like this: </ul> ``` -Here's how it looks with some CSS: +Así es como se ve con algo de CSS: [iframe height=70 src="menu" link edit] -Menu items are implemented as HTML-links `<a>`, not buttons `<button>`. There are several reasons to do so, for instance: +Los elementos del menú se implementan como enlaces HTML `<a>`, no como botones `<botón>`. Hay varias razones para hacerlo, por ejemplo: -- Many people like to use "right click" -- "open in a new window". If we use `<button>` or `<span>`, that doesn't work. -- Search engines follow `<a href="...">` links while indexing. +- A muchas personas les gusta usar "clic derecho" -- "abrir en una nueva ventana". Si usamos `<button>` o `<span>`, eso no funciona. +- Los motores de búsqueda siguen los enlaces `<a href="...">` durante la indexación. -So we use `<a>` in the markup. But normally we intend to handle clicks in JavaScript. So we should prevent the default browser action. +Entonces usamos `<a>` en el markup. Pero normalmente pretendemos manejar clics en JavaScript. Por tanto, deberíamos evitar la acción predeterminada del navegador. -Like here: +Como aquí: ```js menu.onclick = function(event) { if (event.target.nodeName != 'A') return; let href = event.target.getAttribute('href'); - alert( href ); // ...can be loading from the server, UI generation etc + alert( href ); // ...se puede cargar desde el servidor, generación de interfaz de usuario, etc. *!* - return false; // prevent browser action (don't go to the URL) + return false; // evitar la acción del navegador (no vaya a la URL) */!* }; ``` -If we omit `return false`, then after our code executes the browser will do its "default action" -- navigating to the URL in `href`. And we don't need that here, as we're handling the click by ourselves. +Si omitimos `return false`, luego de ejecutar nuestro código el navegador realizará su "acción predeterminada": navegar a la URL en `href`. Y no lo necesitamos aquí, ya que estamos manejando el clic nosotros mismos. -By the way, using event delegation here makes our menu very flexible. We can add nested lists and style them using CSS to "slide down". +Por cierto, usar la delegación de eventos aquí hace que nuestro menú sea muy flexible. Podemos agregar listas anidadas y diseñarlas usando CSS para "deslizarlas hacia abajo". -````smart header="Follow-up events" -Certain events flow one into another. If we prevent the first event, there will be no second. +````smart header="Eventos de seguimiento" +Ciertos eventos fluyen unos a otros. Si evitamos el primer evento, no habrá segundo. -For instance, `mousedown` on an `<input>` field leads to focusing in it, and the `focus` event. If we prevent the `mousedown` event, there's no focus. +Por ejemplo, `mousedown` en un campo `<input>` conduce a enfocarse en él, y al evento `focus`. Si evitamos el evento `mousedown`, no hay enfoque. -Try to click on the first `<input>` below -- the `focus` event happens. But if you click the second one, there's no focus. +Intenta hacer clic en el primer `<input>` a continuación: se produce el evento `focus`. Pero si haces clic en el segundo, no hay enfoque. ```html run autorun -<input value="Focus works" onfocus="this.value=''"> -<input *!*onmousedown="return false"*/!* onfocus="this.value=''" value="Click me"> +<input value="Enfoque funciona" onfocus="this.value=''"> +<input *!*onmousedown="return false"*/!* onfocus="this.value=''" value="Haz clic en mí"> ``` -That's because the browser action is canceled on `mousedown`. The focusing is still possible if we use another way to enter the input. For instance, the `key:Tab` key to switch from the 1st input into the 2nd. But not with the mouse click any more. +Eso es porque la acción del navegador se cancela en `mousedown`. El enfoque aún es posible si usamos otra forma de ingresar la entrada. Por ejemplo, la tecla `key:Tab` para cambiar de la primera entrada a la segunda. Pero ya no con el clic del ratón. ```` -## The "passive" handler option +## La opción de controlador "pasivo" -The optional `passive: true` option of `addEventListener` signals the browser that the handler is not going to call `preventDefault()`. +La opción opcional `passive:true` de `addEventListener` indica al navegador que el controlador no llamará a `preventDefault()`. -Why might that be needed? +¿Para qué podría ser necesario? -There are some events like `touchmove` on mobile devices (when the user moves their finger across the screen), that cause scrolling by default, but that scrolling can be prevented using `preventDefault()` in the handler. +Hay algunos eventos como `touchmove` en dispositivos móviles (cuando el usuario mueve el dedo por la pantalla), que provocan el desplazamiento por defecto, pero ese desplazamiento se puede evitar usando `preventDefault()` en el controlador. -So when the browser detects such event, it has first to process all handlers, and then if `preventDefault` is not called anywhere, it can proceed with scrolling. That may cause unnecessary delays and "jitters" in the UI. +Entonces, cuando el navegador detecta tal evento, primero tiene que procesar todos los controladores, y luego, si no se llama a `preventDefault` en ninguna parte, puede continuar con el desplazamiento. Eso puede causar retrasos innecesarios y "movimientos de salto repentinos" en la interfaz de usuario. -The `passive: true` options tells the browser that the handler is not going to cancel scrolling. Then browser scrolls immediately providing a maximally fluent experience, and the event is handled by the way. +Las opciones `passive: true` le dicen al navegador que el controlador no va a cancelar el desplazamiento. Entonces el navegador se desplaza de inmediato para brindar una experiencia con la máxima fluidez, y el evento se maneja de inmediato. -For some browsers (Firefox, Chrome), `passive` is `true` by default for `touchstart` and `touchmove` events. +Para algunos navegadores (Firefox, Chrome), `passive` es `true` por defecto para los eventos `touchstart` y `touchmove`. ## event.defaultPrevented -The property `event.defaultPrevented` is `true` if the default action was prevented, and `false` otherwise. +La propiedad `event.defaultPrevented` es `true` si se impidió la acción predeterminada y `false` en caso contrario. -There's an interesting use case for it. +Hay un caso de uso interesante para ello. -You remember in the chapter <info:bubbling-and-capturing> we talked about `event.stopPropagation()` and why stopping bubbling is bad? +¿Recuerdas que en el capítulo <info:bubbling-and-capturing> hablamos sobre `event.stopPropagation()` y por qué detener propagación es malo? -Sometimes we can use `event.defaultPrevented` instead, to signal other event handlers that the event was handled. +A veces podemos usar `event.defaultPrevented` en su lugar, para señalar a otros controladores de eventos que el evento fue manejado. -Let's see a practical example. +Veamos un ejemplo práctico. -By default the browser on `contextmenu` event (right mouse click) shows a context menu with standard options. We can prevent it and show our own, like this: +Por defecto, el navegador en el evento `contextmenu` (clic derecho del ratón) muestra un menú contextual con opciones estándar. Podemos prevenirlo y mostrar el nuestro, así: ```html autorun height=50 no-beautify run -<button>Right-click shows browser context menu</button> +<button>El clic derecho muestra el menú contextual del navegador</button> -<button *!*oncontextmenu="alert('Draw our menu'); return false"*/!*> - Right-click shows our context menu +<button *!*oncontextmenu="alert('Dibuja nuestro menú'); return false"*/!*> + El clic derecho muestra nuestro menú contextual </button> ``` -Now, in addition to that context menu we'd like to implement document-wide context menu. +Ahora, además de ese menú contextual, nos gustaría implementar un menú contextual para todo el documento. -Upon right click, the closest context menu should show up. +Al hacer clic derecho, debería aparecer el menú contextual más cercano. ```html autorun height=80 no-beautify run -<p>Right-click here for the document context menu</p> -<button id="elem">Right-click here for the button context menu</button> +<p>Haz clic derecho aquí para el menú contextual del documento</p> +<button id="elem">Haz clic derecho aquí para el menú contextual del botón</button> <script> elem.oncontextmenu = function(event) { event.preventDefault(); - alert("Button context menu"); + alert("Menú contextual del botón"); }; document.oncontextmenu = function(event) { event.preventDefault(); - alert("Document context menu"); + alert("Menú contextual del documento"); }; </script> ``` -The problem is that when we click on `elem`, we get two menus: the button-level and (the event bubbles up) the document-level menu. +El problema es que cuando hacemos clic en `elem`, obtenemos dos menús: el de nivel de botón y (el evento emerge) el menú de nivel de documento. -How to fix it? One of solutions is to think like: "When we handle right-click in the button handler, let's stop its bubbling" and use `event.stopPropagation()`: +¿Como arreglarlo? Una de las soluciones es pensar así: "Cuando hagamos clic con el botón derecho en el controlador de botones, detengamos su propagación" y usemos `event.stopPropagation()`: ```html autorun height=80 no-beautify run -<p>Right-click for the document menu</p> -<button id="elem">Right-click for the button menu (fixed with event.stopPropagation)</button> +<p>Haz clic derecho para el menú del documento</p> +<button id="elem">Haz clic derecho para el menú del botón (arreglado con event.stopPropagation)</button> <script> elem.oncontextmenu = function(event) { @@ -164,29 +164,29 @@ How to fix it? One of solutions is to think like: "When we handle right-click in *!* event.stopPropagation(); */!* - alert("Button context menu"); + alert("Menú contextual del botón"); }; document.oncontextmenu = function(event) { event.preventDefault(); - alert("Document context menu"); + alert("Menú contextual del documento"); }; </script> ``` -Now the button-level menu works as intended. But the price is high. We forever deny access to information about right-clicks for any outer code, including counters that gather statistics and so on. That's quite unwise. +Ahora, el menú de nivel de botón funciona según lo previsto. Pero el precio es alto. Siempre negamos el acceso a la información sobre los clics con el botón derecho del ratón para cualquier código externo, incluidos los contadores que recopilan estadísticas, etc. Eso es bastante imprudente. -An alternative solution would be to check in the `document` handler if the default action was prevented? If it is so, then the event was handled, and we don't need to react on it. +¿Sería una solución alternativa verificar en el controlador `document` si se evitó la acción predeterminada? Si es así, entonces se manejó el evento y no necesitamos reaccionar ante él. ```html autorun height=80 no-beautify run -<p>Right-click for the document menu (added a check for event.defaultPrevented)</p> -<button id="elem">Right-click for the button menu</button> +<p>Haz clic con el botón derecho en el menú del documento (se agregó una marca de verificación para event.defaultPrevented)</p> +<button id="elem">Haz clic derecho para el menú de botones</button> <script> elem.oncontextmenu = function(event) { event.preventDefault(); - alert("Button context menu"); + alert("Menú contextual del botón"); }; document.oncontextmenu = function(event) { @@ -195,50 +195,50 @@ An alternative solution would be to check in the `document` handler if the defau */!* event.preventDefault(); - alert("Document context menu"); + alert("Menú contextual del documento"); }; </script> ``` -Now everything also works correctly. If we have nested elements, and each of them has a context menu of its own, that would also work. Just make sure to check for `event.defaultPrevented` in each `contextmenu` handler. +Ahora todo también funciona correctamente. Si tenemos elementos anidados, y cada uno de ellos tiene un menú contextual propio, eso también funcionaría. Solo asegúrate de buscar `event.defaultPrevented` en cada controlador de `contextmenu`. -```smart header="event.stopPropagation() and event.preventDefault()" -As we can clearly see, `event.stopPropagation()` and `event.preventDefault()` (also known as `return false`) are two different things. They are not related to each other. +```smart header="event.stopPropagation() y event.preventDefault()" +Como podemos ver claramente, `event.stopPropagation()` y `event.preventDefault()` (también conocido como `return false`) son dos cosas diferentes. No están relacionados entre sí. ``` -```smart header="Nested context menus architecture" -There are also alternative ways to implement nested context menus. One of them is to have a single global object with a handler for `document.oncontextmenu`, and also methods that allow us to store other handlers in it. +```smart header="Arquitectura de menús contextuales anidados" +También hay formas alternativas de implementar menús contextuales anidados. Uno de ellos es tener un único objeto global con un manejador para `document.oncontextmenu`, y también métodos que nos permitan almacenar otros manejadores en él. -The object will catch any right-click, look through stored handlers and run the appropriate one. +El objeto detectará cualquier clic derecho, examinará los controladores almacenados y ejecutará el apropiado. -But then each piece of code that wants a context menu should know about that object and use its help instead of the own `contextmenu` handler. +Pero entonces cada fragmento de código que quiera un menú contextual debe conocer ese objeto y usar su ayuda en lugar del propio controlador `contextmenu`. ``` -## Summary +## Resumen -There are many default browser actions: +Hay muchas acciones predeterminadas del navegador: -- `mousedown` -- starts the selection (move the mouse to select). -- `click` on `<input type="checkbox">` -- checks/unchecks the `input`. -- `submit` -- clicking an `<input type="submit">` or hitting `key:Enter` inside a form field causes this event to happen, and the browser submits the form after it. -- `keydown` -- pressing a key may lead to adding a character into a field, or other actions. -- `contextmenu` -- the event happens on a right-click, the action is to show the browser context menu. -- ...there are more... +- `mousedown` -- inicia la selección (mueva el ratón para seleccionar). +- `click` en `<input type="checkbox">` -- marca/desmarca el `input`. +- `submit` -- dar clic en `<input type="submit">` o presionar `key:Enter` dentro de un campo de formulario hace que suceda este evento y el navegador envía el formulario a continuación. +- `keydown` -- presionar una tecla puede llevar a agregar un carácter a un campo u otras acciones. +- `contextmenu` -- el evento ocurre con un clic derecho, la acción es mostrar el menú contextual del navegador. +- ...hay mas... -All the default actions can be prevented if we want to handle the event exclusively by JavaScript. +Todas las acciones predeterminadas se pueden evitar si queremos manejar el evento exclusivamente mediante JavaScript. -To prevent a default action -- use either `event.preventDefault()` or `return false`. The second method works only for handlers assigned with `on<event>`. +Para evitar una acción predeterminada, utiliza `event.preventDefault()` o `return false`. El segundo método funciona solo para los controladores asignados con `on<event>`. -The `passive: true` option of `addEventListener` tells the browser that the action is not going to be prevented. That's useful for some mobile events, like `touchstart` and `touchmove`, to tell the browser that it should not wait for all handlers to finish before scrolling. +La opción `passive: true` de `addEventListener` le dice al navegador que la acción no se evitará. Eso es útil para algunos eventos móviles, como `touchstart` y `touchmove`, para decirle al navegador que no debe esperar a que todos los controladores terminen antes de desplazarse. -If the default action was prevented, the value of `event.defaultPrevented` becomes `true`, otherwise it's `false`. +Si se evitó la acción predeterminada, el valor de `event.defaultPrevented` se convierte en `true`, de lo contrario, es `false`. -```warn header="Stay semantic, don't abuse" -Technically, by preventing default actions and adding JavaScript we can customize the behavior of any elements. For instance, we can make a link `<a>` work like a button, and a button `<button>` behave as a link (redirect to another URL or so). +```warn header="Mantente semántico, no abuses" +Técnicamente, al evitar acciones predeterminadas y agregar JavaScript, podemos personalizar el comportamiento de cualquier elemento. Por ejemplo, podemos hacer que un enlace `<a>` funcione como un botón, y un botón `<button>` se comporte como un enlace (redirigir a otra URL o algo así). -But we should generally keep the semantic meaning of HTML elements. For instance, `<a>` should perform navigation, not a button. +Pero en general deberíamos mantener el significado semántico de los elementos HTML. Por ejemplo, la navegación debe realizarla `<a>`, no un botón. -Besides being "just a good thing", that makes your HTML better in terms of accessibility. +Además de ser "algo bueno", hace que su HTML sea mejor en términos de accesibilidad. -Also if we consider the example with `<a>`, then please note: a browser allows us to open such links in a new window (by right-clicking them and other means). And people like that. But if we make a button behave as a link using JavaScript and even look like a link using CSS, then `<a>`-specific browser features still won't work for it. +Además, si consideramos el ejemplo con `<a>`, ten en cuenta: un navegador nos permite abrir dichos enlaces en una nueva ventana (usando el botón derecho u otros medios). Y a la gente le gusta. Pero si hacemos que un botón se comporte como un enlace usando JavaScript e incluso parezca un enlace usando CSS, las características específicas de `<a>` no funcionarán en él. ``` diff --git a/2-ui/2-events/04-default-browser-action/menu.view/menu.js b/2-ui/2-events/04-default-browser-action/menu.view/menu.js index 69025c13b..8dd70db10 100644 --- a/2-ui/2-events/04-default-browser-action/menu.view/menu.js +++ b/2-ui/2-events/04-default-browser-action/menu.view/menu.js @@ -4,5 +4,5 @@ menu.onclick = function(event) { let href = event.target.getAttribute('href'); alert(href); - return false; // prevent url change + return false; // evita el cambio de URL }; diff --git a/2-ui/2-events/05-dispatch-events/article.md b/2-ui/2-events/05-dispatch-events/article.md index 047413fd3..f9ab366bd 100644 --- a/2-ui/2-events/05-dispatch-events/article.md +++ b/2-ui/2-events/05-dispatch-events/article.md @@ -1,40 +1,40 @@ -# Dispatching custom events +# Envío de eventos personalizados -We can not only assign handlers, but also generate events from JavaScript. +No solo podemos asignar controladores, sino también generar eventos desde JavaScript. -Custom events can be used to create "graphical components". For instance, a root element of our own JS-based menu may trigger events telling what happens with the menu: `open` (menu open), `select` (an item is selected) and so on. Another code may listen for the events and observe what's happening with the menu. +Los eventos personalizados se pueden utilizar para crear "componentes gráficos". Por ejemplo, un elemento raíz de nuestro propio menú basado en JS puede desencadenar eventos que indiquen lo que sucede con el menú: `abrir` (menú abierto),` seleccionar` (se selecciona un elemento) y así sucesivamente. Otro código puede escuchar los eventos y observar lo que sucede con el menú. -We can generate not only completely new events, that we invent for our own purposes, but also built-in ones, such as `click`, `mousedown` etc. That may be helpful for automated testing. +No solo podemos generar eventos completamente nuevos, que inventamos para nuestros propios fines, sino también eventos integrados, como `click`, `mousedown`, etc. Eso puede ser útil para las pruebas automatizadas. -## Event constructor +## Constructor de eventos -Built-in event classes form a hierarchy, similar to DOM element classes. The root is the built-in [Event](https://dom.spec.whatwg.org/#events) class. +Las clases de eventos integradas forman una jerarquía, similar a las clases de elementos DOM. La raíz es la clase incorporada [Event](https://dom.spec.whatwg.org/#events). -We can create `Event` objects like this: +Podemos crear objetos `Event` así: ```js let event = new Event(type[, options]); ``` -Arguments: +Argumentos: -- *type* -- event type, a string like `"click"` or our own like `"my-event"`. -- *options* -- the object with two optional properties: - - `bubbles: true/false` -- if `true`, then the event bubbles. - - `cancelable: true/false` -- if `true`, then the "default action" may be prevented. Later we'll see what it means for custom events. +- *type* -- tipo de event, un string como `"click"` o nuestro propio evento como `"mi-evento"`. +- *options* -- el objeto con 2 propiedades opcionales: + - `bubbles: true/false` -- si es `true`, entonces el evento se propaga. + - `cancelable: true/false` -- si es `true`, entonces la "acción predeterminada" puede ser prevenida. Más adelante veremos qué significa para los eventos personalizados. - By default both are false: `{bubbles: false, cancelable: false}`. + Por defecto, los dos son false: `{bubbles: false, cancelable: false}`. ## dispatchEvent -After an event object is created, we should "run" it on an element using the call `elem.dispatchEvent(event)`. +Después de que se crea un objeto de evento, debemos "ejecutarlo" en un elemento usando la llamada `elem.dispatchEvent(event)`. -Then handlers react on it as if it were a regular browser event. If the event was created with the `bubbles` flag, then it bubbles. +Luego, los controladores reaccionan como si fuera un evento normal del navegador. Si el evento fue creado con la bandera `bubbles`, entonces se propaga. -In the example below the `click` event is initiated in JavaScript. The handler works same way as if the button was clicked: +En el siguiente ejemplo, el evento `click` se inicia en JavaScript. El controlador funciona de la misma manera que si se hiciera clic en el botón: ```html run no-beautify -<button id="elem" onclick="alert('Click!');">Autoclick</button> +<button id="elem" onclick="alert('Clic!');">Click automático</button> <script> let event = new Event("click"); @@ -43,46 +43,46 @@ In the example below the `click` event is initiated in JavaScript. The handler w ``` ```smart header="event.isTrusted" -There is a way to tell a "real" user event from a script-generated one. +Hay una forma de diferenciar un evento de usuario "real" de uno generado por script. -The property `event.isTrusted` is `true` for events that come from real user actions and `false` for script-generated events. +La propiedad `event.isTrusted` es `true` para eventos que provienen de acciones de usuarios reales y `false` para eventos generados por script. ``` -## Bubbling example +## Ejemplo de Bubbling -We can create a bubbling event with the name `"hello"` and catch it on `document`. +Podemos crear un evento bubbling con el nombre `"hello"` y capturarlo en `document`. -All we need is to set `bubbles` to `true`: +Todo lo que necesitamos es establecer `bubbles` en `true`: ```html run no-beautify -<h1 id="elem">Hello from the script!</h1> +<h1 id="elem">Hola desde el script!</h1> <script> - // catch on document... + // Captura en document... document.addEventListener("hello", function(event) { // (1) - alert("Hello from " + event.target.tagName); // Hello from H1 + alert("Hola desde " + event.target.tagName); // Hola desde H1 }); - // ...dispatch on elem! + // ...Envío en elem! let event = new Event("hello", {bubbles: true}); // (2) elem.dispatchEvent(event); - // the handler on document will activate and display the message. +// el controlador del documento se activará y mostrará el mensaje. </script> ``` -Notes: +Notas: -1. We should use `addEventListener` for our custom events, because `on<event>` only exists for built-in events, `document.onhello` doesn't work. -2. Must set `bubbles:true`, otherwise the event won't bubble up. +1. Debemos usar `addEventListener` para nuestros eventos personalizados, porque `on<event>` solo existe para eventos incorporados, `document.onhello` no funciona. +2. Debes poner `bubbles:true`, de otra manera el evento no se propagará. -The bubbling mechanics is the same for built-in (`click`) and custom (`hello`) events. There are also capturing and bubbling stages. +La mecánica de bubbling es la misma para los eventos integrados (`click`) y personalizados (`hello`). También hay etapas de captura y propagación. -## MouseEvent, KeyboardEvent and others +## MouseEvent, KeyboardEvent y otros -Here's a short list of classes for UI Events from the [UI Event specification](https://www.w3.org/TR/uievents): +Aquí hay una breve lista de clases para eventos UI (interfaz de usuario) de la [especificación de eventos UI](https://www.w3.org/TR/uievents): - `UIEvent` - `FocusEvent` @@ -91,11 +91,11 @@ Here's a short list of classes for UI Events from the [UI Event specification](h - `KeyboardEvent` - ... -We should use them instead of `new Event` if we want to create such events. For instance, `new MouseEvent("click")`. +Deberíamos usarlos en lugar de `new Event` si queremos crear tales eventos. Por ejemplo, `new MouseEvent("click")`. -The right constructor allows to specify standard properties for that type of event. +El constructor correcto permite especificar propiedades estándar para ese tipo de evento. -Like `clientX/clientY` for a mouse event: +Como `clientX/clientY` para un evento de mouse: ```js run let event = new MouseEvent("click", { @@ -110,40 +110,40 @@ alert(event.clientX); // 100 */!* ``` -Please note: the generic `Event` constructor does not allow that. +Tenga en cuenta: el constructor genérico `Event` no lo permite. -Let's try: +Intentemos: ```js run let event = new Event("click", { - bubbles: true, // only bubbles and cancelable - cancelable: true, // work in the Event constructor + bubbles: true, // solo bubbles y cancelable + cancelable: true, // funcionan en el constructor de Event clientX: 100, clientY: 100 }); *!* -alert(event.clientX); // undefined, the unknown property is ignored! +alert(event.clientX); // undefined, se ignora la propiedad desconocida! */!* ``` -Technically, we can work around that by assigning directly `event.clientX=100` after creation. So that's a matter of convenience and following the rules. Browser-generated events always have the right type. +Técnicamente, podemos solucionarlo asignando directamente `event.clientX=100` después de la creación. Entonces eso es una cuestión de conveniencia y de seguir las reglas. Los eventos generados por el navegador siempre tienen el tipo correcto. -The full list of properties for different UI events is in the specification, for instance, [MouseEvent](https://www.w3.org/TR/uievents/#mouseevent). +La lista completa de propiedades para diferentes eventos de UI se encuentra en la especificación, por ejemplo, [MouseEvent](https://www.w3.org/TR/uievents/#mouseevent). -## Custom events +## Eventos personalizados -For our own, completely new events types like `"hello"` we should use `new CustomEvent`. Technically [CustomEvent](https://dom.spec.whatwg.org/#customevent) is the same as `Event`, with one exception. +Para nuestros tipos de eventos completamente nuevos, como `"hello"`, deberíamos usar `new CustomEvent`. Técnicamente, [CustomEvent](https://dom.spec.whatwg.org/#customevent) es lo mismo que `Event`, con una excepción. -In the second argument (object) we can add an additional property `detail` for any custom information that we want to pass with the event. +En el segundo argumento (objeto) podemos agregar una propiedad adicional `detail` para cualquier información personalizada que queramos pasar con el evento. -For instance: +Por ejemplo: ```html run refresh -<h1 id="elem">Hello for John!</h1> +<h1 id="elem">Hola para John!</h1> <script> - // additional details come with the event to the handler + // detalles adicionales que vienen con el evento para el controlador. elem.addEventListener("hello", function(event) { alert(*!*event.detail.name*/!*); }); @@ -156,25 +156,25 @@ For instance: </script> ``` -The `detail` property can have any data. Technically we could live without, because we can assign any properties into a regular `new Event` object after its creation. But `CustomEvent` provides the special `detail` field for it to evade conflicts with other event properties. +La propiedad `detail` puede tener cualquier dato. Técnicamente podríamos vivir sin él, porque podemos asignar cualquier propiedad a un objeto `new Event` regular después de su creación. Pero `CustomEvent` proporciona el campo especial `detail` para evitar conflictos con otras propiedades del evento. -Besides, the event class describes "what kind of event" it is, and if the event is custom, then we should use `CustomEvent` just to be clear about what it is. +Además, la clase de evento describe "qué tipo de evento" es, y si el evento es personalizado, entonces deberíamos usar `CustomEvent` solo para tener claro qué es. ## event.preventDefault() -Many browser events have a "default action", such as navigating to a link, starting a selection, and so on. +Muchos eventos del navegador tienen una "acción predeterminada", como ir a un enlace, iniciar una selección, etc. -For new, custom events, there are definitely no default browser actions, but a code that dispatches such event may have its own plans what to do after triggering the event. +Para eventos nuevos y personalizados, definitivamente no hay acciones predeterminadas del navegador, pero un código que distribuye dicho evento puede tener sus propios planes de qué hacer después de activar el evento. -By calling `event.preventDefault()`, an event handler may send a signal that those actions should be canceled. +Al llamar a `event.preventDefault()`, un controlador de eventos puede enviar una señal de que esas acciones deben cancelarse. -In that case the call to `elem.dispatchEvent(event)` returns `false`. And the code that dispatched it knows that it shouldn't continue. +En ese caso, la llamada a `elem.dispatchEvent(event)` devuelve `false`. Y el código que lo envió sabe que no debería continuar. -Let's see a practical example - a hiding rabbit (could be a closing menu or something else). +Veamos un ejemplo práctico: un conejo escondido (podría ser un menú de cierre u otra cosa). -Below you can see a `#rabbit` and `hide()` function that dispatches `"hide"` event on it, to let all interested parties know that the rabbit is going to hide. +A continuación puede ver una función `#rabbit` y `hide()` que distribuye el evento `"hide"` en él, para que todas las partes interesadas sepan que el conejo se va a esconder. -Any handler can listen for that event with `rabbit.addEventListener('hide',...)` and, if needed, cancel the action using `event.preventDefault()`. Then the rabbit won't disappear: +Cualquier controlador puede escuchar ese evento con `rabbit.addEventListener('hide', ...)` y, si es necesario, cancelar la acción usando `event.preventDefault()`. Entonces el conejo no desaparecerá: ```html run refresh autorun <pre id="rabbit"> @@ -184,43 +184,43 @@ Any handler can listen for that event with `rabbit.addEventListener('hide',...)` =\_Y_/= {>o<} </pre> -<button onclick="hide()">Hide()</button> +<button onclick="hide()">Esconder()</button> <script> + // hide() será llamado automáticamente en 2 segundos. function hide() { let event = new CustomEvent("hide", { - cancelable: true // without that flag preventDefault doesn't work + cancelable: true // sin esa bandera preventDefault no funciona }); if (!rabbit.dispatchEvent(event)) { - alert('The action was prevented by a handler'); + alert('La acción fue impedida por un controlador'); } else { rabbit.hidden = true; } } rabbit.addEventListener('hide', function(event) { - if (confirm("Call preventDefault?")) { + if (confirm("¿Llamar a preventDefault?")) { event.preventDefault(); } }); </script> ``` -Please note: the event must have the flag `cancelable: true`, otherwise the call `event.preventDefault()` is ignored. +Tenga en cuenta: el evento debe tener la bandera `cancelable: true`, de lo contrario, la llamada `event.preventDefault()` se ignora. -## Events-in-events are synchronous +## Los eventos dentro de eventos son sincrónicos -Usually events are processed in a queue. That is: if the browser is processing `onclick` and a new event occurs, e.g. mouse moved, then its handling is queued up, corresponding `mousemove` handlers will be called after `onclick` processing is finished. +Usualmente los eventos se procesan en una cola. Por ejemplo: si el navegador está procesando `onclick` y ocurre un nuevo evento porque el mouse se movió, entonces el manejo de este último se pone en cola, y el controlador correspondiente `mousemove` será llamado cuando el procesamiento de `onclick` haya terminado. -The notable exception is when one event is initiated from within another one, e.g. using `dispatchEvent`. Such events are processed immediately: the new event handlers are called, and then the current event handling is resumed. +La excepción notable es cuando un evento se inicia desde dentro de otro, por ejemplo, usando `dispatchEvent`. Dichos eventos se procesan inmediatamente: se llaman los nuevos controladores de eventos y luego se reanuda el manejo de eventos actual. -For instance, in the code below the `menu-open` event is triggered during the `onclick`. - -It's processed immediately, without waiting for `onclick` handler to end: +Por ejemplo, en el código siguiente, el evento `menu-open` se activa durante el `onclick`. +Se procesa inmediatamente, sin esperar a que termine el controlador `onclick`: ```html run autorun -<button id="menu">Menu (click me)</button> +<button id="menu">Menu (dame clic)</button> <script> menu.onclick = function() { @@ -233,23 +233,23 @@ It's processed immediately, without waiting for `onclick` handler to end: alert(2); }; - // triggers between 1 and 2 - document.addEventListener('menu-open', () => alert('nested')); + // se dispara entre 1 y 2 + document.addEventListener('menu-open', () => alert('anidado')); </script> ``` -The output order is: 1 -> nested -> 2. +El orden de salida es: 1 -> anidado -> 2. -Please note that the nested event `menu-open` is caught on the `document`. The propagation and handling of the nested event is finished before the processing gets back to the outer code (`onclick`). +Tenga en cuenta que el evento anidado `menu-open` se captura en `document`. La propagación y el manejo del evento anidado finaliza antes de que el procesamiento vuelva al código externo (`onclick`). -That's not only about `dispatchEvent`, there are other cases. If an event handler calls methods that trigger other events -- they are processed synchronously too, in a nested fashion. +No se trata solo de `dispatchEvent`, hay otros casos. Si un controlador de eventos llama a métodos que desencadenan otros eventos, también se procesan sincrónicamente, de forma anidada. -Let's say we don't like it. We'd want `onclick` to be fully processed first, independently from `menu-open` or any other nested events. +Supongamos que no nos gusta. Querríamos que `onclick` se procesara por completo primero, independientemente de `menu-open` o cualquier otro evento anidado. -Then we can either put the `dispatchEvent` (or another event-triggering call) at the end of `onclick` or, maybe better, wrap it in the zero-delay `setTimeout`: +Entonces podemos poner el `dispatchEvent` (u otra llamada de activación de eventos) al final de `onclick` o, mejor aún, envolverlo en el `setTimeout` de retardo cero: ```html run -<button id="menu">Menu (click me)</button> +<button id="menu">Menu (dame clic)</button> <script> menu.onclick = function() { @@ -262,33 +262,33 @@ Then we can either put the `dispatchEvent` (or another event-triggering call) at alert(2); }; - document.addEventListener('menu-open', () => alert('nested')); + document.addEventListener('menu-open', () => alert('anidado')); </script> ``` -Now `dispatchEvent` runs asynchronously after the current code execution is finished, including `menu.onclick`, so event handlers are totally separate. +Ahora `dispatchEvent` se ejecuta asincrónicamente después de que la ejecución del código actual finaliza, incluyendo `menu.onclick`. Los controladores de eventos están totalmente separados. -The output order becomes: 1 -> 2 -> nested. +El orden de salida se convierte en: 1 -> 2 -> anidado. -## Summary +## Resumen -To generate an event from code, we first need to create an event object. +Para generar un evento a partir del código, primero necesitamos crear un objeto de evento. -The generic `Event(name, options)` constructor accepts an arbitrary event name and the `options` object with two properties: -- `bubbles: true` if the event should bubble. -- `cancelable: true` if the `event.preventDefault()` should work. +El constructor genérico `Event(name, options)` acepta un nombre de evento arbitrario y el objeto `options` con dos propiedades: +- `bubbles: true` si el evento debe propagarse. +- `cancelable: true` si `event.preventDefault()` debe funcionar. -Other constructors of native events like `MouseEvent`, `KeyboardEvent` and so on accept properties specific to that event type. For instance, `clientX` for mouse events. +Otros constructores de eventos nativos como `MouseEvent`, `KeyboardEvent`, y similares, aceptan propiedades específicas para ese tipo de evento. Por ejemplo, `clientX` para eventos de mouse. -For custom events we should use `CustomEvent` constructor. It has an additional option named `detail`, we should assign the event-specific data to it. Then all handlers can access it as `event.detail`. +Para eventos personalizados deberíamos usar el constructor `CustomEvent`. Este tiene una opción adicional llamada `detail` a la que podemos asignarle los datos específicos del evento. De esta forma, todos los controladores pueden accederlos como `event.detail`. -Despite the technical possibility of generating browser events like `click` or `keydown`, we should use them with great care. +A pesar de la posibilidad técnica de generar eventos del navegador como `click` o `keydown`, debemos usarlo con mucho cuidado. -We shouldn't generate browser events as it's a hacky way to run handlers. That's bad architecture most of the time. +No deberíamos generar eventos de navegador, ya que es una forma trillada de ejecutar controladores. Esa es una mala arquitectura la mayor parte del tiempo. -Native events might be generated: +Se pueden generar eventos nativos: -- As a dirty hack to make 3rd-party libraries work the needed way, if they don't provide other means of interaction. -- For automated testing, to "click the button" in the script and see if the interface reacts correctly. +- Como un truco sucio para hacer que las bibliotecas de terceros funcionen de la manera necesaria, si es que ellas no proporcionan otros medios de interacción. +- Para pruebas automatizadas, que el script "haga clic en el botón" y vea si la interfaz reacciona correctamente. -Custom events with our own names are often generated for architectural purposes, to signal what happens inside our menus, sliders, carousels etc. +Los eventos personalizados con nuestros propios nombres a menudo se generan con fines arquitectónicos, para señalar lo que sucede dentro de nuestros menús, controles deslizantes, carruseles, etc. diff --git a/2-ui/2-events/index.md b/2-ui/2-events/index.md index f4996083c..537888828 100644 --- a/2-ui/2-events/index.md +++ b/2-ui/2-events/index.md @@ -1,3 +1,3 @@ -# Introduction to Events +# Introducción a los eventos -An introduction to browser events, event properties and handling patterns. +Una introducción a los eventos del navegador, las propiedades de los eventos y los patrones de manejo. diff --git a/2-ui/3-event-details/1-mouse-events-basics/01-selectable-list/solution.view/index.html b/2-ui/3-event-details/1-mouse-events-basics/01-selectable-list/solution.view/index.html index 4d1e2ea93..382800435 100644 --- a/2-ui/3-event-details/1-mouse-events-basics/01-selectable-list/solution.view/index.html +++ b/2-ui/3-event-details/1-mouse-events-basics/01-selectable-list/solution.view/index.html @@ -16,7 +16,7 @@ <body> - Click on a list item to select it. + Haz click en un elemento de la lista para seleccionarlo <br> <ul id="ul"> @@ -39,7 +39,7 @@ } - // prevent unneeded selection of list elements on clicks + // evitando la selección innecesaria de elementos de la lista en los clics ul.onmousedown = function() { return false; }; diff --git a/2-ui/3-event-details/1-mouse-events-basics/01-selectable-list/source.view/index.html b/2-ui/3-event-details/1-mouse-events-basics/01-selectable-list/source.view/index.html index e18d4a994..fb158c563 100644 --- a/2-ui/3-event-details/1-mouse-events-basics/01-selectable-list/source.view/index.html +++ b/2-ui/3-event-details/1-mouse-events-basics/01-selectable-list/source.view/index.html @@ -16,7 +16,7 @@ <body> - Click on a list item to select it. + Haz click en un elemento de la lista para seleccionarlo. <br> <ul id="ul"> @@ -28,7 +28,7 @@ </ul> <script> - // ...your code... + //...Tu código </script> </body> diff --git a/2-ui/3-event-details/1-mouse-events-basics/01-selectable-list/task.md b/2-ui/3-event-details/1-mouse-events-basics/01-selectable-list/task.md index 8d29134ff..66ef27d74 100644 --- a/2-ui/3-event-details/1-mouse-events-basics/01-selectable-list/task.md +++ b/2-ui/3-event-details/1-mouse-events-basics/01-selectable-list/task.md @@ -2,17 +2,17 @@ importance: 5 --- -# Selectable list +# Lista seleccionable -Create a list where elements are selectable, like in file-managers. +Cree una lista donde los elementos son seleccionables, como en los administradores de archivos. -- A click on a list element selects only that element (adds the class `.selected`), deselects all others. -- If a click is made with `key:Ctrl` (`key:Cmd` for Mac), then the selection is toggled on the element, but other elements are not modified. +- Un clic en un elemento de la lista selecciona solo ese elemento (agrega la clase `.selected`), deselecciona todos los demás. +- Si se hace un clic con `key:Ctrl` (`key:Cmd` para Mac), el estado seleccionado/deseleccionado cambia para ese solo elemento, los otros elementos no se modifican. -The demo: +Demo: [iframe border="1" src="solution" height=180] -P.S. For this task we can assume that list items are text-only. No nested tags. +PD: Para esta tarea, podemos suponer que los elementos de la lista son solo de texto. No hay etiquetas anidadas. -P.P.S. Prevent the native browser selection of the text on clicks. +PPD: Evita la selección nativa del navegador del texto en los clics. diff --git a/2-ui/3-event-details/1-mouse-events-basics/article.md b/2-ui/3-event-details/1-mouse-events-basics/article.md index 9574b0c83..794bac617 100644 --- a/2-ui/3-event-details/1-mouse-events-basics/article.md +++ b/2-ui/3-event-details/1-mouse-events-basics/article.md @@ -1,212 +1,213 @@ -# Mouse events +# Eventos del Mouse -In this chapter we'll get into more details about mouse events and their properties. +En este capítulo vamos a entrar en más detalles sobre los eventos del mouse y sus propiedades. -Please note: such events may come not only from "mouse devices", but are also from other devices, such as phones and tablets, where they are emulated for compatibility. +Ten en cuenta que tales eventos pueden provenir no sólo del "dispositivo mouse", sino también de otros dispositivos, como teléfonos y tabletas, donde se emulan por compatibilidad. -## Mouse event types +## Tipos de eventos del mouse -We've already seen some of these events: +Ya hemos visto algunos de estos eventos: `mousedown/mouseup` -: Mouse button is clicked/released over an element. +: Se oprime/suelta el botón del ratón sobre un elemento. `mouseover/mouseout` -: Mouse pointer comes over/out from an element. +: El puntero del mouse se mueve sobre/sale de un elemento. `mousemove` -: Every mouse move over an element triggers that event. +: Cualquier movimiento del mouse sobre un elemento activa el evento. `click` -: Triggers after `mousedown` and then `mouseup` over the same element if the left mouse button was used. +: Se activa después de `mousedown` y un `mouseup` enseguida sobre el mismo elemento si se usó el botón. `dblclick` -: Triggers after two clicks on the same element within a short timeframe. Rarely used nowadays. +: Se activa después de dos clicks seguidos sobre el mismo elemento. Hoy en día se usa raramente. `contextmenu` -: Triggers when the right mouse button is pressed. There are other ways to open a context menu, e.g. using a special keyboard key, it triggers in that case also, so it's not exactly the mouse event. +: Se activa al pulsar el botón derecho del ratón. Existen otras formas de abrir el menú contextual, por ejemplo: usando un comando especial de teclado también puede activarse, de manera que no es exactamente un evento exclusivo del mouse. -...There are several other events too, we'll cover them later. +...Existen otros eventos más que cubriremos más tarde. -## Events order +## El orden de los eventos -As you can see from the list above, a user action may trigger multiple events. +Como pudiste ver en la lista anterior, una acción del usuario puede desencadenar varios eventos. -For instance, a left-button click first triggers `mousedown`, when the button is pressed, then `mouseup` and `click` when it's released. +Por ejemplo , un click izquierdo primero activa `mousedown`cuando se presiona el botón, enseguida `mouseup` y `click` cuando se suelta. -In cases when a single action initiates multiple events, their order is fixed. That is, the handlers are called in the order `mousedown` -> `mouseup` -> `click`. +En casos así, el orden es fijo. Es decir, los controladores son llamados en el siguiente orden `mousedown` -> `mouseup` -> `click`. ```online -Click the button below and you'll see the events. Try double-click too. +Haz click en el botón abajo y verás los eventos. Intenta con doble click también. -On the teststand below, all mouse events are logged, and if there is more than a 1 second delay between them, they are separated by a horizontal rule. +En el testeador de abajo todos los eventos quedan registrados. Si hay mas de un segundo de retraso entre cada uno de ellos quedan separados por una línea horizontal. -Also, we can see the `button` property that allows us to detect the mouse button; it's explained below. +Además podemos ver la propiedad de `button` que nos permite detectar el botón del mouse. Lo explicaremos a continuación. -<input onmousedown="return logMouse(event)" onmouseup="return logMouse(event)" onclick="return logMouse(event)" oncontextmenu="return logMouse(event)" ondblclick="return logMouse(event)" value="Click me with the right or the left mouse button" type="button"> <input onclick="logClear('test')" value="Clear" type="button"> <form id="testform" name="testform"> <textarea style="font-size:12px;height:150px;width:360px;"></textarea></form> +<input onmousedown="return logMouse(event)" onmouseup="return logMouse(event)" onclick="return logMouse(event)" oncontextmenu="return logMouse(event)" ondblclick="return logMouse(event)" value="Haz click sobre mí con el botón izquierdo o derecho del mouse" type="button"> <input onclick="logClear('test')" value="Clear" type="button"> <form id="testform" name="testform"> <textarea style="font-size:12px;height:150px;width:360px;"></textarea></form> ``` -## Mouse button +## El botón del mouse -Click-related events always have the `button` property, which allows to get the exact mouse button. +Los eventos relacionados con clics siempre tienen la propiedad `button`, esta nos permite conocer el botón exacto del mouse. -We usually don't use it for `click` and `contextmenu` events, because the former happens only on left-click, and the latter -- only on right-click. +Normalmente no la usamos para eventos `click` y `contextmenu` events, porque sabemos que ocurren solo con click izquierdo y derecho respectivamente. -On the other hand, `mousedown` and `mouseup` handlers may need `event.button`, because these events trigger on any button, so `button` allows to distinguish between "right-mousedown" and "left-mousedown". +Por otro lado, los controladores `mousedown` y `mouseup` pueden necesitar `event.button` ya que estos eventos se activan con cualquier botón, y `button` nos permitirá distinguir entre "mousedown derecho" y "mousedown izquierdo". -The possible values of `event.button` are: +Los valores posibles para `event.button` son: -| Button state | `event.button` | +| Estado del botón | `event.button` | |--------------|----------------| -| Left button (primary) | 0 | -| Middle button (auxiliary) | 1 | -| Right button (secondary) | 2 | -| X1 button (back) | 3 | -| X2 button (forward) | 4 | +| Botón izquierdo (primario) | 0 | +| Botón central (auxiliar) | 1 | +| Botón derecho (secundario) | 2 | +| Botón X1 (atrás) | 3 | +| Botón X2 (adelante) | 4 | -Most mouse devices only have the left and right buttons, so possible values are `0` or `2`. Touch devices also generate similar events when one taps on them. +La mayoría de los dispositivos de ratón sólo tienen los botones izquierdo y derecho, por lo que los valores posibles son `0` o `2`. Los dispositivos táctiles también generan eventos similares cuando se toca sobre ellos. -Also there's `event.buttons` property that has all currently pressed buttons as an integer, one bit per button. In practice this property is very rarely used, you can find details at [MDN](mdn:/api/MouseEvent/buttons) if you ever need it. +También hay una propiedad `event.buttons` que guarda todos los botones presionados actuales en un solo entero, un bit por botón. En la práctica, esta propiedad es raramente utilizada. Puedes encontrar más detalles en [MDN](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons) si alguna vez lo necesitas. -```warn header="The outdated `event.which`" -Old code may use `event.which` property that's an old non-standard way of getting a button, with possible values: +```warn header="El obsoleto `event.which`" +El código puede utilizar la propiedad `event.which` que es una forma antigua no estándar de obtener un botón con los valores posibles: -- `event.which == 1` – left button, -- `event.which == 2` – middle button, -- `event.which == 3` – right button. +- `event.which == 1` – botón izquierdo, +- `event.which == 2` – botón central, +- `event.which == 3` – botón derecho. -As of now, `event.which` is deprecated, we shouldn't use it. +Ahora `event.which` está en desuso, no deberíamos usarlo. ``` -## Modifiers: shift, alt, ctrl and meta +## Modificadores: shift, alt, ctrl y meta -All mouse events include the information about pressed modifier keys. +Todos los eventos del mouse incluyen la información sobre las teclas modificadoras presionadas. -Event properties: +Propiedades del evento: - `shiftKey`: `key:Shift` -- `altKey`: `key:Alt` (or `key:Opt` for Mac) +- `altKey`: `key:Alt` (p `key:Opt` para Mac) - `ctrlKey`: `key:Ctrl` -- `metaKey`: `key:Cmd` for Mac +- `metaKey`: `key:Cmd` para Mac -They are `true` if the corresponding key was pressed during the event. +Su valor es `true` si la tecla fue presionada durante el evento. -For instance, the button below only works on `key:Alt+Shift`+click: +Por ejemplo, el botón abajo solo funciona con`key:Alt+Shift`+click: ```html autorun height=60 -<button id="button">Alt+Shift+Click on me!</button> +<button id="button">Alt+Shift+¡Click sobre mí!</button> <script> button.onclick = function(event) { *!* if (event.altKey && event.shiftKey) { */!* - alert('Hooray!'); + alert('¡Genial!'); } }; </script> ``` -```warn header="Attention: on Mac it's usually `Cmd` instead of `Ctrl`" -On Windows and Linux there are modifier keys `key:Alt`, `key:Shift` and `key:Ctrl`. On Mac there's one more: `key:Cmd`, corresponding to the property `metaKey`. +```warn header="Atención: en Mac suele ser `Cmd` en lugar de `Ctrl`" +En Windows y Linux existen las teclas modificadoras `key:Alt`, `key:Shift` y `key:Ctrl`. En Mac hay una más: `key:Cmd`, correspondiente a la propiedad `metaKey`. -In most applications, when Windows/Linux uses `key:Ctrl`, on Mac `key:Cmd` is used. +En la mayoría de las aplicaciones, cuando Windows/Linux usan `key:Ctrl`, en Mac se usa `key:Cmd`. -That is: where a Windows user presses `key:Ctrl+Enter` or `key:Ctrl+A`, a Mac user would press `key:Cmd+Enter` or `key:Cmd+A`, and so on. +Es decir: cuando un usuario de Windows usa `key:Ctrl+Enter` o `key:Ctrl+A`, un usuario Mac presionaría `key:Cmd+Enter` o `key:Cmd+A`, y así sucesivamente. -So if we want to support combinations like `key:Ctrl`+click, then for Mac it makes sense to use `key:Cmd`+click. That's more comfortable for Mac users. +Entonces si queremos darle soporte a combinaciones como `key:Ctrl`+click, entonces para Mac tendría más sentido usar `key:Cmd`+click. Esto es más cómodo para los usuarios de Mac. -Even if we'd like to force Mac users to `key:Ctrl`+click -- that's kind of difficult. The problem is: a left-click with `key:Ctrl` is interpreted as a *right-click* on MacOS, and it generates the `contextmenu` event, not `click` like Windows/Linux. +Incluso si quisiéramos obligar a los usuarios de Mac a hacer `key:Ctrl`+click -- esto supone algo de dificultad. El problema es que: un click izquierdo con `key:Ctrl` es interpretado como *click derecho* en MacOS, y esto genera un evento `contextmenu`, no un `click` como en Windows/Linux. -So if we want users of all operating systems to feel comfortable, then together with `ctrlKey` we should check `metaKey`. +Así que si queremos que los usuarios de todos los sistemas operativos se sientan cómodos, entonces junto con `ctrlKey` debemos verificar `metaKey`. -For JS-code it means that we should check `if (event.ctrlKey || event.metaKey)`. +Para código JS significa que debemos hacer la comprobación `if (event.ctrlKey || event.metaKey)`. ``` -```warn header="There are also mobile devices" -Keyboard combinations are good as an addition to the workflow. So that if the visitor uses a keyboard -- they work. +```warn header="También hay dispositivos móviles" +Las combinaciones de teclado son buenas como una adición al flujo de trabajo. De modo que si el visitante usa un teclado -- funcionan. + +Pero si su dispositivo no lo tiene -- entonces debería haber una manera de vivir sin teclas modificadoras. -But if their device doesn't have it -- then there should be a way to live without modifier keys. ``` -## Coordinates: clientX/Y, pageX/Y +## Coordenadas: clientX/Y, pageX/Y -All mouse events provide coordinates in two flavours: +Todos los eventos del ratón proporcionan coordenadas en dos sabores: -1. Window-relative: `clientX` and `clientY`. -2. Document-relative: `pageX` and `pageY`. +1. Relativas a la ventana: `clientX` y `clientY`. +2. Relativos al documento: `pageX` y `pageY`. -We already covered the difference between them in the chapter <info:coordinates>. +Ya cubrimos la diferencia entre ellos en el capítulo <info:coordinates>. -In short, document-relative coordinates `pageX/Y` are counted from the left-upper corner of the document, and do not change when the page is scrolled, while `clientX/Y` are counted from the current window left-upper corner. When the page is scrolled, they change. +En resumen, las coordenadas relativas al documento `pageX/Y`se cuentan desde la esquina superior izquierda del documento y no cambian cuando se desplaza la página, mientras que `clientX/Y` se cuentan desde la esquina superior actual. Cambian cuando se desplaza la página. -For instance, if we have a window of the size 500x500, and the mouse is in the left-upper corner, then `clientX` and `clientY` are `0`, no matter how the page is scrolled. +Por ejemplo, si tenemos una ventana del tamaño 500x500, y el mouse está en la esquina superior izquierda, entonces `clientX` y `clientY` son `0`, sin importar cómo se desplace la página. -And if the mouse is in the center, then `clientX` and `clientY` are `250`, no matter what place in the document it is. They are similar to `position:fixed` in that aspect. +Y si el mouse está en el centro, entonces `clientX` y `clientY` son `250`, No importa en qué parte del documento se encuentren. Esto es similar a `position:fixed` en ese aspecto. ````online -Move the mouse over the input field to see `clientX/clientY` (the example is in the `iframe`, so coordinates are relative to that `iframe`): +Mueve el mouse sobre el campo de entrada para ver `clientX/clientY` (el ejemplo está dentro del `iframe`, así que las coordenadas son relativas al `iframe`): ```html autorun height=50 -<input onmousemove="this.value=event.clientX+':'+event.clientY" value="Mouse over me"> +<input onmousemove="this.value=event.clientX+':'+event.clientY" value="Pasa el mouse sobre mí"> ``` ```` -## Preventing selection on mousedown +## Impedir la selección en mousedown -Double mouse click has a side effect that may be disturbing in some interfaces: it selects text. +El doble clic del mouse tiene un efecto secundario que puede ser molesto en algunas interfaces: selecciona texto. -For instance, double-clicking on the text below selects it in addition to our handler: +Por ejemplo, un doble clic en el texto de abajo lo selecciona además de activar nuestro controlador: ```html autorun height=50 -<span ondblclick="alert('dblclick')">Double-click me</span> +<span ondblclick="alert('dblclick')">Haz doble click en mi</span> ``` -If one presses the left mouse button and, without releasing it, moves the mouse, that also makes the selection, often unwanted. +Si se pulsa el botón izquierdo del ratón y, sin soltarlo, mueve el ratón, también hace la selección, a menudo no deseado. -There are multiple ways to prevent the selection, that you can read in the chapter <info:selection-range>. +Hay varias maneras de impedir la selección, que se pueden leer en el capítulo <info:selection-range>. -In this particular case the most reasonable way is to prevent the browser action on `mousedown`. It prevents both these selections: +En este caso particular, la forma más razonable es impedir la acción del navegador `mousedown`. Esto impide ambas selecciones: ```html autorun height=50 -Before... +Antes... <b ondblclick="alert('Click!')" *!*onmousedown="return false"*/!*> - Double-click me + Haz doble click en mí </b> -...After +...Después ``` -Now the bold element is not selected on double clicks, and pressing the left button on it won't start the selection. +Ahora el elemento en negrita no se selecciona con doble clic, y al mantener presionado el botón izquierdo y arrastrar no se iniciará la selección. -Please note: the text inside it is still selectable. However, the selection should start not on the text itself, but before or after it. Usually that's fine for users. +Tenga en cuenta: el texto dentro de él todavía es seleccionable. Sin embargo, la selección no debe comenzar en el texto en sí, sino antes o después. Por lo general, eso está bien para los usuarios. -````smart header="Preventing copying" -If we want to disable selection to protect our page content from copy-pasting, then we can use another event: `oncopy`. +````smart header="Impedir copias" +Si queremos impedir la selección para proteger nuestro contenido de la página del copy-paste, entonces podemos utilizar otro evento: `oncopy`. ```html autorun height=80 no-beautify -<div *!*oncopy="alert('Copying forbidden!');return false"*/!*> - Dear user, - The copying is forbidden for you. - If you know JS or HTML, then you can get everything from the page source though. +<div *!*oncopy="alert('¡Copiado prohibido!');return false"*/!*> + Querido usuario, + El copiado está prohibido para ti. + Si sabes JS o HTML entonces puedes obtener todo de la fuente de la página. </div> ``` -If you try to copy a piece of text in the `<div>`, that won't work, because the default action `oncopy` is prevented. +Si intenta copiar un fragmento de texto en el `<div>` no va a funcionar porque la acción predeterminada de `oncopy` fue deshabilitada. -Surely the user has access to HTML-source of the page, and can take the content from there, but not everyone knows how to do it. +Seguramente el usuario tiene acceso a la fuente HTML de la página, y puede tomar el contenido desde allí, pero no todos saben cómo hacerlo. ```` -## Summary +## Resumen -Mouse events have the following properties: +Los eventos del mouse tienen las siguientes propiedades: -- Button: `button`. -- Modifier keys (`true` if pressed): `altKey`, `ctrlKey`, `shiftKey` and `metaKey` (Mac). - - If you want to handle `key:Ctrl`, then don't forget Mac users, they usually use `key:Cmd`, so it's better to check `if (e.metaKey || e.ctrlKey)`. +- Botón: `button`. +- Teclas modificadoras (`true` si fueron presionadas): `altKey`, `ctrlKey`, `shiftKey` y `metaKey` (Mac). + - Si quieres controlar las acciones de la tecla `key:Ctrl` no te olvides de los usuarios de Mac que generalmente usan `key:Cmd`, de manera que es mejor verificar con la condicional: `if (e.metaKey || e.ctrlKey)`. -- Window-relative coordinates: `clientX/clientY`. -- Document-relative coordinates: `pageX/pageY`. +- Coordenadas relativas a la ventana: `clientX/clientY`. +- Coordenadas relativas al documento: `pageX/pageY`. -The default browser action of `mousedown` is text selection, if it's not good for the interface, then it should be prevented. +La acción predeterminada del navegador `mousedown` es la selección del texto, si no es bueno para la interfaz, entonces debe impedirse. -In the next chapter we'll see more details about events that follow pointer movement and how to track element changes under it. +En el próximo capítulo veremos más detalles sobre los eventos que siguen al movimiento del puntero y cómo rastrear los cambios de elementos debajo de él. diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html index 84d52b18c..a6903dd0c 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html @@ -6,7 +6,7 @@ <style> body { height: 2000px; - /* the tooltip should work after page scroll too */ + /* El tooltip debe seguir funcionando aúnn después de hacer scroll */ } .tooltip { @@ -66,25 +66,25 @@ let tooltip; document.onmouseover = function(event) { - // important: a fast-moving mouse may "jump" right to a child on an annotated node, skipping the parent - // so mouseover may happen on a child. + // Importante: un movimiento rápido del mouse podría saltar directamente al descendiente en un nodo anotado, ignorando el padre + // por lo tanto mouseover podría ejecutarse en un descendiente. let anchorElem = event.target.closest('[data-tooltip]'); if (!anchorElem) return; - // show tooltip and remember it + // Mostrando el tooltip y recordándolo. tooltip = showTooltip(anchorElem, anchorElem.dataset.tooltip); } document.onmouseout = function() { - // it is possible that mouseout triggered, but we're still inside the element - // (its target was inside, and it bubbled) - // but in this case we'll have an immediate mouseover, - // so the tooltip will be destroyed and shown again + // Es posible que mouseout se active, pero estaremos dentro del elemento + // (el target ocurrirá dentro y se aparecerá) + // pero en este caso tendremos un mouse over inmediato, + // entonces el tooltip será destruido y mostrado otra vez. // - // luckily, the "blinking" won't be visible, - // as both events happen almost at the same time + // Afortunadamente, el parpadeo no es visble, + // ya que ambos eventos ocurren casi al mismo tiempo. if (tooltip) { tooltip.remove(); tooltip = false; @@ -101,7 +101,7 @@ let coords = anchorElem.getBoundingClientRect(); - // position the tooltip over the center of the element + // Posicionando el tooltip sobre el centro del elemento let left = coords.left + (anchorElem.offsetWidth - tooltipElem.offsetWidth) / 2; if (left < 0) left = 0; diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/source.view/index.html b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/source.view/index.html index 774e24a21..727ad272f 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/source.view/index.html +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/source.view/index.html @@ -6,7 +6,7 @@ <style> body { height: 2000px; - /* the tooltip should work after page scroll too */ + /* El tooltip debe funcionar también despues de hacer scroll */ } .tooltip { @@ -49,8 +49,8 @@ <body> - <div data-tooltip="Here is the house interior" id="house"> - <div data-tooltip="Here is the roof" id="roof"></div> + <div data-tooltip="Aquí esta el interior de la casa" id="house"> + <div data-tooltip="Aquí está el techo" id="roof"></div> <p>Once upon a time there was a mother pig who had three little pigs.</p> @@ -58,12 +58,12 @@ <p>The three little pigs set off. "We will take care that the wolf does not catch us," they said.</p> - <p>Soon they met a man. <a href="https://en.wikipedia.org/wiki/The_Three_Little_Pigs" data-tooltip="Read on…">Hover over me</a></p> + <p>Soon they met a man. <a href="https://en.wikipedia.org/wiki/The_Three_Little_Pigs" data-tooltip="Continúa leyendo…">Colócate sobre mí</a></p> </div> <script> - // ...your code... + // ...tu código... </script> </body> diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/task.md b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/task.md index c77aa0728..79123f95a 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/task.md +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/task.md @@ -2,24 +2,24 @@ importance: 5 --- -# Improved tooltip behavior +# Comportamiento mejorado de un tooltip -Write JavaScript that shows a tooltip over an element with the attribute `data-tooltip`. The value of this attribute should become the tooltip text. +Escribe JavaScript que muestre un tooltip sobre un elemento con el atributo `data-tooltip`. El valor de este atributo debe convertirse en el texto del tooltip. -That's like the task <info:task/behavior-tooltip>, but here the annotated elements can be nested. The most deeply nested tooltip is shown. +Es como la tarea <info:task/behavior-tooltip>, pero aquí los elementos anotados se pueden anidar. Los tooltips más internos se muestran. -Only one tooltip may show up at the same time. +Solamente un tooltip puede aparecer a la vez. -For instance: +Por ejemplo: ```html -<div data-tooltip="Here – is the house interior" id="house"> - <div data-tooltip="Here – is the roof" id="roof"></div> +<div data-tooltip="Aquí – está el interior de la casa" id="house"> + <div data-tooltip="Aquí – está el techo" id="roof"></div> ... - <a href="https://en.wikipedia.org/wiki/The_Three_Little_Pigs" data-tooltip="Read on…">Hover over me</a> + <a href="https://en.wikipedia.org/wiki/The_Three_Little_Pigs" data-tooltip="Continúa leyendo…">Colócate sobre mi</a> </div> ``` -The result in iframe: +El resultado en el iframe: [iframe src="solution" height=300 border=1] diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.md b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.md index 36eaca1a3..00686ef0b 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.md +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.md @@ -1,18 +1,18 @@ -The algorithm looks simple: -1. Put `onmouseover/out` handlers on the element. Also can use `onmouseenter/leave` here, but they are less universal, won't work if we introduce delegation. -2. When a mouse cursor entered the element, start measuring the speed on `mousemove`. -3. If the speed is slow, then run `over`. -4. When we're going out of the element, and `over` was executed, run `out`. +El algoritmo se ve simple: +1. Coloca los controladores `onmouseover/out` en el elemento. Aquí también podemos usar `onmouseenter/leave`, pero son menos universales, no funcionan si introducimos delegaciones. +2. Cuando el cursor ingrese al elemento debes medir la velocidad en `mousemove`. +3. Si la velocidad es lenta hay que ejecutar `over`. +4. Si estamos saliendo del elemento, y `over` ya se había ejecutado, ahora ejecutamos `out`. -But how to measure the speed? +¿Pero cómo mediremos la velocidad? -The first idea can be: run a function every `100ms` and measure the distance between previous and new coordinates. If it's small, then the speed is small. +La primera idea puede ser: correr una función cada `100ms` y medir la distancia entre la coordenada anterior y la actual. Si es pequeña entonces la velocidad fue rápida. -Unfortunately, there's no way to get "current mouse coordinates" in JavaScript. There's no function like `getCurrentMouseCoordinates()`. +Desafortunadamente no hay manera para obtener las coordenadas actuales del mouse en JavaScript. No existe algo así como `getCurrentMouseCoordinates()`. -The only way to get coordinates is to listen for mouse events, like `mousemove`, and take coordinates from the event object. +La única manera es registrando los eventos del mouse, como `mousemove`, y tomar las coordenadas del objeto del evento. -So let's set a handler on `mousemove` to track coordinates and remember them. And then compare them, once per `100ms`. +Entonces configuremos un `mousemove` para registrar las coordenadas y recordarlas. Y entonces las comparamos, una por cada `100ms`. -P.S. Please note: the solution tests use `dispatchEvent` to see if the tooltip works right. +PD. Toma nota: El test de la solución usa `dispatchEvent` para ver si el tooltip funciona bien. diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/hoverIntent.js b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/hoverIntent.js index 7503ca9c2..2ff1a7c1c 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/hoverIntent.js +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/hoverIntent.js @@ -3,8 +3,8 @@ class HoverIntent { constructor({ - sensitivity = 0.1, // speed less than 0.1px/ms means "hovering over an element" - interval = 100, // measure mouse speed once per 100ms + sensitivity = 0.1, // Velocidad menor a 0.1px/ms supone un "posicionamiento sobre el elemento" + interval = 100, // Medida de la velocidad del mouse una vez por cada 100ms elem, over, out @@ -15,12 +15,12 @@ class HoverIntent { this.over = over; this.out = out; - // make sure "this" is the object in event handlers. + // Segurándonos de que "this" es el objeto en los controladores de eventos this.onMouseMove = this.onMouseMove.bind(this); this.onMouseOver = this.onMouseOver.bind(this); this.onMouseOut = this.onMouseOut.bind(this); - // and in time-measuring function (called from setInterval) + // y en la función para medir el tiempo (llamada desde setInterval) this.trackSpeed = this.trackSpeed.bind(this); elem.addEventListener("mouseover", this.onMouseOver); @@ -32,16 +32,16 @@ class HoverIntent { onMouseOver(event) { if (this.isOverElement) { - // if we're over the element, then ignore the event - // we are already measuring the speed + // Si estamos sobre el elemento ignoramos el evento + // Ya estamos midiendo la velocidad return; } this.isOverElement = true; - // after every mousemove we'll be check the distance - // between the previous and the current mouse coordinates - // if it's less than sensivity, then the speed is slow + //Después de cada mousemove vamos a checar la distancia + // entre la coordinada previa y actual del mouse + // si es menor que sensivity entonces la velocidad es muy rápida this.prevX = event.pageX; this.prevY = event.pageY; @@ -52,13 +52,13 @@ class HoverIntent { } onMouseOut(event) { - // if left the element + // Si abandomanos el elemento if (!event.relatedTarget || !elem.contains(event.relatedTarget)) { this.isOverElement = false; this.elem.removeEventListener('mousemove', this.onMouseMove); clearInterval(this.checkSpeedInterval); if (this.isHover) { - // if there was a stop over the element + // Si nos detenemos sobre el elemento this.out.call(this.elem, event); this.isHover = false; } @@ -76,7 +76,7 @@ class HoverIntent { let speed; if (!this.lastTime || this.lastTime == this.prevTime) { - // cursor didn't move + // Cursor sin movimieto (detenido) speed = 0; } else { speed = Math.sqrt( @@ -90,7 +90,7 @@ class HoverIntent { this.isHover = true; this.over.call(this.elem); } else { - // speed fast, remember new coordinates as the previous ones + // Hubo movimiento rápido, registramos las coordenadas actuales como las anteriores this.prevX = this.lastX; this.prevY = this.lastY; this.prevTime = this.lastTime; diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/hoverIntent.js b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/hoverIntent.js index a38b42bc2..211466f23 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/hoverIntent.js +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/hoverIntent.js @@ -1,12 +1,12 @@ 'use strict'; -// Here's a brief sketch of the class -// with things that you'll need anyway +// Aquí hay un breve sketch con la clase +// de cosas que vas a ocupar de cualquier forma class HoverIntent { constructor({ - sensitivity = 0.1, // speed less than 0.1px/ms means "hovering over an element" - interval = 100, // measure mouse speed once per 100ms: calculate the distance between previous and next points + sensitivity = 0.1, // Velocidad menor a 0.1px/ms supone un "posicionamiento sobre el elemento" + interval = 100, // Medida de la velocidad del mouse una vez por cada 100ms: cualcula la distancia entre el punto anterior y el actual elem, over, out @@ -17,16 +17,16 @@ class HoverIntent { this.over = over; this.out = out; - // make sure "this" is the object in event handlers. + // // Segurándonos de que "this" es el objeto en los controladores de eventos this.onMouseMove = this.onMouseMove.bind(this); this.onMouseOver = this.onMouseOver.bind(this); this.onMouseOut = this.onMouseOut.bind(this); - // assign the handlers + // Asignando los controladores elem.addEventListener("mouseover", this.onMouseOver); elem.addEventListener("mouseout", this.onMouseOut); - // continue from this point + // Continua trabajando desde este punto } @@ -44,8 +44,8 @@ class HoverIntent { destroy() { - /* your code to "disable" the functionality, remove all handlers */ - /* it's needed for the tests to work */ + /* Tu código para "deshabilitar" la funcionalidad, remueve los controladores */ + /* Es necesario para que las pruebas funcionen*/ } } diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/task.md b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/task.md index 72e615bdd..465d68d8f 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/task.md +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/task.md @@ -1,31 +1,31 @@ -importance: 5 +Importance: 5 --- -# "Smart" tooltip +# Tooltip "inteligente" -Write a function that shows a tooltip over an element only if the visitor moves the mouse *to it*, but not *through it*. +Escribe una función que muestre un tooltip sobre un elemento solamente si el visitante mueve el mouse *hacia él*, pero no *a través de él*. -In other words, if the visitor moves the mouse to the element and stops there -- show the tooltip. And if they just moved the mouse through, then no need, who wants extra blinking? +En otras palabras, si el visitante mueve el mouse hacia el elemento y para ahí, muestra el tooltip. Y si solamente mueve el mouse a través, entonces no lo necesitamos. ¿Quién quiere parpadeos extra? -Technically, we can measure the mouse speed over the element, and if it's slow then we assume that it comes "over the element" and show the tooltip, if it's fast -- then we ignore it. +Técnicamente, podemos medir la velocidad del mouse sobre el elemento, y si es lenta podemos asumir que el mouse viene "sobre el elemento" y mostramos el tooltip, si es rápida -- entonces lo ignoramos. -Make a universal object `new HoverIntent(options)` for it. +Hay que crear un objeto universal `new HoverIntent(options)` para ello. -Its `options`: -- `elem` -- element to track. -- `over` -- a function to call if the mouse came to the element: that is, it moves slowly or stopped over it. -- `out` -- a function to call when the mouse leaves the element (if `over` was called). +Sus `options`: +- `elem` -- elemento a seguir. +- `over` -- una función a llamar si el el mouse viene hacia el elemento: o sea, si viene lentamente o para sobre él. +- `out` -- una función a llamar cuando el mouse abandona el elemento (si `over` fue llamado). -An example of using such object for the tooltip: +Un ejemplo de dicho objeto siendo usado para el tooltip: ```js -// a sample tooltip +// Un tooltip de muestra let tooltip = document.createElement('div'); tooltip.className = "tooltip"; tooltip.innerHTML = "Tooltip"; -// the object will track mouse and call over/out +// El objeto va a rastrear al mouse y llamar a over/out new HoverIntent({ elem, over() { @@ -39,10 +39,10 @@ new HoverIntent({ }); ``` -The demo: +El demo: [iframe src="solution" height=140] -If you move the mouse over the "clock" fast then nothing happens, and if you do it slow or stop on them, then there will be a tooltip. +Si mueves el mouse sobre el "reloj" rápido no pasará nada, y si lo haces lento o paras sobre él entonces habrá un tooltip. -Please note: the tooltip doesn't "blink" when the cursor moves between the clock subelements. +Toma en cuenta que el tooltip no "parpadea" cuando el cursor se mueve entre subelementos del reloj. diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/article.md b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/article.md index d409c3f12..f6deefc2e 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/article.md +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/article.md @@ -1,78 +1,78 @@ -# Moving the mouse: mouseover/out, mouseenter/leave +# Moviendo el mouse: mouseover/out, mouseenter/leave -Let's dive into more details about events that happen when the mouse moves between elements. +Entremos en detalle sobre los eventos que suceden cuando el mouse se mueve entre elementos. -## Events mouseover/mouseout, relatedTarget +## Eventos mouseover/mouseout, relatedTarget -The `mouseover` event occurs when a mouse pointer comes over an element, and `mouseout` -- when it leaves. +El evento `mouseover` se produce cuando el cursor del mouse aparece sobre un elemento y `mouseout` cuando se va. ![](mouseover-mouseout.svg) -These events are special, because they have property `relatedTarget`. This property complements `target`. When a mouse leaves one element for another, one of them becomes `target`, and the other one - `relatedTarget`. +Estos eventos son especiales porque tienen la propiedad `relatedTarget`. Esta propiedad complementa a `target`. Cuando el puntero del mouse deja un elemento por otro, uno de ellos se convierte en `target` y el otro en `relatedTarget`. -For `mouseover`: +Para `mouseover`: -- `event.target` -- is the element where the mouse came over. -- `event.relatedTarget` -- is the element from which the mouse came (`relatedTarget` -> `target`). +- `event.target` -- Es el elemento al que se acerca el mouse. +- `event.relatedTarget` -- Es el elemento de donde proviene el mouse (`relatedTarget` -> `target`). -For `mouseout` the reverse: +Para `mouseout` sucede al contrario: -- `event.target` -- is the element that the mouse left. -- `event.relatedTarget` -- is the new under-the-pointer element, that mouse left for (`target` -> `relatedTarget`). +- `event.target` -- Es el elemento que el mouse dejó. +- `event.relatedTarget` -- es el nuevo elemento bajo el cursor por cuál el cursor dejó al anterior (`target` -> `relatedTarget`). ```online -In the example below each face and its features are separate elements. When you move the mouse, you can see mouse events in the text area. +En el siguiente ejemplo, cada cara y sus características son elementos separados. Puedes ver en el área de texto los eventos que ocurren cuando mueves el mouse. -Each event has the information about both `target` and `relatedTarget`: +Cada evento tiene la información sobre ambas propiedades: `target` y `relatedTarget`: [codetabs src="mouseoverout" height=280] ``` -```warn header="`relatedTarget` can be `null`" -The `relatedTarget` property can be `null`. +```warn header="`relatedTarget` puede ser `null`" +La propiedad `relatedTarget` puede tener un valor `null`. -That's normal and just means that the mouse came not from another element, but from out of the window. Or that it left the window. +Eso es normal y solo significa que el mouse no vino de otro elemento, sino de la ventana o que salió de la ventana. -We should keep that possibility in mind when using `event.relatedTarget` in our code. If we access `event.relatedTarget.tagName`, then there will be an error. +Debemos tener en cuenta esa posibilidad cuando usemos `event.relatedTarget` en nuestro código. Si accedemos a `event.relatedTarget.tagName` entonces habrá un error. ``` -## Skipping elements +## Saltando elementos -The `mousemove` event triggers when the mouse moves. But that doesn't mean that every pixel leads to an event. +El evento `mousemove` se activa cuando el mouse se mueve, pero eso no significa que cada píxel nos lleve a un evento. -The browser checks the mouse position from time to time. And if it notices changes then triggers the events. +El navegador verifica la posición del mouse de vez en cuando y si nota cambios entonces activan los eventos. -That means that if the visitor is moving the mouse very fast then some DOM-elements may be skipped: +Eso significa que si el visitante mueve el mouse muy rápido, entonces algunos elementos del DOM podrían estar siendo ignorados: ![](mouseover-mouseout-over-elems.svg) -If the mouse moves very fast from `#FROM` to `#TO` elements as painted above, then intermediate `<div>` elements (or some of them) may be skipped. The `mouseout` event may trigger on `#FROM` and then immediately `mouseover` on `#TO`. +Si el mouse se mueve muy rápido de los elementos `#FROM` a `#TO`, como se muestra arriba, entonces los elementos intermedios `<div>` (o algunos de ellos) podrían ser ignorados. El evento `mouseout` se podría activar en `#FROM` e inmediatamente `mouseover` en `#TO`. -That's good for performance, because there may be many intermediate elements. We don't really want to process in and out of each one. +Eso es bueno para el rendimiento porque puede haber muchos elementos intermedios. Realmente no queremos procesar todo lo que sucede dentro y fuera de cada uno. -On the other hand, we should keep in mind that the mouse pointer doesn't "visit" all elements along the way. It can "jump". +Por otro lado, debemos tener en cuenta que el puntero del mouse no "visita" todos los elementos en el camino. Los puede "saltar". -In particular, it's possible that the pointer jumps right inside the middle of the page from out of the window. In that case `relatedTarget` is `null`, because it came from "nowhere": +En particular, es posible que el puntero salte dentro de la mitad de la página desde la ventana. En ese caso `relatedTarget` es `null`, porque vino de "la nada": ![](mouseover-mouseout-from-outside.svg) ```online -You can check it out "live" on a teststand below. +Puedes verlo "en vivo" en un testeador a continuación. -Its HTML has two nested elements: the `<div id="child">` is inside the `<div id="parent">`. If you move the mouse fast over them, then maybe only the child div triggers events, or maybe the parent one, or maybe there will be no events at all. +Este HTML tiene dos elementos: el `<div id="child">` está adentro del `<div id="parent">`. Si mueves el mouse rápidamente sobre ellos entonces probablemente solo el `div` hijo active los eventos, o probablemente el padre, o probablemente no ocurran eventos en lo absoluto. -Also move the pointer into the child `div`, and then move it out quickly down through the parent one. If the movement is fast enough, then the parent element is ignored. The mouse will cross the parent element without noticing it. +También prueba a mover el cursor hacia el `div` hijo y luego muévelo rápidamente hacia abajo a través del padre. Si el movimiento es lo suficientemente rápido entonces el padre será ignorado. El mouse cruzará el elemento padre sin notarlo. [codetabs height=360 src="mouseoverout-fast"] ``` -```smart header="If `mouseover` triggered, there must be `mouseout`" -In case of fast mouse movements, intermediate elements may be ignored, but one thing we know for sure: if the pointer "officially" entered an element (`mouseover` event generated), then upon leaving it we always get `mouseout`. +```smart header="Si `mouseover` se activa, deberá haber `mouseout`" +En caso de movimientos rápidos, los elementos intermedios podrían ser ignorados, pero una cosa segura sabemos: si el cursor ingresa "oficialmente" dentro de un elemento(evento `mouseover` generado), una vez que lo deje obtendremos `mouseout`. ``` -## Mouseout when leaving for a child +## Mouseout, cuando se deja un elemento por uno anidado. -An important feature of `mouseout` -- it triggers, when the pointer moves from an element to its descendant, e.g. from `#parent` to `#child` in this HTML: +Una característica importante de `mouseout` -- se activa cuando el cursor se mueve de un elemento hacia su descendiente (elemento anidado o interno). Por ejemplo de `#parent` a `#child` en este HTML: ```html <div id="parent"> @@ -80,92 +80,92 @@ An important feature of `mouseout` -- it triggers, when the pointer moves from a </div> ``` -If we're on `#parent` and then move the pointer deeper into `#child`, we get `mouseout` on `#parent`! +Si estamos sobre `#parent` y luego movemos el cursor hacia dentro de `#child`, ¡vamos a obtener `mouseout` en `#parent`! ![](mouseover-to-child.svg) -That may seem strange, but can be easily explained. +Eso puede parecer extraño, pero puede explicarse fácilmente. -**According to the browser logic, the mouse cursor may be only over a *single* element at any time -- the most nested one and top by z-index.** +**De acuerdo con la lógica del navegador, el cursor podría estar sobre un elemento *individual* en cualquier momento -- el anidado y el más alto según el z-index.** -So if it goes to another element (even a descendant), then it leaves the previous one. +Entonces si se dirige hacia otro elemento (incluso uno anidado), está dejando al anterior. -Please note another important detail of event processing. +Por favor, note otro importante detalle sobre el procesamiento de eventos. -The `mouseover` event on a descendant bubbles up. So, if `#parent` has `mouseover` handler, it triggers: +El evento `mouseover` se aparece en un un elemento anidado (brota o nace, por decirlo así). Entonces si `#parent` tiene el controlador `mouseover`, se activa: ![](mouseover-bubble-nested.svg) ```online -You can see that very well in the example below: `<div id="child">` is inside the `<div id="parent">`. There are `mouseover/out` handlers on `#parent` element that output event details. +Puedes verlo muy bien a continuación: `<div id="child">` está dentro de`<div id="parent">`. Hay controladores `mouseover/out` en el elemento `#parent` que arrojan los detalles de los eventos. -If you move the mouse from `#parent` to `#child`, you see two events on `#parent`: -1. `mouseout [target: parent]` (left the parent), then -2. `mouseover [target: child]` (came to the child, bubbled). +Si mueves el mouse de `#parent` a `#child`, verás dos eventos sobre `#parent`: +1. `mouseout [target: parent]` (dejó al padre), luego +2. `mouseover [target: child]` (vino hacia el hijo, y este evento brotó). [codetabs height=360 src="mouseoverout-child"] ``` -As shown, when the pointer moves from `#parent` element to `#child`, two handlers trigger on the parent element: `mouseout` and `mouseover`: +Como se muestra, cuando el cursor se mueve del elemento `#parent` a `#child`, los dos controladores se activan en el elemento padre: `mouseout` y `mouseover`: ```js parent.onmouseout = function(event) { - /* event.target: parent element */ + /* event.target: elemento padre */ }; parent.onmouseover = function(event) { - /* event.target: child element (bubbled) */ + /* event.target: elemento hijo (brota) */ }; ``` -**If we don't examine `event.target` inside the handlers, then it may seem that the mouse pointer left `#parent` element, and then immediately came back over it.** +**Si no examinamos `event.target` dentro de los controladores podría parecer que el cursor dejo el elemento `#parent` y volvió a él inmediatamente.** -But that's not the case! The pointer is still over the parent, it just moved deeper into the child element. +Pero ese no es el caso. El cursor aún está sobre el elemento padre, simplemente se adentró más en el elemento hijo. -If there are some actions upon leaving the parent element, e.g. an animation runs in `parent.onmouseout`, we usually don't want it when the pointer just goes deeper into `#parent`. +Si hay algunas acciones al abandonar el elemento padre,por ejemplo: una animación se ejecuta con `parent.onmouseout`, usualmente no la queremos cuando el cursor se adentre más sobre `#parent`. -To avoid it, we can check `relatedTarget` in the handler and, if the mouse is still inside the element, then ignore such event. +Para evitar esto lo que podemos hacer es checar `relatedTarget` en el controlador y si el mouse aún permanece dentro del elemento entonces ignorar dicho evento. -Alternatively we can use other events: `mouseenter` and `mouseleave`, that we'll be covering now, as they don't have such problems. +Alternativamente podemos usar otros eventos: `mouseenter` y `mouseleave`, los cuales cubriremos a continuación, ya que con ellos no hay tales problemas. -## Events mouseenter and mouseleave +## Eventos mouseenter y mouseleave -Events `mouseenter/mouseleave` are like `mouseover/mouseout`. They trigger when the mouse pointer enters/leaves the element. +Los eventos `mouseenter/mouseleave` son como `mouseover/mouseout`. Se activan cuando el cursor del mouse entra/sale del elemento. -But there are two important differences: +Pero hay dos diferencias importantes: -1. Transitions inside the element, to/from descendants, are not counted. -2. Events `mouseenter/mouseleave` do not bubble. +1. Las transiciones hacia/desde los descendientes no se cuentan. +2. Los eventos `mouseenter/mouseleave` no brotan. -These events are extremely simple. +Son eventos extremadamente simples. -When the pointer enters an element -- `mouseenter` triggers. The exact location of the pointer inside the element or its descendants doesn't matter. +Cuando el cursor entra en un elemento `mouseenter` se activa. La ubicación exacta del cursor dentro del elemento o sus descendientes no importa. -When the pointer leaves an element -- `mouseleave` triggers. +Cuando el cursor deja el elemento `mouseleave` se activa. ```online -This example is similar to the one above, but now the top element has `mouseenter/mouseleave` instead of `mouseover/mouseout`. +Este ejemplo es similar al anterior, pero ahora el elemento tiene `mouseenter/mouseleave` en lugar de `mouseover/mouseout`. -As you can see, the only generated events are the ones related to moving the pointer in and out of the top element. Nothing happens when the pointer goes to the child and back. Transitions between descendants are ignored +Como puedes ver, los únicos eventos generados son los relacionados con mover el puntero dentro y fuera del elemento superior. No pasa nada cuando el puntero va hacia el descendiente y regresa. Las transiciones entre descendientes se ignoran: [codetabs height=340 src="mouseleave"] ``` -## Event delegation +## Delegación de eventos -Events `mouseenter/leave` are very simple and easy to use. But they do not bubble. So we can't use event delegation with them. +Los eventos `mouseenter/leave` son muy simples de usar. Pero no brotan por sí solos. Por lo tanto no podemos usar la delegación de eventos con ellos. -Imagine we want to handle mouse enter/leave for table cells. And there are hundreds of cells. +Imagina que queremos manejar entrada/salida para celdas de tabla y hay cientos de celdas. -The natural solution would be -- to set the handler on `<table>` and process events there. But `mouseenter/leave` don't bubble. So if such event happens on `<td>`, then only a handler on that `<td>` is able to catch it. +La solución natural sería: ajustar el controlador en `<table>` y manejar los eventos desde ahí. Pero `mouseenter/leave` no aparece. Entonces si cada evento sucede en `<td>`, solamente un controlador `<td>` es capaz de detectarlo. -Handlers for `mouseenter/leave` on `<table>` only trigger when the pointer enters/leaves the table as a whole. It's impossible to get any information about transitions inside it. +Los controladores `mouseenter/leave` en `<table>` solamente se activan cuando el cursor entra/deja la tabla completa. Es imposible obtener alguna información sobre las transiciones dentro de ella. -So, let's use `mouseover/mouseout`. +Pues usemos `mouseover/mouseout`. -Let's start with simple handlers that highlight the element under mouse: +Comencemos con controladores simples que resaltan el elemento debajo del mouse: ```js -// let's highlight an element under the pointer +// Resaltemos un elemento debajo del cursor table.onmouseover = function(event) { let target = event.target; target.style.background = 'pink'; @@ -178,44 +178,44 @@ table.onmouseout = function(event) { ``` ```online -Here they are in action. As the mouse travels across the elements of this table, the current one is highlighted: +Aquí se muestran en acción. A medida que el mouse recorre los elementos de esta tabla, se resalta la actual: [codetabs height=480 src="mouseenter-mouseleave-delegation"] ``` -In our case we'd like to handle transitions between table cells `<td>`: entering a cell and leaving it. Other transitions, such as inside the cell or outside of any cells, don't interest us. Let's filter them out. +En nuestro caso nos gustaría manejar las transiciones entre las celdas de la tabla `<td>`: entradas y salidas de una celda a otra. Otras transiciones, como dentro de una celda o fuera de cualquiera de ellas no nos interesan. Vamos a filtrarlas. -Here's what we can do: +Esto es lo que podemos hacer: -- Remember the currently highlighted `<td>` in a variable, let's call it `currentElem`. -- On `mouseover` -- ignore the event if we're still inside the current `<td>`. -- On `mouseout` -- ignore if we didn't leave the current `<td>`. +- Recordar el elemento `<td>` resaltado actualmente en una variable, llamémosla `currentElem`. +- En `mouseover` ignoraremos el evento si permanecemos dentro del `<td>` actual. +- En `mouseout` ignoraremos el evento si no hemos dejado el `<td>` actual. -Here's an example of code that accounts for all possible situations: +Aquí hay un ejemplo de código que explica todas las situaciones posibles: [js src="mouseenter-mouseleave-delegation-2/script.js"] -Once again, the important features are: -1. It uses event delegation to handle entering/leaving of any `<td>` inside the table. So it relies on `mouseover/out` instead of `mouseenter/leave` that don't bubble and hence allow no delegation. -2. Extra events, such as moving between descendants of `<td>` are filtered out, so that `onEnter/Leave` runs only if the pointer leaves or enters `<td>` as a whole. +Una vez más, las características importantes son: +1. Utilizar la delegación de eventos para manejar la entrada/salida de cualquier `<td>` dentro de la tabla. Pues depende de `mouseover/out` en lugar de `mouseenter/leave` que no broten y por lo tanto no permita ninguna delegación. +2. Los eventos adicionales, como moverse entre descendientes de `<td>` son filtrados, así que `onEnter/Leave` solamente se ejecuta si el cursor ingresa a `<td>` o lo deja absolutamente. ```online -Here's the full example with all details: +Aquí está el ejemplo completo con todos los detalles: [codetabs height=460 src="mouseenter-mouseleave-delegation-2"] -Try to move the cursor in and out of table cells and inside them. Fast or slow -- doesn't matter. Only `<td>` as a whole is highlighted, unlike the example before. +Intenta mover el cursor dentro y fuera de las celdas de la tabla y dentro de cada una de ellas. Rápido o lento -- no importa --. Solo se ilumina `<td>` como un todo, a diferencia del ejemplo anterior. ``` -## Summary +## Resumen -We covered events `mouseover`, `mouseout`, `mousemove`, `mouseenter` and `mouseleave`. +Hemos cubierto `mouseover`, `mouseout`, `mousemove`, `mouseenter` y`mouseleave`. -These things are good to note: +Estas cosas son buenas de destacar: -- A fast mouse move may skip intermediate elements. -- Events `mouseover/out` and `mouseenter/leave` have an additional property: `relatedTarget`. That's the element that we are coming from/to, complementary to `target`. +- Un movimiento rápido del mouse puede omitir elementos intermedios. +- Los eventos `mouseover/out` y `mouseenter/leave` tienen una propiedad adicional: `relatedTarget`. Es el elemento de donde venimos o hacia donde vamos, complementario con `target`. -Events `mouseover/out` trigger even when we go from the parent element to a child element. The browser assumes that the mouse can be only over one element at one time -- the deepest one. +Los eventos `mouseover/out` se activan incluso cuando vamos de un elemento padre a su descendiente. El navegador asume que de el mouse solo puede estar sobre un elemento a la vez -- el más interno. -Events `mouseenter/leave` are different in that aspect: they only trigger when the mouse comes in and out the element as a whole. Also they do not bubble. +Los eventos `mouseenter/leave` son diferentes en ese aspecto: solo se activan cuando el mouse viene hacia el elemento o lo deja como un todo. Así que no se aparecen de repente. diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseenter-mouseleave-delegation-2.view/script.js b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseenter-mouseleave-delegation-2.view/script.js index 6a3202467..bd5a14d42 100755 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseenter-mouseleave-delegation-2.view/script.js +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseenter-mouseleave-delegation-2.view/script.js @@ -1,54 +1,54 @@ -// <td> under the mouse right now (if any) +// Los elementos <td> bajo el maouse justo ahora(si es que hay) let currentElem = null; table.onmouseover = function(event) { - // before entering a new element, the mouse always leaves the previous one - // if currentElem is set, we didn't leave the previous <td>, - // that's a mouseover inside it, ignore the event + // antes de ingresar un uevo elemento, el mouse siempre abandonará al anterior + // si currentElem está establecido, no abandonamos el <td> anterior, + // hay un mouseover dentro de él, ignoramos el evento if (currentElem) return; let target = event.target.closest('td'); - // we moved not into a <td> - ignore + // si no hay movimientos dentro de un <td> - lo ignoramos if (!target) return; - // moved into <td>, but outside of our table (possible in case of nested tables) - // ignore + //si hay movimientos dentro de un <td>, pero afuera de una tabla(posiblemente en caso de tablas anidadas) + // lo ignoramos if (!table.contains(target)) return; - // hooray! we entered a new <td> + // ¡Genial! ingresamos a un nuevo <td> currentElem = target; onEnter(currentElem); }; table.onmouseout = function(event) { - // if we're outside of any <td> now, then ignore the event - // that's probably a move inside the table, but out of <td>, - // e.g. from <tr> to another <tr> + // si estamos afuera de algún <td> ahora, entonces ignoramos el evento + // puede haber movimientos dentro de una tabla, pero fuera de <td>, + // por ejemplo: de un <tr> a otro <tr> if (!currentElem) return; - // we're leaving the element – where to? Maybe to a descendant? + // abandonamos el elemento – ¿pero hacia dónde? ¿podría ser hacia un descendiente? let relatedTarget = event.relatedTarget; while (relatedTarget) { - // go up the parent chain and check – if we're still inside currentElem - // then that's an internal transition – ignore it + // vamos a la cadena de padres y verificamos – si aún estamos dentro de currentElem + // entonces hay una transición interna – la ignoramos if (relatedTarget == currentElem) return; relatedTarget = relatedTarget.parentNode; } - // we left the <td>. really. + // abandonamos el <td>. onLeave(currentElem); currentElem = null; }; -// any functions to handle entering/leaving an element +// algunas funciones para manejar entradas/salidas de un elemento function onEnter(elem) { elem.style.background = 'pink'; - // show that in textarea + // lo mostramos en el área de texto text.value += `over -> ${currentElem.tagName}.${currentElem.className}\n`; text.scrollTop = 1e6; } @@ -56,7 +56,7 @@ function onEnter(elem) { function onLeave(elem) { elem.style.background = ''; - // show that in textarea + // lo mostramos en el area de texto text.value += `out <- ${elem.tagName}.${elem.className}\n`; text.scrollTop = 1e6; } diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout.view/index.html b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout.view/index.html index 022fd00db..52ab45f45 100755 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout.view/index.html +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout.view/index.html @@ -28,7 +28,7 @@ </div> </div> - <textarea id="log">Events will show up here! + <textarea id="log">¡Los eventos se mostrarán aquí! </textarea> <script src="script.js"></script> diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/solution.md b/2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/solution.md index 6d8878d4a..7a30cf0ac 100644 --- a/2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/solution.md +++ b/2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/solution.md @@ -1,5 +1,5 @@ -As we can see from HTML/CSS, the slider is a `<div>` with a colored background, that contains a runner -- another `<div>` with `position:relative`. +Como podemos ver en el HTML/CSS, la barra de desplazamiento es un `<div>` con un fondo de color, que contiene un pasador: otro `<div>` con `position:relative`. -To position the runner we use `position:relative`, to provide the coordinates relative to its parent, here it's more convenient here than `position:absolute`. +Para posicionar el pasador usamos `position:relative`, para proveer las coordenadas relativas a su padre, aquí es más conveniente que `position:absolute`. -Then we implement horizontal-only Drag'n'Drop with limitation by width. +En este caso implementamos un Arrastrar y Soltar horizontal limitado por el ancho. diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/solution.view/index.html b/2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/solution.view/index.html index c364b42d9..1c4579817 100644 --- a/2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/solution.view/index.html +++ b/2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/solution.view/index.html @@ -16,10 +16,10 @@ let thumb = slider.querySelector('.thumb'); thumb.onmousedown = function(event) { - event.preventDefault(); // prevent selection start (browser action) + event.preventDefault(); // evita el inicio de la selección (acción del navegador) let shiftX = event.clientX - thumb.getBoundingClientRect().left; - // shiftY not needed, the thumb moves only horizontally + // shiftY no es necesario, el dedo se mueve solo horizontalmente document.addEventListener('mousemove', onMouseMove); document.addEventListener('mouseup', onMouseUp); @@ -27,7 +27,7 @@ function onMouseMove(event) { let newLeft = event.clientX - shiftX - slider.getBoundingClientRect().left; - // the pointer is out of slider => lock the thumb within the bounaries + // el puntero está fuera del slider => bloquear el dedo dentro de los límites if (newLeft < 0) { newLeft = 0; } diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/source.view/index.html b/2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/source.view/index.html index a9a545c09..1958ce407 100644 --- a/2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/source.view/index.html +++ b/2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/source.view/index.html @@ -13,7 +13,7 @@ </div> <script> - // ...your code... + // ... tu código... </script> </body> diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/task.md b/2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/task.md index 0c6da4e2c..8f914be93 100644 --- a/2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/task.md +++ b/2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/task.md @@ -2,15 +2,15 @@ importance: 5 --- -# Slider +# Control deslizante -Create a slider: +Crea un control deslizante: [iframe src="solution" height=60 border=1] -Drag the blue thumb with the mouse and move it. +Arrastra el pasador azul con el ratón y muévelo. -Important details: +Detalles importantes: -- When the mouse button is pressed, during the dragging the mouse may go over or below the slider. The slider will still work (convenient for the user). -- If the mouse moves very fast to the left or to the right, the thumb should stop exactly at the edge. +- Cuando el botón del ratón es presionado, durante el arrastrado del ratón puedes ir por arriba o debajo de la barra deslizante. Ésta seguirá funcionando (es lo conveniente para el usuario). +- Si el ratón se mueve muy rápido hacia la izquierda o la derecha, el pasador se detiene exactamente en el borde. diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.md b/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.md index 62cbdb9c5..1c87698da 100644 --- a/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.md +++ b/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.md @@ -1,5 +1,5 @@ -To drag the element we can use `position:fixed`, it makes coordinates easier to manage. At the end we should switch it back to `position:absolute` to lay the element into the document. +Para arrastrar el elemento podemos usar `position:fixed`, esto hace las coordenadas más fáciles de manejar. Al final deberíamos devolverla a `position:absolute` para fijar el elemento en el documento. -When coordinates are at window top/bottom, we use `window.scrollTo` to scroll it. +Cuando las coordinadas están en el tope/fondo de la ventana, usamos `window.scrollTo` para desplazarla. -More details in the code, in comments. +Más detalles en el código, en los comentarios. diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/index.html b/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/index.html index d79ef30a4..886d3ac5d 100644 --- a/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/index.html +++ b/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/index.html @@ -8,17 +8,17 @@ <body> - <h2>Place superheroes around the soccer field.</h2> + <h2>Ubica los superhéroes por el campo.</h2> - <p>Superheroes and the ball are elements with the class "draggable". Make them really draggable.</p> + <p>Los superhéroes y la pelota son elementos con la clase "draggable". Haz que sean realmente arrastrables.</p> - <p>Important: limit dragging by the window. If a draggable reaches window top or bottom, then the page should scroll to let us drag it further.</p> + <p>Importante: limita el arrastre a la ventana. Si el arrastrable alcanza el borde inferior o superior de la ventana, la página debe desplazarse para permitir seguir arrasrtrando.</p> - <p>If your screen is big enough to fit the whole document -- make the window smaller to get vertical scrolling, so that you could test it.</p> + <p>Si en tu monitor cabe el documento entero, haz la ventana del navegador más pequeña para que aparezca la barra de desplazamiento vertical y así puedas probarlo.</p> - <p>In this task it's enough to handle vertical scrolling. There's no horizontal scrolling usually, and it's handled the similar way if needed.</p> + <p>En esta tarea es suficiente manejar solo el desplazamiento vertical. No suele usarse el desplazamiento horizontal, y el manejo es similar si se necesita.</p> - <p>And one more thing: heroes may never leave the page. If they reach the edge of the document, no dragging outside of it.</p> + <p>Y algo más: los héroes nunca pueden dejar la página. Si alcanzan el borde del documento, no pueden arrastrarse fuera de él.</p> <div id="field"> diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/soccer.js b/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/soccer.js index 10ae2eeed..cb69f037a 100644 --- a/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/soccer.js +++ b/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/soccer.js @@ -24,9 +24,9 @@ document.addEventListener('mousedown', function(event) { moveAt(event.clientX, event.clientY); } - // on drag start: - // remember the initial shift - // move the element position:fixed and a direct child of body + // on drag start (inicio del arrastre): + // recordar el desplazamiento inicial + // hacer el elemento position:fixed, hijo directo de body y moverlo function startDrag(element, clientX, clientY) { if(isDragging) { return; @@ -45,7 +45,7 @@ document.addEventListener('mousedown', function(event) { moveAt(clientX, clientY); }; - // switch to absolute coordinates at the end, to fix the element in the document + // cambiar a coordenadas absolutas al final, para fijar el elemento en el documento function finishDrag() { if(!isDragging) { return; @@ -65,45 +65,45 @@ document.addEventListener('mousedown', function(event) { let newX = clientX - shiftX; let newY = clientY - shiftY; - // check if the new coordinates are below the bottom window edge - let newBottom = newY + dragElement.offsetHeight; // new bottom + // verifica si las nuevas coordenadas están debajo del borde inferior de la ventana + let newBottom = newY + dragElement.offsetHeight; // nueva base - // below the window? let's scroll the page + // ¿debajo de la ventana?, desplacemos la página if (newBottom > document.documentElement.clientHeight) { - // window-relative coordinate of document end + // coordenadas relativas a la ventana del fin de documento let docBottom = document.documentElement.getBoundingClientRect().bottom; - // scroll the document down by 10px has a problem - // it can scroll beyond the end of the document - // Math.min(how much left to the end, 10) + // desplazar hacia abajo el documento en 10px tiene un problema + // puede desplazarse más allá del fin del documento + // Math.min(cuánto-falta-para-el-final, 10) let scrollY = Math.min(docBottom - newBottom, 10); - // calculations are imprecise, there may be rounding errors that lead to scrolling up - // that should be impossible, fix that here + // los cálculos son imprecisos, puede haber errores de redondeo que hagan un desplazamiento hacia arriba + // eso no debe ser posible, corrijámoslo aquí if (scrollY < 0) scrollY = 0; window.scrollBy(0, scrollY); - // a swift mouse move make put the cursor beyond the document end - // if that happens - - // limit the new Y by the maximally possible (right at the bottom of the document) + // un movimiento rápido del mouse puede poner el cursor después del final del documento + // si ocurre, + // limitar el nuevo "Y" al máximo posible (justo en la base del documento) newY = Math.min(newY, document.documentElement.clientHeight - dragElement.offsetHeight); } - // check if the new coordinates are above the top window edge (similar logic) + // verificar si las nuevas coordenadas están arriba del borde superior de la ventana (lógica similar a la previa) if (newY < 0) { - // scroll up + // desplazamiento hacia arriba let scrollY = Math.min(-newY, 10); - if (scrollY < 0) scrollY = 0; // check precision errors + if (scrollY < 0) scrollY = 0; // corrige errores de precisión window.scrollBy(0, -scrollY); - // a swift mouse move can put the cursor beyond the document start - newY = Math.max(newY, 0); // newY may not be below 0 + // un movimiento rápido del mouse puede poner el cursor más allá del principio del documento + newY = Math.max(newY, 0); // newY no puede ser menor a 0 } - // limit the new X within the window boundaries - // there's no scroll here so it's simple + // limita el nuevo "X" dentro de los límites de la ventana + // aquí no hay desplazamiento, entonces es simple if (newX < 0) newX = 0; if (newX > document.documentElement.clientWidth - dragElement.offsetWidth) { newX = document.documentElement.clientWidth - dragElement.offsetWidth; diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/source.view/index.html b/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/source.view/index.html index d79ef30a4..3c0f245ea 100644 --- a/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/source.view/index.html +++ b/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/source.view/index.html @@ -8,17 +8,17 @@ <body> - <h2>Place superheroes around the soccer field.</h2> + <h2>Ubica los superhéroes por el campo.</h2> - <p>Superheroes and the ball are elements with the class "draggable". Make them really draggable.</p> + <p>Los superhéroes y ls pelota son elementos con la clase "draggable". Haz que sean realmente arrastrables.</p> - <p>Important: limit dragging by the window. If a draggable reaches window top or bottom, then the page should scroll to let us drag it further.</p> + <p>Importante: limita el arrastre a la ventana. Si el arrastrable alcanza el borde inferior o superior de la ventana, la página debe desplazarse para permitir seguir arrasrtrando.</p> - <p>If your screen is big enough to fit the whole document -- make the window smaller to get vertical scrolling, so that you could test it.</p> + <p>Si en tu monitor cabe el documento entero, haz la ventana del navegador más pequeña para que aparezca la barra de desplazamiento vertical y así puedas probarlo.</p> - <p>In this task it's enough to handle vertical scrolling. There's no horizontal scrolling usually, and it's handled the similar way if needed.</p> + <p>En esta tarea es suficiente manejar solo el desplazamiento vertical. No suele usarse el desplazamiento horizontal, y el manejo es similar si se necesita.</p> - <p>And one more thing: heroes may never leave the page. If they reach the edge of the document, no dragging outside of it.</p> + <p>Y algo más: los héroes nunca pueden dejar la página. Si alcanzan el borde del documento, no pueden arrastrarse fuera de él.</p> <div id="field"> diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/task.md b/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/task.md index 91fbaa0f2..b3008f79d 100644 --- a/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/task.md +++ b/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/task.md @@ -2,19 +2,19 @@ importance: 5 --- -# Drag superheroes around the field +# Arrastrar super héroes por el campo -This task can help you to check understanding of several aspects of Drag'n'Drop and DOM. +Esta tarea te puede ayudar a comprobar tu entendimiento de varios aspectos de Arrastrar y Soltar, y del DOM. -Make all elements with class `draggable` -- draggable. Like a ball in the chapter. +Hacer que todos los elementos con clase `draggable` sean arrastrables. Como la pelota de este capítulo. -Requirements: +Requerimientos: -- Use event delegation to track drag start: a single event handler on `document` for `mousedown`. -- If elements are dragged to top/bottom window edges -- the page scrolls up/down to allow further dragging. -- There is no horizontal scroll (this makes the task a bit simpler, adding it is easy). -- Draggable elements or their parts should never leave the window, even after swift mouse moves. +- Usa delegación de eventos para detectar el inicio del arrastrado: un solo manejador de eventos en el `document` para `mousedown`. +- Si los elementos son arrastrados a los bordes superior/inferior de la ventana: la página se desliza hacia arriba/abajo para permitir dicho arrastre. +- Sin desplazamiento horizontal (esto hace la tarea un poco más simple, añadirlo es fácil). +- Los elementos arrastrables o sus partes nunca deben dejar la ventana, incluso después de movimientos rápidos del ratón. -The demo is too big to fit it here, so here's the link. +La demostración es demasiado grande para caber aquí, así que aquí está el enlace. [demo src="solution"] diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/article.md b/2-ui/3-event-details/4-mouse-drag-and-drop/article.md index 4c928eef1..61165ca5b 100644 --- a/2-ui/3-event-details/4-mouse-drag-and-drop/article.md +++ b/2-ui/3-event-details/4-mouse-drag-and-drop/article.md @@ -1,54 +1,54 @@ -# Drag'n'Drop with mouse events +# Arrastrar y Soltar con eventos del ratón -Drag'n'Drop is a great interface solution. Taking something and dragging and dropping it is a clear and simple way to do many things, from copying and moving documents (as in file managers) to ordering (dropping items into a cart). +Arrastrar y Soltar es una excelente solución de interfaz. Tomar algo, arrastrar y soltarlo es una forma clara y simple de hacer muchas cosas, desde copiar y mover documentos (como en los manejadores de archivos) hasta ordenar (arrastrando ítems al carrito). -In the modern HTML standard there's a [section about Drag and Drop](https://html.spec.whatwg.org/multipage/interaction.html#dnd) with special events such as `dragstart`, `dragend`, and so on. +En el estándar moderno de HTML hay una [sección sobre Arrastrar y Soltar](https://html.spec.whatwg.org/multipage/interaction.html#dnd) con eventos especiales tales como `dragstart`, `dragend`, y así por el estilo. -These events allow us to support special kinds of drag'n'drop, such as handling dragging a file from OS file-manager and dropping it into the browser window. Then JavaScript can access the contents of such files. +Estos eventos nos permiten soportar tipos especiales de Arrastrar y Soltar, como manejar el arrastrado de archivos desde el manejador de archivos del Sistema Operativo y soltarlo en la ventana del navegador. Así JavaScript puede acceder al contenido de dichos archivos. -But native Drag Events also have limitations. For instance, we can't prevent dragging from a certain area. Also we can't make the dragging "horizontal" or "vertical" only. And there are many other drag'n'drop tasks that can't be done using them. Also, mobile device support for such events is very weak. +Pero los eventos nativos de arrastrar tienen limitaciones. Por ejemplo, no nos deja evitar el arrastre desde cierta área. Tampoco podemos hacer que el arrastre sea solamente "horizontal" o "vertical". Y hay muchas otras tareas de "Arrastrar y Soltar" que no pueden hacerse utilizándolos. Además, el soporte para dichos eventos es muy pobre en dispositivos móviles. -So here we'll see how to implement Drag'n'Drop using mouse events. +Así que aquí veremos cómo implementar "Arrastrar y Soltar" usando eventos del ratón. -## Drag'n'Drop algorithm +## Algoritmo de "Arrastrar y Soltar" -The basic Drag'n'Drop algorithm looks like this: +El algoritmo básico de Arrastrar y Soltar se ve así: -1. On `mousedown` - prepare the element for moving, if needed (maybe create a clone of it, add a class to it or whatever). -2. Then on `mousemove` move it by changing `left/top` with `position:absolute`. -3. On `mouseup` - perform all actions related to finishing the drag'n'drop. +1. En `mousedown` - preparar el elemento para moverlo, si es necesario (quizá creando un clon de este, añadiéndole una clase, o lo que sea). +2. En `mousemove` - moverlo cambiando `left/top` con `position:absolute`. +3. En `mouseup` - realizar todas las acciones relacionadas con finalizar el Arrastrar y Soltar. -These are the basics. Later we'll see how to add other features, such as highlighting current underlying elements while we drag over them. +Esto es lo básico. Luego veremos como añadir características, como resaltar los elementos subyacentes mientras arrastramos sobre ellos. -Here's the implementation of dragging a ball: +Aquí esta la implementación de arrastrar una pelota: ```js -ball.onmousedown = function(event) { - // (1) prepare to moving: make absolute and on top by z-index +ball.onmousedown = function(event) { + // (1) preparar para mover: hacerlo absoluto y ponerlo sobre todo con el z-index ball.style.position = 'absolute'; ball.style.zIndex = 1000; - // move it out of any current parents directly into body - // to make it positioned relative to the body - document.body.append(ball); + // quitar cualquier padre actual y moverlo directamente a body + // para posicionarlo relativo al body + document.body.append(ball); - // centers the ball at (pageX, pageY) coordinates + // centrar la pelota en las coordenadas (pageX, pageY) function moveAt(pageX, pageY) { ball.style.left = pageX - ball.offsetWidth / 2 + 'px'; ball.style.top = pageY - ball.offsetHeight / 2 + 'px'; } - // move our absolutely positioned ball under the pointer + // mover nuestra pelota posicionada absolutamente bajo el puntero moveAt(event.pageX, event.pageY); function onMouseMove(event) { moveAt(event.pageX, event.pageY); } - // (2) move the ball on mousemove + // (2) mover la pelota con mousemove document.addEventListener('mousemove', onMouseMove); - // (3) drop the ball, remove unneeded handlers + // (3) soltar la pelota, quitar cualquier manejador de eventos innecesario ball.onmouseup = function() { document.removeEventListener('mousemove', onMouseMove); ball.onmouseup = null; @@ -57,19 +57,19 @@ ball.onmousedown = function(event) { }; ``` -If we run the code, we can notice something strange. On the beginning of the drag'n'drop, the ball "forks": we start dragging its "clone". +Si ejecutamos el código, nos enteramos de algo extraño. Al inicio de arrastrar y soltar, la pelota se duplica: empezamos a arrastrar su "clon". ```online -Here's an example in action: +Aquí hay un ejemplo en acción: [iframe src="ball" height=230] -Try to drag'n'drop with the mouse and you'll see such behavior. +Trata de arrastrar con el ratón y verás dicho comportamiento. ``` -That's because the browser has its own drag'n'drop support for images and some other elements. It runs automatically and conflicts with ours. +Esto es porque el navegador tiene su propio soporte para arrastrar y soltar para imágenes y otros elementos. Se ejecuta automáticamente y entra en conflicto con el nuestro. -To disable it: +Para deshabilitarlo: ```js ball.ondragstart = function() { @@ -77,42 +77,42 @@ ball.ondragstart = function() { }; ``` -Now everything will be all right. +Ahora todo estará bien. ```online -In action: +En acción: [iframe src="ball2" height=230] ``` -Another important aspect -- we track `mousemove` on `document`, not on `ball`. From the first sight it may seem that the mouse is always over the ball, and we can put `mousemove` on it. +Otro aspecto importante: seguimos `mousemove` en `document`, no en `ball`. Desde el primer momento debe verse que el ratón está siempre sobre la pelota, y podemos poner `mousemove` en ella. -But as we remember, `mousemove` triggers often, but not for every pixel. So after swift move the pointer can jump from the ball somewhere in the middle of document (or even outside of the window). +Pero como recordamos, `mousemove` se dispara a menudo, pero no por cada pixel. Así que después de un movimiento rápido el puntero puede saltar de la pelota a algún lugar en el medio del documento (o incluso fuera de la ventana). -So we should listen on `document` to catch it. +Así que tenemos que escuchar en `document` para captarlo. -## Correct positioning +## Posicionamiento correcto -In the examples above the ball is always moved so that its center is under the pointer: +En los ejemplos de arriba la pelota siempre se mueve, de manera que su centro queda debajo del puntero: ```js ball.style.left = pageX - ball.offsetWidth / 2 + 'px'; ball.style.top = pageY - ball.offsetHeight / 2 + 'px'; ``` -Not bad, but there's a side effect. To initiate the drag'n'drop, we can `mousedown` anywhere on the ball. But if "take" it from its edge, then the ball suddenly "jumps" to become centered under the mouse pointer. +Nada mal, pero hay un efecto secundario. Para iniciar el arrastrar y soltar, podemos hacer `mousedown` en cualquier lugar de la pelota. Pero si la "tomamos" por el borde, entonces la pelota "salta" de repente para centrarse bajo el puntero del ratón. -It would be better if we keep the initial shift of the element relative to the pointer. +Sería mejor si mantenemos la posición inicial del elemento, relativo al puntero. -For instance, if we start dragging by the edge of the ball, then the pointer should remain over the edge while dragging. +Por ejemplo, si empezamos a arrastrar por el borde de la pelota, entonce el puntero debería quedarse sobre el borde mientras se arrastra. ![](ball_shift.svg) -Let's update our algorithm: +Vamos a actualizar nuestro algoritmo: -1. When a visitor presses the button (`mousedown`) - remember the distance from the pointer to the left-upper corner of the ball in variables `shiftX/shiftY`. We'll keep that distance while dragging. +1. Cuando un visitante presiona el botón (`mousedown`) - recordar la distancia del puntero a la esquina superior izquierda de la pelota in variables `shiftX/shiftY`. Mantendremos esa distancia mientras arrastramos. - To get these shifts we can substract the coordinates: + Para obtener esas posiciones podemos restar las coordenadas: ```js // onmousedown @@ -120,16 +120,16 @@ Let's update our algorithm: let shiftY = event.clientY - ball.getBoundingClientRect().top; ``` -2. Then while dragging we position the ball on the same shift relative to the pointer, like this: +2. Entonces mientras arrastra posicionamos la pelota en la misma posición relativa al puntero, de esta forma: ```js // onmousemove - // ball has position:absolute + // la pelota tiene position:absolute ball.style.left = event.pageX - *!*shiftX*/!* + 'px'; ball.style.top = event.pageY - *!*shiftY*/!* + 'px'; ``` -The final code with better positioning: +El código final con mejor posicionamiento: ```js ball.onmousedown = function(event) { @@ -145,8 +145,8 @@ ball.onmousedown = function(event) { moveAt(event.pageX, event.pageY); - // moves the ball at (pageX, pageY) coordinates - // taking initial shifts into account + // mueve la pelota a las coordenadas (pageX, pageY) + // tomando la posición inicial en cuenta function moveAt(pageX, pageY) { ball.style.left = pageX - *!*shiftX*/!* + 'px'; ball.style.top = pageY - *!*shiftY*/!* + 'px'; @@ -156,10 +156,10 @@ ball.onmousedown = function(event) { moveAt(event.pageX, event.pageY); } - // move the ball on mousemove + // mueve la pelota con mousemove document.addEventListener('mousemove', onMouseMove); - // drop the ball, remove unneeded handlers + // suelta la pelota, elimina el manejador innecesario ball.onmouseup = function() { document.removeEventListener('mousemove', onMouseMove); ball.onmouseup = null; @@ -173,32 +173,32 @@ ball.ondragstart = function() { ``` ```online -In action (inside `<iframe>`): +En acción (dentro de un `<iframe>`): [iframe src="ball3" height=230] ``` -The difference is especially noticeable if we drag the ball by its right-bottom corner. In the previous example the ball "jumps" under the pointer. Now it fluently follows the pointer from the current position. +La diferencia es notable especialmente si arrastramos la pelota por su esquina inferior derecha. En el ejemplo anterior la pelota "salta" bajo el puntero. Ahora sigue el puntero fluidamente desde su posición actual. -## Potential drop targets (droppables) +## Objetivos receptores potenciales (droppables) -In previous examples the ball could be dropped just "anywhere" to stay. In real-life we usually take one element and drop it onto another. For instance, a "file" into a "folder" or something else. +En los ejemplos anteriores la pelota debe ser soltada simplemente "en cualquier lugar" para quedarse. En la vida real normalmente tomamos un elemento para soltarlo en otro. Por ejemplo, un "archivo" en una "carpeta" o algo más. -Speaking abstract, we take a "draggable" element and drop it onto "droppable" element. +Hablando abstracto, tomamos un elemento "arrastrable" y lo soltamos sobre un elemento "receptor". -We need to know: -- where the element was dropped at the end of Drag'n'Drop -- to do the corresponding action, -- and, preferably, know the droppable we're dragging over, to highlight it. +Necesitamos saber: +- dónde el elemento fue soltado al final del Arrastrar y Soltar -- para hacer la acción correspondiente, +- y, preferiblemente, saber el receptor sobre el que estamos arrastrando, para resaltarlo. -The solution is kind-of interesting and just a little bit tricky, so let's cover it here. +La solución es algo interesante y un poco complicado, así que vamos a cubrirlo aquí. -What may be the first idea? Probably to set `mouseover/mouseup` handlers on potential droppables? +¿Cuál puede ser la primera idea? ¿Probablemente configurar `mouseover/mouseup` en receptores potenciales? -But that doesn't work. +Pero eso no funciona. -The problem is that, while we're dragging, the draggable element is always above other elements. And mouse events only happen on the top element, not on those below it. +El problema es que, mientras estamos arrastrando, el elemento arrastrable siempre está encima de los demás elementos. Y los eventos del ratón solo suceden en el elemento superior, no en los que están debajo. -For instance, below are two `<div>` elements, red one on top of the blue one (fully covers). There's no way to catch an event on the blue one, because the red is on top: +Por ejemplo, debajo hay dos elementos `<div>`, el rojo sobre el azul (totalmente cubierto). No hay forma de captar un evento en el azul, porque el rojo está encima: ```html run autorun height=60 <style> @@ -209,38 +209,38 @@ For instance, below are two `<div>` elements, red one on top of the blue one (fu top: 0; } </style> -<div style="background:blue" onmouseover="alert('never works')"></div> -<div style="background:red" onmouseover="alert('over red!')"></div> +<div style="background:blue" onmouseover="alert('nunca funciona')"></div> +<div style="background:red" onmouseover="alert('sobre el rojo!')"></div> ``` -The same with a draggable element. The ball is always on top over other elements, so events happen on it. Whatever handlers we set on lower elements, they won't work. +Lo mismo con un elemento arrastrable. La pelota está siempre sobre los demás elementos, así que los eventos pasan en él. Cualquier manejador que pongamos en los elementos de abajo, no funcionará. -That's why the initial idea to put handlers on potential droppables doesn't work in practice. They won't run. +Por eso la idea inicial de poner manejadores en receptores potenciales no funciona en práctica. No se activarán. -So, what to do? +Entonces, ¿Qué hacer? -There's a method called `document.elementFromPoint(clientX, clientY)`. It returns the most nested element on given window-relative coordinates (or `null` if given coordinates are out of the window). If there are multiple overlapping elements on the same coordinates, then the topmost one is returned. +Existe un método llamado `document.elementFromPoint(clientX, clientY)`. Este devuelve el elemento más anidado en las coordenadas relativas a la ventana proporcionada (o `null` si las coordenadas están fuera de la ventana). Si hay muchos elementos superpuestos en las mismas coordenadas, se devuelve el que está en el tope. -We can use it in any of our mouse event handlers to detect the potential droppable under the pointer, like this: +Podemos utilizarlo en cualquiera de nuestros manejadores para detectar los receptores potenciales bajo el puntero, de esta forma: ```js -// in a mouse event handler -ball.hidden = true; // (*) hide the element that we drag +// en un manejador de evento del ratón +ball.hidden = true; // (*) ocultar el elemento que arrastramos let elemBelow = document.elementFromPoint(event.clientX, event.clientY); -// elemBelow is the element below the ball, may be droppable +// elemBelow es el elemento debajo de la pelota, puede ser receptor ball.hidden = false; ``` -Please note: we need to hide the ball before the call `(*)`. Otherwise we'll usually have a ball on these coordinates, as it's the top element under the pointer: `elemBelow=ball`. So we hide it and immediately show again. +Favor notar: necesitamos ocultar la pelota antes de llamar `(*)`. De otra forma usualmente tendremos una pelota con esas coordenadas, ya que es el elemento superior bajo el puntero: `elemBelow=ball`. Así que lo ocultamos e inmediatamente lo mostramos de nuevo. -We can use that code to check what element we're "flying over" at any time. And handle the drop when it happens. +Podemos usar este código para verificar el elemento sobre el que estamos "flotando" en todo momento. Y manejar la caída cuando sucede. -An extended code of `onMouseMove` to find "droppable" elements: +Un código extendido de `onMouseMove` para hallar elementos "receptores": ```js -// potential droppable that we're flying over right now +// elemento potencialmente arrastrable sobre el que flotamos ahora mismo let currentDroppable = null; function onMouseMove(event) { @@ -250,54 +250,54 @@ function onMouseMove(event) { let elemBelow = document.elementFromPoint(event.clientX, event.clientY); ball.hidden = false; - // mousemove events may trigger out of the window (when the ball is dragged off-screen) - // if clientX/clientY are out of the window, then elementFromPoint returns null + // los eventos mousemove se pueden activar fuera de la ventana (cuando la pelota se arrastra fuera de la ventana) + // si clientX/clientY están fuera de la ventana, entonces elementFromPoint devuelve null if (!elemBelow) return; - // potential droppables are labeled with the class "droppable" (can be other logic) + // receptores potenciales se etiquetan con la clase "droppable" (puede tener otra lógica) let droppableBelow = elemBelow.closest('.droppable'); if (currentDroppable != droppableBelow) { - // we're flying in or out... - // note: both values can be null - // currentDroppable=null if we were not over a droppable before this event (e.g over an empty space) - // droppableBelow=null if we're not over a droppable now, during this event + // estamos flotando dentro o afuera + // nota: ambos valores pueden ser null + // currentDroppable=null si no estábamos sobre un receptor antes de este evento (ej. sobre un espacio en blanco) + // droppableBelow=null si no estamos sobre un receptor ahora, durante este evento if (currentDroppable) { - // the logic to process "flying out" of the droppable (remove highlight) + // la lógica para procesar "flying out" del receptor (elimina el resaltado) leaveDroppable(currentDroppable); } currentDroppable = droppableBelow; if (currentDroppable) { - // the logic to process "flying in" of the droppable + // la lógica para procesar "flying in" del receptor enterDroppable(currentDroppable); } } } ``` -In the example below when the ball is dragged over the soccer goal, the goal is highlighted. +En el siguiente ejemplo cuando la pelota se arrastra sobre la portería, esta se resalta. [codetabs height=250 src="ball4"] -Now we have the current "drop target", that we're flying over, in the variable `currentDroppable` during the whole process and can use it to highlight or any other stuff. +Ahora tenemos el "destino" actual, sobre el que estamos flotando, en la variable `currentDroppable` durante el proceso completo y podemos usarlo para resaltar o cualquier otra cosa. -## Summary +## Resumen -We considered a basic Drag'n'Drop algorithm. +Consideramos un algoritmo básico de Arrastrar y Soltar. -The key components: +Los componentes clave: -1. Events flow: `ball.mousedown` -> `document.mousemove` -> `ball.mouseup` (don't forget to cancel native `ondragstart`). -2. At the drag start -- remember the initial shift of the pointer relative to the element: `shiftX/shiftY` and keep it during the dragging. -3. Detect droppable elements under the pointer using `document.elementFromPoint`. +1. Flujo de eventos: `ball.mousedown` -> `document.mousemove` -> `ball.mouseup` (no olvides cancelar el `ondragstart` nativo). +2. El inicio del arrastrado -- recuerda la posición inicial del puntero relativo al elemento: `shiftX/shiftY` y lo mantiene durante el arrastrado. +3. Detectar elementos arrastrables bajo el puntero usando `document.elementFromPoint`. -We can lay a lot on this foundation. +Podemos poner mucho sobre esta base. -- On `mouseup` we can intellectually finalize the drop: change data, move elements around. -- We can highlight the elements we're flying over. -- We can limit dragging by a certain area or direction. -- We can use event delegation for `mousedown/up`. A large-area event handler that checks `event.target` can manage Drag'n'Drop for hundreds of elements. -- And so on. +- Con `mouseup` podemos intelectualmente finalizar el arrastre: cambiar datos, mover elementos alrededor. +- Podemos resaltar los elementos sobre los que estamos volando. +- Podemos limitar el arrastrado a cierta área o dirección. +- Podemos usar delegación de eventos para `mousedown/up`. Un manejador de eventos para un área grande que compruebe `event.target` puede manejar Arrastrar y Soltar para cientos de elementos. +- Y así por el estilo. -There are frameworks that build architecture over it: `DragZone`, `Droppable`, `Draggable` and other classes. Most of them do the similar stuff to what's described above, so it should be easy to understand them now. Or roll your own, as you can see that that's easy enough to do, sometimes easier than adapting a third-party solution. +Hay frameworks que construyen una arquitectura sobre esto: `DragZone`, `Droppable`, `Draggable` y otras clases. La mayoría de ellos hacen cosas similares a lo que hemos descrito, así que debería ser fácil entenderlos ahora. O crea el tuyo propio: como puedes ver es fácil de hacer, a veces es más fácil que adaptarse a una solución de terceros. diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/ball4.view/index.html b/2-ui/3-event-details/4-mouse-drag-and-drop/ball4.view/index.html index aaef2f3b8..210fd21be 100644 --- a/2-ui/3-event-details/4-mouse-drag-and-drop/ball4.view/index.html +++ b/2-ui/3-event-details/4-mouse-drag-and-drop/ball4.view/index.html @@ -44,12 +44,12 @@ let droppableBelow = elemBelow.closest('.droppable'); if (currentDroppable != droppableBelow) { - if (currentDroppable) { // null when we were not over a droppable before this event + if (currentDroppable) { // null si no estábamos sobre un receptor (droppable) antes de este evento leaveDroppable(currentDroppable); } currentDroppable = droppableBelow; - if (currentDroppable) { // null if we're not coming over a droppable now - // (maybe just left the droppable) + if (currentDroppable) { // null si no estamos sobre un receptor ahora + // (pudo haber dejado el receptor recién) enterDroppable(currentDroppable); } } diff --git a/2-ui/3-event-details/6-pointer-events/article.md b/2-ui/3-event-details/6-pointer-events/article.md index ecc144712..7c7e24a5c 100644 --- a/2-ui/3-event-details/6-pointer-events/article.md +++ b/2-ui/3-event-details/6-pointer-events/article.md @@ -1,36 +1,36 @@ -# Pointer events +# Eventos de puntero -Pointer events are a modern way to handle input from a variety of pointing devices, such as a mouse, a pen/stylus, a touchscreen, and so on. +Los eventos de puntero son una forma moderna de manejar la entrada de una variedad de dispositivos señaladores, como un mouse, un lápiz, una pantalla táctil, etc. -## The brief history +## Una breve historia -Let's make a small overview, so that you understand the general picture and the place of Pointer Events among other event types. +Hagamos una pequeña descripción general para que comprenda la imagen general y el lugar de los Eventos de Puntero entre otros tipos de eventos. -- Long ago, in the past, there were only mouse events. +- Hace mucho tiempo, en el pasado, solo existían eventos de mouse. - Then touch devices became widespread, phones and tablets in particular. For the existing scripts to work, they generated (and still generate) mouse events. For instance, tapping a touchscreen generates `mousedown`. So touch devices worked well with web pages. + Luego aparecieron los dispositivos táctiles, teléfonos y tablets en particular. Para que el código antiguo funcionara, generanban (y aún lo hacen) eventos de mouse. Por ejemplo, tocar la pantalla táctil genera `mousedown`. Así los dispositivos táctiles funcionaban bien con las páginas web. + + Pero los dispositivos táctiles tienen más capacidades que un mouse. Por ejemplo, es posible tocar múltiples puntos al mismo ("multi-touch"). Los eventos del mouse no tienen las propiedades necesarias para manejar tal multi-touch. - But touch devices have more capabilities than a mouse. For example, it's possible to touch multiple points at once ("multi-touch"). Although, mouse events don't have necessary properties to handle such multi-touches. +- Por lo tanto, se introdujeron eventos táctiles, como `touchstart`,`touchend`, `touchmove`, que tienen propiedades específicas de toque (no los cubrimos en detalle aquí, porque los eventos de puntero son aún mejores). -- So touch events were introduced, such as `touchstart`, `touchend`, `touchmove`, that have touch-specific properties (we don't cover them in detail here, because pointer events are even better). + Aún así no fue suficiente, ya que hay muchos otros dispositivos, como los lápices, que tienen sus propias funciones. Y escribir código que escuchara ambos eventos, los táctiles y los del mouse, era engorroso. - Still, it wasn't enough, as there are many other devices, such as pens, that have their own features. Also, writing code that listens for both touch and mouse events was cumbersome. +- Para resolver estos problemas, se introdujo el nuevo estándar: *Pointer Events*. Este proporciona un conjunto único de eventos para todo tipo de dispositivos señaladores. -- To solve these issues, the new standard Pointer Events was introduced. It provides a single set of events for all kinds of pointing devices. +Al momento la especificación [Pointer Events Level 2](https://www.w3.org/TR/pointerevents2/) es soportada en todos los principales navegadores, mientras que [Pointer Events Level 3](https://w3c.github.io/pointerevents/) está en proceso y es mayormente compatible con Pointer Events level 2. -As of now, [Pointer Events Level 2](https://www.w3.org/TR/pointerevents2/) specification is supported in all major browsers, while the newer [Pointer Events Level 3](https://w3c.github.io/pointerevents/) is in the works and is mostly compatible with Pointer Events level 2. +A menos que escribas código para navegadores viejos tales como Internet Explorer o Safari 12 y versiones anteriores, ya no tiene sentido usar eventos de mouse o táctiles: podemos pasar a eventos de puntero. -Unless you develop for old browsers, such as Internet Explorer 10, or for Safari 12 or below, there's no point in using mouse or touch events any more -- we can switch to pointer events. +Así tu código funcionará tanto con mouse como con dispositivos táctiles. -Then your code will work well with both touch and mouse devices. +Dicho esto, hay peculiaridades importantes, uno debe saber usarlas correctamente y evitar sorpresas adicionales. Les prestaremos atención en este artículo. -That said, there are some important peculiarities that one should know in order to use Pointer Events correctly and avoid surprises. We'll make note of them in this article. +## Tipos de eventos de puntero -## Pointer event types +Los eventos de puntero se llaman de forma similar a los eventos del mouse: -Pointer events are named similarly to mouse events: - -| Pointer event | Similar mouse event | +| Evento de puntero | Evento de mouse similar | |---------------|-------------| | `pointerdown` | `mousedown` | | `pointerup` | `mouseup` | @@ -43,142 +43,142 @@ Pointer events are named similarly to mouse events: | `gotpointercapture` | - | | `lostpointercapture` | - | -As we can see, for every `mouse<event>`, there's a `pointer<event>` that plays a similar role. Also there are 3 additional pointer events that don't have a corresponding `mouse...` counterpart, we'll explain them soon. +Como podemos ver, para cada `mouse<event>`, hay un `pointer<event>` que juega un papel similar. También hay 3 eventos de puntero adicionales que no tienen una contraparte correspondiente de `mouse ...`, pronto hablaremos sobre ellos. -```smart header="Replacing `mouse<event>` with `pointer<event>` in our code" -We can replace `mouse<event>` events with `pointer<event>` in our code and expect things to continue working fine with mouse. +```smart header="Remplazando *mouse<event>* con *pointer<event>* en nuestro código" +Podemos reemplazar los eventos `mouse<event>` con `pointer<event>` en nuestro código y esperar que las cosas sigan funcionando bien con el mouse. -The support for touch devices will also "magically" improve. Although, we may need to add `touch-action: none` in some places in CSS. We'll cover it below in the section about `pointercancel`. +El soporte para dispositivos táctiles mejorará "mágicamente". Pero probablemente necesitemos agregar la regla `touch-action: none` en CSS. Cubriremos esto en la sección sobre `pointercancel`. ``` -## Pointer event properties - -Pointer events have the same properties as mouse events, such as `clientX/Y`, `target`, etc., plus some others: +## Propiedades de los eventos de puntero -- `pointerId` - the unique identifier of the pointer causing the event. +Los eventos de puntero tienen las mismas propiedades que los eventos del mouse, como `clientX/Y`, `target`, etc., más algunos adicionales: - Browser-generated. Allows us to handle multiple pointers, such as a touchscreen with stylus and multi-touch (examples will follow). -- `pointerType` - the pointing device type. Must be a string, one of: "mouse", "pen" or "touch". +- `pointerId` - el identificador único del puntero que causa el evento. + + Generado por el navegador. Permite manejar múltiples punteros, como una pantalla táctil con lápiz y multitáctil (explicado a continuación). +- `pointerType` - el tipo de dispositivo señalador. Debe ser una cadena, uno de los siguientes: "mouse", "pen" o "touch". - We can use this property to react differently on various pointer types. -- `isPrimary` - is `true` for the primary pointer (the first finger in multi-touch). + Podemos usar esta propiedad para reaccionar de manera diferente en varios tipos de punteros. +- `isPrimary` - `true` para el puntero principal (el primer dedo en multitáctil). -Some pointer devices measure contact area and pressure, e.g. for a finger on the touchscreen, there are additional properties for that: +Para punteros que miden un área de contacto y presión, p. Ej. un dedo en la pantalla táctil, las propiedades adicionales pueden ser útiles: -- `width` - the width of the area where the pointer (e.g. a finger) touches the device. Where unsupported, e.g. for a mouse, it's always `1`. -- `height` - the height of the area where the pointer touches the device. Where unsupported, it's always `1`. -- `pressure` - the pressure of the pointer tip, in range from 0 to 1. For devices that don't support pressure must be either `0.5` (pressed) or `0`. -- `tangentialPressure` - the normalized tangential pressure. -- `tiltX`, `tiltY`, `twist` - pen-specific properties that describe how the pen is positioned relative to the surface. +- `width` - el ancho del área donde el puntero (p.ej. el dedo) toca el dispositivo. Si el dispositivo no lo soporta (como el mouse), es siempre `1`. +- `height` - el alto del área donde el puntero toca el dispositivo. Donde no lo soporte es siempre `1`. +- `pressure` - la presión de la punta del puntero, en el rango de 0 a 1. En dispositivos que no soportan presión, debe ser `0.5` (presionada) o `0`. +- `tangentialPressure` - la presión tangencial normalizada. +- `tiltX`, `tiltY`, `twist` - propiedades específicas para un lápiz digital, describen cómo se lo coloca en relación con la superficie. -These properties aren't supported by most devices, so they are rarely used. You can find the details about them in the [specification](https://w3c.github.io/pointerevents/#pointerevent-interface) if needed. +En la mayoría de los dispositivos estas propiedades no están soportadas, por lo que rara vez se utilizan. Si lo necesita puede encontrar los detalles en la [especificación](https://w3c.github.io/pointerevents/#pointerevent-interface). -## Multi-touch +## Multi-touch (Multitáctil) -One of the things that mouse events totally don't support is multi-touch: a user can touch in several places at once on their phone or tablet, or perform special gestures. +Una de las cosas que los eventos del mouse no soportan es la propiedad multitáctil: un usuario puede tocar en varios lugares a la vez en su teléfono o tableta, realizar gestos especiales. -Pointer Events allow handling multi-touch with the help of the `pointerId` and `isPrimary` properties. +Los eventos de puntero permiten manejar multitáctiles con la ayuda de las propiedades `pointerId` e `isPrimary`. -Here's what happens when a user touches a touchscreen in one place, then puts another finger somewhere else on it: +Esto es lo que sucede cuando un usuario toca una pantalla en un lugar y luego coloca otro dedo en otro lugar: -1. At the first finger touch: - - `pointerdown` with `isPrimary=true` and some `pointerId`. -2. For the second finger and more fingers (assuming the first one is still touching): - - `pointerdown` with `isPrimary=false` and a different `pointerId` for every finger. +1. En el primer toque: + - `pointerdown` with `isPrimary=true` y algún `pointerId`. +2. Para el segundo dedo y toques posteriores (asumiendo que el primero sigue tocando): + - `pointerdown` con `isPrimary=false` y un diferente `pointerId` por cada dedo. -Please note: the `pointerId` is assigned not to the whole device, but for each touching finger. If we use 5 fingers to simultaneously touch the screen, we have 5 `pointerdown` events, each with their respective coordinates and a different `pointerId`. +Tenga en cuenta: el `pointerId` no se asigna a todo el dispositivo, sino a cada dedo que se toca. Si usamos 5 dedos para tocar simultáneamente la pantalla, tenemos 5 eventos `pointerdown` con coordenadas respectivas y diferentes `pointerId`. -The events associated with the first finger always have `isPrimary=true`. +Los eventos asociados con el primer dedo siempre tienen `isPrimary = true`. -We can track multiple touching fingers using their `pointerId`. When the user moves and then removes a finger, we get `pointermove` and `pointerup` events with the same `pointerId` as we had in `pointerdown`. +Podemos rastrear el toque de varios dedos usando sus respectivos `pointerId`. Cuando el usuario mueve un dedo y luego lo quita, obtenemos los eventos `pointermove` y `pointerup` con el mismo `pointerId` que teníamos en `pointerdown` ```online -Here's the demo that logs `pointerdown` and `pointerup` events: +Aquí está la demostración que registra los eventos `pointerdown` y `pointerup`: [iframe src="multitouch" edit height=200] -Please note: you must be using a touchscreen device, such as a phone or a tablet, to actually see the difference in `pointerId/isPrimary`. For single-touch devices, such as a mouse, there'll be always same `pointerId` with `isPrimary=true`, for all pointer events. +Tenga en cuenta que debe utilizar un dispositivo con pantalla táctil, como un teléfono o una tableta, para ver realmente la diferencia en `pointerId/isPrimary`. Para dispositivos de un solo toque, como el de un mouse, siempre será el mismo `pointerId` con `isPrimary=true` para todos los eventos de puntero. ``` -## Event: pointercancel +## Evento: pointercancel -The `pointercancel` event fires when there's an ongoing pointer interaction, and then something happens that causes it to be aborted, so that no more pointer events are generated. +El evento `pointercancel` se dispara cuando, mientras hay una interacción de puntero en curso, sucede algo que hace que esta se anule de modo que no se generan más eventos de puntero. -Such causes are: -- The pointer device hardware was physically disabled. -- The device orientation changed (tablet rotated). -- The browser decided to handle the interaction on its own, considering it a mouse gesture or zoom-and-pan action or something else. +Las causas son: +- Se deshabilitó el hardware del dispositivo de puntero. +- La orientación del dispositivo cambió (tableta rotada). +- El navegador decidió manejar la interacción por su cuenta: porque lo consideró un gesto de mouse, una acción de zoom, o alguna otra cosa. -We'll demonstrate `pointercancel` on a practical example to see how it affects us. +Demostraremos `pointercancel` en un ejemplo práctico para ver cómo nos afecta. -Let's say we're implementing drag'n'drop for a ball, just as in the beginning of the article <info:mouse-drag-and-drop>. +Digamos que queremos una implementación de "arrastrar y soltar" en una pelota, como la que está al principio del artículo <info:mouse-drag-and-drop>. -Here is the flow of user actions and the corresponding events: +A continuación, se muestra el flujo de acciones del usuario y los eventos correspondientes: -1) The user presses on an image, to start dragging - - `pointerdown` event fires -2) Then they start moving the pointer (thus dragging the image) - - `pointermove` fires, maybe several times -3) And then the surprise happens! The browser has native drag'n'drop support for images, that kicks in and takes over the drag'n'drop process, thus generating `pointercancel` event. - - The browser now handles drag'n'drop of the image on its own. The user may even drag the ball image out of the browser, into their Mail program or a File Manager. - - No more `pointermove` events for us. +1) El usuario presiona sobre una imagen para comenzar a arrastrar + - `pointerdown` el evento se dispara +2) Luego comienzan a mover el puntero (arrastrando la imagen) + - `pointermove` se dispara, tal vez varias veces +3) ¡Sorpresa! El navegador tiene soporte nativo de arrastrar y soltar para imágenes, este bloquea el nuestro y se hace cargo del proceso de arrastrar y soltar, generando el evento `pointercancel`. + - El navegador ahora maneja arrastrar y soltar la imagen por sí solo. El usuario puede incluso arrastrar la imagen de la bola fuera del navegador, a su programa de correo o al administrador de archivos. + - No más eventos `pointermove` para nosotros. -So the issue is that the browser "hijacks" the interaction: `pointercancel` fires in the beginning of the "drag-and-drop" process, and no more `pointermove` events are generated. +Así que el problema es que el navegador "secuestra" la interacción: `pointercancel` se dispara y no se generan más eventos de `pointermove`. ```online -Here's the drag'n'drop demo with logging of pointer events (only `up/down`, `move` and `cancel`) in the `textarea`: +Esta es la demo "arrastrar y soltar" con el registro de eventos de puntero (solamente `arriba/abajo`, `mover` y `cancelar`) registrados en `textarea`: [iframe src="ball" height=240 edit] ``` -We'd like to implement the drag'n'drop on our own, so let's tell the browser not to take it over. +Queremos implementar nuestro propio arrastrar y soltar, así que digámosle al navegador que no se haga cargo. -**Prevent the default browser action to avoid `pointercancel`.** +**Evitar las acciones predeterminadas del navegador para evitar `pointercancel`.** -We need to do two things: +Necesitaremos dos cosas: -1. Prevent native drag'n'drop from happening: - - We can do this by setting `ball.ondragstart = () => false`, just as described in the article <info:mouse-drag-and-drop>. - - That works well for mouse events. -2. For touch devices, there are other touch-related browser actions (besides drag'n'drop). To avoid problems with them too: - - Prevent them by setting `#ball { touch-action: none }` in CSS. - - Then our code will start working on touch devices. +1. Evitar que suceda la función nativa de arrastrar y soltar: + - Puede hacerlo configurando `ball.ondragstart = () => false`, tal como se describe en el artículo <info:mouse-drag-and-drop>. + - Eso funciona bien para eventos de mouse. +2. Para los dispositivos táctiles, también existen acciones del navegador relacionadas con el tacto (además de arrastrar y soltar). Para evitar problemas con ellos también: + - Configurar `#ball{touch-action: none}` en CSS. + - Entonces nuestro código comenzará a funcionar en dispositivos táctiles. -After we do that, the events will work as intended, the browser won't hijack the process and doesn't emit `pointercancel`. +Después de hacer eso, los eventos funcionarán según lo previsto, el navegador no secuestrará el proceso y no emitirá ningún `pointercancel`. ```online -This demo adds these lines: +Esta demostración agrega estas líneas: [iframe src="ball-2" height=240 edit] -As you can see, there's no `pointercancel` any more. +Como puede ver, ya no hay `pointercancel`. ``` -Now we can add the code to actually move the ball, and our drag'n'drop will work for mouse devices and touch devices. +Ahora podemos agregar el código para mover realmente la bola, y nuestro método de arrastrar y soltar funcionará en dispositivos de mouse y dispositivos táctiles. -## Pointer capturing +## Captura del puntero -Pointer capturing is a special feature of pointer events. +La captura de puntero es una característica especial de los eventos de puntero. -The idea is very simple, but may seem quite odd at first, as nothing like that exists for any other event type. +La idea es muy simple, pero puede verse extraña al principio, porque no existe algo así para ningún otro tipo de evento. -The main method is: -- `elem.setPointerCapture(pointerId)` -- binds events with the given `pointerId` to `elem`. After the call all pointer events with the same `pointerId` will have `elem` as the target (as if happened on `elem`), no matter where in document they really happened. +El método principal es: +- `elem.setPointerCapture(pointerId)` -- vincula el `pointerId` dado a `elem`. Después del llamado todos los eventos de puntero con el mismo `pointerId` tendrán `elem` como objetivo (como si ocurrieran sobre `elem`), no importa dónde hayan ocurrido en realidad. -In other words, `elem.setPointerCapture(pointerId)` retargets all subsequent events with the given `pointerId` to `elem`. +En otras palabras: `elem.setPointerCapture(pointerId)` redirige hacia `elem` todos los eventos subsecuentes que tengan el `pointerId` dado. -The binding is removed: -- automatically when `pointerup` or `pointercancel` events occur, -- automatically when `elem` is removed from the document, -- when `elem.releasePointerCapture(pointerId)` is called. +El vínculo se deshace:: +- automáticamente cuando ocurren los eventos `pointerup` o `pointercancel`, +- automáticamente cuando `elem` es quitado del documento, +- cuando `elem.releasePointerCapture(pointerId)` es llamado. -Now what is it good for? It's time to see a real-life example. +Ahora, ¿para qué es bueno esto? Momento de ver un ejemplo de la vida real. -**Pointer capturing can be used to simplify drag'n'drop kind of interactions.** +**La captura de puntero puede utilizarse para simplificar interacciones del tipo "arrastrar y soltar".** -Let's recall how one can implement a custom slider, described in the <info:mouse-drag-and-drop>. +Recordemos cómo uno puede implementar un control deslizante personalizado, descrito en el artículo <info:mouse-drag-and-drop>. -We can make a `slider` element to represent the strip and the "runner" (`thumb`) inside it: +Podemos hacer un elemento `slider` que representa la corredera con una perilla `thumb` dentro. ```html <div class="slider"> @@ -186,97 +186,97 @@ We can make a `slider` element to represent the strip and the "runner" (`thumb`) </div> ``` -With styles, it looks like this: +Con estilos, se ve así: [iframe src="slider-html" height=40 edit] <p></p> -And here's the working logic, as it was described, after replacing mouse events with similar pointer events: +Esta es la lógica de funcionamientodespués de reemplazar eventos de mouse con sus equivalentes de puntero: -1. The user presses on the slider `thumb` -- `pointerdown` triggers. -2. Then they move the pointer -- `pointermove` triggers, and our code moves the `thumb` element along. - - ...As the pointer moves, it may leave the slider `thumb` element, go above or below it. The `thumb` should move strictly horizontally, remaining aligned with the pointer. +1. El usuario presiona en el deslizante `thumb`: se dispara `pointerdown` +2. Entonces mueve el puntero: se dispara `pointermove` y nuestro código mueve el botón `thumb` a lo largo. + - ...Mientras el puntero se mueve, puede salirse del control deslizante: que vaya por debajo o por encima de él. El botón debe moverse de forma estrictamente horizontal, permaneciendo alineado con el puntero. -In the mouse event based solution, to track all pointer movements, including when it goes above/below the `thumb`, we had to assign `mousemove` event handler on the whole `document`. +En la solución basada en eventos de mouse, para rastrear todos los movimientos del puntero incluyendo aquellos por arriba o por debajo de `thumb`, asignamos el controlador de evento `mousemove` al `document` entero. -That's not a cleanest solution, though. One of the problems is that when a user moves the pointer around the document, it may trigger event handlers (such as `mouseover`) on some other elements, invoke totally unrelated UI functionality, and we don't want that. +No es la solución más limpia. Uno de los problemas es que cuando el usuario mueve el puntero por el documento puede disparar manejadores de eventos (como `mouseover`) en otros elementos invocando funcionalidad de interfaz completamente sin relación al deslizante. -This is the place where `setPointerCapture` comes into play. +Aquí es donde entra en juego `setPointerCapture`. -- We can call `thumb.setPointerCapture(event.pointerId)` in `pointerdown` handler, -- Then future pointer events until `pointerup/cancel` will be retargeted to `thumb`. -- When `pointerup` happens (dragging complete), the binding is removed automatically, we don't need to care about it. +- Podemos llamar `thumb.setPointerCapture(event.pointerId)` en el controlador `pointerdown`, +- Entonces futuros eventos de puntero hasta `pointerup/cancel` serán redirigidos a `thumb`. +- Cuando ocurre `pointerup` (arrastre finalizado), el vínculo se deshace automáticamente, no necesitamos atender eso. -So, even if the user moves the pointer around the whole document, events handlers will be called on `thumb`. Nevertheless, coordinate properties of the event objects, such as `clientX/clientY` will still be correct - the capturing only affects `target/currentTarget`. +Entonces, incluso si el usuario mueve el puntero alrededor de todo el documento, los controladores de eventos serán llamados sobre `thumb`. A pesar de ello las propiedades de coordenadas de los eventos, tales como `clientX/clientY` aún serán correctas, la captura solo afecta a `target/currentTarget`. -Here's the essential code: +Aquí está el código esencial: ```js thumb.onpointerdown = function(event) { - // retarget all pointer events (until pointerup) to thumb + // reorienta todos los eventos de puntero (hasta pointerup) a thumb thumb.setPointerCapture(event.pointerId); - // start tracking pointer moves + // comienza a rastrear movimientos de puntero thumb.onpointermove = function(event) { - // moving the slider: listen on the thumb, as all pointer events are retargeted to it + // se mueve el control deslizante: escucha a thumb, ya que todos los eventos se redirigen a él let newLeft = event.clientX - slider.getBoundingClientRect().left; thumb.style.left = newLeft + 'px'; }; - - // on pointer up finish tracking pointer moves + + // on pointer up finaliza el seguimiento thumb.onpointerup = function(event) { thumb.onpointermove = null; thumb.onpointerup = null; - // ...also process the "drag end" if needed + // ...también procesa "fin de arrastre" si es necesario }; }; -// note: no need to call thumb.releasePointerCapture, -// it happens on pointerup automatically +// nota: no es necesario llamar a thumb.releasePointerCapture, +// esto sucede con el pointerup automáticamente ``` ```online -The full demo: +La demostración completa: [iframe src="slider" height=100 edit] <p></p> -In the demo, there's also an additional element with `onmouseover` handler showing the current date. +En el demo, hay un elemento adicional con un controlador `onmouseover` que muestra la fecha actual. -Please note: while you're dragging the thumb, you may hover over this element, and its handler *does not* trigger. +Nota que mientras arrastras el dial, puedes pasar sobre ese elemento y su controlador *no* se dispara. -So the dragging is now free of side effects, thanks to `setPointerCapture`. +Entonces el arrastre esta ahora libre de efectos secundarios gracias a `setPointerCapture`. ``` -At the end, pointer capturing gives us two benefits: -1. The code becomes cleaner as we don't need to add/remove handlers on the whole `document` any more. The binding is released automatically. -2. If there are other pointer event handlers in the document, they won't be accidentally triggered by the pointer while the user is dragging the slider. +Finalizando, la captura de puntero nos brinda dos beneficios: +1. El código se vuelve más claro, ya no necesitamos agregar o quitar controladores para el `document` entero. El vínculo se deshace automáticamente. +2. Si hay cualquier otro controlador de evento en el documento, no serán disparados accidentalmente mientras el usuario está arrastrando el deslizante. -### Pointer capturing events +### Eventos de captura de puntero -There's one more thing to mention here, for the sake of completeness. +Una cosa más por mencionar en bien de la exhaustividad. -There are two events associated with pointer capturing: +Hay dos eventos de puntero asociados con la captura de puntero: -- `gotpointercapture` fires when an element uses `setPointerCapture` to enable capturing. -- `lostpointercapture` fires when the capture is released: either explicitly with `releasePointerCapture` call, or automatically on `pointerup`/`pointercancel`. +- `gotpointercapture` se dispara cuando un elemento usa `setPointerCapture` para permitir la captura. +- `lostpointercapture` se dispara cuando se libera la captura: ya sea explícitamente con la llamada a `releasePointerCapture`, o automáticamente con `pointerup`/`pointercancel`. -## Summary +## Resumen -Pointer events allow handling mouse, touch and pen events simultaneously, with a single piece of code. +Los eventos de puntero permiten manejar eventos de mouse, toque y lápiz simultáneamente con una simple pieza de código. -Pointer events extend mouse events. We can replace `mouse` with `pointer` in event names and expect our code to continue working for mouse, with better support for other device types. +Los eventos de puntero extienden los eventos del mouse. Podemos reemplazar `mouse` con `pointer` en los nombres de los eventos y esperar que nuestro código continúe funcionando para el mouse, con mejor soporte para otros tipos de dispositivos. -For drag'n'drops and complex touch interactions that the browser may decide to hijack and handle on its own - remember to cancel the default action on events and set `touch-action: none` in CSS for elements that we engage. +Para arrastrar y soltar, y complejas interacciones que el navegador pudiera decidir secuestrar y manejar por su cuenta, recuerde cancelar la acción predeterminada sobre eventos y establecer `touch-action: none` en CSS para los elementos que involucramos. -Additional abilities of pointer events are: +Las habilidades adicionales de los eventos Pointer son: -- Multi-touch support using `pointerId` and `isPrimary`. -- Device-specific properties, such as `pressure`, `width/height`, and others. -- Pointer capturing: we can retarget all pointer events to a specific element until `pointerup`/`pointercancel`. +- Soporte multitáctil usando `pointerId` y `isPrimary`. +- Propiedades específicas del dispositivo, como `pressure`, `width/height` y otras. +- Captura de puntero: podemos redirigir todos los eventos de puntero a un elemento específico hasta `pointerup`/`pointercancel`. -As of now, pointer events are supported in all major browsers, so we can safely switch to them, especially if IE10- and Safari 12- are not needed. And even with those browsers, there are polyfills that enable the support of pointer events. +Al momento los eventos de puntero son compatibles con todos los navegadores principales, por lo que podemos cambiarlos de forma segura si no se necesitan IE10 y Safari 12. E incluso con esos navegadores, existen polyfills que permiten la compatibilidad con eventos de puntero. diff --git a/2-ui/3-event-details/6-pointer-events/slider.view/index.html b/2-ui/3-event-details/6-pointer-events/slider.view/index.html index b29e646a1..a98891865 100644 --- a/2-ui/3-event-details/6-pointer-events/slider.view/index.html +++ b/2-ui/3-event-details/6-pointer-events/slider.view/index.html @@ -12,7 +12,7 @@ let shiftX; function onThumbDown(event) { - event.preventDefault(); // prevent selection start (browser action) + event.preventDefault(); // evita el inicio de la selección (acción del navegador) shiftX = event.clientX - thumb.getBoundingClientRect().left; @@ -21,8 +21,8 @@ thumb.onpointermove = onThumbMove; thumb.onpointerup = event => { - // dragging finished, no need to track pointer any more - // ...any other "drag end" logic here... + // arrastre finalizado, no se necesita seguir rastreando el puntero + // ... toda otra lógica de finalización de arrastre aquí... thumb.onpointermove = null; thumb.onpointerup = null; } @@ -31,7 +31,7 @@ function onThumbMove(event) { let newLeft = event.clientX - shiftX - slider.getBoundingClientRect().left; - // if the pointer is out of slider => adjust left to be within the boundaries + // si el puntero está fuera del slider => ajustar "izquierda" para que esté dentro de los límites if (newLeft < 0) { newLeft = 0; } diff --git a/2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/solution.md b/2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/solution.md index 453f8c946..7171b93cf 100644 --- a/2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/solution.md +++ b/2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/solution.md @@ -1,6 +1,6 @@ -We should use two handlers: `document.onkeydown` and `document.onkeyup`. +Debemos manejar dos eventos: `document.onkeydown` y `document.onkeyup`. -Let's create a set `pressed = new Set()` to keep currently pressed keys. +Creemos un set `pressed = new Set()` para registrar las teclas presionads actualmente. -The first handler adds to it, while the second one removes from it. Every time on `keydown` we check if we have enough keys pressed, and run the function if it is so. +El primer manejador las agrega en él, mientras que el segundo las quita. Con cada `keydown` verificamos si tenemos suficientes teclas presionadas, y ejecutamos la función si es así. diff --git a/2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/solution.view/index.html b/2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/solution.view/index.html index 6e86d2455..660d584fa 100644 --- a/2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/solution.view/index.html +++ b/2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/solution.view/index.html @@ -2,7 +2,7 @@ <html> <body> - <p>Press "Q" and "W" together (can be in any language).</p> + <p>Presione "Q" y "W" juntas (puede ser en cualquier lenguaje).</p> <script> function runOnKeys(func, ...codes) { @@ -11,19 +11,19 @@ document.addEventListener('keydown', function(event) { pressed.add(event.code); - for (let code of codes) { // are all keys in the set? + for (let code of codes) { // ¿están todas las teclas en el set? if (!pressed.has(code)) { return; } } - // yes, they are + // sí, lo están - // during the alert, if the visitor releases the keys, - // JavaScript does not get the "keyup" event - // and pressed set will keep assuming that the key is pressed - // so, to evade "sticky" keys, we reset the status - // if the user wants to run the hotkey again - let them press all keys again + // durante el alert, si el visitante suelta las teclas, + // JavaScript no obtiene el evento "keyup" + // y el set "pressed" las mantendrá asumiendo que las teclas siguen presionadas; + // por ello, para evitar teclas "pegadas", reseteamos el estado + // si el usuario quiere usa el atajo de nuevo, permitámoslo pressed.clear(); func(); diff --git a/2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/task.md b/2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/task.md index 51c2fa2bd..0156df9f5 100644 --- a/2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/task.md +++ b/2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/task.md @@ -2,15 +2,15 @@ importance: 5 --- -# Extended hotkeys +# Extendiendo atajos de teclado -Create a function `runOnKeys(func, code1, code2, ... code_n)` that runs `func` on simultaneous pressing of keys with codes `code1`, `code2`, ..., `code_n`. +Crea una función `runOnKeys(func, code1, code2, ... code_n)` que ejecute `func` al presionar simultáneamente las teclas con códigos `code1`, `code2`, ..., `code_n`. -For instance, the code below shows `alert` when `"Q"` and `"W"` are pressed together (in any language, with or without CapsLock) +Por ejemplo, el siguiente código muestra un `alert` cuando `"Q"` y `"W"` se presionan juntas (en cualquier lenguaje, con o sin mayúscula) ```js no-beautify runOnKeys( - () => alert("Hello!"), + () => alert("¡Hola!"), "KeyQ", "KeyW" ); diff --git a/2-ui/3-event-details/7-keyboard-events/article.md b/2-ui/3-event-details/7-keyboard-events/article.md index 12fe63201..5d6711035 100644 --- a/2-ui/3-event-details/7-keyboard-events/article.md +++ b/2-ui/3-event-details/7-keyboard-events/article.md @@ -1,67 +1,67 @@ -# Keyboard: keydown and keyup +# Teclado: keydown y keyup -Before we get to keyboard, please note that on modern devices there are other ways to "input something". For instance, people use speech recognition (especially on mobile devices) or copy/paste with the mouse. +Antes de llegar al teclado, por favor ten en cuenta que en los dispositivos modernos hay otras formas de "ingresar algo". Por ejemplo, el uso de reconocimiento de voz (especialmente en dispositivos móviles) o copiar/pegar con el mouse. -So if we want to track any input into an `<input>` field, then keyboard events are not enough. There's another event named `input` to track changes of an `<input>` field, by any means. And it may be a better choice for such task. We'll cover it later in the chapter <info:events-change-input>. +Entonces, si queremos hacer el seguimiento de cualquier ingreso en un campo `<input>`, los eventos de teclado no son suficientes. Existe otro evento llamado `input` para detectar cambios en un campo `<input>` producidos por cualquier medio. Y puede ser una mejor opción para esa tarea. Lo estudiaremos más adelante, en el capítulo <info:events-change-input>. -Keyboard events should be used when we want to handle keyboard actions (virtual keyboard also counts). For instance, to react on arrow keys `key:Up` and `key:Down` or hotkeys (including combinations of keys). +Los eventos de teclado solo deberían ser usados cuando queremos manejar acciones de teclado (también cuentan los teclados virtuales). Por ejemplo, para reaccionar a las teclas de flecha `key:Up` y `key:Down` o a atajos de teclado "hotkeys" (incluyendo combinaciones de teclas). ## Teststand [#keyboard-test-stand] ```offline -To better understand keyboard events, you can use the [teststand](sandbox:keyboard-dump). +Para entender mejor los eventos de teclado, puedes usar [teststand](sandbox:keyboard-dump). ``` ```online -To better understand keyboard events, you can use the teststand below. +Para entender mejor los eventos de teclado, puedes usar "teststand" aquí abajo. -Try different key combinations in the text field. +Prueba diferentes combinaciones de tecla en el campo de texto. [codetabs src="keyboard-dump" height=480] ``` -## Keydown and keyup +## Keydown y keyup -The `keydown` events happens when a key is pressed down, and then `keyup` -- when it's released. +Los eventos `keydown` ocurren cuando se presiona una tecla, y `keyup` cuando se suelta. -### event.code and event.key +### event.code y event.key -The `key` property of the event object allows to get the character, while the `code` property of the event object allows to get the "physical key code". +La propiedad `key` del objeto de evento permite obtener el carácter, mientras que la propiedad `code` del evento permite obtener el "código físico de la tecla". -For instance, the same key `key:Z` can be pressed with or without `key:Shift`. That gives us two different characters: lowercase `z` and uppercase `Z`. +Por ejemplo, la misma tecla `key:Z` puede ser presionada con o sin `key:Shift`. Esto nos da dos caracteres diferentes: `z` minúscula y `Z` mayúscula. -The `event.key` is exactly the character, and it will be different. But `event.code` is the same: +`event.key` es el carácter exacto, y será diferente. Pero `event.code` es el mismo: -| Key | `event.key` | `event.code` | +| Tecla | `event.key` | `event.code` | |--------------|-------------|--------------| -| `key:Z` |`z` (lowercase) |`KeyZ` | -| `key:Shift+Z`|`Z` (uppercase) |`KeyZ` | +| `key:Z` |`z` (minúscula) |`KeyZ` | +| `key:Shift+Z`|`Z` (mayúscula) |`KeyZ` | -If a user works with different languages, then switching to another language would make a totally different character instead of `"Z"`. That will become the value of `event.key`, while `event.code` is always the same: `"KeyZ"`. +Si un usuario trabaja con diferentes lenguajes, el cambio a otro lenguaje podría producir un carácter totalmente diferente a `"Z"`. Este se volverá el valor de `event.key`, mientras que `event.code` es siempre el mismo: `"KeyZ"`. -```smart header="\"KeyZ\" and other key codes" -Every key has the code that depends on its location on the keyboard. Key codes described in the [UI Events code specification](https://www.w3.org/TR/uievents-code/). +```smart header="\"KeyZ\" y otros códigos de tecla" +Cada tecla tiene el código que depende de su ubicación en el teclado. Los códigos de tecla están descritos en la especificación [UI Events code](https://www.w3.org/TR/uievents-code/). -For instance: -- Letter keys have codes `"Key<letter>"`: `"KeyA"`, `"KeyB"` etc. -- Digit keys have codes: `"Digit<number>"`: `"Digit0"`, `"Digit1"` etc. -- Special keys are coded by their names: `"Enter"`, `"Backspace"`, `"Tab"` etc. +Por ejemplo: +- Las letras tienen códigos como `"Key<letter>"`: `"KeyA"`, `"KeyB"` etc. +- Los dígitos tienen códigos como `"Digit<number>"`: `"Digit0"`, `"Digit1"` etc. +- Las teclas especiales están codificadas por sus nombres: `"Enter"`, `"Backspace"`, `"Tab"` etc. -There are several widespread keyboard layouts, and the specification gives key codes for each of them. +Hay varias distribuciones de teclado esparcidos, y la especificación nos da los códigos de tecla para cada una de ellas. -Read the [alphanumeric section of the spec](https://www.w3.org/TR/uievents-code/#key-alphanumeric-section) for more codes, or just press a key in the [teststand](#keyboard-test-stand) above. +Para más códigos, puedes leer la [sección alfanumérica de la especificación](https://www.w3.org/TR/uievents-code/#key-alphanumeric-section), o simplemente presionar una tecla en el [teststand](#keyboard-test-stand) arriba. ``` -```warn header="Case matters: `\"KeyZ\"`, not `\"keyZ\"`" -Seems obvious, but people still make mistakes. +```warn header="La mayúscula importa: es `\"KeyZ\"`, no `\"keyZ\"`" +Parece obvio, pero aún se cometen estos errores. -Please evade mistypes: it's `KeyZ`, not `keyZ`. The check like `event.code=="keyZ"` won't work: the first letter of `"Key"` must be uppercase. +Por favor evita errores de tipeo: es `KeyZ`, no `keyZ`. Una verificación como `event.code=="keyZ"` no funcionará: la primera letra de `"Key"` debe estar en mayúscula. ``` -What if a key does not give any character? For instance, `key:Shift` or `key:F1` or others. For those keys, `event.key` is approximately the same as `event.code`: +¿Qué pasa si una tecla no da ningún carácter? Por ejemplo, `key:Shift` o `key:F1` u otras. Para estas teclas, `event.key` es aproximadamente lo mismo que `event.code`: | Key | `event.key` | `event.code` | |--------------|-------------|--------------| @@ -69,15 +69,15 @@ What if a key does not give any character? For instance, `key:Shift` or `key:F1` | `key:Backspace` |`Backspace` |`Backspace` | | `key:Shift`|`Shift` |`ShiftRight` or `ShiftLeft` | -Please note that `event.code` specifies exactly which key is pressed. For instance, most keyboards have two `key:Shift` keys: on the left and on the right side. The `event.code` tells us exactly which one was pressed, and `event.key` is responsible for the "meaning" of the key: what it is (a "Shift"). +Ten en cuenta que `event.code` especifica con exactitud la tecla que es presionada. Por ejemplo, la mayoría de los teclados tienen dos teclas `key:Shift`: una a la izquierda y otra a la derecha. `event.code` nos dice exactamente cuál fue presionada, en cambio `event.key` es responsable del "significado" de la tecla: lo que "es" (una "Mayúscula"). -Let's say, we want to handle a hotkey: `key:Ctrl+Z` (or `key:Cmd+Z` for Mac). Most text editors hook the "Undo" action on it. We can set a listener on `keydown` and check which key is pressed. +Digamos que queremos manejar un atajo de teclado: `key:Ctrl+Z` (o `key:Cmd+Z` en Mac). La mayoría de los editores de texto "cuelgan" la acción "Undo" en él. Podemos configurar un "listener" para escuchar el evento `keydown` y verificar qué tecla es presionada. -There's a dilemma here: in such a listener, should we check the value of `event.key` or `event.code`? +Hay un dilema aquí: en ese "listener", ¿debemos verificar el valor de `event.key` o el de `event.code`? -On one hand, the value of `event.key` is a character, it changes depending on the language. If the visitor has several languages in OS and switches between them, the same key gives different characters. So it makes sense to check `event.code`, it's always the same. +Por un lado, el valor de `event.key` es un carácter que cambia dependiendo del lenguaje. Si el visitante tiene varios lenguajes en el sistema operativo y los cambia, la misma tecla dará diferentes caracteres. Entonces tiene sentido chequear `event.code` que es siempre el mismo. -Like this: +Como aquí: ```js run document.addEventListener('keydown', function(event) { @@ -87,54 +87,54 @@ document.addEventListener('keydown', function(event) { }); ``` -On the other hand, there's a problem with `event.code`. For different keyboard layouts, the same key may have different characters. +Por otro lado, hay un problema con `event.code`. Para diferentes distribuciones de teclado, la misma tecla puede tener diferentes caracteres. -For example, here are US layout ("QWERTY") and German layout ("QWERTZ") under it (from Wikipedia): +Por ejemplo, aquí abajo mostramos la distribución de EE.UU. "QWERTY" y la alemana "QWERTZ" (de Wikipedia): ![](us-layout.svg) ![](german-layout.svg) -For the same key, US layout has "Z", while German layout has "Y" (letters are swapped). +Para la misma tecla, la distribución norteamericana tiene "Z", mientras que la alemana tiene "Y" (las letras son intercambiadas). -Literally, `event.code` will equal `KeyZ` for people with German layout when they press `key:Y`. +Efectivamente, `event.code` será igual a `KeyZ` para las personas con distribución de teclas alemana cuando presionen `key:Y`. -If we check `event.code == 'KeyZ'` in our code, then for people with German layout such test will pass when they press `key:Y`. +Si chequeamos `event.code == 'KeyZ'` en nuestro código, las personas con distribución alemana pasarán el test cuando presionen `key:Y`. -That sounds really odd, but so it is. The [specification](https://www.w3.org/TR/uievents-code/#table-key-code-alphanumeric-writing-system) explicitly mentions such behavior. +Esto suena realmente extraño, y lo es. La [especificación](https://www.w3.org/TR/uievents-code/#table-key-code-alphanumeric-writing-system) explícitamente menciona este comportamiento. -So, `event.code` may match a wrong character for unexpected layout. Same letters in different layouts may map to different physical keys, leading to different codes. Luckily, that happens only with several codes, e.g. `keyA`, `keyQ`, `keyZ` (as we've seen), and doesn't happen with special keys such as `Shift`. You can find the list in the [specification](https://www.w3.org/TR/uievents-code/#table-key-code-alphanumeric-writing-system). +Entonces, `event.code` puede coincidir con un carácter equivocado en una distribución inesperada. Las mismas letras en diferentes distribuciones pueden mapear a diferentes teclas físicas, llevando a diferentes códigos. Afortunadamente, ello solo ocurre en algunos códigos, por ejemplo `keyA`, `keyQ`, `keyZ` (que ya hemos visto), y no ocurre con teclas especiales como `Shift`. Puedes encontrar la lista en la [especificación](https://www.w3.org/TR/uievents-code/#table-key-code-alphanumeric-writing-system). -To reliably track layout-dependent characters, `event.key` may be a better way. +Para un seguimiento confiable de caracteres que dependen de la distribución, `event.key` puede ser una mejor opción. -On the other hand, `event.code` has the benefit of staying always the same, bound to the physical key location. So hotkeys that rely on it work well even in case of a language switch. +Por otro lado, `event.code` tiene el beneficio de quedar siempre igual, ligado a la ubicación física de la tecla. Así los atajos de teclado que dependen de él funcionan bien aunque cambie el lenguaje. -Do we want to handle layout-dependant keys? Then `event.key` is the way to go. +¿Queremos manejar teclas que dependen de la distribución? Entonces `event.key` es lo adecuado. -Or we want a hotkey to work even after a language switch? Then `event.code` may be better. +¿O queremos que un atajo funcione en el mismo lugar incluso si cambia el lenguaje? Entonces `event.code` puede ser mejor. -## Auto-repeat +## Autorepetición -If a key is being pressed for a long enough time, it starts to "auto-repeat": the `keydown` triggers again and again, and then when it's released we finally get `keyup`. So it's kind of normal to have many `keydown` and a single `keyup`. +Si una tecla es presionada durante suficiente tiempo, comienza a "autorepetirse": `keydown` se dispara una y otra vez, y cuando es soltada finalmente se obtiene `keyup`. Por ello es normal tener muchos `keydown` y un solo `keyup`. -For events triggered by auto-repeat, the event object has `event.repeat` property set to `true`. +Para eventos disparados por autorepetición, el objeto de evento tiene la propiedad `event.repeat` establecida a `true`. -## Default actions +## Acciones predeterminadas -Default actions vary, as there are many possible things that may be initiated by the keyboard. +Las acciones predeterminadas varían, al haber muchas cosas posibles que pueden ser iniciadas por el teclado. -For instance: +Por ejemplo: -- A character appears on the screen (the most obvious outcome). -- A character is deleted (`key:Delete` key). -- The page is scrolled (`key:PageDown` key). -- The browser opens the "Save Page" dialog (`key:Ctrl+S`) -- ...and so on. +- Un carácter aparece en la pantalla (el resultado más obvio). +- Un carácter es borrado (tecla `key:Delete`). +- Un avance de página (tecla `key:PageDown`). +- El navegador abre el diálogo "guardar página" (`key:Ctrl+S`) +- ...y otras. -Preventing the default action on `keydown` can cancel most of them, with the exception of OS-based special keys. For instance, on Windows `key:Alt+F4` closes the current browser window. And there's no way to stop it by preventing the default action in JavaScript. +Evitar la acción predeterminada en `keydown` puede cancelar la mayoría de ellos, con la excepción de las teclas especiales basadas en el sistema operativo. Por ejemplo, en Windows la tecla `key:Alt+F4` cierra la ventana actual del navegador. Y no hay forma de detenerla por medio de "evitar la acción predeterminada" de JavaScript. -For instance, the `<input>` below expects a phone number, so it does not accept keys except digits, `+`, `()` or `-`: +Por ejemplo, el `<input>` debajo espera un número telefónico, entonces no acepta teclas excepto dígitos, `+`, `()` or `-`: ```html autorun height=60 run <script> @@ -142,16 +142,16 @@ function checkPhoneKey(key) { return (key >= '0' && key <= '9') || ['+','(',')','-'].includes(key); } </script> -<input *!*onkeydown="return checkPhoneKey(event.key)"*/!* placeholder="Phone, please" type="tel"> +<input *!*onkeydown="return checkPhoneKey(event.key)"*/!* placeholder="Teléfono, por favor" type="tel"> ``` -The `onkeydown` handler here uses `checkPhoneKey` to check for the key pressed. If it's valid (from `0..9` or one of `+-()`), then it returns `true`, otherwise `false`. +Aquí el manejador `onkeydown` usa `checkPhoneKey` para chequear la tecla presionada. Si es válida (de `0..9` o uno de `+-()`), entonces devuelve `true`, de otro modo, `false`. -As we know, the `false` value returned from the event handler, assigned using a DOM property or an attribute, such as above, prevents the default action, so nothing appears in the `<input>` for keys that don't pass the test. (The `true` value returned doesn't affect anything, only returning `false` matters) +Como ya sabemos, el valor `false` devuelto por el manejador de eventos, asignado usando una propiedad DOM o un atributo tal como lo hicimos arriba, evita la acción predeterminada; entonces nada aparece en `<input>` para las teclas que no pasan el test. (El valor `true` no afecta en nada, solo importa el valor `false`) -Please note that special keys, such as `key:Backspace`, `key:Left`, `key:Right`, do not work in the input. That's a side effect of the strict filter `checkPhoneKey`. These keys make it return `false`. +Ten en cuenta que las teclas especiales como `key:Backspace`, `key:Left`, `key:Right`, no funcionan en el input. Este es un efecto secundario del filtro estricto que hace `checkPhoneKey`. Estas teclas hacen que devuelva `false`. -Let's relax the filter a little bit by allowing arrow keys `key:Left`, `key:Right` and `key:Delete`, `key:Backspace`: +Aliviemos un poco el filtro permitiendo las tecla de flecha `key:Left`, `key:Right`, y `key:Delete`, `key:Backspace`: ```html autorun height=60 run <script> @@ -160,41 +160,41 @@ function checkPhoneKey(key) { ['+','(',')','-',*!*'ArrowLeft','ArrowRight','Delete','Backspace'*/!*].includes(key); } </script> -<input onkeydown="return checkPhoneKey(event.key)" placeholder="Phone, please" type="tel"> +<input onkeydown="return checkPhoneKey(event.key)" placeholder="Teléfono, por favor" type="tel"> ``` -Now arrows and deletion works well. +Ahora las flechas y el borrado funcionan bien. -Even though we have the key filter, one still can enter anything using a mouse and right-click + Paste. Mobile devices provide other means to enter values. So the filter is not 100% reliable. +Aunque tenemos el filtro de teclas, aún se puede ingresar cualquier cosa usando un mouse y "botón secundario + pegar". Dispositivos móviles brindan otros medios para ingresar valores. Así que el filtro no es 100% confiable. -The alternative approach would be to track the `oninput` event -- it triggers *after* any modification. There we can check the new `input.value` and modify it/highlight the `<input>` when it's invalid. Or we can use both event handlers together. +Un enfoque alternativo sería vigilar el evento `oninput`, este se dispara *después* de cualquier modificación. Allí podemos chequear el nuevo `input.value` y modificar o resaltar `<input>` cuando es inválido. O podemos usar ambos manejadores de eventos juntos. -## Legacy +## Código heredado -In the past, there was a `keypress` event, and also `keyCode`, `charCode`, `which` properties of the event object. +En el pasado existía un evento `keypress`, y también las propiedades del objeto evento `keyCode`, `charCode`, `which`. -There were so many browser incompatibilities while working with them, that developers of the specification had no way, other than deprecating all of them and creating new, modern events (described above in this chapter). The old code still works, as browsers keep supporting them, but there's totally no need to use those any more. +Al trabajar con ellos había tantas incompatibilidades entre los navegadores que los desarrolladores de la especificación no tuvieron otra alternativa que declararlos obsoletos y crear nuevos y modernos eventos (los descritos arriba en este capítulo). El viejo código todavía funciona porque los navegadores aún lo soportan, pero no hay necesidad de usarlos más, en absoluto. -## Mobile Keyboards +## Teclados en dispositivos móviles -When using virtual/mobile keyboards, formally known as IME (Input-Method Editor), the W3C standard states that a KeyboardEvent's [`e.keyCode` should be `229`](https://www.w3.org/TR/uievents/#determine-keydown-keyup-keyCode) and [`e.key` should be `"Unidentified"`](https://www.w3.org/TR/uievents-key/#key-attr-values). +Cuando se usan teclados virtuales o los de dispositivos móviles, formalmente conocidos como IME (Input-Method Editor), el estándar W3C establece que la propiedad de KeyboardEvent [`e.keyCode` debe ser `229`](https://www.w3.org/TR/uievents/#determine-keydown-keyup-keyCode) y [`e.key` debe ser `"Unidentified"`](https://www.w3.org/TR/uievents-key/#key-attr-values). -While some of these keyboards might still use the right values for `e.key`, `e.code`, `e.keyCode`... when pressing certain keys such as arrows or backspace, there's no guarantee, so your keyboard logic might not always work on mobile devices. +Mientras algunos de estos teclados pueden aún usar los valores correctos para `e.key`, `e.code`, `e.keyCode`..., cuando se presionan ciertas teclas tales como flechas o retroceso no hay garantía, entonces nuestra lógica de teclado podría no siempre funcionar bien en dispositivos móviles. -## Summary +## Resumen -Pressing a key always generates a keyboard event, be it symbol keys or special keys like `key:Shift` or `key:Ctrl` and so on. The only exception is `key:Fn` key that sometimes presents on a laptop keyboard. There's no keyboard event for it, because it's often implemented on lower level than OS. +Presionar una tecla siempre genera un evento de teclado, sean teclas de símbolos o teclas especiales como `key:Shift` o `key:Ctrl` y demás. La única excepción es la tecla `key:Fn` que a veces está presente en teclados de laptops. No hay un evento de teclado para ella porque suele estar implementado en un nivel más bajo que el del sistema operativo. -Keyboard events: +Eventos de teclado: -- `keydown` -- on pressing the key (auto-repeats if the key is pressed for long), -- `keyup` -- on releasing the key. +- `keydown` -- al presionar la tecla (comienza a autorepetir si la tecla queda presionada por un tiempo), +- `keyup` -- al soltar la tecla. -Main keyboard event properties: +Principales propiedades de evento de teclado: -- `code` -- the "key code" (`"KeyA"`, `"ArrowLeft"` and so on), specific to the physical location of the key on keyboard. -- `key` -- the character (`"A"`, `"a"` and so on), for non-character keys, such as `key:Esc`, usually has the same value as `code`. +- `code` -- el código de tecla "key code" (`"KeyA"`, `"ArrowLeft"` y demás), especifica la ubicación física de la tecla en el teclado. +- `key` -- el carácter (`"A"`, `"a"` y demás). Para las teclas que no son de caracteres como `key:Esc`, suele tener el mismo valor que `code`. -In the past, keyboard events were sometimes used to track user input in form fields. That's not reliable, because the input can come from various sources. We have `input` and `change` events to handle any input (covered later in the chapter <info:events-change-input>). They trigger after any kind of input, including copy-pasting or speech recognition. +En el pasado, los eventos de teclado eran usados para detectar cambios en los campos de formulario. Esto no es confiable, porque el ingreso puede venir desde varias fuentes. Para manejar cualquier ingreso tenemos los eventos `input` y `change` (tratados en el capítulo <info:events-change-input>). Ellos se disparan después de cualquier clase de ingreso, incluyendo copiar/pegar y el reconocimiento de voz. -We should use keyboard events when we really want keyboard. For example, to react on hotkeys or special keys. +Deberíamos usar eventos de teclado solamente cuando realmente queremos el teclado. Por ejemplo, para reaccionar a atajos o a teclas especiales. diff --git a/2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/index.html b/2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/index.html index a0d5a4f40..bb0765b66 100644 --- a/2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/index.html +++ b/2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/index.html @@ -10,7 +10,7 @@ <form id="form" onsubmit="return false"> - Prevent default for: + Evitar el predeterminado (prevent default) para: <label> <input type="checkbox" name="keydownStop" value="1"> keydown</label>    <label> @@ -24,7 +24,7 @@ <input type="checkbox" name="keyupIgnore" value="1"> keyup</label> </p> - <p>Focus on the input field and press a key.</p> + <p>Haz foco en el campo input y presiona una tecla.</p> <input type="text" placeholder="Press keys here" id="kinput"> diff --git a/2-ui/3-event-details/8-onscroll/1-endless-page/solution.md b/2-ui/3-event-details/8-onscroll/1-endless-page/solution.md index 54c101193..ce4fb3ee4 100644 --- a/2-ui/3-event-details/8-onscroll/1-endless-page/solution.md +++ b/2-ui/3-event-details/8-onscroll/1-endless-page/solution.md @@ -1,65 +1,65 @@ -The core of the solution is a function that adds more dates to the page (or loads more stuff in real-life) while we're at the page end. +El núcleo de la solución es una función que añade más fechas a la página (o carga más cosas en la vida real) mientras estamos en el final de la página. -We can call it immediately and add as a `window.onscroll` handler. +Podemos llamarlo inmediatamente o agregarlo como un manejador de `window.onscroll`. -The most important question is: "How do we detect that the page is scrolled to bottom?" +La pregunta más importante es: "¿Cómo detectamos que la página se desplaza hasta el fondo?" -Let's use window-relative coordinates. +Usaremos las coordenadas de la ventana. -The document is represented (and contained) within `<html>` tag, that is `document.documentElement`. +El documento está representado (y contenido) dentro de la etiqueta `<html>`, que es `document.documentElement`. -We can get window-relative coordinates of the whole document as `document.documentElement.getBoundingClientRect()`, the `bottom` property will be window-relative coordinate of the document bottom. +Podemos obtener las coordenadas relativas a la ventana de todo el documento como `document.documentElement.getBoundingClientRect()`, la propiedad `bottom` será la coordenada relativa a la ventana del fondo del documento. -For instance, if the height of the whole HTML document is `2000px`, then: +Por ejemplo, si la altura de todo el documento es `2000px`, entonces: ```js -// when we're on the top of the page -// window-relative top = 0 +// cuando estamos en la parte superior de la página +// window-relative top = 0 (relativo a la ventana, límite superior = 0 ) document.documentElement.getBoundingClientRect().top = 0 -// window-relative bottom = 2000 -// the document is long, so that is probably far beyond the window bottom +// window-relative bottom = 2000 (relativo a la ventana, límite inferior = 2000) +// el documento es largo, así que probablemente esté más allá del fondo de la ventana document.documentElement.getBoundingClientRect().bottom = 2000 ``` -If we scroll `500px` below, then: +Si nos desplazamos `500px` abajo, entonces: ```js -// document top is above the window 500px +// la parte superior del documento está 500px por encima de la ventana document.documentElement.getBoundingClientRect().top = -500 -// document bottom is 500px closer +// la parte inferior del documento está 500px más cerca document.documentElement.getBoundingClientRect().bottom = 1500 ``` -When we scroll till the end, assuming that the window height is `600px`: +Cuando nos desplazamos hasta el final, asumiendo que la altura de la venta es `600px`: ```js -// document top is above the window 1400px +// La parte superior del documento está 1400px sobre la ventana document.documentElement.getBoundingClientRect().top = -1400 -// document bottom is below the window 600px +// la parte inferior del documento está a 600px debajo de la ventana document.documentElement.getBoundingClientRect().bottom = 600 ``` -Please note that the `bottom` can't be `0`, because it never reaches the window top. The lowest limit of the `bottom` coordinate is the window height (we assumed it to be `600`), we can't scroll it any more up. +Tened en cuenta que el fondo del documento `bottom` nunca puede ser `0`, porque nunca llega a la parte superior de la ventana. El límite más bajo de la coordenada `bottom` es la altura de la ventana (asumimos que es `600`), no podemos desplazarla más hacia arriba. -We can obtain the window height as `document.documentElement.clientHeight`. +Podemos obtener la altura de la ventana con `document.documentElement.clientHeight`. -For our task, we need to know when the document bottom is not no more than `100px` away from it (that is: `600-700px`, if the height is `600`). +Para nuestra tarea, necesitamos saber cuando tenemos el final del documento a unos `100px` (esto es: `600-700px`, si la altura es de `600`). -So here's the function: +Así que aquí está la función: ```js function populate() { - while(true) { - // document bottom + while(true) + { + // final del documento let windowRelativeBottom = document.documentElement.getBoundingClientRect().bottom; - // if the user hasn't scrolled far enough (>100px to the end) + // si el usuario no se ha desplazado lo suficiente (> 100px hasta el final) if (windowRelativeBottom > document.documentElement.clientHeight + 100) break; - - // let's add more data - document.body.insertAdjacentHTML("beforeend", `<p>Date: ${new Date()}</p>`); + // vamos añadir más datos + document.body.insertAdjacentHTML("beforeend", `<p>Date: ${new Date()}</p>`); } } ``` diff --git a/2-ui/3-event-details/8-onscroll/1-endless-page/solution.view/index.html b/2-ui/3-event-details/8-onscroll/1-endless-page/solution.view/index.html index 8dc1b2949..8ae564bd8 100644 --- a/2-ui/3-event-details/8-onscroll/1-endless-page/solution.view/index.html +++ b/2-ui/3-event-details/8-onscroll/1-endless-page/solution.view/index.html @@ -6,7 +6,7 @@ <body> -<h1>Scroll me</h1> +<h1>Prueba mi Scroll</h1> <script> function populate() { @@ -19,7 +19,7 @@ <h1>Scroll me</h1> window.addEventListener('scroll', populate); - populate(); // init document + populate(); // inicializar document </script> </body> diff --git a/2-ui/3-event-details/8-onscroll/1-endless-page/source.view/index.html b/2-ui/3-event-details/8-onscroll/1-endless-page/source.view/index.html index f58c3467e..3e9926475 100644 --- a/2-ui/3-event-details/8-onscroll/1-endless-page/source.view/index.html +++ b/2-ui/3-event-details/8-onscroll/1-endless-page/source.view/index.html @@ -9,7 +9,7 @@ <h1>Scroll me</h1> <script> - // ... your code ... + // ... tu código... </script> </body> diff --git a/2-ui/3-event-details/8-onscroll/1-endless-page/task.md b/2-ui/3-event-details/8-onscroll/1-endless-page/task.md index 7c8d14fca..1819a55a8 100644 --- a/2-ui/3-event-details/8-onscroll/1-endless-page/task.md +++ b/2-ui/3-event-details/8-onscroll/1-endless-page/task.md @@ -2,19 +2,19 @@ importance: 5 --- -# Endless page +# Página sin fin -Create an endless page. When a visitor scrolls it to the end, it auto-appends current date-time to the text (so that a visitor can scroll more). +Crear una página interminable. Cuando un visitante la desplace hasta el final, se auto-añadirá la fecha y hora actual al texto (así el visitante podrá seguir desplazándose) -Like this: +Así: [iframe src="solution" height=200] -Please note two important features of the scroll: +Por favor tenga en cuenta dos características importantes del desplazamiento: -1. **The scroll is "elastic".** We can scroll a little beyond the document start or end in some browsers/devices (empty space below is shown, and then the document will automatically "bounces back" to normal). -2. **The scroll is imprecise.** When we scroll to page end, then we may be in fact like 0-50px away from the real document bottom. +1. **El scroll es "elástico".** En algunos navegadores/dispositivos podemos desplazarnos un poco más allá del inicio o final del documento (se muestra un espacio vacío abajo, y luego el documento "rebota" automáticamente a la normalidad). +2. **El scroll es impreciso.** Cuando nos desplazamos hasta el final de la página, podemos estar de hecho como a 0-50px del fondo del documento real. -So, "scrolling to the end" should mean that the visitor is no more than 100px away from the document end. +Así que, "desplazarse hasta el final" debería significar que el visitante no está a más de 100px del final del documento. -P.S. In real life we may want to show "more messages" or "more goods". +P.D. En la vida real podemos querer mostrar "más mensajes" o "más bienes". diff --git a/2-ui/3-event-details/8-onscroll/2-updown-button/solution.view/index.html b/2-ui/3-event-details/8-onscroll/2-updown-button/solution.view/index.html index 93f888357..e7b8631b9 100644 --- a/2-ui/3-event-details/8-onscroll/2-updown-button/solution.view/index.html +++ b/2-ui/3-event-details/8-onscroll/2-updown-button/solution.view/index.html @@ -49,7 +49,7 @@ arrowTop.onclick = function() { window.scrollTo(pageXOffset, 0); - // after scrollTo, there will be a "scroll" event, so the arrow will hide automatically + // después de scrollTo, habrá un evento "scroll", entonces la flecha se ocultará automáticamente }; window.addEventListener('scroll', function() { diff --git a/2-ui/3-event-details/8-onscroll/2-updown-button/source.view/index.html b/2-ui/3-event-details/8-onscroll/2-updown-button/source.view/index.html index f90055cd8..ea654a280 100644 --- a/2-ui/3-event-details/8-onscroll/2-updown-button/source.view/index.html +++ b/2-ui/3-event-details/8-onscroll/2-updown-button/source.view/index.html @@ -46,7 +46,7 @@ <div id="arrowTop"></div> <script> - // ... your code ... + // ... tu código ... </script> </body> diff --git a/2-ui/3-event-details/8-onscroll/2-updown-button/task.md b/2-ui/3-event-details/8-onscroll/2-updown-button/task.md index c9f0f6225..042d4adfd 100644 --- a/2-ui/3-event-details/8-onscroll/2-updown-button/task.md +++ b/2-ui/3-event-details/8-onscroll/2-updown-button/task.md @@ -2,15 +2,15 @@ importance: 5 --- -# Up/down button +# Botón para subir/bajar -Create a "to the top" button to help with page scrolling. +Crea un botón "ir arriba" para ayudar con el desplazamiento de la página. -It should work like this: -- While the page is not scrolled down at least for the window height -- it's invisible. -- When the page is scrolled down more than the window height -- there appears an "upwards" arrow in the left-top corner. If the page is scrolled back, it disappears. -- When the arrow is clicked, the page scrolls to the top. +Debería funcionar así: +- Mientras que la página no se desplace hacia abajo al menos la altura de la ventana... es invisible. +- Cuando la página se desplaza hacia abajo más que la altura de la ventana -- aparece una flecha "hacia arriba" en la esquina superior izquierda. Si la página se desplaza hacia atrás desaparece. +- Cuando se hace click en la flecha, la página se desplaza hacia arriba hasta el tope. -Like this (top-left corner, scroll to see): +Así (esquina superior izquierda, desplácese para ver): [iframe border="1" height="200" link src="solution"] diff --git a/2-ui/3-event-details/8-onscroll/3-load-visible-img/solution.md b/2-ui/3-event-details/8-onscroll/3-load-visible-img/solution.md index 1649251b9..0fec50ea2 100644 --- a/2-ui/3-event-details/8-onscroll/3-load-visible-img/solution.md +++ b/2-ui/3-event-details/8-onscroll/3-load-visible-img/solution.md @@ -1,13 +1,13 @@ -The `onscroll` handler should check which images are visible and show them. +El manejador `onscroll` debería comprobar qué imágenes son visibles y mostrarlas. -We also want to run it when the page loads, to detect immediately visible images and load them. +También queremos que se ejecute cuando se cargue la página, para detectar las imágenes visibles inmediatamente y cargarlas. -The code should execute when the document is loaded, so that it has access to its content. +El código debería ejecutarse cuando se cargue el documento, para que tenga acceso a su contenido. -Or put it at the `<body>` bottom: +O ponerlo en la parte inferior del `<body>`: ```js -// ...the page content is above... +// ...el contenido de la página está arriba... function isVisible(elem) { @@ -15,17 +15,17 @@ function isVisible(elem) { let windowHeight = document.documentElement.clientHeight; - // top elem edge is visible? + // ¿El borde superior del elemento es visible? let topVisible = coords.top > 0 && coords.top < windowHeight; - // bottom elem edge is visible? + // ¿El borde inferior del elemento es visible? let bottomVisible = coords.bottom < windowHeight && coords.bottom > 0; return topVisible || bottomVisible; } ``` -The `showVisible()` function uses the visibility check, implemented by `isVisible()`, to load visible images: +La función `showVisible()` utiliza el control de visibilidad, implementado por `isVisible()`, para cargar imágenes visibles: ```js function showVisible() { @@ -46,4 +46,4 @@ window.onscroll = showVisible; */!* ``` -P.S. The solution also has a variant of `isVisible` that "preloads" images that are within 1 page above/below the current document scroll. +P.D. La solución tiene una variante de `isVisible` que "precarga" imágenes que están dentro de 1 página por encima/debajo del desplazamiento del documento actual. diff --git a/2-ui/3-event-details/8-onscroll/3-load-visible-img/solution.view/index.html b/2-ui/3-event-details/8-onscroll/3-load-visible-img/solution.view/index.html index 5c6027a6f..afb0ce86e 100644 --- a/2-ui/3-event-details/8-onscroll/3-load-visible-img/solution.view/index.html +++ b/2-ui/3-event-details/8-onscroll/3-load-visible-img/solution.view/index.html @@ -6,9 +6,9 @@ <body> - <p>Text and pictures are from https://wikipedia.org.</p> + <p>Texto e imágenes son de https://wikipedia.org.</p> - <h3>All images with <code>data-src</code> load when become visible.</h3> + <h3>Todas las imágenes con <code>data-src</code> se cargan cuando se vuelven visibles.</h3> <h1>Solar system</h1> @@ -165,8 +165,8 @@ <h1>Neptune</h1> <script> /** - * Tests if the element is visible (within the visible part of the page) - * It's enough that the top or bottom edge of the element are visible + * Testea si el elemento es visible (que está dentro de la parte visible de la página) + * Es suficiente que los bordes superior o inferior del elemento sean visibles */ function isVisible(elem) { @@ -174,7 +174,7 @@ <h1>Neptune</h1> let windowHeight = document.documentElement.clientHeight; - // top elem edge is visible OR bottom elem edge is visible + // el borde superior del elemento es visible O el borde inferior es visible let topVisible = coords.top > 0 && coords.top < windowHeight; let bottomVisible = coords.bottom < windowHeight && coords.bottom > 0; @@ -182,8 +182,8 @@ <h1>Neptune</h1> } /** - A variant of the test that considers the element visible if it's no more than - one page after/behind the current screen. + Una variante del test, considera al elemento visible si no hay más que + una página delante/detrás de la pantalla actual. function isVisible(elem) { @@ -208,8 +208,8 @@ <h1>Neptune</h1> if (!realSrc) continue; if (isVisible(img)) { - // disable caching - // this line should be removed in production code + // deshabilita caching + // esta línea debe ser quitada del código de producción realSrc += '?nocache=' + Math.random(); img.src = realSrc; diff --git a/2-ui/3-event-details/8-onscroll/3-load-visible-img/source.view/index.html b/2-ui/3-event-details/8-onscroll/3-load-visible-img/source.view/index.html index 9953ace66..2f228ca25 100644 --- a/2-ui/3-event-details/8-onscroll/3-load-visible-img/source.view/index.html +++ b/2-ui/3-event-details/8-onscroll/3-load-visible-img/source.view/index.html @@ -6,9 +6,9 @@ <body> - <p>Text and pictures are from https://wikipedia.org.</p> + <p>Texto e imágenes son de https://wikipedia.org</p> - <h3>All images with <code>data-src</code> load when become visible.</h3> + <h3>Todas las imágenes con <code>data-src</code> se cargan cuando se vuelven visibles.</h3> <h1>Solar system</h1> @@ -165,11 +165,11 @@ <h1>Neptune</h1> <script> /** - * Tests if the element is visible (within the visible part of the page) - * It's enough that the top or bottom edge of the element are visible + * Testea si el elemento es visible (que está dentro de la parte visible de la página) + * Es suficiente que los bordes superior o inferior del elemento sean visibles */ function isVisible(elem) { - // todo: your code + // por hacer: tu código... } function showVisible() { @@ -178,8 +178,8 @@ <h1>Neptune</h1> if (!realSrc) continue; if (isVisible(img)) { - // disable caching - // this line should be removed in production code + // deshabilita caching + // esta línea debe ser quitada del código de producción realSrc += '?nocache=' + Math.random(); img.src = realSrc; diff --git a/2-ui/3-event-details/8-onscroll/3-load-visible-img/task.md b/2-ui/3-event-details/8-onscroll/3-load-visible-img/task.md index 323788982..eb45175bc 100644 --- a/2-ui/3-event-details/8-onscroll/3-load-visible-img/task.md +++ b/2-ui/3-event-details/8-onscroll/3-load-visible-img/task.md @@ -2,29 +2,29 @@ importance: 4 --- -# Load visible images +# Cargar imágenes visibles -Let's say we have a slow-speed client and want to save their mobile traffic. +Digamos que tenemos un cliente con baja velocidad de conexión y queremos cuidar su tarifa de datos. -For that purpose we decide not to show images immediately, but rather replace them with placeholders, like this: +Para ello decidimos no mostrar las imágenes inmediatamente, sino sustituirlas por marcadores de posición, como este: ```html <img *!*src="placeholder.svg"*/!* width="128" height="128" *!*data-src="real.jpg"*/!*> ``` -So, initially all images are `placeholder.svg`. When the page scrolls to the position where the user can see the image -- we change `src` to the one in `data-src`, and so the image loads. +Así que, inicialmente todas las imágenes son `placeholder.svg`. Cuando la página se desplaza a la posición donde el usuario puede ver la imagen -- cambiamos `src` a `data-src`, y así la imagen se carga. -Here's an example in `iframe`: +Aquí hay un ejemplo en `iframe`: [iframe src="solution"] -Scroll it to see images load "on-demand". +Desplázate para ver las imágenes cargadas "bajo demanda". -Requirements: -- When the page loads, those images that are on-screen should load immediately, prior to any scrolling. -- Some images may be regular, without `data-src`. The code should not touch them. -- Once an image is loaded, it should not reload any more when scrolled in/out. +Requerimientos: +- Cuando la página se carga, las imágenes que están en pantalla deben cargarse inmediatamente, antes de cualquier desplazamiento. +- Algunas imágenes pueden ser regulares, sin `data-src`. El código no debe tocarlas. +- Una vez que una imagen se carga, no debe recargarse más cuando haya desplazamiento arriba/abajo. -P.S. If you can, make a more advanced solution that would "preload" images that are one page below/after the current position. +P.D. Si puedes, haz una solución más avanzada para "precargar" las imágenes que están más abajo/después de la posición actual. -P.P.S. Only vertical scroll is to be handled, no horizontal scrolling. +Post P.D. Sólo se debe manejar el desplazamiento vertical, no el horizontal. diff --git a/2-ui/3-event-details/8-onscroll/article.md b/2-ui/3-event-details/8-onscroll/article.md index 734bd84c6..0a8ab8f6f 100644 --- a/2-ui/3-event-details/8-onscroll/article.md +++ b/2-ui/3-event-details/8-onscroll/article.md @@ -1,12 +1,12 @@ -# Scrolling +# Desplazamiento -The `scroll` event allows reacting to a page or element scrolling. There are quite a few good things we can do here. +El evento `scroll` permite reaccionar al desplazamiento de una página o elemento. Hay bastantes cosas buenas que podemos hacer aquí. -For instance: -- Show/hide additional controls or information depending on where in the document the user is. -- Load more data when the user scrolls down till the end of the page. +Por ejemplo: +- Mostrar/ocultar controles o información adicional según el lugar del documento en el que se encuentre el/la usuario/a. +- Cargar más datos cuando el/la usuario/a se desplaza hacia abajo hasta el final del documento. -Here's a small function to show the current scroll: +Aquí hay una pequeña función para mostrar el desplazamiento actual: ```js autorun window.addEventListener('scroll', function() { @@ -17,21 +17,21 @@ window.addEventListener('scroll', function() { ```online In action: -Current scroll = <b id="showScroll">scroll the window</b> +Desplazamiento actual = <b id="showScroll">Desplazamiento de la ventana</b> ``` -The `scroll` event works both on the `window` and on scrollable elements. +El evento `scroll` funciona tanto en `window` como en los elementos desplazables. -## Prevent scrolling +## Evitar el desplazamiento -How do we make something unscrollable? +¿Qué hacemos para que algo no se pueda desplazar? -We can't prevent scrolling by using `event.preventDefault()` in `onscroll` listener, because it triggers *after* the scroll has already happened. +No podemos evitar el desplazamiento utilizando `event.preventDefault()` oyendo al evento `onscroll`, porque este se activa *después* de que el desplazamiento haya ocurrido. -But we can prevent scrolling by `event.preventDefault()` on an event that causes the scroll, for instance `keydown` event for `key:pageUp` and `key:pageDown`. +Pero podemos prevenir el desplazamiento con `event.preventDefault()` en un evento que cause el desplazamiento, por ejemplo en el evento `keydown` para `key:pageUp` y `key:pageDown`. -If we add an event handler to these events and `event.preventDefault()` in it, then the scroll won't start. +Si añadimos un manejador de eventos a estos eventos y un `event.preventDefault()` en el manejador, entonces el desplazamiento no se iniciará. -There are many ways to initiate a scroll, so it's more reliable to use CSS, `overflow` property. +Hay muchas maneras de iniciar un desplazamiento, la más fiable es usar CSS, la propiedad `overflow`. -Here are few tasks that you can solve or look through to see applications of `onscroll`. +Aquí hay algunas tareas que puede resolver o revisar para ver aplicaciones de `onscroll`. diff --git a/2-ui/3-event-details/index.md b/2-ui/3-event-details/index.md index 569f08137..5bc5ec34f 100644 --- a/2-ui/3-event-details/index.md +++ b/2-ui/3-event-details/index.md @@ -1,3 +1,3 @@ -# UI Events +# Eventos en la UI -Here we cover most important user interface events and how to work with them. +Aquí cubriremos los eventos más importantes de la interfaz de usuario y cómo podemos trabajar con ellos. diff --git a/2-ui/4-forms-controls/1-form-elements/1-add-select-option/solution.md b/2-ui/4-forms-controls/1-form-elements/1-add-select-option/solution.md index 80fad3850..7cd78466f 100644 --- a/2-ui/4-forms-controls/1-form-elements/1-add-select-option/solution.md +++ b/2-ui/4-forms-controls/1-form-elements/1-add-select-option/solution.md @@ -1,4 +1,4 @@ -The solution, step by step: +La solución, paso a paso: ```html run <select id="genres"> diff --git a/2-ui/4-forms-controls/1-form-elements/1-add-select-option/task.md b/2-ui/4-forms-controls/1-form-elements/1-add-select-option/task.md index a0e74da57..f17675753 100644 --- a/2-ui/4-forms-controls/1-form-elements/1-add-select-option/task.md +++ b/2-ui/4-forms-controls/1-form-elements/1-add-select-option/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# Add an option to select +# Añade una opción al select -There's a `<select>`: +Tenemos un `<select>`: ```html <select id="genres"> @@ -13,10 +13,10 @@ There's a `<select>`: </select> ``` -Use JavaScript to: +Utiliza JavaScript para: -1. Show the value and the text of the selected option. -2. Add an option: `<option value="classic">Classic</option>`. -3. Make it selected. +1. Mostrar el valor y el texto del option seleccionado. +2. Añadir un option: `<option value="classic">Classic</option>`. +3. Seleccionarlo. -Note, if you've done everything right, your alert should show `blues`. +Nota, si haz hecho todo bien, tu alert debería mostrar `blues`. diff --git a/2-ui/4-forms-controls/1-form-elements/article.md b/2-ui/4-forms-controls/1-form-elements/article.md index 7bc87a0f0..05a1bea70 100644 --- a/2-ui/4-forms-controls/1-form-elements/article.md +++ b/2-ui/4-forms-controls/1-form-elements/article.md @@ -1,23 +1,23 @@ -# Form properties and methods +# Propiedades y Métodos de Formularios -Forms and control elements, such as `<input>` have a lot of special properties and events. +Los formularios y controles, como `<input>`, tienen muchos eventos y propiedades especiales. -Working with forms will be much more convenient when we learn them. +Trabajar con formularios será mucho más conveniente cuando los aprendamos. -## Navigation: form and elements +## Navegación: Formularios y elementos -Document forms are members of the special collection `document.forms`. +Los formularios del documento son miembros de la colección especial `document.forms`. -That's a so-called *"named collection"*: it's both named and ordered. We can use both the name or the number in the document to get the form. +Esa es la llamada "Colección nombrada": es ambas cosas, nombrada y ordenada. Podemos usar el nombre o el número en el documento para conseguir el formulario. ```js no-beautify -document.forms.my; // the form with name="my" -document.forms[0]; // the first form in the document +document.forms.my; // el formulario con name="my" +document.forms[0]; // el primer formulario en el documento ``` -When we have a form, then any element is available in the named collection `form.elements`. +Cuando tenemos un formulario, cualquier elemento se encuentra disponible en la colección nombrada `form.elements`. -For instance: +Por ejemplo: ```html run height=40 <form name="my"> @@ -26,19 +26,19 @@ For instance: </form> <script> - // get the form - let form = document.forms.my; // <form name="my"> element + // obtención del formulario + let form = document.forms.my; // elemento <form name="my"> // get the element - let elem = form.elements.one; // <input name="one"> element + let elem = form.elements.one; // elemento <input name="one"> alert(elem.value); // 1 </script> ``` -There may be multiple elements with the same name. This is typical with radio buttons and checkboxes. +Puede haber múltiples elementos con el mismo nombre. Esto es típico en el caso de los botones de radio y checkboxes. -In that case, `form.elements[name]` is a *collection*. For instance: +En ese caso `form.elements[name]` es una *colección*. Por ejemplo: ```html run height=40 <form> @@ -57,13 +57,13 @@ alert(ageElems[0]); // [object HTMLInputElement] </script> ``` -These navigation properties do not depend on the tag structure. All control elements, no matter how deep they are in the form, are available in `form.elements`. +Estas propiedades de navegación no dependen de la estructura de las etiquetas. Todos los controles, sin importar qué tan profundos se encuentren en el formulario, están disponibles en `form.elements`. -````smart header="Fieldsets as \"subforms\"" -A form may have one or many `<fieldset>` elements inside it. They also have `elements` property that lists form controls inside them. +````smart header="Fieldsets como \"sub-formularios\"" +Un formulario puede tener uno o varios elementos `<fieldset>` dentro. Estos también tienen la propiedad `elements` que lista los controles del formulario dentro de ellos. -For instance: +Por ejemplo: ```html run height=80 <body> @@ -81,7 +81,7 @@ For instance: let fieldset = form.elements.userFields; alert(fieldset); // HTMLFieldSetElement - // we can get the input by name both from the form and from the fieldset + // podemos obtener el input por su nombre tanto desde el formulario como desde el fieldset alert(fieldset.elements.login == form.elements.login); // true */!* </script> @@ -89,14 +89,14 @@ For instance: ``` ```` -````warn header="Shorter notation: `form.name`" -There's a shorter notation: we can access the element as `form[index/name]`. +````warn header="Notación corta: `form.name`" +Hay una notación corta: podemos acceder el elemento como `form[index/name]`. -In other words, instead of `form.elements.login` we can write `form.login`. +En otras palabras, en lugar de `form.elements.login` podemos escribir `form.login`. -That also works, but there's a minor issue: if we access an element, and then change its `name`, then it is still available under the old name (as well as under the new one). +Esto también funciona, pero tiene un error menor: si accedemos un elemento, y cambiamos su `name`, se mantendrá disponible mediante el nombre anterior (así como mediante el nuevo). -That's easy to see in an example: +Esto es fácil de ver en un ejemplo: ```html run height=40 <form id="form"> @@ -104,34 +104,34 @@ That's easy to see in an example: </form> <script> - alert(form.elements.login == form.login); // true, the same <input> + alert(form.elements.login == form.login); // true, el mismo <input> - form.login.name = "username"; // change the name of the input + form.login.name = "username"; // cambiamos el nombre el <input> - // form.elements updated the name: + // form.elements actualiza el nombre: alert(form.elements.login); // undefined alert(form.elements.username); // input *!* - // form allows both names: the new one and the old one + // form permite ambos nombres: el nuevo y el viejo alert(form.username == form.login); // true */!* </script> ``` -That's usually not a problem, however, because we rarely change names of form elements. +Esto usualmente no es un problema, porque raramente se cambian los nombres de los elementos de un formulario. ```` -## Backreference: element.form +## Referencia inversa: element.form -For any element, the form is available as `element.form`. So a form references all elements, and elements reference the form. +Para cualquier elemento, el formulario está disponible como `element.form`. Así que un formulario referencia todos los elementos, y los elementos referencian el formulario. -Here's the picture: +Aquí la imagen: ![](form-navigation.svg) -For instance: +Por ejemplo: ```html run height=40 <form id="form"> @@ -149,44 +149,44 @@ For instance: </script> ``` -## Form elements +## Elementos del formulario -Let's talk about form controls. +Hablemos de los controles de los formularios. -### input and textarea +### input y textarea -We can access their value as `input.value` (string) or `input.checked` (boolean) for checkboxes and radio buttons. +Podemos acceder sus valores como `input.value` (cadena) o `input.checked` (booleano) para casillas de verificación (checkboxes) y botones de opción (radio buttons). -Like this: +De esta manera: ```js input.value = "New value"; textarea.value = "New text"; -input.checked = true; // for a checkbox or radio button +input.checked = true; // para checkboxes o radios ``` -```warn header="Use `textarea.value`, not `textarea.innerHTML`" -Please note that even though `<textarea>...</textarea>` holds its value as nested HTML, we should never use `textarea.innerHTML` to access it. +```warn header="Usa `textarea.value`, no `textarea.innerHTML`" +Observa que incluso aunque `<textarea>...</textarea>` contenga su valor como HTML anidado, nunca deberíamos usar `textarea.innerHTML` para acceder a él. -It stores only the HTML that was initially on the page, not the current value. +Esto solo guarda el HTML que había inicialmente en la página, no su valor actual. ``` -### select and option +### select y option -A `<select>` element has 3 important properties: +Un elemento `<select>` tiene 3 propiedades importantes: -1. `select.options` -- the collection of `<option>` subelements, -2. `select.value` -- the *value* of the currently selected `<option>`, -3. `select.selectedIndex` -- the *number* of the currently selected `<option>`. +1. `select.options` -- la colección de subelementos `<option>`, +2. `select.value` -- el valor del `<option>` seleccionado actualmente, y +3. `select.selectedIndex` -- el número del `<option>` seleccionado actualmente. -They provide three different ways of setting a value for a `<select>`: +Ellas proveen tres formas diferentes de asignar un valor para un elemento `<select>`: -1. Find the corresponding `<option>` element (e.g. among `select.options`) and set its `option.selected` to `true`. -2. If we know a new value: set `select.value` to the new value. -3. If we know the new option number: set `select.selectedIndex` to that number. +1. Encontrar el elemento `<option>` correspondiente (por ejemplo entre `select.options`) y asignar a su `option.selected` un `true`. +2. Si conocemos un nuevo valor: Asignar tal valor a `select.value`. +3. Si conocemos el nuevo número de opción: Asignar tal número a `select.selectedIndex`. -Here is an example of all three methods: +Aquí hay un ejemplo de los tres métodos: ```html run <select id="select"> @@ -196,19 +196,19 @@ Here is an example of all three methods: </select> <script> - // all three lines do the same thing - select.options[2].selected = true; + // las tres líneas hacen lo mismo + select.options[2].selected = true; select.selectedIndex = 2; select.value = 'banana'; - // please note: options start from zero, so index 2 means the 3rd option. + // Recuerda que las opciones comienzan en cero, así que index 2 significa la tercera opción. </script> ``` -Unlike most other controls, `<select>` allows to select multiple options at once if it has `multiple` attribute. This attribute is rarely used, though. +A diferencia de la mayoría de controles, `<select>` permite seleccionar múltiples opciones a la vez si tiene el atributo `multiple`. Esta característica es raramente utilizada. -For multiple selected values, use the first way of setting values: add/remove the `selected` property from `<option>` subelements. +En ese caso, necesitamos usar la primera forma: Añade/elimina la propiedad `selected` de los subelementos `<option>`. -Here's an example of how to get selected values from a multi-select: +Podemos obtener su colección como `select.options`, por ejemplo: ```html run <select id="select" *!*multiple*/!*> @@ -218,7 +218,7 @@ Here's an example of how to get selected values from a multi-select: </select> <script> - // get all selected values from multi-select + // obtener todos los valores seleccionados del multi-select let selected = Array.from(select.options) .filter(option => option.selected) .map(option => option.value); @@ -227,72 +227,72 @@ Here's an example of how to get selected values from a multi-select: </script> ``` -The full specification of the `<select>` element is available in the specification <https://html.spec.whatwg.org/multipage/forms.html#the-select-element>. +La especificación completa del elemento `<select>` está disponible en la especificación <https://html.spec.whatwg.org/multipage/forms.html#the-select-element>. ### new Option -In the [specification](https://html.spec.whatwg.org/multipage/forms.html#the-option-element) there's a nice short syntax to create an `<option>` element: +En la [especificación](https://html.spec.whatwg.org/multipage/forms.html#the-option-element) hay una sintaxis muy corta para crear elementos `<option>`: ```js option = new Option(text, value, defaultSelected, selected); ``` -This syntax is optional. We can use `document.createElement('option')` and set attributes manually. Still, it may be shorter, so here are the parameters: +Esta sintaxis es opcional. Podemos usar `document.createElement('option')` y asignar atributos manualmente. Aún puede ser más corta, aquí los parámetros: -- `text` -- the text inside the option, -- `value` -- the option value, -- `defaultSelected` -- if `true`, then `selected` HTML-attribute is created, -- `selected` -- if `true`, then the option is selected. +- `text` -- el texto dentro del option, +- `value` -- el valor del option, +- `defaultSelected` -- si es `true`, entonces se le crea el atributo HTML `selected`, +- `selected` -- si es `true`, el option se selecciona. -The difference between `defaultSelected` and `selected` is that `defaultSelected` sets the HTML-attribute (that we can get using `option.getAttribute('selected')`), while `selected` sets whether the option is selected or not. +La diferencia entre `defaultSelected` y `selected` es que `defaultSelected` define si el atributo HTML (el que obtenemos con `option.getAttribute('selected')`), mientras que `selected` define si el option está seleccionado o no en el DOM. -In practice, one should usually set _both_ values to `true` or `false`. (Or, simply omit them; both default to `false`.) +En la práctica, uno debería usualmente establecer _ambos_ valores en `true` o `false`. O simplemente omitirlos, quedarán con el predeterminado `false`. -For instance, here's a new "unselected" option: +Por ejemplo, aquí creamos un nuevo Option "unselected": ```js let option = new Option("Text", "value"); -// creates <option value="value">Text</option> +// crea <option value="value">Text</option> ``` -The same option, but selected: +El mismo elemento, pero seleccionado: ```js let option = new Option("Text", "value", true, true); ``` -Option elements have properties: +Los elementos Option tienen propiedades: `option.selected` -: Is the option selected. +: Es el option seleccionado. `option.index` -: The number of the option among the others in its `<select>`. +: El número del option respecto a los demás en su `<select>`. `option.text` -: Text content of the option (seen by the visitor). +: El contenido del option (visto por el visitante). -## References +## Referencias -- Specification: <https://html.spec.whatwg.org/multipage/forms.html>. +- Especificación: <https://html.spec.whatwg.org/multipage/forms.html>. -## Summary +## Resumen -Form navigation: +Navegación de formularios: `document.forms` -: A form is available as `document.forms[name/index]`. +: Un formulario está disponible como `document.forms[name/index]`. -`form.elements` -: Form elements are available as `form.elements[name/index]`, or can use just `form[name/index]`. The `elements` property also works for `<fieldset>`. +`form.elements` +: Los elementos del formulario están disponibles como `form.elements[name/index]`, o puedes usar solo `form[name/index]`. La propiedad `elements` también funciona para los `<fieldset>`. `element.form` -: Elements reference their form in the `form` property. +: Los elementos referencian a su formulario en la propiedad `form`. -Value is available as `input.value`, `textarea.value`, `select.value`, etc. (For checkboxes and radio buttons, use `input.checked` to determine whether a value is selected.) +El valor está disponible con `input.value`, `textarea.value`, `select.value` etc. Para checkboxes y radios, usa `input.checked` para determinar si el valor está seleccionado. -For `<select>`, one can also get the value by the index `select.selectedIndex` or through the options collection `select.options`. +Para `<select>` también podemos obtener el valor con el índice `select.selectedIndex` o a través de la colección de opciones `select.options`. -These are the basics to start working with forms. We'll meet many examples further in the tutorial. +Esto es lo básico para empezar a trabajar con formularios. Conoceremos muchos ejemplos más adelante en el tutorial. -In the next chapter we'll cover `focus` and `blur` events that may occur on any element, but are mostly handled on forms. +En el siguiente capítulo vamos a hablar sobre los eventos `focus` y `blur` que pueden ocurrir en cualquier elemento, pero son manejados mayormente en formularios. diff --git a/2-ui/4-forms-controls/2-focus-blur/3-editable-div/solution.view/index.html b/2-ui/4-forms-controls/2-focus-blur/3-editable-div/solution.view/index.html index c0dd30802..491419d5b 100644 --- a/2-ui/4-forms-controls/2-focus-blur/3-editable-div/solution.view/index.html +++ b/2-ui/4-forms-controls/2-focus-blur/3-editable-div/solution.view/index.html @@ -9,11 +9,11 @@ <body> <ul> - <li>Click the div to edit.</li> - <li>Enter or blur saves the result.</li> + <li>Clica el div para editar.</li> + <li>Enter o blur guarda el resultado.</li> </ul> - HTML is allowed. + Se permite HTML. <div id="view" class="view">Text</div> diff --git a/2-ui/4-forms-controls/2-focus-blur/3-editable-div/source.view/index.html b/2-ui/4-forms-controls/2-focus-blur/3-editable-div/source.view/index.html index b21432e84..d6c3f76c9 100644 --- a/2-ui/4-forms-controls/2-focus-blur/3-editable-div/source.view/index.html +++ b/2-ui/4-forms-controls/2-focus-blur/3-editable-div/source.view/index.html @@ -9,18 +9,18 @@ <body> <ul> - <li>Click the div to edit.</li> - <li>Enter or blur saves the result.</li> + <li>Clica el div para editar.</li> + <li>Enter o blur guarda el resultado.</li> </ul> - HTML is allowed. + Se permite HTML. <div id="view" class="view">Text</div> <script> - // ...your code... - // Note: <textarea> should have class="edit" - // my.css has styles to make it the same size as div + // ...tu código... + // Nota: <textarea> debe tener class="edit" + // my.css tiene styles que lo hacen del mismo tamaño que div </script> </body> diff --git a/2-ui/4-forms-controls/2-focus-blur/3-editable-div/task.md b/2-ui/4-forms-controls/2-focus-blur/3-editable-div/task.md index 8e45bfa86..69af2e44c 100644 --- a/2-ui/4-forms-controls/2-focus-blur/3-editable-div/task.md +++ b/2-ui/4-forms-controls/2-focus-blur/3-editable-div/task.md @@ -2,12 +2,12 @@ importance: 5 --- -# Editable div +# Un div editable -Create a `<div>` that turns into `<textarea>` when clicked. +Crea un `<div>` que se vuelva `<textarea>` cuando es clicado. -The textarea allows to edit the HTML in the `<div>`. +El textarea permite editar HTML en `<div>`. -When the user presses `key:Enter` or it loses focus, the `<textarea>` turns back into `<div>`, and its content becomes HTML in `<div>`. +Cuando el usuario presiona `key:Enter` o se pierde el foco, el `<textarea>` se vuelve `<div>` de nuevo, y su contenido se vuelve el HTML del `<div>`. [demo src="solution"] diff --git a/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/solution.md b/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/solution.md index b5ae45a60..a6786b93e 100644 --- a/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/solution.md +++ b/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/solution.md @@ -1,5 +1,5 @@ -1. On click -- replace `innerHTML` of the cell by `<textarea>` with same sizes and no border. Can use JavaScript or CSS to set the right size. -2. Set `textarea.value` to `td.innerHTML`. -3. Focus on the textarea. -4. Show buttons OK/CANCEL under the cell, handle clicks on them. +1. Al clicar (onclick) se reemplaza el `innerHTML` de la celda por un `<textarea>` com los mismos tamaños y sin bordes. Se puede usar JavaScript o CSS para establecer el tamaño correcto. +2. Establece `textarea.value` a `td.innerHTML`. +3. Pone el foco en textarea. +4. Muestra los botones OK/CANCEL bajo la celda, y maneja los clics en ellos. diff --git a/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/solution.view/index.html b/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/solution.view/index.html index 08f233898..96365c605 100644 --- a/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/solution.view/index.html +++ b/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/solution.view/index.html @@ -10,7 +10,7 @@ <link rel="stylesheet" href="my.css"> - <p>Click on a table cell to edit it. Press OK or CANCEL when you finish.</p> + <p>Clica en una celda de la tabla para editarla. Presiona OK o CANCEL para finalizar.</p> <table id="bagua-table"> <tr> diff --git a/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/source.view/index.html b/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/source.view/index.html index 08f233898..96365c605 100644 --- a/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/source.view/index.html +++ b/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/source.view/index.html @@ -10,7 +10,7 @@ <link rel="stylesheet" href="my.css"> - <p>Click on a table cell to edit it. Press OK or CANCEL when you finish.</p> + <p>Clica en una celda de la tabla para editarla. Presiona OK o CANCEL para finalizar.</p> <table id="bagua-table"> <tr> diff --git a/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/source.view/my.css b/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/source.view/my.css index e144e9018..622671329 100644 --- a/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/source.view/my.css +++ b/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/source.view/my.css @@ -1 +1 @@ -/* your styles */ +/* tus estilos */ diff --git a/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/source.view/script.js b/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/source.view/script.js index 015719829..196c09df2 100644 --- a/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/source.view/script.js +++ b/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/source.view/script.js @@ -1,3 +1,3 @@ let table = document.getElementById('bagua-table'); -/* your code */ +/* tu código */ diff --git a/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/task.md b/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/task.md index 378bd1f54..2634ebed4 100644 --- a/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/task.md +++ b/2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/task.md @@ -2,15 +2,15 @@ importance: 5 --- -# Edit TD on click +# Editar TD al clicar -Make table cells editable on click. +Haz las celdas de la tabla editables al clicarlas. -- On click -- the cell should become "editable" (textarea appears inside), we can change HTML. There should be no resize, all geometry should remain the same. -- Buttons OK and CANCEL appear below the cell to finish/cancel the editing. -- Only one cell may be editable at a moment. While a `<td>` is in "edit mode", clicks on other cells are ignored. -- The table may have many cells. Use event delegation. +- Al clicar, la celda se vuelve "editable" (aparece un textarea dentro), y podemos cambiar el HTML. No debe haber cambios de tamaño, la geometría debe conservarse. +- Bajo la celda aparecen los botones OK y CANCEL para terminar/cancelar la edición. +- Solo una celda a la vez puede ser editable. Mientras un `<td>` esté en "modo de edición", los clics en otras celdas son ignorados. +- La tabla puede tener varias celdas. Usa delegación de eventos. -The demo: +El demo: [iframe src="solution" height=400] diff --git a/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/solution.md b/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/solution.md index 4d1682176..9a954b280 100644 --- a/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/solution.md +++ b/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/solution.md @@ -1,6 +1,6 @@ -We can use `mouse.onclick` to handle the click and make the mouse "moveable" with `position:fixed`, then `mouse.onkeydown` to handle arrow keys. +Podemos usar `mouse.onclick` para manejar el clic y hacer el ratón "movible" con `position:fixed`, y luego `mouse.onkeydown` para manejar las flechas del teclado. -The only pitfall is that `keydown` only triggers on elements with focus. So we need to add `tabindex` to the element. As we're forbidden to change HTML, we can use `mouse.tabIndex` property for that. +La única trampa es que `keydown` solo se dispara en elementos con foco. Así que necesitamos agregar `tabindex` al elemento. Como un requisito es no cambiar el HTML, podemos usar la propiedad `mouse.tabIndex` para eso. -P.S. We also can replace `mouse.onclick` with `mouse.onfocus`. +P.S. También podemos reemplazar `mouse.onclick` con `mouse.onfocus`. diff --git a/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/solution.view/index.html b/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/solution.view/index.html index d39bd41ff..f64a2cdcf 100644 --- a/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/solution.view/index.html +++ b/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/solution.view/index.html @@ -18,7 +18,7 @@ <body> - <p>Click on the mouse and move it with arrow keys.</p> + <p>Clica en el ratón y muévelo con las flechas del teclado.</p> <pre id="mouse"> _ _ diff --git a/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/source.view/index.html b/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/source.view/index.html index f64011675..ac67d3e0e 100644 --- a/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/source.view/index.html +++ b/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/source.view/index.html @@ -18,7 +18,7 @@ <body> - <p>Click on the mouse and move it with arrow keys.</p> + <p>Clica en el ratón y muévelo con las flechas del teclado.</p> <pre id="mouse"> _ _ @@ -34,7 +34,7 @@ <script> - // ...your code... + // ...tu código... </script> </body> diff --git a/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/task.md b/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/task.md index 644d814d9..6f974a6de 100644 --- a/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/task.md +++ b/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/task.md @@ -2,12 +2,12 @@ importance: 4 --- -# Keyboard-driven mouse +# Ratón manejado por teclado -Focus on the mouse. Then use arrow keys to move it: +Enfoca el ratón. Luego usa las flechas del teclado para moverlo: [demo src="solution"] -P.S. Don't put event handlers anywhere except the `#mouse` element. +P.S. No pongas manejadores de eventos en ningún lado excepto el elemento `#mouse`. -P.P.S. Don't modify HTML/CSS, the approach should be generic and work with any element. +P.P.S. No modifiques HTML/CSS, el proceso debe ser genérico y trabajar con cualquier elemento. diff --git a/2-ui/4-forms-controls/2-focus-blur/article.md b/2-ui/4-forms-controls/2-focus-blur/article.md index c253dc11d..611a32820 100644 --- a/2-ui/4-forms-controls/2-focus-blur/article.md +++ b/2-ui/4-forms-controls/2-focus-blur/article.md @@ -1,25 +1,25 @@ -# Focusing: focus/blur +# Enfocado: enfoque/desenfoque -An element receives the focus when the user either clicks on it or uses the `key:Tab` key on the keyboard. There's also an `autofocus` HTML attribute that puts the focus onto an element by default when a page loads and other means of getting the focus. +Un elemento se enfoca cuando el usuario hace click sobre él o al pulsar `key:Tab` en el teclado. Existen también un atributo `autofocus` de HTML que enfoca un elemento por defecto cuando una página carga, y otros medios de conseguir el enfoque. -Focusing on an element generally means: "prepare to accept the data here", so that's the moment when we can run the code to initialize the required functionality. +Enfocarse sobre un elemento generalmente significa: "prepárate para aceptar estos datos", por lo que es el momento en el cual podemos correr el código para inicializar la funcionalidad requerida. -The moment of losing the focus ("blur") can be even more important. That's when a user clicks somewhere else or presses `key:Tab` to go to the next form field, or there are other means as well. +El momento de desenfoque ("blur") puede ser incluso más importante. Ocurre cuando un usuario clica en otro punto o presiona `key:Tab` para ir al siguiente campo de un formulario. También hay otras maneras. -Losing the focus generally means: "the data has been entered", so we can run the code to check it or even to save it to the server and so on. +Perder el foco o desenfocarse generalmente significa: "los datos ya han sido introducidos", entonces podemos correr el código para comprobarlo, o para guardarlo en el servidor, etc. -There are important peculiarities when working with focus events. We'll do the best to cover them further on. +Existen importantes peculiaridades al trabajar con eventos de enfoque. Haremos lo posible para abarcarlas a continuación. -## Events focus/blur +## Eventos focus/blur -The `focus` event is called on focusing, and `blur` -- when the element loses the focus. +El evento `focus` es llamado al enfocar, y el `blur` cuando el elemento pierde el foco. -Let's use them for validation of an input field. +Utilicémoslos para la validación de un campo de entrada. -In the example below: +En el ejemplo a continuación: -- The `blur` handler checks if the field has an email entered, and if not -- shows an error. -- The `focus` handler hides the error message (on `blur` it will be checked again): +- El manejador `blur` comprueba si se ha introducido un correo, y en caso contrario muestra un error. +- El manejador `focus` esconde el mensaje de error (en `blur` se volverá a comprobar): ```html run autorun height=60 <style> @@ -27,7 +27,7 @@ In the example below: #error { color: red } </style> -Your email please: <input type="email" id="input"> +Su correo por favor: <input type="email" id="input"> <div id="error"></div> @@ -35,13 +35,13 @@ Your email please: <input type="email" id="input"> *!*input.onblur*/!* = function() { if (!input.value.includes('@')) { // not email input.classList.add('invalid'); - error.innerHTML = 'Please enter a correct email.' + error.innerHTML = 'Por favor introduzca un correo válido.' } }; *!*input.onfocus*/!* = function() { if (this.classList.contains('invalid')) { - // remove the "error" indication, because the user wants to re-enter something + // quitar la indicación "error", porque el usuario quiere reintroducir algo this.classList.remove('invalid'); error.innerHTML = ""; } @@ -49,14 +49,14 @@ Your email please: <input type="email" id="input"> </script> ``` -Modern HTML allows us to do many validations using input attributes: `required`, `pattern` and so on. And sometimes they are just what we need. JavaScript can be used when we want more flexibility. Also we could automatically send the changed value to the server if it's correct. +El HTML actual nos permite efectuar diversas validaciones utilizando atributos de entrada: `required`, `pattern`, etc. Y muchas veces son todo lo que necesitamos. JavaScript puede ser utilizado cuando queremos más flexibilidad. También podríamos enviar automáticamente el valor modificado al servidor si es correcto. -## Methods focus/blur +## Métodos focus/blur -Methods `elem.focus()` and `elem.blur()` set/unset the focus on the element. +Los métodos `elem.focus()` y `elem.blur()` ponen/quitan el foco sobre el elemento. -For instance, let's make the visitor unable to leave the input if the value is invalid: +Por ejemplo, impidamos al visitante que deje la entrada si el valor es inválido: ```html run autorun height=80 <style> @@ -65,16 +65,16 @@ For instance, let's make the visitor unable to leave the input if the value is i } </style> -Your email please: <input type="email" id="input"> -<input type="text" style="width:220px" placeholder="make email invalid and try to focus here"> +Su correo por favor: <input type="email" id="input"> +<input type="text" style="width:220px" placeholder="hacer que el correo sea inválido y tratar de enfocar aquí"> <script> input.onblur = function() { - if (!this.value.includes('@')) { // not email - // show the error + if (!this.value.includes('@')) { // no es un correo + // mostrar error this.classList.add("error"); *!* - // ...and put the focus back + // ...y volver a enfocar input.focus(); */!* } else { @@ -84,61 +84,61 @@ Your email please: <input type="email" id="input"> </script> ``` -It works in all browsers except Firefox ([bug](https://bugzilla.mozilla.org/show_bug.cgi?id=53579)). +Funciona en todos los navegadores excepto Firefox ([bug](https://bugzilla.mozilla.org/show_bug.cgi?id=53579)). -If we enter something into the input and then try to use `key:Tab` or click away from the `<input>`, then `onblur` returns the focus back. +Si introducimos algo en la entrada y luego intentamos pulsar `key:Tab` o hacer click fuera del `<input>`, entonces `onblur` lo vuelve a enfocar. -Please note that we can't "prevent losing focus" by calling `event.preventDefault()` in `onblur`, because `onblur` works *after* the element lost the focus. +Por favor tened en cuenta que no podemos "prevenir perder el foco" llamando a `event.preventDefault()` en `onblur`, porque `onblur` funciona *después* de que el elemento perdió el foco. -In practice though, one should think well, before implementing something like this, because we generally *should show errors* to the user, but *should not prevent their progress* in filling our form. They may want to fill other fields first. +Aunque en la práctica uno debería pensarlo bien antes de implementar algo como esto, porque generalmente *debemos mostrar errores* al usuario, pero *no evitar que siga adelante* al llenar nuestro formulario. Podría querer llenar otros campos primero. -```warn header="JavaScript-initiated focus loss" -A focus loss can occur for many reasons. +```warn header="Pérdida de foco iniciada por JavaScript" +Una pérdida de foco puede ocurrir por diversas razones. -One of them is when the visitor clicks somewhere else. But also JavaScript itself may cause it, for instance: +Una de ellas ocurre cuando el visitante clica en algún otro lado. Pero el propio JavaScript podría causarlo, por ejemplo: -- An `alert` moves focus to itself, so it causes the focus loss at the element (`blur` event), and when the `alert` is dismissed, the focus comes back (`focus` event). -- If an element is removed from DOM, then it also causes the focus loss. If it is reinserted later, then the focus doesn't return. +- Un `alert` traslada el foco hacia sí mismo, lo que causa la pérdida de foco sobre el elemento (evento `blur`). Y cuando el `alert` es cerrado, el foco vuelve (evento `focus`). +- Si un elemento es eliminado del DOM, también causa pérdida de foco. Si es reinsertado el foco no vuelve. -These features sometimes cause `focus/blur` handlers to misbehave -- to trigger when they are not needed. +Estas situaciones a veces causan que los manejadores `focus/blur` no funcionen adecuadamente y se activen cuando no son necesarios. -The best recipe is to be careful when using these events. If we want to track user-initiated focus-loss, then we should avoid causing it ourselves. +Es recomendable tener cuidado al utilizar estos eventos. Si queremos monitorear pérdidas de foco iniciadas por el usuario deberíamos evitar causarlas nosotros mismos. ``` -## Allow focusing on any element: tabindex +## Permitir enfocado sobre cualquier elemento: tabindex -By default, many elements do not support focusing. +Por defecto, muchos elementos no permiten enfoque. -The list varies a bit between browsers, but one thing is always correct: `focus/blur` support is guaranteed for elements that a visitor can interact with: `<button>`, `<input>`, `<select>`, `<a>` and so on. +La lista varía un poco entre navegadores, pero una cosa es siempre cierta: `focus/blur` está garantizado para elementos con los que el visitante puede interactuar: `<button>`, `<input>`, `<select>`, `<a>`, etc. -On the other hand, elements that exist to format something, such as `<div>`, `<span>`, `<table>` -- are unfocusable by default. The method `elem.focus()` doesn't work on them, and `focus/blur` events are never triggered. +En cambio, elementos que existen para formatear algo, tales como `<div>`, `<span>`, `<table>`, por defecto no son posibles de enfocar. El método `elem.focus()` no funciona en ellos, y los eventos `focus/blur` no son desencadenados. -This can be changed using HTML-attribute `tabindex`. +Esto puede ser modificado usando el atributo HTML `tabindex`. -Any element becomes focusable if it has `tabindex`. The value of the attribute is the order number of the element when `key:Tab` (or something like that) is used to switch between them. +Cualquier elemento se vuelve enfocable si contiene `tabindex`. El valor del atributo es el número de orden del elemento cuando `key:Tab` (o algo similar) es utilizado para cambiar entre ellos. -That is: if we have two elements, the first has `tabindex="1"`, and the second has `tabindex="2"`, then pressing `key:Tab` while in the first element -- moves the focus into the second one. +Es decir: si tenemos dos elementos donde el primero contiene `tabindex="1"` y el segundo contiene `tabindex="2"`, al presionar `key:Tab` estando situado sobre el primer elemento se traslada el foco al segundo. -The switch order is: elements with `tabindex` from `1` and above go first (in the `tabindex` order), and then elements without `tabindex` (e.g. a regular `<input>`). +El orden de cambio es el siguiente: los elementos con `tabindex` de valor "1" y mayores tienen prioridad (en el orden `tabindex`) y después los elementos sin `tabindex` (por ejemplo un <input> estándar). -Elements without matching `tabindex` are switched in the document source order (the default order). +Elementos sin el `tabindex` correspondiente van cambiando en el orden del código fuente del documento (el orden por defecto). -There are two special values: +Existen dos valores especiales: -- `tabindex="0"` puts an element among those without `tabindex`. That is, when we switch elements, elements with `tabindex=0` go after elements with `tabindex ≥ 1`. +- `tabindex="0"` incluye al elemento entre los que carecen de `tabindex`. Esto es, cuando cambiamos entre elementos, elementos con `tabindex="0"` van después de elementos con `tabindex ≥ "1"`. - Usually it's used to make an element focusable, but keep the default switching order. To make an element a part of the form on par with `<input>`. +Habitualmente se utiliza para hacer que un elemento sea enfocable y a la vez mantener intacto el orden de cambio por defecto. Para hacer que un elemento sea parte del formulario a la par con <input>. -- `tabindex="-1"` allows only programmatic focusing on an element. The `key:Tab` key ignores such elements, but method `elem.focus()` works. +- `tabindex="-1"` permite enfocar un elemento solamente a través de código. `key:Tab` ignora estos elementos, pero el método `elem.focus()` funciona. -For instance, here's a list. Click the first item and press `key:Tab`: +Por ejemplo, he aquí una lista. Clique sobre el primer ítem y pulse `key:Tab`: ```html autorun no-beautify -Click the first item and press Tab. Keep track of the order. Please note that many subsequent Tabs can move the focus out of the iframe in the example. +Clique sobre el primer ítem y pulse `key:Tab`. Fíjese en el orden. Note que subsiguientes `key:Tab` pueden desplazar el foco fuera del iframe en el ejemplo. <ul> - <li tabindex="1">One</li> - <li tabindex="0">Zero</li> - <li tabindex="2">Two</li> - <li tabindex="-1">Minus one</li> + <li tabindex="1">Uno</li> + <li tabindex="0">Cero</li> + <li tabindex="2">Dos</li> + <li tabindex="-1">Menos uno</li> </ul> <style> @@ -147,20 +147,20 @@ Click the first item and press Tab. Keep track of the order. Please note that ma </style> ``` -The order is like this: `1 - 2 - 0`. Normally, `<li>` does not support focusing, but `tabindex` full enables it, along with events and styling with `:focus`. +El orden es el siguiente: `1 - 2 - 0`. Normalmente, `<li>` no admite enfocado, pero `tabindex` lo habilita, junto con eventos y estilado con `:focus`. -```smart header="The property `elem.tabIndex` works too" -We can add `tabindex` from JavaScript by using the `elem.tabIndex` property. That has the same effect. +```smart header="La propiedad `elem.tabIndex` también funciona" +Podemos añadir `tabindex` desde JavaScript utilizando la propiedad `elem.tabIndex`. Se consigue el mismo resultado. ``` -## Delegation: focusin/focusout +## Delegación: focusin/focusout -Events `focus` and `blur` do not bubble. +Los eventos `focus` y `blur` no se propagan. -For instance, we can't put `onfocus` on the `<form>` to highlight it, like this: +Por ejemplo, no podemos añadir `onfocus` en <form> para resaltarlo, así: ```html autorun height=80 -<!-- on focusing in the form -- add the class --> +<!-- enfocando en el formulario -- añadir la clase --> <form *!*onfocus="this.className='focused'"*/!*> <input type="text" name="name" value="Name"> <input type="text" name="surname" value="Surname"> @@ -169,13 +169,13 @@ For instance, we can't put `onfocus` on the `<form>` to highlight it, like this: <style> .focused { outline: 1px solid red; } </style> ``` -The example above doesn't work, because when user focuses on an `<input>`, the `focus` event triggers on that input only. It doesn't bubble up. So `form.onfocus` never triggers. +El ejemplo anterior no funciona porque cuando el usuario enfoca sobre un <input> el evento ´focus´ se dispara solamente sobre esa entrada y no se propaga, por lo que `form.onfocus` nunca se dispara. -There are two solutions. +Existen dos soluciones. -First, there's a funny historical feature: `focus/blur` do not bubble up, but propagate down on the capturing phase. +Primera: hay una peculiar característica histórica: `focus/blur` no se propagan hacia arriba, pero lo hacen hacia abajo en la fase de captura. -This will work: +Esto funcionará: ```html autorun height=80 <form id="form"> @@ -187,18 +187,18 @@ This will work: <script> *!* - // put the handler on capturing phase (last argument true) + // pon el manejador en fase de captura (último argumento "verdadero") form.addEventListener("focus", () => form.classList.add('focused'), true); form.addEventListener("blur", () => form.classList.remove('focused'), true); */!* </script> ``` -Second, there are `focusin` and `focusout` events -- exactly the same as `focus/blur`, but they bubble. +Segunda: existen los eventos `focusin` y `focusout`, exactamente iguales a `focus/blur`, pero se propagan. -Note that they must be assigned using `elem.addEventListener`, not `on<event>`. +Hay que tener en cuenta que han de asignarse utilizando `elem.addEventListener`, no `on<event>`. -So here's another working variant: +La otra opción que funciona: ```html autorun height=80 <form id="form"> @@ -216,12 +216,12 @@ So here's another working variant: </script> ``` -## Summary +## Resumen -Events `focus` and `blur` trigger on an element focusing/losing focus. +Los eventos `focus` y `blur` hacen que un elemento se enfoque/pierda el foco. -Their specials are: -- They do not bubble. Can use capturing state instead or `focusin/focusout`. -- Most elements do not support focus by default. Use `tabindex` to make anything focusable. +Se caracterizan por lo siguiente: +- No se propagan. En su lugar se puede capturar el estado o usar `focusin/focusout`. +- La mayoría de los elementos no permiten enfoque por defecto. Utiliza `tabindex` para hacer cualquier elemento enfocable. -The current focused element is available as `document.activeElement`. +El elemento que en el momento tiene el foco está disponible como `document.activeElement`. diff --git a/2-ui/4-forms-controls/3-events-change-input/1-deposit-calculator/solution.view/index.html b/2-ui/4-forms-controls/3-events-change-input/1-deposit-calculator/solution.view/index.html index 0515c839e..e6acb8413 100644 --- a/2-ui/4-forms-controls/3-events-change-input/1-deposit-calculator/solution.view/index.html +++ b/2-ui/4-forms-controls/3-events-change-input/1-deposit-calculator/solution.view/index.html @@ -23,33 +23,33 @@ <body> - Deposit calculator. + Calculadora de depósito. <form name="calculator"> <table> <tr> - <td>Initial deposit</td> + <td>Depósito inicial</td> <td> <input name="money" type="number" value="10000" required> </td> </tr> <tr> - <td>How many months?</td> + <td>¿Cuántos meses?</td> <td> <select name="months"> - <option value="3">3 (minimum)</option> - <option value="6">6 (half-year)</option> - <option value="12" selected>12 (one year)</option> - <option value="18">18 (1.5 years)</option> - <option value="24">24 (2 years)</option> - <option value="30">30 (2.5 years)</option> - <option value="36">36 (3 years)</option> - <option value="60">60 (5 years)</option> + <option value="3">3 (mínimo)</option> + <option value="6">6 (medio año)</option> + <option value="12" selected>12 (un año)</option> + <option value="18">18 (1.5 años)</option> + <option value="24">24 (2 años)</option> + <option value="30">30 (2.5 años)</option> + <option value="36">36 (3 años)</option> + <option value="60">60 (5 años)</option> </select> </td> </tr> <tr> - <td>Interest per year?</td> + <td>¿Interés anual?</td> <td> <input name="interest" type="number" value="5" required> </td> @@ -62,8 +62,8 @@ <table id="diagram"> <tr> - <th>Was:</th> - <th>Becomes:</th> + <th>Inicial:</th> + <th>Final:</th> </tr> <tr> <th id="money-before"></th> diff --git a/2-ui/4-forms-controls/3-events-change-input/1-deposit-calculator/source.view/index.html b/2-ui/4-forms-controls/3-events-change-input/1-deposit-calculator/source.view/index.html index 7f464020d..c20919050 100644 --- a/2-ui/4-forms-controls/3-events-change-input/1-deposit-calculator/source.view/index.html +++ b/2-ui/4-forms-controls/3-events-change-input/1-deposit-calculator/source.view/index.html @@ -23,33 +23,33 @@ <body> - Deposit calculator. + Calculadora de depósito. <form name="calculator"> <table> <tr> - <td>Initial deposit</td> + <td>Depósito inicial</td> <td> <input name="money" type="number" value="10000" required> </td> </tr> <tr> - <td>How many months?</td> + <td>¿Cuántos meses?</td> <td> <select name="months"> - <option value="3">3 (minimum)</option> - <option value="6">6 (half-year)</option> - <option value="12" selected>12 (one year)</option> - <option value="18">18 (1.5 years)</option> - <option value="24">24 (2 years)</option> - <option value="30">30 (2.5 years)</option> - <option value="36">36 (3 years)</option> - <option value="60">60 (5 years)</option> + <option value="3">3 (mínimo)</option> + <option value="6">6 (medio año)</option> + <option value="12" selected>12 (un año)</option> + <option value="18">18 (1.5 años)</option> + <option value="24">24 (2 años)</option> + <option value="30">30 (2.5 años)</option> + <option value="36">36 (3 años)</option> + <option value="60">60 (5 años)</option> </select> </td> </tr> <tr> - <td>Interest per year?</td> + <td>¿Interés anual?</td> <td> <input name="interest" type="number" value="5" required> </td> @@ -62,8 +62,8 @@ <table id="diagram"> <tr> - <th>Was:</th> - <th>Becomes:</th> + <th>Inicial:</th> + <th>Final:</th> </tr> <tr> <th id="money-before"></th> @@ -81,7 +81,7 @@ let form = document.forms.calculator; - // your code + // ... tu código... </script> diff --git a/2-ui/4-forms-controls/3-events-change-input/1-deposit-calculator/task.md b/2-ui/4-forms-controls/3-events-change-input/1-deposit-calculator/task.md index 73f0477ff..82c366dc7 100644 --- a/2-ui/4-forms-controls/3-events-change-input/1-deposit-calculator/task.md +++ b/2-ui/4-forms-controls/3-events-change-input/1-deposit-calculator/task.md @@ -2,20 +2,20 @@ importance: 5 --- -# Deposit calculator +# Calculadora de depósito -Create an interface that allows to enter a sum of bank deposit and percentage, then calculates how much it will be after given periods of time. +Crea una interfaz que permita ingresar una suma de depósito bancario y porcentaje, luego calcula cuánto será después de un periodo de tiempo determinado. -Here's the demo: +Acá una demostración: [iframe src="solution" height="350" border="1"] -Any input change should be processed immediately. +Cualquier modificación debe ser procesada de inmediato. -The formula is: +La fórmula es: ```js -// initial: the initial money sum -// interest: e.g. 0.05 means 5% per year -// years: how many years to wait +// initial: la suma inicial de dinero +// interest: e.g. 0.05 significa 5% anual +// years: cuántos años esperar let result = Math.round(initial * (1 + interest) ** years); ``` diff --git a/2-ui/4-forms-controls/3-events-change-input/article.md b/2-ui/4-forms-controls/3-events-change-input/article.md index 480197ae5..d0dcffc93 100644 --- a/2-ui/4-forms-controls/3-events-change-input/article.md +++ b/2-ui/4-forms-controls/3-events-change-input/article.md @@ -1,21 +1,21 @@ -# Events: change, input, cut, copy, paste +# Eventos: change, input, cut, copy, paste -Let's cover various events that accompany data updates. +Veamos varios eventos que acompañan la actualización de datos. -## Event: change +## Evento: change -The `change` event triggers when the element has finished changing. +El evento `change` se activa cuando el elemento finaliza un cambio. -For text inputs that means that the event occurs when it loses focus. +Para ingreso de texto significa que el evento ocurre cuando se pierde foco en el elemento. -For instance, while we are typing in the text field below -- there's no event. But when we move the focus somewhere else, for instance, click on a button -- there will be a `change` event: +Por ejemplo, mientras estamos escribiendo en el siguiente cuadro de texto, no hay evento. Pero cuando movemos el focus (enfoque) a otro lado, por ejemplo hacemos click en un botón, entonces ocurre el evento `change`: ```html autorun height=40 run <input type="text" onchange="alert(this.value)"> <input type="button" value="Button"> ``` -For other elements: `select`, `input type=checkbox/radio` it triggers right after the selection changes: +Para otros elementos: `select`, `input type=checkbox/radio` se dispara inmediatamente después de cambiar la opción seleccionada: ```html autorun height=40 run <select onchange="alert(this.value)"> @@ -27,13 +27,13 @@ For other elements: `select`, `input type=checkbox/radio` it triggers right afte ``` -## Event: input +## Evento: input -The `input` event triggers every time after a value is modified by the user. +El evento `input` se dispara cada vez que un valor es modificado por el usuario. -Unlike keyboard events, it triggers on any value change, even those that does not involve keyboard actions: pasting with a mouse or using speech recognition to dictate the text. +A diferencia de los eventos de teclado, ocurre con el cambio a cualquier valor, incluso aquellos que no involucran acciones de teclado: copiar/pegar con el mouse o usar reconocimiento de voz para dictar texto. -For instance: +Por ejemplo: ```html autorun height=40 run <input type="text" id="input"> oninput: <span id="result"></span> @@ -44,25 +44,25 @@ For instance: </script> ``` -If we want to handle every modification of an `<input>` then this event is the best choice. +Si queremos manejar cualquier modificación en un `<input>` entonces este evento es la mejor opción. -On the other hand, `input` event doesn't trigger on keyboard input and other actions that do not involve value change, e.g. pressing arrow keys `key:⇦` `key:⇨` while in the input. +Por otro lado, el evento `input` no se activa con entradas del teclado u otras acciones que no involucren modificar un valor, por ejemplo presionar las flechas de dirección `key:⇦` `key:⇨` mientras se está en el input. -```smart header="Can't prevent anything in `oninput`" -The `input` event occurs after the value is modified. +```smart header="No podemos prevenir nada en oninput" +El evento `input` se dispara después de que el valor es modificado. -So we can't use `event.preventDefault()` there -- it's just too late, there would be no effect. +Por lo tanto no podemos usar `event.preventDefault()` aquí, es demasiado tarde y no tendría efecto. ``` -## Events: cut, copy, paste +## Eventos: cut, copy, paste -These events occur on cutting/copying/pasting a value. +Estos eventos ocurren al cortar/copiar/pegar un valor. -They belong to [ClipboardEvent](https://www.w3.org/TR/clipboard-apis/#clipboard-event-interfaces) class and provide access to the data that is cut/copied/pasted. +Estos pertenecen a la clase [ClipboardEvent](https://www.w3.org/TR/clipboard-apis/#clipboard-event-interfaces) y dan acceso a los datos cortados/copiados/pegados. -We also can use `event.preventDefault()` to abort the action, then nothing gets copied/pasted. +También podemos usar `event.preventDefault()` para cancelar la acción y que nada sea cortado/copiado/pegado. -For instance, the code below prevents all `cut/copy/paste` events and shows the text we're trying to cut/copy/paste: +El siguiente código también evita todo evento `cut/copy/paste` y muestra qué es los que estamos intentando cortar/copiar/pegar: ```html autorun height=40 run <input type="text" id="input"> @@ -79,36 +79,36 @@ For instance, the code below prevents all `cut/copy/paste` events and shows the </script> ``` -Please note: inside `cut` and `copy` event handlers a call to `event.clipboardData.getData(...)` returns an empty string. That's because technically the data isn't in the clipboard yet. If we use `event.preventDefault()` it won't be copied at all. +Nota que dentro de los manejadores `cut` y `copy`, llamar a `event.clipboardData.getData(...)` devuelve un string vacío. Esto es porque el dato no está en el portapapeles aún. Y si usamos `event.preventDefault()` no será copiado en absoluto. -So the example above uses `document.getSelection()` to get the selected text. You can find more details about document selection in the article <info:selection-range>. +Por ello el ejemplo arriba usa `document.getSelection()` para obtener el texto seleccionado. Puedes encontrar más detalles acerca de selección en el artículo <info:selection-range>. -It's possible to copy/paste not just text, but everything. For instance, we can copy a file in the OS file manager, and paste it. +No solo es posible copiar/pegar texto, sino cualquier cosa. Por ejemplo, podemos copiar un archivo en el gestor de archivos del SO y pegarlo. -That's because `clipboardData` implements `DataTransfer` interface, commonly used for drag'n'drop and copy/pasting. It's a bit beyond our scope now, but you can find its methods in the [DataTransfer specification](https://html.spec.whatwg.org/multipage/dnd.html#the-datatransfer-interface). +Esto es porque `clipboardData` implementa la interfaz `DataTransfer`, usada comúnmente para "arrastrar y soltar" y "copiar y pegar". Ahora esto está fuera de nuestro objetivo, pero puedes encontrar sus métodos [en la especificación DataTransfer](https://html.spec.whatwg.org/multipage/dnd.html#the-datatransfer-interface). -Also, there's an additional asynchronous API of accessing the clipboard: `navigator.clipboard`. More about it in the specification [Clipboard API and events](https://www.w3.org/TR/clipboard-apis/), [not supported by Firefox](https://caniuse.com/async-clipboard). +Hay además una API asincrónica adicional para acceso al portapapeles: `navigator.clipboard`. Más en la especificación [Clipboard API and events](https://www.w3.org/TR/clipboard-apis/), [no soportado en Firefox](https://caniuse.com/async-clipboard). -### Safety restrictions +### Restricciones de seguridad -The clipboard is a "global" OS-level thing. A user may switch between various applications, copy/paste different things, and a browser page shouldn't see all that. +El portapapeles es algo a nivel "global" del SO. Un usuario puede alternar entre ventanas, copiar y pegar diferentes cosas, y el navegador no debería ver todo eso. -So most browsers allow seamless read/write access to the clipboard only in the scope of certain user actions, such as copying/pasting etc. +Por ello la mayoría de los navegadores dan acceso al portapapeles únicamente bajo determinadas acciones del usuario, como copiar y pegar. -It's forbidden to generate "custom" clipboard events with `dispatchEvent` in all browsers except Firefox. And even if we manage to dispatch such event, the specification clearly states that such "synthetic" events must not provide access to the clipboard. +Está prohibido generar eventos "personalizados" del portapapeles con `dispatchEvent` en todos los navegadores excepto Firefox. Incluso si logramos enviar tal evento, la especificación establece claramente que tal evento "sintético" no debe brindar acceso al portapapeles. -Even if someone decides to save `event.clipboardData` in an event handler, and then access it later -- it won't work. +Incluso si alguien decide guardar `event.clipboardData` en un manejador de evento para accederlo luego, esto no funcionará. -To reiterate, [event.clipboardData](https://www.w3.org/TR/clipboard-apis/#clipboardevent-clipboarddata) works solely in the context of user-initiated event handlers. +Para reiterar, [event.clipboardData](https://www.w3.org/TR/clipboard-apis/#clipboardevent-clipboarddata) funciona únicamente en el contexto de manejadores de eventos iniciados por el usuario. -On the other hand, [navigator.clipboard](https://www.w3.org/TR/clipboard-apis/#h-navigator-clipboard) is the more recent API, meant for use in any context. It asks for user permission, if needed. +Por otro lado, [navigator.clipboard](https://www.w3.org/TR/clipboard-apis/#h-navigator-clipboard) es una API más reciente, pensada para el uso en cualquier contexto. Esta pide autorización al usuario cuando la necesita. -## Summary +## Resumen -Data change events: +Eventos de modificación de datos: -| Event | Description | Specials | +| Evento | Descripción | Especiales | |---------|----------|-------------| -| `change`| A value was changed. | For text inputs triggers on focus loss. | -| `input` | For text inputs on every change. | Triggers immediately unlike `change`. | -| `cut/copy/paste` | Cut/copy/paste actions. | The action can be prevented. The `event.clipboardData` property gives access to the clipboard. All browsers except Firefox also support `navigator.clipboard`. | +| `change`| Un valor fue cambiado. | En ingreso de texto, se dispara cuando el elemento pierde el foco | +| `input` | Cada cambio de entrada de texto | Se dispara de inmediato con cada cambio, a diferencia de `change`. | +| `cut/copy/paste` | Acciones cortar/copiar/pegar | La acción puede ser cancelada. La propiedad `event.clipboardData` brinda acceso al portapeles. Todos los navegadores excepto Firefox también soportan `navigator.clipboard`. | diff --git a/2-ui/4-forms-controls/4-forms-submit/1-modal-dialog/solution.md b/2-ui/4-forms-controls/4-forms-submit/1-modal-dialog/solution.md index 781ce5d58..0a5f2adac 100644 --- a/2-ui/4-forms-controls/4-forms-submit/1-modal-dialog/solution.md +++ b/2-ui/4-forms-controls/4-forms-submit/1-modal-dialog/solution.md @@ -1,4 +1,4 @@ -A modal window can be implemented using a half-transparent `<div id="cover-div">` that covers the whole window, like this: +Una ventana modal puede ser implementada utilizando un `<div id="cover-div">` semi-transparente que cubra completamente la ventana, como a continuación: ```css #cover-div { @@ -13,8 +13,8 @@ A modal window can be implemented using a half-transparent `<div id="cover-div"> } ``` -Because the `<div>` covers everything, it gets all clicks, not the page below it. +Debido a que el `<div>` cubre toda la ventana, recibe todos los clicks, en vez de la página tras él. -Also we can prevent page scroll by setting `body.style.overflowY='hidden'`. +También podemos evitar el scroll en la página utilizando `body.style.overflowY='hidden'`. -The form should be not in the `<div>`, but next to it, because we don't want it to have `opacity`. +El formulario no debe estar en el `<div>` sino junto a él, porque no queremos que tenga `opacity`. diff --git a/2-ui/4-forms-controls/4-forms-submit/1-modal-dialog/solution.view/index.html b/2-ui/4-forms-controls/4-forms-submit/1-modal-dialog/solution.view/index.html index ec9e1b5ee..b7040c95a 100644 --- a/2-ui/4-forms-controls/4-forms-submit/1-modal-dialog/solution.view/index.html +++ b/2-ui/4-forms-controls/4-forms-submit/1-modal-dialog/solution.view/index.html @@ -8,9 +8,9 @@ <body style="height:3000px"> - <h2>Click the button below</h2> + <h2>Pulsa el botón de abajo</h2> - <input type="button" value="Click to show the form" id="show-button"> + <input type="button" value="Click aquí para mostrar el formulario" id="show-button"> <div id="prompt-form-container"> @@ -18,18 +18,18 @@ <h2>Click the button below</h2> <div id="prompt-message"></div> <input name="text" type="text"> <input type="submit" value="Ok"> - <input type="button" name="cancel" value="Cancel"> + <input type="button" name="cancel" value="Cancelar"> </form> </div> <script> - // Show a half-transparent DIV to "shadow" the page - // (the form is not inside, but near it, because it shouldn't be half-transparent) + // Mostrar un DIV semi-transparente para cubrir la página. + // (el formulario no está dentro sino junto a él, porque no debe tener transparencia. function showCover() { let coverDiv = document.createElement('div'); coverDiv.id = 'cover-div'; - // make the page unscrollable while the modal form is open + // evitar el scroll en la página cuando el modal esta abierto document.body.style.overflowY = 'hidden'; document.body.append(coverDiv); @@ -56,7 +56,7 @@ <h2>Click the button below</h2> form.onsubmit = function() { let value = form.text.value; - if (value == '') return false; // ignore empty submit + if (value == '') return false; // ignorar submit vacíos complete(value); return false; @@ -94,8 +94,8 @@ <h2>Click the button below</h2> } document.getElementById('show-button').onclick = function() { - showPrompt("Enter something<br>...smart :)", function(value) { - alert("You entered: " + value); + showPrompt("Escribe algo<br>...inteligente :)", function(value) { + alert("Escribiste: " + value); }); }; </script> diff --git a/2-ui/4-forms-controls/4-forms-submit/1-modal-dialog/source.view/index.html b/2-ui/4-forms-controls/4-forms-submit/1-modal-dialog/source.view/index.html index 230a51174..e86bda255 100644 --- a/2-ui/4-forms-controls/4-forms-submit/1-modal-dialog/source.view/index.html +++ b/2-ui/4-forms-controls/4-forms-submit/1-modal-dialog/source.view/index.html @@ -11,11 +11,11 @@ <div id="prompt-form-container"> <form id="prompt-form"> - <div id="prompt-message">Enter something... - <br>Please..</div> + <div id="prompt-message">Escribe algo... + <br>Por favor...</div> <input name="text" type="text"> <input type="submit" value="Ok"> - <input type="button" name="cancel" value="Cancel"> + <input type="button" name="cancel" value="Cancelar"> </form> </div> diff --git a/2-ui/4-forms-controls/4-forms-submit/1-modal-dialog/task.md b/2-ui/4-forms-controls/4-forms-submit/1-modal-dialog/task.md index bf6841c92..1803791eb 100644 --- a/2-ui/4-forms-controls/4-forms-submit/1-modal-dialog/task.md +++ b/2-ui/4-forms-controls/4-forms-submit/1-modal-dialog/task.md @@ -2,32 +2,32 @@ importance: 5 --- -# Modal form +# Formulario modal -Create a function `showPrompt(html, callback)` that shows a form with the message `html`, an input field and buttons `OK/CANCEL`. +Crea una función `showPrompt(html, callback)` que muestre un formulario con el mensaje `html`, un campo input y botones `OK/CANCELAR`. -- A user should type something into a text field and press `key:Enter` or the OK button, then `callback(value)` is called with the value they entered. -- Otherwise if the user presses `key:Esc` or CANCEL, then `callback(null)` is called. +- Un usuario debe escribir algo en el campo de texto y pulsar `key:Enter` o el botón OK, entonces `callback(value)` es llamado con el valor introducido. +- En caso contrario, si el usuario pulsa `key:Esc` o CANCELAR, entonces `callback(null)` es llamado. -In both cases that ends the input process and removes the form. +En ambos casos se finaliza el proceso se y borra el formulario. -Requirements: +Requisitos: -- The form should be in the center of the window. -- The form is *modal*. In other words, no interaction with the rest of the page is possible until the user closes it. -- When the form is shown, the focus should be inside the `<input>` for the user. -- Keys `key:Tab`/`key:Shift+Tab` should shift the focus between form fields, don't allow it to leave for other page elements. +- El formulario debe estar en el centro de la ventana. +- El formulario es *modal*. Es decir que no habrá interacción con el resto de la página, siempre que sea posible, hasta que el usuario lo cierre. +- Cuando se muestra el formulario, el foco debe estar en el `<input>` del usuario. +- Las teclas `key:Tab`/`key:Shift+Tab` deben alternar el foco entre los diferentes campos del formulario, no se permite cambiar el foco a otros elementos de la página. -Usage example: +Ejemplo de uso: ```js -showPrompt("Enter something<br>...smart :)", function(value) { +showPrompt("Escribe algo<br>...inteligente :)", function(value) { alert(value); }); ``` -A demo in the iframe: +Demo en el iframe: [iframe src="solution" height=160 border=1] -P.S. The source document has HTML/CSS for the form with fixed positioning, but it's up to you to make it modal. +P.S. El código fuente tiene el HTML/CSS para el formulario con posición fija. Pero tú decides cómo haces el modal. diff --git a/2-ui/4-forms-controls/4-forms-submit/article.md b/2-ui/4-forms-controls/4-forms-submit/article.md index c00c559c0..a15ff289b 100644 --- a/2-ui/4-forms-controls/4-forms-submit/article.md +++ b/2-ui/4-forms-controls/4-forms-submit/article.md @@ -1,55 +1,55 @@ -# Forms: event and method submit +# Formularios: evento y método submit -The `submit` event triggers when the form is submitted, it is usually used to validate the form before sending it to the server or to abort the submission and process it in JavaScript. +El evento `submit` se activa cuando el formulario es enviado, normalmente se utiliza para validar el formulario antes de ser enviado al servidor o bien para abortar el envío y procesarlo con JavaScript. -The method `form.submit()` allows to initiate form sending from JavaScript. We can use it to dynamically create and send our own forms to server. +El método `form.submit()` permite iniciar el envío del formulario mediante JavaScript. Podemos utilizarlo para crear y enviar nuestros propios formularios al servidor. -Let's see more details of them. +Veamos más detalles sobre ellos. -## Event: submit +## Evento: submit -There are two main ways to submit a form: +Mayormente un formulario puede enviarse de dos maneras: -1. The first -- to click `<input type="submit">` or `<input type="image">`. -2. The second -- press `key:Enter` on an input field. +1. La primera -- Haciendo click en `<input type="submit">` o en `<input type="image">`. +2. La segunda -- Pulsando la tecla `key:Enter` en un campo del formulario. -Both actions lead to `submit` event on the form. The handler can check the data, and if there are errors, show them and call `event.preventDefault()`, then the form won't be sent to the server. +Ambas acciones causan que el evento `submit` sea activado en el formulario. El handler puede comprobar los datos, y si hay errores, mostrarlos e invocar `event.preventDefault()`, entonces el formulario no será enviado al servidor. -In the form below: -1. Go into the text field and press `key:Enter`. -2. Click `<input type="submit">`. +En el formulario de abajo: +1. Ve al campo tipo texto y pulsa la tecla `key:Enter`. +2. Haz click en `<input type="submit">`. -Both actions show `alert` and the form is not sent anywhere due to `return false`: +Ambas acciones muestran `alert` y el formulario no es enviado debido a la presencia de `return false`: ```html autorun height=60 no-beautify <form onsubmit="alert('submit!');return false"> - First: Enter in the input field <input type="text" value="text"><br> - Second: Click "submit": <input type="submit" value="Submit"> + Primero: Enter en el campo de texto <input type="text" value="texto"><br> + Segundo: Click en "submit": <input type="submit" value="Submit"> </form> ``` -````smart header="Relation between `submit` and `click`" -When a form is sent using `key:Enter` on an input field, a `click` event triggers on the `<input type="submit">`. +````smart header="Relación entre `submit` y `click`" +Cuando un formulario es enviado utilizando `key:Enter` en un campo tipo texto, un evento `click` se genera en el `<input type="submit">` -That's rather funny, because there was no click at all. +Muy curioso, dado que no hubo ningún click en absoluto. -Here's the demo: +Aquí esta la demo: ```html autorun height=60 <form onsubmit="return false"> - <input type="text" size="30" value="Focus here and press enter"> + <input type="text" size="30" value="Sitúa el cursor aquí y pulsa Enter"> <input type="submit" value="Submit" *!*onclick="alert('click')"*/!*> </form> ``` ```` -## Method: submit +## Método: submit -To submit a form to the server manually, we can call `form.submit()`. +Para enviar un formulario al servidor manualmente, podemos usar `form.submit()`. -Then the `submit` event is not generated. It is assumed that if the programmer calls `form.submit()`, then the script already did all related processing. +Entonces el evento `submit` no será generado. Se asume que si el programador llama `form.submit()`, entonces el script ya realizó todo el procesamiento relacionado. -Sometimes that's used to manually create and send a form, like this: +A veces es usado para crear y enviar un formulario manualmente, como en este ejemplo: ```js run let form = document.createElement('form'); @@ -58,7 +58,7 @@ form.method = 'GET'; form.innerHTML = '<input name="q" value="test">'; -// the form must be in the document to submit it +// el formulario debe estar en el document para poder enviarlo document.body.append(form); form.submit(); diff --git a/2-ui/4-forms-controls/index.md b/2-ui/4-forms-controls/index.md index 726474b19..4cc72ec8e 100644 --- a/2-ui/4-forms-controls/index.md +++ b/2-ui/4-forms-controls/index.md @@ -1,3 +1,3 @@ -# Forms, controls +# Formularios y controles -Special properties and events for forms `<form>` and controls: `<input>`, `<select>` and other. +Propiedades especiales y eventos de formularios `<form>` y controles: `<input>`, `<select>` y otros. diff --git a/2-ui/5-loading/01-onload-ondomcontentloaded/article.md b/2-ui/5-loading/01-onload-ondomcontentloaded/article.md index 07624a658..c68bd0b75 100644 --- a/2-ui/5-loading/01-onload-ondomcontentloaded/article.md +++ b/2-ui/5-loading/01-onload-ondomcontentloaded/article.md @@ -1,39 +1,39 @@ -# Page: DOMContentLoaded, load, beforeunload, unload +# Página: DOMContentLoaded, load, beforeunload, unload -The lifecycle of an HTML page has three important events: +El ciclo de vida de una página HTML tiene tres eventos importantes: -- `DOMContentLoaded` -- the browser fully loaded HTML, and the DOM tree is built, but external resources like pictures `<img>` and stylesheets may not yet have loaded. -- `load` -- not only HTML is loaded, but also all the external resources: images, styles etc. -- `beforeunload/unload` -- the user is leaving the page. +- `DOMContentLoaded` -- el navegador HTML está completamente cargado y el árbol DOM está construido, pero es posible que los recursos externos como `<img>` y hojas de estilo aún no se hayan cargado. +- `load` -- no solo se cargó el HTML, sino también todos los recursos externos: imágenes, estilos, etc. +- `beforeunload/unload` -- el usuario sale de la pagina. -Each event may be useful: +Cada evento puede ser útil: -- `DOMContentLoaded` event -- DOM is ready, so the handler can lookup DOM nodes, initialize the interface. -- `load` event -- external resources are loaded, so styles are applied, image sizes are known etc. -- `beforeunload` event -- the user is leaving: we can check if the user saved the changes and ask them whether they really want to leave. -- `unload` -- the user almost left, but we still can initiate some operations, such as sending out statistics. +- Evento `DOMContentLoaded` -- DOM está listo, por lo que el controlador puede buscar nodos DOM, inicializar la interfaz. +- Evento `load` -- se cargan recursos externos, por lo que se aplican estilos, se conocen tamaños de imagen, etc. +- Evento `beforeunload` -- el usuario se va: podemos comprobar si el usuario guardó los cambios y preguntarle si realmente quiere irse. +- Evento `unload` -- el usuario casi se fue, pero aún podemos iniciar algunas operaciones, como enviar estadísticas. -Let's explore the details of these events. +Exploremos los detalles de estos eventos. ## DOMContentLoaded -The `DOMContentLoaded` event happens on the `document` object. +El evento `DOMContentLoaded` ocurre en el objeto `document`. -We must use `addEventListener` to catch it: +Debemos usar `addEventListener` para capturarlo: ```js document.addEventListener("DOMContentLoaded", ready); -// not "document.onDOMContentLoaded = ..." +// no "document.onDOMContentLoaded = ..." ``` -For instance: +Por ejemplo: ```html run height=200 refresh <script> function ready() { alert('DOM is ready'); - // image is not yet loaded (unless it was cached), so the size is 0x0 + // la imagen aún no está cargada (a menos que se haya almacenado en caché), por lo que el tamaño es 0x0 alert(`Image size: ${img.offsetWidth}x${img.offsetHeight}`); } @@ -45,80 +45,80 @@ For instance: <img id="img" src="https://en.js.cx/clipart/train.gif?speed=1&cache=0"> ``` -In the example, the `DOMContentLoaded` handler runs when the document is loaded, so it can see all the elements, including `<img>` below. +En el ejemplo, el controlador del evento `DOMContentLoaded` se ejecuta cuando el documento está cargado, por lo que puede ver todos los elementos, incluido el `<img>` que está después de él. -But it doesn't wait for the image to load. So `alert` shows zero sizes. +Pero no espera a que se cargue la imagen. Entonces, `alert` muestra los tamaños en cero. -At first sight, the `DOMContentLoaded` event is very simple. The DOM tree is ready -- here's the event. There are few peculiarities though. +A primera vista, el evento `DOMContentLoaded` es muy simple. El árbol DOM está listo -- aquí está el evento. Sin embargo, hay algunas peculiaridades. -### DOMContentLoaded and scripts +### DOMContentLoaded y scripts -When the browser processes an HTML-document and comes across a `<script>` tag, it needs to execute before continuing building the DOM. That's a precaution, as scripts may want to modify DOM, and even `document.write` into it, so `DOMContentLoaded` has to wait. +Cuando el navegador procesa un documento HTML y se encuentra con una etiqueta `<script>`, debe ejecutarla antes de continuar construyendo el DOM. Esa es una precaución, ya que los scripts pueden querer modificar el DOM, e incluso hacer `document.write` en él, por lo que` DOMContentLoaded` tiene que esperar. -So DOMContentLoaded definitely happens after such scripts: +Entonces DOMContentLoaded siempre ocurre después de tales scripts: ```html run <script> document.addEventListener("DOMContentLoaded", () => { - alert("DOM ready!"); + alert("DOM listo!"); }); </script> <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.3.0/lodash.js"></script> <script> - alert("Library loaded, inline script executed"); + alert("Libreria cargada, linea de script ejecutada"); </script> ``` -In the example above, we first see "Library loaded...", and then "DOM ready!" (all scripts are executed). +En el ejemplo anterior, primero vemos "Biblioteca cargada ..." y luego "¡DOM listo!" (se ejecutan todos los scripts). -```warn header="Scripts that don't block DOMContentLoaded" -There are two exceptions from this rule: -1. Scripts with the `async` attribute, that we'll cover [a bit later](info:script-async-defer), don't block `DOMContentLoaded`. -2. Scripts that are generated dynamically with `document.createElement('script')` and then added to the webpage also don't block this event. +```warn header="Scripts que no bloquean DOMContentLoaded" +Hay dos excepciones a esta regla: +1. Scripts con el atributo `async`, que cubriremos [un poco más tarde](info:script-async-defer), no bloquea el `DOMContentLoaded`. +2. Los scripts que se generan dinámicamente con `document.createElement('script')` y luego se agregan a la página web, tampoco bloquean este evento. ``` -### DOMContentLoaded and styles +### DOMContentLoaded y estilos -External style sheets don't affect DOM, so `DOMContentLoaded` does not wait for them. +Las hojas de estilo externas no afectan a DOM, por lo que `DOMContentLoaded` no las espera. -But there's a pitfall. If we have a script after the style, then that script must wait until the stylesheet loads: +Pero hay una trampa. Si tenemos un script después del estilo, entonces ese script debe esperar hasta que se cargue la hoja de estilo: ```html run <link type="text/css" rel="stylesheet" href="style.css"> <script> - // the script doesn't execute until the stylesheet is loaded + // el script no se ejecuta hasta que se cargue la hoja de estilo alert(getComputedStyle(document.body).marginTop); </script> ``` -The reason for this is that the script may want to get coordinates and other style-dependent properties of elements, like in the example above. Naturally, it has to wait for styles to load. +La razón de esto es que el script puede querer obtener coordenadas y otras propiedades de elementos dependientes del estilo, como en el ejemplo anterior. Naturalmente, tiene que esperar a que se carguen los estilos. -As `DOMContentLoaded` waits for scripts, it now waits for styles before them as well. +Como DOMContentLoaded espera los scripts, ahora también espera a los estilos que están antes que ellos. -### Built-in browser autofill +### Autocompletar del navegador integrado -Firefox, Chrome and Opera autofill forms on `DOMContentLoaded`. +Firefox, Chrome y Opera autocompletan formularios en `DOMContentLoaded`. -For instance, if the page has a form with login and password, and the browser remembered the values, then on `DOMContentLoaded` it may try to autofill them (if approved by the user). +Por ejemplo, si la página tiene un formulario con nombre de usuario y contraseña, y el navegador recuerda los valores, entonces en `DOMContentLoaded` puede intentar completarlos automáticamente (si el usuario lo aprueba). -So if `DOMContentLoaded` is postponed by long-loading scripts, then autofill also awaits. You probably saw that on some sites (if you use browser autofill) -- the login/password fields don't get autofilled immediately, but there's a delay till the page fully loads. That's actually the delay until the `DOMContentLoaded` event. +Entonces, si `DOMContentLoaded` es pospuesto por scripts de largo tiempo de carga, el autocompletado también espera. Probablemente haya visto eso en algunos sitios (si usa la función de autocompletar del navegador): los campos de inicio de sesión/contraseña no se autocompletan inmediatamente, sino con retraso hasta que la página se carga por completo. En realidad es el retraso hasta el evento `DOMContentLoaded`. ## window.onload [#window-onload] -The `load` event on the `window` object triggers when the whole page is loaded including styles, images and other resources. This event is available via the `onload` property. +El evento `load` en el objeto `window` se activa cuando se carga toda la página, incluidos estilos, imágenes y otros recursos. Este evento está disponible a través de la propiedad `onload`. -The example below correctly shows image sizes, because `window.onload` waits for all images: +El siguiente ejemplo muestra correctamente los tamaños de las imágenes, porque `window.onload` espera todas las imágenes: ```html run height=200 refresh <script> - window.onload = function() { // can also use window.addEventListener('load', (event) => { - alert('Page loaded'); + window.onload = function() { // también puede usar window.addEventListener('load', (event) => { + alert('Página cargada'); - // image is loaded at this time - alert(`Image size: ${img.offsetWidth}x${img.offsetHeight}`); + // la imagen es cargada al mismo tiempo + alert(`Tamaño de imagen: ${img.offsetWidth}x${img.offsetHeight}`); }; </script> @@ -127,45 +127,45 @@ The example below correctly shows image sizes, because `window.onload` waits for ## window.onunload -When a visitor leaves the page, the `unload` event triggers on `window`. We can do something there that doesn't involve a delay, like closing related popup windows. +Cuando un visitante abandona la página, el evento `unload` se activa en `window`. Podemos hacer algo allí que no implique un retraso, como cerrar ventanas emergentes relacionadas. -The notable exception is sending analytics. +La excepción notable es el envío de análisis. -Let's say we gather data about how the page is used: mouse clicks, scrolls, viewed page areas, and so on. +Supongamos que recopilamos datos sobre cómo se usa la página: clicks del mouse, desplazamientos, áreas de página visitadas, etc. -Naturally, `unload` event is when the user leaves us, and we'd like to save the data on our server. +Naturalmente, el evento `unload` sucede cuando el usuario nos deja y nos gustaría guardar los datos en nuestro servidor. -There exists a special `navigator.sendBeacon(url, data)` method for such needs, described in the specification <https://w3c.github.io/beacon/>. +Existe un método especial `navigator.sendBeacon(url, data)` para tales necesidades, descrito en la especificación <https://w3c.github.io/beacon/>. -It sends the data in background. The transition to another page is not delayed: the browser leaves the page, but still performs `sendBeacon`. +Este envía los datos en segundo plano. La transición a otra página no se retrasa: el navegador abandona la página, pero aún realiza `sendBeacon`. -Here's how to use it: +Así es como se usa: ```js -let analyticsData = { /* object with gathered data */ }; +let analyticsData = { /* objeto con datos recopilados */ }; window.addEventListener("unload", function() { navigator.sendBeacon("/analytics", JSON.stringify(analyticsData)); }); ``` -- The request is sent as POST. -- We can send not only a string, but also forms and other formats, as described in the chapter <info:fetch>, but usually it's a stringified object. -- The data is limited by 64kb. +- La solicitud se envía como POST. +- Podemos enviar no solo una cadena, sino también formularios y otros formatos, como se describe en el capítulo <info: fetch>, pero generalmente es un objeto string. +- Los datos están limitados por 64 kb. -When the `sendBeacon` request is finished, the browser probably has already left the document, so there's no way to get server response (which is usually empty for analytics). +Cuando finaliza la solicitud `sendBeacon`, es probable que el navegador ya haya abandonado el documento, por lo que no hay forma de obtener la respuesta del servidor (que suele estar vacía para análisis). -There's also a `keepalive` flag for doing such "after-page-left" requests in [fetch](info:fetch) method for generic network requests. You can find more information in the chapter <info:fetch-api>. +También hay una bandera `keepalive` para hacer tales solicitudes "after-page-left" en el método [fetch](info: fetch) para solicitudes de red genéricas. Puede encontrar más información en el capítulo <info: fetch-api>. -If we want to cancel the transition to another page, we can't do it here. But we can use another event -- `onbeforeunload`. +Si queremos cancelar la transición a otra página, no podemos hacerlo aquí. Pero podemos usar otro evento: `onbeforeunload`. ## window.onbeforeunload [#window.onbeforeunload] -If a visitor initiated navigation away from the page or tries to close the window, the `beforeunload` handler asks for additional confirmation. +Si un visitante inició la navegación fuera de la página o intenta cerrar la ventana, el controlador `beforeunload` solicita una confirmación adicional. -If we cancel the event, the browser may ask the visitor if they are sure. +Si cancelamos el evento, el navegador puede preguntar al visitante si está seguro. -You can try it by running this code and then reloading the page: +Puede probarlo ejecutando este código y luego recargando la página: ```js run window.onbeforeunload = function() { @@ -173,85 +173,85 @@ window.onbeforeunload = function() { }; ``` -For historical reasons, returning a non-empty string also counts as canceling the event. Some time ago browsers used to show it as a message, but as the [modern specification](https://html.spec.whatwg.org/#unloading-documents) says, they shouldn't. +Por razones históricas, devolver una cadena no vacía también cuenta como cancelar el evento. Hace algún tiempo, los navegadores solían mostrarlo como un mensaje, pero como dice la [especificación moderna](https://html.spec.whatwg.org/#unloading-documents), no deberían. -Here's an example: +Aquí hay un ejemplo: ```js run window.onbeforeunload = function() { - return "There are unsaved changes. Leave now?"; + return "Hay cambios sin guardar. ¿Salir ahora?"; }; ``` -The behavior was changed, because some webmasters abused this event handler by showing misleading and annoying messages. So right now old browsers still may show it as a message, but aside of that -- there's no way to customize the message shown to the user. +El comportamiento se modificó, porque algunos webmasters abusaron de este controlador de eventos mostrando mensajes engañosos y molestos. Entonces, en este momento, los navegadores antiguos aún pueden mostrarlo como un mensaje, pero aparte de eso, no hay forma de personalizar el mensaje que se muestra al usuario. -````warn header="The `event.preventDefault()` doesn't work from a `beforeunload` handler" -That may sound weird, but most browsers ignore `event.preventDefault()`. +````warn header="El `event.preventDefault()` no funciona desde un manejador `beforeunload`" +Esto puede sonar extraño, pero la mayoría de los navegadores ignoran `event.preventDefault()`. -Which means, following code may not work: +Lo que significa que el siguiente código puede no funcionar: ```js run window.addEventListener("beforeunload", (event) => { - // doesn't work, so this event handler doesn't do anything + // no funciona, así que el manejador de evento no hace nada event.preventDefault(); }); ``` -Instead, in such handlers one should set `event.returnValue` to a string to get the result similar to the code above: +En lugar de ello, en tales manejadores uno debe establecer `event.returnValue` a un string para obtener un resultado similar al pretendido en el código de arriba: ```js run window.addEventListener("beforeunload", (event) => { - // works, same as returning from window.onbeforeunload - event.returnValue = "There are unsaved changes. Leave now?"; + // funciona, lo mismo que si devolviera desde window.onbeforeunload + event.returnValue = "Hsy cambios sin grabar. ¿Abandonar ahora?"; }); ``` ```` ## readyState -What happens if we set the `DOMContentLoaded` handler after the document is loaded? +¿Qué sucede si configuramos el controlador `DOMContentLoaded` después de cargar el documento? -Naturally, it never runs. +Naturalmente, nunca se ejecutará. -There are cases when we are not sure whether the document is ready or not. We'd like our function to execute when the DOM is loaded, be it now or later. +Hay casos en los que no estamos seguros de si el documento está listo o no. Nos gustaría que nuestra función se ejecute cuando se cargue el DOM, ya sea ahora o más tarde. -The `document.readyState` property tells us about the current loading state. +La propiedad `document.readyState` nos informa sobre el estado de carga actual. -There are 3 possible values: +Hay 3 valores posibles: -- `"loading"` -- the document is loading. -- `"interactive"` -- the document was fully read. -- `"complete"` -- the document was fully read and all resources (like images) are loaded too. +- `"loading"` -- el documento se está cargando. +- `"interactive"` -- el documento fue leído por completo. +- `"complete"` -- el documento se leyó por completo y todos los recursos (como imágenes) también se cargaron. -So we can check `document.readyState` and setup a handler or execute the code immediately if it's ready. +Entonces podemos verificar `document.readyState` y configurar un controlador o ejecutar el código inmediatamente si está listo. -Like this: +Como esto: ```js function work() { /*...*/ } if (document.readyState == 'loading') { - // still loading, wait for the event + // cargando todavía, esperar el evento document.addEventListener('DOMContentLoaded', work); } else { - // DOM is ready! + // DOM está listo! work(); } ``` -There's also the `readystatechange` event that triggers when the state changes, so we can print all these states like this: +También existe el evento `readystatechange` que se activa cuando cambia el estado, por lo que podemos imprimir todos estos estados así: ```js run -// current state +// estado actual console.log(document.readyState); -// print state changes +//imprimir los cambios de estado document.addEventListener('readystatechange', () => console.log(document.readyState)); ``` -The `readystatechange` event is an alternative mechanics of tracking the document loading state, it appeared long ago. Nowadays, it is rarely used. +El evento `readystatechange` es una mecánica alternativa para rastrear el estado de carga del documento, apareció hace mucho tiempo. Hoy en día, rara vez se usa. -Let's see the full events flow for the completeness. +Veamos el flujo de eventos completo para ver si están completados. -Here's a document with `<iframe>`, `<img>` and handlers that log events: +Aquí hay un documento con `<iframe>`, `<img>` y controladores que registran eventos: ```html <script> @@ -271,34 +271,34 @@ Here's a document with `<iframe>`, `<img>` and handlers that log events: </script> ``` -The working example is [in the sandbox](sandbox:readystate). +El ejemplo práctico está [en el sandbox](sandbox:readystate). -The typical output: -1. [1] initial readyState:loading -2. [2] readyState:interactive +La salida típica: +1. [1] readyState inicial: loading +2. [2] readyState: interactive 3. [2] DOMContentLoaded 4. [3] iframe onload 5. [4] img onload -6. [4] readyState:complete +6. [4] readyState: complete 7. [4] window onload -The numbers in square brackets denote the approximate time of when it happens. Events labeled with the same digit happen approximately at the same time (+- a few ms). +Los números entre corchetes denotan el tiempo aproximado en el que ocurre. Los eventos etiquetados con el mismo dígito ocurren aproximadamente al mismo tiempo (+ - unos pocos ms). -- `document.readyState` becomes `interactive` right before `DOMContentLoaded`. These two things actually mean the same. -- `document.readyState` becomes `complete` when all resources (`iframe` and `img`) are loaded. Here we can see that it happens in about the same time as `img.onload` (`img` is the last resource) and `window.onload`. Switching to `complete` state means the same as `window.onload`. The difference is that `window.onload` always works after all other `load` handlers. +- `document.readyState` se convierte en `interactive` justo antes de `DOMContentLoaded`. Estas dos cosas realmente significan lo mismo. +- `document.readyState` se convierte en `complete` cuando se cargan todos los recursos (`iframe` e `img`). Aquí podemos ver que ocurre aproximadamente al mismo tiempo que `img.onload` (`img` es el último recurso) y `window.onload`. Cambiar al estado `complete` significa lo mismo que "window.onload". La diferencia es que `window.onload` siempre funciona después de todos los demás controladores `load`. -## Summary +## Resumen -Page load events: +Eventos de carga de página: -- The `DOMContentLoaded` event triggers on `document` when the DOM is ready. We can apply JavaScript to elements at this stage. - - Script such as `<script>...</script>` or `<script src="..."></script>` block DOMContentLoaded, the browser waits for them to execute. - - Images and other resources may also still continue loading. -- The `load` event on `window` triggers when the page and all resources are loaded. We rarely use it, because there's usually no need to wait for so long. -- The `beforeunload` event on `window` triggers when the user wants to leave the page. If we cancel the event, browser asks whether the user really wants to leave (e.g we have unsaved changes). -- The `unload` event on `window` triggers when the user is finally leaving, in the handler we can only do simple things that do not involve delays or asking a user. Because of that limitation, it's rarely used. We can send out a network request with `navigator.sendBeacon`. -- `document.readyState` is the current state of the document, changes can be tracked in the `readystatechange` event: - - `loading` -- the document is loading. - - `interactive` -- the document is parsed, happens at about the same time as `DOMContentLoaded`, but before it. - - `complete` -- the document and resources are loaded, happens at about the same time as `window.onload`, but before it. +- El evento `DOMContentLoaded` se activa en el `document` cuando el DOM está listo. Podemos aplicar JavaScript a elementos en esta etapa. + - Secuencias de comandos como `<script> ... </script>` o `<script src =" ... "> </script>` bloquean DOMContentLoaded, el navegador espera a que se ejecuten. + - Las imágenes y otros recursos también pueden seguir cargándose. +- El evento `load` en `window` se activa cuando se cargan la página y todos los recursos. Rara vez lo usamos, porque generalmente no hay necesidad de esperar tanto. +- El evento `beforeunload` en `window` se activa cuando el usuario quiere salir de la página. Si cancelamos el evento, el navegador pregunta si el usuario realmente quiere irse (por ejemplo, tenemos cambios sin guardar). +- El evento `unload` en `window` se dispara cuando el usuario finalmente se está yendo, en el controlador solo podemos hacer cosas simples que no impliquen demoras o preguntas al usuario. Debido a esa limitación, rara vez se usa. Podemos enviar una solicitud de red con `navigator.sendBeacon`. +- `document.readyState` es el estado actual del documento, los cambios se pueden rastrear con el evento `readystatechange`: + - `loading` -- el documento esta cargando. + - `interactive` -- el documento se analiza, ocurre aproximadamente casi al mismo tiempo que `DOMContentLoaded`, pero antes. + - `complete` -- el documento y los recursos se cargan, ocurre aproximadamente casi al mismo tiempo que `window.onload`, pero antes. diff --git a/2-ui/5-loading/01-onload-ondomcontentloaded/readystate.view/index.html b/2-ui/5-loading/01-onload-ondomcontentloaded/readystate.view/index.html index 27df70939..4a422edf6 100644 --- a/2-ui/5-loading/01-onload-ondomcontentloaded/readystate.view/index.html +++ b/2-ui/5-loading/01-onload-ondomcontentloaded/readystate.view/index.html @@ -4,7 +4,7 @@ <meta charset="utf-8"> </head> <body> - <!-- the sequence should be like this: + <!-- la secuencia debe ser así: [10] initial readyState:loading [20] readyState:interactive [21] DOMContentLoaded diff --git a/2-ui/5-loading/02-script-async-defer/article.md b/2-ui/5-loading/02-script-async-defer/article.md index f97c000d6..e80d97d1c 100644 --- a/2-ui/5-loading/02-script-async-defer/article.md +++ b/2-ui/5-loading/02-script-async-defer/article.md @@ -1,147 +1,147 @@ # Scripts: async, defer -In modern websites, scripts are often "heavier" than HTML: their download size is larger, and processing time is also longer. +En los sitios web modernos los scripts suelen ser más "pesados" que el HTML, el tamaño de la descarga es grande y el tiempo de procesamiento es mayor. -When the browser loads HTML and comes across a `<script>...</script>` tag, it can't continue building the DOM. It must execute the script right now. The same happens for external scripts `<script src="..."></script>`: the browser must wait for the script to download, execute the downloaded script, and only then can it process the rest of the page. +Cuando el navegador carga el HTML y se encuentra con una etiqueta `<script>...</script>`, no puede continuar construyendo el DOM. Debe ejecutar el script en el momento. Lo mismo sucede con los scripts externos `<script src="..."></script>`, el navegador tiene que esperar hasta que el script sea descargado, ejecutarlo y solo después procesa el resto de la página. -That leads to two important issues: +Esto nos lleva a dos importantes problemas: -1. Scripts can't see DOM elements below them, so they can't add handlers etc. -2. If there's a bulky script at the top of the page, it "blocks the page". Users can't see the page content till it downloads and runs: +1. Los scripts no pueden ver los elementos del DOM que se encuentran debajo de él por lo que no pueden agregar controladores de eventos, etc. +2. Si hay un script muy pesado en la parte superior de la página, este "bloquea la página". Los usuarios no pueden ver el contenido de la página hasta que sea descargado y ejecutado. ```html run height=100 -<p>...content before script...</p> +<p>...contenido previo al script...</p> <script src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script> -<!-- This isn't visible until the script loads --> -<p>...content after script...</p> +<!-- Esto no es visible hasta que el script sea cargado --> +<p>...contenido posterior al script...</p> ``` -There are some workarounds to that. For instance, we can put a script at the bottom of the page. Then it can see elements above it, and it doesn't block the page content from showing: +Hay algunas soluciones para eso. Por ejemplo podemos poner el script en la parte inferior de la página por lo que podrá ver los elementos sobre él y no bloqueará la visualización del contenido de la página. ```html <body> - ...all content is above the script... + ...todo el contenido está arriba del script... <script src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script> </body> ``` -But this solution is far from perfect. For example, the browser notices the script (and can start downloading it) only after it downloaded the full HTML document. For long HTML documents, that may be a noticeable delay. +Pero esta solución está lejos de ser perfecta. Por ejemplo el navegador solo se dará cuenta del script (y podrá empezar a descargarlo) después de descargar todo el documento HTML. Para documentos HTML extensos eso puede ser un retraso notable. -Such things are invisible for people using very fast connections, but many people in the world still have slow internet speeds and use a far-from-perfect mobile internet connection. +Este tipo de cosas son imperceptibles para las personas que usan conexiones muy rápidas, pero muchas personas en el mundo todavía tienen velocidades de internet lentas y utilizan una conexión de internet móvil que esta lejos de ser perfecta. -Luckily, there are two `<script>` attributes that solve the problem for us: `defer` and `async`. +Afortunadamente hay dos atributos de `<script>` que resuelven ese problema para nosotros: `defer` y `async`. ## defer -The `defer` attribute tells the browser not to wait for the script. Instead, the browser will continue to process the HTML, build DOM. The script loads "in the background", and then runs when the DOM is fully built. +El atributo `defer` indica al navegador que no espere por el script. En lugar de ello, debe seguir procesando el HTML, construir el DOM. El script carga "en segundo plano" y se ejecuta cuando el DOM esta completo. -Here's the same example as above, but with `defer`: +Aquí está el mismo ejemplo de arriba, pero con `defer`: ```html run height=100 -<p>...content before script...</p> +<p>...contenido previo script...</p> <script defer src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script> -<!-- visible immediately --> -<p>...content after script...</p> +<!-- Inmediatamete visible --> +<p>...contenido posterior al script...</p> ``` -In other words: +En otras palabras: -- Scripts with `defer` never block the page. -- Scripts with `defer` always execute when the DOM is ready (but before `DOMContentLoaded` event). +- Los scripts con `defer` nunca bloquean la página. +- Los scripts con `defer` siempre se ejecutan cuando el DOM esta listo (pero antes del evento `DOMContentLoaded`). -The following example demonstrates the second part: +Los siguientes ejemplos demuestran la segunda parte: ```html run height=100 -<p>...content before scripts...</p> +<p>...contenido previo a los scripts...</p> <script> - document.addEventListener('DOMContentLoaded', () => alert("DOM ready after defer!")); + document.addEventListener('DOMContentLoaded', () => alert("¡DOM listo después del defer!")); </script> <script defer src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script> -<p>...content after scripts...</p> +<p>...contenido posterior a los scripts...</p> ``` -1. The page content shows up immediately. -2. `DOMContentLoaded` event handler waits for the deferred script. It only triggers when the script is downloaded and executed. +1. El contenido de la página se muestra inmediatamente. +2. `DOMContentLoaded` espera por el script diferido. Solo se dispara cuando el script es descargado y ejecutado. -**Deferred scripts keep their relative order, just like regular scripts.** +**Los scripts diferidos mantienen su orden relativo, tal cual los scripts regulares. -Let's say, we have two deferred scripts: the `long.js` and then `small.js`: +Digamos que tenemos dos scripts diferidos, `long.js` (largo) y luego `small.js` (corto): ```html <script defer src="https://javascript.info/article/script-async-defer/long.js"></script> <script defer src="https://javascript.info/article/script-async-defer/small.js"></script> ``` -Browsers scan the page for scripts and download them in parallel, to improve performance. So in the example above both scripts download in parallel. The `small.js` probably finishes first. +Los navegadores analizan la página en busca de scripts y los descarga en paralelo para mejorar el rendimiento. Entonces en el ejemplo superior ambos scripts se descargan en paralelo, el `small.js` probablemente lo haga primero. -...But the `defer` attribute, besides telling the browser "not to block", ensures that the relative order is kept. So even though `small.js` loads first, it still waits and runs after `long.js` executes. +...Pero el atributo `defer`, además de decirle al navegador "no bloquear", asegura que el orden relativo se mantenga. Entonces incluso si `small.js` se carga primero, aún espera y se ejecuta después de `long.js`. -That may be important for cases when we need to load a JavaScript library and then a script that depends on it. +Por ello es importante para casos donde necesitamos cargar un librería JavaScript y entonces un script que depende de ella. -```smart header="The `defer` attribute is only for external scripts" -The `defer` attribute is ignored if the `<script>` tag has no `src`. +```smart header="El atributo `defer` es solo para scripts externos" +El atributo `defer` es ignorado si el `<script>` no tiene el atributo `src`. ``` ## async -The `async` attribute is somewhat like `defer`. It also makes the script non-blocking. But it has important differences in the behavior. +El atributo `async` es de alguna manera como `defer`. También hace el script no bloqueante. Pero tiene importantes diferencias de comportamiento. -The `async` attribute means that a script is completely independent: +El atributo `async` significa que el script es completamente independiente: -- The browser doesn't block on `async` scripts (like `defer`). -- Other scripts don't wait for `async` scripts, and `async` scripts don't wait for them. -- `DOMContentLoaded` and async scripts don't wait for each other: - - `DOMContentLoaded` may happen both before an async script (if an async script finishes loading after the page is complete) - - ...or after an async script (if an async script is short or was in HTTP-cache) +- El navegador no se bloquea con scripts `async` (como `defer`). +- Otros scripts no esperan por scripts `async`, y scripts `async` no espera por ellos. +- `DOMContentLoaded` y los scripts asincrónicos no se esperan entre sí: + - `DOMContentLoaded` puede suceder antes que un script asincrónico (si un script asincrónico termina de cargar una vez la página está completa) + - ...o después de un script asincrónico (si tal script asincrónico es pequeño o está en cache) -In other words, `async` scripts load in the background and run when ready. The DOM and other scripts don't wait for them, and they don't wait for anything. A fully independent script that runs when loaded. As simple, as it can get, right? +En otras palabras, los scripts `async` cargan en segundo plano y se ejecutan cuando están listos. El DOM y otros scripts no esperan por ellos, y ellos no esperan por nada. Un script totalmente independiente que se ejecuta en cuanto se ha cargado. Tan simple como es posible, ¿cierto? -Here's an example similar to what we've seen with `defer`: two scripts `long.js` and `small.js`, but now with `async` instead of `defer`. +Aquí hay un ejemplo similar al que vimos con `defer`: Dos scripts `long.js` y `small.js`, pero ahora con `async` en lugar de `defer`. -They don't wait for each other. Whatever loads first (probably `small.js`) -- runs first: +Los unos no esperan por lo otros. El que cargue primero (probablemente `small.js`), se ejecuta primero. ```html run height=100 -<p>...content before scripts...</p> +<p>...contenido previo a los scripts...</p> <script> - document.addEventListener('DOMContentLoaded', () => alert("DOM ready!")); + document.addEventListener('DOMContentLoaded', () => alert("¡DOM listo!")); </script> <script async src="https://javascript.info/article/script-async-defer/long.js"></script> <script async src="https://javascript.info/article/script-async-defer/small.js"></script> -<p>...content after scripts...</p> +<p>...contenido posterior a los scripts...</p> ``` -- The page content shows up immediately: `async` doesn't block it. -- `DOMContentLoaded` may happen both before and after `async`, no guarantees here. -- A smaller script `small.js` goes second, but probably loads before `long.js`, so `small.js` runs first. Although, it might be that `long.js` loads first, if cached, then it runs first. In other words, async scripts run in the "load-first" order. +- El contenido de la página se muestra inmediatamente: `async` no lo bloquea. +- El evento `DOMContentLoaded` puede suceder antes o después de `async`, no hay garantías aquí. +- Un script más pequeño `small.js` que esté segundo probablemente cargue antes que uno más largo `long.js`, entonces se ejecutará primero. Aunque podría ser que `long.js` cargue primero si está en caché y ejecute primero. A eso lo llamamos "load-first order", se ejecuta primero el que cargue antes . -Async scripts are great when we integrate an independent third-party script into the page: counters, ads and so on, as they don't depend on our scripts, and our scripts shouldn't wait for them: +Los scripts asincrónicos son excelentes cuando incluimos scripts de terceros (contadores, anuncios, etc) en la página debido a que ellos no dependen de nuestros scripts y nuestros scripts no deberían esperar por ellos. ```html <!-- Google Analytics is usually added like this --> <script async src="https://google-analytics.com/analytics.js"></script> ``` -```smart header="The `async` attribute is only for external scripts" -Just like `defer`, the `async` attribute is ignored if the `<script>` tag has no `src`. +```smart header="El atributo `async` es solo para scripts externos" +Tal como `defer`, el atributo `async` se ignora si la etiqueta `<script>` no tiene `src`. ``` -## Dynamic scripts +## Scripts dinámicos -There's one more important way of adding a script to the page. +Hay otra manera importante de agregar un script a la página. -We can create a script and append it to the document dynamically using JavaScript: +Podemos crear un script y agregarlo dinámicamente al documento usando JavaScript: ```js run let script = document.createElement('script'); @@ -149,19 +149,19 @@ script.src = "/article/script-async-defer/long.js"; document.body.append(script); // (*) ``` -The script starts loading as soon as it's appended to the document `(*)`. +El script comienza a cargar tan pronto como es agregado al documento `(*)`. -**Dynamic scripts behave as "async" by default.** +**Los scripts dinámicos se comportan como `async` por defecto** -That is: -- They don't wait for anything, nothing waits for them. -- The script that loads first -- runs first ("load-first" order). +Esto es: +- Ellos no esperan a nadie y nadie espera por ellos. +- El script que carga primero se ejecuta primero (`load-first order`) -This can be changed if we explicitly set `script.async=false`. Then scripts will be executed in the document order, just like `defer`. +Esto puede ser cambiado si explícitamente establecemos `script.async=false`. Así los scripts serán ejecutados en el orden del documento, tal como en `defer`. -In this example, `loadScript(src)` function adds a script and also sets `async` to `false`. +En este ejemplo, la función `loadScript(src)` añade un script y también establece `async` a `false`. -So `long.js` always runs first (as it's added first): +Entonces `long.js` siempre ejecuta primero (por haber sido agregado primero): ```js run function loadScript(src) { @@ -171,35 +171,35 @@ function loadScript(src) { document.body.append(script); } -// long.js runs first because of async=false +// long.js se ejecuta primero a causa del async=false loadScript("/article/script-async-defer/long.js"); loadScript("/article/script-async-defer/small.js"); ``` -Without `script.async=false`, scripts would execute in default, load-first order (the `small.js` probably first). +Sin `script.async=false`, los scripts es ejecutarían de forma predeterminada, en el orden de carga primero (probablemente `small.js` primero). -Again, as with the `defer`, the order matters if we'd like to load a library and then another script that depends on it. +De nuevo, como con `defer`, el orden importa si queremos cargar una librería y luego otro script que depende de ella. -## Summary +## Resumen -Both `async` and `defer` have one common thing: downloading of such scripts doesn't block page rendering. So the user can read page content and get acquainted with the page immediately. +Ambos, `async` y `defer`, tienen algo en común: la descarga de tales scripts no bloquean el renderizado de la página. Por lo cual el usuario puede leer el contenido de la página y familiarizarse con la página inmediatamente. -But there are also essential differences between them: +Pero hay algunas diferencias esenciales entre ellos: -| | Order | `DOMContentLoaded` | +| | Orden | `DOMContentLoaded` | |---------|---------|---------| -| `async` | *Load-first order*. Their document order doesn't matter -- which loads first runs first | Irrelevant. May load and execute while the document has not yet been fully downloaded. That happens if scripts are small or cached, and the document is long enough. | -| `defer` | *Document order* (as they go in the document). | Execute after the document is loaded and parsed (they wait if needed), right before `DOMContentLoaded`. | +| `async` | *Load-first order*. El orden del documento no importa. El que carga primero ejecuta primero | Irrelevante. Puede cargar y ejecutarse mientras el documento no ha sido completamente descargado, eso puede pasar si el script es pequeño o está en cache y el documento es suficientemente extenso. | +| `defer` | *Document order* (como van en el documento). | Ejecutan después de que el documento es cargado y analizado (espera si es necesario), justo antes de `DOMContentLoaded`. | -In practice, `defer` is used for scripts that need the whole DOM and/or their relative execution order is important. +En la práctica, `defer` es usado para scripts que necesitan todo el DOM y/o si su orden de ejecución relativa es importante. -And `async` is used for independent scripts, like counters or ads. And their relative execution order does not matter. +Y `async` es usado para scripts independientes, como contadores y anuncios donde el orden de ejecución no importa. -```warn header="Page without scripts should be usable" -Please note: if you're using `defer` or `async`, then user will see the page *before* the script loads. +```warn header="La página sin scripts debe ser utilizable" +Ten en cuenta: si usas `defer` o `async`, el usuario verá la página *antes* de que el script sea cargado. -In such case, some graphical components are probably not initialized yet. +En tal caso algunos componentes gráficos probablemente no estén listos. -Don't forget to put "loading" indication and disable buttons that aren't functional yet. Let the user clearly see what he can do on the page, and what's still getting ready. +No olvides poner alguna señal de "cargando" y deshabilitar los botones que aún no estén funcionando. Esto permite al usuario ver claramente qué puede hacer en la página y qué está listo y qué no. ``` diff --git a/2-ui/5-loading/03-onload-onerror/1-load-img-callback/solution.md b/2-ui/5-loading/03-onload-onerror/1-load-img-callback/solution.md index 4b8fe50b4..0942ec159 100644 --- a/2-ui/5-loading/03-onload-onerror/1-load-img-callback/solution.md +++ b/2-ui/5-loading/03-onload-onerror/1-load-img-callback/solution.md @@ -1,6 +1,6 @@ -The algorithm: -1. Make `img` for every source. -2. Add `onload/onerror` for every image. -3. Increase the counter when either `onload` or `onerror` triggers. -4. When the counter value equals to the sources count -- we're done: `callback()`. +El algoritmo: +1. Crear una `img` para cada fuente. +2. Agregar los eventos `onload/onerror` para cada imágen. +3. Incrementar el contador cuando el evento `onload` o el evento `onerror` se dispare. +4. Cuando el valor del contador es igual a la cantidad de fuentes, hemos terminado: `callback()`. diff --git a/2-ui/5-loading/03-onload-onerror/1-load-img-callback/solution.view/index.html b/2-ui/5-loading/03-onload-onerror/1-load-img-callback/solution.view/index.html index 50b9e741d..713454f8f 100644 --- a/2-ui/5-loading/03-onload-onerror/1-load-img-callback/solution.view/index.html +++ b/2-ui/5-loading/03-onload-onerror/1-load-img-callback/solution.view/index.html @@ -29,13 +29,13 @@ "https://en.js.cx/images-load/3.jpg" ]; - // add random characters to prevent browser caching + // agrega caracteres aleatorios para evitar el "caching" del navegador for (let i = 0; i < sources.length; i++) { sources[i] += '?' + Math.random(); } - // for each image, - // let's create another img with the same src and check that we have its width + // para cada imagen, + // creemos otra img con el mismo src y verificamos que tenemos su ancho "width" function testLoaded() { let widthSum = 0; for (let i = 0; i < sources.length; i++) { @@ -46,7 +46,7 @@ alert(widthSum); } - // should output 300 + // la salida debe ser 300 preloadImages(sources, testLoaded); </script> diff --git a/2-ui/5-loading/03-onload-onerror/1-load-img-callback/source.view/index.html b/2-ui/5-loading/03-onload-onerror/1-load-img-callback/source.view/index.html index 799350232..06573bb9d 100644 --- a/2-ui/5-loading/03-onload-onerror/1-load-img-callback/source.view/index.html +++ b/2-ui/5-loading/03-onload-onerror/1-load-img-callback/source.view/index.html @@ -18,13 +18,13 @@ "https://en.js.cx/images-load/3.jpg" ]; - // add random characters to prevent browser caching + // agrega caracteres aleatorios para evitar el "caching" del navegador for (let i = 0; i < sources.length; i++) { sources[i] += '?' + Math.random(); } - // for each image, - // let's create another img with the same src and check that we have its width immediately + // para cada imagen, + // creemos otra img con el mismo src y verificamos que tenemos su ancho "width" function testLoaded() { let widthSum = 0; for (let i = 0; i < sources.length; i++) { @@ -35,7 +35,7 @@ alert(widthSum); } - // every image is 100x100, the total width should be 300 + // cada imagen es de 100x100, el ancho total debe ser 300 preloadImages(sources, testLoaded); </script> diff --git a/2-ui/5-loading/03-onload-onerror/1-load-img-callback/task.md b/2-ui/5-loading/03-onload-onerror/1-load-img-callback/task.md index b7583550b..b744fa521 100644 --- a/2-ui/5-loading/03-onload-onerror/1-load-img-callback/task.md +++ b/2-ui/5-loading/03-onload-onerror/1-load-img-callback/task.md @@ -2,35 +2,35 @@ importance: 4 --- -# Load images with a callback +# Cargando imágenes con una un función de retorno (`callback`) -Normally, images are loaded when they are created. So when we add `<img>` to the page, the user does not see the picture immediately. The browser needs to load it first. +Normalmente, las imágenes son cargadas cuando son creadas. Entonces, cuando nosotros agregamos `<img>` a la página el usuario no ve la imágen inmediatamente. El navegador necesita cargarlo primero. -To show an image immediately, we can create it "in advance", like this: +Para mostrar una imágen inmediatamente, podemos crearlo "en avance", como esto: ```js let img = document.createElement('img'); img.src = 'my.jpg'; ``` -The browser starts loading the image and remembers it in the cache. Later, when the same image appears in the document (no matter how), it shows up immediately. +El navegador comienza a cargar la imágen y lo guarda en el cache. Después cuando la misma imágen aparece en el documento (no importa cómo) la muestra inmediatamente. -**Create a function `preloadImages(sources, callback)` that loads all images from the array `sources` and, when ready, runs `callback`.** +**Crear una función `preloadImages(sources, callback)` que cargue todas las imágenes desde una lista de fuentes (`sources`) y, cuando estén listas, ejecutar la función de retorno (`callback`).** -For instance, this will show an `alert` after the images are loaded: +Por ejemplo: esto puede mostrar una alerta (`alert`) después de que la imágen sea cargada: ```js function loaded() { - alert("Images loaded") + alert("Imágenes cargadas") } preloadImages(["1.jpg", "2.jpg", "3.jpg"], loaded); ``` -In case of an error, the function should still assume the picture "loaded". +En caso de un error, la función debería seguir asumiendo que la imágen ha sido "cargada". -In other words, the `callback` is executed when all images are either loaded or errored out. +En otras palabras, la función de retorno (`callback`) es ejecutada cuando todas las imágenes han sido cargadas o no. -The function is useful, for instance, when we plan to show a gallery with many scrollable images, and want to be sure that all images are loaded. +La función es útil, por ejemplo, cuando planeamos mostrar una galería con muchas imágenes desplazables y estar seguros de que todas las imágenes están cargadas. -In the source document you can find links to test images, and also the code to check whether they are loaded or not. It should output `300`. +En el documento fuente puedes encontrar enlaces para probar imágenes y también el código para verificar si han sido cargadas o no. Debería devolver `300`. diff --git a/2-ui/5-loading/03-onload-onerror/article.md b/2-ui/5-loading/03-onload-onerror/article.md index 590e54ab4..22cccc082 100644 --- a/2-ui/5-loading/03-onload-onerror/article.md +++ b/2-ui/5-loading/03-onload-onerror/article.md @@ -1,187 +1,187 @@ -# Resource loading: onload and onerror +# Carga de recursos: onload y onerror -The browser allows us to track the loading of external resources -- scripts, iframes, pictures and so on. +El navegador nos permite hacer seguimiento de la carga de recursos externos: scripts, iframes, imágenes y más. -There are two events for it: +Hay dos eventos para eso: -- `onload` -- successful load, -- `onerror` -- an error occurred. +- `onload` -- cuando cargó exitosamente, +- `onerror` -- cuando un error ha ocurrido. -## Loading a script +## Cargando un script -Let's say we need to load a third-party script and call a function that resides there. +Digamos que tenemos que cargar un script de terceros y llamar una función que se encuentra dentro. -We can load it dynamically, like this: +Podemos cargarlo dinámicamente de esta manera: ```js -let script = document.createElement('script'); +let script = document.createElement("script"); script.src = "my.js"; document.head.append(script); ``` -...But how to run the function that is declared inside that script? We need to wait until the script loads, and only then we can call it. +...pero ¿cómo podemos ejecutar la función que esta dentro del script? Necesitamos esperar hasta que el script haya cargado, y solo después podemos llamarlo. ```smart -For our own scripts we could use [JavaScript modules](info:modules) here, but they are not widely adopted by third-party libraries. +Para nuestros scripts podemos usar [JavaScript modules](info:modules) aquí, pero no está adoptado ampliamente por bibliotecas de terceros. ``` ### script.onload -The main helper is the `load` event. It triggers after the script was loaded and executed. +El evento `load` se dispara después de que script sea cargado y ejecutado. -For instance: +Por ejemplo: ```js run untrusted let script = document.createElement('script'); -// can load any script, from any domain +// podemos cargar cualquier script desde cualquier dominio script.src = "https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.3.0/lodash.js" document.head.append(script); *!* script.onload = function() { - // the script creates a variable "_" - alert( _.VERSION ); // shows library version + // el script crea una variable "_" + alert( _.VERSION ); // muestra la versión de la librería }; */!* ``` -So in `onload` we can use script variables, run functions etc. +Entonces en `onload` podemos usar variables, ejecutar funciones, etc. -...And what if the loading failed? For instance, there's no such script (error 404) or the server is down (unavailable). +...¿y si la carga falla? Por ejemplo: no hay tal script (error 404) en el servidor o el servidor está caído (no disponible). ### script.onerror -Errors that occur during the loading of the script can be tracked in an `error` event. +Los errores que ocurren durante la carga de un script pueden ser rastreados en el evento `error`. -For instance, let's request a script that doesn't exist: +Por ejemplo, hagamos una petición a un script que no existe: ```js run let script = document.createElement('script'); -script.src = "https://example.com/404.js"; // no such script +script.src = "https://example.com/404.js"; // no hay tal script document.head.append(script); *!* script.onerror = function() { - alert("Error loading " + this.src); // Error loading https://example.com/404.js + alert("Error al cargar " + this.src); // Error al cargar https://example.com/404.js }; */!* ``` -Please note that we can't get HTTP error details here. We don't know if it was an error 404 or 500 or something else. Just that the loading failed. +Por favor nota que como no podemos obtener detalles del error HTTP aquí, no podemos saber if fue un error 404 o algo diferente. Solo el error de carga. ```warn -Events `onload`/`onerror` track only the loading itself. +Los eventos `onload/onerror` rastrean solamente la carga de ellos mismos. -Errors that may occur during script processing and execution are out of scope for these events. That is: if a script loaded successfully, then `onload` triggers, even if it has programming errors in it. To track script errors, one can use `window.onerror` global handler. +Los errores que pueden ocurrir durante el procesamiento y ejecución están fuera del alcance para esos eventos. Eso es: si un script es cargado de manera exitosa, incluso si tiene errores de programación adentro, el evento `onload` se dispara. Para rastrear los errores del script un puede usar el manejador global `window.onerror`; ``` -## Other resources +## Otros recursos -The `load` and `error` events also work for other resources, basically for any resource that has an external `src`. +Los eventos `load` y `error` también funcionan para otros recursos, básicamente para cualquiera que tenga una `src` externa. -For example: +Por ejemplo: ```js run -let img = document.createElement('img'); +let img = document.createElement("img"); img.src = "https://js.cx/clipart/train.gif"; // (*) -img.onload = function() { +img.onload = function () { alert(`Image loaded, size ${img.width}x${img.height}`); }; -img.onerror = function() { +img.onerror = function () { alert("Error occurred while loading image"); }; ``` -There are some notes though: +Sin embargo, hay algunas notas: -- Most resources start loading when they are added to the document. But `<img>` is an exception. It starts loading when it gets a src `(*)`. -- For `<iframe>`, the `iframe.onload` event triggers when the iframe loading finished, both for successful load and in case of an error. +- La mayoría de recursos empiezan a cargarse cuando son agregados al documento. Pero `<img>` es una excepción, comienza la carga cuando obtiene una fuente ".src" `(*)`. +- Para `<iframe>`, el evento `iframe.onload` se dispara cuando el iframe ha terminado de cargar, tanto para una carga exitosa como en caso de un error. -That's for historical reasons. +Esto es por razones históricas. -## Crossorigin policy +## Política de origen cruzado -There's a rule: scripts from one site can't access contents of the other site. So, e.g. a script at `https://facebook.com` can't read the user's mailbox at `https://gmail.com`. +Hay una regla: los scripts de un sitio no pueden acceder al contenido de otro sitio. Por ejemplo: un script de `https://facebook.com` no puede leer la bandeja de correos del usuario en `https://gmail.com`. -Or, to be more precise, one origin (domain/port/protocol triplet) can't access the content from another one. So even if we have a subdomain, or just another port, these are different origins with no access to each other. +O para ser más precisos, un origen (el trío dominio/puerto/protocolo) no puede acceder al contenido de otro. Entonces, incluso si tenemos un sub-dominio o solo un puerto distinto, son considerados orígenes diferentes sin acceso al otro. -This rule also affects resources from other domains. +Esta regla también afecta a recursos de otros dominios. -If we're using a script from another domain, and there's an error in it, we can't get error details. +Si usamos un script de otro dominio y tiene un error, no podemos obtener detalles del error. -For example, let's take a script `error.js` that consists of a single (bad) function call: +Por ejemplo, tomemos un script `error.js` que consta de una sola llamada a una función (con errores). ```js // 📁 error.js noSuchFunction(); ``` -Now load it from the same site where it's located: +Ahora cargalo desde el mismo sitio donde esta alojado: ```html run height=0 <script> -window.onerror = function(message, url, line, col, errorObj) { - alert(`${message}\n${url}, ${line}:${col}`); -}; + window.onerror = function (message, url, line, col, errorObj) { + alert(`${message}\n${url}, ${line}:${col}`); + }; </script> <script src="/article/onload-onerror/crossorigin/error.js"></script> ``` -We can see a good error report, like this: +Podemos ver un buen reporte de error, como este: ``` Uncaught ReferenceError: noSuchFunction is not defined https://javascript.info/article/onload-onerror/crossorigin/error.js, 1:1 ``` -Now let's load the same script from another domain: +Ahora carguemos el mismo script desde otro dominio: ```html run height=0 <script> -window.onerror = function(message, url, line, col, errorObj) { - alert(`${message}\n${url}, ${line}:${col}`); -}; + window.onerror = function (message, url, line, col, errorObj) { + alert(`${message}\n${url}, ${line}:${col}`); + }; </script> <script src="https://cors.javascript.info/article/onload-onerror/crossorigin/error.js"></script> ``` -The report is different, like this: +El reporte es diferente, como este: ``` Script error. , 0:0 ``` -Details may vary depending on the browser, but the idea is the same: any information about the internals of a script, including error stack traces, is hidden. Exactly because it's from another domain. +Los detalles pueden variar dependiendo del navegador, pero la idea es la misma: cualquier información sobre las partes internas de un script, incluyendo el rastreo de la pila de errores, se oculta. Exactamente porque es de otro dominio. -Why do we need error details? +¿Por qué necesitamos detalles de error? -There are many services (and we can build our own) that listen for global errors using `window.onerror`, save errors and provide an interface to access and analyze them. That's great, as we can see real errors, triggered by our users. But if a script comes from another origin, then there's not much information about errors in it, as we've just seen. +Hay muchos servicios (y podemos construir uno nuestro) que escuchan los errores globales usando `window.onerror`, guardan los errores y proveen una interfaz para acceder a ellos y analizarlos. Eso es grandioso ya que podemos ver los errores originales ocasionados por nuestros usuarios. Pero si el script viene desde otro origen no hay mucha información sobre los errores como acabamos de ver. -Similar cross-origin policy (CORS) is enforced for other types of resources as well. +También se aplican políticas similares de origen cruzado (CORS) a otros tipos de recursos. -**To allow cross-origin access, the `<script>` tag needs to have the `crossorigin` attribute, plus the remote server must provide special headers.** +**Para permitir el acceso de origen cruzado, la etiqueta `<script>` necesita tener el atributo `crossorigin`, además el servidor remoto debe proporcionar cabeceras especiales.** -There are three levels of cross-origin access: +Hay 3 niveles de acceso de origen cruzado: -1. **No `crossorigin` attribute** -- access prohibited. -2. **`crossorigin="anonymous"`** -- access allowed if the server responds with the header `Access-Control-Allow-Origin` with `*` or our origin. Browser does not send authorization information and cookies to remote server. -3. **`crossorigin="use-credentials"`** -- access allowed if the server sends back the header `Access-Control-Allow-Origin` with our origin and `Access-Control-Allow-Credentials: true`. Browser sends authorization information and cookies to remote server. +1. **Sin el atributo `crossorigin`** -- acceso prohibido. +2. **`crossorigin="anonymous"`** -- acceso permitido si el servidor responde con la cabecera `Access-Control-Allow-Origin` con `*` o nuestro origen. El navegador no envía la información de la autorización y cookies al servidor remoto. +3. **`crossorigin="use-credentials"`** -- acceso permitido si el servidor envia de vuelta la cabecera `Access-Control-Allow-Origin` con nuestro origen y `Access-Control-Allow-Credentials: true`. El navegador envía la información de la autorización y las cookies al servidor remoto. ```smart -You can read more about cross-origin access in the chapter <info:fetch-crossorigin>. It describes the `fetch` method for network requests, but the policy is exactly the same. +Puedes leer más sobre accesos de origen cruzado en el capítulo <info:fetch-crossorigin>. Este describe el método `fetch` para requerimientos de red, pero la política es exactamente la misma. -Such thing as "cookies" is out of our current scope, but you can read about them in the chapter <info:cookie>. +Cosas como las "cookies" están fuera de nuestro alcance, pero podemos leer sobre ellas en <info:cookie>. ``` -In our case, we didn't have any crossorigin attribute. So the cross-origin access was prohibited. Let's add it. +En nuestro caso no teníamos ningún atributo de origen cruzado (`cross-origin`). Por lo que se prohibió el acceso de origen cruzado. Vamos a agregarlo. -We can choose between `"anonymous"` (no cookies sent, one server-side header needed) and `"use-credentials"` (sends cookies too, two server-side headers needed). +Podemos elegir entre `"anonymous"` (no se envían las cookies, una sola cabecera esa necesaria en el lado del servidor) y `"use-credentials"` (envía las cookies, dos cabeceras son necesarias en el lado del servidor). -If we don't care about cookies, then `"anonymous"` is the way to go: +Si no nos importan las `cookies`, entonces `"anonymous"` es el camino a seguir: ```html run height=0 <script> @@ -192,15 +192,15 @@ window.onerror = function(message, url, line, col, errorObj) { <script *!*crossorigin="anonymous"*/!* src="https://cors.javascript.info/article/onload-onerror/crossorigin/error.js"></script> ``` -Now, assuming that the server provides an `Access-Control-Allow-Origin` header, everything's fine. We have the full error report. +Ahora, asumiendo que el servidor brinda una cabecera `Access-Control-Allow-Origin`, todo está bien. Podemos tener el reporte completo del error. -## Summary +## Resumen -Images `<img>`, external styles, scripts and other resources provide `load` and `error` events to track their loading: +Las imágenes `<img>`, estilos externos, scripts y otros recursos proveen los eventos `load` y `error` para rastrear sus cargas: -- `load` triggers on a successful load, -- `error` triggers on a failed load. +- `load` se ejecuta cuando la carga ha sido exitosa, +- `error` se ejecuta cuando una carga ha fallado. -The only exception is `<iframe>`: for historical reasons it always triggers `load`, for any load completion, even if the page is not found. +La única excepción es el `<iframe>`: por razones históricas siempre dispara el evento `load`, incluso si no encontró la página. -The `readystatechange` event also works for resources, but is rarely used, because `load/error` events are simpler. +El evento `readystatechange` también funciona para recursos, pero es muy poco usado debido a que los eventos `load/error` son mas simples. diff --git a/2-ui/5-loading/index.md b/2-ui/5-loading/index.md index 9e01193d3..93a646149 100644 --- a/2-ui/5-loading/index.md +++ b/2-ui/5-loading/index.md @@ -1,2 +1,2 @@ -# Document and resource loading +# El documento y carga de recursos diff --git a/2-ui/99-ui-misc/01-mutation-observer/article.md b/2-ui/99-ui-misc/01-mutation-observer/article.md index ecc89f6e6..1aab8f204 100644 --- a/2-ui/99-ui-misc/01-mutation-observer/article.md +++ b/2-ui/99-ui-misc/01-mutation-observer/article.md @@ -1,82 +1,82 @@ # Mutation observer -`MutationObserver` is a built-in object that observes a DOM element and fires a callback when it detects a change. +`MutationObserver` es un objeto nativo que observa un elemento DOM y dispara un callback cuando hay cambios en él. -We'll first take a look at the syntax, and then explore a real-world use case, to see where such thing may be useful. +Primero veremos su sintaxis, luego exploraremos un caso de la vida real para ver dónde puede ser útil. -## Syntax +## Sintaxis -`MutationObserver` is easy to use. +`MutationObserver` es fácil de usar. -First, we create an observer with a callback-function: +Primero creamos un observador con una función callback: ```js let observer = new MutationObserver(callback); ``` -And then attach it to a DOM node: +Y luego lo vinculamos a un nodo DOM: ```js observer.observe(node, config); ``` -`config` is an object with boolean options "what kind of changes to react on": -- `childList` -- changes in the direct children of `node`, -- `subtree` -- in all descendants of `node`, -- `attributes` -- attributes of `node`, -- `attributeFilter` -- an array of attribute names, to observe only selected ones. -- `characterData` -- whether to observe `node.data` (text content), +`config` es un objeto con opciones booleanas "a qué clase de cambios reaccionar": +- `childList` -- cambios en los hijos directos de `node`, +- `subtree` -- en todos los descendientes de `node`, +- `attributes` -- atributos de `node`, +- `attributeFilter` -- un array de nombres de atributos, para observar solamente a los seleccionados, +- `characterData` -- establece si debe observar cambios de texto en `node.data` o no, -Few other options: -- `attributeOldValue` -- if `true`, pass both the old and the new value of attribute to callback (see below), otherwise only the new one (needs `attributes` option), -- `characterDataOldValue` -- if `true`, pass both the old and the new value of `node.data` to callback (see below), otherwise only the new one (needs `characterData` option). +Algunas otras opciones: +- `attributeOldValue` -- si es `true`, tanto el valor viejo como el nuevo del atributo son pasados al callback (ver abajo), de otro modo pasa solamente el nuevo (necesita la opción `attributes`), +- `characterDataOldValue` -- si es `true`, tanto el valor viejo como el nuevo de `node.data` son pasados al callback (ver abajo), de otro modo pasa solamente el nuevo (necesita la opción `characterData`). -Then after any changes, the `callback` is executed: changes are passed in the first argument as a list of [MutationRecord](https://dom.spec.whatwg.org/#mutationrecord) objects, and the observer itself as the second argument. +Entonces, después de cualquier cambio, el `callback` es ejecutado: los cambios son pasados en el primer argumento como una lista objetos [MutationRecord](https://dom.spec.whatwg.org/#mutationrecord), y el observador en sí mismo como segundo argumento. -[MutationRecord](https://dom.spec.whatwg.org/#mutationrecord) objects have properties: +Los objetos [MutationRecord](https://dom.spec.whatwg.org/#mutationrecord) tienen como propiedades: -- `type` -- mutation type, one of - - `"attributes"`: attribute modified - - `"characterData"`: data modified, used for text nodes, - - `"childList"`: child elements added/removed, -- `target` -- where the change occurred: an element for `"attributes"`, or text node for `"characterData"`, or an element for a `"childList"` mutation, -- `addedNodes/removedNodes` -- nodes that were added/removed, -- `previousSibling/nextSibling` -- the previous and next sibling to added/removed nodes, -- `attributeName/attributeNamespace` -- the name/namespace (for XML) of the changed attribute, -- `oldValue` -- the previous value, only for attribute or text changes, if the corresponding option is set `attributeOldValue`/`characterDataOldValue`. +- `type` -- tipo de mutación, uno de: + - `"attributes"`: atributo modificado, + - `"characterData"`: dato modificado, usado para nodos de texto, + - `"childList"`: elementos hijos agregados o quitados, +- `target` -- dónde ocurrió el cambio: un elemento para `"attributes"`, o un nodo de texto para `"characterData"`, o un elemento para una mutación de `"childList"`, +- `addedNodes/removedNodes` -- nodos que fueron agregados o quitados, +- `previousSibling/nextSibling` -- los nodos "hermanos", previos y siguientes a los nodos agregados y quitados, +- `attributeName/attributeNamespace` -- el nombre o namespace (para XML) del atributo cambiado, +- `oldValue` -- el valor previo, solamente cambios de atributo o cambios de texto si se establece la opción correspondiente `attributeOldValue`/`characterDataOldValue`. -For example, here's a `<div>` with a `contentEditable` attribute. That attribute allows us to focus on it and edit. +Por ejemplo, aquí hay un `<div>` con un atributo `contentEditable`. Ese atributo nos permite poner el foco en él y editarlo. ```html run <div contentEditable id="elem">Click and <b>edit</b>, please</div> <script> let observer = new MutationObserver(mutationRecords => { - console.log(mutationRecords); // console.log(the changes) + console.log(mutationRecords); // console.log(los cambios) }); -// observe everything except attributes +// observa todo exceptuando atributos observer.observe(elem, { - childList: true, // observe direct children - subtree: true, // and lower descendants too - characterDataOldValue: true // pass old data to callback + childList: true, // observa hijos directos + subtree: true, // y descendientes inferiores también + characterDataOldValue: true // pasa el dato viejo al callback }); </script> ``` -If we run this code in the browser, then focus on the given `<div>` and change the text inside `<b>edit</b>`, `console.log` will show one mutation: +Si ejecutamos este código en el navegador, el foco en el `<div>` dado y el cambio en texto dentro de `<b>edit</b>`, `console.log` mostrará una mutación: ```js mutationRecords = [{ type: "characterData", oldValue: "edit", target: <text node>, - // other properties empty + // otras propiedades vacías }]; ``` -If we make more complex editing operations, e.g. remove the `<b>edit</b>`, the mutation event may contain multiple mutation records: +Si hacemos operaciones de edición más complejas, como eliminar el `<b>edit</b>`, el evento de mutación puede contener múltiples registros de mutación: ```js mutationRecords = [{ @@ -85,49 +85,58 @@ mutationRecords = [{ removedNodes: [<b>], nextSibling: <text node>, previousSibling: <text node> - // other properties empty + // otras propiedades vacías }, { type: "characterData" target: <text node> - // ...mutation details depend on how the browser handles such removal - // it may coalesce two adjacent text nodes "edit " and ", please" into one node - // or it may leave them separate text nodes + // ...detalles de mutación dependen de cómo el navegador maneja tal eliminación + // puede unir dos nodos de texto adyacentes "edit " y ", please" en un nodo + // o puede dejarlos como nodos de texto separados }]; ``` -So, `MutationObserver` allows to react on any changes within DOM subtree. +Así, `MutationObserver` permite reaccionar a cualquier cambio dentro del subárbol DOM. -## Usage for integration +## Uso para integración -When such thing may be useful? +¿Cuándo puede ser práctico esto? -Imagine the situation when you need to add a third-party script that contains useful functionality, but also does something unwanted, e.g. shows ads `<div class="ads">Unwanted ads</div>`. +Imagina la situación cuando necesitas añadir un script de terceros que contiene funcionalidad útil, pero también hace algo no deseado, por ejemplo añadir publicidad `<div class="ads">Unwanted ads</div>`. -Naturally, the third-party script provides no mechanisms to remove it. +Naturalmente el script de terceras partes no proporciona mecanismos para removerlo. -Using `MutationObserver`, we can detect when the unwanted element appears in our DOM and remove it. +Usando `MutationObserver` podemos detectar cuándo aparece el elemento no deseado en nuestro DOM y removerlo. -There are other situations when a third-party script adds something into our document, and we'd like to detect, when it happens, to adapt our page, dynamically resize something etc. +Hay otras situaciones, como cuando un script de terceras partes agrega algo en nuestro documento y quisiéramos detectarlo para adaptar nuestra página y cambiar el tamaño de algo dinámicamente, etc. -`MutationObserver` allows to implement this. +`MutationObserver` permite implementarlo. -## Usage for architecture +## Uso para arquitectura -There are also situations when `MutationObserver` is good from architectural standpoint. +Hay también situaciones donde `MutationObserver` es bueno desde el punto de vista de la arquitectura. -Let's say we're making a website about programming. Naturally, articles and other materials may contain source code snippets. +Digamos que estamos haciendo un sitio web acerca de programación. Naturalmente, los artículos y otros materiales pueden contener fragmentos de código. -Such snippet in an HTML markup looks like this: +Tal fragmento en un markup HTML se ve como esto: ```html ... <pre class="language-javascript"><code> - // here's the code + // aquí el código let hello = "world"; </code></pre> ... ``` +<<<<<<< HEAD +Para mejorar la legibilidad y al mismo tiempo embellecerlo, usaremos una librería JavaScript de "highlighting" para resaltar elementos de nuestro sitio, por ejemplo [Prism.js](https://prismjs.com/). Para obtener sintaxis resaltada para el fragmento de arriba en Prism, llamamos a `Prism.highlightElem(pre)`, que examina el contenido de tales elementos y les agrega tags y styles especiales para obtener sintaxis resaltada con color, similares a los que ves en esta página. + +¿Exactamente cuándo ejecutar tal método de highlighting? Bien, podemos hacerlo en el evento `DOMContentLoaded`, o poner el script al final de la página. En el momento en que tenemos nuestro DOM listo buscamos los elementos `pre[class*="language"]` y llamamos `Prism.highlightElem` en ellos: + +```js +// resaltar todos los fragmentos de código en la página +document.querySelectorAll('pre[class*="language"]').forEach(Prism.highlightElem); +======= For better readability and at the same time, to beautify it, we'll be using a JavaScript syntax highlighting library on our site, like [Prism.js](https://prismjs.com/). To get syntax highlighting for above snippet in Prism, `Prism.highlightElement(pre)` is called, which examines the contents of such `pre` elements and adds special tags and styles for colored syntax highlighting into those elements, similar to what you see in examples here, on this page. When exactly should we run that highlighting method? Well, we can do it on `DOMContentLoaded` event, or put the script at the bottom of the page. The moment our DOM is ready, we can search for elements `pre[class*="language"]` and call `Prism.highlightElement` on them: @@ -135,25 +144,32 @@ When exactly should we run that highlighting method? Well, we can do it on `DOMC ```js // highlight all code snippets on the page document.querySelectorAll('pre[class*="language"]').forEach(elem => Prism.highlightElement(elem)); +>>>>>>> 20208769e528337949e946f526534d61d38bac47 ``` -Everything's simple so far, right? We find code snippets in HTML and highlight them. +Todo es simple hasta ahora, ¿verdad? Buscamos fragmentos de código en HTML y los resaltamos. -Now let's go on. Let's say we're going to dynamically fetch materials from a server. We'll study methods for that [later in the tutorial](info:fetch). For now it only matters that we fetch an HTML article from a webserver and display it on demand: +Continuemos. Digamos que vamos a buscar dinámicamente material desde un servidor. Estudiaremos métodos para ello [más adelante](info:fetch) en el tutorial. Por ahora solamente importa que buscamos un artículo HTML desde un servidor web y lo mostramos bajo demanda: ```js -let article = /* fetch new content from server */ +let article = /* busca contenido nuevo desde un servidor */ articleElem.innerHTML = article; ``` +<<<<<<< HEAD +El nuevo elemento HTML `article` puede contener fragmentos de código. Necesitamos llamar `Prism.highlightElem` en ellos, de otro modo no se resaltarían. + +**¿Dónde y cuándo llamar `Prism.highlightElem` en un artículo cargado dinámicamente?** +======= The new `article` HTML may contain code snippets. We need to call `Prism.highlightElement` on them, otherwise they won't get highlighted. **Where and when to call `Prism.highlightElement` for a dynamically loaded article?** +>>>>>>> 20208769e528337949e946f526534d61d38bac47 -We could append that call to the code that loads an article, like this: +Podríamos agregar el llamado al código que carga un "article", como esto: ```js -let article = /* fetch new content from server */ +let article = /* busca contenido nuevo desde un servidor */ articleElem.innerHTML = article; *!* @@ -162,38 +178,38 @@ snippets.forEach(elem => Prism.highlightElement(elem)); */!* ``` -...But, imagine if we have many places in the code where we load our content - articles, quizzes, forum posts, etc. Do we need to put the highlighting call everywhere, to highlight the code in content after loading? That's not very convenient. +...Pero imagina que tenemos muchos lugares en el código donde cargamos contenido: artículos, cuestionarios, entradas de foros. ¿Necesitamos poner el llamado al "highlighting" en todos lugares? Eso no es muy conveniente. -And what if the content is loaded by a third-party module? For example, we have a forum written by someone else, that loads content dynamically, and we'd like to add syntax highlighting to it. No one likes patching third-party scripts. +¿Y si el contenido es cargado por un módulo de terceras partes? Por ejemplo tenemos un foro, escrito por algún otro, que carga contenido dinámicamente y quisiéramos añadirle sintaxis resaltada. A nadie le gusta emparchar scripts de terceras partes. -Luckily, there's another option. +Afortunadamente hay otra opción. -We can use `MutationObserver` to automatically detect when code snippets are inserted into the page and highlight them. +Podemos usar `MutationObserver` para detectar automáticamente cuándo los fragmentos de código son insertados en la página y resaltarlos. -So we'll handle the highlighting functionality in one place, relieving us from the need to integrate it. +Entonces manejaremos la funcionalidad de "highlighting" en un único lugar, liberándonos de la necesidad de integrarlo. -### Dynamic highlight demo +### Demo de highlight dinámico -Here's the working example. +Aquí el ejemplo funcionando. -If you run this code, it starts observing the element below and highlighting any code snippets that appear there: +Si ejecutas el código, este comienza a observar el elemento debajo y resalta cualquier fragmento de código que aparezca allí: ```js run let observer = new MutationObserver(mutations => { for(let mutation of mutations) { - // examine new nodes, is there anything to highlight? + // examine nodos nuevos, ¿hay algo para resaltar? for(let node of mutation.addedNodes) { - // we track only elements, skip other nodes (e.g. text nodes) + // seguimos elementos solamente, saltamos los otros nodos (es decir nodos de texto) if (!(node instanceof HTMLElement)) continue; - // check the inserted element for being a code snippet + // verificamos que el elemento insertado sea un fragmento de código if (node.matches('pre[class*="language-"]')) { Prism.highlightElement(node); } - // or maybe there's a code snippet somewhere in its subtree? + // ¿o tal vez haya un fragmento de código en su sub-árbol? for(let elem of node.querySelectorAll('pre[class*="language-"]')) { Prism.highlightElement(elem); } @@ -207,18 +223,18 @@ let demoElem = document.getElementById('highlight-demo'); observer.observe(demoElem, {childList: true, subtree: true}); ``` -Here, below, there's an HTML-element and JavaScript that dynamically fills it using `innerHTML`. +Aquí, abajo, hay un elemento HTML y JavaScript que lo llena dinámicamente usando `innerHTML`. -Please run the previous code (above, observes that element), and then the code below. You'll see how `MutationObserver` detects and highlights the snippet. +Por favor ejecuta el código anterior (arriba, que observa aquel elemento) y luego el código de abajo. Verás cómo `MutationObserver` detecta y resalta el fragmento. <p id="highlight-demo" style="border: 1px solid #ddd">A demo-element with <code>id="highlight-demo"</code>, run the code above to observe it.</p> -The following code populates its `innerHTML`, that causes the `MutationObserver` to react and highlight its contents: +El siguiente código llena su `innerHTML`, lo que causa que `MutationObserver` reaccione y resalte su contenido: ```js run let demoElem = document.getElementById('highlight-demo'); -// dynamically insert content with code snippets +// inserta contenido con fragmentos de código demoElem.innerHTML = `A code snippet is below: <pre class="language-javascript"><code> let hello = "world!"; </code></pre> <div>Another one:</div> @@ -228,46 +244,46 @@ demoElem.innerHTML = `A code snippet is below: `; ``` -Now we have `MutationObserver` that can track all highlighting in observed elements or the whole `document`. We can add/remove code snippets in HTML without thinking about it. +Ahora tenemos un `MutationObserver` que puede rastrear todo el "highlighting" en los elementos observados del `document` entero. Podemos agregar o quitar fragmentos de código en el HTML sin siquiera pensar en ello. -## Additional methods +## Métodos adicionales -There's a method to stop observing the node: +Hay un método para detener la observación del nodo: -- `observer.disconnect()` -- stops the observation. +- `observer.disconnect()` -- detiene la observación. -When we stop the observing, it might be possible that some changes were not yet processed by the observer. In such cases, we use +Cuando detenemos la observación, algunos cambios todavía podrían quedar sin ser procesados por el observador. En tales casos usamos -- `observer.takeRecords()` -- gets a list of unprocessed mutation records - those that happened, but the callback has not handled them. +- `observer.takeRecords()` -- obtiene una lista de registros de mutaciones sin procesar, aquellos que ocurrieron pero el callback no manejó. -These methods can be used together, like this: +Estos métodos pueden ser usados juntos, como esto: ```js -// get a list of unprocessed mutations -// should be called before disconnecting, -// if you care about possibly unhandled recent mutations +// obtener una lista de mutaciones sin procesar +// debe ser llamada antes de la desconexión, +// si te interesa las posibles mutaciones recientes sin manejar let mutationRecords = observer.takeRecords(); -// stop tracking changes +// detener el rastreo de cambios observer.disconnect(); ... ``` -```smart header="Records returned by `observer.takeRecords()` are removed from the processing queue" -The callback won't be called for records, returned by `observer.takeRecords()`. +```smart header="Lo registros devueltos por `observer.takeRecords()` son quitados de la cola de procesamiento" +El callback no será llamado en registros devueltos por `observer.takeRecords()`. ``` -```smart header="Garbage collection interaction" -Observers use weak references to nodes internally. That is, if a node is removed from the DOM, and becomes unreachable, then it can be garbage collected. +```smart header="Interacción con la recolección de basura" +Los observadores usan internamente referencias débiles a nodos. Esto es: si un nodo es quitado del DOM y se hace inalcanzable, se vuelve basura para ser recolectada. -The mere fact that a DOM node is observed doesn't prevent the garbage collection. +El mero hecho de que un nodo DOM sea observado no evita la recolección de basura. ``` -## Summary +## Resumen -`MutationObserver` can react to changes in DOM - attributes, text content and adding/removing elements. +`MutationObserver` puede reaccionar a cambios en el DOM: atributos, contenido de texto y añadir o quitar elementos. -We can use it to track changes introduced by other parts of our code, as well as to integrate with third-party scripts. +Podemos usarlo para rastrear cambios introducidos por otras partes de nuestro código o bien para integrarlo con scripts de terceras partes. -`MutationObserver` can track any changes. The config "what to observe" options are used for optimizations, not to spend resources on unneeded callback invocations. +`MutationObserver` puede rastrear cualquier cambio. Las opciones de `config` permiten establecer qué se va a observar, se usa para optimización y no desperdiciar recursos en llamados al callback innecesarios. diff --git a/2-ui/99-ui-misc/02-selection-range/article.md b/2-ui/99-ui-misc/02-selection-range/article.md index 09a20bc67..0d04a6760 100644 --- a/2-ui/99-ui-misc/02-selection-range/article.md +++ b/2-ui/99-ui-misc/02-selection-range/article.md @@ -4,37 +4,37 @@ libs: --- -# Selection and Range +# Selection y Range -In this chapter we'll cover selection in the document, as well as selection in form fields, such as `<input>`. +En este capítulo cubriremos la selección en el documento, así como la selección en campos de formulario, como `<input>`. -JavaScript can access an existing selection, select/deselect DOM nodes as a whole or partially, remove the selected content from the document, wrap it into a tag, and so on. +JavaScript puede acceder una selección existente, seleccionar/deseleccionar nodos DOM tanto en su totalidad como parcialmente, eliminar la parte seleccionada del documento, envolverla en una etiqueta, etc. -You can find some recipes for common tasks at the end of the chapter, in "Summary" section. Maybe that covers your current needs, but you'll get much more if you read the whole text. +Puedes encontrar algunas recetas para tareas comunes al final del artículo, en la sección "Resumen". Pero será mucho más beneficiosa la lectura de todo el capítulo. -The underlying `Range` and `Selection` objects are easy to grasp, and then you'll need no recipes to make them do what you want. +Los objetos subyacentes `Range` y `Selection` son fáciles de captar y no necesitarás recetas para que hagan lo que deseas. ## Range -The basic concept of selection is [Range](https://dom.spec.whatwg.org/#ranges), that is essentially a pair of "boundary points": range start and range end. +El concepto básico de selección [Range](https://dom.spec.whatwg.org/#ranges), es básicamente un par de "puntos límite": inicio y fin del rango. -A `Range` object is created without parameters: +Un objeto rango se crea sin parámetros: ```js let range = new Range(); ``` -Then we can set the selection boundaries using `range.setStart(node, offset)` and `range.setEnd(node, offset)`. +Entonces podemos establecer los límites de selección usando `range.setStart(node, offset)` y `range.setEnd(node, offset)`. -As you might guess, further we'll use the `Range` objects for selection, but first let's create few such objects. +En adelante usaremos objetos `Range` para selección, pero primero creemos algunos de ellos. -### Selecting the text partially +### Seleccionando el texto parcialmente -The interesting thing is that the first argument `node` in both methods can be either a text node or an element node, and the meaning of the second argument depends on that. +Lo interesante es que el primer argumento `node` en ambos métodos puede ser tanto un nodo de texto o un nodo de elemento, y el significado del segundo argumento depende de ello. -**If `node` is a text node, then `offset` must be the position in its text.** +**Si `node` es un nodo de texto, `offset` debe ser la posición en su texto.** -For example, given the element `<p>Hello</p>`, we can create the range containing the letters "ll" as follows: +Por ejemplo, dado el elemento `<p>Hello</p>`, podemos crear el rango conteniendo las letras "ll": ```html run <p id="p">Hello</p> @@ -43,28 +43,28 @@ For example, given the element `<p>Hello</p>`, we can create the range containin range.setStart(p.firstChild, 2); range.setEnd(p.firstChild, 4); - // toString of a range returns its content as text + // toString de un rango devuelve su contenido como un texto console.log(range); // ll </script> ``` -Here we take the first child of `<p>` (that's the text node) and specify the text positions inside it: +Aquí tomamos el primer hijo de `<p>` (que es el nodo de texto) y especificamos la posición del texto dentro de él: ![](range-hello-1.svg) -### Selecting element nodes +### Seleccionando nodos de elemento -**Alternatively, if `node` is an element node, then `offset` must be the child number.** +**Alternativamente, si `node` es un nodo de elemento, `offset` debe ser el número de hijo.** -That's handy for making ranges that contain nodes as a whole, not stop somewhere inside their text. +Esto es práctico para hacer rangos que contienen nodos como un todo, no detenerse en algún lugar dentro de su texto. -For example, we have a more complex document fragment: +Por ejemplo, tenemos un fragmento de documento más complejo: ```html autorun <p id="p">Example: <i>italic</i> and <b>bold</b></p> ``` -Here's its DOM structure with both element and text nodes: +Aquí está su estructura DOM usando ambos, nodos de texto y nodos de elemento: <div class="select-p-domtree"></div> @@ -102,20 +102,20 @@ let selectPDomtree = { drawHtmlTree(selectPDomtree, 'div.select-p-domtree', 690, 320); </script> -Let's make a range for `"Example: <i>italic</i>"`. +Hagamos un rango para `"Example: <i>italic</i>"`. -As we can see, this phrase consists of exactly two children of `<p>`, with indexes `0` and `1`: + Como podemos ver, esta frase consiste de exactamente dos hijos de `<p>` con índices `0` y `1`: ![](range-example-p-0-1.svg) -- The starting point has `<p>` as the parent `node`, and `0` as the offset. +- El punto de inicio tiene `<p>` como nodo padre `node`, y `0` como offset. - So we can set it as `range.setStart(p, 0)`. -- The ending point also has `<p>` as the parent `node`, but `2` as the offset (it specifies the range up to, but not including `offset`). +Así que podemos establecerlo como `range.setStart(p, 0)`. +- El punto final también tiene `<p>` como nodo padre, but `2` como offset (especifica el rango "hasta", pero no incluyendo, `offset`). - So we can set it as `range.setEnd(p, 2)`. + Entonces podemos establecerlo como `range.setEnd(p, 2)`. -Here's the demo. If you run it, you can see that the text gets selected: +Aquí la demo. Si la ejeutas, puedes ver el texto siendo seleccionado:: ```html run <p id="p">Example: <i>italic</i> and <b>bold</b></p> @@ -128,15 +128,15 @@ Here's the demo. If you run it, you can see that the text gets selected: range.setEnd(p, 2); */!* - // toString of a range returns its content as text, without tags - console.log(range); // Example: italic + // toString de un rango devuelve su contenido como texto (sin etiquetas) + alert(range); // Ejemplo: italic - // apply this range for document selection (explained later below) + // aplicar este rango para la selección de documentos (explicado más adelante) document.getSelection().addRange(range); </script> ``` -Here's a more flexible test stand where you can set range start/end numbers and explore other variants: +Aquí hay un banco de pruebas más flexible donde puedes establecer números de principio y fin y explorar otras variantes: ```html run autorun <p id="p">Example: <i>italic</i> and <b>bold</b></p> @@ -152,32 +152,32 @@ From <input id="start" type="number" value=1> – To <input id="end" type="numbe range.setEnd(p, end.value); */!* - // apply the selection, explained later below + // aplicar la selección, explicado más adelante document.getSelection().removeAllRanges(); document.getSelection().addRange(range); }; </script> ``` -E.g. selecting in the same `<p>` from offset `1` to `4` gives us the range `<i>italic</i> and <b>bold</b>`: +Ej. seleccionando de `1` a `4` da como rango `<i>italic</i> and <b>bold</b>`. ![](range-example-p-1-3.svg) -```smart header="Starting and ending nodes can be different" -We don't have to use the same node in `setStart` and `setEnd`. A range may span across many unrelated nodes. It's only important that the end is after the start in the document. +```smart header="Los nodos de inicio y final pueden ser diferentes" +No tenemos que usar el mismo nodo en `setStart` y `setEnd`. Un rango puede abarcar muchos nodos no relacionados. Solo es importante que el final sea posterior al comienzo. ``` -### Selecting a bigger fragment +### Seleccionar partes de nodos de texto -Let's make a bigger selection in our example, like this: +Seleccionemos el texto parcialmente, así: ![](range-example-p-2-b-3.svg) -We already know how to do that. We just need to set the start and the end as a relative offset in text nodes. +Eso también es posible, solo necesitamos establecer el inicio y el final como un desplazamiento relativo en los nodos de texto. -We need to create a range, that: -- starts from position 2 in `<p>` first child (taking all but two first letters of "Ex<b>ample:</b> ") -- ends at the position 3 in `<b>` first child (taking first three letters of "<b>bol</b>d", but no more): +Necesitamos crear un rango, que: +- comienza desde la posición 2 en `<p>` primer hijo (tomando todas menos dos primeras letras de "Ex<b>ample:</b>") +- termina en la posición 3 de `<b>` primer hijo (tomando las primeras tres letras de "<b>bol</b>d", pero no más): ```html run <p id="p">Example: <i>italic</i> and <b>bold</b></p> @@ -188,77 +188,77 @@ We need to create a range, that: range.setStart(p.firstChild, 2); range.setEnd(p.querySelector('b').firstChild, 3); - console.log(range); // ample: italic and bol + alert(range); // amplio: italic and bol - // use this range for selection (explained later) + // use este rango para la selección (explicado más adelante) window.getSelection().addRange(range); </script> ``` -As you can see, it's fairly easy to make a range of whatever we want. +Como puedes ver, es fácil hacer un rango con lo que quieras. -If we'd like to take nodes as a whole, we can pass elements in `setStart/setEnd`. Otherwise, we can work on the text level. +Si queremos tomar los nodos como un todo, podemos pasar los elementos en `setStart/setEnd`. Si no, podemos trabajar en el nivel de texto. -## Range properties +## Propiedades de Range -The range object that we created in the example above has following properties: +El objeto rango que creamos arriba tiene las siguientes propiedades: ![](range-example-p-2-b-3-range.svg) -- `startContainer`, `startOffset` -- node and offset of the start, - - in the example above: first text node inside `<p>` and `2`. -- `endContainer`, `endOffset` -- node and offset of the end, - - in the example above: first text node inside `<b>` and `3`. -- `collapsed` -- boolean, `true` if the range starts and ends on the same point (so there's no content inside the range), - - in the example above: `false` -- `commonAncestorContainer` -- the nearest common ancestor of all nodes within the range, - - in the example above: `<p>` +- `startContainer`, `startOffset` -- nodo y desplazamiento del inicio, + - en el ejemplo anterior: primer nodo de texto dentro de `<p>` y `2`. +- `endContainer`, `endOffset` -- nodo y desplazamiento del final, + - en el ejemplo anterior: primer nodo de texto dentro de `<b>` y `3`. +- `collapsed` -- booleano, `true` si el rango comienza y termina en el mismo punto (por lo que no hay contenido dentro del rango), + - en el ejemplo anterior: `false` +- `commonAncestorContainer` -- el ancestro común más cercano de todos los nodos dentro del rango, + - en el ejemplo anterior: `<p>` -## Range selection methods +## Métodos de selección de rango -There are many convenient methods to manipulate ranges. +Hay muchos métodos convenientes para manipular rangos. -We've already seen `setStart` and `setEnd`, here are other similar methods. +Ya hemos visto `setStart` y `setEnd`, aquí hay otros métodos similares. -Set range start: +Establecer inicio de rango: -- `setStart(node, offset)` set start at: position `offset` in `node` -- `setStartBefore(node)` set start at: right before `node` -- `setStartAfter(node)` set start at: right after `node` +- `setStart(node, offset)` establecer inicio en: posición `offset` en `node` +- `setStartBefore(node)` establecer inicio en: justo antes `node` +- `setStartAfter(node)` establecer inicio en: justo después `node` -Set range end (similar methods): +Establecer fin de rango (métodos similares): -- `setEnd(node, offset)` set end at: position `offset` in `node` -- `setEndBefore(node)` set end at: right before `node` -- `setEndAfter(node)` set end at: right after `node` +- `setEnd(node, offset)` establecer final en: posición `offset` en `node` +- `setEndBefore(node)` establecer final en: justo antes `node` +- `setEndAfter(node)` establecer final en: justo después `node` -Technically, `setStart/setEnd` can do anything, but more methods provide more convenience. +Técnicamente, `setStart/setEnd` puede hacer cualquier cosa, pero más métodos brindan más conveniencia. -In all these methods, `node` can be both a text or element node: for text nodes `offset` skips that many of characters, while for element nodes that many child nodes. +En todos estos métodos `node` puede ser un nodo de texto o de elemento: para nodos de texto `offset` salta esa cantidad de caracteres, mientras que para los nodos de elementos es la cantidad de nodos secundarios.** -Even more methods to create ranges: -- `selectNode(node)` set range to select the whole `node` -- `selectNodeContents(node)` set range to select the whole `node` contents -- `collapse(toStart)` if `toStart=true` set end=start, otherwise set start=end, thus collapsing the range -- `cloneRange()` creates a new range with the same start/end +Más métodos aún para crear rangos: +- `selectNode(node)` establecer rango para seleccionar el `node` +- `selectNodeContents(node)` establecer rango para seleccionar todo el contenido de `node` +- `collapse(toStart)` si `toStart=true` establece final=comienzo, de otra manera comienzo=final, colapsando así el rango +- `cloneRange()` crea un nuevo rango con el mismo inicio/final -## Range editing methods +## Métodos para edición en el rango: -Once the range is created, we can manipulate its content using these methods: +Una vez creado el rango, podemos manipular su contenido usando estos métodos: -- `deleteContents()` -- remove range content from the document -- `extractContents()` -- remove range content from the document and return as [DocumentFragment](info:modifying-document#document-fragment) -- `cloneContents()` -- clone range content and return as [DocumentFragment](info:modifying-document#document-fragment) -- `insertNode(node)` -- insert `node` into the document at the beginning of the range -- `surroundContents(node)` -- wrap `node` around range content. For this to work, the range must contain both opening and closing tags for all elements inside it: no partial ranges like `<i>abc`. +- `deleteContents()` -- eliminar el contenido de rango del documento +- `extractContents()` -- eliminar el contenido de rango del documento y lo retorna como [DocumentFragment](info:modifying-document#document-fragment) +- `cloneContents()` -- clonar el contenido del rango y lo retorna como [DocumentFragment](info:modifying-document#document-fragment) +- `insertNode(node)` -- inserta `node` en el documento al comienzo del rango +- `surroundContents(node)` -- envuelve `node` alrededor del contenido del rango. Para que esto funcione, el rango debe contener etiquetas de apertura y cierre para todos los elementos dentro de él, sin rangos parciales como `<i>abc`. -With these methods we can do basically anything with selected nodes. +Con estos métodos podemos hacer básicamente cualquier cosa con los nodos seleccionados. -Here's the test stand to see them in action: +Aquí está el banco de pruebas para verlos en acción: ```html run refresh autorun height=260 -Click buttons to run methods on the selection, "resetExample" to reset it. +Haga clic en los botones para ejecutar métodos en la selección, "resetExample" para restablecerla. <p id="p">Example: <i>italic</i> and <b>bold</b></p> @@ -266,7 +266,7 @@ Click buttons to run methods on the selection, "resetExample" to reset it. <script> let range = new Range(); - // Each demonstrated method is represented here: + // Cada método demostrado se representa aquí: let methods = { deleteContents() { range.deleteContents() @@ -312,79 +312,79 @@ Click buttons to run methods on the selection, "resetExample" to reset it. </script> ``` -There also exist methods to compare ranges, but these are rarely used. When you need them, please refer to the [spec](https://dom.spec.whatwg.org/#interface-range) or [MDN manual](mdn:/api/Range). +También existen métodos para comparar rangos, pero rara vez se utilizan. Cuando los necesite, consulte el [spec](https://dom.spec.whatwg.org/#interface-range) o [manual MDN](https://developer.mozilla.org/es/docs/Web/API/Range). ## Selection -`Range` is a generic object for managing selection ranges. Although, creating a `Range` doesn't mean that we see a selection on screen. +`Range` es un objeto genérico para gestionar rangos de selección. Pero crearlos no significa que podamos ver la selección en la pantalla. -We may create `Range` objects, pass them around -- they do not visually select anything on their own. +Podemos crear objetos `Range`, pasarlos; no seleccionan nada visualmente por sí mismos. -The document selection is represented by `Selection` object, that can be obtained as `window.getSelection()` or `document.getSelection()`. A selection may include zero or more ranges. At least, the [Selection API specification](https://www.w3.org/TR/selection-api/) says so. In practice though, only Firefox allows to select multiple ranges in the document by using `key:Ctrl+click` (`key:Cmd+click` for Mac). +La selección de documento está representada por el objeto `Selection`, que se puede obtener como `window.getSelection()` o `document.getSelection()` Una selección puede incluir cero o más rangos. Al menos, la [especificación Selection API](https://www.w3.org/TR/selection-api/) lo dice. Sin embargo, en la práctica, solo Firefox permite seleccionar múltiples rangos en el documento usando `key:Ctrl+click` (`key:Cmd+click` para Mac). -Here's a screenshot of a selection with 3 ranges, made in Firefox: +Aquí hay una captura de pantalla de una selección con 3 rangos en Firefox: ![](selection-firefox.svg) -Other browsers support at maximum 1 range. As we'll see, some of `Selection` methods imply that there may be many ranges, but again, in all browsers except Firefox, there's at maximum 1. +Otros navegadores admiten un rango máximo de 1. Como veremos, algunos de los métodos de `Selection` implica que puede haber muchos rangos, pero nuevamente, en todos los navegadores excepto Firefox, hay un máximo de 1. -Here's a small demo that shows the current selection (select something and click) as text: +Aquí hay una pequeña demo que muestra la selección actual (selecciona algo y haz clic) como texto: <button onclick="alert(document.getSelection())">alert(document.getSelection())</button> -## Selection properties +## Propiedades de Selection -As said, a selection may in theory contain multiple ranges. We can get these range objects using the method: +Como dijimos antes, una selección en teoría tiene múltiples rangos. Podemos obtener estos objetos rango usando el método: -- `getRangeAt(i)` -- get i-th range, starting from `0`. In all browsers except Firefox, only `0` is used. +- `getRangeAt(i)` -- obtiene el rango "i" comenzando desde `0`. En todos los navegadores excepto Firefox, solo `0` es usado. -Also, there exist properties that often provide better convenience. +También existen propiedades que a menudo brindan conveniencia. -Similar to a range, a selection object has a start, called "anchor", and the end, called "focus". +Similar a Range, una selección tiene un inicio, llamado "ancla(anchor)", y un final, llamado "foco(focus)". -The main selection properties are: +Las principales propiedades de selection son: -- `anchorNode` -- the node where the selection starts, -- `anchorOffset` -- the offset in `anchorNode` where the selection starts, -- `focusNode` -- the node where the selection ends, -- `focusOffset` -- the offset in `focusNode` where the selection ends, -- `isCollapsed` -- `true` if selection selects nothing (empty range), or doesn't exist. -- `rangeCount` -- count of ranges in the selection, maximum `1` in all browsers except Firefox. +- `anchorNode` -- el nodo donde comienza la selección, +- `anchorOffset` -- el desplazamiento en `anchorNode` donde comienza la selección, +- `focusNode` -- el nodo donde termina la selección, +- `focusOffset` -- el desplazamiento en `focusNode` donde termina la selección, +- `isCollapsed` -- `true` si la selección no selecciona nada (rango vacío), o no existe. +- `rangeCount` -- recuento de rangos en la selección, máximo "1" en todos los navegadores excepto Firefox. -```smart header="Selection end/start vs Range" +```smart header="Inicio/final, Selection vs. Range" -There's an important difference between a selection anchor/focus compared with a `Range` start/end. +Hay una diferencia importante entre anchor/focus (ancla/foco) de una selección comparado al inicio/fin de un `Range`. -As we know, `Range` objects always have their start before the end. +Sabemos que los objetos `Range` siempre tienen el inicio antes que el final. -For selections, that's not always the case. +En las selecciones, no siempre es así. -Selecting something with a mouse can be done in both directions: either "left-to-right" or "right-to-left". +Seleccionar algo con el ratón puede hacerse en ambas direcciones: tanto de izquierda a derecha como de derecha a izquierda. -In other words, when the mouse button is pressed, and then it moves forward in the document, then its end (focus) will be after its start (anchor). +Cuando el botón es presionado, cuando se mueve hacia adelante en el documento, entonces su final (foco) estará después del inicio (ancla). -E.g. if the user starts selecting with mouse and goes from "Example" to "italic": +Ej. si el usuario comienza a seleccionar con el mouse y pasa de "Example" a "italic": ![](selection-direction-forward.svg) -...But the same selection could be done backwards: starting from "italic" to "Example" (backward direction), then its end (focus) will be before the start (anchor): +...Pero la selección puede hacerse hacia atrás: comenzando por "italic" terminando en "Example", su foco estará antes del ancla: ![](selection-direction-backward.svg) ``` -## Selection events +## Eventos Selection -There are events on to keep track of selection: +Hay eventos para realizar un seguimiento de la selección: -- `elem.onselectstart` -- when a selection *starts* specifically on element `elem` (or inside it). For instance, when the user presses the mouse button on it and starts to move the pointer. - - Preventing the default action cancels the selection start. So starting a selection from this element becomes impossible, but the element is still selectable. The visitor just needs to start the selection from elsewhere. -- `document.onselectionchange` -- whenever a selection changes or starts. - - Please note: this handler can be set only on `document`, it tracks all selections in it. +- `elem.onselectstart` -- cuando una selección comienza en `elem`, ej. el usuario comienza a mover el mouse con el botón presionado. + - Evitar la acción predeterminada hace que la selección no se inicie. +- `document.onselectionchange` -- siempre que cambie una selección. + - Tenga en cuenta: este controlador solo se puede configurar en `document`. -### Selection tracking demo +### Demostración de seguimiento de selección -Here's a small demo. It tracks the current selection on the `document` and shows its boundaries: +Aquí hay una pequeña demostración que muestra los límites de selección de forma dinámica a medida que cambia: ```html run height=80 <p id="p">Select me: <i>italic</i> and <b>bold</b></p> @@ -396,21 +396,21 @@ From <input id="from" disabled> – To <input id="to" disabled> let {anchorNode, anchorOffset, focusNode, focusOffset} = selection; - // anchorNode and focusNode are text nodes usually + // anchorNode y focusNode usualmente son nodos de texto from.value = `${anchorNode?.data}, offset ${anchorOffset}`; to.value = `${focusNode?.data}, offset ${focusOffset}`; }; </script> ``` -### Selection copying demo +### Demostración de copia de selección -There are two approaches to copying the selected content: +Hay dos enfoques para la copia de contenido seleccionado: -1. We can use `document.getSelection().toString()` to get it as text. -2. Otherwise, to copy the full DOM, e.g. if we need to keep formatting, we can get the underlying ranges with `getRangeAt(...)`. A `Range` object, in turn, has `cloneContents()` method that clones its content and returns as `DocumentFragment` object, that we can insert elsewhere. +1. Podemos usar `document.getSelection().toString()` para obtenerlo como texto. +2. O copiar el DOM entero; por ejemplo, si necesitamos mantener el formato, podemos obtener los rangos correspondientes con `getRangeAt(...)`. Un objeto `Range`, a su vez, tiene el método `cloneContents()` que clona su contenido y devuelve un objeto `DocumentFragment`, que podemos insertar en algún otro lugar. -Here's the demo of copying the selected content both as text and as DOM nodes: +Una demostración de cómo obtener la selección como texto y como nodos DOM: ```html run height=100 <p id="p">Select me: <i>italic</i> and <b>bold</b></p> @@ -425,104 +425,104 @@ As text: <span id="astext"></span> cloned.innerHTML = astext.innerHTML = ""; - // Clone DOM nodes from ranges (we support multiselect here) + // Clonar nodos DOM de rangos (admitimos selección múltiple aquí) for (let i = 0; i < selection.rangeCount; i++) { cloned.append(selection.getRangeAt(i).cloneContents()); } - // Get as text + // Obtener como texto astext.innerHTML += selection; }; </script> ``` -## Selection methods +## Métodos de selección -We can work with the selection by adding/removing ranges: +Podemos trabajar con métodos de selección para agregar y eliminar rangos: -- `getRangeAt(i)` -- get i-th range, starting from `0`. In all browsers except Firefox, only `0` is used. -- `addRange(range)` -- add `range` to selection. All browsers except Firefox ignore the call, if the selection already has an associated range. -- `removeRange(range)` -- remove `range` from the selection. -- `removeAllRanges()` -- remove all ranges. -- `empty()` -- alias to `removeAllRanges`. +- `getRangeAt(i)` -- obtener el rango i-ésimo, comenzando desde "0". En todos los navegadores, excepto Firefox, solo se utiliza `0`. +- `addRange(rango)` -- agrega un `rango` a la selección. Todos los navegadores excepto Firefox ignoran la llamada, si la selección ya tiene un rango asociado. +- `removeRange(rango)` --elimina `rango` de la selección. +- `removeAllRanges()` --elimina todos los rangos. +- `empty()` -- alias para `removeAllRanges`. -There are also convenience methods to manipulate the selection range directly, without intermediate `Range` calls: +Además, existen métodos convenientes para manipular el rango de selección directamente, sin llamadas intermedias a `Range`: -- `collapse(node, offset)` -- replace selected range with a new one that starts and ends at the given `node`, at position `offset`. -- `setPosition(node, offset)` -- alias to `collapse`. -- `collapseToStart()` - collapse (replace with an empty range) to selection start, -- `collapseToEnd()` - collapse to selection end, -- `extend(node, offset)` - move focus of the selection to the given `node`, position `offset`, -- `setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset)` - replace selection range with the given start `anchorNode/anchorOffset` and end `focusNode/focusOffset`. All content in-between them is selected. -- `selectAllChildren(node)` -- select all children of the `node`. -- `deleteFromDocument()` -- remove selected content from the document. -- `containsNode(node, allowPartialContainment = false)` -- checks whether the selection contains `node` (partially if the second argument is `true`) +- `collapse(node, offset)` -- Reemplazar el rango seleccionado con uno nuevo que comienza y termina en el `node` dado, en posición `offset`. +- `setPosition(node, offset)` -- alias para `collapse`. +- `collapseToStart()` - colapsar (reemplazar con un rango vacío) al inicio de la selección, +- `collapseToEnd()` - colapso hasta el final de la selección, +- `extend(node, offset)` - mover el foco de la selección al `node` dado, posición `offset`, +- `setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset)` - reemplazar el rango de selección con el inicio dado `anchorNode/anchorOffset` y final `focusNode/focusOffset`. Se selecciona todo el contenido entre ellos. +- `selectAllChildren(node)` -- seleccionar todos los hijos del `node`. +- `deleteFromDocument()` -- eliminar el contenido seleccionado del documento. +- `containsNode(node, allowPartialContainment = false)` -- comprueba si la selección contiene `node` (parcialmente si el segundo argumento es `true`) -For most tasks these methods are just fine, there's no need to access the underlying `Range` object. +Entonces, para muchas tareas podemos llamar a los métodos de `Selection`, y no es necesario acceder al objeto `Range` subyacente. -For example, selecting the whole contents of the paragraph `<p>`: +Por ejemplo, seleccionando todo el contenido del párrafo `<p>`: ```html run <p id="p">Select me: <i>italic</i> and <b>bold</b></p> <script> - // select from 0th child of <p> to the last child + // seleccione desde el 0 hijo de <p> hasta el último hijo document.getSelection().setBaseAndExtent(p, 0, p, p.childNodes.length); </script> ``` -The same thing using ranges: +Lo mismo usando rangos: ```html run <p id="p">Select me: <i>italic</i> and <b>bold</b></p> <script> let range = new Range(); - range.selectNodeContents(p); // or selectNode(p) to select the <p> tag too + range.selectNodeContents(p); // o selectNode(p) para seleccionar el tag <p> también - document.getSelection().removeAllRanges(); // clear existing selection if any + document.getSelection().removeAllRanges(); // borrar la selección existente si la hubiera document.getSelection().addRange(range); </script> ``` -```smart header="To select something, remove the existing selection first" -If a document selection already exists, empty it first with `removeAllRanges()`. And then add ranges. Otherwise, all browsers except Firefox ignore new ranges. +```smart header="Para seleccionar, primero elimine la selección existente" +Si la selección ya existe, vacíelo primero con `removeAllRanges()`. Y luego agregue rangos. De lo contrario, todos los navegadores excepto Firefox ignoran los nuevos rangos. -The exception is some selection methods, that replace the existing selection, such as `setBaseAndExtent`. +La excepción son algunos métodos de selección, que reemplazan la selección existente, como `setBaseAndExtent`. ``` -## Selection in form controls +## Selección en controles de formulario -Form elements, such as `input` and `textarea` provide [special API for selection](https://html.spec.whatwg.org/#textFieldSelection), without `Selection` or `Range` objects. As an input value is a pure text, not HTML, there's no need for such objects, everything's much simpler. +Elementos de formulario, como `input` y `textarea` proporciona [API especial para la selección](https://html.spec.whatwg.org/#textFieldSelection), sin objetos `Selection` o `Range`. Como un valor de entrada es un texto puro, no HTML, no hay necesidad de tales objetos, todo es mucho más simple. -Properties: -- `input.selectionStart` -- position of selection start (writeable), -- `input.selectionEnd` -- position of selection end (writeable), -- `input.selectionDirection` -- selection direction, one of: "forward", "backward" or "none" (if e.g. selected with a double mouse click), +Propiedades: +- `input.selectionStart` -- posición de inicio de selección (escribible), +- `input.selectionEnd` -- posición del final de la selección (escribible), +- `input.selectionDirection` -- dirección de selección, una de: "adelante" "hacia atrás" o "ninguno" (si, por ejemplo, se selecciona con un doble clic del mouse), -Events: -- `input.onselect` -- triggers when something is selected. +Eventos: +- `input.onselect` -- se activa cuando se selecciona algo. -Methods: +Métodos: -- `input.select()` -- selects everything in the text control (can be `textarea` instead of `input`), -- `input.setSelectionRange(start, end, [direction])` -- change the selection to span from position `start` till `end`, in the given direction (optional). -- `input.setRangeText(replacement, [start], [end], [selectionMode])` -- replace a range of text with the new text. +- `input.select()` -- selecciona todo en el control de texto (puede ser `textarea` en vez de `input`), +- `input.setSelectionRange(start, end, [direction])` -- cambiar la selección para abarcar desde la posición `start` hasta `end`, en la dirección indicada (opcional). +- `input.setRangeText(replacement, [start], [end], [selectionMode])` -- reemplace un rango de texto con el nuevo texto. - Optional arguments `start` and `end`, if provided, set the range start and end, otherwise user selection is used. + Los argumentos opcionales `start` y `end`, si se proporcionan, establecen el inicio y el final del rango; de lo contrario, se utiliza la selección del usuario. - The last argument, `selectionMode`, determines how the selection will be set after the text has been replaced. The possible values are: + El último argumento, `selectionMode`, determina cómo se establecerá la selección después de que se haya reemplazado el texto. Los posibles valores son: - - `"select"` -- the newly inserted text will be selected. - - `"start"` -- the selection range collapses just before the inserted text (the cursor will be immediately before it). - - `"end"` -- the selection range collapses just after the inserted text (the cursor will be right after it). - - `"preserve"` -- attempts to preserve the selection. This is the default. + - `"select"` -- se seleccionará el texto recién insertado. + - `"start"` -- el rango de selección se colapsa justo antes del texto insertado (el cursor estará inmediatamente antes). + - `"end"` -- el rango de selección se colapsa justo después del texto insertado (el cursor estará justo después). + - `"preserve"` -- intenta preservar la selección. Este es el predeterminado. -Now let's see these methods in action. +Ahora veamos estos métodos en acción. -### Example: tracking selection +### Ejemplo: Seguimiento de selección -For example, this code uses `onselect` event to track selection: +Por ejemplo, este código usa el evento `onselect` para rastrear la selección: ```html run autorun <textarea id="area" style="width:80%;height:60px"> @@ -539,20 +539,20 @@ From <input id="from" disabled> – To <input id="to" disabled> </script> ``` -Please note: -- `onselect` triggers when something is selected, but not when the selection is removed. -- `document.onselectionchange` event should not trigger for selections inside a form control, according to the [spec](https://w3c.github.io/selection-api/#dfn-selectionchange), as it's not related to `document` selection and ranges. Some browsers generate it, but we shouldn't rely on it. +Tenga en cuenta: +- `onselect` se activa cuando se selecciona algo, pero no cuando se elimina la selección. +- El evento `document.onselectionchange` no debería activarse para las selecciones dentro de un control de formulario, según el [spec](https://w3c.github.io/selection-api/#dfn-selectionchange), ya que no está relacionado con la selección y los rangos del "documento". Algunos navegadores lo generan, pero no debemos confiar en él. -### Example: moving cursor +### Ejemplo: cursor en movimiento -We can change `selectionStart` and `selectionEnd`, that sets the selection. +Podemos cambiar `selectionStart` y `selectionEnd`, que establece la selección. -An important edge case is when `selectionStart` and `selectionEnd` equal each other. Then it's exactly the cursor position. Or, to rephrase, when nothing is selected, the selection is collapsed at the cursor position. +Un caso límite importante es cuando `selectionStart` y `selectionEnd` son iguales entre sí. Entonces es exactamente la posición del cursor. O, para reformular, cuando no se selecciona nada, la selección se contrae en la posición del cursor. -So, by setting `selectionStart` and `selectionEnd` to the same value, we move the cursor. +Entonces, al establecer `selectionStart` y `selectionEnd` en el mismo valor, movemos el cursor. -For example: +Por ejemplo: ```html run autorun <textarea id="area" style="width:80%;height:60px"> @@ -571,13 +571,13 @@ Focus on me, the cursor will be at position 10. </script> ``` -### Example: modifying selection +### Ejemplo: modificar la selección -To modify the content of the selection, we can use `input.setRangeText()` method. Of course, we can read `selectionStart/End` and, with the knowledge of the selection, change the corresponding substring of `value`, but `setRangeText` is more powerful and often more convenient. +Para modificar el contenido de la selección, podemos utilizar el método `input.setRangeText()` Por supuesto, podemos leer `selectionStart/End` y, con el conocimiento de la selección, cambiar la subcadena correspondiente de `value`, pero `setRangeText` es más poderoso y a menudo más conveniente. -That's a somewhat complex method. In its simplest one-argument form it replaces the user selected range and removes the selection. +Ese es un método algo complejo. En su forma más simple de un argumento, reemplaza el rango seleccionado por el usuario y elimina la selección. -For example, here the user selection will be wrapped by `*...*`: +Por ejemplo, aquí la selección de usuario estará envuelta por `*...*`: ```html run autorun <input id="input" style="width:200px" value="Select here and click the button"> @@ -586,7 +586,7 @@ For example, here the user selection will be wrapped by `*...*`: <script> button.onclick = () => { if (input.selectionStart == input.selectionEnd) { - return; // nothing is selected + return; // nada fue seleccionado } let selected = input.value.slice(input.selectionStart, input.selectionEnd); @@ -595,9 +595,9 @@ button.onclick = () => { </script> ``` -With more arguments, we can set range `start` and `end`. +Con más argumentos, podemos establecer un rango `start` y `end`. -In this example we find `"THIS"` in the input text, replace it and keep the replacement selected: +En este ejemplo, encontramos `THIS` en el texto de entrada, lo reemplazamos y mantenemos el reemplazo seleccionado: ```html run autorun <input id="input" style="width:200px" value="Replace THIS in text"> @@ -614,13 +614,13 @@ button.onclick = () => { </script> ``` -### Example: insert at cursor +### Ejemplo: insertar en el cursor -If nothing is selected, or we use equal `start` and `end` in `setRangeText`, then the new text is just inserted, nothing is removed. +Si no se selecciona nada, o usamos el mismo `comienzo` y `final` en `setRangeText`, entonces el nuevo texto se acaba de insertar, no se elimina nada. -We can also insert something "at the cursor" using `setRangeText`. +También podemos insertar algo "en el cursor" usando `setRangeText`. -Here's a button that inserts `"HELLO"` at the cursor position and puts the cursor immediately after it. If the selection is not empty, then it gets replaced (we can detect it by comparing `selectionStart!=selectionEnd` and do something else instead): +Aquí hay un botón que se inserta `"HELLO"` en la posición del cursor y lo coloca inmediatamente después. Si la selección no está vacía, entonces se reemplaza (podemos detectarla comparando `selectionStart!=selectionEnd` y hacer otra cosa en su lugar): ```html run autorun <input id="input" style="width:200px" value="Text Text Text Text Text"> @@ -635,11 +635,11 @@ Here's a button that inserts `"HELLO"` at the cursor position and puts the curso ``` -## Making unselectable +## Haciendo no seleccionable -To make something unselectable, there are three ways: +Para hacer algo no seleccionable, hay tres formas: -1. Use CSS property `user-select: none`. +1. Usar propiedad CSS `user-select: none`. ```html run <style> @@ -650,12 +650,12 @@ To make something unselectable, there are three ways: <div>Selectable <div id="elem">Unselectable</div> Selectable</div> ``` - This doesn't allow the selection to start at `elem`. But the user may start the selection elsewhere and include `elem` into it. + Esto no permite que la selección comience en `elem`. Pero el usuario puede iniciar la selección en otro lugar e incluir `elem` en ella. - Then `elem` will become a part of `document.getSelection()`, so the selection actually happens, but its content is usually ignored in copy-paste. + Entonces, `elem` se convertirá en parte de `document.getSelection )`, por lo que la selección realmente ocurre, pero su contenido generalmente se ignora al copiar y pegar. -2. Prevent default action in `onselectstart` or `mousedown` events. +2. Evita la acción predeterminada en los eventos `onselectstart` o `mousedown`. ```html run <div>Selectable <div id="elem">Unselectable</div> Selectable</div> @@ -665,52 +665,52 @@ To make something unselectable, there are three ways: </script> ``` - This prevents starting the selection on `elem`, but the visitor may start it at another element, then extend to `elem`. + Esto evita que la selección se inicie en `elem`, pero el visitante puede iniciarla en otro elemento y luego extenderla a `elem`. - That's convenient when there's another event handler on the same action that triggers the select (e.g. `mousedown`). So we disable the selection to avoid conflict, still allowing `elem` contents to be copied. +Eso es conveniente cuando hay otro controlador de eventos en la misma acción que activa la selección (por ejemplo, `mousedown`). Así que deshabilitamos la selección para evitar conflictos, permitiendo que se copien los contenidos de `elem`. -3. We can also clear the selection post-factum after it happens with `document.getSelection().empty()`. That's rarely used, as this causes unwanted blinking as the selection appears-disappears. +3. También podemos borrar la selección post-factum después de que suceda con `document.getSelection().Empty ()`. Eso se usa con poca frecuencia, ya que provoca un parpadeo no deseado cuando la selección aparece o desaparece. -## References +## Referencias - [DOM spec: Range](https://dom.spec.whatwg.org/#ranges) - [Selection API](https://www.w3.org/TR/selection-api/#dom-globaleventhandlers-onselectstart) - [HTML spec: APIs for the text control selections](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#textFieldSelection) -## Summary +## Resumen -We covered two different APIs for selections: +Cubrimos dos API diferentes para las selecciones: -1. For document: `Selection` and `Range` objects. -2. For `input`, `textarea`: additional methods and properties. +1. Para el documento: objetos `Selection` y `Range`. +2. Para `input`, `textarea`: métodos y propiedades adicionales. -The second API is very simple, as it works with text. +La segunda API es muy simple, ya que funciona con texto. -The most used recipes are probably: +Las recetas más utilizadas probablemente sean: -1. Getting the selection: - ```js +1. Obteniendo la selección: + ```js let selection = document.getSelection(); - let cloned = /* element to clone the selected nodes to */; + let cloned = /* elemento para clonar los nodos seleccionados para */; - // then apply Range methods to selection.getRangeAt(0) - // or, like here, to all ranges to support multi-select + // luego aplica los métodos Range a selection.getRangeAt (0) + // o, como aquí, a todos los rangos para admitir selección múltiple for (let i = 0; i < selection.rangeCount; i++) { cloned.append(selection.getRangeAt(i).cloneContents()); } ``` -2. Setting the selection: - ```js +2. Configuración de la selección: + ```js let selection = document.getSelection(); - // directly: + // directamente: selection.setBaseAndExtent(...from...to...); - // or we can create a range and: + // o podemos crear un rango y: selection.removeAllRanges(); selection.addRange(range); ``` -And finally, about the cursor. The cursor position in editable elements, like `<textarea>` is always at the start or the end of the selection. We can use it to get cursor position or to move the cursor by setting `elem.selectionStart` and `elem.selectionEnd`. +Y finalmente, sobre el cursor. La posición del cursor en elementos editables, como `<textarea>` está siempre al principio o al final de la selección. Podemos usarlo para obtener la posición del cursor o para mover el cursor configurando `elem.selectionStart` y `elem.selectionEnd`. diff --git a/2-ui/99-ui-misc/02-selection-range/selection-direction-backward.svg b/2-ui/99-ui-misc/02-selection-range/selection-direction-backward.svg index 85615d38f..9fca41073 100644 --- a/2-ui/99-ui-misc/02-selection-range/selection-direction-backward.svg +++ b/2-ui/99-ui-misc/02-selection-range/selection-direction-backward.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="559" height="94" viewBox="0 0 559 94"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><path id="path-1" d="M47 36h262v21H47z"/><mask id="mask-2" width="262" height="21" x="0" y="0" fill="#fff" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox"><use xlink:href="#path-1"/></mask></defs><g id="selection-range" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="selection-direction-backward.svg"><use id="Rectangle" fill="#C9DCEA" stroke="#C9DCEA" stroke-dasharray="5,2" stroke-width="2" mask="url(#mask-2)" xlink:href="#path-1"/><text id="focus" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="25" y="80">focus</tspan></text><text id="anchor" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="277.2" y="80">anchor</tspan></text><path id="Line-Copy" fill="#C06334" fill-rule="nonzero" d="M62 21v6h247v2H62v6l-14-7 14-7z"/><g id="<p>Example:-<i>itali" fill="#AF6E24" fill-rule="nonzero" transform="translate(13 37)"><path id="<p>Example:<i>italic</i>and<b>bold</b></p>" d="M.81 10.51v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08L.81 10.51zm12.718-.36a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zm7.426 7.308l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM37.326 3.4h7.398v1.332H38.82v4.122h5.454v1.332H38.82v4.482h5.994V16h-7.488V3.4zm14.428 7.992L48.28 7h1.8l2.592 3.384L55.336 7h1.62l-3.474 4.32 3.69 4.68h-1.728l-2.862-3.69L49.666 16h-1.638l3.726-4.608zm9.19-3.69a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zM75.516 16v-5.994a9.51 9.51 0 00-.027-.729 2.442 2.442 0 00-.126-.63 1.01 1.01 0 00-.288-.441.734.734 0 00-.495-.162c-.408 0-.753.168-1.035.504-.282.336-.495.756-.639 1.26V16h-1.368V7h.936l.27 1.098h.072c.12-.18.237-.351.351-.513.114-.162.246-.303.396-.423.15-.12.327-.213.531-.279.204-.066.462-.099.774-.099.18 0 .366.027.558.081.192.054.369.138.531.252.162.114.303.267.423.459s.198.426.234.702c.276-.468.585-.834.927-1.098.342-.264.813-.396 1.413-.396.396 0 .717.066.963.198s.438.321.576.567c.138.246.234.54.288.882.054.342.081.723.081 1.143V16h-1.368V9.88c0-.252-.012-.489-.036-.711a2.268 2.268 0 00-.135-.585.967.967 0 00-.27-.396c-.114-.096-.267-.144-.459-.144-.42 0-.774.168-1.062.504-.288.336-.504.816-.648 1.44V16h-1.368zm8.812-5.85a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zM96.542 3.4h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zm19.18 11.466a4.53 4.53 0 01-.765.54 5.645 5.645 0 01-.963.432 7.06 7.06 0 01-1.089.279 6.633 6.633 0 01-1.143.099c-.72 0-1.356-.111-1.908-.333a3.674 3.674 0 01-1.386-.954 4.158 4.158 0 01-.846-1.485c-.192-.576-.288-1.224-.288-1.944 0-.756.105-1.428.315-2.016.21-.588.513-1.08.909-1.476a3.959 3.959 0 011.44-.909c.564-.21 1.194-.315 1.89-.315.504 0 1.002.066 1.494.198a2.95 2.95 0 011.305.747c.378.366.669.87.873 1.512.204.642.276 1.473.216 2.493h-6.966c0 1.08.291 1.887.873 2.421.582.534 1.359.801 2.331.801.324 0 .645-.039.963-.117.318-.078.621-.171.909-.279.288-.108.543-.228.765-.36.222-.132.393-.252.513-.36l.558 1.026zm-3.798-6.894a4.86 4.86 0 00-1.125.126 2.564 2.564 0 00-.936.423c-.27.198-.492.456-.666.774-.174.318-.285.711-.333 1.179h5.598c-.06-.792-.315-1.407-.765-1.845-.45-.438-1.041-.657-1.773-.657zm10.27-.036c0-.384.108-.681.324-.891.216-.21.504-.315.864-.315.384 0 .684.105.9.315.216.21.324.507.324.891 0 .348-.108.636-.324.864-.216.228-.516.342-.9.342-.36 0-.648-.114-.864-.342-.216-.228-.324-.516-.324-.864zm0 7.074c0-.384.108-.681.324-.891.216-.21.504-.315.864-.315.384 0 .684.105.9.315.216.21.324.507.324.891 0 .348-.108.636-.324.864-.216.228-.516.342-.9.342-.36 0-.648-.114-.864-.342-.216-.228-.324-.516-.324-.864zm20.216-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zM155.29 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm8.758 10.962l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM178.89 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zM189.358 7h2.142V5.218l1.404-.396V7h4.806v1.206h-4.806v4.248c0 .876.213 1.524.639 1.944.426.42 1.035.63 1.827.63.54 0 1.011-.102 1.413-.306a7.614 7.614 0 001.089-.666l.468 1.062c-.42.336-.921.606-1.503.81a5.377 5.377 0 01-1.791.306c-.48 0-.933-.069-1.359-.207a3.12 3.12 0 01-1.125-.639 3.06 3.06 0 01-.774-1.107c-.192-.45-.288-.987-.288-1.611V8.206h-2.142V7zm13.186.702a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zM214.542 3.4h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zM226.09 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm15.166 3.942a7.111 7.111 0 00-.891-.243 4.64 4.64 0 00-.945-.099c-1.176 0-2.052.276-2.628.828-.576.552-.864 1.428-.864 2.628 0 .528.084.999.252 1.413.168.414.408.765.72 1.053.312.288.687.51 1.125.666.438.156.921.234 1.449.234.564 0 1.113-.096 1.647-.288.534-.192.981-.444 1.341-.756l.63 1.044a5.095 5.095 0 01-.63.45 6.053 6.053 0 01-1.971.774 6.38 6.38 0 01-1.305.126c-.78 0-1.467-.111-2.061-.333a3.914 3.914 0 01-1.485-.954 4.083 4.083 0 01-.9-1.494 5.818 5.818 0 01-.306-1.935c0-.756.105-1.428.315-2.016.21-.588.513-1.08.909-1.476a3.959 3.959 0 011.44-.909c.564-.21 1.194-.315 1.89-.315.888 0 1.611.078 2.169.234.558.156 1.029.33 1.413.522l-.018.054v2.502h-1.296v-1.71zm4.708 2.124v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zM273.29 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm8.758 10.962l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zm24.05-7.704a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zm8.884-4.806a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.412l.162 1.458h.09c.12-.204.279-.405.477-.603a3.741 3.741 0 011.575-.927 3.593 3.593 0 011.026-.144c.552 0 1.041.06 1.467.18.426.12.78.333 1.062.639.282.306.495.72.639 1.242.144.522.216 1.185.216 1.989V16h-1.404v-4.896c0-.996-.162-1.746-.486-2.25-.324-.504-.912-.756-1.764-.756-.312 0-.615.063-.909.189a3.137 3.137 0 00-.801.495c-.24.204-.45.441-.63.711-.18.27-.312.555-.396.855V16h-1.386v-5.85zm16.408-6.75h2.916v9.522c0 .108.006.243.018.405a20.058 20.058 0 00.108 1.044c.024.174.048.327.072.459h1.206V16h-2.394l-.18-1.386h-.072c-.264.456-.657.837-1.179 1.143-.522.306-1.125.459-1.809.459-1.356 0-2.355-.381-2.997-1.143-.642-.762-.963-1.947-.963-3.555 0-.756.108-1.425.324-2.007a3.873 3.873 0 01.927-1.458c.402-.39.885-.687 1.449-.891.564-.204 1.194-.306 1.89-.306.252 0 .477.006.675.018.198.012.381.03.549.054.168.024.327.057.477.099.15.042.315.087.495.135V4.606h-1.512V3.4zm-1.08 11.61c.732 0 1.308-.189 1.728-.567.42-.378.708-.945.864-1.701V8.566a2.788 2.788 0 00-.891-.396c-.33-.084-.765-.126-1.305-.126-.96 0-1.716.279-2.268.837-.552.558-.828 1.437-.828 2.637 0 .492.045.951.135 1.377.09.426.24.795.45 1.107.21.312.489.558.837.738.348.18.774.27 1.278.27zm19.208-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm11.026-7.11h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.66 7.416l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM389.436 3.4h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.192 3.51c0-1.452.378-2.601 1.134-3.447.756-.846 1.836-1.269 3.24-1.269.756 0 1.407.123 1.953.369s.999.579 1.359.999c.36.42.627.918.801 1.494.174.576.261 1.194.261 1.854 0 .72-.096 1.371-.288 1.953a4.057 4.057 0 01-.846 1.485 3.738 3.738 0 01-1.377.945c-.546.222-1.167.333-1.863.333-.744 0-1.392-.123-1.944-.369a3.815 3.815 0 01-1.368-.999 4.073 4.073 0 01-.801-1.494 6.381 6.381 0 01-.261-1.854zm1.494 0c0 .42.051.84.153 1.26.102.42.267.798.495 1.134.228.336.525.606.891.81.366.204.813.306 1.341.306.96 0 1.683-.297 2.169-.891.486-.594.729-1.467.729-2.619 0-.432-.051-.855-.153-1.269a3.353 3.353 0 00-.504-1.125 2.656 2.656 0 00-.9-.81c-.366-.204-.813-.306-1.341-.306-.96 0-1.68.294-2.16.882-.48.588-.72 1.464-.72 2.628zm11.422-8.1h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zm15.94 0h2.916v9.522c0 .108.006.243.018.405a20.058 20.058 0 00.108 1.044c.024.174.048.327.072.459h1.206V16h-2.394l-.18-1.386h-.072c-.264.456-.657.837-1.179 1.143-.522.306-1.125.459-1.809.459-1.356 0-2.355-.381-2.997-1.143-.642-.762-.963-1.947-.963-3.555 0-.756.108-1.425.324-2.007a3.873 3.873 0 01.927-1.458c.402-.39.885-.687 1.449-.891.564-.204 1.194-.306 1.89-.306.252 0 .477.006.675.018.198.012.381.03.549.054.168.024.327.057.477.099.15.042.315.087.495.135V4.606h-1.512V3.4zm-1.08 11.61c.732 0 1.308-.189 1.728-.567.42-.378.708-.945.864-1.701V8.566a2.788 2.788 0 00-.891-.396c-.33-.084-.765-.126-1.305-.126-.96 0-1.716.279-2.268.837-.552.558-.828 1.437-.828 2.637 0 .492.045.951.135 1.377.09.426.24.795.45 1.107.21.312.489.558.837.738.348.18.774.27 1.278.27zm7.408-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zm3.718.216h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.66 7.416l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zm11.116-4.896v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zm5.41 6.966a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zm7.426 7.308l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896z"/></g><text id="mouse-move-direction" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="93" y="22">mouse move direction</tspan></text></g></g></svg> \ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="559" height="94" viewBox="0 0 559 94"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><path id="path-1" d="M47 36h262v21H47z"/><mask id="mask-2" width="262" height="21" x="0" y="0" fill="#fff" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox"><use xlink:href="#path-1"/></mask></defs><g id="selection-range" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="selection-direction-backward.svg"><use id="Rectangle" fill="#C9DCEA" stroke="#C9DCEA" stroke-dasharray="5,2" stroke-width="2" mask="url(#mask-2)" xlink:href="#path-1"/><text id="focus" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="25" y="80">enfoque</tspan></text><text id="anchor" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="277.2" y="80">ancla</tspan></text><path id="Line-Copy" fill="#C06334" fill-rule="nonzero" d="M62 21v6h247v2H62v6l-14-7 14-7z"/><g id="Group" fill="#AF6E24" fill-rule="nonzero" transform="translate(13 37)"><path id="<p>Example:<i>italic</i>and<b>bold</b></p>" d="M.81 10.51v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08L.81 10.51zm12.718-.36a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zm7.426 7.308l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM37.326 3.4h7.398v1.332H38.82v4.122h5.454v1.332H38.82v4.482h5.994V16h-7.488V3.4zm14.428 7.992L48.28 7h1.8l2.592 3.384L55.336 7h1.62l-3.474 4.32 3.69 4.68h-1.728l-2.862-3.69L49.666 16h-1.638l3.726-4.608zm9.19-3.69a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zM75.516 16v-5.994a9.51 9.51 0 00-.027-.729 2.442 2.442 0 00-.126-.63 1.01 1.01 0 00-.288-.441.734.734 0 00-.495-.162c-.408 0-.753.168-1.035.504-.282.336-.495.756-.639 1.26V16h-1.368V7h.936l.27 1.098h.072c.12-.18.237-.351.351-.513.114-.162.246-.303.396-.423.15-.12.327-.213.531-.279.204-.066.462-.099.774-.099.18 0 .366.027.558.081.192.054.369.138.531.252.162.114.303.267.423.459s.198.426.234.702c.276-.468.585-.834.927-1.098.342-.264.813-.396 1.413-.396.396 0 .717.066.963.198s.438.321.576.567c.138.246.234.54.288.882.054.342.081.723.081 1.143V16h-1.368V9.88c0-.252-.012-.489-.036-.711a2.268 2.268 0 00-.135-.585.967.967 0 00-.27-.396c-.114-.096-.267-.144-.459-.144-.42 0-.774.168-1.062.504-.288.336-.504.816-.648 1.44V16h-1.368zm8.812-5.85a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zM96.542 3.4h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zm19.18 11.466a4.53 4.53 0 01-.765.54 5.645 5.645 0 01-.963.432 7.06 7.06 0 01-1.089.279 6.633 6.633 0 01-1.143.099c-.72 0-1.356-.111-1.908-.333a3.674 3.674 0 01-1.386-.954 4.158 4.158 0 01-.846-1.485c-.192-.576-.288-1.224-.288-1.944 0-.756.105-1.428.315-2.016.21-.588.513-1.08.909-1.476a3.959 3.959 0 011.44-.909c.564-.21 1.194-.315 1.89-.315.504 0 1.002.066 1.494.198a2.95 2.95 0 011.305.747c.378.366.669.87.873 1.512.204.642.276 1.473.216 2.493h-6.966c0 1.08.291 1.887.873 2.421.582.534 1.359.801 2.331.801.324 0 .645-.039.963-.117.318-.078.621-.171.909-.279.288-.108.543-.228.765-.36.222-.132.393-.252.513-.36l.558 1.026zm-3.798-6.894a4.86 4.86 0 00-1.125.126 2.564 2.564 0 00-.936.423c-.27.198-.492.456-.666.774-.174.318-.285.711-.333 1.179h5.598c-.06-.792-.315-1.407-.765-1.845-.45-.438-1.041-.657-1.773-.657zm10.27-.036c0-.384.108-.681.324-.891.216-.21.504-.315.864-.315.384 0 .684.105.9.315.216.21.324.507.324.891 0 .348-.108.636-.324.864-.216.228-.516.342-.9.342-.36 0-.648-.114-.864-.342-.216-.228-.324-.516-.324-.864zm0 7.074c0-.384.108-.681.324-.891.216-.21.504-.315.864-.315.384 0 .684.105.9.315.216.21.324.507.324.891 0 .348-.108.636-.324.864-.216.228-.516.342-.9.342-.36 0-.648-.114-.864-.342-.216-.228-.324-.516-.324-.864zm20.216-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zM155.29 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm8.758 10.962l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM178.89 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zM189.358 7h2.142V5.218l1.404-.396V7h4.806v1.206h-4.806v4.248c0 .876.213 1.524.639 1.944.426.42 1.035.63 1.827.63.54 0 1.011-.102 1.413-.306a7.614 7.614 0 001.089-.666l.468 1.062c-.42.336-.921.606-1.503.81a5.377 5.377 0 01-1.791.306c-.48 0-.933-.069-1.359-.207a3.12 3.12 0 01-1.125-.639 3.06 3.06 0 01-.774-1.107c-.192-.45-.288-.987-.288-1.611V8.206h-2.142V7zm13.186.702a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zM214.542 3.4h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zM226.09 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm15.166 3.942a7.111 7.111 0 00-.891-.243 4.64 4.64 0 00-.945-.099c-1.176 0-2.052.276-2.628.828-.576.552-.864 1.428-.864 2.628 0 .528.084.999.252 1.413.168.414.408.765.72 1.053.312.288.687.51 1.125.666.438.156.921.234 1.449.234.564 0 1.113-.096 1.647-.288.534-.192.981-.444 1.341-.756l.63 1.044a5.095 5.095 0 01-.63.45 6.053 6.053 0 01-1.971.774 6.38 6.38 0 01-1.305.126c-.78 0-1.467-.111-2.061-.333a3.914 3.914 0 01-1.485-.954 4.083 4.083 0 01-.9-1.494 5.818 5.818 0 01-.306-1.935c0-.756.105-1.428.315-2.016.21-.588.513-1.08.909-1.476a3.959 3.959 0 011.44-.909c.564-.21 1.194-.315 1.89-.315.888 0 1.611.078 2.169.234.558.156 1.029.33 1.413.522l-.018.054v2.502h-1.296v-1.71zm4.708 2.124v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zM273.29 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm8.758 10.962l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zm24.05-7.704a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zm8.884-4.806a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.412l.162 1.458h.09c.12-.204.279-.405.477-.603a3.741 3.741 0 011.575-.927 3.593 3.593 0 011.026-.144c.552 0 1.041.06 1.467.18.426.12.78.333 1.062.639.282.306.495.72.639 1.242.144.522.216 1.185.216 1.989V16h-1.404v-4.896c0-.996-.162-1.746-.486-2.25-.324-.504-.912-.756-1.764-.756-.312 0-.615.063-.909.189a3.137 3.137 0 00-.801.495c-.24.204-.45.441-.63.711-.18.27-.312.555-.396.855V16h-1.386v-5.85zm16.408-6.75h2.916v9.522c0 .108.006.243.018.405a20.058 20.058 0 00.108 1.044c.024.174.048.327.072.459h1.206V16h-2.394l-.18-1.386h-.072c-.264.456-.657.837-1.179 1.143-.522.306-1.125.459-1.809.459-1.356 0-2.355-.381-2.997-1.143-.642-.762-.963-1.947-.963-3.555 0-.756.108-1.425.324-2.007a3.873 3.873 0 01.927-1.458c.402-.39.885-.687 1.449-.891.564-.204 1.194-.306 1.89-.306.252 0 .477.006.675.018.198.012.381.03.549.054.168.024.327.057.477.099.15.042.315.087.495.135V4.606h-1.512V3.4zm-1.08 11.61c.732 0 1.308-.189 1.728-.567.42-.378.708-.945.864-1.701V8.566a2.788 2.788 0 00-.891-.396c-.33-.084-.765-.126-1.305-.126-.96 0-1.716.279-2.268.837-.552.558-.828 1.437-.828 2.637 0 .492.045.951.135 1.377.09.426.24.795.45 1.107.21.312.489.558.837.738.348.18.774.27 1.278.27zm19.208-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm11.026-7.11h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.66 7.416l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM389.436 3.4h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.192 3.51c0-1.452.378-2.601 1.134-3.447.756-.846 1.836-1.269 3.24-1.269.756 0 1.407.123 1.953.369s.999.579 1.359.999c.36.42.627.918.801 1.494.174.576.261 1.194.261 1.854 0 .72-.096 1.371-.288 1.953a4.057 4.057 0 01-.846 1.485 3.738 3.738 0 01-1.377.945c-.546.222-1.167.333-1.863.333-.744 0-1.392-.123-1.944-.369a3.815 3.815 0 01-1.368-.999 4.073 4.073 0 01-.801-1.494 6.381 6.381 0 01-.261-1.854zm1.494 0c0 .42.051.84.153 1.26.102.42.267.798.495 1.134.228.336.525.606.891.81.366.204.813.306 1.341.306.96 0 1.683-.297 2.169-.891.486-.594.729-1.467.729-2.619 0-.432-.051-.855-.153-1.269a3.353 3.353 0 00-.504-1.125 2.656 2.656 0 00-.9-.81c-.366-.204-.813-.306-1.341-.306-.96 0-1.68.294-2.16.882-.48.588-.72 1.464-.72 2.628zm11.422-8.1h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zm15.94 0h2.916v9.522c0 .108.006.243.018.405a20.058 20.058 0 00.108 1.044c.024.174.048.327.072.459h1.206V16h-2.394l-.18-1.386h-.072c-.264.456-.657.837-1.179 1.143-.522.306-1.125.459-1.809.459-1.356 0-2.355-.381-2.997-1.143-.642-.762-.963-1.947-.963-3.555 0-.756.108-1.425.324-2.007a3.873 3.873 0 01.927-1.458c.402-.39.885-.687 1.449-.891.564-.204 1.194-.306 1.89-.306.252 0 .477.006.675.018.198.012.381.03.549.054.168.024.327.057.477.099.15.042.315.087.495.135V4.606h-1.512V3.4zm-1.08 11.61c.732 0 1.308-.189 1.728-.567.42-.378.708-.945.864-1.701V8.566a2.788 2.788 0 00-.891-.396c-.33-.084-.765-.126-1.305-.126-.96 0-1.716.279-2.268.837-.552.558-.828 1.437-.828 2.637 0 .492.045.951.135 1.377.09.426.24.795.45 1.107.21.312.489.558.837.738.348.18.774.27 1.278.27zm7.408-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zm3.718.216h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.66 7.416l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zm11.116-4.896v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zm5.41 6.966a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zm7.426 7.308l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896z"/></g><text id="mouse-move-direction" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="93" y="22">mouse move direction</tspan></text></g></g></svg> \ No newline at end of file diff --git a/2-ui/99-ui-misc/02-selection-range/selection-direction-forward.svg b/2-ui/99-ui-misc/02-selection-range/selection-direction-forward.svg index 511b00a26..663078e53 100644 --- a/2-ui/99-ui-misc/02-selection-range/selection-direction-forward.svg +++ b/2-ui/99-ui-misc/02-selection-range/selection-direction-forward.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="563" height="94" viewBox="0 0 563 94"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><path id="path-1" d="M47 38h262v21H47z"/><mask id="mask-2" width="262" height="21" x="0" y="0" fill="#fff" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox"><use xlink:href="#path-1"/></mask></defs><g id="selection-range" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="selection-direction-forward.svg"><text id="anchor" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="18.2" y="78">anchor</tspan></text><text id="focus" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="285" y="78">focus</tspan></text><use id="Rectangle" fill="#C9DCEA" stroke="#C9DCEA" stroke-dasharray="5,2" stroke-width="2" mask="url(#mask-2)" xlink:href="#path-1"/><g id="<p>Example:-<i>itali" fill="#AF6E24" fill-rule="nonzero" transform="translate(13 39)"><path id="<p>Example:<i>italic</i>and<b>bold</b></p>" d="M.81 10.51v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08L.81 10.51zm12.718-.36a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zm7.426 7.308l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM37.326 3.4h7.398v1.332H38.82v4.122h5.454v1.332H38.82v4.482h5.994V16h-7.488V3.4zm14.428 7.992L48.28 7h1.8l2.592 3.384L55.336 7h1.62l-3.474 4.32 3.69 4.68h-1.728l-2.862-3.69L49.666 16h-1.638l3.726-4.608zm9.19-3.69a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zM75.516 16v-5.994a9.51 9.51 0 00-.027-.729 2.442 2.442 0 00-.126-.63 1.01 1.01 0 00-.288-.441.734.734 0 00-.495-.162c-.408 0-.753.168-1.035.504-.282.336-.495.756-.639 1.26V16h-1.368V7h.936l.27 1.098h.072c.12-.18.237-.351.351-.513.114-.162.246-.303.396-.423.15-.12.327-.213.531-.279.204-.066.462-.099.774-.099.18 0 .366.027.558.081.192.054.369.138.531.252.162.114.303.267.423.459s.198.426.234.702c.276-.468.585-.834.927-1.098.342-.264.813-.396 1.413-.396.396 0 .717.066.963.198s.438.321.576.567c.138.246.234.54.288.882.054.342.081.723.081 1.143V16h-1.368V9.88c0-.252-.012-.489-.036-.711a2.268 2.268 0 00-.135-.585.967.967 0 00-.27-.396c-.114-.096-.267-.144-.459-.144-.42 0-.774.168-1.062.504-.288.336-.504.816-.648 1.44V16h-1.368zm8.812-5.85a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zM96.542 3.4h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zm19.18 11.466a4.53 4.53 0 01-.765.54 5.645 5.645 0 01-.963.432 7.06 7.06 0 01-1.089.279 6.633 6.633 0 01-1.143.099c-.72 0-1.356-.111-1.908-.333a3.674 3.674 0 01-1.386-.954 4.158 4.158 0 01-.846-1.485c-.192-.576-.288-1.224-.288-1.944 0-.756.105-1.428.315-2.016.21-.588.513-1.08.909-1.476a3.959 3.959 0 011.44-.909c.564-.21 1.194-.315 1.89-.315.504 0 1.002.066 1.494.198a2.95 2.95 0 011.305.747c.378.366.669.87.873 1.512.204.642.276 1.473.216 2.493h-6.966c0 1.08.291 1.887.873 2.421.582.534 1.359.801 2.331.801.324 0 .645-.039.963-.117.318-.078.621-.171.909-.279.288-.108.543-.228.765-.36.222-.132.393-.252.513-.36l.558 1.026zm-3.798-6.894a4.86 4.86 0 00-1.125.126 2.564 2.564 0 00-.936.423c-.27.198-.492.456-.666.774-.174.318-.285.711-.333 1.179h5.598c-.06-.792-.315-1.407-.765-1.845-.45-.438-1.041-.657-1.773-.657zm10.27-.036c0-.384.108-.681.324-.891.216-.21.504-.315.864-.315.384 0 .684.105.9.315.216.21.324.507.324.891 0 .348-.108.636-.324.864-.216.228-.516.342-.9.342-.36 0-.648-.114-.864-.342-.216-.228-.324-.516-.324-.864zm0 7.074c0-.384.108-.681.324-.891.216-.21.504-.315.864-.315.384 0 .684.105.9.315.216.21.324.507.324.891 0 .348-.108.636-.324.864-.216.228-.516.342-.9.342-.36 0-.648-.114-.864-.342-.216-.228-.324-.516-.324-.864zm20.216-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zM155.29 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm8.758 10.962l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM178.89 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zM189.358 7h2.142V5.218l1.404-.396V7h4.806v1.206h-4.806v4.248c0 .876.213 1.524.639 1.944.426.42 1.035.63 1.827.63.54 0 1.011-.102 1.413-.306a7.614 7.614 0 001.089-.666l.468 1.062c-.42.336-.921.606-1.503.81a5.377 5.377 0 01-1.791.306c-.48 0-.933-.069-1.359-.207a3.12 3.12 0 01-1.125-.639 3.06 3.06 0 01-.774-1.107c-.192-.45-.288-.987-.288-1.611V8.206h-2.142V7zm13.186.702a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zM214.542 3.4h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zM226.09 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm15.166 3.942a7.111 7.111 0 00-.891-.243 4.64 4.64 0 00-.945-.099c-1.176 0-2.052.276-2.628.828-.576.552-.864 1.428-.864 2.628 0 .528.084.999.252 1.413.168.414.408.765.72 1.053.312.288.687.51 1.125.666.438.156.921.234 1.449.234.564 0 1.113-.096 1.647-.288.534-.192.981-.444 1.341-.756l.63 1.044a5.095 5.095 0 01-.63.45 6.053 6.053 0 01-1.971.774 6.38 6.38 0 01-1.305.126c-.78 0-1.467-.111-2.061-.333a3.914 3.914 0 01-1.485-.954 4.083 4.083 0 01-.9-1.494 5.818 5.818 0 01-.306-1.935c0-.756.105-1.428.315-2.016.21-.588.513-1.08.909-1.476a3.959 3.959 0 011.44-.909c.564-.21 1.194-.315 1.89-.315.888 0 1.611.078 2.169.234.558.156 1.029.33 1.413.522l-.018.054v2.502h-1.296v-1.71zm4.708 2.124v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zM273.29 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm8.758 10.962l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zm24.05-7.704a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zm8.884-4.806a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.412l.162 1.458h.09c.12-.204.279-.405.477-.603a3.741 3.741 0 011.575-.927 3.593 3.593 0 011.026-.144c.552 0 1.041.06 1.467.18.426.12.78.333 1.062.639.282.306.495.72.639 1.242.144.522.216 1.185.216 1.989V16h-1.404v-4.896c0-.996-.162-1.746-.486-2.25-.324-.504-.912-.756-1.764-.756-.312 0-.615.063-.909.189a3.137 3.137 0 00-.801.495c-.24.204-.45.441-.63.711-.18.27-.312.555-.396.855V16h-1.386v-5.85zm16.408-6.75h2.916v9.522c0 .108.006.243.018.405a20.058 20.058 0 00.108 1.044c.024.174.048.327.072.459h1.206V16h-2.394l-.18-1.386h-.072c-.264.456-.657.837-1.179 1.143-.522.306-1.125.459-1.809.459-1.356 0-2.355-.381-2.997-1.143-.642-.762-.963-1.947-.963-3.555 0-.756.108-1.425.324-2.007a3.873 3.873 0 01.927-1.458c.402-.39.885-.687 1.449-.891.564-.204 1.194-.306 1.89-.306.252 0 .477.006.675.018.198.012.381.03.549.054.168.024.327.057.477.099.15.042.315.087.495.135V4.606h-1.512V3.4zm-1.08 11.61c.732 0 1.308-.189 1.728-.567.42-.378.708-.945.864-1.701V8.566a2.788 2.788 0 00-.891-.396c-.33-.084-.765-.126-1.305-.126-.96 0-1.716.279-2.268.837-.552.558-.828 1.437-.828 2.637 0 .492.045.951.135 1.377.09.426.24.795.45 1.107.21.312.489.558.837.738.348.18.774.27 1.278.27zm19.208-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm11.026-7.11h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.66 7.416l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM389.436 3.4h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.192 3.51c0-1.452.378-2.601 1.134-3.447.756-.846 1.836-1.269 3.24-1.269.756 0 1.407.123 1.953.369s.999.579 1.359.999c.36.42.627.918.801 1.494.174.576.261 1.194.261 1.854 0 .72-.096 1.371-.288 1.953a4.057 4.057 0 01-.846 1.485 3.738 3.738 0 01-1.377.945c-.546.222-1.167.333-1.863.333-.744 0-1.392-.123-1.944-.369a3.815 3.815 0 01-1.368-.999 4.073 4.073 0 01-.801-1.494 6.381 6.381 0 01-.261-1.854zm1.494 0c0 .42.051.84.153 1.26.102.42.267.798.495 1.134.228.336.525.606.891.81.366.204.813.306 1.341.306.96 0 1.683-.297 2.169-.891.486-.594.729-1.467.729-2.619 0-.432-.051-.855-.153-1.269a3.353 3.353 0 00-.504-1.125 2.656 2.656 0 00-.9-.81c-.366-.204-.813-.306-1.341-.306-.96 0-1.68.294-2.16.882-.48.588-.72 1.464-.72 2.628zm11.422-8.1h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zm15.94 0h2.916v9.522c0 .108.006.243.018.405a20.058 20.058 0 00.108 1.044c.024.174.048.327.072.459h1.206V16h-2.394l-.18-1.386h-.072c-.264.456-.657.837-1.179 1.143-.522.306-1.125.459-1.809.459-1.356 0-2.355-.381-2.997-1.143-.642-.762-.963-1.947-.963-3.555 0-.756.108-1.425.324-2.007a3.873 3.873 0 01.927-1.458c.402-.39.885-.687 1.449-.891.564-.204 1.194-.306 1.89-.306.252 0 .477.006.675.018.198.012.381.03.549.054.168.024.327.057.477.099.15.042.315.087.495.135V4.606h-1.512V3.4zm-1.08 11.61c.732 0 1.308-.189 1.728-.567.42-.378.708-.945.864-1.701V8.566a2.788 2.788 0 00-.891-.396c-.33-.084-.765-.126-1.305-.126-.96 0-1.716.279-2.268.837-.552.558-.828 1.437-.828 2.637 0 .492.045.951.135 1.377.09.426.24.795.45 1.107.21.312.489.558.837.738.348.18.774.27 1.278.27zm7.408-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zm3.718.216h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.66 7.416l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zm11.116-4.896v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zm5.41 6.966a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zm7.426 7.308l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896z"/></g><path id="Line-Copy" fill="#C06334" fill-rule="nonzero" d="M296 21l14 7-14 7-.001-6H46v-2h249.999l.001-6z"/><text id="mouse-move-direction" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="93" y="22">mouse move direction</tspan></text></g></g></svg> \ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="563" height="94" viewBox="0 0 563 94"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><path id="path-1" d="M47 38h262v21H47z"/><mask id="mask-2" width="262" height="21" x="0" y="0" fill="#fff" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox"><use xlink:href="#path-1"/></mask></defs><g id="selection-range" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="selection-direction-forward.svg"><text id="anchor" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="18.2" y="78">ancla</tspan></text><text id="focus" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="285" y="78">enfoque</tspan></text><use id="Rectangle" fill="#C9DCEA" stroke="#C9DCEA" stroke-dasharray="5,2" stroke-width="2" mask="url(#mask-2)" xlink:href="#path-1"/><g id="Group" fill="#AF6E24" fill-rule="nonzero" transform="translate(13 39)"><path id="<p>Example:<i>italic</i>and<b>bold</b></p>" d="M.81 10.51v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08L.81 10.51zm12.718-.36a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zm7.426 7.308l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM37.326 3.4h7.398v1.332H38.82v4.122h5.454v1.332H38.82v4.482h5.994V16h-7.488V3.4zm14.428 7.992L48.28 7h1.8l2.592 3.384L55.336 7h1.62l-3.474 4.32 3.69 4.68h-1.728l-2.862-3.69L49.666 16h-1.638l3.726-4.608zm9.19-3.69a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zM75.516 16v-5.994a9.51 9.51 0 00-.027-.729 2.442 2.442 0 00-.126-.63 1.01 1.01 0 00-.288-.441.734.734 0 00-.495-.162c-.408 0-.753.168-1.035.504-.282.336-.495.756-.639 1.26V16h-1.368V7h.936l.27 1.098h.072c.12-.18.237-.351.351-.513.114-.162.246-.303.396-.423.15-.12.327-.213.531-.279.204-.066.462-.099.774-.099.18 0 .366.027.558.081.192.054.369.138.531.252.162.114.303.267.423.459s.198.426.234.702c.276-.468.585-.834.927-1.098.342-.264.813-.396 1.413-.396.396 0 .717.066.963.198s.438.321.576.567c.138.246.234.54.288.882.054.342.081.723.081 1.143V16h-1.368V9.88c0-.252-.012-.489-.036-.711a2.268 2.268 0 00-.135-.585.967.967 0 00-.27-.396c-.114-.096-.267-.144-.459-.144-.42 0-.774.168-1.062.504-.288.336-.504.816-.648 1.44V16h-1.368zm8.812-5.85a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zM96.542 3.4h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zm19.18 11.466a4.53 4.53 0 01-.765.54 5.645 5.645 0 01-.963.432 7.06 7.06 0 01-1.089.279 6.633 6.633 0 01-1.143.099c-.72 0-1.356-.111-1.908-.333a3.674 3.674 0 01-1.386-.954 4.158 4.158 0 01-.846-1.485c-.192-.576-.288-1.224-.288-1.944 0-.756.105-1.428.315-2.016.21-.588.513-1.08.909-1.476a3.959 3.959 0 011.44-.909c.564-.21 1.194-.315 1.89-.315.504 0 1.002.066 1.494.198a2.95 2.95 0 011.305.747c.378.366.669.87.873 1.512.204.642.276 1.473.216 2.493h-6.966c0 1.08.291 1.887.873 2.421.582.534 1.359.801 2.331.801.324 0 .645-.039.963-.117.318-.078.621-.171.909-.279.288-.108.543-.228.765-.36.222-.132.393-.252.513-.36l.558 1.026zm-3.798-6.894a4.86 4.86 0 00-1.125.126 2.564 2.564 0 00-.936.423c-.27.198-.492.456-.666.774-.174.318-.285.711-.333 1.179h5.598c-.06-.792-.315-1.407-.765-1.845-.45-.438-1.041-.657-1.773-.657zm10.27-.036c0-.384.108-.681.324-.891.216-.21.504-.315.864-.315.384 0 .684.105.9.315.216.21.324.507.324.891 0 .348-.108.636-.324.864-.216.228-.516.342-.9.342-.36 0-.648-.114-.864-.342-.216-.228-.324-.516-.324-.864zm0 7.074c0-.384.108-.681.324-.891.216-.21.504-.315.864-.315.384 0 .684.105.9.315.216.21.324.507.324.891 0 .348-.108.636-.324.864-.216.228-.516.342-.9.342-.36 0-.648-.114-.864-.342-.216-.228-.324-.516-.324-.864zm20.216-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zM155.29 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm8.758 10.962l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM178.89 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zM189.358 7h2.142V5.218l1.404-.396V7h4.806v1.206h-4.806v4.248c0 .876.213 1.524.639 1.944.426.42 1.035.63 1.827.63.54 0 1.011-.102 1.413-.306a7.614 7.614 0 001.089-.666l.468 1.062c-.42.336-.921.606-1.503.81a5.377 5.377 0 01-1.791.306c-.48 0-.933-.069-1.359-.207a3.12 3.12 0 01-1.125-.639 3.06 3.06 0 01-.774-1.107c-.192-.45-.288-.987-.288-1.611V8.206h-2.142V7zm13.186.702a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zM214.542 3.4h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zM226.09 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm15.166 3.942a7.111 7.111 0 00-.891-.243 4.64 4.64 0 00-.945-.099c-1.176 0-2.052.276-2.628.828-.576.552-.864 1.428-.864 2.628 0 .528.084.999.252 1.413.168.414.408.765.72 1.053.312.288.687.51 1.125.666.438.156.921.234 1.449.234.564 0 1.113-.096 1.647-.288.534-.192.981-.444 1.341-.756l.63 1.044a5.095 5.095 0 01-.63.45 6.053 6.053 0 01-1.971.774 6.38 6.38 0 01-1.305.126c-.78 0-1.467-.111-2.061-.333a3.914 3.914 0 01-1.485-.954 4.083 4.083 0 01-.9-1.494 5.818 5.818 0 01-.306-1.935c0-.756.105-1.428.315-2.016.21-.588.513-1.08.909-1.476a3.959 3.959 0 011.44-.909c.564-.21 1.194-.315 1.89-.315.888 0 1.611.078 2.169.234.558.156 1.029.33 1.413.522l-.018.054v2.502h-1.296v-1.71zm4.708 2.124v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zM273.29 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm8.758 10.962l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zm24.05-7.704a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zm8.884-4.806a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.412l.162 1.458h.09c.12-.204.279-.405.477-.603a3.741 3.741 0 011.575-.927 3.593 3.593 0 011.026-.144c.552 0 1.041.06 1.467.18.426.12.78.333 1.062.639.282.306.495.72.639 1.242.144.522.216 1.185.216 1.989V16h-1.404v-4.896c0-.996-.162-1.746-.486-2.25-.324-.504-.912-.756-1.764-.756-.312 0-.615.063-.909.189a3.137 3.137 0 00-.801.495c-.24.204-.45.441-.63.711-.18.27-.312.555-.396.855V16h-1.386v-5.85zm16.408-6.75h2.916v9.522c0 .108.006.243.018.405a20.058 20.058 0 00.108 1.044c.024.174.048.327.072.459h1.206V16h-2.394l-.18-1.386h-.072c-.264.456-.657.837-1.179 1.143-.522.306-1.125.459-1.809.459-1.356 0-2.355-.381-2.997-1.143-.642-.762-.963-1.947-.963-3.555 0-.756.108-1.425.324-2.007a3.873 3.873 0 01.927-1.458c.402-.39.885-.687 1.449-.891.564-.204 1.194-.306 1.89-.306.252 0 .477.006.675.018.198.012.381.03.549.054.168.024.327.057.477.099.15.042.315.087.495.135V4.606h-1.512V3.4zm-1.08 11.61c.732 0 1.308-.189 1.728-.567.42-.378.708-.945.864-1.701V8.566a2.788 2.788 0 00-.891-.396c-.33-.084-.765-.126-1.305-.126-.96 0-1.716.279-2.268.837-.552.558-.828 1.437-.828 2.637 0 .492.045.951.135 1.377.09.426.24.795.45 1.107.21.312.489.558.837.738.348.18.774.27 1.278.27zm19.208-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm11.026-7.11h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.66 7.416l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM389.436 3.4h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.192 3.51c0-1.452.378-2.601 1.134-3.447.756-.846 1.836-1.269 3.24-1.269.756 0 1.407.123 1.953.369s.999.579 1.359.999c.36.42.627.918.801 1.494.174.576.261 1.194.261 1.854 0 .72-.096 1.371-.288 1.953a4.057 4.057 0 01-.846 1.485 3.738 3.738 0 01-1.377.945c-.546.222-1.167.333-1.863.333-.744 0-1.392-.123-1.944-.369a3.815 3.815 0 01-1.368-.999 4.073 4.073 0 01-.801-1.494 6.381 6.381 0 01-.261-1.854zm1.494 0c0 .42.051.84.153 1.26.102.42.267.798.495 1.134.228.336.525.606.891.81.366.204.813.306 1.341.306.96 0 1.683-.297 2.169-.891.486-.594.729-1.467.729-2.619 0-.432-.051-.855-.153-1.269a3.353 3.353 0 00-.504-1.125 2.656 2.656 0 00-.9-.81c-.366-.204-.813-.306-1.341-.306-.96 0-1.68.294-2.16.882-.48.588-.72 1.464-.72 2.628zm11.422-8.1h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zm15.94 0h2.916v9.522c0 .108.006.243.018.405a20.058 20.058 0 00.108 1.044c.024.174.048.327.072.459h1.206V16h-2.394l-.18-1.386h-.072c-.264.456-.657.837-1.179 1.143-.522.306-1.125.459-1.809.459-1.356 0-2.355-.381-2.997-1.143-.642-.762-.963-1.947-.963-3.555 0-.756.108-1.425.324-2.007a3.873 3.873 0 01.927-1.458c.402-.39.885-.687 1.449-.891.564-.204 1.194-.306 1.89-.306.252 0 .477.006.675.018.198.012.381.03.549.054.168.024.327.057.477.099.15.042.315.087.495.135V4.606h-1.512V3.4zm-1.08 11.61c.732 0 1.308-.189 1.728-.567.42-.378.708-.945.864-1.701V8.566a2.788 2.788 0 00-.891-.396c-.33-.084-.765-.126-1.305-.126-.96 0-1.716.279-2.268.837-.552.558-.828 1.437-.828 2.637 0 .492.045.951.135 1.377.09.426.24.795.45 1.107.21.312.489.558.837.738.348.18.774.27 1.278.27zm7.408-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zm3.718.216h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.66 7.416l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zm11.116-4.896v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zm5.41 6.966a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zm7.426 7.308l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896z"/></g><path id="Line-Copy" fill="#C06334" fill-rule="nonzero" d="M296 21l14 7-14 7-.001-6H46v-2h249.999l.001-6z"/><text id="mouse-move-direction" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="93" y="22">mouse move direction</tspan></text></g></g></svg> \ No newline at end of file diff --git a/2-ui/99-ui-misc/02-selection-range/selection-firefox.svg b/2-ui/99-ui-misc/02-selection-range/selection-firefox.svg index aa7ff1eb7..fb499304d 100644 --- a/2-ui/99-ui-misc/02-selection-range/selection-firefox.svg +++ b/2-ui/99-ui-misc/02-selection-range/selection-firefox.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="556" height="428" viewBox="0 0 556 428"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><path id="rect-1" d="M48 88h448v281H48z"/></defs><g id="selection-range" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="selection-firefox.svg"><g id="Bitmap"><image width="448" height="281" x="48" y="88" xlink:href="data:image/png;base64,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ARIgARLIdALZmQ6A/ScBEiABEiABEiABEiABEiABEiABEog/gZyNGzfKvn374l8TayABEiABEiABEiABEiABEiABEiABEshYAtlcASNjrz07TgIkQAIkQAIkQAIkQAIkQAIkQAKBEcgyBIg8Oh8JjDcrIgESIAESIAESIAESIAESIAESIIGMJEAfEBl52dlpEiABEiABEiABEiABEiABEiABEgiWAAWIYHmzNhIgARIgARIgARIgARIgARIgARLISALZnH6RkdednSYBEiABEiABEiABEiABEiABEiCBQAnQAiJQ3KyMBEiABEiABEiABEiABEiABEiABDKTQPaBAwcys+fsNQmQAAmQAAmQAAmQAAmQAAmQAAmQQGAEsvKMEFhtrIgESIAESIAESIAESIAESIAESIAESCAjCWRTf8jI685OkwAJkAAJkAAJkAAJkAAJkAAJkECgBOiEMlDcrIwESIAESIAESIAESIAESIAESIAEMpMALSAy87qz1yRAAiRAAiRAAiRAAiRAAiRAAiQQKAGughEoblZGAiRAAiRAAiRAAiRAAiRAAiRAAplJgAJEZl539poESIAESIAESIAESIAESIAESIAEAiVAASJQ3KyMBEiABEiABEiABEiABEiABEiABDKTAAWIzLzu7DUJkAAJkAAJkAAJkAAJkAAJkAAJBEqAq2AEipuVkQAJkAAJkAAJkAAJkAAJkAAJkEBmEqAFRGZed/aaBEiABEiABEiABEiABEiABEiABAIlQAEiUNysjARIgARIgARIgARIgARIgARIgAQykwAFiMy87uw1CZAACZAACZAACZAACZAACZAACQRKgAJEoLhZGQmQAAmQAAmQAAmQAAmQAAmQAAlkJgEKEJl53dlrEiABEiABEiABEiABEiABEiABEgiUAAWIQHGzMhIgARIgARIgARIgARIgARIgARLITAIUIDLzurPXJEACJEACJEACJEACJEACJEACJBAoAQoQgeJmZSRAAiRAAiRAAiRAAiRAAiRAAiSQmQQoQGTmdWevSYAESIAESIAESIAESIAESIAESCBQAtl5eXmBVsjKSIAESIAESIAESIAESIAESIAESIAEMo9AdlZWVub1mj0mARIgARIgARIgARIgARIgARIgARIIlACnYASKm5WRAAmQAAmQAAmQAAmQAAmQAAmQQGYSoACRmdedvSYBEiABEiABEiABEiABEiABEiCBQAnQB0SguFkZCZAACZAACZAACZAACZAACZAACWQmAVpAZOZ1Z69JgARIgARIgARIgARIgARIgARIIFACdEIZKG5WRgIkQAIkQAIkQAIkQAIkQAIkQAKZSYAWEJl53dlrEiABEiABEiABEiABEiABEiABEgiUAH1ABIqblZEACZAACZAACZAACZAACZAACZBAZhKgBURmXnf2mgRIgARIgARIgARIgARIgARIgAQCJUABIlDcrIwESIAESIAESIAESIAESIAESIAEMpMAnVBm5nVnr0mABEiABEiABEiABEiABEiABEggUAK0gAgUNysjARIgARIgARIgARIgARIgARIggcwkkH3gwIHM7Dl7TQIkQAIkQAIkQAIkQAIkQAIkQAIkEBiBrDwjBFYbKyIBEiABEiABEiABEiABEiABEiABEshIAlyGMyMvOztNAiRAAiRAAiRAAiRAAiRAAiRAAsESoBPKYHmzNhIgARIgARIgARIgARIgARIgARLISAK0gMjIy85OkwAJkAAJkAAJkAAJkAAJkAAJkECwBLgKRrC8WRsJkAAJkAAJkAAJkAAJkAAJkAAJZCQBChAZednZaRIgARIgARIgARIgARIgARIgARIIlgAFiGB5szYSIAESIAESIAESIAESIAESIAESyEgCFCAy8rKz0yRAAiRAAiRAAiRAAiRAAiRAAiQQLAGughEsb9ZGAiRAAiRAAiRAAiRAAiRAAiRAAhlJgBYQGXnZ2WkSIAESIAESIAESIAESIAESIAESCJYABYhgebM2EiABEiABEiABEiABEiABEiABEshIAhQgMvKys9MkQAIkQAIkQAIkQAIkQAIkQAIkECwBChDB8mZtJEACJEACJEACJEACJEACJEACJJCRBChAZORlZ6dJgARIgARIgARIgARIgARIgARIIFgCFCCC5c3aSIAESIAESIAESIAESIAESIAESCAjCVCAyMjLzk6TAAmQAAmQAAmQAAmQAAmQAAmQQLAEKEAEy5u1kQAJkAAJkAAJkAAJkAAJkAAJkEBGEqAAkZGXnZ0mARIgARIgARIgARIgARIgARIggWAJZOfl5QVbI2sjARIgARIgARIgARIgARIgARIgARLIOALZWVlZGddpdpgESIAESIAESIAESIAESIAESIAESCBYApyCESxv1kYCJEACJEACJEACJEACJEACJEACGUmAAkRGXnZ2mgRIgARIgARIgARIgARIgARIgASCJUAfEMHyZm0kQAIkQAIkQAIkQAIkQAIkQAIkkJEEaAGRkZednSYBEiABEiABEiABEiABEiABEiCBYAnQCWWwvFkbCZAACZAACZAACZAACZAACZAACWQkAVpAZORlZ6dJgARIgARIgARIgARIgARIgARIIFgC9AERLG/WRgIkQAIkQAIkQAIkQAIkQAIkQAIZSYAWEBl52dlpEiABEiABEiABEiABEiABEiABEgiWAAWIYHmzNhIgARIgARIgARIgARIgARIgARLISAJ0QpmRl52dJgESIAESIAESIAESIAESIAESIIFgCdACIljerI0ESIAESIAESIAESIAESIAESIAEMpJA9oEDBzKy4+w0CZAACZAACZAACZAACZAACZAACZBAcASy8owQXHWsiQRIgARIgARIgARIgARIgARIgARIIBMJcBnOTLzq7DMJkAAJkAAJkAAJkAAJkAAJkAAJBEyATigDBs7qSIAESIAESIAESIAESIAESIAESCATCdACIhOvOvtMAiRAAiRAAiRAAiRAAiRAAiRAAgET4CoYAQNndSRAAiRAAiRAAiRAAiRAAiRAAiSQiQRyEt3p9xYmugWsnwRIIFUJdGuQqi1nu0mABEiABEiABEiABEgg8wjQAiLzrjl7TAIkQAIkQAIkQAIkQAIkQAIkQAKBE6AAEThyVkgCJEACJEACJEACJEACJEACJEACmUeAq2Bk3jVnj0mABEiABEiABEiABEiABEiABEggcAK0gAgcOSskARIgARIgARIgARIgARIgARIggcwjQAEi8645e0wCJEACJEACJEACJEACJEACJEACgROgABE4clZIAiRAAiRAAiRAAiRAAiRAAiRAAplHgAJE5l1z9pgESIAESIAESIAESIAESIAESIAEAidAASJw5KyQBEiABEiABEiABEiABEiABEiABDKPAAWIzLvm7DEJkAAJkAAJkAAJkAAJkAAJkAAJBE6AAkTgyFkhCZAACZAACZAACZAACZAACZAACWQeAQoQmXfN2WMSIAESIAESIAESIAESIAESIAESCJwABYjAkbNCEiABEiABEiABEiABEiABEiABEsg8Atl5eXmZ12v2mARIgARIgARIgARI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CBABIkAEiAARIAJEgAgQASJQUQjQBkRFVReZJQJEgAgQASJABIgAESACRIAIEAEiUJkIUABRmfVGrokAESACRIAIEAEiQASIABEgAkSACFQUAvXsEYxMRXFMZokAESACRIAIEAEiQASIABEgAkSACBCBikOAGhAVV2VkmAgQASJABIgAESACRIAIEAEiQASIQOUhUH/x4sWVxzU5JgJEgAgQASJABIgAESACRIAIEAEiQAQqCgEewaio6iKzRIAIEAEiQASIABEgAkSACBABIkAEKhOB+jQBUZkVR66JABEgAkSACBABIkAEiAARIAJEgAhUEgL169WrV0n8klciQASIABEgAkSACBABIkAEiAARIAJEoAIRoAZEBVYaWSYCRIAIEAEiQASIABEgAkSACBABIlBpCPAWjEqrMfJLBIgAESACRIAIEAEiQASIABEgAkSgAhGgAKICK40sEwEiQASIABEgAkSACBABIkAEiAARqDQEKICotBojv0SACBABIkAEiAARIAJEgAgQASJABCoQAQogKrDSyDIRIAJEgAgQASJABIgAESACRIAIEIFKQ4C3YFRajZFfIkAEiAARIAJEgAgQASJABIgAESACFYgANSAqsNLIMhEgAkSACBABIkAEiAARIAJEgAgQgUpDgAKISqsx8ksEiAARIAJEgAgQASJABIgAESACRKACEaAAogIrjSwTASJABIgAESACRIAIEAEiQASIABGoNAQogKi0GiO/RIAIEAEiQASIABEgAkSACBABIkAEKhABCiAqsNLIMhEgAkSACBABIkAEiAARIAJEgAgQgUpDgAKISqsx8ksEiAARIAJEgAgQASJABIgAESACRKACEaAAogIrjSwTASJABIgAESACRIAIEAEiQASIABGoNAQogKi0GiO/RIAIEAEiQASIABEgAkSACBABIkAEKhABCiAqsNLIMhEgAkSACBABIkAEiAARIAJEgAgQgUpDoH4mk6k0nskvESACRIAIEAEiQASIABEgAkSACBABIlBhCNSvV69ehbFMdokAESACRIAIEAEiQASIABEgAkSACBCBSkOARzAqrcbILxEgAkSACBABIkAEiAARIAJEgAgQgQpEgAKICqw0skwEiAARIAJEgAgQASJABIgAESACRKDSEKANiEqrMfJLBIgAESACRIAIEAEiQASIABEgAkSgAhGgBkQFVhpZJgJEgAgQASJABIgAESACRIAIEAEiUGkI0AhlpdUY+SUCRIAIEAEiQASIABEgAkSACBABIlCBCFADogIrjSwTASJABIgAESACRIAIEAEiQASIABGoNARoA6LSaoz8EgEiQASIABEgAkSACBABIkAEiAARqEAEqAFRgZVGlokAESACRIAIEAEiQASIABEgAkSACFQaAhRAVFqNkV8iQASIABEgAkSACBABIkAEiAARIAIViACNUFZgpZFlIkAEiAARIAJEgAgQASJABIgAESAClYYANSAqrcbILxEgAkSACBABIkAEiAARIAJEgAgQgQpEoP7ixYsrkG2yTASIABEgAkSACBABIkAEiAARIAJEgAhUEgL1MpYqiWHySgSIABEgAkSACBABIkAEiAARIAJEgAhUHgK8hrPy6owcEwEiQASIABEgAkSACBABIkAEiAARqDgEaISy4qqMDBMBIkAEiAARIAJEgAgQASJABIgAEag8BKgBUXl1Ro6JABEgAkSACBABIkAEiAARIAJEgAhUHAK8BaPiqowMEwEiQASIABEgAkSACBABIkAEiAARqDwEKICovDojx0SACBABIkAEiAARIAJEgAgQASJABCoOAQogKq7KyDARIAJEgAgQASJABIgAESACRIAIEIHKQ4ACiMqrM3JMBIgAESACRIAIEAEiQASIABEgAkSg4hBoUK9evYpjmgwTASJABIgAESACRIAIEAEiULcQePDjulVeljZdBA5YN930mFphCFADojDcGIsIEAEiQASIABEgAkSACBABIkAEiAARyAMBCiDyAItBiQARIAJEgAgQASJABIgAESACRIAIEIHCEKAAojDcGIsIEAEiQASIABEgAkSACBABIkAEiAARyAMBCiDyAItBiQARIAJEgAgQASJABIgAESACRIAIEIHCEKAAojDcGIsIEAEiQASIABEgAkSACBABIkAEiAARyAMBCiDyAItBiQARIAJEgAgQASJABIgAESACRIAIEIHCEKAAojDcGIsIEAEiQASIABEgAkSACBABIkAEiAARyAMBCiDyAItBiQARIAJEgAgQASJABIgAESACRIAIEIHCEKAAojDcGIsIEAEiQASIABEgAkSACBABIkAEiAARyAOBBplMxtSrVy+PKOUd9Oabbzavvvqqx+QFF1xg1ltvvfJmOEXu+vXrZ2bPnm0aNGhg7rzzTrP00kunmDqTIgK1D4ETTzzR/Pzzz963Mnz4cLPUUksVXMjvvvvOnH766V78jh07mvPOO6/gtAqJmGZZCsk/jTjDhg0zL7/8spfUgAEDzAYbbJBGsmWRBsqF8oEOPPBAs88++5QFX2QiGoEff/zRnHbaaebvv//22iPaZTE0Z84cc+utt5opU6aYjz76yLRo0cJ07tzZoM846KCDTOPGjYtJvsbjJukHa0NfVeNAkwEiUKYI/PzjLDPyslM87lZdp5PpdcK5Zcop2apJBOpZAUSmJhnAALxgwYKiWGjTpo1ZeeWVvTQOPvhgc//993vvL730ktlhhx0KTvuvv/7yJh4TJ040Z599ttl3330LTqs6IrZu3dpgsgSaP39+xU9kSonZ0KFDDRacPXv2NMVOKNPgs9z4SaNMlZBGs2bNzG+//eax+scff5hlllmmYLanTZtmNtxwQy/+dtttZ8aNG5eVVqn7kzTLksV4Nf7o06ePufvuu70cn3vuObPzzjtXY+6lzQqLzuOPP97LZPDgwWbgwIGlzZCpp4LA9OnTTYcOHby0tt12W/Paa68VnC76hAMOOMCN0/6EKmHcjuvH4vpBlLk29FX+uquk35gbDxo0yLRv395ce+21pnnz5pXEvnnw44pit84x+9Wn080Z3Zf0metv3tUMvveVssLggHWTsVPp30myUtZcqAY1l/WSnLfffvvQwTgpb9iduPrqq5MGTxwOk4Ubb7zRC9+/f39vx6o2aYskBqKWBcSCEzswoLffftsceuihZq211qqxUpYbPzUGRC3PmP1JLa9gFo8IRCCAjRYtfGjatKnZaqutzA8//GDef/9907t374rYNGA/FlHJFeKFTZfx48d72sI77rijOfzwwyuEc7JJBKoPAX4npcW6xgUQpS1ecalrYcMvv/xiattxleLQqdzYfjX733//vUYLU2781CQYjz/+uPniiy88odAKK6xQk6yknjf7k9QhDUwQC7rRo0ebtdde2+yxxx6BYehIBKobARyLFA3Fdddd1+BIjmhuLly40EADqxKoLvZjlTIuJeWzfv0q82/QuiERASKQiwC/k1xM0nSpcRsQsNOAM9hBdNddd5mPP16ia9WrVy+z6aabBgXzdhECPYp03Gabbczee+9t3njjDXPxxRcb3RiLTJrRaxABnLFF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UQR4jEoEiAARIAJEgAgQASJABIhA9SAQJ4CoHi5qVy5aAHHvlMVWWFPWCvJFgU8BRFHwpRa59raw1CBiQkSACBABIkAEiAARIAJEgAgQASJABIhAsQhQAFEsgoxPBIgAESACRIAIEAEiQASIABEgAkSACMQiQAFELEQMQASIABEgAkSACBABIkAEiAARIAJEgAgUiwAFEMUiyPhEgAgQASJABIgAESACRIAIEAEiQASIQCwCFEDEQsQARIAIEAEiQASIABEgAkSACBABIkAEiECxCDTAFVa4P5hEBIgAESACRIAIEAEiQASIABEgAnUHge16Hm5+/+1ns9RSDUz9pbg3XXdqvuZKyms4aw575kwEiAARIAJEgAgQASJABIhAQgR4DWdCoBgsEAFewxkIS7U7UsxV7ZAzQyJABIgAESACRIAIEAEiQASIABEgAnUPAWpA1L06Z4mJABEgAkSACBCBOoYAd47rWIWnWFzuGqcIJpMiAkTA1IcNCBIRIAJEgAgQASJABIgAESACRIAIEAEiQARKiQCPYJQSXaZNBIgAESACRIAIEAEiQASIABEgAkSACHgI1OcNGGwJRIAIEAEiQASIABEgAkSACBABIkAEiECpEaAGRKkRZvpEgAgQASJABIgAESACRIAIEAEiQASIAG1AsA0QASJABIgAESACRIAIEAEiQASIABEgAqVHgBoQpceYORABIkAEiAARIAJEgAgQASJABIgAEajzCFAAUeebAAEgAkSACBABIkAEiAARIAJEgAgQASJQegRohLL0GDMHIkAEiAARIAJEgAgQASJABIgAESACdR4BakDU+SZAAIgAESACRIAIEAEiQASIABEgAkSACJQegQaLFy82Sy21VOlzSjmHfv36mdmzZ5sGDRqYO++80yy99NIp58DkiAARIAJEoJwQ+O6778zpp5/usdSxY0dz3nnnlRN7JeVl3LhxZujQoV4e+++/v+nVq1dJ82PiRIAIVC8CUye8ap6/f8k3vs0eB5t/7dKzehmoo7m9/PLLZtiwYV7pDzzwQLPPPvvUUSRYbCJQfQjUr0ThA+AZPXq0uf/++80999xj/vzzz+pDjDkRgTwQ+Pbbb827777r/X3xxRd5xDTm888/d3F/+OGHvOIyMBGojQj8/PPPXr+Pvv+ZZ54puogvvfSS6dq1qznyyCPNL7/8UnR6pUzg008/dWV///33S5lVxaeNvna//fYze+65p/nggw9qvDzlxk+NA0IGAhH49vPpZvyY+7y/mdPfCwxDxyUIQBi7xRZbmIsuuqhoSD7++GP2rUWjWDMJVNIYXjMIlW+uDTKZjKlXr175ckjOiEAFI3DfffeZM8880yvBSiut5AkVmjRpElsiLLQ23nhj89tvv3lhhw8fbvr27RsbjwGIABFIjsCAAQPM+PHjzauvvmp23HFHc/jhhyePzJBli8CIESPMI4884vG38sormzvuuKNGeS03fmoUDGZOBIpEAPOiE0880Uvl7bffNoceeqhZa621ikyV0SsRAY7hlVhrS3imEcrKrTty/g8CN998s7ntttvKEo8TTjjBQPAA+vHHH82NN96YiM+rr77aCR/WXXddc9hhhyWKx0BEoBIRgIbPDTfcYJ5++ulqZb9+/SozSPPnz6/WvJlZ6RDQmyrz5s0rXUYJUy43fhKyzWBEoCwR8Gtu//7774F8Pv744964MmfOnED/uupYm3DhGF65rbg+NCBIRKBSEYB67UknnWSOO+64sixC48aNzf/93/853i655BID7YYowrGNiy++2AUZMmSIZ+vEOfCFCNQyBLBb3b9/f3cOt7qKd8YZZ5hVV13V036gTYXqQr30+eAc96abbmogvD3llFNKn2FMDuXGTwy79CYCZY0A5lUXXHCBadq0qTf3gz0gP8G+HTZuMK7ke/zVn1Zt+l3bcOEYXrmts0Hlsk7OiYCp9h3TQjDv06ePufzyyw3OGUJ18MorrzSXXnppaFIQUghtt912pkePHvKTTyJQKxHAjkxN0N57723wR6pdCKy33nrmnXfeKZtClRs/ZQMMGSECBSIwaNAgg78wwtEMOcIaFqYuutc2XDiGV24rrtI/rdwykPM6jMBjjz1W9qXHTS1XXHGF4xMaEdByCKJPPvnE4EiJEIQVJCJQmxHAJLG6j17UZjxZNiJABIhAXUdg7NixdR2CwPITl0BY6FgDCJSlBgTOysOyKW4PmDRpkoG61b/+9S+z2WabeRbL8TsfwtVtr732mpk6daqZPHmyZ+18o4028oz8bb755qZTp06Jk8ORFaQF1f8pU6Z4T/ADNV4Ywdl55509y7yS4PTp040sksH/TjvtJF6Bz+eff94rMzwPOOAAs+aaa2aFGzNmjFeOFVdc0bPcDk/g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o+BwdCUCRIAI1EkEYIXfL3gAEFdccYU57rjj6iQmLDQRIAJEgAgQASJABIgAESgEgQZQK466frCQRBmHCBABIlBbEOjTp493bReuRmzatKl3pRTuKMe1uyQiQASIABGomwgs12wFs233g73Cr7pOp7oJAktdZxHAtc4HHXSQV/6NNtqozuLAgheGAK/hLAw3xiICRIAIEAEiQASIQMUgwGs4K6aqyo5RXsNZdlVChohARSPAazgruvrIPBEgAkSACBABIkAEiAARIAJEgAgQgcpAgBoQlVFP5JIIEAEiQASIABEgAkSACBABIkAEiEBFI0ANiIquPjJPBIgAESACRIAIEAEiQASIABEgAkSgMhCoXxlskksiQASIABEgAkSACBABIkAEiAARIAJEoJIRoACikmuPvBMBIkAEiAARIAJEgAgQASJABIgAEagQBCiAqJCKIptEgAgQASJABIgAESACRIAIEAEiQAQqGQEKICq59sg7ESACRIAIEAEiQASIABEgAkSACBCBCkGgfr169SqEVbJJBIgAESACRIAIEAEiQASIABEgAkSACFQqAtSAqNSaI99EgAgQASJABIgAESACRIAIEAEiQAQqCAEKICqossgqESACRIAIEAEiQASIABEgAkSACBCBSkWAAohKrTnyTQSIABEgAkSACBABIkAEiAARIAJEoIIQaFBBvJLVEAT69etnZs+ebRo0aGDuvPNOs/TSS4eEpDMRqL0InHjiiebnn3/22v/IkSNrb0GLKNlff/1l7rrrLjNhwgQzdepUs3jxYrPRRhuZTp06mZ49e5o11lijiNQZlQgQASJABIgAESACRIAIRCNQL2MpOgh9yx2B1q1bmx9//NFjc/78+aZx48ZZLA8dOtQMHz7cW2AMGDAgy6+2/KgLZawtdVWqcjRr1sz89ttvXvKFdGtYnJ922mlm4sSJ5uyzzzb77rtvqVitkXS/+uor07t3b/Pmm28G5v/qq6+aLl26BPrRsfYg8NJLL5lBgwaZ9u3bm2uvvdY0b968bArHfrxsqoKMEAEiQASIABEoGQIUQJQM2upLOEoAgQUZFmZCn332mVlrrbXkZ6141oUy1oqKKnEhihVAvPjii2an/2/vPuCtKA49js+9IBZsICioIAQpKvaCsYA99oLYu1ETsbwYNep7L7aosRETY+8+O6JiARtGsZFo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AAEEEEAAAQRCFqAGRMi5R9oRQAABBBBAAAEEEEAAAQQQCESAAohAMopkIoAAAggggAACCCCAAAIIIBCyAAUQIeceaUcAAQQQQAABBBBAAAEEEEAgEAEKIALJKJKJAAIIIIAAAggggAACCCCAQMgCFECEnHukHQEEEEAAAQQQQAABBBBAAIFABCiACCSjSCYCCCCAAAIIIIAAAggggAACIQtQABFy7pF2BBBAAAEEEEAAAQQQQAABBAIRoAAikIwimQgggAACCCCAAAIIIIAAAgiELEABRMi5R9oRQAABBBBAAAEEEEAAAQQQCESgNpfLBZJUkokAAggggAACCCCAAAIIIIAAAqEK1NbU1ISadtKNAAIIIIAAAggggAACCCCAAAKBCNAEI5CMIpkIIIAAAggggAACCCCAAAIIhCxAAUTIuUfaEUAAAQQQQAABBBBAAAEEEAhEgD4gAskokokAAggggAACCCCAAAIIIIBAyALUgAg590g7AggggAACCCCAAAIIIIAAAoEI0AllIBlFMhFAAAEEEEAAAQQQQAABBBAIWYAaECHnHmlHAAEEEEAAAQQQQAABBBBAIBAB+oAIJKNIJgIIIIAAAggggAACCCCAAAIhC1ADIuTcI+0IIIAAAggggAACCCCAAAIIBCJAAUQgGUUyEUAAAQQQQAABBBBAAAEEEAhZgE4oQ8490o4AAggggAACCCCAAAIIIIBAIALUgAgko0gmAggggAACCCCAAAIIIIAAAiEL1NbV1YWcftKOAAIIIIAAAggggAACCCCAAAIBCNTkbAggnSQRAQQQQAABBBBAAAEEEEAAAQQCFmAYzoAzj6QjgAACCCCAAAIIIIAAAgggEIoAnVCGklOkEwEEEEAAAQQQQAABBBBAAIGABagBEXDmkXQEEEAAAQQQQAABBBBAAAEEQhFgFIxQcop0IoAAAggggAACCCCAAAIIIBCwAAUQAWceSUcAAQQQQAABBBBAAAEEEEAgFAEKIELJKdKJAAIIIIAAAggggAACCCCAQMACFEAEnHkkHQEEEEAAAQQQQAABBBBAAIFQBBgFI5ScIp0IIIAAAggggAACCCCAAAIIBCxADYiAM4+kI4AAAggggAACCCCAAAIIIBCKAAUQoeQU6UQAAQQQQAABBBBAAAEEEEAgYAEKIALOPJKOAAIIIIAAAggggAACCCCA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"/><use stroke="#7E7C7B" xlink:href="#rect-1"/></g><path id="Line" fill="#C06334" fill-rule="nonzero" d="M309.315 59.871l1.064 1.694-.847.532-130.147 81.799 3.193 5.08L167 150.5l8.128-13.377 3.193 5.079 130.147-81.799.847-.532z"/><path id="Line-Copy" fill="#C06334" fill-rule="nonzero" d="M349.496 60.872l.004 1 .45 128.874 6.001-.02L349 204.75l-7.049-13.975 5.999-.022-.45-128.875-.003-1 2-.006z"/><path id="Line-Copy-2" fill="#C06334" fill-rule="nonzero" d="M386.71 57.027l.256.966 35.404 133.219 5.8-1.54L425 205l-10.361-11.732 5.798-1.542-35.403-133.22-.257-.966 1.933-.513z"/><text id="selection" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="20" font-weight="normal"><tspan x="295" y="44">selection</tspan></text></g></g></svg> \ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="556" height="428" viewBox="0 0 556 428"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><path id="rect-1" d="M48 88h448v281H48z"/></defs><g id="selection-range" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="selection-firefox.svg"><g id="Bitmap"><image width="448" height="281" x="48" y="88" xlink:href="data:image/png;base64,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5YNp/frug8PwkowpRvFBfdGDnYJl4R0U4B9r6WAtH2wT7bE9b+Orc2vbZNug5TajhHytbxulDZmmTWb5oPKyUWOV16q6/rbZuGzK9pdXHfv2zYWrmJBJG1W2fpyD8y4zKStVHlla/N///Z+ZM2dOlWQffvih0eexxx7znKpus8023vmmTZuanj17eoNGRcjpqgQBf9AAXvEKcjzkv9Y//fST2WWXXYy2/qD6hw8f7n3uuusub1lbDVL9QfNKFXbeeeeE0Heoef311ytPS0DQ55lnnjGfffaZJz4cdthhlee1ozr1eeqppzyhQssI2yCLMvVXQSvWaDlUTYHxB9Wvj+a6ytFsFB88GnxroK3lOf1B/X711Ve9zwsvvGCefPLJtG/Q/Pm1r7I10JcAEwy6FvrccsstXrv33HPPKkkkekgc08odwfD+++8bfe69917z+OOPe1OD/GkkAllmo0ePNv37968i7rz77rtGHwkjYn7aaaet8qZN1+yCCy4wb731lvfJxGLD3yb2IQCBeAlITND9UoNrGzQY12+XlpQODkrznd62IY5t0DrRCgwSqoPTMew5+/xg25fP5whbR6pt8PrYtHrZqf7Zj9Klaqv6q98SBV17XWf11X7Cpo0nq9u2oVi29hnHPt/adttje97G22N73sbbY3vexrONj0BSJ5T2CywT+WyCHDjqgSksKD7ZubD0UeL0pq9bt27eG7Uo+fxpmzRZ6f/CsvCfi7qvB0g9ACZ74xi1PNf0GvDrLZt/AOCaNx/pfv99WpViNWiKEoIrZ7jkzcdyNC71+tNowGdveP74fO+rTv1YyTtwMQb7v+f646kfY5dVMPwsbNm2Lv+5XO3r+u+zzz6VxR1++OGmd+/enmn8v//9b2+Ar8G43oRra/9fNVh9+eWXvYHto48+apRPlg4KGtDqLbwNEjCs1ZZM7iV2WPFB/2fyd9GpUycjy4B//etfnhCi87LICAobtkwtdamw/fbbm7///e/m66+/9oQHDeaVV+3VfU1Bb+Q7duzoDb5f/0uwUDpZT9x+++1emuAfWShofq7m9ErEUH7dsx588EGvfbIW2G+//byBteuAWcv4+sWHAQMGeA9z+i267777vAc2DeYPPvhg89FHH0WaOnHHHXdUER90PWRN8sMPPxiZudolgPfaay/zzTffeNYXts8SJvzig6YI9erV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IqAIKAKKgCKgCCgCioAioAgoAoqAIqAIVIaACiAqw01jKQKKgCKgCCgCioAioAgoAoqAIqAIKAKKQBkItC0UCmaWWWYpI0p9B73sssvMk08+GTA5YMAA07Vr1/pmOEfu+vTpYyZPnmzatm1rrr76ajPbbLPlmLompQi0PgSOPvpo88033wTfyjXXXGNmnXXWigs5ceJEc8IJJwTxV1llFXPaaadVnFYlEfMsSyX55xHniiuuMI8//niQVL9+/czKK6+cR7J1kQblonzQXnvtZXbeeee64EuZSEbgyy+/NMcff7z53//+F7RH2mU1NGXKFPPvf//bjBs3zrz11lumQ4cOpkePHoY+Y++99zZzzz13Ncm3eNws/WBr6KtaHGhlQBGoUwSmfDnRXHXujLnQUsuvYvY6snnnQnUKi7IVQmAWK4AohNya9ScD8M8//1xVnosssohZdNFFgzT22Wcfc/PNNwfvjz32mNl0000rTvu3334LJh4vvfSSOeWUU8wuu+xScVrNEbFTp06GyRI0derUhp/I1BKzYcOGGRacO+ywg6l2QpkHn/XGTx5laoQ05p13XvPDDz8ErE6fPt3MPvvsFbM9fvx4061btyD+xhtvbEaPHl2UVq37kzzLUsR4M/448MADzfXXXx/k+NBDD5levXo1Y+61zYpF5xFHHBFkMnDgQHPGGWfUNkNNPRcE3n77bbPiiisGaW244Ybmqaeeqjhd+oQ99tjDj9PhhBph3E7rx9L6QcrcGvqqcN010m/mxv379zfLLbecufDCC818883XSOybe99uKHZnOmY/fm+8Oab3jLlQt7U2NueMKJ4LtTQgvTPuSzf6d9LSOKfl3zYtQK39e/bsGTsYZ82b3Ynzzz8/a/DM4ZgsXHLJJUH4vn37BjtWrUlbJDMQrSwgC052YKAXX3zR7LfffmbZZZdtsVLWGz8tBkQrz1j7k1ZewVo8RSABATZapPChffv2Zr311jNffPGFee2118yee+7ZEJsG2o8lVHKDeLHp8vTTTwfawptvvrk54IADGoRzZVMRaD4E9DupLdYtLoCobfGqS10KG7777jvT2o6rVIdO48YOq9n/9NNPLVqYeuOnJcG4++67zUcffRQIhRZYYIGWZCX3vLU/yR3SyARZ0N16662mS5cuZtttt40Mo46KQHMjwLFIp6G4wgorGI7kOM3N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tOlrd911V7PddtuZ119/vcXLU2/8tDggykAkAp9NeMeMvu/m4G/C2/qNR4L0hyPC2HXWWccMGjQoKVgmv3feeUf71kxI1V+gRhrD6w+9luWobaFQMLPMMkvLcqG5KwKtFIGbbrrJnHTSSUHpFl544UCo0K5du9TSstBabbXVzA8//BCEveaaa8xBBx2UGk8DKAKKQHYE+vXrZ55++mnz5JNPms0339wccMAB2SNryLpF4NprrzV33nlnwN+iiy5qrrrqqhbltd74aVEwNHNFoEoEmBcdffTRQSovvvii2W+//cyyyy5bZaoavRER0DG8EWttBs9qhLJx6045/wOByy67zFx55ZV1icdRRx1lEDxAX375pbnkkksy8Xn++ed74cMKK6xg9t9//0zxNJAi0IgIoOFz8cUXm1GjRjUr+23aNJlBmjp1arPmrZnVDgG5qfLjjz/WLqOMKdcbPxnZ1mCKQF0iENbc/umnnyL5vPvuu4NxZcqUKZH+M6tja8JFx/DGbcVt0IBQUgQaFQHUa4855hhz+OGH12UR5p57bvPPf/7T8/aPf/zDoN2QRBzbOOuss3yQoUOHBrZOvIO+KAKtDAF2q/v27evP4TZX8U488USzxBJLBNoPalOhuVCvfT6c415jjTUMwtu//OUvtc8wJYd64yeFXfVWBOoaAeZVAwYMMO3btw/mftgDChP27di4YVwp9/hrOK3W9Lu14aJjeOO2zraNy7pyrgiYZt8xrQTzAw880Jx77rmGc4aoDg4ePNicffbZsUkhpHC08cYbm969e7uf+lQEWiUC7Mi0BO20006GP6XWhUDXrl3Nyy+/XDeFqjd+6gYYZUQRqBCB/v37G/7iiKMZ7ghrXJiZ0b214aJjeOO24ib908Ytg3I+EyNw11131X3puanlvPPO83yiEYGWQxS9++67hiMljhBWKCkCrRkBJonNffSiNeOpZVMEFAFFYGZH4IEHHpjZIYgsv+ISCYs6tgACdakBwVl5LJtye8CYMWMM6lZrr722WXPNNQOL5fwuh7i67amnnjJvvPGGeeWVVwJr56uuumpg5G+ttdYy3bt3z5wcR1ZIC9X/cePGBU/4QY0XIzi9evUKLPO6BN9++23jFsnwv8UWWzivyOfDDz8clBnPPfbYwyyzzDJF4e67776gHAsttFBguR1PcHruuecCviZNmhRg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xsJvoYUWct76bOUInHjiiQYNn80339zstttudVNa7cfrpiqUEUVAEVAEFAFFoKYI6BGMmsLbPIknHcFoHg5qk8t2221nRo0aFSwKR44cWZtMNNVWg0C1RzBaDRARBTnjjDPMoEGDAh8M1R588MERodRJEVAEFAFFQBFQBBQBRUARqC0CbSox1lZbljR1RcAExgQRPigpAopA9QhggNIRGh5KioAioAgoAoqAIqAIKAKKQEsg0LYezzgjFHnqqafM66+/bsaNGxc8Uc9EbRQDir169TLrrLNOLF7jx48PrpkjPmefuZ5y9dVXD66b23jjjc1iiy0WGzfsMXHixIAXrqx75ZVXDGfDubZutdVWM2uttVZwfV04jvxN/g888EDgdOyxxwY3VEyfPt3ceOON5uGHHzaTJk0K/DbddFMTdUMFt1tgtXzs2LFmzJgxQfy1117brLnmmmaTTTYpufFC5u3esSyOgS9wOO6440pUy++7777gSj7UsN3O6Lvvvmuef/754I+8F1hgAbPyyisH5d59990zGav7/PPPzYMPPmg++OCD4I/fGLsDu3XXXTew6xCl5o6BtHPPPdexH7SB8847z//mpVu3bmb77bf3bmll9AHtS7V1Wiu8JI9x79jwoD3QHvk2JkyYYLp27Rq0b65SpH1HYerSq7Y9unTinpwpR3D09ttvB7xhyBF+aDt8g7vuuqtp165dXPTAHdsffB/vvPOO+eijj8z3338ffLOLL754UO9oxpTzDcvMpk2bZi6//HLzyy+/BM58S5tttlnwzhWeV155ZfC+zDLLmD322ENGDd5dO+P7w2Al9hP4Lrk5A7sKfDedO3c2K664ounZs6fhu04jbt+4/fbbg/7lvffeC/oEykpfN//885dEB79jjjmmxD3K4ccffwzaizyCgSFK2o8jvsetttrK/fTPem1r4ToAvyeeeCKohxdffNF89dVXQVujXHwP1GUWcunQz1Ofn332WXBkrUePHkE98p3FUSXfVVQc2jtjH30g/ow12Or4//buA06PovD/+NwlBAhIDSV0CKGDgPTeIop0CZ2AlB9FUEG6fxCQpjQFQVACgVCkl9AhGoIg0gOEUCVA6C0oTfHy/Oe7yczN7u3u89xz89w9l3zm9Uqe3Z3d2d337vPczuwU/a3Rd8gF/Y3U90P3nNZ/8cUXk/tl8ODBZvvt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AAIIIIAAAggggAACxQIMw1lsQwwCCCCAAAIIIIAAAggggAACCEQSoBPKSJAkgwACCCCAAAIIIIAAAggggAACxQLUgCi2IQYBBBBAAAEEEEAAAQQQQAABBCIJMApGJEiSQQABBBBAAAEEEEAAAQQQQACBYgEKIIptiEEAAQQQQAABBBBAAAEEEEAAgUgCFEBEgiQZBBBAAAEEEEAAAQQQQAABBBAoFqAAotiGGAQQQAABBBBAAAEEEEAAAQQQiCTAKBiRIEkGAQQQQAABBBBAAAEEEEAAAQSKBagBUWxDDAIIIIAAAggggAACCCCAAAIIRBKgACISJMkggAACCCCAAAIIIIAAAggggECxAAUQxTbEIIAAAggggAACCCCAAAIIIIBAJAEKICJBkgwCCCCAAAIIIIAAAggggAACCBQLUABRbEMMAggggAACCCCAAAIIIIAAAghEEqAAIhIkySCAAAIIIIAAAggggAACCCCAQLEABRDFNsQggAACCCCAAAIIIIAAAggggEAkAQogIkGSDAIIIIAAAggggAACCCCAAAIIFAtQAFFsQwwCCCCAAAIIIIAAAggggAACCEQSaK1UKpGSIhkEEEAAAQQQQAABBBBAAAEEEEAgX6C1paUlP4alCCCAAAIIIIAAAggggAACCCCAQCQBmmBEgiQZBBBAAAEEEEAAAQQQQAABBBAoFqAAotiGGAQQQAABBBBAAAEEEEAAAQQQiCRAHxCRIEkGAQQQQAABBBBAAAEEEEAAAQSKBagBUWxDDAIIIIAAAggggAACCCCAAAIIRBKgE8pIkCSDAAIIIIAAAggggAACCCCAAALFAtSAKLYhBgEEEEAAAQQQQAABBBBAAAEEIgnQB0QkSJJBAAEEEEAAAQQQQAABBBBAAIFiAWpAFNsQgwACCCCAAAIIIIAAAggggAACkQQogIgESTIIIIAAAggggAACCCCAAAIIIFAsQCeUxTbEIIAAAggggAACCCCAAAIIIIBAJAFqQESCJBkEEEAAAQQQQAABBBBAAAEEECgWaG1rayuOJQYBBBBAAAEEEEAAAQQQQAABBBCIINBSsSFCOiSBAAIIIIAAAggggAACCCCAAAIIFAowDGchDREIIIAAAggggAACCCCAAAIIIBBLgE4oY0mSDgIIIIAAAggggAACCCCAAAIIFApQA6KQhggEEEAAAQQQQAABBBBAAAEEEIglwCgYsSRJBwEEEEAAAQQQQAABBBBAAAEECgUogCikIQIBBBBAAAEEEEAAAQQQQAABBGIJUAARS5J0EEAAAQQQQAABBBBAAAEEEECgUIACiEIaIhBAAAEEEEAAAQQQQAABBBBAIJYAo2DEkiQdBBBAAAEEEEAAAQQQQAABBBAoFKAGRCENEQgggAACCCCAAAIIIIAAAgggEEuAAohYkqSDAAIIIIAAAggggAACCCCAAAKFAhRAFNIQgQACCCCAAAIIIIAAAggggAACsQQogIglSToIIIAAAggggAACCCCAAAIIIFAoQAFEIQ0RCCCAAAIIIIAAAggggAACCCAQS4ACiFiSpIMAAggggAACCCCAAAIIIIAAAoUCFEAU0hCBAAIIIIAAAggggAACCCCAAAKxBCiAiCVJOggggAACCCCAAAIIIIAAAgggUChAAUQhDREIIIAAAggggAACCCCAAAIIIBBLoLVSqcRKi3QQQAABBBBAAAEEEEAAAQQQQACBXIHWlpaW3AgWIoAAAggggAACCCCAAAIIIIAAArEEaIIRS5J0EEAAAQQQQAABBBBAAAEEEECgUIACiEIaIhBAAAEEEEAAAQQQQAABBBBAIJYAfUDEkiQdBBBAAAEEEEAAAQQQQAABBBAoFKAGRCENEQgggAACCCCAAAIIIIAAAgggEEuATihjSZIOAggggAACCCCAAAIIIIAAAggUClADopCGCAQQQAABBBBAAAEEEEAAAQQQiCVAHxCxJEkHAQQQQAABBBBAAAEEEEAAAQQKBagBUUhDBAIIIIAAAggggAACCCCAAAIIxBKgACKWJOkggAACCCCAAAIIIIAAAggggEChAJ1QFtIQgQACCCCAAAIIIIAAAggggAACsQSoARFLknQQQAABBBBAAAEEEEAAAQQQQKBQoLWtra0wkggEEEAAAQQQQAABBBBAAAEEEEAghsD/BwDhXHdqSizjAAAAAElFTkSuQmCC"/><use stroke="#7E7C7B" xlink:href="#rect-1"/></g><path id="Line" fill="#C06334" fill-rule="nonzero" d="M309.315 59.871l1.064 1.694-.847.532-130.147 81.799 3.193 5.08L167 150.5l8.128-13.377 3.193 5.079 130.147-81.799.847-.532z"/><path id="Line-Copy" fill="#C06334" fill-rule="nonzero" d="M349.496 60.872l.004 1 .45 128.874 6.001-.02L349 204.75l-7.049-13.975 5.999-.022-.45-128.875-.003-1 2-.006z"/><path id="Line-Copy-2" fill="#C06334" fill-rule="nonzero" d="M386.71 57.027l.256.966 35.404 133.219 5.8-1.54L425 205l-10.361-11.732 5.798-1.542-35.403-133.22-.257-.966 1.933-.513z"/><text id="selection" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="20" font-weight="normal"><tspan x="295" y="44">selección</tspan></text></g></g></svg> \ No newline at end of file diff --git a/2-ui/99-ui-misc/03-event-loop/2-micro-macro-queue/solution.md b/2-ui/99-ui-misc/03-event-loop/2-micro-macro-queue/solution.md index 2911b76cf..46844473d 100644 --- a/2-ui/99-ui-misc/03-event-loop/2-micro-macro-queue/solution.md +++ b/2-ui/99-ui-misc/03-event-loop/2-micro-macro-queue/solution.md @@ -1,50 +1,50 @@ -The console output is: 1 7 3 5 2 6 4. +La salida en la consola es: 1 7 3 5 2 6 4. -The task is quite simple, we just need to know how microtask and macrotask queues work. +La tarea es bastante simple, solamente necesitamos saber cómo funcionan las colas de micro y macrotareas. -Let's see what's going on, step by step. +Veámoslo paso a paso. ```js console.log(1); -// The first line executes immediately, it outputs `1`. -// Macrotask and microtask queues are empty, as of now. +// La primera línea se ejecuta inmediatamente, e imprime`1`. +// Por ahora, las colas de micro y macrotareas están vacías. setTimeout(() => console.log(2)); -// `setTimeout` appends the callback to the macrotask queue. -// - macrotask queue content: +// `setTimeout` agrega la callback a la cola de macrotareas. +// - contenido de la cola de macrotareas: // `console.log(2)` Promise.resolve().then(() => console.log(3)); -// The callback is appended to the microtask queue. -// - microtask queue content: +// La callback es agregada a la cola de microtareas. +// - contenido de la cola de microtareas: // `console.log(3)` Promise.resolve().then(() => setTimeout(() => console.log(4))); -// The callback with `setTimeout(...4)` is appended to microtasks -// - microtask queue content: +// La callback con `setTimeout(...4)` es agregada a las microtareas. +// - contenido de la cola de microtareas: // `console.log(3); setTimeout(...4)` Promise.resolve().then(() => console.log(5)); -// The callback is appended to the microtask queue -// - microtask queue content: +// La callback es agregada a la cola de microtareas +// - contenido de la cola de microtareas: // `console.log(3); setTimeout(...4); console.log(5)` setTimeout(() => console.log(6)); -// `setTimeout` appends the callback to macrotasks -// - macrotask queue content: +// `setTimeout` agrega la callback a las macrotareas +// - contenido de la cola de macrotareas: // `console.log(2); console.log(6)` console.log(7); -// Outputs 7 immediately. +// Imprime 7 inmediatamente. ``` -To summarize, +Concluyendo: -1. Numbers `1` and `7` show up immediately, because simple `console.log` calls don't use any queues. -2. Then, after the main code flow is finished, the microtask queue runs. - - It has commands: `console.log(3); setTimeout(...4); console.log(5)`. - - Numbers `3` and `5` show up, while `setTimeout(() => console.log(4))` adds the `console.log(4)` call to the end of the macrotask queue. - - The macrotask queue is now: `console.log(2); console.log(6); console.log(4)`. -3. After the microtask queue becomes empty, the macrotask queue executes. It outputs `2`, `6`, `4`. +1. Los números `1` y `7` se muestran inmediatamente, porque simples llamados a `console.log` no usan ninguna cola. +2. Solo entonces, después de que el flujo del código principal finaliza, se ejecuta la cola de microtareas. + - esta tiene los comandos: `console.log(3); setTimeout(...4); console.log(5)`. + - se muestran los números `3` y `5`, mientras que`setTimeout(() => console.log(4))` agrega el llamado a `console.log(4)` al final de la cola de macrotareas. + - La cola de macrotareas ahora es: `console.log(2); console.log(6); console.log(4)`. +3. Una vez que la cola de microtareas se vacía, se ejecuta la de macrotareas. Esta imprime `2`, `6`, `4`. -Finally, we have the output: `1 7 3 5 2 6 4`. \ No newline at end of file +Finalmente, tenemos que la salida es: `1 7 3 5 2 6 4`. diff --git a/2-ui/99-ui-misc/03-event-loop/2-micro-macro-queue/task.md b/2-ui/99-ui-misc/03-event-loop/2-micro-macro-queue/task.md index ad406b3be..0732ad0f0 100644 --- a/2-ui/99-ui-misc/03-event-loop/2-micro-macro-queue/task.md +++ b/2-ui/99-ui-misc/03-event-loop/2-micro-macro-queue/task.md @@ -2,7 +2,7 @@ importance: 5 --- -# What will be the output of this code? +# ¿Cuál será la salida en consola de este código? ```js console.log(1); diff --git a/2-ui/99-ui-misc/03-event-loop/article.md b/2-ui/99-ui-misc/03-event-loop/article.md index f33188491..db67694ff 100644 --- a/2-ui/99-ui-misc/03-event-loop/article.md +++ b/2-ui/99-ui-misc/03-event-loop/article.md @@ -1,64 +1,64 @@ -# Event loop: microtasks and macrotasks +# Loop de eventos: microtareas y macrotareas -Browser JavaScript execution flow, as well as in Node.js, is based on an *event loop*. +El flujo de ejecución de JavaScript en el navegador, así como en Node.js, está basado en un *event loop* (loop de eventos). -Understanding how event loop works is important for optimizations, and sometimes for the right architecture. +Entender como este loop de eventos funciona es importante para optimizaciones y en algunos casos para utilizar la arquitectura correcta. -In this chapter we first cover theoretical details about how things work, and then see practical applications of that knowledge. +En este capítulo primero vamos a ver detalles teóricos acerca de cómo funcionan las cosas y luego veremos aplicaciones prácticas de ese conocimiento. -## Event Loop +## Loop de eventos -The *event loop* concept is very simple. There's an endless loop, where the JavaScript engine waits for tasks, executes them and then sleeps, waiting for more tasks. +El concepto de *loop de eventos* es muy simple. Existe un ciclo infinito en el que el motor de JavaScript espera por una tarea, luego ejecuta la tarea requerida y finalmente vuelve a dormir esperando por una nueva tarea. -The general algorithm of the engine: +EL algoritmo general del motor: -1. While there are tasks: - - execute them, starting with the oldest task. -2. Sleep until a task appears, then go to 1. +1. Mientras haya tareas: + - ejecutarlas comenzando por la más antigua. +2. Dormir hasta que aparezca una tarea, luego volver a 1. -That's a formalization of what we see when browsing a page. The JavaScript engine does nothing most of the time, it only runs if a script/handler/event activates. +Eso es una formalización de lo que vemos cuando navegamos por una página. La mayor parte del tiempo, el motor de JavaScript no hace nada, solo se ejecuta cuando un script/controlador/evento lo activa. -Examples of tasks: +Ejemplos de tareas: -- When an external script `<script src="...">` loads, the task is to execute it. -- When a user moves their mouse, the task is to dispatch `mousemove` event and execute handlers. -- When the time is due for a scheduled `setTimeout`, the task is to run its callback. -- ...and so on. +- Cuando un script externo `<script src="...">` se carga, la tarea es ejecutarlo. +- Cuando un usuario mueve el mouse, la tarea es enviar el evento `mousemove` y ejecutar el controlador. +- Cuando llega el momento de un `setTimeout` programado, la tarea es ejecutar su callback. +- ... y así sucesivamente. -Tasks are set -- the engine handles them -- then waits for more tasks (while sleeping and consuming close to zero CPU). +Las tareas son programadas --> el motor las ejecuta --> <span class="x x-first x-last">espera</span> por más tareas (mientras duerme y prácticamente no consume CPU). -It may happen that a task comes while the engine is busy, then it's enqueued. +Puede ocurrir que una tarea llegue mientras el motor está ocupado, entonces es puesta en cola. -The tasks form a queue, the so-called "macrotask queue" ([v8](https://v8.dev/) term): +Las tareas forman una cola, la llamada "Cola de macrotareas" (terminología de [v8](https://v8.dev/)): ![](eventLoop.svg) -For instance, while the engine is busy executing a `script`, a user may move their mouse causing `mousemove`, and `setTimeout` may be due and so on, these tasks form a queue, as illustrated in the picture above. +Por ejemplo, mientras el motor está ocupado ejecutando un `script`, un usuario podría mover su mouse causando `mousemove`, o también podría ocurrir un `setTimeout`, etc. Todas estas tareas ingresan a la cola como se observa en la imagen arriba. -Tasks from the queue are processed on a "first come – first served" basis. When the engine browser is done with the `script`, it handles `mousemove` event, then `setTimeout` handler, and so on. +Las tareas de la cola son ejecutadas según la base "El que primero llega primero se atiende". Cuando el motor del navegador termina con el `script`, se encarga del evento `mousemove`, continúa con `setTimeout`, etc. -So far, quite simple, right? +Hasta ahora bastante simple, ¿no? -Two more details: -1. Rendering never happens while the engine executes a task. It doesn't matter if the task takes a long time. Changes to the DOM are painted only after the task is complete. -2. If a task takes too long, the browser can't do other tasks, such as processing user events. So after some time, it raises an alert like "Page Unresponsive", suggesting killing the task with the whole page. That happens when there are a lot of complex calculations or a programming error leading to an infinite loop. +Dos detalles más: +1. El renderizado nunca ocurre mientras el motor ejecuta una tarea. No importa si la tarea ocupa mucho tiempo. Solo se realizarán cambios al DOM una vez que la tarea finalice. +2. Si una tarea consume demasiado tiempo, el navegador no puede ejecutar otras tareas, procesos ni eventos. Cuando esto ocurre, el navegador nos mostrará una alerta "La página no responde" sugiriendo detener la tarea junto con la página completa. Esto puede ocurrir cuando hay muchos cálculos complejos, o por un error en la programación que lleva a un bucle infinito. -That was the theory. Now let's see how we can apply that knowledge. +Esa fue la teoría. Ahora veamos como podemos aplicar ese conocimiento. -## Use-case 1: splitting CPU-hungry tasks +## Caso de uso 1: dividiendo tareas que demandan alto consumo de CPU -Let's say we have a CPU-hungry task. +Digamos que tenemos una tarea con un alto consumo de CPU. -For example, syntax-highlighting (used to colorize code examples on this page) is quite CPU-heavy. To highlight the code, it performs the analysis, creates many colored elements, adds them to the document -- for a large amount of text that takes a lot of time. +Por ejemplo, el resaltado de sintaxis (usado para colorear ejemplos de código en esta página) demanda un alto consumo de CPU. Para resaltar el código, realiza el análisis, crea muchos elementos coloreados, los agrega al documento... para una gran cantidad de texto esto lleva mucho tiempo. -While the engine is busy with syntax highlighting, it can't do other DOM-related stuff, process user events, etc. It may even cause the browser to "hiccup" or even "hang" for a bit, which is unacceptable. +Mientras el motor está ocupado con el resaltado de sintaxis, no puede hacer otras cosas relacionadas a DOM, procesar eventos de usuario, etc. Podría incluso provocar que el navegador se "congele" por un momento, lo que es inaceptable. -We can avoid problems by splitting the big task into pieces. Highlight the first 100 lines, then schedule `setTimeout` (with zero-delay) for the next 100 lines, and so on. +Podemos evitar el problema dividiendo la tarea en piezas más pequeñas. Resaltar primero 100 líneas, después programar `setTimeout` (con cero delay) para las próximas 100 líneas, y así sucesivamente. -To demonstrate this approach, for the sake of simplicity, instead of text-highlighting, let's take a function that counts from `1` to `1000000000`. +Para demostrar este enfoque y en pos de una mayor simplicidad, en lugar de resaltado de texto tomemos una función que cuenta desde `1` hasta `1000000000`. -If you run the code below, the engine will "hang" for some time. For server-side JS that's clearly noticeable, and if you are running it in-browser, then try to click other buttons on the page -- you'll see that no other events get handled until the counting finishes. +Si ejecutas el código siguiente, el navegador se va a "congelar" por un instante. Para JS desde el lado del servidor esto es claramente notable y si lo ejecutas en el navegador intenta hacer click en otros botones de la página. Verás que ningún otro evento es procesado hasta que termine el conteo. ```js run let i = 0; @@ -67,7 +67,7 @@ let start = Date.now(); function count() { - // do a heavy job + // realiza una tarea pesada for (let j = 0; j < 1e9; j++) { i++; } @@ -78,9 +78,9 @@ function count() { count(); ``` -The browser may even show a "the script takes too long" warning. +Puede que incluso se muestre una advertencia: "Un script en esta página está provocando que el navegador se ejecute con lentitud". -Let's split the job using nested `setTimeout` calls: +Dividamos la tarea usando llamadas anidadas a `setTimeout`: ```js run let i = 0; @@ -89,7 +89,7 @@ let start = Date.now(); function count() { - // do a piece of the heavy job (*) + // realiza una parte de la tarea pesada(*) do { i++; } while (i % 1e6 != 0); @@ -97,7 +97,7 @@ function count() { if (i == 1e9) { alert("Done in " + (Date.now() - start) + 'ms'); } else { - setTimeout(count); // schedule the new call (**) + setTimeout(count); // programa una nueva llamada (**) } } @@ -105,21 +105,21 @@ function count() { count(); ``` -Now the browser interface is fully functional during the "counting" process. +Ahora la interfaz del navegador es completamente funcional durante el conteo. -A single run of `count` does a part of the job `(*)`, and then re-schedules itself `(**)` if needed: +Una solo ejecución de `count` realiza parte del trabajo `(*)` y luego se reprograma a sí misma `(**)` si lo necesita: -1. First run counts: `i=1...1000000`. -2. Second run counts: `i=1000001..2000000`. -3. ...and so on. +1. La primer ejecución cuenta: `i=1...1000000`. +2. La segunda cuenta: `i=1000001..2000000`. +3. ...y así sucesivamente. -Now, if a new side task (e.g. `onclick` event) appears while the engine is busy executing part 1, it gets queued and then executes when part 1 finished, before the next part. Periodic returns to the event loop between `count` executions provide just enough "air" for the JavaScript engine to do something else, to react to other user actions. +Ahora, si una tarea secundaria (por ejemplo el evento `onclick`) aparece mientras el motor está ocupado ejecutando la parte 1, entonces es puesta en lista y ejecutada cuando la parte 1 termina, antes de la siguiente parte. Retornos periódicos al ciclo de eventos entre ejecuciones de `count` brinda suficiente "aire" al motor de JavaScript para hacer algo más, para reaccionar a otras acciones del usuario. -The notable thing is that both variants -- with and without splitting the job by `setTimeout` -- are comparable in speed. There's not much difference in the overall counting time. +Lo notable es que ambas variantes, con y sin división de la tarea haciendo uso de `setTimeout`, son comparables en velocidad. No hay mucha diferencia en el tiempo de conteo general. -To make them closer, let's make an improvement. +Para acercar aún más los tiempos, hagamos una mejora. -We'll move the scheduling to the beginning of the `count()`: +Movamos la programación de `setTimeout` al inicio de `count()`: ```js run let i = 0; @@ -128,9 +128,9 @@ let start = Date.now(); function count() { - // move the scheduling to the beginning + // movemos la programación al principio if (i < 1e9 - 1e6) { - setTimeout(count); // schedule the new call + setTimeout(count); // programamos la nueva llamada } do { @@ -146,25 +146,25 @@ function count() { count(); ``` -Now when we start to `count()` and see that we'll need to `count()` more, we schedule that immediately, before doing the job. +Ahora cuando iniciamos `count()` y vemos que necesitaremos más `count()`, lo programamos inmediatamente, antes de hacer el trabajo. -If you run it, it's easy to notice that it takes significantly less time. +Si lo ejecutas, es fácil notar que lleva bastante menos tiempo. -Why? +¿Por qué pasa esto? -That's simple: as you remember, there's the in-browser minimal delay of 4ms for many nested `setTimeout` calls. Even if we set `0`, it's `4ms` (or a bit more). So the earlier we schedule it - the faster it runs. +Es simple: como recordarás existe un retraso mínimo en el navegador de 4ms para varias llamadas anidadas a `setTimeout`. Si configuramos `0`, es `4ms` (o un poco más). Por lo que mientras antes lo programemos más rápido se ejecutará. -Finally, we've split a CPU-hungry task into parts - now it doesn't block the user interface. And its overall execution time isn't much longer. +Finalmente hemos dividido una tarea con un alto consumo de CPU en partes y ahora no bloquea la interfaz de usuario. Y el tiempo general de ejecución no es mucho mayor. -## Use case 2: progress indication +## Caso de uso 2: indicación de progreso -Another benefit of splitting heavy tasks for browser scripts is that we can show progress indication. +Otro beneficio de dividir tareas pesadas para scripts de navegadores es que podemos indicar el progreso. -As mentioned earlier, changes to DOM are painted only after the currently running task is completed, irrespective of how long it takes. +Usualmente el navegador renderiza al terminar la ejecución del código que actualmente se está ejecutando. No importa si la tarea lleva demasiado tiempo. -On one hand, that's great, because our function may create many elements, add them one-by-one to the document and change their styles -- the visitor won't see any "intermediate", unfinished state. An important thing, right? +Por un lado eso es genial porque nuestra función puede crear muchos elementos, agregarlos de a uno al documento y cambiar sus estilos... el visitante no verá ningún estado intermedio, sin finalizar. Lo cuál es importante, ¿no? -Here's the demo, the changes to `i` won't show up until the function finishes, so we'll see only the last value: +Acá hay una demostración, los cambios a `i` no se mostrarán hasta que la función finalice, por lo que veremos solo el último valor: ```html run @@ -183,11 +183,11 @@ Here's the demo, the changes to `i` won't show up until the function finishes, s </script> ``` -...But we also may want to show something during the task, e.g. a progress bar. +... Pero puede que queramos mostrar algo durante la tarea, por ejemplo una barra de progreso. -If we split the heavy task into pieces using `setTimeout`, then changes are painted out in-between them. +Si dividimos la tarea más pesada en partes más pequeñas usando `setTimeout`, entonces los cambios son aplicados entre ellos. -This looks prettier: +Esto se ve mejor: ```html run <div id="progress"></div> @@ -197,7 +197,7 @@ This looks prettier: function count() { - // do a piece of the heavy job (*) + // realiza una parte del trabajo pesado (*) do { i++; progress.innerHTML = i; @@ -213,40 +213,40 @@ This looks prettier: </script> ``` -Now the `<div>` shows increasing values of `i`, a kind of a progress bar. +Ahora el `<div>` muestra el incremento en el valor `i`, una especie de barra de progreso. -## Use case 3: doing something after the event +## Caso de uso 3: hacer algo después del evento -In an event handler we may decide to postpone some actions until the event bubbled up and was handled on all levels. We can do that by wrapping the code in zero delay `setTimeout`. +En un controlador de evento nosotros podemos decidir posponer alguna acción hasta que el evento aparezca y sea controlado en todos los niveles. Podemos hacer esto envolviendo el código en un `setTimeout` con retraso cero. -In the chapter <info:dispatch-events> we saw an example: custom event `menu-open` is dispatched in `setTimeout`, so that it happens after the "click" event is fully handled. +En el capítulo <info:dispatch-events> vimos un ejemplo: el evento personalizado `menu-open` es distribuido en `setTimeout`, de modo que ocurre después de que el evento "click" se maneja por completo. ```js menu.onclick = function() { // ... - // create a custom event with the clicked menu item data + // crea un evento personalizado con los datos del elemento de menú en el que se hizo clic let customEvent = new CustomEvent("menu-open", { bubbles: true }); - // dispatch the custom event asynchronously + // envia el evento personalizado de forma asincrónica setTimeout(() => menu.dispatchEvent(customEvent)); }; ``` -## Macrotasks and Microtasks +## Macrotareas y Microtareas -Along with *macrotasks*, described in this chapter, there are *microtasks*, mentioned in the chapter <info:microtask-queue>. +Junto con las *macrotareas* descritas en este capítulo, existen *microtareas* mencionadas en el capítulo <info:microtask-queue>. -Microtasks come solely from our code. They are usually created by promises: an execution of `.then/catch/finally` handler becomes a microtask. Microtasks are used "under the cover" of `await` as well, as it's another form of promise handling. +Las microtareas provienen únicamente de nuestro código. Por lo general, se crean mediante promesas: una ejecución del controlador `.then / catch / finally` se convierte en una microtarea. Las microtareas también se utilizan "bajo la cubierta" de "await", ya que es otra forma de manejo de promesas. -There's also a special function `queueMicrotask(func)` that queues `func` for execution in the microtask queue. +Existe también una función especial `queueMicrotask(func)` que pone a `func` en la cola de microtareas. -**Immediately after every *macrotask*, the engine executes all tasks from *microtask* queue, prior to running any other macrotasks or rendering or anything else.** +**Inmediatamente después de cada *macrotarea*, el motor ejecuta todas las tareas desde la cola de *microtareas*, antes de ejecutar cualquier otra macrotarea o renderización o cualquier otra cosa.** -For instance, take a look: +Por ejemplo: ```js run setTimeout(() => alert("timeout")); @@ -257,23 +257,23 @@ Promise.resolve() alert("code"); ``` -What's going to be the order here? +¿Cuál será el orden en este caso? -1. `code` shows first, because it's a regular synchronous call. -2. `promise` shows second, because `.then` passes through the microtask queue, and runs after the current code. -3. `timeout` shows last, because it's a macrotask. +1. `code` se muestra primero porque es una llamada sincrónica regular. +2. `promise` aparece segundo, porque `.then` pasa a través de la cola de microtareas y se ejecuta después del código actual. +3. `timeout` aparece última, porque es una macrotarea. -The richer event loop picture looks like this (order is from top to bottom, that is: the script first, then microtasks, rendering and so on): +La imagen del loop de eventos completa se ve así (el orden es de arriba a abajo, es decir: primero el script, luego las microtareas, el renderizado, etc.): ![](eventLoop-full.svg) -All microtasks are completed before any other event handling or rendering or any other macrotask takes place. +Todas las microtareas se completan antes de que se lleve a cabo cualquier otro manejo o renderizado o cualquier otra macrotarea. -That's important, as it guarantees that the application environment is basically the same (no mouse coordinate changes, no new network data, etc) between microtasks. +Eso es importante, ya que garantiza que el entorno de la aplicación es básicamente el mismo (sin cambios de coordenadas del mouse, sin nuevos datos de red, etc.) entre las microtareas. -If we'd like to execute a function asynchronously (after the current code), but before changes are rendered or new events handled, we can schedule it with `queueMicrotask`. +Si quisiéramos ejecutar una función de forma asíncrónica (después del código actual), pero antes de que se procesen los cambios o se manejen nuevos eventos, podemos programarla con `queueMicrotask`. -Here's an example with "counting progress bar", similar to the one shown previously, but `queueMicrotask` is used instead of `setTimeout`. You can see that it renders at the very end. Just like the synchronous code: +Aquí hay un ejemplo con la "barra de progreso de conteo", similar al que se mostró anteriormente, pero se usa `queueMicrotask` en lugar de` setTimeout`. Puedes ver que se renderiza al final. Al igual que el código sincrónico: ```html run <div id="progress"></div> @@ -283,7 +283,7 @@ Here's an example with "counting progress bar", similar to the one shown previou function count() { - // do a piece of the heavy job (*) + // realiza una parte del trabajo pesado (*) do { i++; progress.innerHTML = i; @@ -301,39 +301,39 @@ Here's an example with "counting progress bar", similar to the one shown previou </script> ``` -## Summary +## Resumen -A more detailed event loop algorithm (though still simplified compared to the [specification](https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model)): +El algoritmo más detallado del loop de eventos (aunque aún simplificado en comparación con la [especificación](https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model)): -1. Dequeue and run the oldest task from the *macrotask* queue (e.g. "script"). -2. Execute all *microtasks*: - - While the microtask queue is not empty: - - Dequeue and run the oldest microtask. -3. Render changes if any. -4. If the macrotask queue is empty, wait till a macrotask appears. -5. Go to step 1. +1. Retirar de la cola y ejecutar la tarea más antigua de la cola *macrotareas* (por ejemplo, "script"). +2. Ejecutar todas las *microtareas*: + - Mientras la cola de microtareas no esté vacía: + - Retirar de la cola y ejecutar la microtarea más antigua. +3. Renderizar los cambios si los hubiera. +4. Si la cola de macrotareas está vacía, esperar hasta que aparezca una macrotarea. +5. Ejecutar el paso 1. -To schedule a new *macrotask*: -- Use zero delayed `setTimeout(f)`. +Para programar una nueva *macrotarea*: +- Usar `setTimeout(f)` con un retraso de cero. -That may be used to split a big calculation-heavy task into pieces, for the browser to be able to react to user events and show progress between them. +Eso puede usarse para dividir una gran tarea de cálculo en partes, para que el navegador pueda reaccionar a los eventos del usuario y mostrar el progreso entre ellos. -Also, used in event handlers to schedule an action after the event is fully handled (bubbling done). +Además, se utiliza en los controladores de eventos para programar una acción después de que el evento se haya manejado por completo. -To schedule a new *microtask* -- Use `queueMicrotask(f)`. -- Also promise handlers go through the microtask queue. +Para programar una nueva *microtarea* +- Usar `queueMicrotask(f)`. +- También se usan promesas para que los controladores pasen por la cola de microtareas. -There's no UI or network event handling between microtasks: they run immediately one after another. +No hay gestión de eventos de red o de UI entre las microtareas: se ejecutan inmediatamente una tras otra. -So one may want to `queueMicrotask` to execute a function asynchronously, but within the environment state. +Por lo tanto, es posible que desee `queueMicrotask` para ejecutar una función de forma asincrónica, pero dentro del estado del entorno. ```smart header="Web Workers" -For long heavy calculations that shouldn't block the event loop, we can use [Web Workers](https://html.spec.whatwg.org/multipage/workers.html). +Para cálculos largos y pesados que no deberían bloquear el ciclo de eventos, podemos usar [Web Workers](https://html.spec.whatwg.org/multipage/workers.html). -That's a way to run code in another, parallel thread. +Esa es una forma de ejecutar código en otro hilo paralelo. -Web Workers can exchange messages with the main process, but they have their own variables, and their own event loop. +Los Web Workers pueden intercambiar mensajes con el proceso principal, pero tienen sus propias variables y su propio ciclo de eventos. -Web Workers do not have access to DOM, so they are useful, mainly, for calculations, to use multiple CPU cores simultaneously. +Los Web Workers no tienen acceso a DOM, por lo que son útiles principalmente para cálculos, para utilizar varios núcleos de CPU simultáneamente. ``` diff --git a/2-ui/99-ui-misc/03-event-loop/eventLoop-full.svg b/2-ui/99-ui-misc/03-event-loop/eventLoop-full.svg index 593cbab9b..d86537fde 100644 --- a/2-ui/99-ui-misc/03-event-loop/eventLoop-full.svg +++ b/2-ui/99-ui-misc/03-event-loop/eventLoop-full.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="407" height="391" viewBox="0 0 407 391"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="promise" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="eventLoop-full.svg"><text id="..." fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="257.4" y="71">...</tspan></text><path id="Rectangle-1-Copy-6" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M216 86h108v28H216z"/><path id="Rectangle-1-Copy-8" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M216 195h108v28H216z"/><text id="mousemove" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="232.2" y="214">mousemove</tspan></text><path id="Rectangle-1-Copy-9" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M216 302h108v28H216z"/><text id="event-loop" fill="#181717" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal"><tspan x="44.922" y="189">event</tspan> <tspan x="51.115" y="222">loop</tspan></text><text id="render-copy-2" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="297.3" y="176">render</tspan></text><g id="np_paint_2296353_000000-copy-2" fill="#C06334" fill-rule="nonzero" transform="translate(256 158)"><path id="Shape" d="M3.079 0A3.095 3.095 0 000 3.079V22.92A3.095 3.095 0 003.079 26H22.92A3.095 3.095 0 0026 22.921V3.08A3.095 3.095 0 0022.921 0H3.08zm0 2.053H22.92c.587 0 1.026.44 1.026 1.026v13.043l-2.886-2.684a1.027 1.027 0 00-1.443.032l-5.72 5.955-5.035-4.148a1.021 1.021 0 00-1.38.075l-5.43 5.43V3.08c0-.587.44-1.026 1.026-1.026zM6.158 3.42A3.095 3.095 0 003.078 6.5a3.095 3.095 0 003.08 3.079A3.095 3.095 0 009.237 6.5a3.095 3.095 0 00-3.08-3.079zm0 2.053c.579 0 1.026.447 1.026 1.026s-.447 1.026-1.026 1.026A1.012 1.012 0 015.132 6.5c0-.579.447-1.026 1.026-1.026zm14.176.566c-.377 0-.759.722-.759 1.08-.46.112-.748.358-.748.716s.371.717.748.717h2.662c.73 0 1.11-1.433-.021-1.433-.022-.677-.445-.86-1.134-.716-.183-.284-.375-.386-.748-.364zM16.71 9.237c-.314 0-.631.584-.631.876-.382.093-.63.296-.63.588 0 .293.316.589.63.589h2.213c.608 0 .931-1.176-.01-1.176-.018-.555-.379-.706-.952-.588-.152-.232-.31-.308-.62-.29zm3.688 6.382l3.55 3.293v4.01c0 .586-.44 1.025-1.027 1.025H3.08a1.01 1.01 0 01-.855-.449l6.05-6.04 4.202 3.453-1.24 1.294 1.475 1.422 7.687-8.008z"/></g><text id="microtasks-copy-2" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="297.5" y="140">microtasks</tspan></text><g id="Group-Copy-2" fill="#C06334" transform="translate(256 124)"><path id="Fill-52" d="M23.145.978C21.737.348 20.186 0 18.56 0c-2.59 0-4.983.885-6.921 2.38L10.474.618a.286.286 0 00-.305-.125.303.303 0 00-.226.249L8.797 7.779a.317.317 0 00.07.258.287.287 0 00.22.102h.018l3.112-.206 3.504-.233a.296.296 0 00.258-.204.315.315 0 00-.078-.33l-1.46-1.415a7.18 7.18 0 014.118-1.301c1.07 0 2.083.238 3.003.662a.284.284 0 00.23.003.306.306 0 00.162-.171l1.35-3.568a.314.314 0 00-.159-.398" transform="matrix(-1 0 0 1 32.117 0)"/><path id="Fill-54" d="M12.787 13.315a12.66 12.66 0 001.346 4.7c1.225 2.393 3.101 4.186 5.274 5.27l-.929 1.908a.318.318 0 00.04.342c.077.094.201.13.315.09l6.454-2.269a.3.3 0 00.183-.187.32.32 0 00-.018-.251l-.01-.016-1.644-2.779-1.852-3.127a.29.29 0 00-.295-.142.298.298 0 00-.24.228l-.498 2.018a7.607 7.607 0 01-3.041-3.19 8.083 8.083 0 01-.865-3.087.314.314 0 00-.105-.214.286.286 0 00-.22-.068l-3.636.44a.305.305 0 00-.26.334" transform="matrix(-1 0 0 1 38.273 0)"/><path id="Fill-56" d="M2.646 22.333a12.003 12.003 0 003.205-3.576 12.684 12.684 0 001.713-7.424l2.04-.112a.293.293 0 00.262-.207.316.316 0 00-.084-.332L4.681 5.958a.28.28 0 00-.247-.072.291.291 0 00-.198.14l-.01.018-1.47 2.883-1.654 3.247a.32.32 0 00.03.339.285.285 0 00.307.104l1.916-.558a8.078 8.078 0 01-1.111 4.358 7.652 7.652 0 01-2.117 2.33.31.31 0 00-.123.203.325.325 0 00.053.234l2.183 3.08a.285.285 0 00.406.069" transform="matrix(-1 0 0 1 9.882 0)"/></g><text id="render-copy-2" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="297.3" y="285">render</tspan></text><g id="np_paint_2296353_000000-copy-2" fill="#C06334" fill-rule="nonzero" transform="translate(256 267)"><path id="Shape" d="M3.079 0A3.095 3.095 0 000 3.079V22.92A3.095 3.095 0 003.079 26H22.92A3.095 3.095 0 0026 22.921V3.08A3.095 3.095 0 0022.921 0H3.08zm0 2.053H22.92c.587 0 1.026.44 1.026 1.026v13.043l-2.886-2.684a1.027 1.027 0 00-1.443.032l-5.72 5.955-5.035-4.148a1.021 1.021 0 00-1.38.075l-5.43 5.43V3.08c0-.587.44-1.026 1.026-1.026zM6.158 3.42A3.095 3.095 0 003.078 6.5a3.095 3.095 0 003.08 3.079A3.095 3.095 0 009.237 6.5a3.095 3.095 0 00-3.08-3.079zm0 2.053c.579 0 1.026.447 1.026 1.026s-.447 1.026-1.026 1.026A1.012 1.012 0 015.132 6.5c0-.579.447-1.026 1.026-1.026zm14.176.566c-.377 0-.759.722-.759 1.08-.46.112-.748.358-.748.716s.371.717.748.717h2.662c.73 0 1.11-1.433-.021-1.433-.022-.677-.445-.86-1.134-.716-.183-.284-.375-.386-.748-.364zM16.71 9.237c-.314 0-.631.584-.631.876-.382.093-.63.296-.63.588 0 .293.316.589.63.589h2.213c.608 0 .931-1.176-.01-1.176-.018-.555-.379-.706-.952-.588-.152-.232-.31-.308-.62-.29zm3.688 6.382l3.55 3.293v4.01c0 .586-.44 1.025-1.027 1.025H3.08a1.01 1.01 0 01-.855-.449l6.05-6.04 4.202 3.453-1.24 1.294 1.475 1.422 7.687-8.008z"/></g><text id="microtasks-copy-2" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="297.5" y="249">microtasks</tspan></text><g id="Group-Copy-2" fill="#C06334" transform="translate(256 233)"><path id="Fill-52" d="M23.145.978C21.737.348 20.186 0 18.56 0c-2.59 0-4.983.885-6.921 2.38L10.474.618a.286.286 0 00-.305-.125.303.303 0 00-.226.249L8.797 7.779a.317.317 0 00.07.258.287.287 0 00.22.102h.018l3.112-.206 3.504-.233a.296.296 0 00.258-.204.315.315 0 00-.078-.33l-1.46-1.415a7.18 7.18 0 014.118-1.301c1.07 0 2.083.238 3.003.662a.284.284 0 00.23.003.306.306 0 00.162-.171l1.35-3.568a.314.314 0 00-.159-.398" transform="matrix(-1 0 0 1 32.117 0)"/><path id="Fill-54" d="M12.787 13.315a12.66 12.66 0 001.346 4.7c1.225 2.393 3.101 4.186 5.274 5.27l-.929 1.908a.318.318 0 00.04.342c.077.094.201.13.315.09l6.454-2.269a.3.3 0 00.183-.187.32.32 0 00-.018-.251l-.01-.016-1.644-2.779-1.852-3.127a.29.29 0 00-.295-.142.298.298 0 00-.24.228l-.498 2.018a7.607 7.607 0 01-3.041-3.19 8.083 8.083 0 01-.865-3.087.314.314 0 00-.105-.214.286.286 0 00-.22-.068l-3.636.44a.305.305 0 00-.26.334" transform="matrix(-1 0 0 1 38.273 0)"/><path id="Fill-56" d="M2.646 22.333a12.003 12.003 0 003.205-3.576 12.684 12.684 0 001.713-7.424l2.04-.112a.293.293 0 00.262-.207.316.316 0 00-.084-.332L4.681 5.958a.28.28 0 00-.247-.072.291.291 0 00-.198.14l-.01.018-1.47 2.883-1.654 3.247a.32.32 0 00.03.339.285.285 0 00.307.104l1.916-.558a8.078 8.078 0 01-1.111 4.358 7.652 7.652 0 01-2.117 2.33.31.31 0 00-.123.203.325.325 0 00.053.234l2.183 3.08a.285.285 0 00.406.069" transform="matrix(-1 0 0 1 9.882 0)"/></g><path id="Path" fill="#C06334" fill-rule="nonzero" d="M240.536 23.5c18.067 0 28.19 5.407 30.493 19.865l.067.441.016.179.264 10.223 5.906-11.165 2.652 1.402-8.582 16.229-1.247 2.357-1.367-2.29-9.412-15.76 2.576-1.539 6.476 10.845-.263-10.128-.046-.305c-1.998-12.562-10.586-17.259-27.028-17.353l-.505-.001h-51.072c-18.854 0-27.785 5.925-27.961 22.709l-.003.512v294.675c0 9.183 2.661 14.683 7.881 17.703 4.3 2.487 10.047 3.365 19.488 3.4l.595.001h51.072c9.796 0 15.693-.862 20.083-3.401 5.127-2.966 7.785-8.324 7.878-17.214l.003-.489v-6.472h3v6.472c0 10.183-3.152 16.697-9.379 20.3-4.884 2.825-11.037 3.766-20.972 3.803l-.613.001h-51.072c-10.296 0-16.602-.921-21.585-3.804-6.122-3.542-9.271-9.897-9.376-19.785l-.003-.515V49.721c0-18.944 10.246-26.078 30.352-26.219l.612-.002h51.072z"/><text id="script" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="245.3" y="105">script</tspan></text><text id="setTimeout" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="228.5" y="321">setTimeout</tspan></text></g></g></svg> \ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" width="407" height="391" viewBox="0 0 407 391"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="promise" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="eventLoop-full.svg"><text id="..." fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="257.4" y="71">...</tspan></text><path id="Rectangle-1-Copy-6" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M216 86h108v28H216z"/><path id="Rectangle-1-Copy-8" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M216 195h108v28H216z"/><text id="mousemove" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="232.2" y="214">mousemove</tspan></text><path id="Rectangle-1-Copy-9" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M216 302h108v28H216z"/><text id="event-loop" fill="#181717" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal"><tspan x="37.422" y="189">evento</tspan> <tspan x="46.615" y="222">bucle</tspan></text><text id="render-copy-2" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="297.3" y="176">mostrar</tspan></text><g id="np_paint_2296353_000000-copy-2" fill="#C06334" fill-rule="nonzero" transform="translate(256 158)"><path id="Shape" d="M3.079 0A3.095 3.095 0 000 3.079V22.92A3.095 3.095 0 003.079 26H22.92A3.095 3.095 0 0026 22.921V3.08A3.095 3.095 0 0022.921 0H3.08zm0 2.053H22.92c.587 0 1.026.44 1.026 1.026v13.043l-2.886-2.684a1.027 1.027 0 00-1.443.032l-5.72 5.955-5.035-4.148a1.021 1.021 0 00-1.38.075l-5.43 5.43V3.08c0-.587.44-1.026 1.026-1.026zM6.158 3.42A3.095 3.095 0 003.078 6.5a3.095 3.095 0 003.08 3.079A3.095 3.095 0 009.237 6.5a3.095 3.095 0 00-3.08-3.079zm0 2.053c.579 0 1.026.447 1.026 1.026s-.447 1.026-1.026 1.026A1.012 1.012 0 015.132 6.5c0-.579.447-1.026 1.026-1.026zm14.176.566c-.377 0-.759.722-.759 1.08-.46.112-.748.358-.748.716s.371.717.748.717h2.662c.73 0 1.11-1.433-.021-1.433-.022-.677-.445-.86-1.134-.716-.183-.284-.375-.386-.748-.364zM16.71 9.237c-.314 0-.631.584-.631.876-.382.093-.63.296-.63.588 0 .293.316.589.63.589h2.213c.608 0 .931-1.176-.01-1.176-.018-.555-.379-.706-.952-.588-.152-.232-.31-.308-.62-.29zm3.688 6.382l3.55 3.293v4.01c0 .586-.44 1.025-1.027 1.025H3.08a1.01 1.01 0 01-.855-.449l6.05-6.04 4.202 3.453-1.24 1.294 1.475 1.422 7.687-8.008z"/></g><text id="microtasks-copy-2" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="297.5" y="140">microtareas</tspan></text><g id="Group-Copy-2" fill="#C06334" transform="translate(256 124)"><path id="Fill-52" d="M23.145.978C21.737.348 20.186 0 18.56 0c-2.59 0-4.983.885-6.921 2.38L10.474.618a.286.286 0 00-.305-.125.303.303 0 00-.226.249L8.797 7.779a.317.317 0 00.07.258.287.287 0 00.22.102h.018l3.112-.206 3.504-.233a.296.296 0 00.258-.204.315.315 0 00-.078-.33l-1.46-1.415a7.18 7.18 0 014.118-1.301c1.07 0 2.083.238 3.003.662a.284.284 0 00.23.003.306.306 0 00.162-.171l1.35-3.568a.314.314 0 00-.159-.398" transform="matrix(-1 0 0 1 32.117 0)"/><path id="Fill-54" d="M12.787 13.315a12.66 12.66 0 001.346 4.7c1.225 2.393 3.101 4.186 5.274 5.27l-.929 1.908a.318.318 0 00.04.342c.077.094.201.13.315.09l6.454-2.269a.3.3 0 00.183-.187.32.32 0 00-.018-.251l-.01-.016-1.644-2.779-1.852-3.127a.29.29 0 00-.295-.142.298.298 0 00-.24.228l-.498 2.018a7.607 7.607 0 01-3.041-3.19 8.083 8.083 0 01-.865-3.087.314.314 0 00-.105-.214.286.286 0 00-.22-.068l-3.636.44a.305.305 0 00-.26.334" transform="matrix(-1 0 0 1 38.273 0)"/><path id="Fill-56" d="M2.646 22.333a12.003 12.003 0 003.205-3.576 12.684 12.684 0 001.713-7.424l2.04-.112a.293.293 0 00.262-.207.316.316 0 00-.084-.332L4.681 5.958a.28.28 0 00-.247-.072.291.291 0 00-.198.14l-.01.018-1.47 2.883-1.654 3.247a.32.32 0 00.03.339.285.285 0 00.307.104l1.916-.558a8.078 8.078 0 01-1.111 4.358 7.652 7.652 0 01-2.117 2.33.31.31 0 00-.123.203.325.325 0 00.053.234l2.183 3.08a.285.285 0 00.406.069" transform="matrix(-1 0 0 1 9.882 0)"/></g><text id="render-copy-2" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="297.3" y="285">mostrar</tspan></text><g id="np_paint_2296353_000000-copy-2" fill="#C06334" fill-rule="nonzero" transform="translate(256 267)"><path id="Shape" d="M3.079 0A3.095 3.095 0 000 3.079V22.92A3.095 3.095 0 003.079 26H22.92A3.095 3.095 0 0026 22.921V3.08A3.095 3.095 0 0022.921 0H3.08zm0 2.053H22.92c.587 0 1.026.44 1.026 1.026v13.043l-2.886-2.684a1.027 1.027 0 00-1.443.032l-5.72 5.955-5.035-4.148a1.021 1.021 0 00-1.38.075l-5.43 5.43V3.08c0-.587.44-1.026 1.026-1.026zM6.158 3.42A3.095 3.095 0 003.078 6.5a3.095 3.095 0 003.08 3.079A3.095 3.095 0 009.237 6.5a3.095 3.095 0 00-3.08-3.079zm0 2.053c.579 0 1.026.447 1.026 1.026s-.447 1.026-1.026 1.026A1.012 1.012 0 015.132 6.5c0-.579.447-1.026 1.026-1.026zm14.176.566c-.377 0-.759.722-.759 1.08-.46.112-.748.358-.748.716s.371.717.748.717h2.662c.73 0 1.11-1.433-.021-1.433-.022-.677-.445-.86-1.134-.716-.183-.284-.375-.386-.748-.364zM16.71 9.237c-.314 0-.631.584-.631.876-.382.093-.63.296-.63.588 0 .293.316.589.63.589h2.213c.608 0 .931-1.176-.01-1.176-.018-.555-.379-.706-.952-.588-.152-.232-.31-.308-.62-.29zm3.688 6.382l3.55 3.293v4.01c0 .586-.44 1.025-1.027 1.025H3.08a1.01 1.01 0 01-.855-.449l6.05-6.04 4.202 3.453-1.24 1.294 1.475 1.422 7.687-8.008z"/></g><text id="microtasks-copy-2" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="297.5" y="249">microtareas</tspan></text><g id="Group-Copy-2" fill="#C06334" transform="translate(256 233)"><path id="Fill-52" d="M23.145.978C21.737.348 20.186 0 18.56 0c-2.59 0-4.983.885-6.921 2.38L10.474.618a.286.286 0 00-.305-.125.303.303 0 00-.226.249L8.797 7.779a.317.317 0 00.07.258.287.287 0 00.22.102h.018l3.112-.206 3.504-.233a.296.296 0 00.258-.204.315.315 0 00-.078-.33l-1.46-1.415a7.18 7.18 0 014.118-1.301c1.07 0 2.083.238 3.003.662a.284.284 0 00.23.003.306.306 0 00.162-.171l1.35-3.568a.314.314 0 00-.159-.398" transform="matrix(-1 0 0 1 32.117 0)"/><path id="Fill-54" d="M12.787 13.315a12.66 12.66 0 001.346 4.7c1.225 2.393 3.101 4.186 5.274 5.27l-.929 1.908a.318.318 0 00.04.342c.077.094.201.13.315.09l6.454-2.269a.3.3 0 00.183-.187.32.32 0 00-.018-.251l-.01-.016-1.644-2.779-1.852-3.127a.29.29 0 00-.295-.142.298.298 0 00-.24.228l-.498 2.018a7.607 7.607 0 01-3.041-3.19 8.083 8.083 0 01-.865-3.087.314.314 0 00-.105-.214.286.286 0 00-.22-.068l-3.636.44a.305.305 0 00-.26.334" transform="matrix(-1 0 0 1 38.273 0)"/><path id="Fill-56" d="M2.646 22.333a12.003 12.003 0 003.205-3.576 12.684 12.684 0 001.713-7.424l2.04-.112a.293.293 0 00.262-.207.316.316 0 00-.084-.332L4.681 5.958a.28.28 0 00-.247-.072.291.291 0 00-.198.14l-.01.018-1.47 2.883-1.654 3.247a.32.32 0 00.03.339.285.285 0 00.307.104l1.916-.558a8.078 8.078 0 01-1.111 4.358 7.652 7.652 0 01-2.117 2.33.31.31 0 00-.123.203.325.325 0 00.053.234l2.183 3.08a.285.285 0 00.406.069" transform="matrix(-1 0 0 1 9.882 0)"/></g><path id="Path" fill="#C06334" fill-rule="nonzero" d="M240.536 23.5c18.067 0 28.19 5.407 30.493 19.865l.067.441.016.179.264 10.223 5.906-11.165 2.652 1.402-8.582 16.229-1.247 2.357-1.367-2.29-9.412-15.76 2.576-1.539 6.476 10.845-.263-10.128-.046-.305c-1.998-12.562-10.586-17.259-27.028-17.353l-.505-.001h-51.072c-18.854 0-27.785 5.925-27.961 22.709l-.003.512v294.675c0 9.183 2.661 14.683 7.881 17.703 4.3 2.487 10.047 3.365 19.488 3.4l.595.001h51.072c9.796 0 15.693-.862 20.083-3.401 5.127-2.966 7.785-8.324 7.878-17.214l.003-.489v-6.472h3v6.472c0 10.183-3.152 16.697-9.379 20.3-4.884 2.825-11.037 3.766-20.972 3.803l-.613.001h-51.072c-10.296 0-16.602-.921-21.585-3.804-6.122-3.542-9.271-9.897-9.376-19.785l-.003-.515V49.721c0-18.944 10.246-26.078 30.352-26.219l.612-.002h51.072z"/><text id="script" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="245.3" y="105">script</tspan></text><text id="setTimeout" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="228.5" y="321">setTimeout</tspan></text></g></g></svg> \ No newline at end of file diff --git a/2-ui/99-ui-misc/03-event-loop/eventLoop.svg b/2-ui/99-ui-misc/03-event-loop/eventLoop.svg index 6dc459ef8..d26c4fb79 100644 --- a/2-ui/99-ui-misc/03-event-loop/eventLoop.svg +++ b/2-ui/99-ui-misc/03-event-loop/eventLoop.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="500" height="279" viewBox="0 0 500 279"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="promise" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="eventLoop.svg"><path id="Rectangle-1-Copy-5" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M218 173h108v28H218z"/><text id="..." fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="258.9" y="189">...</tspan></text><path id="Rectangle-1-Copy-6" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M218 90h108v28H218z"/><path id="Rectangle-1-Copy-8" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M218 117h108v28H218z"/><text id="mousemove" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="233.7" y="136">mousemove</tspan></text><path id="Rectangle-1-Copy-9" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M218 145h108v28H218z"/><text id="script" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="246.8" y="109">script</tspan></text><text id="event-loop" fill="#C06334" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal"><tspan x="76.422" y="134">event</tspan> <tspan x="82.615" y="167">loop</tspan></text><text id="macrotask-queue" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal"><tspan x="348.371" y="136">macrotask</tspan> <tspan x="371.451" y="169">queue</tspan></text><path id="Path" fill="#C06334" fill-rule="nonzero" d="M192 40c22.467 0 43.818 7.359 61.285 20.712l.622.479.143.122 8.961 8.422-4.584-14.61 3.817-1.197 6.869 21.895 1.064 3.392-3.452-.852-22.278-5.5.959-3.883 14.864 3.669-8.886-8.352-.55-.424c-16.564-12.656-36.757-19.698-58.036-19.87L192 44c-30.748 0-59.095 14.403-77.315 38.412l-.549.73-3.208-2.387C129.87 55.303 159.664 40 192 40z"/><path id="Path-Copy-2" fill="#C06334" fill-rule="nonzero" d="M269.882 208.148l2.823 2.834-.366.365a533.245 533.245 0 00-3.982 4.033l-.333.34c-9.922 10.12-14.79 14.544-22.017 19.272-15.185 9.934-34.01 15.688-51.594 15.688-25.222 0-47.144-6.827-64.077-19.673l-.589-.45-.089-.07-.08-.078-8.854-8.581 4.36 14.68-3.835 1.138-6.532-21.998-1.012-3.409 3.439.906 22.19 5.841-1.018 3.868-14.808-3.898 8.766 8.497.488.374c16.02 12.15 36.782 18.698 60.792 18.85l.859.003c16.796 0 34.862-5.522 49.404-15.035 6.911-4.521 11.624-8.806 21.35-18.725l.334-.34a633.126 633.126 0 013.46-3.511l.736-.736.185-.185z"/><text id="setTimeout" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="230" y="164">setTimeout</tspan></text></g></g></svg> \ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" width="479" height="279" viewBox="0 0 479 279"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="promise" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="eventLoop.svg"><path id="Rectangle-1-Copy-5" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M218 173h108v28H218z"/><text id="..." fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="258.9" y="189">...</tspan></text><path id="Rectangle-1-Copy-6" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M218 90h108v28H218z"/><path id="Rectangle-1-Copy-8" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M218 117h108v28H218z"/><text id="mousemove" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="233.7" y="136">mousemove</tspan></text><path id="Rectangle-1-Copy-9" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M218 145h108v28H218z"/><text id="script" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="246.8" y="109">script</tspan></text><text id="event-loop" fill="#C06334" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal"><tspan x="61.922" y="134">evento</tspan> <tspan x="71.115" y="167">bucle</tspan></text><text id="macrotask-queue" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal"><tspan x="335.871" y="136">tarea macro</tspan> <tspan x="380.951" y="169">cola</tspan></text><path id="Path" fill="#C06334" fill-rule="nonzero" d="M192 40c22.467 0 43.818 7.359 61.285 20.712l.622.479.143.122 8.961 8.422-4.584-14.61 3.817-1.197 6.869 21.895 1.064 3.392-3.452-.852-22.278-5.5.959-3.883 14.864 3.669-8.886-8.352-.55-.424c-16.564-12.656-36.757-19.698-58.036-19.87L192 44c-30.748 0-59.095 14.403-77.315 38.412l-.549.73-3.208-2.387C129.87 55.303 159.664 40 192 40z"/><path id="Path-Copy-2" fill="#C06334" fill-rule="nonzero" d="M269.882 208.148l2.823 2.834-.366.365a533.245 533.245 0 00-3.982 4.033l-.333.34c-9.922 10.12-14.79 14.544-22.017 19.272-15.185 9.934-34.01 15.688-51.594 15.688-25.222 0-47.144-6.827-64.077-19.673l-.589-.45-.089-.07-.08-.078-8.854-8.581 4.36 14.68-3.835 1.138-6.532-21.998-1.012-3.409 3.439.906 22.19 5.841-1.018 3.868-14.808-3.898 8.766 8.497.488.374c16.02 12.15 36.782 18.698 60.792 18.85l.859.003c16.796 0 34.862-5.522 49.404-15.035 6.911-4.521 11.624-8.806 21.35-18.725l.334-.34a633.126 633.126 0 013.46-3.511l.736-.736.185-.185z"/><text id="setTimeout" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="230" y="164">setTimeout</tspan></text></g></g></svg> \ No newline at end of file diff --git a/2-ui/99-ui-misc/index.md b/2-ui/99-ui-misc/index.md index 79cd72fe7..eef30bcfb 100644 --- a/2-ui/99-ui-misc/index.md +++ b/2-ui/99-ui-misc/index.md @@ -1,2 +1,2 @@ -# Miscellaneous +# Temas diversos diff --git a/2-ui/index.md b/2-ui/index.md index d94378cf3..76e49ace7 100644 --- a/2-ui/index.md +++ b/2-ui/index.md @@ -1,3 +1,3 @@ -# Browser: Document, Events, Interfaces +# El navegador: Documentos, Eventos e Interfaces -Learning how to manage the browser page: add elements, manipulate their size and position, dynamically create interfaces and interact with the visitor. +Aprenderemos a manejar la página del navegador: agregar elementos, manipular su tamaño y posición, crear interfaces dinámicamente e interactuar con el visitante. diff --git a/3-frames-and-windows/01-popup-windows/article.md b/3-frames-and-windows/01-popup-windows/article.md index f2c87d1e0..36c347ecb 100644 --- a/3-frames-and-windows/01-popup-windows/article.md +++ b/3-frames-and-windows/01-popup-windows/article.md @@ -1,108 +1,108 @@ -# Popups and window methods +# Ventanas emergentes y métodos de ventana -A popup window is one of the oldest methods to show additional document to user. +Una ventana emergente (popup window) es uno de los métodos más antiguos para mostrar documentos adicionales al usuario. -Basically, you just run: +Básicamente, solo ejecutas : ```js -window.open('https://javascript.info/') +window.open("https://javascript.info/"); ``` -...And it will open a new window with given URL. Most modern browsers are configured to open url in new tabs instead of separate windows. +...Y eso abrirá una nueva ventana con la URL dada. La mayoría de los navegadores modernos están configurados para abrir pestañas nuevas en vez de ventanas separadas. -Popups exist from really ancient times. The initial idea was to show another content without closing the main window. As of now, there are other ways to do that: we can load content dynamically with [fetch](info:fetch) and show it in a dynamically generated `<div>`. So, popups is not something we use everyday. +Los popups existen desde tiempos realmente antiguos. La idea inicial fue mostrar otro contenido sin cerrar la ventana principal. Ahora hay otras formas de hacerlo: podemos cargar contenido dinámicamente con [fetch](info:fetch) y mostrarlo de forma dinámica con `<div>`. Entonces, los popups no son algo que usamos todos los días. -Also, popups are tricky on mobile devices, that don't show multiple windows simultaneously. +Además, los popups son complicados en dispositivos móviles, que no muestran varias ventanas simultáneamente. -Still, there are tasks where popups are still used, e.g. for OAuth authorization (login with Google/Facebook/...), because: +Aún así, hay tareas donde los popups todavía son usados, por ejemplo para autorización o autenticación (Ingreso con Google/Facebook/...), porque: -1. A popup is a separate window which has its own independent JavaScript environment. So opening a popup from a third-party, non-trusted site is safe. -2. It's very easy to open a popup. -3. A popup can navigate (change URL) and send messages to the opener window. +1. Un popup es una ventana separada con su propio entorno JavaScript independiente. Por lo tanto es seguro abrir un popup desde un sitio de terceros no confiable. +2. Es muy fácil abrir un popup. +3. Un popup puede navegar (cambiar URL) y enviar mensajes a la ventana que lo abrió. -## Popup blocking +## Bloqueo de ventanas emergentes (Popup) -In the past, evil sites abused popups a lot. A bad page could open tons of popup windows with ads. So now most browsers try to block popups and protect the user. +En el pasado, sitios malvados abusaron mucho de las ventanas emergentes. Una página incorrecta podría abrir toneladas de ventanas emergentes con anuncios. Entonces, la mayoría de los navegadores intentan bloquear las ventanas emergentes y proteger al usuario. -**Most browsers block popups if they are called outside of user-triggered event handlers like `onclick`.** +**La mayoría de los navegadores bloquean las ventanas emergentes si se llaman fuera de los controladores de eventos activados por el usuario, como `onclick`.** -For example: +Por ejemplo: ```js // popup blocked -window.open('https://javascript.info'); +window.open("https://javascript.info"); // popup allowed button.onclick = () => { - window.open('https://javascript.info'); + window.open("https://javascript.info"); }; ``` -This way users are somewhat protected from unwanted popups, but the functionality is not disabled totally. +De esta manera, los usuarios están algo protegidos de ventanas emergentes no deseadas, pero la funcionalidad no está totalmente deshabilitada. ## window.open -The syntax to open a popup is: `window.open(url, name, params)`: +La sintaxis para abrir una ventana emergente es: `window.open(url, name, params)`: url -: An URL to load into the new window. +: Una URL para cargar en la nueva ventana. name -: A name of the new window. Each window has a `window.name`, and here we can specify which window to use for the popup. If there's already a window with such name -- the given URL opens in it, otherwise a new window is opened. +: Un nombre de la nueva ventana. Cada ventana tiene un `window.name`, y aquí podemos especificar cuál ventana usar para la ventana emergente. Si hay una ventana con ese nombre, la URL dada se abre en ella, de lo contrario abre una nueva ventana. params -: The configuration string for the new window. It contains settings, delimited by a comma. There must be no spaces in params, for instance: `width=200,height=100`. +: La cadena de configuración para nueva ventana. Contiene configuraciones, delimitado por una coma. No debe haber espacios en los parámetros, por ejemplo: `width=200,height=100`. -Settings for `params`: +Configuración de `params`: -- Position: - - `left/top` (numeric) -- coordinates of the window top-left corner on the screen. There is a limitation: a new window cannot be positioned offscreen. - - `width/height` (numeric) -- width and height of a new window. There is a limit on minimal width/height, so it's impossible to create an invisible window. -- Window features: - - `menubar` (yes/no) -- shows or hides the browser menu on the new window. - - `toolbar` (yes/no) -- shows or hides the browser navigation bar (back, forward, reload etc) on the new window. - - `location` (yes/no) -- shows or hides the URL field in the new window. FF and IE don't allow to hide it by default. - - `status` (yes/no) -- shows or hides the status bar. Again, most browsers force it to show. - - `resizable` (yes/no) -- allows to disable the resize for the new window. Not recommended. - - `scrollbars` (yes/no) -- allows to disable the scrollbars for the new window. Not recommended. +- Posición: + - `left/top` (numérico) -- coordenadas de la esquina superior izquierda de la ventana en la pantalla.Hay una limitación: no se puede colocar una nueva ventana fuera de la pantalla. + - `width/height` (numérico) -- ancho y alto de una nueva ventana. Hay un límite mínimo de ancho/alto , así que es imposible crear una ventana invisible. +- Características de la ventana: + - `menubar` (yes/no) -- muestra u oculta el menú del navegador en la nueva ventana. + - `toolbar` (yes/no) -- muestra u oculta la barra de navegación del navegador (atrás, adelante, recargar, etc.) en la nueva ventana. + - `location` (yes/no) -- muestra u oculta el campo URL en la nueva ventana. FF e IE no permiten ocultarlo por defecto. + - `status` (yes/no) -- muestra u oculta la barra de estado. De nuevo, la mayoría de los navegadores lo obligan a mostrar. + - `resizable` (yes/no) -- permite deshabilitar el cambio de tamaño para la nueva ventana. No recomendado. + - `scrollbars` (yes/no) -- permite deshabilitar las barras de desplazamiento para la nueva ventana. No recomendado. -There is also a number of less supported browser-specific features, which are usually not used. Check <a href="https://developer.mozilla.org/en/DOM/window.open">window.open in MDN</a> for examples. +También hay una serie de características específicas del navegador menos compatibles, que generalmente no se usan. Revisa <a href="https://developer.mozilla.org/es/docs/Web/API/Window/open">window.open en MDN</a> para ejemplos. -## Example: a minimalistic window +## Ejemplo: Una ventana minimalista -Let's open a window with minimal set of features, just to see which of them browser allows to disable: +Abramos una ventana con un conjunto mínimo de características solo para ver cuál de ellos permite desactivar el navegador: ```js run let params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no, width=0,height=0,left=-1000,top=-1000`; -open('/', 'test', params); +open("/", "test", params); ``` -Here most "window features" are disabled and window is positioned offscreen. Run it and see what really happens. Most browsers "fix" odd things like zero `width/height` and offscreen `left/top`. For instance, Chrome open such a window with full width/height, so that it occupies the full screen. +Aquí la mayoría de las "características de la ventana" están deshabilitadas y la ventana se coloca fuera de la pantalla. Ejecútelo y vea lo que realmente sucede. La mayoría de los navegadores "arreglan" cosas extrañas como cero `ancho/alto` y fuera de pantalla `Izquierda/superior`. Por ejemplo, Chrome abre una ventana con ancho/alto completo, para que ocupe la pantalla completa. -Let's add normal positioning options and reasonable `width`, `height`, `left`, `top` coordinates: +Agreguemos opciones de posicionamiento normal y coordenadas razonables de `ancho`,`altura`, `izquierda`,`arriba`: ```js run let params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no, width=600,height=300,left=100,top=100`; -open('/', 'test', params); +open("/", "test", params); ``` -Most browsers show the example above as required. +La mayoría de los navegadores muestran el ejemplo anterior según sea necesario. -Rules for omitted settings: +Reglas para configuraciones omitidas: -- If there is no 3rd argument in the `open` call, or it is empty, then the default window parameters are used. -- If there is a string of params, but some `yes/no` features are omitted, then the omitted features assumed to have `no` value. So if you specify params, make sure you explicitly set all required features to yes. -- If there is no `left/top` in params, then the browser tries to open a new window near the last opened window. -- If there is no `width/height`, then the new window will be the same size as the last opened. +- Si no hay un tercer argumento en la llamada a `open` o está vacío, se usan los parámetros de ventana predeterminados. +- Si hay una cadena de params, pero se omiten algunas características sí/no (`yes/no`), las características omitidas se asumen con valor `no` . Entonces, si especifica parámetros, asegúrese de establecer explícitamente todas las funciones requeridas en `yes`. +- Si no hay `izquierda/arriba` en params, entonces el navegador intenta abrir una nueva ventana cerca de la última ventana abierta. +- Si no hay `ancho/altura`, entonces la nueva ventana tendrá el mismo tamaño que la última abierta. -## Accessing popup from window +## Acceder a la ventana emergente desde la ventana -The `open` call returns a reference to the new window. It can be used to manipulate its properties, change location and even more. +La llamada `open` devuelve una referencia a la nueva ventana. Se puede usar para manipular sus propiedades, cambiar de ubicación y aún más. -In this example, we generate popup content from JavaScript: +En este ejemplo, generamos contenido emergente a partir de JavaScript: ```js let newWin = window.open("about:blank", "hello", "width=200,height=200"); @@ -110,35 +110,35 @@ let newWin = window.open("about:blank", "hello", "width=200,height=200"); newWin.document.write("Hello, world!"); ``` -And here we modify the contents after loading: +Y aquí modificamos el contenido después de la carga: ```js run -let newWindow = open('/', 'example', 'width=300,height=300') +let newWindow = open("/", "example", "width=300,height=300"); newWindow.focus(); alert(newWindow.location.href); // (*) about:blank, loading hasn't started yet newWindow.onload = function() { let html = `<div style="font-size:30px">Welcome!</div>`; -*!* - newWindow.document.body.insertAdjacentHTML('afterbegin', html); -*/!* +*!* + newWindow.document.body.insertAdjacentHTML("afterbegin", html); +*/!* }; ``` -Please note: immediately after `window.open`, the new window isn't loaded yet. That's demonstrated by `alert` in line `(*)`. So we wait for `onload` to modify it. We could also use `DOMContentLoaded` handler for `newWin.document`. +Por favor, tenga en cuenta: inmediatamente después de `window.open` la nueva ventana no está cargada aún. Esto queda demostrado por el `alert` en la linea `(*)`. Así que esperamos a que `onload` lo modifique. También podríamos usar `DOMContentLoaded` de los manejadores de `newWin.document`. -```warn header="Same origin policy" -Windows may freely access content of each other only if they come from the same origin (the same protocol://domain:port). +```warn header="Política mismo origen" +Las ventanas pueden acceder libremente a los contenidos de las demás sólo si provienen del mismo origen (el mismo protocolo://domain:port). -Otherwise, e.g. if the main window is from `site.com`, and the popup from `gmail.com`, that's impossible for user safety reasons. For the details, see chapter <info:cross-window-communication>. +De lo contrario es imposible por razones de seguridad del usuario, por ejemplo si la ventana principal es de `site.com` y la ventana emergente (popup) es de `gmail.com`. Para los detalles, ver capitulo <info:cross-window-communication>. ``` -## Accessing window from popup +## Acceder a la ventana desde el popup -A popup may access the "opener" window as well using `window.opener` reference. It is `null` for all windows except popups. +Un popup también puede acceder la ventana que lo abrió usando la referencia `window.opener`. Es `null` para todas las ventanas excepto los popups. -If you run the code below, it replaces the opener (current) window content with "Test": +Si ejecutas el código de abajo, reemplaza el contenido de la ventana del opener (actual) con "Test": ```js run let newWin = window.open("about:blank", "hello", "width=200,height=200"); @@ -148,111 +148,111 @@ newWin.document.write( ); ``` -So the connection between the windows is bidirectional: the main window and the popup have a reference to each other. +Así que la conexión entre las ventanas es bidireccional: la ventana principal y el popup tienen una referencia entre sí. -## Closing a popup +## Cerrar una popup -To close a window: `win.close()`. +Para cerrar una ventana: `win.close()`. -To check if a window is closed: `win.closed`. +Para comprobar si una ventana esta cerrada: `win.closed`. -Technically, the `close()` method is available for any `window`, but `window.close()` is ignored by most browsers if `window` is not created with `window.open()`. So it'll only work on a popup. +Técnicamente, el `close()` es un método disponible para cualquier `ventana`, pero `window.close()` es ignorado por la mayoría de los navegadores si `window` no es creada con `window.open()`. Así que solo funcionará en una popup. -The `closed` property is `true` if the window is closed. That's useful to check if the popup (or the main window) is still open or not. A user can close it anytime, and our code should take that possibility into account. +El `closed` es una propiedad `true` si la ventana esta cerrada. Eso es usualmente para comprobar la popup (o la ventana principal) está todavía abierta o no. Un usuario puede cerrarla en cualquier momento, y nuestro código debería tener esa posibilidad en cuenta. -This code loads and then closes the window: +Este código se carga y luego cierra la ventana: ```js run -let newWindow = open('/', 'example', 'width=300,height=300'); +let newWindow = open("/", "example", "width=300,height=300"); -newWindow.onload = function() { +newWindow.onload = function () { newWindow.close(); alert(newWindow.closed); // true }; ``` -## Moving and resizing +## desplazamiento y cambio de tamaño -There are methods to move/resize a window: +Hay métodos para mover/redimensionar una ventana: `win.moveBy(x,y)` -: Move the window relative to current position `x` pixels to the right and `y` pixels down. Negative values are allowed (to move left/up). +: Mueve la ventana en relación con la posición actual `x` píxeles a la derecha y `y` píxeles hacia abajo. Valores negativos están permitidos(para mover a la izquierda/arriba). `win.moveTo(x,y)` -: Move the window to coordinates `(x,y)` on the screen. +: Mover la ventana por coordenadas `(x,y)` en la pantalla. `win.resizeBy(width,height)` -: Resize the window by given `width/height` relative to the current size. Negative values are allowed. +: Cambiar el tamaño de la ventana según el `width/height` dado en relación con el tamaño actual. Se permiten valores negativos. `win.resizeTo(width,height)` -: Resize the window to the given size. +: Redimensionar la ventana al tamaño dado. -There's also `window.onresize` event. +También existe el evento `window.onresize`. -```warn header="Only popups" -To prevent abuse, the browser usually blocks these methods. They only work reliably on popups that we opened, that have no additional tabs. +```warn header="Solo Popup" +Para evitar abusos, el navegador suele bloquear estos métodos. Solo funcionan de manera confiable en las ventanas emergentes que abrimos, que no tienen pestañas adicionales. ``` ```warn header="No minification/maximization" -JavaScript has no way to minify or maximize a window. These OS-level functions are hidden from Frontend-developers. +JavaScript no tiene forma de minimizar o maximizar una ventana. Estas funciones de nivel de sistema operativo están ocultas para los desarrolladores de frontend. -Move/resize methods do not work for maximized/minimized windows. +Los métodos de movimiento/cambio de tamaño no funcionan para ventanas maximizadas/minimizadas. ``` -## Scrolling a window +## desplazando una ventana -We already talked about scrolling a window in the chapter <info:size-and-scroll-window>. +Ya hemos hablado sobre el desplazamiento de una ventana en el capítulo <info:size-and-scroll-window>. `win.scrollBy(x,y)` -: Scroll the window `x` pixels right and `y` down relative the current scroll. Negative values are allowed. +: Desplaza la ventana `x` píxeles a la derecha y `y` hacia abajo en relación con el actual desplazamiento. Se permiten valores negativos. `win.scrollTo(x,y)` -: Scroll the window to the given coordinates `(x,y)`. +: Desplaza la ventana a las coordenadas dadas `(x,y)`. `elem.scrollIntoView(top = true)` -: Scroll the window to make `elem` show up at the top (the default) or at the bottom for `elem.scrollIntoView(false)`. +: Desplaza la ventana para hacer que `elem` aparezca en la parte superior (la predeterminada) o en la parte inferior para `elem.scrollIntoView(false)`. -There's also `window.onscroll` event. +También existe el evento `window.onscroll`. -## Focus/blur on a window +## Enfocar/desenfocar una ventana -Theoretically, there are `window.focus()` and `window.blur()` methods to focus/unfocus on a window. And there are also `focus/blur` events that allow to catch the moment when the visitor focuses on a window and switches elsewhere. +Teóricamente, están los métodos `window.focus()` y `window.blur()` para poner/sacar el foco de una ventana. Y los eventos `focus/blur` que permiten captar el momento en el que el visitante enfoca una ventana y en el que cambia a otro lugar. -Although, in practice they are severely limited, because in the past evil pages abused them. +En la práctica estos métodos están severamente limitado, porque en el pasado las páginas malignas abusaban de ellos. -For instance, look at this code: +Por ejemplo, mira este código: ```js run window.onblur = () => window.focus(); ``` -When a user attempts to switch out of the window (`window.onblur`), it brings the window back into focus. The intention is to "lock" the user within the `window`. +Cuando un usuario intenta salir de la ventana (`window.onblur`), lo vuelve a enfocar. La intención es "bloquear" al usuario dentro de la `window`. -So browsers had to introduce many limitations to forbid the code like that and protect the user from ads and evils pages. They depend on the browser. +Entonces, hay limitaciones que prohíben el código así. Existen muchas limitaciones para proteger al usuario de anuncios y páginas malignas. Ellos dependen del navegador. -For instance, a mobile browser usually ignores `window.focus()` completely. Also focusing doesn't work when a popup opens in a separate tab rather than a new window. +Por ejemplo, un navegador móvil generalmente ignora esa llamada por completo. Además, el enfoque no funciona cuando se abre una ventana emergente en una pestaña separada en lugar de en una nueva ventana. -Still, there are some use cases when such calls do work and can be useful. +Aún así hay algunas cosas que se pueden hacer. -For instance: +Por ejemplo: -- When we open a popup, it might be a good idea to run `newWindow.focus()` on it. Just in case, for some OS/browser combinations it ensures that the user is in the new window now. -- If we want to track when a visitor actually uses our web-app, we can track `window.onfocus/onblur`. That allows us to suspend/resume in-page activities, animations etc. But please note that the `blur` event means that the visitor switched out from the window, but they still may observe it. The window is in the background, but still may be visible. +- Cuando abrimos una popup, puede ser una buena idea ejecutar un `newWindow.focus()` en ella. Solo por si acaso. Para algunas combinaciones de sistema-operativo/navegador, asegura que el usuario ahora esté en la nueva ventana. +- Si queremos saber cuándo un visitante realmente usa nuestra aplicación web, podemos monitorear `window.onfocus/onblur`. Esto nos permite suspender/reanudar las actividades en la página, animaciones etc. Pero tenga en cuenta que el evento `blur` solamente significa que el visitante salió de la ventana. La ventana queda en segundo plano, pero aún puede ser visible. -## Summary +## Resumen -Popup windows are used rarely, as there are alternatives: loading and displaying information in-page, or in iframe. +Las ventanas emergentes se utilizan con poca frecuencia, ya que existen alternativas: cargar y mostrar información en la página o en iframe. -If we're going to open a popup, a good practice is to inform the user about it. An "opening window" icon near a link or button would allow the visitor to survive the focus shift and keep both windows in mind. +Si vamos a abrir una ventana emergente, una buena práctica es informar al usuario al respecto. Un icono de "ventana que se abre" cerca de un enlace o botón permitiría al visitante sobrevivir al cambio de enfoque y tener en cuenta ambas ventanas. -- A popup can be opened by the `open(url, name, params)` call. It returns the reference to the newly opened window. -- Browsers block `open` calls from the code outside of user actions. Usually a notification appears, so that a user may allow them. -- Browsers open a new tab by default, but if sizes are provided, then it'll be a popup window. -- The popup may access the opener window using the `window.opener` property. -- The main window and the popup can freely read and modify each other if they have the same origin. Otherwise, they can change location of each other and [exchange messages](info:cross-window-communication). +- Se puede abrir una ventana emergente con la llamada `open (url, name, params)`. Devuelve la referencia a la ventana recién abierta. +- Los navegadores bloquean las llamadas `open` desde el código fuera de las acciones del usuario. Por lo general aparece una notificación para que un usuario pueda permitirlos. +- Los navegadores abren una nueva pestaña de forma predeterminada, pero si se proporcionan tamaños, será una ventana emergente. +- La ventana emergente puede acceder a la ventana que la abre usando la propiedad `window.opener`. +- La ventana principal y la ventana emergente pueden leerse y modificarse libremente entre sí si tienen el mismo origen. De lo contrario, pueden cambiar de ubicación e [intercambiar mensajes](info:cross-window-communication). -To close the popup: use `close()` call. Also the user may close them (just like any other windows). The `window.closed` is `true` after that. +Para cerrar la ventana emergente: use `close ()`. Además, el usuario puede cerrarlas (como cualquier otra ventana). El `window.closed` es`true` después de eso. -- Methods `focus()` and `blur()` allow to focus/unfocus a window. But they don't work all the time. -- Events `focus` and `blur` allow to track switching in and out of the window. But please note that a window may still be visible even in the background state, after `blur`. +- Los métodos `focus ()` y `blur ()` permiten enfocar/desenfocar una ventana. Pero no funcionan todo el tiempo. +- Los eventos `focus` y`blur` permiten rastrear el cambio dentro y fuera de la ventana. Pero tenga en cuenta que una ventana puede seguir siendo visible incluso en el estado de fondo, después de "desenfoque". diff --git a/3-frames-and-windows/03-cross-window-communication/article.md b/3-frames-and-windows/03-cross-window-communication/article.md index 4d4e320e4..a624dc2d3 100644 --- a/3-frames-and-windows/03-cross-window-communication/article.md +++ b/3-frames-and-windows/03-cross-window-communication/article.md @@ -1,88 +1,88 @@ -# Cross-window communication +# Comunicación entre ventanas -The "Same Origin" (same site) policy limits access of windows and frames to each other. +La política de "Mismo origen" (mismo sitio) limita el acceso de ventanas y marcos entre sí. -The idea is that if a user has two pages open: one from `john-smith.com`, and another one is `gmail.com`, then they wouldn't want a script from `john-smith.com` to read our mail from `gmail.com`. So, the purpose of the "Same Origin" policy is to protect users from information theft. +La idea es que si un usuario tiene dos páginas abiertas: una de `john-smith.com`, y otra es `gmail.com`, entonces no querrán que un script de `john-smith.com` lea nuestro correo de `gmail.com`. Por lo tanto, el propósito de la política de "Mismo origen" es proteger a los usuarios del robo de información. -## Same Origin [#same-origin] +## Mismo origen [#same-origin] -Two URLs are said to have the "same origin" if they have the same protocol, domain and port. +Se dice que dos URL tienen el "mismo origen" si tienen el mismo protocolo, dominio y puerto. -These URLs all share the same origin: +Todas estas URL comparten el mismo origen: - `http://site.com` - `http://site.com/` - `http://site.com/my/page.html` -These ones do not: +Estas no: -- <code>http://<b>www.</b>site.com</code> (another domain: `www.` matters) -- <code>http://<b>site.org</b></code> (another domain: `.org` matters) -- <code><b>https://</b>site.com</code> (another protocol: `https`) -- <code>http://site.com:<b>8080</b></code> (another port: `8080`) +- <code>http://<b>www.</b>site.com</code> (otro dominio: `www.` importa) +- <code>http://<b>site.org</b></code> (otro dominio: `.org` importa) +- <code><b>https://</b>site.com</code> (otro protocolo: `https`) +- <code>http://site.com:<b>8080</b></code> (otro puerto: `8080`) -The "Same Origin" policy states that: +La política "Mismo Origen" establece que: -- if we have a reference to another window, e.g. a popup created by `window.open` or a window inside `<iframe>`, and that window comes from the same origin, then we have full access to that window. -- otherwise, if it comes from another origin, then we can't access the content of that window: variables, document, anything. The only exception is `location`: we can change it (thus redirecting the user). But we cannot *read* location (so we can't see where the user is now, no information leak). +- si tenemos una referencia a otra ventana, por ejemplo, una ventana emergente creada por `window.open` o una ventana dentro de `<iframe>`, y esa ventana viene del mismo origen, entonces tenemos acceso completo a esa ventana. +- en caso contrario, si viene de otro origen, entonces no podemos acceder al contenido de esa ventana: variables, documento, nada. La única excepción es `location`: podemos cambiarla (redirigiendo así al usuario). Pero no podemos *leer* location (por lo que no podemos ver dónde está el usuario ahora, no hay fuga de información). -### In action: iframe +### En acción: iframe -An `<iframe>` tag hosts a separate embedded window, with its own separate `document` and `window` objects. +Una etiqueta `<iframe>` aloja una ventana incrustada por separado, con sus propios objetos `document` y `window` separados. -We can access them using properties: +Podemos acceder a ellos usando propiedades: -- `iframe.contentWindow` to get the window inside the `<iframe>`. -- `iframe.contentDocument` to get the document inside the `<iframe>`, shorthand for `iframe.contentWindow.document`. +- `iframe.contentWindow` para obtener la ventana dentro del `<iframe>`. +- `iframe.contentDocument` para obtener el documento dentro del `<iframe>`, abreviatura de `iframe.contentWindow.document`. -When we access something inside the embedded window, the browser checks if the iframe has the same origin. If that's not so then the access is denied (writing to `location` is an exception, it's still permitted). +Cuando accedemos a algo dentro de la ventana incrustada, el navegador comprueba si el iframe tiene el mismo origen. Si no es así, se niega el acceso (escribir en `location` es una excepción, aún está permitido). -For instance, let's try reading and writing to `<iframe>` from another origin: +Por ejemplo, intentemos leer y escribir en `<iframe>` desde otro origen: ```html run <iframe src="https://example.com" id="iframe"></iframe> <script> iframe.onload = function() { - // we can get the reference to the inner window + // podemos obtener la referencia a la ventana interior *!* let iframeWindow = iframe.contentWindow; // OK */!* try { - // ...but not to the document inside it + // ...pero no al documento que contiene *!* let doc = iframe.contentDocument; // ERROR */!* } catch(e) { - alert(e); // Security Error (another origin) + alert(e); // Error de seguridad (otro origen) } - // also we can't READ the URL of the page in iframe + // tampoco podemos LEER la URL de la página en iframe try { - // Can't read URL from the Location object + // No se puede leer la URL del objeto Location *!* let href = iframe.contentWindow.location.href; // ERROR */!* } catch(e) { - alert(e); // Security Error + alert(e); // Error de seguridad } - // ...we can WRITE into location (and thus load something else into the iframe)! + // ...¡podemos ESCRIBIR en location (y así cargar algo más en el iframe)! *!* iframe.contentWindow.location = '/'; // OK */!* - iframe.onload = null; // clear the handler, not to run it after the location change + iframe.onload = null; // borra el controlador para no ejecutarlo después del cambio de ubicación }; </script> ``` -The code above shows errors for any operations except: +El código anterior muestra errores para cualquier operación excepto: -- Getting the reference to the inner window `iframe.contentWindow` - that's allowed. -- Writing to `location`. +- Obtener la referencia a la ventana interna `iframe.contentWindow` - eso está permitido. +- Escribir a `location`. -Contrary to that, if the `<iframe>` has the same origin, we can do anything with it: +Por el contrario, si el `<iframe>` tiene el mismo origen, podemos hacer cualquier cosa con él: ```html run <!-- iframe from the same site --> @@ -90,48 +90,48 @@ Contrary to that, if the `<iframe>` has the same origin, we can do anything with <script> iframe.onload = function() { - // just do anything - iframe.contentDocument.body.prepend("Hello, world!"); + // solo haz cualquier cosa + iframe.contentDocument.body.prepend("¡Hola, mundo!"); }; </script> ``` ```smart header="`iframe.onload` vs `iframe.contentWindow.onload`" -The `iframe.onload` event (on the `<iframe>` tag) is essentially the same as `iframe.contentWindow.onload` (on the embedded window object). It triggers when the embedded window fully loads with all resources. +El evento `iframe.onload` (en la etiqueta `<iframe>`) es esencialmente el mismo que `iframe.contentWindow.onload` (en el objeto de ventana incrustado). Se activa cuando la ventana incrustada se carga completamente con todos los recursos. -...But we can't access `iframe.contentWindow.onload` for an iframe from another origin, so using `iframe.onload`. +... Pero no podemos acceder a `iframe.contentWindow.onload` para un iframe de otro origen, así que usamos `iframe.onload`. ``` -## Windows on subdomains: document.domain +## Ventanas en subdominios: document.domain -By definition, two URLs with different domains have different origins. +Por definición, dos URL con diferentes dominios tienen diferentes orígenes. -But if windows share the same second-level domain, for instance `john.site.com`, `peter.site.com` and `site.com` (so that their common second-level domain is `site.com`), we can make the browser ignore that difference, so that they can be treated as coming from the "same origin" for the purposes of cross-window communication. +Pero si las ventanas comparten el mismo dominio de segundo nivel, por ejemplo, `john.site.com`, `peter.site.com` y `site.com` (de modo que su dominio de segundo nivel común es `site.com`), podemos hacer que el navegador ignore esa diferencia, de modo que puedan tratarse como si vinieran del "mismo origen" para efecto de la comunicación entre ventanas. -To make it work, each such window should run the code: +Para que funcione, cada una de estas ventanas debe ejecutar el código: ```js document.domain = 'site.com'; ``` -That's all. Now they can interact without limitations. Again, that's only possible for pages with the same second-level domain. +Eso es todo. Ahora pueden interactuar sin limitaciones. Nuevamente, eso solo es posible para páginas con el mismo dominio de segundo nivel. -```warn header="Deprecated, but still working" -The `document.domain` property is in the process of being removed from the [specification](https://html.spec.whatwg.org/multipage/origin.html#relaxing-the-same-origin-restriction). The cross-window messaging (explained soon below) is the suggested replacement. +```warn header="Obsoleto, pero aún funcionando" +La propiedad `document.domain` está en proceso de ser removido de la [especificación](https://html.spec.whatwg.org/multipage/origin.html#relaxing-the-same-origin-restriction). Los mensajería cross-window (explicado pronto más abajo) es el reemplazo sugerido. -That said, as of now all browsers support it. And the support will be kept for the future, not to break old code that relies on `document.domain`. +Dicho esto, hasta ahora todos los navegadores lo soportan. Y tal soporte será mantenido en el futuro, para no romper el código existente que se apoya en `document.domain`. ``` -## Iframe: wrong document pitfall +## Iframe: trampa del documento incorrecto -When an iframe comes from the same origin, and we may access its `document`, there's a pitfall. It's not related to cross-origin things, but important to know. +Cuando un iframe proviene del mismo origen y podemos acceder a su `document`, existe una trampa. No está relacionado con cross-origin, pero es importante saberlo. -Upon its creation an iframe immediately has a document. But that document is different from the one that loads into it! +Tras su creación, un iframe tiene inmediatamente un documento. ¡Pero ese documento es diferente del que se carga en él! -So if we do something with the document immediately, that will probably be lost. +Entonces, si hacemos algo con el documento de inmediato, probablemente se perderá. -Here, look: +Aquí, mira: ```html run @@ -142,20 +142,20 @@ Here, look: iframe.onload = function() { let newDoc = iframe.contentDocument; *!* - // the loaded document is not the same as initial! + // ¡el documento cargado no es el mismo que el inicial! alert(oldDoc == newDoc); // false */!* }; </script> ``` -We shouldn't work with the document of a not-yet-loaded iframe, because that's the *wrong document*. If we set any event handlers on it, they will be ignored. +No deberíamos trabajar con el documento de un iframe aún no cargado, porque ese es el *documento incorrecto*. Si configuramos algún controlador de eventos en él, se ignorarán. -How to detect the moment when the document is there? +¿Cómo detectar el momento en que el documento está ahí? -The right document is definitely at place when `iframe.onload` triggers. But it only triggers when the whole iframe with all resources is loaded. +El documento correcto definitivamente está en su lugar cuando se activa `iframe.onload`. Pero solo se activa cuando se carga todo el iframe con todos los recursos. -We can try to catch the moment earlier using checks in `setInterval`: +Podemos intentar capturar el momento anterior usando comprobaciones en `setInterval`: ```html run <iframe src="/" id="iframe"></iframe> @@ -163,26 +163,26 @@ We can try to catch the moment earlier using checks in `setInterval`: <script> let oldDoc = iframe.contentDocument; - // every 100 ms check if the document is the new one + // cada 100 ms comprueba si el documento es el nuevo let timer = setInterval(() => { let newDoc = iframe.contentDocument; if (newDoc == oldDoc) return; - alert("New document is here!"); + alert("¡El nuevo documento está aquí!"); - clearInterval(timer); // cancel setInterval, don't need it any more + clearInterval(timer); // cancelo setInterval, ya no lo necesito }, 100); </script> ``` -## Collection: window.frames +## Colección: window.frames -An alternative way to get a window object for `<iframe>` -- is to get it from the named collection `window.frames`: +Una forma alternativa de obtener un objeto de ventana para `<iframe>` -- es obtenerlo de la colección nombrada `window.frames`: -- By number: `window.frames[0]` -- the window object for the first frame in the document. -- By name: `window.frames.iframeName` -- the window object for the frame with `name="iframeName"`. +- Por número: `window.frames[0]` -- el objeto de ventana para el primer marco del documento. +- Por nombre: `window.frames.iframeName` -- el objeto de ventana para el marco con `name="iframeName"`. -For instance: +Por ejemplo: ```html run <iframe src="/" style="height:80px" name="win" id="iframe"></iframe> @@ -193,93 +193,93 @@ For instance: </script> ``` -An iframe may have other iframes inside. The corresponding `window` objects form a hierarchy. +Un iframe puede tener otros iframes en su interior. Los objetos `window` correspondientes forman una jerarquía. -Navigation links are: +Los enlaces de navegación son: -- `window.frames` -- the collection of "children" windows (for nested frames). -- `window.parent` -- the reference to the "parent" (outer) window. -- `window.top` -- the reference to the topmost parent window. +- `window.frames` -- la colección de ventanas "hijas" (para marcos anidados). +- `window.parent` -- la referencia a la ventana "padre" (exterior). +- `window.top` -- la referencia a la ventana padre superior. -For instance: +Por ejemplo: ```js run window.frames[0].parent === window; // true ``` -We can use the `top` property to check if the current document is open inside a frame or not: +Podemos usar la propiedad `top` para verificar si el documento actual está abierto dentro de un marco o no: ```js run if (window == top) { // current window == window.top? - alert('The script is in the topmost window, not in a frame'); + alert('El script está en la ventana superior, no en un marco.'); } else { - alert('The script runs in a frame!'); + alert('¡El script se ejecuta en un marco!'); } ``` -## The "sandbox" iframe attribute +## El atributo "sandbox" de iframe -The `sandbox` attribute allows for the exclusion of certain actions inside an `<iframe>` in order to prevent it executing untrusted code. It "sandboxes" the iframe by treating it as coming from another origin and/or applying other limitations. +El atributo `sandbox` permite la exclusión de ciertas acciones dentro de un `<iframe>` para evitar que ejecute código no confiable. Separa el iframe en un "sandbox" tratándolo como si procediera de otro origen y/o aplicando otras limitaciones. -There's a "default set" of restrictions applied for `<iframe sandbox src="...">`. But it can be relaxed if we provide a space-separated list of restrictions that should not be applied as a value of the attribute, like this: `<iframe sandbox="allow-forms allow-popups">`. +Hay un "conjunto predeterminado" de restricciones aplicadas para `<iframe sandbox src="...">`. Pero se puede relajar si proporcionamos una lista de restricciones separadas por espacios que no deben aplicarse como un valor del atributo, así: `<iframe sandbox="allow-forms allow-popups">`. -In other words, an empty `"sandbox"` attribute puts the strictest limitations possible, but we can put a space-delimited list of those that we want to lift. +En otras palabras, un atributo "sandbox" vacío pone las limitaciones más estrictas posibles, pero podemos poner una lista delimitada por espacios de aquellas que queremos levantar. -Here's a list of limitations: +Aquí hay una lista de limitaciones: `allow-same-origin` -: By default `"sandbox"` forces the "different origin" policy for the iframe. In other words, it makes the browser to treat the `iframe` as coming from another origin, even if its `src` points to the same site. With all implied restrictions for scripts. This option removes that feature. +: Por defecto, "sandbox" fuerza la política de "origen diferente" para el iframe. En otras palabras, hace que el navegador trate el `iframe` como si viniera de otro origen, incluso si su `src` apunta al mismo sitio. Con todas las restricciones implícitas para los scripts. Esta opción elimina esa característica. `allow-top-navigation` -: Allows the `iframe` to change `parent.location`. +: Permite que el `iframe` cambie `parent.location`. `allow-forms` -: Allows to submit forms from `iframe`. +: Permite enviar formularios desde `iframe`. `allow-scripts` -: Allows to run scripts from the `iframe`. +: Permite ejecutar scripts desde el `iframe`. `allow-popups` -: Allows to `window.open` popups from the `iframe` +: Permite `window.open` popups desde el `iframe` -See [the manual](mdn:/HTML/Element/iframe) for more. +Consulta [el manual](https://developer.mozilla.org/es/docs/Web/HTML/Elemento/iframe) para obtener más información. -The example below demonstrates a sandboxed iframe with the default set of restrictions: `<iframe sandbox src="...">`. It has some JavaScript and a form. +El siguiente ejemplo muestra un iframe dentro de un entorno controlado con el conjunto de restricciones predeterminado: `<iframe sandbox src="...">`. Tiene algo de JavaScript y un formulario. -Please note that nothing works. So the default set is really harsh: +Tenga en cuenta que nada funciona. Entonces, el conjunto predeterminado es realmente duro: [codetabs src="sandbox" height=140] ```smart -The purpose of the `"sandbox"` attribute is only to *add more* restrictions. It cannot remove them. In particular, it can't relax same-origin restrictions if the iframe comes from another origin. +El propósito del atributo `"sandbox"` es solo *agregar más* restricciones. No puede eliminarlas. En particular, no puede relajar las restricciones del mismo origen si el iframe proviene de otro origen. ``` -## Cross-window messaging +## Mensajería entre ventanas -The `postMessage` interface allows windows to talk to each other no matter which origin they are from. +La interfaz `postMessage` permite que las ventanas se comuniquen entre sí sin importar de qué origen sean. -So, it's a way around the "Same Origin" policy. It allows a window from `john-smith.com` to talk to `gmail.com` and exchange information, but only if they both agree and call corresponding JavaScript functions. That makes it safe for users. +Por lo tanto, es una forma de evitar la política del "mismo origen". Permite a una ventana de `john-smith.com` hablar con `gmail.com` e intercambiar información, pero solo si ambos están de acuerdo y llaman a las funciones de JavaScript correspondientes. Eso lo hace seguro para los usuarios. -The interface has two parts. +La interfaz tiene dos partes. ### postMessage -The window that wants to send a message calls [postMessage](mdn:api/Window.postMessage) method of the receiving window. In other words, if we want to send the message to `win`, we should call `win.postMessage(data, targetOrigin)`. +La ventana que quiere enviar un mensaje llama al método [postMessage](https://developer.mozilla.org/en-US/docs/Web/API/Window.postMessage) de la ventana receptora. En otras palabras, si queremos enviar el mensaje a `win`, debemos llamar a `win.postMessage(data, targetOrigin)`. -Arguments: +Argumentos: `data` -: The data to send. Can be any object, the data is cloned using the "structured serialization algorithm". IE supports only strings, so we should `JSON.stringify` complex objects to support that browser. +: Los datos a enviar. Puede ser cualquier objeto, los datos se clonan mediante el "algoritmo de clonación estructurada". IE solo admite strings, por lo que debemos usar `JSON.stringify` en objetos complejos para admitir ese navegador. `targetOrigin` -: Specifies the origin for the target window, so that only a window from the given origin will get the message. +: Especifica el origen de la ventana de destino, de modo que solo una ventana del origen dado recibirá el mensaje. -The `targetOrigin` is a safety measure. Remember, if the target window comes from another origin, we can't read its `location` in the sender window. So we can't be sure which site is open in the intended window right now: the user could navigate away, and the sender window has no idea about it. +El argumento "targetOrigin" es una medida de seguridad. Recuerde que si la ventana de destino proviene de otro origen, no podemos leer su `location` en la ventana del remitente. Por lo tanto, no podemos estar seguros qué sitio está abierto en la ventana deseada en este momento: el usuario podría navegar fuera del sitio y la ventana del remitente no tener idea de ello. -Specifying `targetOrigin` ensures that the window only receives the data if it's still at the right site. Important when the data is sensitive. +Especificar `targetOrigin` asegura que la ventana solo reciba los datos si todavía está en el sitio correcto. Importante cuando los datos son sensibles. -For instance, here `win` will only receive the message if it has a document from the origin `http://example.com`: +Por ejemplo, aquí `win` solo recibirá el mensaje si tiene un documento del origen `http://example.com`: ```html no-beautify <iframe src="http://example.com" name="example"> @@ -291,7 +291,7 @@ For instance, here `win` will only receive the message if it has a document from </script> ``` -If we don't want that check, we can set `targetOrigin` to `*`. +Si no queremos esa comprobación, podemos establecer `targetOrigin` en `*`. ```html no-beautify <iframe src="http://example.com" name="example"> @@ -308,70 +308,70 @@ If we don't want that check, we can set `targetOrigin` to `*`. ### onmessage -To receive a message, the target window should have a handler on the `message` event. It triggers when `postMessage` is called (and `targetOrigin` check is successful). +Para recibir un mensaje, la ventana destino debe tener un controlador en el evento `message`. Se activa cuando se llama a `postMessage` (y la comprobación de `targetOrigin` es correcta). -The event object has special properties: +El objeto de evento tiene propiedades especiales: `data` -: The data from `postMessage`. +: Los datos de `postMessage`. `origin` -: The origin of the sender, for instance `http://javascript.info`. +: El origen del remitente, por ejemplo, `http://javascript.info`. `source` -: The reference to the sender window. We can immediately `source.postMessage(...)` back if we want. +: La referencia a la ventana del remitente. Podemos llamar inmediatamente `source.postMessage(...)` de regreso si queremos. -To assign that handler, we should use `addEventListener`, a short syntax `window.onmessage` does not work. +Para asignar ese controlador, debemos usar `addEventListener`, una sintaxis corta `window.onmessage` no funciona. -Here's an example: +He aquí un ejemplo: ```js window.addEventListener("message", function(event) { if (event.origin != 'http://javascript.info') { - // something from an unknown domain, let's ignore it + // algo de un dominio desconocido, ignorémoslo return; } - alert( "received: " + event.data ); + alert( "Recibí: " + event.data ); - // can message back using event.source.postMessage(...) + // puedes enviar un mensaje usando event.source.postMessage(...) }); ``` -The full example: +El ejemplo completo: [codetabs src="postmessage" height=120] -## Summary +## Resumen -To call methods and access the content of another window, we should first have a reference to it. +Para llamar a métodos y acceder al contenido de otra ventana, primero debemos tener una referencia a ella. -For popups we have these references: -- From the opener window: `window.open` -- opens a new window and returns a reference to it, -- From the popup: `window.opener` -- is a reference to the opener window from a popup. +Para las ventanas emergentes tenemos estas referencias: +- Desde la ventana de apertura: `window.open` -- abre una nueva ventana y devuelve una referencia a ella, +- Desde la ventana emergente: `window.opener` -- es una referencia a la ventana de apertura desde una ventana emergente. -For iframes, we can access parent/children windows using: -- `window.frames` -- a collection of nested window objects, -- `window.parent`, `window.top` are the references to parent and top windows, -- `iframe.contentWindow` is the window inside an `<iframe>` tag. +Para iframes, podemos acceder a las ventanas padres o hijas usando: +- `window.frames` -- una colección de objetos de ventana anidados, +- `window.parent`, `window.top` son las referencias a las ventanas principales y superiores, +- `iframe.contentWindow` es la ventana dentro de una etiqueta `<iframe>`. -If windows share the same origin (host, port, protocol), then windows can do whatever they want with each other. +Si las ventanas comparten el mismo origen (host, puerto, protocolo), las ventanas pueden hacer lo que quieran entre sí. -Otherwise, only possible actions are: -- Change the `location` of another window (write-only access). -- Post a message to it. +En caso contrario, las únicas acciones posibles son: +- Cambiar `location` en otra ventana (acceso de solo escritura). +- Enviarle un mensaje. -Exceptions are: -- Windows that share the same second-level domain: `a.site.com` and `b.site.com`. Then setting `document.domain='site.com'` in both of them puts them into the "same origin" state. -- If an iframe has a `sandbox` attribute, it is forcefully put into the "different origin" state, unless the `allow-same-origin` is specified in the attribute value. That can be used to run untrusted code in iframes from the same site. +Las excepciones son: +- Ventanas que comparten el mismo dominio de segundo nivel: `a.site.com` y `b.site.com`. Luego, configurar `document.domain='site.com'` en ambos, los coloca en el estado de "mismo origen". +- Si un iframe tiene un atributo `sandbox`, se coloca forzosamente en el estado de "origen diferente", a menos que se especifique `allow-same-origin` en el valor del atributo. Eso se puede usar para ejecutar código que no es de confianza en iframes desde el mismo sitio. -The `postMessage` interface allows two windows with any origins to talk: +La interfaz `postMessage` permite que dos ventanas con cualquier origen hablen: -1. The sender calls `targetWin.postMessage(data, targetOrigin)`. -2. If `targetOrigin` is not `'*'`, then the browser checks if window `targetWin` has the origin `targetOrigin`. -3. If it is so, then `targetWin` triggers the `message` event with special properties: - - `origin` -- the origin of the sender window (like `http://my.site.com`) - - `source` -- the reference to the sender window. - - `data` -- the data, any object in everywhere except IE that supports only strings. +1. El remitente llama a `targetWin.postMessage(data, targetOrigin)`. +2. Si `targetOrigin` no es `'*'`, entonces el navegador comprueba si la ventana `targetWin` tiene el origen `targetOrigin`. +3. Si es así, entonces `targetWin` activa el evento `message` con propiedades especiales: + - `origin` -- el origen de la ventana del remitente (como` http://my.site.com`) + - `source` -- la referencia a la ventana del remitente. + - `data` -- los datos, cualquier objeto en todas partes excepto IE que solo admite cadenas. - We should use `addEventListener` to set the handler for this event inside the target window. + Deberíamos usar `addEventListener` para configurar el controlador para este evento dentro de la ventana de destino. diff --git a/3-frames-and-windows/03-cross-window-communication/postmessage.view/iframe.html b/3-frames-and-windows/03-cross-window-communication/postmessage.view/iframe.html index 88e098b7b..767cf9de3 100644 --- a/3-frames-and-windows/03-cross-window-communication/postmessage.view/iframe.html +++ b/3-frames-and-windows/03-cross-window-communication/postmessage.view/iframe.html @@ -7,10 +7,10 @@ <body> - Receiving iframe. + Recibiendo iframe. <script> window.addEventListener('message', function(event) { - alert(`Received ${event.data} from ${event.origin}`); + alert(`Recibí ${event.data} de ${event.origin}`); }); </script> diff --git a/3-frames-and-windows/03-cross-window-communication/postmessage.view/index.html b/3-frames-and-windows/03-cross-window-communication/postmessage.view/index.html index abcb065c6..b36b5256e 100644 --- a/3-frames-and-windows/03-cross-window-communication/postmessage.view/index.html +++ b/3-frames-and-windows/03-cross-window-communication/postmessage.view/index.html @@ -8,8 +8,8 @@ <body> <form id="form"> - <input type="text" placeholder="Enter message" name="message"> - <input type="submit" value="Click to send"> + <input type="text" placeholder="Ingresa mensaje" name="message"> + <input type="submit" value="Haz clic para enviar"> </form> <iframe src="iframe.html" id="iframe" style="display:block;height:60px"></iframe> diff --git a/3-frames-and-windows/03-cross-window-communication/sandbox.view/index.html b/3-frames-and-windows/03-cross-window-communication/sandbox.view/index.html index 46dd7b5cc..e87a6fed0 100644 --- a/3-frames-and-windows/03-cross-window-communication/sandbox.view/index.html +++ b/3-frames-and-windows/03-cross-window-communication/sandbox.view/index.html @@ -7,7 +7,7 @@ <body> - <div>The iframe below has the <code>sandbox</code> attribute.</div> + <div>El siguiente iframe tiene el atributo <code> sandbox </code>.</div> <iframe sandbox src="sandboxed.html" style="height:60px;width:90%"></iframe> diff --git a/3-frames-and-windows/03-cross-window-communication/sandbox.view/sandboxed.html b/3-frames-and-windows/03-cross-window-communication/sandbox.view/sandboxed.html index c10273255..8db29b86b 100644 --- a/3-frames-and-windows/03-cross-window-communication/sandbox.view/sandboxed.html +++ b/3-frames-and-windows/03-cross-window-communication/sandbox.view/sandboxed.html @@ -7,11 +7,11 @@ <body> - <button onclick="alert(123)">Click to run a script (doesn't work)</button> + <button onclick="alert(123)">Haz clic para ejecutar un script (no funciona)</button> <form action="http://google.com"> <input type="text"> - <input type="submit" value="Submit (doesn't work)"> + <input type="submit" value="Enviar (no funciona)"> </form> </body> diff --git a/3-frames-and-windows/06-clickjacking/article.md b/3-frames-and-windows/06-clickjacking/article.md index 34d0a91ae..9ab5b9f55 100644 --- a/3-frames-and-windows/06-clickjacking/article.md +++ b/3-frames-and-windows/06-clickjacking/article.md @@ -1,79 +1,79 @@ -# The clickjacking attack +# El ataque de secuestro de clics -The "clickjacking" attack allows an evil page to click on a "victim site" *on behalf of the visitor*. +El ataque "secuestro de clics" permite que una página maligna haga clic en un "sitio víctima" * en nombre del visitante *. -Many sites were hacked this way, including Twitter, Facebook, Paypal and other sites. They have all been fixed, of course. +Muchos sitios fueron pirateados de esta manera, incluidos Twitter, Facebook, Paypal y otros sitios. Todos han sido arreglados, por supuesto. -## The idea +## La idea -The idea is very simple. +La idea es muy simple. -Here's how clickjacking was done with Facebook: +Así es como se hizo el secuestro de clics con Facebook: -1. A visitor is lured to the evil page. It doesn't matter how. -2. The page has a harmless-looking link on it (like "get rich now" or "click here, very funny"). -3. Over that link the evil page positions a transparent `<iframe>` with `src` from facebook.com, in such a way that the "Like" button is right above that link. Usually that's done with `z-index`. -4. In attempting to click the link, the visitor in fact clicks the button. +1. Un visitante es atraído a la página maligna. No importa cómo. +2. La página tiene un enlace de apariencia inofensiva (como "hazte rico ahora" o "haz clic aquí, muy divertido"). +3. Sobre ese enlace, la página maligna coloca un `<iframe>` transparente con `src` de facebook.com, de tal manera que el botón" Me gusta "está justo encima de ese enlace. Por lo general, eso se hace con `z-index`. +4. Al intentar hacer clic en el enlace, el visitante de hecho hace clic en el botón. -## The demo +## La demostración -Here's how the evil page looks. To make things clear, the `<iframe>` is half-transparent (in real evil pages it's fully transparent): +Así es como se ve la página malvada. Para aclarar las cosas, el `<iframe>` es semitransparente (en las páginas realmente malvadas es completamente transparente): ```html run height=120 no-beautify <style> -iframe { /* iframe from the victim site */ +iframe { /* iframe del sitio de la víctima */ width: 400px; height: 100px; position: absolute; top:0; left:-20px; *!* - opacity: 0.5; /* in real opacity:0 */ + opacity: 0.5; /* realmente opacity:0 */ */!* z-index: 1; } </style> -<div>Click to get rich now:</div> +<div>Haga clic para hacerse rico ahora:</div> -<!-- The url from the victim site --> +<!-- La URL del sitio de la víctima --> *!* <iframe src="/clickjacking/facebook.html"></iframe> -<button>Click here!</button> +<button>¡Haga clic aquí!</button> */!* -<div>...And you're cool (I'm a cool hacker actually)!</div> +<div>...Y eres genial (en realidad soy un pirata informático genial)!</div> ``` -The full demo of the attack: +La demostración completa del ataque: [codetabs src="clickjacking-visible" height=160] -Here we have a half-transparent `<iframe src="facebook.html">`, and in the example we can see it hovering over the button. A click on the button actually clicks on the iframe, but that's not visible to the user, because the iframe is transparent. +Aquí tenemos un `<iframe src="facebook.html">` semitransparente, y en el ejemplo podemos verlo flotando sobre el botón. Un clic en el botón realmente hace clic en el iframe, pero eso no es visible para el usuario, porque el iframe es transparente. -As a result, if the visitor is authorized on Facebook ("remember me" is usually turned on), then it adds a "Like". On Twitter that would be a "Follow" button. +Como resultado, si el visitante está autorizado en Facebook ("recordarme" generalmente está activado), entonces agrega un "Me gusta". En Twitter sería un botón "Seguir". -Here's the same example, but closer to reality, with `opacity:0` for `<iframe>`: +Este es el mismo ejemplo, pero más cercano a la realidad, con `opacity:0` para `<iframe>`: [codetabs src="clickjacking" height=160] -All we need to attack -- is to position the `<iframe>` on the evil page in such a way that the button is right over the link. So that when a user clicks the link, they actually click the button. That's usually doable with CSS. +Todo lo que necesitamos para atacar es colocar el `<iframe>` en la página maligna de tal manera que el botón esté justo sobre el enlace. De modo que cuando un usuario hace clic en el enlace, en realidad hace clic en el botón. Eso suele ser posible con CSS. -```smart header="Clickjacking is for clicks, not for keyboard" -The attack only affects mouse actions (or similar, like taps on mobile). +```smart header="Clickjacking es para clics, no para teclado" +El ataque solo afecta las acciones del mouse (o similares, como los toques en el móvil). -Keyboard input is much difficult to redirect. Technically, if we have a text field to hack, then we can position an iframe in such a way that text fields overlap each other. So when a visitor tries to focus on the input they see on the page, they actually focus on the input inside the iframe. +La entrada del teclado es muy difícil de redirigir. Técnicamente, si tenemos un campo de texto para piratear, entonces podemos colocar un iframe de tal manera que los campos de texto se superpongan entre sí. Entonces, cuando un visitante intenta concentrarse en la entrada que ve en la página, en realidad se enfoca en la entrada dentro del iframe. -But then there's a problem. Everything that the visitor types will be hidden, because the iframe is not visible. +Pero luego hay un problema. Todo lo que escriba el visitante estará oculto, porque el iframe no es visible. -People will usually stop typing when they can't see their new characters printing on the screen. +Las personas generalmente dejarán de escribir cuando no puedan ver sus nuevos caracteres impresos en la pantalla. ``` -## Old-school defences (weak) +## Defensas de la vieja escuela (débiles) -The oldest defence is a bit of JavaScript which forbids opening the page in a frame (so-called "framebusting"). +La defensa más antigua es un poco de JavaScript que prohíbe abrir la página en un marco (el llamado "framebusting"). -That looks like this: +Eso se ve así: ```js if (top != window) { @@ -81,15 +81,15 @@ if (top != window) { } ``` -That is: if the window finds out that it's not on top, then it automatically makes itself the top. +Es decir: si la ventana descubre que no está en la parte superior, automáticamente se convierte en la parte superior. -This not a reliable defence, because there are many ways to hack around it. Let's cover a few. +Esta no es una defensa confiable, porque hay muchas formas de esquivarla. Cubramos algunas. -### Blocking top-navigation +### Bloquear la navegación superior -We can block the transition caused by changing `top.location` in [beforeunload](info:onload-ondomcontentloaded#window.onbeforeunload) event handler. +Podemos bloquear la transición causada por cambiar `top.location` en el controlador de eventos [beforeunload](info:onload-ondomcontentloaded#window.onbeforeunload). -The top page (enclosing one, belonging to the hacker) sets a preventing handler to it, like this: +La página superior (adjuntando una, que pertenece al pirata informático) establece un controlador de prevención, como este: ```js window.onbeforeunload = function() { @@ -97,68 +97,68 @@ window.onbeforeunload = function() { }; ``` -When the `iframe` tries to change `top.location`, the visitor gets a message asking them whether they want to leave. +Cuando el `iframe` intenta cambiar `top.location`, el visitante recibe un mensaje preguntándole si quiere irse. -In most cases the visitor would answer negatively because they don't know about the iframe - all they can see is the top page, there's no reason to leave. So `top.location` won't change! +En la mayoría de los casos, el visitante respondería negativamente porque no conocen el iframe; todo lo que pueden ver es la página superior, no hay razón para irse. ¡Así que `top.location` no cambiará! -In action: +En acción: [codetabs src="top-location"] -### Sandbox attribute +### Atributo Sandbox -One of the things restricted by the `sandbox` attribute is navigation. A sandboxed iframe may not change `top.location`. +Una de las cosas restringidas por el atributo `sandbox` es la navegación. Un iframe de espacio aislado no puede cambiar `top.location`. -So we can add the iframe with `sandbox="allow-scripts allow-forms"`. That would relax the restrictions, permitting scripts and forms. But we omit `allow-top-navigation` so that changing `top.location` is forbidden. +Entonces podemos agregar el iframe con `sandbox="allow-scripts allow-forms"`. Eso relajaría las restricciones, permitiendo guiones y formularios. Pero omitimos `allow-top-navigation` para que se prohíba cambiar `top.location`. -Here's the code: +Aquí está el código: ```html <iframe *!*sandbox="allow-scripts allow-forms"*/!* src="facebook.html"></iframe> ``` -There are other ways to work around that simple protection too. +También hay otras formas de evitar esa simple protección. ## X-Frame-Options -The server-side header `X-Frame-Options` can permit or forbid displaying the page inside a frame. +El encabezado del lado del servidor `X-Frame-Options` puede permitir o prohibir mostrar la página dentro de un marco. -It must be sent exactly as HTTP-header: the browser will ignore it if found in HTML `<meta>` tag. So, `<meta http-equiv="X-Frame-Options"...>` won't do anything. +Debe enviarse exactamente como encabezado HTTP: el navegador lo ignorará si se encuentra en la etiqueta HTML `<meta>`. Entonces, `<meta http-equiv="X-Frame-Options"...>` no hará nada. -The header may have 3 values: +El encabezado puede tener 3 valores: `DENY` -: Never ever show the page inside a frame. +: Nunca muestra la página dentro de un marco. `SAMEORIGIN` -: Allow inside a frame if the parent document comes from the same origin. +: Permitir dentro de un marco si el documento principal proviene del mismo origen. `ALLOW-FROM domain` -: Allow inside a frame if the parent document is from the given domain. +: Permitir dentro de un marco si el documento principal es del dominio dado. -For instance, Twitter uses `X-Frame-Options: SAMEORIGIN`. +Por ejemplo, Twitter usa `X-Frame-Options: SAMEORIGIN`. ````online -Here's the result: +Aquí está el resultado: ```html <iframe src="https://twitter.com"></iframe> ``` -<!-- ebook: prerender/ chrome headless dies and timeouts on this iframe --> +<!-- ebook: prerender / chrome headless muere y se termina su tiempo de espera en este iframe --> <iframe src="https://twitter.com"></iframe> -Depending on your browser, the `iframe` above is either empty or alerting you that the browser won't permit that page to be navigating in this way. +Dependiendo de su navegador, el `iframe` anterior está vacío o le advierte que el navegador no permitirá que esa página navegue de esta manera. ```` -## Showing with disabled functionality +## Mostrando con funcionalidad deshabilitada -The `X-Frame-Options` header has a side effect. Other sites won't be able to show our page in a frame, even if they have good reasons to do so. +El encabezado `X-Frame-Options` tiene un efecto secundario. Otros sitios no podrán mostrar nuestra página en un marco, incluso si tienen buenas razones para hacerlo. -So there are other solutions... For instance, we can "cover" the page with a `<div>` with styles `height: 100%; width: 100%;`, so that it will intercept all clicks. That `<div>` is to be removed if `window == top` or if we figure out that we don't need the protection. +Así que hay otras soluciones... Por ejemplo, podemos "cubrir" la página con un `<div>` con estilos `height: 100%; width: 100%;`, de modo que interceptará todos los clics. Ese `<div>` debe eliminarse si `window == top` o si descubrimos que no necesitamos la protección. -Something like this: +Algo como esto: ```html <style> @@ -173,49 +173,49 @@ Something like this: </style> <div id="protector"> - <a href="/" target="_blank">Go to the site</a> + <a href="/" target="_blank">Ir al sitio</a> </div> <script> - // there will be an error if top window is from the different origin - // but that's ok here + // habrá un error si la ventana superior es de un origen diferente + // pero esta bien aquí if (top.document.domain == document.domain) { protector.remove(); } </script> ``` -The demo: +La demostración: [codetabs src="protector"] -## Samesite cookie attribute +## Atributo Samesite cookie -The `samesite` cookie attribute can also prevent clickjacking attacks. +El atributo `samesite` cookie también puede prevenir ataques de secuestro de clics. -A cookie with such attribute is only sent to a website if it's opened directly, not via a frame, or otherwise. More information in the chapter <info:cookie#samesite>. +Una cookie con dicho atributo solo se envía a un sitio web si se abre directamente, no a través de un marco o de otra manera. Más información en el capítulo <info:cookie#samesite>. -If the site, such as Facebook, had `samesite` attribute on its authentication cookie, like this: +Si el sitio, como Facebook, tenía el atributo `samesite` en su cookie de autenticación, así: ``` Set-Cookie: authorization=secret; samesite ``` -...Then such cookie wouldn't be sent when Facebook is open in iframe from another site. So the attack would fail. +...Entonces dicha cookie no se enviaría cuando Facebook esté abierto en iframe desde otro sitio. Entonces el ataque fracasaría. -The `samesite` cookie attribute will not have an effect when cookies are not used. This may allow other websites to easily show our public, unauthenticated pages in iframes. +El atributo `samesite` cookie no tendrá efecto cuando no se utilicen cookies. Esto puede permitir que otros sitios web muestren fácilmente nuestras páginas públicas no autenticadas en iframes. -However, this may also allow clickjacking attacks to work in a few limited cases. An anonymous polling website that prevents duplicate voting by checking IP addresses, for example, would still be vulnerable to clickjacking because it does not authenticate users using cookies. +Sin embargo, esto también puede permitir que los ataques de secuestro de clics funcionen en algunos casos limitados. Un sitio web de sondeo anónimo que evita la duplicación de votaciones al verificar las direcciones IP, por ejemplo, aún sería vulnerable al secuestro de clics porque no autentica a los usuarios que usan cookies. -## Summary +## Resumen -Clickjacking is a way to "trick" users into clicking on a victim site without even knowing what's happening. That's dangerous if there are important click-activated actions. +El secuestro de clics es una forma de "engañar" a los usuarios para que hagan clic en el sitio de una víctima sin siquiera saber qué está sucediendo. Eso es peligroso si hay acciones importantes activadas por clic. -A hacker can post a link to their evil page in a message, or lure visitors to their page by some other means. There are many variations. +Un pirata informático puede publicar un enlace a su página maligna en un mensaje o atraer visitantes a su página por otros medios. Hay muchas variaciones. -From one perspective -- the attack is "not deep": all a hacker is doing is intercepting a single click. But from another perspective, if the hacker knows that after the click another control will appear, then they may use cunning messages to coerce the user into clicking on them as well. +Desde una perspectiva, el ataque "no es profundo": todo lo que hace un pirata informático es interceptar un solo clic. Pero desde otra perspectiva, si el pirata informático sabe que después del clic aparecerá otro control, entonces pueden usar mensajes astutos para obligar al usuario a hacer clic en ellos también. -The attack is quite dangerous, because when we engineer the UI we usually don't anticipate that a hacker may click on behalf of the visitor. So vulnerabilities can be found in totally unexpected places. +El ataque es bastante peligroso, porque cuando diseñamos la interfaz de usuario generalmente no anticipamos que un pirata informático pueda hacer clic en nombre del visitante. Entonces, las vulnerabilidades se pueden encontrar en lugares totalmente inesperados. -- It is recommended to use `X-Frame-Options: SAMEORIGIN` on pages (or whole websites) which are not intended to be viewed inside frames. -- Use a covering `<div>` if we want to allow our pages to be shown in iframes, but still stay safe. +- Se recomienda utilizar `X-Frame-Options: SAMEORIGIN` en páginas (o sitios web completos) que no están destinados a verse dentro de marcos. +- Usa una cubierta `<div>` si queremos permitir que nuestras páginas se muestren en iframes, pero aún así permanecer seguras. diff --git a/3-frames-and-windows/06-clickjacking/clickjacking-visible.view/facebook.html b/3-frames-and-windows/06-clickjacking/clickjacking-visible.view/facebook.html index 6b8231c71..00f2e9d5f 100644 --- a/3-frames-and-windows/06-clickjacking/clickjacking-visible.view/facebook.html +++ b/3-frames-and-windows/06-clickjacking/clickjacking-visible.view/facebook.html @@ -3,7 +3,7 @@ <body style="margin:10px;padding:10px"> - <input type="button" onclick="alert('Like pressed on facebook.html!')" value="I LIKE IT !"> + <input type="button" onclick="alert('¡ME GUSTA fue presionado en facebook.html!')" value="ME GUSTA !"> </body> diff --git a/3-frames-and-windows/06-clickjacking/clickjacking-visible.view/index.html b/3-frames-and-windows/06-clickjacking/clickjacking-visible.view/index.html index 9f8d82197..e3cfd79ae 100644 --- a/3-frames-and-windows/06-clickjacking/clickjacking-visible.view/index.html +++ b/3-frames-and-windows/06-clickjacking/clickjacking-visible.view/index.html @@ -19,14 +19,14 @@ } </style> - <div>Click to get rich now:</div> + <div>Haga clic para hacerse rico ahora:</div> - <!-- The url from the victim site --> + <!-- la url de la víctima --> <iframe src="facebook.html"></iframe> - <button>Click here!</button> + <button>¡Haga clic aquí!</button> - <div>...And you're cool (I'm a cool hacker actually)!</div> + <div>...Y eres genial (en realidad soy un pirata informático genial)!</div> </body> </html> diff --git a/3-frames-and-windows/06-clickjacking/clickjacking.view/facebook.html b/3-frames-and-windows/06-clickjacking/clickjacking.view/facebook.html index 6b8231c71..00f2e9d5f 100644 --- a/3-frames-and-windows/06-clickjacking/clickjacking.view/facebook.html +++ b/3-frames-and-windows/06-clickjacking/clickjacking.view/facebook.html @@ -3,7 +3,7 @@ <body style="margin:10px;padding:10px"> - <input type="button" onclick="alert('Like pressed on facebook.html!')" value="I LIKE IT !"> + <input type="button" onclick="alert('¡ME GUSTA fue presionado en facebook.html!')" value="ME GUSTA !"> </body> diff --git a/3-frames-and-windows/06-clickjacking/clickjacking.view/index.html b/3-frames-and-windows/06-clickjacking/clickjacking.view/index.html index 05ec48cb8..5d9ac4ff4 100644 --- a/3-frames-and-windows/06-clickjacking/clickjacking.view/index.html +++ b/3-frames-and-windows/06-clickjacking/clickjacking.view/index.html @@ -19,14 +19,14 @@ } </style> - <div>Click to get rich now:</div> + <div>Haga clic para hacerse rico ahora:</div> - <!-- The url from the victim site --> + <!-- la url de la víctima --> <iframe src="facebook.html"></iframe> - <button>Click here!</button> + <button>¡Haga clic aquí!</button> - <div>...And you're cool (I'm a cool hacker actually)!</div> + <div>...Y eres genial (en realidad soy un pirata informático genial)!</div> </body> </html> diff --git a/3-frames-and-windows/06-clickjacking/protector.view/iframe.html b/3-frames-and-windows/06-clickjacking/protector.view/iframe.html index a1ca6fcc9..3895aec2b 100644 --- a/3-frames-and-windows/06-clickjacking/protector.view/iframe.html +++ b/3-frames-and-windows/06-clickjacking/protector.view/iframe.html @@ -20,7 +20,7 @@ <body> <div id="protector"> - <a href="/" target="_blank">Go to the site</a> + <a href="/" target="_blank">Ir al sitio</a> </div> <script> @@ -31,11 +31,11 @@ </script> - This text is always visible. + Este texto siempre está visible. - But if the page was open inside a document from another domain, the div over it would prevent any actions. + Pero si la página estuviera abierta dentro de un documento de otro dominio, el div sobre ella evitaría cualquier acción. - <button onclick="alert(1)">Click wouldn't work in that case</button> + <button onclick="alert(1)">El clic no funcionaría en ese caso</button> </body> </html> diff --git a/3-frames-and-windows/06-clickjacking/top-location.view/iframe.html b/3-frames-and-windows/06-clickjacking/top-location.view/iframe.html index da477f400..a617dce91 100644 --- a/3-frames-and-windows/06-clickjacking/top-location.view/iframe.html +++ b/3-frames-and-windows/06-clickjacking/top-location.view/iframe.html @@ -7,7 +7,7 @@ <body> - <div>Changes top.location to javascript.info</div> + <div>Cambia top.location a javascript.info</div> <script> top.location = 'https://javascript.info'; diff --git a/3-frames-and-windows/06-clickjacking/top-location.view/index.html b/3-frames-and-windows/06-clickjacking/top-location.view/index.html index d578a8b10..a903b484b 100644 --- a/3-frames-and-windows/06-clickjacking/top-location.view/index.html +++ b/3-frames-and-windows/06-clickjacking/top-location.view/index.html @@ -21,7 +21,7 @@ window.onbeforeunload = function() { window.onbeforeunload = null; - return "Want to leave without learning all the secrets (he-he)?"; + return "¿Quieres irte sin conocer todos los secretos (je-je)?"; }; document.body.insertAdjacentHTML('beforeend', '<iframe src="iframe.html">'); @@ -31,11 +31,11 @@ <body> - <p>After a click on the button the visitor gets a "strange" question about whether they want to leave.</p> + <p>Después de hacer clic en el botón, el visitante recibe una pregunta "extraña" sobre si quiere irse.</p> - <p>Probably they would respond "No", and the iframe protection is hacked.</p> + <p>Probablemente responderían "No", y la protección de iframe está pirateada.</p> - <button onclick="attack()">Add a "protected" iframe</button> + <button onclick="attack()">Agregar un iframe "protegido"</button> </body> </html> diff --git a/3-frames-and-windows/index.md b/3-frames-and-windows/index.md index eb5069e39..b3d79a85d 100644 --- a/3-frames-and-windows/index.md +++ b/3-frames-and-windows/index.md @@ -1 +1 @@ -# Frames and windows +# Marcos y ventanas diff --git a/4-binary/01-arraybuffer-binary-arrays/01-concat/_js.view/solution.js b/4-binary/01-arraybuffer-binary-arrays/01-concat/_js.view/solution.js index 00c37bb94..3387ee3e9 100644 --- a/4-binary/01-arraybuffer-binary-arrays/01-concat/_js.view/solution.js +++ b/4-binary/01-arraybuffer-binary-arrays/01-concat/_js.view/solution.js @@ -1,13 +1,13 @@ function concat(arrays) { - // sum of individual array lengths + // suma de las longitudes de array individuales let totalLength = arrays.reduce((acc, value) => acc + value.length, 0); let result = new Uint8Array(totalLength); if (!arrays.length) return result; - // for each array - copy it over result - // next array is copied right after the previous one + // para cada array: copiarlo sobre "result" + // el siguiente array es copiado inmediatamente después del anterior let length = 0; for(let array of arrays) { result.set(array, length); diff --git a/4-binary/01-arraybuffer-binary-arrays/01-concat/task.md b/4-binary/01-arraybuffer-binary-arrays/01-concat/task.md index 6710104b2..cc27917bf 100644 --- a/4-binary/01-arraybuffer-binary-arrays/01-concat/task.md +++ b/4-binary/01-arraybuffer-binary-arrays/01-concat/task.md @@ -1,4 +1,4 @@ -# Concatenate typed arrays +# Concatenar arrays tipados -Given an array of `Uint8Array`, write a function `concat(arrays)` that returns a concatenation of them into a single array. +Dado un array de `Uint8Array`, escribir una función `concat(arrays)` que devuelva la concatenación de ellos en un único array. diff --git a/4-binary/01-arraybuffer-binary-arrays/article.md b/4-binary/01-arraybuffer-binary-arrays/article.md index 2827e277e..68e4d920f 100644 --- a/4-binary/01-arraybuffer-binary-arrays/article.md +++ b/4-binary/01-arraybuffer-binary-arrays/article.md @@ -1,87 +1,87 @@ -# ArrayBuffer, binary arrays +# ArrayBuffer, arrays binarios -In web-development we meet binary data mostly while dealing with files (create, upload, download). Another typical use case is image processing. +En el desarrollo web nos encontramos con datos binarios sobre todo al tratar con archivos (crear, cargar, descargar). Otro caso de uso típico es el procesamiento de imágenes. -That's all possible in JavaScript, and binary operations are high-performant. +Todo esto es posible en JavaScript y las operaciones binarias son de alto rendimiento. -Although, there's a bit of confusion, because there are many classes. To name a few: +Aunque hay un poco de confusión porque hay muchas clases. Por nombrar algunas: - `ArrayBuffer`, `Uint8Array`, `DataView`, `Blob`, `File`, etc. -Binary data in JavaScript is implemented in a non-standard way, compared to other languages. But when we sort things out, everything becomes fairly simple. +Los datos binarios en JavaScript se implementan de una manera no estándar en comparación con otros lenguajes. Pero cuando ordenamos las cosas, todo se vuelve bastante sencillo. -**The basic binary object is `ArrayBuffer` -- a reference to a fixed-length contiguous memory area.** +**El objeto binario básico es `ArrayBuffer` -- una referencia a un área de memoria contigua de longitud fija.** -We create it like this: +Lo creamos así: ```js run -let buffer = new ArrayBuffer(16); // create a buffer of length 16 +let buffer = new ArrayBuffer(16); // crea un buffer de longitud 16 alert(buffer.byteLength); // 16 ``` -This allocates a contiguous memory area of 16 bytes and pre-fills it with zeroes. +Esto asigna un área de memoria contigua de 16 bytes y la rellena previamente con ceros. -```warn header="`ArrayBuffer` is not an array of something" -Let's eliminate a possible source of confusion. `ArrayBuffer` has nothing in common with `Array`: -- It has a fixed length, we can't increase or decrease it. -- It takes exactly that much space in the memory. -- To access individual bytes, another "view" object is needed, not `buffer[index]`. +```warn header="`ArrayBuffer` no es un array de algo" +Eliminemos una posible fuente de confusión. `ArrayBuffer` no tiene nada en común con `Array`: +- Tiene una longitud fija, no podemos aumentarla ni disminuirla. +- Ocupa exactamente ese espacio en la memoria. +- Para acceder a bytes individuales, se necesita otro objeto "vista", no `buffer[índice]`. ``` -`ArrayBuffer` is a memory area. What's stored in it? It has no clue. Just a raw sequence of bytes. +`ArrayBuffer` es un área de memoria. ¿Qué se almacena en ella? No tiene ninguna pista. Sólo una secuencia cruda de bytes. -**To manipulate an `ArrayBuffer`, we need to use a "view" object.** +**Para manipular un `ArrayBuffer`, necesitamos utilizar un objeto "vista".** -A view object does not store anything on its own. It's the "eyeglasses" that give an interpretation of the bytes stored in the `ArrayBuffer`. +Un objeto vista no almacena nada por sí mismo. Son "gafas" que le dan una interpretación a los bytes almacenados en el `ArrayBuffer`. -For instance: +Por ejemplo: -- **`Uint8Array`** -- treats each byte in `ArrayBuffer` as a separate number, with possible values from 0 to 255 (a byte is 8-bit, so it can hold only that much). Such value is called a "8-bit unsigned integer". -- **`Uint16Array`** -- treats every 2 bytes as an integer, with possible values from 0 to 65535. That's called a "16-bit unsigned integer". -- **`Uint32Array`** -- treats every 4 bytes as an integer, with possible values from 0 to 4294967295. That's called a "32-bit unsigned integer". -- **`Float64Array`** -- treats every 8 bytes as a floating point number with possible values from <code>5.0x10<sup>-324</sup></code> to <code>1.8x10<sup>308</sup></code>. +- **`Uint8Array`** : trata cada byte del `ArrayBuffer` como un número separado, con valores posibles de 0 a 255 (un byte es de 8 bits, por lo que sólo puede contener esa cantidad). Este valor se denomina "entero sin signo de 8 bits". +- **`Uint16Array`** : trata cada 2 bytes como un entero, con valores posibles de 0 a 65535. Es lo que se llama un "entero sin signo de 16 bits". +- **`Uint32Array`** : trata cada 4 bytes como un entero, con valores posibles de 0 a 4294967295. Eso se llama "entero sin signo de 32 bits". +- **`Float64Array`** : trata cada 8 bytes como un número de punto flotante con valores posibles desde <code>5.0x10<sup>-324</sup></code> hasta <code>1.8x10<sup>308</sup></code>. -So, the binary data in an `ArrayBuffer` of 16 bytes can be interpreted as 16 "tiny numbers", or 8 bigger numbers (2 bytes each), or 4 even bigger (4 bytes each), or 2 floating-point values with high precision (8 bytes each). +Así, los datos binarios de un `ArrayBuffer` de 16 bytes pueden interpretarse como 16 "números diminutos", u 8 números más grandes (2 bytes cada uno), o 4 aún más grandes (4 bytes cada uno), o 2 valores de punto flotante con alta precisión (8 bytes cada uno). ![](arraybuffer-views.svg) -`ArrayBuffer` is the core object, the root of everything, the raw binary data. +`ArrayBuffer` es el objeto central, la raíz de todo, los datos binarios en bruto. -But if we're going to write into it, or iterate over it, basically for almost any operation – we must use a view, e.g: +Pero si vamos a escribir en él, o iterar sobre él (básicamente, para casi cualquier operación), debemos utilizar una vista. Por ejemplo: ```js run -let buffer = new ArrayBuffer(16); // create a buffer of length 16 +let buffer = new ArrayBuffer(16); // crea un buffer de longitud 16 *!* -let view = new Uint32Array(buffer); // treat buffer as a sequence of 32-bit integers +let view = new Uint32Array(buffer); // trata el buffer como una secuencia de enteros de 32 bits -alert(Uint32Array.BYTES_PER_ELEMENT); // 4 bytes per integer +alert(Uint32Array.BYTES_PER_ELEMENT); // 4 bytes por entero */!* -alert(view.length); // 4, it stores that many integers -alert(view.byteLength); // 16, the size in bytes +alert(view.length); // 4, almacena esa cantidad de enteros +alert(view.byteLength); // 16, el tamaño en bytes -// let's write a value +// escribamos un valor view[0] = 123456; -// iterate over values +// iteración sobre los valores for(let num of view) { - alert(num); // 123456, then 0, 0, 0 (4 values total) + alert(num); // 123456, luego 0, 0, 0 (4 valores en total) } ``` ## TypedArray -The common term for all these views (`Uint8Array`, `Uint32Array`, etc) is [TypedArray](https://tc39.github.io/ecma262/#sec-typedarray-objects). They share the same set of methods and properties. +El término común para todas estas vistas (`Uint8Array`, `Uint32Array`, etc) es [TypedArray](https://tc39.github.io/ecma262/#sec-typedarray-objects). Comparten el mismo conjunto de métodos y propiedades. -Please note, there's no constructor called `TypedArray`, it's just a common "umbrella" term to represent one of views over `ArrayBuffer`: `Int8Array`, `Uint8Array` and so on, the full list will soon follow. +Por favor ten en cuenta que no hay ningún constructor llamado `TypedArray`, es sólo un término "paraguas" común para representar una de las vistas sobre `ArrayBuffer`: `Int8Array`, `Uint8Array` y así sucesivamente, la lista completa seguirá pronto. -When you see something like `new TypedArray`, it means any of `new Int8Array`, `new Uint8Array`, etc. +Cuando veas algo como `new TypedArray`, significa cualquiera de `new Int8Array`, `new Uint8Array`, etc. -Typed arrays behave like regular arrays: have indexes and are iterable. +Las matrices tipificadas se comportan como las matrices normales: tienen índices y son iterables. -A typed array constructor (be it `Int8Array` or `Float64Array`, doesn't matter) behaves differently depending on argument types. +Un constructor de array tipado (ya sea `Int8Array` o `Float64Array`) se comporta de forma diferente dependiendo del tipo de argumento. -There are 5 variants of arguments: +Hay 5 variantes de argumentos: ```js new TypedArray(buffer, [byteOffset], [length]); @@ -91,92 +91,92 @@ new TypedArray(length); new TypedArray(); ``` -1. If an `ArrayBuffer` argument is supplied, the view is created over it. We used that syntax already. +1. Si se suministra un argumento `ArrayBuffer`, la vista se crea sobre él. Ya usamos esa sintaxis. - Optionally we can provide `byteOffset` to start from (0 by default) and the `length` (till the end of the buffer by default), then the view will cover only a part of the `buffer`. + Opcionalmente podemos proporcionar `byteOffset` para empezar (0 por defecto) y la longitud o `length` (hasta el final del buffer por defecto), entonces la vista cubrirá sólo una parte del `buffer`. -2. If an `Array`, or any array-like object is given, it creates a typed array of the same length and copies the content. +2. Si se da un `Array`, o cualquier objeto tipo array, se crea un array tipado de la misma longitud y se copia el contenido. - We can use it to pre-fill the array with the data: + Podemos usarlo para pre-llenar el array con los datos: ```js run *!* let arr = new Uint8Array([0, 1, 2, 3]); */!* - alert( arr.length ); // 4, created binary array of the same length - alert( arr[1] ); // 1, filled with 4 bytes (unsigned 8-bit integers) with given values - ``` -3. If another `TypedArray` is supplied, it does the same: creates a typed array of the same length and copies values. Values are converted to the new type in the process, if needed. + alert( arr.length ); // 4, creó una matriz binaria de la misma longitud + alert( arr[1] ); // 1, rellenado con 4 bytes (enteros de 8 bits sin signo) con valores dados + ``` +3. Si se suministra otro `TypedArray` hace lo mismo: crea un array tipado de la misma longitud y copia los valores. Los valores se convierten al nuevo tipo en el proceso, si es necesario. ```js run let arr16 = new Uint16Array([1, 1000]); *!* let arr8 = new Uint8Array(arr16); */!* alert( arr8[0] ); // 1 - alert( arr8[1] ); // 232, tried to copy 1000, but can't fit 1000 into 8 bits (explanations below) + alert( arr8[1] ); // 232, trató de copiar 1000, pero no puede encajar 1000 en 8 bits (explicaciones a continuación) ``` -4. For a numeric argument `length` -- creates the typed array to contain that many elements. Its byte length will be `length` multiplied by the number of bytes in a single item `TypedArray.BYTES_PER_ELEMENT`: +4. Para un argumento numérico `length` : crea el array tipado para contener ese número de elementos. Su longitud en bytes será `length` multiplicada por el número de bytes de un solo elemento `TypedArray.BYTES_PER_ELEMENT`: ```js run - let arr = new Uint16Array(4); // create typed array for 4 integers - alert( Uint16Array.BYTES_PER_ELEMENT ); // 2 bytes per integer - alert( arr.byteLength ); // 8 (size in bytes) + let arr = new Uint16Array(4); // crea un array tipado para 4 enteros + alert( Uint16Array.BYTES_PER_ELEMENT ); // 2 bytes por entero + alert( arr.byteLength ); // 8 (tamaño en bytes) ``` -5. Without arguments, creates an zero-length typed array. +5. Sin argumentos crea un array tipado de longitud cero. -We can create a `TypedArray` directly, without mentioning `ArrayBuffer`. But a view cannot exist without an underlying `ArrayBuffer`, so gets created automatically in all these cases except the first one (when provided). +Podemos crear un `TypedArray` directamente sin mencionar `ArrayBuffer`. Pero una vista no puede existir sin un `ArrayBuffer` subyacente, por lo que se crea automáticamente en todos estos casos excepto en el primero (cuando se proporciona). -To access the underlying `ArrayBuffer`, there are following properties in `TypedArray`: -- `buffer` -- references the `ArrayBuffer`. -- `byteLength` -- the length of the `ArrayBuffer`. +Para acceder al `ArrayBuffer` subyacente, en `TypedArray` existen las propiedades: +- `buffer` : hace referencia al `ArrayBuffer`. +- `byteLength` : la longitud del `ArrayBuffer`. -So, we can always move from one view to another: +De esta forma siempre podemos pasar de una vista a otra: ```js let arr8 = new Uint8Array([0, 1, 2, 3]); -// another view on the same data +// otra vista sobre los mismos datos let arr16 = new Uint16Array(arr8.buffer); ``` -Here's the list of typed arrays: +Esta es la lista de arrays tipados: -- `Uint8Array`, `Uint16Array`, `Uint32Array` -- for integer numbers of 8, 16 and 32 bits. - - `Uint8ClampedArray` -- for 8-bit integers, "clamps" them on assignment (see below). -- `Int8Array`, `Int16Array`, `Int32Array` -- for signed integer numbers (can be negative). -- `Float32Array`, `Float64Array` -- for signed floating-point numbers of 32 and 64 bits. +- `Uint8Array`, `Uint16Array`, `Uint32Array` : para números enteros de 8, 16 y 32 bits. + - `Uint8ClampedArray` : para números enteros de 8 bits, los "sujeta" en la asignación (ver más abajo). +- `Int8Array`, `Int16Array`, `Int32Array` : para números enteros con signo (pueden ser negativos). +- `Float32Array`, `Float64Array` : para números de punto flotante con signo de 32 y 64 bits. -```warn header="No `int8` or similar single-valued types" -Please note, despite of the names like `Int8Array`, there's no single-value type like `int`, or `int8` in JavaScript. +```warn header="No existe `int8` o tipos de valor único similares" +Ten en cuenta que a pesar de los nombres como `Int8Array`, no hay ningún tipo de valor único como `int` o `int8` en JavaScript. -That's logical, as `Int8Array` is not an array of these individual values, but rather a view on `ArrayBuffer`. +Esto es lógico ya que `Int8Array` no es un array de estos valores individuales sino una vista sobre `ArrayBuffer`. ``` -### Out-of-bounds behavior +### Comportamiento fuera de los límites -What if we attempt to write an out-of-bounds value into a typed array? There will be no error. But extra bits are cut-off. +¿Qué pasa si intentamos escribir un valor fuera de límites en un array tipado? No habrá ningún error. Pero los bits extra se cortan. -For instance, let's try to put 256 into `Uint8Array`. In binary form, 256 is `100000000` (9 bits), but `Uint8Array` only provides 8 bits per value, that makes the available range from 0 to 255. +Por ejemplo, intentemos poner 256 en `Uint8Array`. En forma binaria 256 es `100000000` (9 bits), pero `Uint8Array` sólo proporciona 8 bits por valor, lo que hace que el rango disponible sea de 0 a 255. -For bigger numbers, only the rightmost (less significant) 8 bits are stored, and the rest is cut off: +Para los números más grandes, sólo se almacenan los 8 bits más a la derecha (menos significativos), y el resto se corta: ![](8bit-integer-256.svg) -So we'll get zero. +Así que obtendremos un cero. -For 257, the binary form is `100000001` (9 bits), the rightmost 8 get stored, so we'll have `1` in the array: +Para el 257 la forma binaria es `100000001` (9 bits), los 8 más a la derecha se almacenan, por lo que tendremos `1` en el array: ![](8bit-integer-257.svg) -In other words, the number modulo 2<sup>8</sup> is saved. +Es decir, se guarda el número módulo 2<sup>8</sup>. -Here's the demo: +Esta es la demo: ```js run let uint8array = new Uint8Array(16); let num = 256; -alert(num.toString(2)); // 100000000 (binary representation) +alert(num.toString(2)); // 100000000 (representación binaria) uint8array[0] = 256; uint8array[1] = 257; @@ -185,49 +185,49 @@ alert(uint8array[0]); // 0 alert(uint8array[1]); // 1 ``` -`Uint8ClampedArray` is special in this aspect, its behavior is different. It saves 255 for any number that is greater than 255, and 0 for any negative number. That behavior is useful for image processing. +`Uint8ClampedArray` es especial en este aspecto y su comportamiento es diferente. Guarda 255 para cualquier número que sea mayor que 255, y 0 para cualquier número negativo. Este comportamiento es útil para el procesamiento de imágenes. -## TypedArray methods +## Métodos TypedArray -`TypedArray` has regular `Array` methods, with notable exceptions. +`TypedArray` tiene los métodos regulares de `Array`, con notables excepciones. -We can iterate, `map`, `slice`, `find`, `reduce` etc. +Podemos iterar, `map`, `slice`, `find`, `reduce` etc. -There are few things we can't do though: +Sin embargo, hay algunas cosas que no podemos hacer: -- No `splice` -- we can't "delete" a value, because typed arrays are views on a buffer, and these are fixed, contiguous areas of memory. All we can do is to assign a zero. -- No `concat` method. +- No hay `splice` : no podemos "borrar" un valor, porque los arrays tipados son vistas en un buffer y estos son áreas fijas y contiguas de memoria. Todo lo que podemos hacer es asignar un cero. +- No hay método `concat`. -There are two additional methods: +Hay dos métodos adicionales: -- `arr.set(fromArr, [offset])` copies all elements from `fromArr` to the `arr`, starting at position `offset` (0 by default). -- `arr.subarray([begin, end])` creates a new view of the same type from `begin` to `end` (exclusive). That's similar to `slice` method (that's also supported), but doesn't copy anything -- just creates a new view, to operate on the given piece of data. +- `arr.set(fromArr, [offset])` copia todos los elementos de `fromArr` al `arr`, empezando en la posición `offset` (0 por defecto). +- `arr.subarray([begin, end])` crea una nueva vista del mismo tipo desde `begin` hasta `end` (excluyéndolo). Es similar al método `slice` (que también está soportado), pero no copia nada, sólo crea una nueva vista para operar sobre el trozo de datos dado. -These methods allow us to copy typed arrays, mix them, create new arrays from existing ones, and so on. +Estos métodos nos permiten copiar arrays tipados, mezclarlos, crear nuevos arrays a partir de los existentes, etc. ## DataView -[DataView](mdn:/JavaScript/Reference/Global_Objects/DataView) is a special super-flexible "untyped" view over `ArrayBuffer`. It allows to access the data on any offset in any format. +[DataView](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView) es una vista especial superflexible "no tipada" sobre `ArrayBuffer`. Permite acceder a los datos en cualquier desplazamiento en cualquier formato. -- For typed arrays, the constructor dictates what the format is. The whole array is supposed to be uniform. The i-th number is `arr[i]`. -- With `DataView` we access the data with methods like `.getUint8(i)` or `.getUint16(i)`. We choose the format at method call time instead of the construction time. +- En el caso de los arrays tipados, el constructor dicta cuál es el formato. Se supone que todo el array es uniforme. El número i es `arr[i]`. +- Con `DataView` accedemos a los datos con métodos como `.getUint8(i)` o `.getUint16(i)`. Elegimos el formato en el momento de la llamada al método en lugar de en el momento de la construcción. -The syntax: +La sintaxis: ```js new DataView(buffer, [byteOffset], [byteLength]) ``` -- **`buffer`** -- the underlying `ArrayBuffer`. Unlike typed arrays, `DataView` doesn't create a buffer on its own. We need to have it ready. -- **`byteOffset`** -- the starting byte position of the view (by default 0). -- **`byteLength`** -- the byte length of the view (by default till the end of `buffer`). +- **`buffer`** : el `ArrayBuffer` subyacente. A diferencia de los arrays tipados, `DataView` no crea un buffer por sí mismo. Necesitamos tenerlo preparado. +- **`byteOffset`** : la posición inicial en bytes de la vista (por defecto 0). +- **`byteLength`** : la longitud en bytes de la vista (por defecto hasta el final del `buffer`). -For instance, here we extract numbers in different formats from the same buffer: +Por ejemplo, aquí extraemos números en diferentes formatos del mismo buffer: ```js run -// binary array of 4 bytes, all have the maximal value 255 +// arreglo binario de 4 bytes, todos tienen el valor máximo 255 let buffer = new Uint8Array([255, 255, 255, 255]).buffer; let dataView = new DataView(buffer); @@ -235,38 +235,38 @@ let dataView = new DataView(buffer); // get 8-bit number at offset 0 alert( dataView.getUint8(0) ); // 255 -// now get 16-bit number at offset 0, it consists of 2 bytes, together interpreted as 65535 -alert( dataView.getUint16(0) ); // 65535 (biggest 16-bit unsigned int) +// ahora obtenemos un número de 16 bits en el offset 0, que consta de 2 bytes, que juntos se interpretan como 65535 +alert( dataView.getUint16(0) ); // 65535 (mayor entero sin signo de 16 bits) -// get 32-bit number at offset 0 -alert( dataView.getUint32(0) ); // 4294967295 (biggest 32-bit unsigned int) +// obtener un número de 32 bits en el offset 0 +alert( dataView.getUint32(0) ); // 4294967295 (mayor entero de 32 bits sin signo) -dataView.setUint32(0, 0); // set 4-byte number to zero, thus setting all bytes to 0 +dataView.setUint32(0, 0); // poner a cero el número de 4 bytes, poniendo así todos los bytes a 0 ``` -`DataView` is great when we store mixed-format data in the same buffer. For example, when we store a sequence of pairs (16-bit integer, 32-bit float), `DataView` allows to access them easily. +`DataView` es genial cuando almacenamos datos de formato mixto en el mismo buffer. Por ejemplo, cuando almacenamos una secuencia de pares (entero de 16 bits, flotante de 32 bits), `DataView` permite acceder a ellos fácilmente. -## Summary +## Resumen -`ArrayBuffer` is the core object, a reference to the fixed-length contiguous memory area. +`ArrayBuffer` es el objeto central, una referencia al área de memoria contigua de longitud fija. -To do almost any operation on `ArrayBuffer`, we need a view. +Para hacer casi cualquier operación sobre `ArrayBuffer`, necesitamos una vista. -- It can be a `TypedArray`: - - `Uint8Array`, `Uint16Array`, `Uint32Array` -- for unsigned integers of 8, 16, and 32 bits. - - `Uint8ClampedArray` -- for 8-bit integers, "clamps" them on assignment. - - `Int8Array`, `Int16Array`, `Int32Array` -- for signed integer numbers (can be negative). - - `Float32Array`, `Float64Array` -- for signed floating-point numbers of 32 and 64 bits. -- Or a `DataView` -- the view that uses methods to specify a format, e.g. `getUint8(offset)`. +- Puede ser un `TypedArray`: + - `Uint8Array`, `Uint16Array`, `Uint32Array` : para enteros sin signo de 8, 16 y 32 bits. + - `Uint8ClampedArray` : para enteros de 8 bits, los "sujeta" en la asignación. + - `Int8Array`, `Int16Array`, `Int32Array` : para números enteros con signo (pueden ser negativos). + - `Float32Array`, `Float64Array` : para números de punto flotante con signo de 32 y 64 bits. +- O una `DataView` : la vista que utiliza métodos para especificar un formato, por ejemplo `getUint8(offset)`. -In most cases we create and operate directly on typed arrays, leaving `ArrayBuffer` under cover, as a "common denominator". We can access it as `.buffer` and make another view if needed. +En la mayoría de los casos creamos y operamos directamente sobre arrays tipados, dejando el `ArrayBuffer` a cubierto, como "denominador común". Podemos acceder a él como `.buffer` y hacer otra vista si es necesario. -There are also two additional terms, that are used in descriptions of methods that operate on binary data: -- `ArrayBufferView` is an umbrella term for all these kinds of views. -- `BufferSource` is an umbrella term for `ArrayBuffer` or `ArrayBufferView`. +También hay dos términos adicionales, que se utilizan en las descripciones de los métodos que operan con datos binarios: +- `ArrayBufferView` es un término paraguas para todos estos tipos de vistas. +- El término `BufferSource` es un término general para `ArrayBuffer` o `ArrayBufferView`. -We'll see these terms in the next chapters. `BufferSource` is one of the most common terms, as it means "any kind of binary data" -- an `ArrayBuffer` or a view over it. +Veremos estos términos en los próximos capítulos. El término `BufferSource` es uno de los más comunes, ya que significa "cualquier tipo de datos binarios" : un `ArrayBuffer` o una vista sobre él. -Here's a cheatsheet: +Aquí tienes la hoja de ruta: ![](arraybuffer-view-buffersource.svg) diff --git a/4-binary/02-text-decoder/article.md b/4-binary/02-text-decoder/article.md index a0c80145c..02f042680 100644 --- a/4-binary/02-text-decoder/article.md +++ b/4-binary/02-text-decoder/article.md @@ -1,35 +1,35 @@ -# TextDecoder and TextEncoder +# TextDecoder y TextEncoder -What if the binary data is actually a string? For instance, we received a file with textual data. +¿Qué pasa si los datos binarios son en realidad un string? Por ejemplo, recibimos un archivo con datos textuales. -The built-in [TextDecoder](https://encoding.spec.whatwg.org/#interface-textdecoder) object allows one to read the value into an actual JavaScript string, given the buffer and the encoding. +El objeto incorporado [TextDecoder](https://encoding.spec.whatwg.org/#interface-textdecoder) nos permite leer el valor y convertirlo en un string de JavaScript, dados el búfer y la codificación. -We first need to create it: +Primero necesitamos crearlo: ```js let decoder = new TextDecoder([label], [options]); ``` -- **`label`** -- the encoding, `utf-8` by default, but `big5`, `windows-1251` and many other are also supported. -- **`options`** -- optional object: - - **`fatal`** -- boolean, if `true` then throw an exception for invalid (non-decodable) characters, otherwise (default) replace them with character `\uFFFD`. - - **`ignoreBOM`** -- boolean, if `true` then ignore BOM (an optional byte-order Unicode mark), rarely needed. +- **`label`** -- la codificación, `utf-8` por defecto, pero `big5`, `windows-1251` y muchos otros también son soportados. +- **`options`** -- objeto opcional: + - **`fatal`** -- booleano, si es `true` arroja una excepción por caracteres inválidos (no-decodificable), de otra manera (por defecto) son reemplazados con el carácter `\uFFFD`. + - **`ignoreBOM`** -- booleano, si es `true` entonces ignora BOM (una marca Unicode de orden de bytes opcional), raramente es necesario. -...And then decode: +...Y luego decodificar: ```js let str = decoder.decode([input], [options]); ``` -- **`input`** -- `BufferSource` to decode. -- **`options`** -- optional object: - - **`stream`** -- true for decoding streams, when `decoder` is called repeatedly with incoming chunks of data. In that case a multi-byte character may occasionally split between chunks. This options tells `TextDecoder` to memorize "unfinished" characters and decode them when the next chunk comes. +- **`input`** -- `BufferSource` para decodificar. +- **`options`** -- objeto opcional: + - **`stream`** -- true para decodificación de secuencias, cuando el `decoder` es usado repetidamente para fragmentos de datos entrantes. En ese caso, un carácter de varios bytes puede ocasionalmente dividirse entre fragmentos. Esta opción le dice al `TextDecoder` que memorice caracteres "incompletos" y que los decodifique cuando venga el siguiente fragmento. -For instance: +Por ejemplo: ```js run -let uint8Array = new Uint8Array([72, 101, 108, 108, 111]); +let uint8Array = new Uint8Array([72, 111, 108, 97]); -alert( new TextDecoder().decode(uint8Array) ); // Hello +alert( new TextDecoder().decode(uint8Array) ); // Hola ``` @@ -39,38 +39,38 @@ let uint8Array = new Uint8Array([228, 189, 160, 229, 165, 189]); alert( new TextDecoder().decode(uint8Array) ); // 你好 ``` -We can decode a part of the buffer by creating a subarray view for it: +Podemos decodificar una parte del búfer al crear una vista de sub arreglo para ello: ```js run -let uint8Array = new Uint8Array([0, 72, 101, 108, 108, 111, 0]); +let uint8Array = new Uint8Array([0, 72, 111, 108, 97, 0]); -// the string is in the middle -// create a new view over it, without copying anything +// El string esta en medio +// crear una nueva vista sobre el string, sin copiar nada let binaryString = uint8Array.subarray(1, -1); -alert( new TextDecoder().decode(binaryString) ); // Hello +alert( new TextDecoder().decode(binaryString) ); // Hola ``` ## TextEncoder -[TextEncoder](https://encoding.spec.whatwg.org/#interface-textencoder) does the reverse thing -- converts a string into bytes. +[TextEncoder](https://encoding.spec.whatwg.org/#interface-textencoder) hace lo contrario: convierte un string en bytes. -The syntax is: +La sintaxis es: ```js let encoder = new TextEncoder(); ``` -The only encoding it supports is "utf-8". +La única codificación que soporta es "utf-8". -It has two methods: -- **`encode(str)`** -- returns `Uint8Array` from a string. -- **`encodeInto(str, destination)`** -- encodes `str` into `destination` that must be `Uint8Array`. +Tiene dos métodos: +- **`encode(str)`** -- regresa un dato de tipo `Uint8Array` de un string. +- **`encodeInto(str, destination)`** -- codifica un `str` en `destination`, este último debe ser de tipo `Uint8Array`. ```js run let encoder = new TextEncoder(); -let uint8Array = encoder.encode("Hello"); -alert(uint8Array); // 72,101,108,108,111 +let uint8Array = encoder.encode("Hola"); +alert(uint8Array); // 72,111,108,97 ``` diff --git a/4-binary/03-blob/article.md b/4-binary/03-blob/article.md index fc0150577..a0378896c 100644 --- a/4-binary/03-blob/article.md +++ b/4-binary/03-blob/article.md @@ -1,69 +1,69 @@ # Blob -`ArrayBuffer` and views are a part of ECMA standard, a part of JavaScript. +Los `ArrayBuffer` y las vistas son parte del estándar ECMA, una parte de JavaScript. -In the browser, there are additional higher-level objects, described in [File API](https://www.w3.org/TR/FileAPI/), in particular `Blob`. +En el navegador, hay objetos de alto nivel adicionales, descritas en la [API de Archivo](https://www.w3.org/TR/FileAPI/), en particular `Blob`. -`Blob` consists of an optional string `type` (a MIME-type usually), plus `blobParts` -- a sequence of other `Blob` objects, strings and `BufferSource`. +`Blob` consta de un tipo especial de cadena (usualmente de tipo MIME), más partes Blob: una secuencia de otros objetos `Blob`, cadenas y `BufferSource`. ![](blob.svg) -The constructor syntax is: +La sintaxis del constructor es: ```js -new Blob(blobParts, options); +new Blob(blobParts, opciones); ``` -- **`blobParts`** is an array of `Blob`/`BufferSource`/`String` values. -- **`options`** optional object: - - **`type`** -- `Blob` type, usually MIME-type, e.g. `image/png`, - - **`endings`** -- whether to transform end-of-line to make the `Blob` correspond to current OS newlines (`\r\n` or `\n`). By default `"transparent"` (do nothing), but also can be `"native"` (transform). +- **`blobParts`** es un array de valores `Blob`/`BufferSource`/`String`. +- **`opciones`** objeto opcional: + - **`tipo`** -- `Blob`, usualmente un tipo MIME, por ej. `image/png`, + - **`endings`** -- para transformar los finales de línea para hacer que el `Blob` coincida con los caracteres de nueva línea del Sistema Operativo actual (`\r\n` or `\n`). Por omisión es `"transparent"` (no hacer nada), pero también puede ser `"native"` (transformar). -For example: +Por ejemplo: ```js -// create Blob from a string +// crear un Blob a partir de una cadena let blob = new Blob(["<html>…</html>"], {type: 'text/html'}); -// please note: the first argument must be an array [...] +// observación: el primer argumento debe ser un array [...] ``` ```js -// create Blob from a typed array and strings -let hello = new Uint8Array([72, 101, 108, 108, 111]); // "Hello" in binary form +// crear un Blob a partir de un array tipado y cadenas +let hello = new Uint8Array([72, 101, 108, 108, 111]); // "Hello" en formato binario let blob = new Blob([hello, ' ', 'world'], {type: 'text/plain'}); ``` -We can extract `Blob` slices with: +Podemos extraer porciones del `Blob` con: ```js blob.slice([byteStart], [byteEnd], [contentType]); ``` -- **`byteStart`** -- the starting byte, by default 0. -- **`byteEnd`** -- the last byte (exclusive, by default till the end). -- **`contentType`** -- the `type` of the new blob, by default the same as the source. +- **`byteStart`** -- el byte inicial, por omisión es 0. +- **`byteEnd`** -- el último byte (exclusivo, por omisión es el final). +- **`contentType`** -- el `tipo` del nuevo blob, por omisión es el mismo que la fuente. -The arguments are similar to `array.slice`, negative numbers are allowed too. +Los argumentos son similares a `array.slice`, los números negativos también son permitidos. -```smart header="`Blob` objects are immutable" -We can't change data directly in a `Blob`, but we can slice parts of a `Blob`, create new `Blob` objects from them, mix them into a new `Blob` and so on. +```smart header="los objetos `Blob` son inmutables" +No podemos cambiar datos directamente en un `Blob`, pero podemos obtener partes de un `Blob`, crear nuevos objetos `Blob` a partir de ellos, mezclarlos en un nuevo `Blob` y así por el estilo. -This behavior is similar to JavaScript strings: we can't change a character in a string, but we can make a new corrected string. +Este comportamiento es similar a las cadenas de JavaScript: no podemos cambiar un carácter en una cadena, pero podemos hacer una nueva, corregida. ``` -## Blob as URL +## Blob como URL -A Blob can be easily used as a URL for `<a>`, `<img>` or other tags, to show its contents. +Un Blob puede ser utilizado fácilmente como una URL para `<a>`, `<img>` u otras etiquetas, para mostrar su contenido. -Thanks to `type`, we can also download/upload `Blob` objects, and the `type` naturally becomes `Content-Type` in network requests. +Gracias al `tipo`, también podemos descargar/cargar objetos `Blob`, y el `tipo` se convierte naturalmente en `Content-Type` en solicitudes de red. -Let's start with a simple example. By clicking on a link you download a dynamically-generated `Blob` with `hello world` contents as a file: +Empecemos con un ejemplo simple. Al hacer click en un link, descargas un `Blob` dinámicamente generado con contenido `hello world` en forma de archivo: ```html run -<!-- download attribute forces the browser to download instead of navigating --> -<a download="hello.txt" href='#' id="link">Download</a> +<!-- descargar atributos fuerza al navegador a descargar en lugar de navegar --> +<a download="hello.txt" href='#' id="link">Descargar</a> <script> let blob = new Blob(["Hello, world!"], {type: 'text/plain'}); @@ -72,9 +72,9 @@ link.href = URL.createObjectURL(blob); </script> ``` -We can also create a link dynamically in JavaScript and simulate a click by `link.click()`, then download starts automatically. +También podemos crear un link dinámicamente en JavaScript y simular un click con `link.click()`, y la descarga inicia automáticamente. -Here's the similar code that causes user to download the dynamically created `Blob`, without any HTML: +Este es un código similar que permite al usuario descargar el `Blob` creado dinámicamente, sin HTML: ```js run let link = document.createElement('a'); @@ -89,50 +89,50 @@ link.click(); URL.revokeObjectURL(link.href); ``` -`URL.createObjectURL` takes a `Blob` and creates a unique URL for it, in the form `blob:<origin>/<uuid>`. +`URL.createObjectURL` toma un `Blob` y crea una URL única para él, con la forma `blob:<origin>/<uuid>`. -That's what the value of `link.href` looks like: +Así es como se ve el valor de `link.href`: ``` blob:https://javascript.info/1e67e00e-860d-40a5-89ae-6ab0cbee6273 ``` -For each URL generated by `URL.createObjectURL` the browser stores a URL -> `Blob` mapping internally. So such URLs are short, but allow to access the `Blob`. +Por cada URL generada por `URL.createObjectURL` el navegador almacena un URL -> `Blob` mapeado internamente. Así que las URLs son cortas, pero permiten acceder al `Blob`. -A generated URL (and hence the link with it) is only valid within the current document, while it's open. And it allows to reference the `Blob` in `<img>`, `<a>`, basically any other object that expects a URL. +Una URL generada (y por lo tanto su enlace) solo es válida en el documento actual, mientras está abierto. Y este permite referenciar al `Blob` en `<img>`, `<a>`, básicamente cualquier otro objeto que espera un URL. -There's a side effect though. While there's a mapping for a `Blob`, the `Blob` itself resides in the memory. The browser can't free it. +También hay efectos secundarios. Mientras haya un mapeado para un `Blob`, el `Blob` en sí mismo se guarda en la memoria. El navegador no puede liberarlo. -The mapping is automatically cleared on document unload, so `Blob` objects are freed then. But if an app is long-living, then that doesn't happen soon. +El mapeado se limpia automáticamente al vaciar un documento, así los objetos `Blob` son liberados. Pero si una aplicación es de larga vida, entonces eso no va a pasar pronto. -**So if we create a URL, that `Blob` will hang in memory, even if not needed any more.** +**Entonces, si creamos una URL, este `Blob` se mantendrá en la memoria, incluso si ya no se necesita.** -`URL.revokeObjectURL(url)` removes the reference from the internal mapping, thus allowing the `Blob` to be deleted (if there are no other references), and the memory to be freed. +`URL.revokeObjectURL(url)` elimina la referencia el mapeo interno, además de permitir que el `Blob` sea borrado (si ya no hay otras referencias), y que la memoria sea liberada. -In the last example, we intend the `Blob` to be used only once, for instant downloading, so we call `URL.revokeObjectURL(link.href)` immediately. +En el último ejemplo, intentamos que el `Blob` sea utilizado una sola vez, para descargas instantáneas, así llamamos `URL.revokeObjectURL(link.href)` inmediatamente. -In the previous example with the clickable HTML-link, we don't call `URL.revokeObjectURL(link.href)`, because that would make the `Blob` url invalid. After the revocation, as the mapping is removed, the URL doesn't work any more. +En el ejemplo anterior con el link HTML cliqueable, no llamamos `URL.revokeObjectURL(link.href)`, porque eso puede hacer la URL del `Blob` inválido. Después de la revocación, como el mapeo es eliminado, la URL ya no volverá a funcionar. -## Blob to base64 +## Blob a base64 -An alternative to `URL.createObjectURL` is to convert a `Blob` into a base64-encoded string. +Una alternativa a `URL.createObjectURL` es convertir un `Blob` en una cadena codificada en base64. -That encoding represents binary data as a string of ultra-safe "readable" characters with ASCII-codes from 0 to 64. And what's more important -- we can use this encoding in "data-urls". +Esa codificación representa datos binarios como una cadena ultra segura de caracteres "legibles" con códigos ASCII desde el 0 al 64. Y lo que es más importante, podemos utilizar codificación en las "URLs de datos". -A [data url](mdn:/http/Data_URIs) has the form `data:[<mediatype>][;base64],<data>`. We can use such urls everywhere, on par with "regular" urls. +Un [URL de datos](https://developer.mozilla.org/es/docs/Web/HTTP/Basics_of_HTTP/Datos_URIs) tiene la forma `data:[<mediatype>][;base64],<data>`. Podemos usar suficientes URLs por doquier, junto a URLs "regulares". -For instance, here's a smiley: +Por ejemplo, aquí hay una sonrisa: ```html <img src="data:image/png;base64,R0lGODlhDAAMAKIFAF5LAP/zxAAAANyuAP/gaP///wAAAAAAACH5BAEAAAUALAAAAAAMAAwAAAMlWLPcGjDKFYi9lxKBOaGcF35DhWHamZUW0K4mAbiwWtuf0uxFAgA7"> ``` -The browser will decode the string and show the image: <img src="data:image/png;base64,R0lGODlhDAAMAKIFAF5LAP/zxAAAANyuAP/gaP///wAAAAAAACH5BAEAAAUALAAAAAAMAAwAAAMlWLPcGjDKFYi9lxKBOaGcF35DhWHamZUW0K4mAbiwWtuf0uxFAgA7"> +El navegador decodificará la cadena y mostrará la imagen: <img src="data:image/png;base64,R0lGODlhDAAMAKIFAF5LAP/zxAAAANyuAP/gaP///wAAAAAAACH5BAEAAAUALAAAAAAMAAwAAAMlWLPcGjDKFYi9lxKBOaGcF35DhWHamZUW0K4mAbiwWtuf0uxFAgA7"> -To transform a `Blob` into base64, we'll use the built-in `FileReader` object. It can read data from Blobs in multiple formats. In the [next chapter](info:file) we'll cover it more in-depth. +Para transformar un `Blob` a base64, usaremos el objeto nativo `FileReader`. Puede leer datos de Blobs en múltiples formatos. En el [siguiente capítulo](info:file) lo cubriremos en profundidad. -Here's the demo of downloading a blob, now via base-64: +Aquí está el demo de descarga de un blob, ahora con base-64: ```js run let link = document.createElement('a'); @@ -142,125 +142,125 @@ let blob = new Blob(['Hello, world!'], {type: 'text/plain'}); *!* let reader = new FileReader(); -reader.readAsDataURL(blob); // converts the blob to base64 and calls onload +reader.readAsDataURL(blob); // convierte el blob a base64 y llama a onload */!* reader.onload = function() { - link.href = reader.result; // data url + link.href = reader.result; // URL de datos link.click(); }; ``` -Both ways of making a URL of a `Blob` are usable. But usually `URL.createObjectURL(blob)` is simpler and faster. +Se pueden utilizar ambas maneras para hacer una URL de un `Blob` . Pero usualmente `URL.createObjectURL(blob)` es más simple y rápido. -```compare title-plus="URL.createObjectURL(blob)" title-minus="Blob to data url" -+ We need to revoke them if care about memory. -+ Direct access to blob, no "encoding/decoding" -- No need to revoke anything. -- Performance and memory losses on big `Blob` objects for encoding. +```compare title-plus="URL.createObjectURL(blob)" title-minus="Blob a URL de datos" ++ Necesitamos revocarlos para cuidar la memoria. ++ Acceso directo al blob, sin "codificación/decodificación" +- No necesitamos revocar nada. +- Se pierde rendimiento y memoria en grandes objetos `Blob` al codificar. ``` -## Image to blob +## imagen a blob -We can create a `Blob` of an image, an image part, or even make a page screenshot. That's handy to upload it somewhere. +Podemos crear un `Blob` de una imagen, una parte de una imagen, o incluso hacer una captura de la página. Es práctico para subirlo a algún lugar. -Image operations are done via `<canvas>` element: +Las operaciones de imágenes se hacen a través del elemento `<canvas>`: -1. Draw an image (or its part) on canvas using [canvas.drawImage](mdn:/api/CanvasRenderingContext2D/drawImage). -2. Call canvas method [.toBlob(callback, format, quality)](mdn:/api/HTMLCanvasElement/toBlob) that creates a `Blob` and runs `callback` with it when done. +1. Dibuja una imagen (o una parte) en el canvas utilizando [canvas.drawImage](https://developer.mozilla.org/es/docs/Web/API/CanvasRenderingContext2D/drawImage). +2. Llama el método de canvas [.toBlob(callback, format, quality)](https://developer.mozilla.org/es/docs/Web/API/HTMLCanvasElement/toBlob) que crea un `Blob` y llama el `callback` cuando termina. -In the example below, an image is just copied, but we could cut from it, or transform it on canvas prior to making a blob: +En el ejemplo siguiente, un imagen se copia, pero no podemos cortarla o transformarla en el canvas hasta convertirla en blob: ```js run -// take any image +// tomar cualquier imagen let img = document.querySelector('img'); -// make <canvas> of the same size +// hacer el <canvas> del mismo tamaño let canvas = document.createElement('canvas'); canvas.width = img.clientWidth; canvas.height = img.clientHeight; let context = canvas.getContext('2d'); -// copy image to it (this method allows to cut image) +// copiar la imagen en él (este método permite cortar la imagen) context.drawImage(img, 0, 0); -// we can context.rotate(), and do many other things on canvas +// podemos hacer un context.rotate(), y muchas otras cosas en canvas -// toBlob is async operation, callback is called when done +// toBlob es una operación asincrónica, callback es llamada al terminar canvas.toBlob(function(blob) { - // blob ready, download it + // blob listo, descárgalo let link = document.createElement('a'); link.download = 'example.png'; link.href = URL.createObjectURL(blob); link.click(); - // delete the internal blob reference, to let the browser clear memory from it + // borrar la referencia interna del blob, para permitir al navegador eliminarlo de la memoria URL.revokeObjectURL(link.href); }, 'image/png'); ``` -If we prefer `async/await` instead of callbacks: +Si preferimos `async/await` en lugar de callbacks: ```js let blob = await new Promise(resolve => canvasElem.toBlob(resolve, 'image/png')); ``` -For screenshotting a page, we can use a library such as <https://github.com/niklasvh/html2canvas>. What it does is just walks the page and draws it on `<canvas>`. Then we can get a `Blob` of it the same way as above. +Para capturar la página, podemos utilizar una librería como <https://github.com/niklasvh/html2canvas>. Que lo que hace es escanear toda la página y dibujarla en el `<canvas>`. Entonces podemos obtener un `Blob` de la misma manera que arriba. -## From Blob to ArrayBuffer +## De Blob a ArrayBuffer -The `Blob` constructor allows to create a blob from almost anything, including any `BufferSource`. +El constructor de `Blob` permite crear un blob de casi cualquier cosa, incluyendo cualquier `BufferSource`. -But if we need to perform low-level processing, we can get the lowest-level `ArrayBuffer` from `blob.arrayBuffer()`: +Pero si queremos ejecutar un procesamiento de bajo nivel, podemos obtener el nivel más bajo de un `ArrayBuffer` desde `blob.arrayBuffer()`: ```js -// get arrayBuffer from blob +// obtener un arrayBuffer desde un blob const bufferPromise = await blob.arrayBuffer(); // or blob.arrayBuffer().then(buffer => /* process the ArrayBuffer */); ``` -## From Blob to stream +## De Blob a stream -When we read and write to a blob of more than `2 GB`, the use of `arrayBuffer` becomes more memory intensive for us. At this point, we can directly convert the blob to a stream. +Cuando leemos y escribimos un blob de más de `2 GB`, `arrayBuffer` hace un uso demasiado intensivo de la memoria para nosotros. En este punto, podemos convertir directamente el blob a un stream. -A stream is a special object that allows to read from it (or write into it) portion by portion. It's outside of our scope here, but here's an example, and you can read more at <https://developer.mozilla.org/en-US/docs/Web/API/Streams_API>. Streams are convenient for data that is suitable for processing piece-by-piece. +Un stream (flujo, corriente) es un objeto especial que permite leer (o escribir) porción por porción. Está fuera de nuestro objetivo aquí, pero este es un ejemplo que puedes leer <https://developer.mozilla.org/en-US/docs/Web/API/Streams_API>. Los streams son convenientes para datos que son adecuados para el proceso pieza por pieza. -The `Blob` interface's `stream()` method returns a `ReadableStream` which upon reading returns the data contained within the `Blob`. +El método interfaz `stream()` de `Blob` devuelve un `ReadableStream` que al leerlo devuelve datos contenidos dentro del `Blob`. -Then we can read from it, like this: +Entonces podemos leerlos desde él, como aquí: ```js -// get readableStream from blob +// obtiene readableStream desde blob const readableStream = blob.stream(); const stream = readableStream.getReader(); while (true) { - // for each iteration: value is the next blob fragment + // para cada iteración: data es el siguiente fragmento del blob let { done, value } = await stream.read(); if (done) { - // no more data in the stream - console.log('all blob processed.'); + // no hay más data en el stream + console.log('todo el blob procesado.'); break; } - // do something with the data portion we've just read from the blob + // hacer algo con la porción de datos que acabamos de leer del blob console.log(value); } ``` -## Summary +## Resumen -While `ArrayBuffer`, `Uint8Array` and other `BufferSource` are "binary data", a [Blob](https://www.w3.org/TR/FileAPI/#dfn-Blob) represents "binary data with type". +Mientras `ArrayBuffer`, `Uint8Array` y otros `BufferSource` son "datos binarios", un [Blob](https://www.w3.org/TR/FileAPI/#dfn-Blob) representa "datos binarios con tipo". -That makes Blobs convenient for upload/download operations, that are so common in the browser. +Esto hace a los Blobs convenientes para operaciones de carga/descarga, estos son muy comunes en el navegador. -Methods that perform web-requests, such as [XMLHttpRequest](info:xmlhttprequest), [fetch](info:fetch) and so on, can work with `Blob` natively, as well as with other binary types. +Los métodos que ejecutan solicitudes web, como [XMLHttpRequest](info:xmlhttprequest), [fetch](info:fetch) y otros, pueden trabajar nativamente con `Blob`, como con otros tipos binarios. -We can easily convert between `Blob` and low-level binary data types: +Podemos convertir fácilmente entre `Blob` y tipos de datos binarios de bajo nivel: -- We can make a `Blob` from a typed array using `new Blob(...)` constructor. -- We can get back `ArrayBuffer` from a Blob using `blob.arrayBuffer()`, and then create a view over it for low-level binary processing. +- Podemos crear un Blob desde un array tipado usando el constructor `new Blob(...)`. +- Podemos obtener de vuelta un `ArrayBuffer` desde un Blob usando `blob.arrayBuffer()`, y entonces crear una vista sobre él para procesamiento binario de bajo nivel. -Conversion streams are very useful when we need to handle large blob. You can easily create a `ReadableStream` from a blob. The `Blob` interface's `stream()` method returns a `ReadableStream` which upon reading returns the data contained within the blob. +Los streams de conversión son muy útiles cuando necesitamos manejar grandes blob. Puedes crear un `ReadableStream` desde un blob. El método interfaz `stream()` de `Blob` devuelve un `ReadableStream` que una vez leído devuelve los datos contenido en el blob. diff --git a/4-binary/04-file/article.md b/4-binary/04-file/article.md index 20878b650..82ea05ea7 100644 --- a/4-binary/04-file/article.md +++ b/4-binary/04-file/article.md @@ -1,27 +1,27 @@ -# File and FileReader +# File y FileReader -A [File](https://www.w3.org/TR/FileAPI/#dfn-file) object inherits from `Blob` and is extended with filesystem-related capabilities. +Un objeto [File](https://www.w3.org/TR/FileAPI/#dfn-file) hereda de `Blob` y extiende las capacidades relacionadas con el sistema de archivos. -There are two ways to obtain it. +Hay dos maneras de obtenerlo -First, there's a constructor, similar to `Blob`: +Primero, hay un constructor, similar al de `Blob`: ```js new File(fileParts, fileName, [options]) ``` -- **`fileParts`** -- is an array of Blob/BufferSource/String values. -- **`fileName`** -- file name string. -- **`options`** -- optional object: - - **`lastModified`** -- the timestamp (integer date) of last modification. +- **`fileParts`** -- es un array con valores de tipo Blob/BufferSource/String. +- **`fileName`** -- el nombre del archivo.. +- **`options`** -- objeto opcional: + - **`lastModified`** -- la marca de tiempo (fecha en mili-segundos, de tipo entero) de la última modificación. -Second, more often we get a file from `<input type="file">` or drag'n'drop or other browser interfaces. In that case, the file gets this information from OS. +Segundo, a menudo obtenemos un archivo mediante un `<input type="file">` o arrastrar y soltar u otras interfaces del navegador. En este caso el archivo obtiene la información del Sistema Operativo. -As `File` inherits from `Blob`, `File` objects have the same properties, plus: -- `name` -- the file name, -- `lastModified` -- the timestamp of last modification. +Como `File` (Archivo) hereda de `Blob`, objetos de tipo `File` tienen las mismas propiedades, mas: +- `name` -- el nombre del archivo, +- `lastModified` -- la marca de tiempo de la última modificación. -That's how we can get a `File` object from `<input type="file">`: +Así es como obtenemos un objeto `File` desde `<input type="file">` : ```html run <input type="file" onchange="showFile(this)"> @@ -37,49 +37,49 @@ function showFile(input) { ``` ```smart -The input may select multiple files, so `input.files` is an array-like object with them. Here we have only one file, so we just take `input.files[0]`. +El input puede seleccionar varios archivos, por lo que `input.files` es un array de dichos archivos . En este caso tenemos un solo archivo por lo que solo es necesario usar `input.files[0]`. ``` ## FileReader -[FileReader](https://www.w3.org/TR/FileAPI/#dfn-filereader) is an object with the sole purpose of reading data from `Blob` (and hence `File` too) objects. +[FileReader](https://www.w3.org/TR/FileAPI/#dfn-filereader) es un objeto con el único porpósito de leer datos desde objetos de tipo `Blob` (por lo tanto `File` también). -It delivers the data using events, as reading from disk may take time. +El entrega los datos usando eventos debido a que leerlos desde el disco puede tomar un tiempo. -The constructor: +El constructor: ```js -let reader = new FileReader(); // no arguments +let reader = new FileReader(); // sin argumentos ``` -The main methods: +Los métodos principales: -- **`readAsArrayBuffer(blob)`** -- read the data in binary format `ArrayBuffer`. -- **`readAsText(blob, [encoding])`** -- read the data as a text string with the given encoding (`utf-8` by default). -- **`readAsDataURL(blob)`** -- read the binary data and encode it as base64 data url. -- **`abort()`** -- cancel the operation. +- **`readAsArrayBuffer(blob)`** -- lee los datos en formato binario `ArrayBuffer`. +- **`readAsText(blob, [codificación])`** -- lee los datos como una cadena de texto con la codificación dada (por defecto es `utf-8`). +- **`readAsDataURL(blob)`** -- lee los datos binarios y los codifica como [Datos URIs] en base 64 (https://developer.mozilla.org/es/docs/Web/HTTP/Basics_of_HTTP/Datos_URIs). +- **`abort()`** -- cancela la operación. -The choice of `read*` method depends on which format we prefer, how we're going to use the data. +La opción del método `read*` depende de qué formato preferimos y cómo vamos a usar los datos. -- `readAsArrayBuffer` -- for binary files, to do low-level binary operations. For high-level operations, like slicing, `File` inherits from `Blob`, so we can call them directly, without reading. -- `readAsText` -- for text files, when we'd like to get a string. -- `readAsDataURL` -- when we'd like to use this data in `src` for `img` or another tag. There's an alternative to reading a file for that, as discussed in chapter <info:blob>: `URL.createObjectURL(file)`. +- `readAsArrayBuffer` -- para archivos binarios, en donde se hacen operaciones binarias de bajo nivel. Para operaciones de alto nivel, como slicing, `File` hereda de `Blob` por lo que podemos llamarlas directamente sin tener que leer. +- `readAsText` -- para archivos de texto, cuando necesitamos obtener una cadena. +- `readAsDataURL` -- cuando necesitamos usar estos datos como valores de `src` en `img` u otras etiquetas html. Hay otra alternativa para leer archivos de ese tipo como discutimos en el capítulo <info:blob>: `URL.createObjectURL(file)`. -As the reading proceeds, there are events: -- `loadstart` -- loading started. -- `progress` -- occurs during reading. -- `load` -- no errors, reading complete. -- `abort` -- `abort()` called. -- `error` -- error has occurred. -- `loadend` -- reading finished with either success or failure. +Mientras se va realizando la lectura, suceden varios eventos: +- `loadstart` -- la carga comenzó. +- `progress` -- ocurre mientras se lee. +- `load` -- lectura completada, sin errores. +- `abort` -- `abort()` ha sido llamado. +- `error` -- ha ocurrido un error . +- `loadend` -- la lectura finalizó exitosa o no . -When the reading is finished, we can access the result as: -- `reader.result` is the result (if successful) -- `reader.error` is the error (if failed). +Cuando la lectura finaliza, podemos acceder al resultado como: +- `reader.result` el resultado (si fue exitoso) +- `reader.error` el error (si hubo fallo). -The most widely used events are for sure `load` and `error`. +Los mas ampliamente usados son seguramente `load` y `error`. -Here's an example of reading a file: +Un ejemplo de como leer un archivo: ```html run <input type="file" onchange="readFile(this)"> @@ -104,35 +104,35 @@ function readFile(input) { </script> ``` -```smart header="`FileReader` for blobs" -As mentioned in the chapter <info:blob>, `FileReader` can read not just files, but any blobs. +```smart header="`FileReader` para blobs" +Como mencionamos en el capítulo <info:blob>, `FileReader` no solo lee archivos sino también cualquier blob. -We can use it to convert a blob to another format: -- `readAsArrayBuffer(blob)` -- to `ArrayBuffer`, -- `readAsText(blob, [encoding])` -- to string (an alternative to `TextDecoder`), -- `readAsDataURL(blob)` -- to base64 data url. +Podemos usarlo para convertir un blob a otro formato: +- `readAsArrayBuffer(blob)` -- a `ArrayBuffer`, +- `readAsText(blob, [encoding])` -- a una cadena (una alternativa al `TextDecoder`), +- `readAsDataURL(blob)` -- a Datos URI en base 64. ``` -```smart header="`FileReaderSync` is available inside Web Workers" -For Web Workers, there also exists a synchronous variant of `FileReader`, called [FileReaderSync](https://www.w3.org/TR/FileAPI/#FileReaderSync). +```smart header="`FileReaderSync` está disponible dentro de Web Workers" +Para los Web Workers también existe una variante síncrona de `FileReader` llamada [FileReaderSync](https://www.w3.org/TR/FileAPI/#FileReaderSync). -Its reading methods `read*` do not generate events, but rather return a result, as regular functions do. +Sus metodos `read*` no generan eventos sino que devuelven un resultado como las funciones regulares. -That's only inside a Web Worker though, because delays in synchronous calls, that are possible while reading from files, in Web Workers are less important. They do not affect the page. +Esto es solo dentro de un Web Worker, debido a que demoras en llamadas síncronas mientras se lee el archivo en Web Worker no son tan importantes. No afectan la página. ``` -## Summary +## Resumen -`File` objects inherit from `Blob`. +Los objetos `File` heredan de `Blob`. -In addition to `Blob` methods and properties, `File` objects also have `name` and `lastModified` properties, plus the internal ability to read from filesystem. We usually get `File` objects from user input, like `<input>` or Drag'n'Drop events (`ondragend`). +Además de los métodos y propiedades de `Blob`, los objetos `File` también tienen las propiedades `name` y `lastModified` mas la habilidad interna de leer del sistema de archivos. Usualmente obtenemos los objetos `File` mediante la entrada del el usuario con `<input>` o eventos Drag'n'Drop (`ondragend`). -`FileReader` objects can read from a file or a blob, in one of three formats: -- String (`readAsText`). +Los objetos `FileReader` pueden leer desde un archivo o un blob en uno de estos tres formatos: +- String (`readAsText`) . - `ArrayBuffer` (`readAsArrayBuffer`). -- Data url, base-64 encoded (`readAsDataURL`). +- Datos URI codificado en base 64 (`readAsDataURL`). -In many cases though, we don't have to read the file contents. Just as we did with blobs, we can create a short url with `URL.createObjectURL(file)` and assign it to `<a>` or `<img>`. This way the file can be downloaded or shown up as an image, as a part of canvas etc. +En muchos casos no necesitamos leer el contenido de un archivo como hicimos con los blobs, podemos crear un enlace corto con `URL.createObjectURL(file)` y asignárselo a un `<a>` o `<img>`. De esta manera el archivo puede ser descargado, mostrado como una imagen o como parte de un canvas, etc. -And if we're going to send a `File` over a network, that's also easy: network API like `XMLHttpRequest` or `fetch` natively accepts `File` objects. +Y si vamos a mandar un `File` por la red, es fácil utilizando APIs como `XMLHttpRequest` o `fetch` que aceptan nativamente objetos `File` . diff --git a/4-binary/index.md b/4-binary/index.md index 2b0c5dc82..917396805 100644 --- a/4-binary/index.md +++ b/4-binary/index.md @@ -1,3 +1,3 @@ -# Binary data, files +# Datos binarios y archivos -Working with binary data and files in JavaScript. +Trabajando con datos binarios y archivos en JavaScript. diff --git a/5-network/01-fetch/01-fetch-users/_js.view/source.js b/5-network/01-fetch/01-fetch-users/_js.view/source.js index 0c62e7bb5..a44549779 100644 --- a/5-network/01-fetch/01-fetch-users/_js.view/source.js +++ b/5-network/01-fetch/01-fetch-users/_js.view/source.js @@ -1,4 +1,4 @@ async function getUsers(names) { - /* your code */ + /* Tú código aquí */ } diff --git a/5-network/01-fetch/01-fetch-users/solution.md b/5-network/01-fetch/01-fetch-users/solution.md index 3cb88e4ea..58b93b168 100644 --- a/5-network/01-fetch/01-fetch-users/solution.md +++ b/5-network/01-fetch/01-fetch-users/solution.md @@ -1,11 +1,11 @@ -To fetch a user we need: `fetch('https://api.github.com/users/USERNAME')`. +Para obtener un usuario tenemos que ejecutar el siguiente código: `fetch('https://api.github.com/users/USERNAME')`. -If the response has status `200`, call `.json()` to read the JS object. +Si la respuesta contiene el status `200`, utilizamos el método `.json()` para leer el objeto JS. -Otherwise, if a `fetch` fails, or the response has non-200 status, we just return `null` in the resulting array. +Por el contrario, si el `fetch` falla o la respuesta no contiene un status 200, devolvemos `null` en el resultado del arreglo. -So here's the code: +Código: ```js demo async function getUsers(names) { @@ -33,8 +33,8 @@ async function getUsers(names) { } ``` -Please note: `.then` call is attached directly to `fetch`, so that when we have the response, it doesn't wait for other fetches, but starts to read `.json()` immediately. +Nota: la función `.then` está directamente vinculada al `fetch`. Por lo tanto, cuando se obtiene la respuesta se procede a ejecutar la función `.json()` inmediatamente en lugar de esperar a las otras peticiones. -If we used `await Promise.all(names.map(name => fetch(...)))`, and call `.json()` on the results, then it would wait for all fetches to respond. By adding `.json()` directly to each `fetch`, we ensure that individual fetches start reading data as JSON without waiting for each other. +Si en su lugar utilizáramos `await Promise.all(names.map(name => fetch(...)))` y llamamos a la función `.json()` sobre los resultados, entonces esperaríamos a que todas las peticiones fetch completen antes de obtener una respuesta. Al agregar `.json()` directamente en cada `fetch`, nos aseguramos de que las peticiones se procesen de manera independiente obteniendo una mejor respuesta en nuestra aplicación. -That's an example of how low-level Promise API can still be useful even if we mainly use `async/await`. +Esto es un ejemplo de cómo la API de Promesas puede ser útil aunque mayormente se utilice `async/await`. diff --git a/5-network/01-fetch/01-fetch-users/task.md b/5-network/01-fetch/01-fetch-users/task.md index 4605b4955..4a07b7c8a 100644 --- a/5-network/01-fetch/01-fetch-users/task.md +++ b/5-network/01-fetch/01-fetch-users/task.md @@ -1,13 +1,13 @@ -# Fetch users from GitHub +# Fetch de usuarios de GitHub -Create an async function `getUsers(names)`, that gets an array of GitHub logins, fetches the users from GitHub and returns an array of GitHub users. +Crear una función async llamada `getUsers(names)`, que tome como parámetro un arreglo de logins de GitHub, obtenga el listado de usuarios de GitHub indicado y devuelva un arreglo de usuarios de GitHub. -The GitHub url with user information for the given `USERNAME` is: `https://api.github.com/users/USERNAME`. +La url de GitHub con la información de usuario especifica `USERNAME` es: `https://api.github.com/users/USERNAME`. -There's a test example in the sandbox. +En el ambiente de prueba (sandbox) hay un ejemplo de referencia. -Important details: +Detalles a tener en cuenta: -1. There should be one `fetch` request per user. -2. Requests shouldn't wait for each other. So that the data arrives as soon as possible. -3. If any request fails, or if there's no such user, the function should return `null` in the resulting array. +1. Debe realizarse una única petición `fetch` por cada usuario. +2. Para que la información esté disponible lo antes posible las peticiones no deben ejecutarse de una por vez. +3. Si alguna de las peticiones fallara o si el usuario no existiese, la función debe devolver `null` en el resultado del arreglo. diff --git a/5-network/01-fetch/article.md b/5-network/01-fetch/article.md index 4669fc451..4129978bb 100644 --- a/5-network/01-fetch/article.md +++ b/5-network/01-fetch/article.md @@ -1,87 +1,87 @@ # Fetch -JavaScript can send network requests to the server and load new information whenever it's needed. +JavaScript puede enviar peticiones de red al servidor y cargar nueva información siempre que se necesite. -For example, we can use a network request to: +Por ejemplo, podemos utilizar una petición de red para: -- Submit an order, -- Load user information, -- Receive latest updates from the server, +- Crear una orden, +- Cargar información de usuario, +- Recibir las últimas actualizaciones desde un servidor, - ...etc. -...And all of that without reloading the page! +...Y todo esto sin la necesidad de refrescar la página. -There's an umbrella term "AJAX" (abbreviated <b>A</b>synchronous <b>J</b>avaScript <b>A</b>nd <b>X</b>ML) for network requests from JavaScript. We don't have to use XML though: the term comes from old times, that's why that word is there. You may have heard that term already. +Se utiliza el término global "AJAX" (abreviado <b>A</b>synchronous <b>J</b>avaScript <b>A</b>nd <b>X</b>ML, en español: "JavaScript y XML Asincrónico") para referirse a las peticiones de red originadas desde JavaScript. Sin embargo, no estamos necesariamente condicionados a utilizar XML dado que el término es antiguo y es por esto que el acrónimo XML se encuentra aquí. Probablemente lo hayáis visto anteriormente. -There are multiple ways to send a network request and get information from the server. +Existen múltiples maneras de enviar peticiones de red y obtener información de un servidor. -The `fetch()` method is modern and versatile, so we'll start with it. It's not supported by old browsers (can be polyfilled), but very well supported among the modern ones. +Comenzaremos con el el método `fetch()` que es moderno y versátil. Este método no es soportado por navegadores antiguos (sin embargo se puede incluir un polyfill), pero es perfectamente soportado por los navegadores actuales y modernos. -The basic syntax is: +La sintaxis básica es la siguiente: ```js let promise = fetch(url, [options]) ``` -- **`url`** -- the URL to access. -- **`options`** -- optional parameters: method, headers etc. +- **`url`** -- representa la dirección URL a la que deseamos acceder. +- **`options`** -- representa los parámetros opcionales, como puede ser un método o los encabezados de nuestra petición, etc. -Without `options`, this is a simple GET request, downloading the contents of the `url`. +Si no especificamos ningún `options`, se ejecutará una simple petición GET, la cual descargará el contenido de lo especificado en el `url`. -The browser starts the request right away and returns a promise that the calling code should use to get the result. +El navegador lanzará la petición de inmediato y devolverá una promesa (promise) que luego será utilizada por el código invocado para obtener el resultado. -Getting a response is usually a two-stage process. +Por lo general, obtener una respuesta es un proceso de dos pasos. -**First, the `promise`, returned by `fetch`, resolves with an object of the built-in [Response](https://fetch.spec.whatwg.org/#response-class) class as soon as the server responds with headers.** +**Primero, la promesa `promise`, devuelta por `fetch`, resuelve la respuesta con un objeto de la clase incorporada [Response](https://fetch.spec.whatwg.org/#response-class) tan pronto como el servidor responde con los encabezados de la petición.** -At this stage we can check HTTP status, to see whether it is successful or not, check headers, but don't have the body yet. +En este paso, podemos chequear el status HTTP para poder ver si nuestra petición ha sido exitosa o no, y chequear los encabezados, pero aún no disponemos del cuerpo de la misma. -The promise rejects if the `fetch` was unable to make HTTP-request, e.g. network problems, or there's no such site. Abnormal HTTP-statuses, such as 404 or 500 do not cause an error. +La promesa es rechazada si el `fetch` no ha podido establecer la petición HTTP, por ejemplo, por problemas de red o si el sitio especificado en la petición no existe. Estados HTTP anormales, como el 404 o 500 no generan errores. -We can see HTTP-status in response properties: +Podemos visualizar los estados HTTP en las propiedades de la respuesta: -- **`status`** -- HTTP status code, e.g. 200. -- **`ok`** -- boolean, `true` if the HTTP status code is 200-299. +- **`status`** -- código de estado HTTP, por ejemplo: 200. +- **`ok`** -- booleana, `true` si el código de estado HTTP es 200 a 299. -For example: +Ejemplo: ```js let response = await fetch(url); -if (response.ok) { // if HTTP-status is 200-299 - // get the response body (the method explained below) +if (response.ok) { // si el HTTP-status es 200-299 + // obtener cuerpo de la respuesta (método debajo) let json = await response.json(); } else { - alert("HTTP-Error: " + response.status); + alert("Error-HTTP: " + response.status); } ``` -**Second, to get the response body, we need to use an additional method call.** +**Segundo, para obtener el cuerpo de la respuesta, necesitamos utilizar un método adicional.** -`Response` provides multiple promise-based methods to access the body in various formats: +`Response` provee múltiples métodos basados en promesas para acceder al cuerpo de la respuesta en distintos formatos: -- **`response.text()`** -- read the response and return as text, -- **`response.json()`** -- parse the response as JSON, -- **`response.formData()`** -- return the response as `FormData` object (explained in the [next chapter](info:formdata)), -- **`response.blob()`** -- return the response as [Blob](info:blob) (binary data with type), -- **`response.arrayBuffer()`** -- return the response as [ArrayBuffer](info:arraybuffer-binary-arrays) (low-level representation of binary data), -- additionally, `response.body` is a [ReadableStream](https://streams.spec.whatwg.org/#rs-class) object, it allows you to read the body chunk-by-chunk, we'll see an example later. +- **`response.text()`** -- lee y devuelve la respuesta en formato texto, +- **`response.json()`** -- convierte la respuesta como un JSON, +- **`response.formData()`** -- devuelve la respuesta como un objeto `FormData` (explicado en [el siguiente capítulo](info:formdata)), +- **`response.blob()`** -- devuelve la respuesta como [Blob](info:blob) (datos binarios tipados), +- **`response.arrayBuffer()`** -- devuelve la respuesta como un objeto [ArrayBuffer](info:arraybuffer-binary-arrays) (representación binaria de datos de bajo nivel), +- Adicionalmente, `response.body` es un objeto [ReadableStream](https://streams.spec.whatwg.org/#rs-class), el cual nos permite acceder al cuerpo como si fuera un stream y leerlo por partes. Veremos un ejemplo de esto más adelante. -For instance, let's get a JSON-object with latest commits from GitHub: +Por ejemplo, si obtenemos un objeto de tipo JSON con los últimos commits de GitHub: ```js run async let url = 'https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits'; let response = await fetch(url); *!* -let commits = await response.json(); // read response body and parse as JSON +let commits = await response.json(); // leer respuesta del cuerpo y devolver como JSON */!* alert(commits[0].author.login); ``` -Or, the same without `await`, using pure promises syntax: +O también usando promesas, en lugar de `await`: ```js run fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits') @@ -89,17 +89,17 @@ fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commi .then(commits => alert(commits[0].author.login)); ``` -To get the response text, `await response.text()` instead of `.json()`: +Para obtener la respuesta como texto, `await response.text()` en lugar de `.json()`: ```js run async let response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits'); -let text = await response.text(); // read response body as text +let text = await response.text(); // leer cuerpo de la respuesta como texto alert(text.slice(0, 80) + '...'); ``` -As a show-case for reading in binary format, let's fetch and show a logo image of ["fetch" specification](https://fetch.spec.whatwg.org) (see chapter [Blob](info:blob) for details about operations on `Blob`): +Como demostración de una lectura en formato binario, hagamos un fetch y mostremos una imagen del logotipo de ["especificación fetch"](https://fetch.spec.whatwg.org) (ver capítulo [Blob](info:blob) para más detalles acerca de las operaciones con `Blob`): ```js async run let response = await fetch('/article/fetch/logo-fetch.svg'); @@ -108,52 +108,52 @@ let response = await fetch('/article/fetch/logo-fetch.svg'); let blob = await response.blob(); // download as Blob object */!* -// create <img> for it +// crear tag <img> para imagen let img = document.createElement('img'); img.style = 'position:fixed;top:10px;left:10px;width:100px'; document.body.append(img); -// show it +// mostrar img.src = URL.createObjectURL(blob); -setTimeout(() => { // hide after three seconds +setTimeout(() => { // ocultar luego de tres segundos img.remove(); URL.revokeObjectURL(img.src); }, 3000); ``` ````warn -We can choose only one body-reading method. +Podemos elegir un solo método de lectura para el cuerpo de la respuesta. -If we've already got the response with `response.text()`, then `response.json()` won't work, as the body content has already been processed. +Si ya obtuvimos la respuesta con `response.text()`, entonces `response.json()` no funcionará, dado que el contenido del cuerpo ya ha sido procesado. ```js -let text = await response.text(); // response body consumed -let parsed = await response.json(); // fails (already consumed) +let text = await response.text(); // cuerpo de respuesta obtenido y procesado +let parsed = await response.json(); // fallo (ya fue procesado) ``` ```` -## Response headers +## Encabezados de respuesta -The response headers are available in a Map-like headers object in `response.headers`. +Los encabezados de respuesta están disponibles como un objeto de tipo Map dentro del `response.headers`. -It's not exactly a Map, but it has similar methods to get individual headers by name or iterate over them: +No es exactamente un Map, pero posee métodos similares para obtener de manera individual encabezados por nombre o si quisiéramos recorrerlos como un objeto: ```js run async let response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits'); -// get one header +// obtenemos un encabezado alert(response.headers.get('Content-Type')); // application/json; charset=utf-8 -// iterate over all headers +// iteramos todos los encabezados for (let [key, value] of response.headers) { alert(`${key} = ${value}`); } ``` -## Request headers +## Encabezados de petición -To set a request header in `fetch`, we can use the `headers` option. It has an object with outgoing headers, like this: +Para especificar un encabezado en nuestro `fetch`, podemos utilizar la opción `headers`. La misma posee un objeto con los encabezados salientes, como se muestra en el siguiente ejemplo: ```js let response = fetch(protectedUrl, { @@ -163,7 +163,7 @@ let response = fetch(protectedUrl, { }); ``` -...But there's a list of [forbidden HTTP headers](https://fetch.spec.whatwg.org/#forbidden-header-name) that we can't set: +...Pero existe una [lista de encabezados](https://fetch.spec.whatwg.org/#forbidden-header-name) que no pueden ser especificados: - `Accept-Charset`, `Accept-Encoding` - `Access-Control-Request-Headers` @@ -186,27 +186,27 @@ let response = fetch(protectedUrl, { - `Proxy-*` - `Sec-*` -These headers ensure proper and safe HTTP, so they are controlled exclusively by the browser. +Estos encabezados nos aseguran que nuestras peticiones HTTP sean controladas exclusivamente por el navegador, de manera correcta y segura. -## POST requests +## Peticiones POST -To make a `POST` request, or a request with another method, we need to use `fetch` options: +Para ejecutar una petición `POST`, o cualquier otro método, utilizaremos las opciones de `fetch`: -- **`method`** -- HTTP-method, e.g. `POST`, -- **`body`** -- the request body, one of: - - a string (e.g. JSON-encoded), - - `FormData` object, to submit the data as `multipart/form-data`, - - `Blob`/`BufferSource` to send binary data, - - [URLSearchParams](info:url), to submit the data in `x-www-form-urlencoded` encoding, rarely used. +- **`method`** -- método HTTP, por ej: `POST`, +- **`body`** -- cuerpo de la respuesta, cualquiera de las siguientes: + - cadena de texto (ej. JSON-encoded), + - Objeto `FormData`, para enviar información como `multipart/form-data`, + - `Blob`/`BufferSource` para enviar información en formato binario, + - [URLSearchParams](info:url), para enviar información en cifrado `x-www-form-urlencoded` (no utilizado frecuentemente). -The JSON format is used most of the time. +El formato JSON es el más utilizado. -For example, this code submits `user` object as JSON: +Por ejemplo, el código debajo envía la información `user` como un objeto JSON: ```js run async let user = { - name: 'John', - surname: 'Smith' + nombre: 'Juan', + apellido: 'Perez' }; *!* @@ -223,21 +223,21 @@ let result = await response.json(); alert(result.message); ``` -Please note, if the request `body` is a string, then `Content-Type` header is set to `text/plain;charset=UTF-8` by default. +Tener en cuenta, si la respuesta del `body` es una cadena de texto, entonces el encabezado `Content-Type` será especificado como `text/plain;charset=UTF-8` por defecto. -But, as we're going to send JSON, we use `headers` option to send `application/json` instead, the correct `Content-Type` for JSON-encoded data. +Pero, cómo vamos a enviar un objeto JSON, en su lugar utilizaremos la opción `headers` especificada a `application/json`, que es la opción correcta `Content-Type` para información en formato JSON. -## Sending an image +## Enviando una imagen -We can also submit binary data with `fetch` using `Blob` or `BufferSource` objects. +También es posible enviar datos binarios con `fetch`, utilizando los objetos `Blob` o `BufferSource`. -In this example, there's a `<canvas>` where we can draw by moving a mouse over it. A click on the "submit" button sends the image to the server: +En el siguiente ejemplo, utilizaremos un `<canvas>` donde podremos dibujar utilizando nuestro ratón. Haciendo click en el botón "enviar" enviará la imagen al servidor: ```html run autorun height="90" <body style="margin:0"> <canvas id="canvasElem" width="100" height="80" style="border:1px solid"></canvas> - <input type="button" value="Submit" onclick="submit()"> + <input type="button" value="Enviar" onclick="submit()"> <script> canvasElem.onmousemove = function(e) { @@ -253,7 +253,7 @@ In this example, there's a `<canvas>` where we can draw by moving a mouse over i body: blob }); - // the server responds with confirmation and the image size + // el servidor responde con una confirmación y el tamaño de nuestra imagen let result = await response.json(); alert(result.message); } @@ -262,9 +262,9 @@ In this example, there's a `<canvas>` where we can draw by moving a mouse over i </body> ``` -Please note, here we don't set `Content-Type` header manually, because a `Blob` object has a built-in type (here `image/png`, as generated by `toBlob`). For `Blob` objects that type becomes the value of `Content-Type`. +Una aclaración, aquí no especificamos el `Content-Type` de manera manual, precisamente porque el objeto `Blob` posee un tipo incorporado (en este caso `image/png`, el cual es generado por la función `toBlob`). Para objetos `Blob` ese es el valor por defecto del encabezado `Content-Type`. -The `submit()` function can be rewritten without `async/await` like this: +Podemos reescribir la función `submit()` sin utilizar `async/await` de la siguiente manera: ```js function submit() { @@ -279,38 +279,38 @@ function submit() { } ``` -## Summary +## Resumen -A typical fetch request consists of two `await` calls: +Una petición fetch típica está formada por dos llamadas `await`: ```js -let response = await fetch(url, options); // resolves with response headers -let result = await response.json(); // read body as json +let response = await fetch(url, options); // resuelve con los encabezados de respuesta +let result = await response.json(); // accede al cuerpo de respuesta como json ``` -Or, without `await`: +También se puede acceder sin utilizar `await`: ```js fetch(url, options) .then(response => response.json()) - .then(result => /* process result */) + .then(result => /* procesa resultado */) ``` -Response properties: -- `response.status` -- HTTP code of the response, -- `response.ok` -- `true` if the status is 200-299. -- `response.headers` -- Map-like object with HTTP headers. +Propiedades de respuesta: +- `response.status` -- Código HTTP de la respuesta. +- `response.ok` -- Devuelve `true` si el código HTTP es 200-299. +- `response.headers` -- Objeto simil-Map que contiene los encabezados HTTP. -Methods to get response body: -- **`response.text()`** -- return the response as text, -- **`response.json()`** -- parse the response as JSON object, -- **`response.formData()`** -- return the response as `FormData` object (`multipart/form-data` encoding, see the next chapter), -- **`response.blob()`** -- return the response as [Blob](info:blob) (binary data with type), -- **`response.arrayBuffer()`** -- return the response as [ArrayBuffer](info:arraybuffer-binary-arrays) (low-level binary data), +Métodos para obtener el cuerpo de la respuesta: +- **`response.text()`** -- lee y devuelve la respuesta en formato texto, +- **`response.json()`** -- convierte la respuesta como un JSON, +- **`response.formData()`** -- devuelve la respuesta como un objeto `FormData` (codificación `multipart/form-data`, explicado en [el siguiente capítulo](info:formdata)), +- **`response.blob()`** -- devuelve la respuesta como [Blob](info:blob) (datos binarios tipados), +- **`response.arrayBuffer()`** -- devuelve la respuesta como un objeto [ArrayBuffer](info:arraybuffer-binary-arrays) (datos binarios de bajo nivel) -Fetch options so far: -- `method` -- HTTP-method, -- `headers` -- an object with request headers (not any header is allowed), -- `body` -- the data to send (request body) as `string`, `FormData`, `BufferSource`, `Blob` or `UrlSearchParams` object. +Opciones de fetch hasta el momento: +- `method` -- método HTTP, +- `headers` -- un objeto los encabezados de la petición (no todos los encabezados están permitidos), +- `body` -- los datos/información a enviar (cuerpo de la petición) como `string`, `FormData`, `BufferSource`, `Blob` u objeto `UrlSearchParams`. -In the next chapters we'll see more options and use cases of `fetch`. +En los próximos capítulos veremos más sobre opciones y casos de uso para `fetch`. diff --git a/5-network/02-formdata/article.md b/5-network/02-formdata/article.md index a73d554b1..7fccc7106 100644 --- a/5-network/02-formdata/article.md +++ b/5-network/02-formdata/article.md @@ -1,26 +1,26 @@ # FormData -This chapter is about sending HTML forms: with or without files, with additional fields and so on. +Este capítulo trata sobre el envío de formularios HTML: con o sin archivos, con campos adicionales y cosas similares. -[FormData](https://xhr.spec.whatwg.org/#interface-formdata) objects can help with that. As you might have guessed, it's the object to represent HTML form data. +Los objetos [FormData](https://xhr.spec.whatwg.org/#interface-formdata) pueden ser de ayuda en esta tarea. Tal como habrás supuesto, éste es el objeto encargado de representar los datos de los formularios HTML. -The constructor is: +El constructor es: ```js let formData = new FormData([form]); ``` -If HTML `form` element is provided, it automatically captures its fields. +Si se le brinda un elemento HTML `form`, el objeto automáticamente capturará sus campos. -The special thing about `FormData` is that network methods, such as `fetch`, can accept a `FormData` object as a body. It's encoded and sent out with `Content-Type: multipart/form-data`. +Lo que hace especial al objeto `FormData` es que los métodos de red, tales como `fetch`, pueden aceptar un objeto `FormData` como el cuerpo. Es codificado y enviado como `Content-Type: multipart/form-data`. -From the server point of view, that looks like a usual form submission. +Desde el punto de vista del servidor, se ve como una entrega normal. -## Sending a simple form +## Enviando un formulario simple -Let's send a simple form first. +Enviemos un formulario simple. -As you can see, that's almost one-liner: +Tal como se puede ver, es prácticamente una línea: ```html run autorun <form id="formElem"> @@ -47,48 +47,48 @@ As you can see, that's almost one-liner: </script> ``` -In this example, the server code is not presented, as it's beyond our scope. The server accepts the POST request and replies "User saved". +En este ejemplo, el código del servidor no es representado ya que está fuera de nuestro alcance. El servidor acepta la solicitud POST y responde "Usuario registrado". -## FormData Methods +## Métodos de FormData -We can modify fields in `FormData` with methods: +Contamos con métodos para poder modificar los campos del `FormData`: -- `formData.append(name, value)` - add a form field with the given `name` and `value`, -- `formData.append(name, blob, fileName)` - add a field as if it were `<input type="file">`, the third argument `fileName` sets file name (not form field name), as it were a name of the file in user's filesystem, -- `formData.delete(name)` - remove the field with the given `name`, -- `formData.get(name)` - get the value of the field with the given `name`, -- `formData.has(name)` - if there exists a field with the given `name`, returns `true`, otherwise `false` +- `formData.append(name, value)` - agrega un campo al formulario con el nombre `name` y el valor `value`, +- `formData.append(name, blob, fileName)` - agrega un campo tal como si se tratara de un `<input type="file">`, el tercer argumento `fileName` establece el nombre del archivo (no el nombre del campo), tal como si se tratara del nombre del archivo en el sistema de archivos del usuario, +- `formData.delete(name)` - elimina el campo de nombre `name`, +- `formData.get(name)` - obtiene el valor del campo con el nombre `name`, +- `formData.has(name)` - en caso de que exista el campo con el nombre `name`, devuelve `true`, de lo contrario `false` -A form is technically allowed to have many fields with the same `name`, so multiple calls to `append` add more same-named fields. +Un formulario técnicamente tiene permitido contar con muchos campos con el mismo atributo `name`, por lo que múltiples llamadas a `append` agregarán más campos con el mismo nombre. -There's also method `set`, with the same syntax as `append`. The difference is that `.set` removes all fields with the given `name`, and then appends a new field. So it makes sure there's only one field with such `name`, the rest is just like `append`: +Por otra parte existe un método `set`, con la misma sintaxis que `append`. La diferencia está en que `.set` remueve todos los campos con el `name` que se le ha pasado, y luego agrega el nuevo campo. De este modo nos aseguramos de que exista solamente un campo con determinado `name`, el resto es tal como en `append`: - `formData.set(name, value)`, - `formData.set(name, blob, fileName)`. -Also we can iterate over formData fields using `for..of` loop: +También es posible iterar por los campos del objeto formData utilizando un bucle `for..of`: ```js run let formData = new FormData(); formData.append('key1', 'value1'); formData.append('key2', 'value2'); -// List key/value pairs +// Se listan los pares clave/valor for(let [name, value] of formData) { - alert(`${name} = ${value}`); // key1 = value1, then key2 = value2 + alert(`${name} = ${value}`); // key1 = value1, luego key2 = value2 } ``` -## Sending a form with a file +## Enviando un formulario con un archivo -The form is always sent as `Content-Type: multipart/form-data`, this encoding allows to send files. So, `<input type="file">` fields are sent also, similar to a usual form submission. +El formulario siempre es enviado como `Content-Type: multipart/form-data`, esta codificación permite enviar archivos. Por lo tanto los campos `<input type="file">` también son enviados, tal como sucede en un envío normal. -Here's an example with such form: +Aquí un ejemplo con un formulario de este tipo: ```html run autorun <form id="formElem"> <input type="text" name="firstName" value="John"> - Picture: <input type="file" name="picture" accept="image/*"> + Imagen: <input type="file" name="picture" accept="image/*"> <input type="submit"> </form> @@ -110,15 +110,15 @@ Here's an example with such form: </script> ``` -## Sending a form with Blob data +## Enviando un formulario con datos Blob -As we've seen in the chapter <info:fetch>, it's easy to send dynamically generated binary data e.g. an image, as `Blob`. We can supply it directly as `fetch` parameter `body`. +Tal como pudimos ver en el capítulo <info:fetch>, es fácil enviar datos binarios generados dinámicamente (por ejemplo una imagen) como `Blob`. Podemos proporcionarlos directamente en un `fetch` con el parámetro `body`. -In practice though, it's often convenient to send an image not separately, but as a part of the form, with additional fields, such as "name" and other metadata. +De todos modos, en la práctica suele ser conveniente enviar la imagen como parte del formulario junto a otra metadata tal como el nombre y no de forma separada. -Also, servers are usually more suited to accept multipart-encoded forms, rather than raw binary data. +Además los servidores suelen ser más propensos a aceptar formularios multipart, en lugar de datos binarios sin procesar. -This example submits an image from `<canvas>`, along with some other fields, as a form, using `FormData`: +Este ejemplo envía una imagen desde un `<canvas>` junto con algunos campos más, como un formulario utilizando `FormData`: ```html run autorun height="90" <body style="margin:0"> @@ -154,36 +154,36 @@ This example submits an image from `<canvas>`, along with some other fields, as </body> ``` -Please note how the image `Blob` is added: +Nota como la imagen `Blob` es agregada: ```js formData.append("image", imageBlob, "image.png"); ``` -That's same as if there were `<input type="file" name="image">` in the form, and the visitor submitted a file named `"image.png"` (3rd argument) with the data `imageBlob` (2nd argument) from their filesystem. +Es lo mismo que si hubiera un campo `<input type="file" name="image">` en el formulario, y el usuario enviara un archivo con nombre `"image.png"` (3er argumento) con los datos `imageBlob` (2do argumento) desde su sistema de archivos. -The server reads form data and the file, as if it were a regular form submission. +El servidor lee el formulario `form-data` y el archivo tal como si de un formulario regular se tratara. -## Summary +## Resumen -[FormData](https://xhr.spec.whatwg.org/#interface-formdata) objects are used to capture HTML form and submit it using `fetch` or another network method. +Los objetos [FormData](https://xhr.spec.whatwg.org/#interface-formdata) son utilizados para capturar un formulario HTML y enviarlo utilizando `fetch` u otro método de red. -We can either create `new FormData(form)` from an HTML form, or create an object without a form at all, and then append fields with methods: +Podemos crear el objeto con `new FormData(form)` desde un formulario HTML, o crear un objeto sin un formulario en absoluto y agregar los campos con los siguientes métodos: -- `formData.append(name, value)` -- `formData.append(name, blob, fileName)` -- `formData.set(name, value)` -- `formData.set(name, blob, fileName)` +- `formData.append(nombre, valor)` +- `formData.append(nombre, blob, nombreDeArchivo)` +- `formData.set(nombre, valor)` +- `formData.set(nombre, blob, nombreDeArchivo)` -Let's note two peculiarities here: +Nótese aquí dos particularidades: -1. The `set` method removes fields with the same name, `append` doesn't. That's the only difference between them. -2. To send a file, 3-argument syntax is needed, the last argument is a file name, that normally is taken from user filesystem for `<input type="file">`. +1. El método `set` remueve campos con el mismo nombre, mientras que `append` no. Esta es la única diferencia entre estos dos métodos. +2. Para enviar un archivo, se requiere de tres argumentos, el último argumento es el nombre del archivo, el cual normalmente es tomado desde el sistema de archivos del usuario por el `<input type="file">`. -Other methods are: +Otros métodos son: -- `formData.delete(name)` -- `formData.get(name)` -- `formData.has(name)` +- `formData.delete(nombre)` +- `formData.get(nombre)` +- `formData.has(nombre)` -That's it! +¡Esto es todo! diff --git a/5-network/02-formdata/post.view/server.js b/5-network/02-formdata/post.view/server.js index a335b07b1..2ad370408 100644 --- a/5-network/02-formdata/post.view/server.js +++ b/5-network/02-formdata/post.view/server.js @@ -9,7 +9,7 @@ let router = new Router(); router.post('/user', async (ctx) => { ctx.body = { - message: "User saved" + message: "Usuario registrado" }; }); @@ -18,7 +18,7 @@ router.post('/image-form', async (ctx) => { let files = []; const { fields } = await busboy(ctx.req, { onFile(fieldname, file, filename, encoding, mimetype) { - // read all file stream to continue + // se lee todo el flujo del archivo para continuar let length = 0; file.on('data', function(data) { length += data.length; @@ -34,7 +34,7 @@ router.post('/image-form', async (ctx) => { }); ctx.body = { - message: `Image saved, firstName: ${fields.firstName}, Image size:${files[0].length}, fileName: ${files[0].filename}` + message: `Imagen guardada, nombre: ${fields.firstName}, tamaño del archivo:${files[0].length}, nombre del archivo: ${files[0].filename}` }; }); @@ -44,7 +44,7 @@ router.post('/user-avatar', async (ctx) => { let files = []; const { fields } = await busboy(ctx.req, { onFile(fieldname, file, filename, encoding, mimetype) { - // read all file stream to continue + // se lee todo el flujo del archivo para continuar let length = 0; file.on('data', function(data) { length += data.length; @@ -61,7 +61,7 @@ router.post('/user-avatar', async (ctx) => { }); ctx.body = { - message: `User with picture, firstName: ${fields.firstName}, picture size:${files[0].length}` + message: `Usuario con imagen, nombre: ${fields.firstName}, tamaño de la imagen:${files[0].length}` }; }); diff --git a/5-network/03-fetch-progress/article.md b/5-network/03-fetch-progress/article.md index 76b05d514..6362adb2d 100644 --- a/5-network/03-fetch-progress/article.md +++ b/5-network/03-fetch-progress/article.md @@ -1,60 +1,60 @@ -# Fetch: Download progress +# Fetch: Progreso de la descarga -The `fetch` method allows to track *download* progress. +El método `fetch` permite rastrear el progreso de *descarga*. -Please note: there's currently no way for `fetch` to track *upload* progress. For that purpose, please use [XMLHttpRequest](info:xmlhttprequest), we'll cover it later. +Ten en cuenta: actualmente no hay forma de que `fetch` rastree el progreso de *carga*. Para ese propósito, utiliza [XMLHttpRequest](info:xmlhttprequest), lo cubriremos más adelante. -To track download progress, we can use `response.body` property. It's a `ReadableStream` -- a special object that provides body chunk-by-chunk, as it comes. Readable streams are described in the [Streams API](https://streams.spec.whatwg.org/#rs-class) specification. +Para rastrear el progreso de la descarga, podemos usar la propiedad `response.body`. Esta propiedad es un `ReadableStream`, un objeto especial que proporciona la transmisión del cuerpo fragmento a fragmento tal como viene. Estas se describen en la especificación de la [API de transmisiones](https://streams.spec.whatwg.org/#rs-class). -Unlike `response.text()`, `response.json()` and other methods, `response.body` gives full control over the reading process, and we can count how much is consumed at any moment. +A diferencia de `response.text()`, `response.json()` y otros métodos, `response.body` da control total sobre el proceso de lectura, y podemos contar cuánto se consume en cualquier momento. -Here's the sketch of code that reads the response from `response.body`: +Aquí está el bosquejo del código que lee la respuesta de `response.body`: ```js -// instead of response.json() and other methods +// en lugar de response.json() y otros métodos const reader = response.body.getReader(); -// infinite loop while the body is downloading +// bucle infinito mientras el cuerpo se descarga while(true) { - // done is true for the last chunk - // value is Uint8Array of the chunk bytes + // done es true para el último fragmento + // value es Uint8Array de los bytes del fragmento const {done, value} = await reader.read(); if (done) { break; } - console.log(`Received ${value.length} bytes`) + console.log(`Recibí ${value.length} bytes`) } ``` -The result of `await reader.read()` call is an object with two properties: -- **`done`** -- `true` when the reading is complete, otherwise `false`. -- **`value`** -- a typed array of bytes: `Uint8Array`. +El resultado de la llamada `await reader.read()` es un objeto con dos propiedades: +- **`done`** -- `true` cuando la lectura está completa, de lo contrario `false`. +- **`value`** -- una matriz de tipo bytes: `Uint8Array`. ```smart -Streams API also describes asynchronous iteration over `ReadableStream` with `for await..of` loop, but it's not yet widely supported (see [browser issues](https://github.com/whatwg/streams/issues/778#issuecomment-461341033)), so we use `while` loop. +La API de transmisiones también describe la iteración asincrónica sobre `ReadableStream` con el bucle `for await..of`, pero aún no es ampliamente compatible (consulta [problemas del navegador](https://github.com/whatwg/streams/issues/778#issuecomment-461341033)), por lo que usamos el bucle `while`. ``` -We receive response chunks in the loop, until the loading finishes, that is: until `done` becomes `true`. +Recibimos fragmentos de respuesta en el bucle, hasta que finaliza la carga, es decir: hasta que `done` se convierte en `true`. -To log the progress, we just need for every received fragment `value` to add its length to the counter. +Para registrar el progreso, solo necesitamos que cada `value` de fragmento recibido agregue su longitud al contador. -Here's the full working example that gets the response and logs the progress in console, more explanations to follow: +Aquí está el ejemplo funcional completo que obtiene la respuesta y registra el progreso en la consola, seguido de su explicación: ```js run async -// Step 1: start the fetch and obtain a reader -let response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits?per_page=100'); +// Paso 1: iniciar la búsqueda y obtener un lector +let response = await fetch('https://api.github.com/repos/javascript-tutorial/es.javascript.info/commits?per_page=100'); const reader = response.body.getReader(); -// Step 2: get total length +// Paso 2: obtener la longitud total const contentLength = +response.headers.get('Content-Length'); -// Step 3: read the data -let receivedLength = 0; // received that many bytes at the moment -let chunks = []; // array of received binary chunks (comprises the body) +// Paso 3: leer los datos +let receivedLength = 0; // cantidad de bytes recibidos hasta el momento +let chunks = []; // matriz de fragmentos binarios recibidos (comprende el cuerpo) while(true) { const {done, value} = await reader.read(); @@ -65,10 +65,10 @@ while(true) { chunks.push(value); receivedLength += value.length; - console.log(`Received ${receivedLength} of ${contentLength}`) + console.log(`Recibí ${receivedLength} de ${contentLength}`) } -// Step 4: concatenate chunks into single Uint8Array +// Paso 4: concatenar fragmentos en un solo Uint8Array let chunksAll = new Uint8Array(receivedLength); // (4.1) let position = 0; for(let chunk of chunks) { @@ -76,39 +76,39 @@ for(let chunk of chunks) { position += chunk.length; } -// Step 5: decode into a string +// Paso 5: decodificar en un string let result = new TextDecoder("utf-8").decode(chunksAll); -// We're done! +// ¡Hemos terminado! let commits = JSON.parse(result); alert(commits[0].author.login); ``` -Let's explain that step-by-step: +Expliquemos esto paso a paso: -1. We perform `fetch` as usual, but instead of calling `response.json()`, we obtain a stream reader `response.body.getReader()`. +1. Realizamos `fetch` como de costumbre, pero en lugar de llamar a `response.json()`, obtenemos un lector de transmisión `response.body.getReader()`. - Please note, we can't use both these methods to read the same response: either use a reader or a response method to get the result. -2. Prior to reading, we can figure out the full response length from the `Content-Length` header. + Ten en cuenta que no podemos usar ambos métodos para leer la misma respuesta: usa un lector o un método de respuesta para obtener el resultado. +2. Antes de leer, podemos averiguar la longitud completa de la respuesta del encabezado `Content-Length`. - It may be absent for cross-origin requests (see chapter <info:fetch-crossorigin>) and, well, technically a server doesn't have to set it. But usually it's at place. -3. Call `await reader.read()` until it's done. + Puede estar ausente para solicitudes cross-origin (consulta el capítulo <info:fetch-crossorigin>) y, bueno, técnicamente un servidor no tiene que configurarlo. Pero generalmente está en su lugar. +3. Llama a `await reader.read()` hasta que esté listo. - We gather response chunks in the array `chunks`. That's important, because after the response is consumed, we won't be able to "re-read" it using `response.json()` or another way (you can try, there'll be an error). -4. At the end, we have `chunks` -- an array of `Uint8Array` byte chunks. We need to join them into a single result. Unfortunately, there's no single method that concatenates those, so there's some code to do that: - 1. We create `chunksAll = new Uint8Array(receivedLength)` -- a same-typed array with the combined length. - 2. Then use `.set(chunk, position)` method to copy each `chunk` one after another in it. -5. We have the result in `chunksAll`. It's a byte array though, not a string. + Recopilamos fragmentos de respuesta en la matriz `chunks`. Eso es importante, porque después de consumir la respuesta, no podremos "releerla" usando `response.json()` u otra forma (puedes intentarlo, habrá un error). +4. Al final, tenemos `chunks` - una matriz de fragmentos de bytes `Uint8Array`. Necesitamos unirlos en un solo resultado. Desafortunadamente, no hay un método simple que los concatene, por lo que hay un código para hacerlo: + 1. Creamos `chunksAll = new Uint8Array(selectedLength)` -- una matriz del mismo tipo con la longitud combinada. + 2. Luego usa el método `.set(chunk, position)` para copiar cada `chunk` uno tras otro en él. +5. Tenemos el resultado en `chunksAll`. Sin embargo, es una matriz de bytes, no un string. - To create a string, we need to interpret these bytes. The built-in [TextDecoder](info:text-decoder) does exactly that. Then we can `JSON.parse` it, if necessary. + Para crear un string, necesitamos interpretar estos bytes. El [TextDecoder](info:text-decoder) nativo hace exactamente eso. Luego podemos usar el resultado en `JSON.parse`, si es necesario. - What if we need binary content instead of a string? That's even simpler. Replace steps 4 and 5 with a single line that creates a `Blob` from all chunks: + ¿Qué pasa si necesitamos contenido binario en lugar de un string? Eso es aún más sencillo. Reemplaza los pasos 4 y 5 con una sola línea que crea un `Blob` de todos los fragmentos: ```js let blob = new Blob(chunks); ``` -At the end we have the result (as a string or a blob, whatever is convenient), and progress-tracking in the process. +Al final tenemos el resultado (como un string o un blob, lo que sea conveniente) y el seguimiento del progreso en el proceso. -Once again, please note, that's not for *upload* progress (no way now with `fetch`), only for *download* progress. +Una vez más, ten en cuenta que eso no es para el progreso de *carga* (hasta ahora eso no es posible con `fetch`), solo para el progreso de *descarga*. -Also, if the size is unknown, we should check `receivedLength` in the loop and break it once it reaches a certain limit. So that the `chunks` won't overflow the memory. +Además, si el tamaño es desconocido, deberíamos chequear `receivedLength` en el bucle y cortarlo en cuanto alcance cierto límite, así los `chunks` no agotarán la memoria. diff --git a/5-network/03-fetch-progress/progress.view/index.html b/5-network/03-fetch-progress/progress.view/index.html index ba7f76065..4ae6e28e5 100644 --- a/5-network/03-fetch-progress/progress.view/index.html +++ b/5-network/03-fetch-progress/progress.view/index.html @@ -12,13 +12,13 @@ const chunk = await reader.read(); if (chunk.done) { - console.log("done!"); + console.log("listo!"); break; } chunks.push(chunk.value); receivedLength += chunk.value.length; - console.log(`${receivedLength}/${contentLength} received`) + console.log(`recibí ${receivedLength}/${contentLength}`) } diff --git a/5-network/04-fetch-abort/article.md b/5-network/04-fetch-abort/article.md index eadc5aac2..1a88f1e19 100644 --- a/5-network/04-fetch-abort/article.md +++ b/5-network/04-fetch-abort/article.md @@ -1,60 +1,60 @@ # Fetch: Abort -As we know, `fetch` returns a promise. And JavaScript generally has no concept of "aborting" a promise. So how can we cancel an ongoing `fetch`? E.g. if the user actions on our site indicate that the `fetch` isn't needed any more. +Como sabemos `fetch` devuelve una promesa. Y generalmente JavaScript no tiene un concepto de "abortar" una promesa. Entonces, ¿cómo podemos abortar una llamada al método `fetch`? Por ejemplo si las acciones del usuario en nuestro sitio indican que `fetch` no se necesitará más. -There's a special built-in object for such purposes: `AbortController`. It can be used to abort not only `fetch`, but other asynchronous tasks as well. +Existe para esto de forma nativa un objeto especial: `AbortController`. Puede ser utilizado para abortar no solo `fetch` sino otras tareas asincrónicas también. -The usage is very straightforward: +Su uso es muy sencillo: -## The AbortController object +## El objeto AbortController -Create a controller: +Crear un controlador: ```js let controller = new AbortController(); ``` -A controller is an extremely simple object. +Este controlador es un objeto extremadamente simple. -- It has a single method `abort()`, -- And a single property `signal` that allows to set event listeners on it. +- Tiene un único método `abort()`, +- y una única propiedad `signal` que permite establecerle escuchadores de eventos. -When `abort()` is called: -- `controller.signal` emits the `"abort"` event. -- `controller.signal.aborted` property becomes `true`. +Cuando `abort()` es invocado: +- `controller.signal` emite el evento `"abort"`. +- La propiedad `controller.signal.aborted` toma el valor `true`. -Generally, we have two parties in the process: -1. The one that performs a cancelable operation, it sets a listener on `controller.signal`. -2. The one that cancels: it calls `controller.abort()` when needed. +Generalmente tenemos dos partes en el proceso: +1. El que ejecuta la operación de cancelación, genera un listener que escucha a `controller.signal`. +2. El que cancela: este llama a `controller.abort()` cuando es necesario. -Here's the full example (without `fetch` yet): +Tal como se muestra a continuación (por ahora sin `fetch`): ```js run let controller = new AbortController(); let signal = controller.signal; -// The party that performs a cancelable operation -// gets the "signal" object -// and sets the listener to trigger when controller.abort() is called +// La parte que ejecuta la operación de cancelación +// obtiene el objeto "signal" +// y genera un listener que se dispara cuando es llamado controller.abort() signal.addEventListener('abort', () => alert("abort!")); -// The other party, that cancels (at any point later): +// El que cancela (más tarde en cualquier punto): controller.abort(); // abort! -// The event triggers and signal.aborted becomes true +// El evento se dispara y signal.aborted se vuelve true alert(signal.aborted); // true ``` -As we can see, `AbortController` is just a mean to pass `abort` events when `abort()` is called on it. +Como podemos ver, `AbortController` es simplemente la via para pasar eventos `abort` cuando `abort()` es llamado sobre él. -We could implement the same kind of event listening in our code on our own, without the `AbortController` object. +Podríamos implementar alguna clase de escucha de evento en nuestro código por nuestra cuenta, sin el objeto `AbortController` en absoluto. -But what's valuable is that `fetch` knows how to work with the `AbortController` object. It's integrated in it. +Pero lo valioso es que `fetch` sabe cómo trabajar con el objeto `AbortController`, está integrado con él. -## Using with fetch +## Uso con fetch -To be able to cancel `fetch`, pass the `signal` property of an `AbortController` as a `fetch` option: +Para posibilitar la cancelación de `fetch`, pasa la propiedad `signal` de un `AbortController` como una opción de `fetch`: ```js let controller = new AbortController(); @@ -63,22 +63,22 @@ fetch(url, { }); ``` -The `fetch` method knows how to work with `AbortController`. It will listen to `abort` events on `signal`. +El método `fetch` conoce cómo trabajar con `AbortController`. Este escuchará eventos `abort` sobre `signal`. -Now, to abort, call `controller.abort()`: +Ahora, para abortar, llamamos `controller.abort()`: ```js controller.abort(); ``` -We're done: `fetch` gets the event from `signal` and aborts the request. +Terminamos: `fetch` obtiene el evento desde `signal` y aborta el requerimiento. -When a fetch is aborted, its promise rejects with an error `AbortError`, so we should handle it, e.g. in `try..catch`. +Cuando un fetch es abortado, su promesa es rechazada con un error `AbortError`, así podemos manejarlo, por ejemplo en `try..catch`. -Here's the full example with `fetch` aborted after 1 second: +Aquí hay un ejemplo completo con `fetch` abortado después de 1 segundo: ```js run async -// abort in 1 second +// Se abortara en un segundo let controller = new AbortController(); setTimeout(() => controller.abort(), 1000); @@ -87,7 +87,7 @@ try { signal: controller.signal }); } catch(err) { - if (err.name == 'AbortError') { // handle abort() + if (err.name == 'AbortError') { // se maneja el abort() alert("Aborted!"); } else { throw err; @@ -95,54 +95,54 @@ try { } ``` -## AbortController is scalable +## AbortController es escalable -`AbortController` is scalable. It allows to cancel multiple fetches at once. +`AbortController` es escalable, permite cancelar múltiples fetch de una vez. -Here's a sketch of code that fetches many `urls` in parallel, and uses a single controller to abort them all: +Aquí hay un bosquejo de código que de muchos fetch de `url` en paralelo, y usa un simple controlador para abortarlos a todos: ```js -let urls = [...]; // a list of urls to fetch in parallel +let urls = [...]; // una lista de urls para utilizar fetch en paralelo let controller = new AbortController(); -// an array of fetch promises +// un array de promesas fetch let fetchJobs = urls.map(url => fetch(url, { signal: controller.signal })); let results = await Promise.all(fetchJobs); -// if controller.abort() is called from anywhere, -// it aborts all fetches +// si controller.abort() es llamado, +// se abortaran todas las solicitudes fetch ``` -If we have our own asynchronous tasks, different from `fetch`, we can use a single `AbortController` to stop those, together with fetches. +En el caso de tener nuestras propias tareas asincrónicas aparte de `fetch`, podemos utilizar un único `AbortController` para detenerlas junto con fetch. -We just need to listen to its `abort` event in our tasks: +Solo es necesario escuchar el evento `abort` en nuestras tareas: ```js let urls = [...]; let controller = new AbortController(); -let ourJob = new Promise((resolve, reject) => { // our task +let ourJob = new Promise((resolve, reject) => { // nuestra tarea ... controller.signal.addEventListener('abort', reject); }); -let fetchJobs = urls.map(url => fetch(url, { // fetches +let fetchJobs = urls.map(url => fetch(url, { // varios fetch signal: controller.signal })); -// Wait for fetches and our task in parallel +// Se espera por la finalización de los fetch y nuestra tarea let results = await Promise.all([...fetchJobs, ourJob]); -// if controller.abort() is called from anywhere, -// it aborts all fetches and ourJob +// en caso de que se llame al método controller.abort() desde algún sitio, +// se abortan todos los fetch y nuestra tarea. ``` -## Summary +## Resumen -- `AbortController` is a simple object that generates an `abort` event on its `signal` property when the `abort()` method is called (and also sets `signal.aborted` to `true`). -- `fetch` integrates with it: we pass the `signal` property as the option, and then `fetch` listens to it, so it's possible to abort the `fetch`. -- We can use `AbortController` in our code. The "call `abort()`" -> "listen to `abort` event" interaction is simple and universal. We can use it even without `fetch`. +- `AbortController` es un simple objeto que genera un evento `abort` sobre su propiedad `signal` cuando el método `abort()` es llamado (y también establece `signal.aborted` en `true`). +- `fetch` está integrado con él: pasamos la propiedad `signal` como opción, y entonces `fetch` la escucha, así se vuelve posible abortar `fetch`. +- Podemos usar `AbortController` en nuestro código. La interacción "llamar `abort()`" -> "escuchar evento `abort`" es simple y universal. Podemos usarla incluso sin `fetch`. diff --git a/5-network/05-fetch-crossorigin/1-do-we-need-origin/solution.md b/5-network/05-fetch-crossorigin/1-do-we-need-origin/solution.md index d44494e18..b6d71b3c7 100644 --- a/5-network/05-fetch-crossorigin/1-do-we-need-origin/solution.md +++ b/5-network/05-fetch-crossorigin/1-do-we-need-origin/solution.md @@ -1,9 +1,9 @@ -We need `Origin`, because sometimes `Referer` is absent. For instance, when we `fetch` HTTP-page from HTTPS (access less secure from more secure), then there's no `Referer`. +Necesitamos la cabecera `Origin`, ya que en algunos casos `Referer` no está presente. Por ejemplo, cuando realizamos un `fetch` a una página HTTP desde una HTTPS (acceder a un sitio menos seguro desde uno más seguro), en ese caso no tendremos el campo `Referer`. -The [Content Security Policy](http://en.wikipedia.org/wiki/Content_Security_Policy) may forbid sending a `Referer`. +La [Política de seguridad de contenido](http://en.wikipedia.org/wiki/Content_Security_Policy) puede prohibir el envío de `Referer`. -As we'll see, `fetch` has options that prevent sending the `Referer` and even allow to change it (within the same site). +Como veremos, `fetch` tiene opciones con las que es posible evitar el envío de `Referer` e incluso permite su modificación (dentro del mismo sitio). -By specification, `Referer` is an optional HTTP-header. +Por especificación, `Referer` es una cabecera HTTP opcional. -Exactly because `Referer` is unreliable, `Origin` was invented. The browser guarantees correct `Origin` for cross-origin requests. +Por el hecho de que `Referer` no es confiable, la cabecera `Origin` ha sido creada. El navegador garantiza el envío correcto de `Origin` para las solicitudes de origen cruzado. diff --git a/5-network/05-fetch-crossorigin/1-do-we-need-origin/task.md b/5-network/05-fetch-crossorigin/1-do-we-need-origin/task.md index fb8142a3b..85d98558c 100644 --- a/5-network/05-fetch-crossorigin/1-do-we-need-origin/task.md +++ b/5-network/05-fetch-crossorigin/1-do-we-need-origin/task.md @@ -2,11 +2,11 @@ importance: 5 --- -# Why do we need Origin? +# ¿Por que necesitamos el origen (Origin)? -As you probably know, there's HTTP-header `Referer`, that usually contains an url of the page which initiated a network request. +Como seguramente ya sepas, existe la cabecera HTTP `Referer`, la cual por lo general contiene la url del sitio que generó la solicitud. -For instance, when fetching `http://google.com` from `http://javascript.info/some/url`, the headers look like this: +Por ejemplo, cuando solicitamos la url `http://google.com` desde `http://javascript.info/alguna/url`, las cabeceras se ven de este modo: ``` Accept: */* @@ -16,13 +16,13 @@ Connection: keep-alive Host: google.com *!* Origin: http://javascript.info -Referer: http://javascript.info/some/url +Referer: http://javascript.info/alguna/url */!* ``` -As you can see, both `Referer` and `Origin` are present. +Tal como se puede ver, tanto `Referer` como `Origin` están presentes. -The questions: +Las preguntas: -1. Why `Origin` is needed, if `Referer` has even more information? -2. Is it possible that there's no `Referer` or `Origin`, or is it incorrect? +1. ¿Por qué la cabecera `Origin` es necesaria, si `Referer` contiene incluso más información? +2. ¿Es posible que no se incluya `Referer` u `Origin`, o que contengan datos incorrectos? diff --git a/5-network/05-fetch-crossorigin/article.md b/5-network/05-fetch-crossorigin/article.md index 4420f43c7..33e037d0f 100644 --- a/5-network/05-fetch-crossorigin/article.md +++ b/5-network/05-fetch-crossorigin/article.md @@ -1,50 +1,50 @@ # Fetch: Cross-Origin Requests -If we send a `fetch` request to another web-site, it will probably fail. +Si enviamos una petición `fetch` hacia otro sitio seguramente fallará. -For instance, let's try fetching `http://example.com`: +Por ejemplo, probemos una petición a `https://example.com`: ```js run async try { - await fetch('http://example.com'); + await fetch('https://example.com'); } catch(err) { alert(err); // Failed to fetch } ``` -Fetch fails, as expected. +El método fetch falla, tal como lo esperábamos. -The core concept here is *origin* -- a domain/port/protocol triplet. +El concepto clave aquí es *el origen* (*origin*), triple combinación de dominio/puerto/protocolo. -Cross-origin requests -- those sent to another domain (even a subdomain) or protocol or port -- require special headers from the remote side. +Las solicitudes de origen cruzado `Cross-origin requests` (aquellas que son enviadas hacia otro dominio --incluso subdominio--, protocolo o puerto), requieren de unas cabeceras especiales desde el sitio remoto. -That policy is called "CORS": Cross-Origin Resource Sharing. +Esta política es denominada "CORS", por sus siglas en inglés Cross-Origin Resource Sharing. -## Why is CORS needed? A brief history +## ¿Por que CORS es necesario?, Una breve historia -CORS exists to protect the internet from evil hackers. +CORS existe para proteger Internet de los hackers malvados. -Seriously. Let's make a very brief historical digression. +En verdad... Déjame contarte un breve resumen de esta historia. -**For many years a script from one site could not access the content of another site.** +**Durante muchos años un script de un sitio no podía acceder al contenido de otro sitio.** -That simple, yet powerful rule was a foundation of the internet security. E.g. an evil script from website `hacker.com` could not access the user's mailbox at website `gmail.com`. People felt safe. +Esta simple, pero poderosa regla, fue parte fundacional de la seguridad de Internet. Por ejemplo, un script malicioso desde el sitio `hacker.com` no podía acceder a la casilla de correo en el sitio `gmail.com`. La gente se podía sentir segura. -JavaScript also did not have any special methods to perform network requests at that time. It was a toy language to decorate a web page. +Así mismo en ese momento, JavaScript no tenía ningún método especial para realizar solicitudes de red. Simplemente era un lenguaje juguete para decorar páginas web. -But web developers demanded more power. A variety of tricks were invented to work around the limitation and make requests to other websites. +Pero los desarrolladores web demandaron más poder. Una variedad de trucos fueron inventados para poder pasar por alto las limitaciones, y realizar solicitudes a otros sitios. -### Using forms +### Utilizando formularios -One way to communicate with another server was to submit a `<form>` there. People submitted it into `<iframe>`, just to stay on the current page, like this: +Una forma de comunicarse con otros servidores es y era utilizando un `<form>`. Se lo utilizaba para enviar el resultado hacia un `<iframe>`, y de este modo mantenerse en el mismo sitio: ```html -<!-- form target --> +<!-- objetivo del form --> *!* <iframe name="iframe"></iframe> */!* -<!-- a form could be dynamically generated and submitted by JavaScript --> +<!-- Un formulario puede ser generado de forma dinámica y ser enviado por JavaScript --> *!* <form target="iframe" method="POST" action="http://another.com/…"> */!* @@ -52,36 +52,36 @@ One way to communicate with another server was to submit a `<form>` there. Peopl </form> ``` -So, it was possible to make a GET/POST request to another site, even without networking methods, as forms can send data anywhere. But as it's forbidden to access the content of an `<iframe>` from another site, it wasn't possible to read the response. +Entonces, de este modo era posible realizar solicitudes GET/POST hacia otro sitio, incluso sin métodos de red, ya que los formularios pueden enviar mensajes a cualquier sitio. Pero ya que no es posible acceder al contenido de un `<iframe>` de otro sitio, esto evita que sea posible leer la respuesta. -To be precise, there were actually tricks for that, they required special scripts at both the iframe and the page. So the communication with the iframe was technically possible. Right now there's no point to go into details, let these dinosaurs rest in peace. +Para ser precisos, en realidad había trucos para eso, requerían scripts especiales tanto en el iframe como en la página. Entonces la comunicación con el iframe era técnicamente posible. Pero ya no hay necesidad de entrar en detalles, dejemos a los dinosaurios descansar en paz. -### Using scripts +### Utilizando scripts -Another trick was to use a `script` tag. A script could have any `src`, with any domain, like `<script src="http://another.com/…">`. It's possible to execute a script from any website. +Otro truco es en el modo de utilizar la etiqueta `script`. Un script puede tener cualquier origen `src`, con cualquier dominio, tal como `<script src="http://another.com/…">`. De este modo es posible ejecutar un script de cualquier sitio web. -If a website, e.g. `another.com` intended to expose data for this kind of access, then a so-called "JSONP (JSON with padding)" protocol was used. +Si un sitio, por ejemplo, `another.com` requiere exponer datos con este tipo de acceso, se utilizaba el protocolo llamado en ese entonces "JSONP (JSON con padding)" . -Here's how it worked. +Veamos como se utilizaba. -Let's say we, at our site, need to get the data from `http://another.com`, such as the weather: +Digamos que, en nuestro sitio es necesario obtener datos de `http://another.com`, como podría ser el pronóstico del tiempo: -1. First, in advance, we declare a global function to accept the data, e.g. `gotWeather`. +1. Primero, adelantándonos, creamos una función global para aceptar los datos, por ejemplo: `gotWeather`. ```js - // 1. Declare the function to process the weather data + // 1. Se declara la función para procesar los datos del tiempo function gotWeather({ temperature, humidity }) { alert(`temperature: ${temperature}, humidity: ${humidity}`); } ``` -2. Then we make a `<script>` tag with `src="http://another.com/weather.json?callback=gotWeather"`, using the name of our function as the `callback` URL-parameter. +2. Entonces creamos una etiqueta `<script>` donde `src="http://another.com/weather.json?callback=gotWeather"`, utilizando el nombre de nuestra función como un parámetro `callback`, dentro de la URL. ```js let script = document.createElement('script'); script.src = `http://another.com/weather.json?callback=gotWeather`; document.body.append(script); ``` -3. The remote server `another.com` dynamically generates a script that calls `gotWeather(...)` with the data it wants us to receive. +3. El servidor remoto `another.com` de forma dinámica genera un script que invoca el método `gotWeather(...)` con los datos que nosotros necesitamos recibir. ```js // The expected answer from the server looks like this: gotWeather({ @@ -89,51 +89,51 @@ Let's say we, at our site, need to get the data from `http://another.com`, such humidity: 78 }); ``` -4. When the remote script loads and executes, `gotWeather` runs, and, as it's our function, we have the data. +4. Entonces el script remoto carga y es ejecutado, la función `gotWeather` se invoca, y ya que es nuestra función, obtenemos los datos. -That works, and doesn't violate security, because both sides agreed to pass the data this way. And, when both sides agree, it's definitely not a hack. There are still services that provide such access, as it works even for very old browsers. +Esto funciona, y no viola la seguridad ya que ambos sitios acuerdan en intercambiar los datos de este modo. Y cuando ambos lados concuerdan, definitivamente no se trata de un hackeo. Aún hay servicios que proveen este tipo de acceso, lo que puede ser útil ya que funciona en navegadores obsoletos. -After a while, networking methods appeared in browser JavaScript. +Tiempo después aparecieron métodos de red en los navegadores para JavaScript. -At first, cross-origin requests were forbidden. But as a result of long discussions, cross-origin requests were allowed, but with any new capabilities requiring an explicit allowance by the server, expressed in special headers. +Al comienzo, las solicitudes de origen cruzado fueron prohibidas, pero luego de prolongadas discusiones se permitieron, requiriendo consentimiento explicito por parte del servidor, esto expresado en cabezales especiales. -## Safe requests +## Solicitudes seguras -There are two types of cross-origin requests: +Existen dos tipos de solicitudes de origen cruzado: -1. Safe requests. -2. All the others. +1. Solicitudes seguras. +2. Todas las demás. -Safe Requests are simpler to make, so let's start with them. +Las solicitudes seguras son más fáciles de hacer, comencemos con ellas. -A request is safe if it satisfies two conditions: +Una solicitud es segura si cumple dos condiciones: -1. [Safe method](https://fetch.spec.whatwg.org/#cors-safelisted-method): GET, POST or HEAD -2. [Safe headers](https://fetch.spec.whatwg.org/#cors-safelisted-request-header) -- the only allowed custom headers are: +1. [método seguro](https://fetch.spec.whatwg.org/#cors-safelisted-method): GET, POST o HEAD +2. [Cabeceras seguras](https://fetch.spec.whatwg.org/#cors-safelisted-request-header) -- Las únicas cabeceras permitidas son: - `Accept`, - `Accept-Language`, - `Content-Language`, - - `Content-Type` with the value `application/x-www-form-urlencoded`, `multipart/form-data` or `text/plain`. + - `Content-Type` con el valor `application/x-www-form-urlencoded`, `multipart/form-data` o `text/plain`. -Any other request is considered "unsafe". For instance, a request with `PUT` method or with an `API-Key` HTTP-header does not fit the limitations. +Cualquier otra solicitud es considerada "insegura". Por lo tanto, una solicitud con el método `PUT` o con una cabecera HTTP `API-Key` no cumple con las limitaciones. -**The essential difference is that a safe request can be made with a `<form>` or a `<script>`, without any special methods.** +**La diferencia esencial es que una solicitud segura puede ser realizada mediante un `<form>` o un `<script>`, sin la necesidad de utilizar un método especial.** -So, even a very old server should be ready to accept a safe request. +Por lo tanto, incluso un servidor obsoleto debería ser capaz de aceptar una solicitud segura. -Contrary to that, requests with non-standard headers or e.g. method `DELETE` can't be created this way. For a long time JavaScript was unable to do such requests. So an old server may assume that such requests come from a privileged source, "because a webpage is unable to send them". +Contrario a esto, las solicitudes con cabeceras no estándar o métodos como el `DELETE` no pueden ser creados de este modo. Durante mucho tiempo no fue posible para JavaScript realizar este tipo de solicitudes. Por lo que un viejo servidor podía asumir que ese tipo de solicitudes provenía desde una fuente privilegiada, "ya que una página web es incapaz de enviarlas". -When we try to make a unsafe request, the browser sends a special "preflight" request that asks the server -- does it agree to accept such cross-origin requests, or not? +Cuando intentamos realizar una solicitud insegura, el navegador envía una solicitud especial de "pre-vuelo" consultando al servidor: ¿está de acuerdo en aceptar tal solicitud de origen cruzado o no? -And, unless the server explicitly confirms that with headers, an unsafe request is not sent. +Y, salvo que el servidor lo confirme de forma explícita, cualquier solicitud insegura no es enviada. -Now we'll go into details. +Vayamos ahora a los detalles. -## CORS for safe requests +## CORS para solicitudes seguras -If a request is cross-origin, the browser always adds the `Origin` header to it. +Si una solicitud es de origen cruzado, el navegador siempre le agregará una cabecera `Origin`. -For instance, if we request `https://anywhere.com/request` from `https://javascript.info/page`, the headers will look like: +Por ejemplo, si realizamos una solicitud de `https://anywhere.com/request` a `https://javascript.info/page`, las cabeceras podrían ser algo así: ```http GET /request @@ -144,17 +144,17 @@ Origin: https://javascript.info ... ``` -As you can see, the `Origin` header contains exactly the origin (domain/protocol/port), without a path. +Tal como se puede ver, la cabecera `Origin` contiene exactamente el origen (protocolo/dominio/puerto), sin el path. -The server can inspect the `Origin` and, if it agrees to accept such a request, add a special header `Access-Control-Allow-Origin` to the response. That header should contain the allowed origin (in our case `https://javascript.info`), or a star `*`. Then the response is successful, otherwise it's an error. +El servidor puede inspeccionar el origen `Origin` y, si esta de acuerdo en aceptar ese tipo de solicitudes, agrega una cabecera especial `Access-Control-Allow-Origin` a la respuesta. Esta cabecera debe contener el origen permitido (en nuestro caso `https://javascript.info`), o un asterisco `*`. En ese caso la respuesta es satisfactoria, de otro modo falla. -The browser plays the role of a trusted mediator here: -1. It ensures that the correct `Origin` is sent with a cross-origin request. -2. It checks for permitting `Access-Control-Allow-Origin` in the response, if it exists, then JavaScript is allowed to access the response, otherwise it fails with an error. +El navegador cumple el papel de mediador de confianza: +1. Ante una solicitud de origen cruzado, se asegura de que se envíe el origen correcto. +2. Chequea que la respuesta contenga la cabecera `Access-Control-Allow-Origin`, de ser así JavaScript tiene permitido acceder a la respuesta, de no ser así la solicitud falla con un error. ![](xhr-another-domain.svg) -Here's an example of a permissive server response: +Aquí tenemos un ejemplo de una respuesta permisiva desde el servidor: ```http 200 OK Content-Type:text/html; charset=UTF-8 @@ -163,9 +163,9 @@ Access-Control-Allow-Origin: https://javascript.info */!* ``` -## Response headers +## Cabeceras de respuesta -For cross-origin request, by default JavaScript may only access so-called "safe" response headers: +Para las respuestas de origen cruzado, por defecto JavaScript sólo puede acceder a las cabeceras llamadas "seguras": - `Cache-Control` - `Content-Language` @@ -175,11 +175,11 @@ For cross-origin request, by default JavaScript may only access so-called "safe" - `Last-Modified` - `Pragma` -Accessing any other response header causes an error. +El acceso a otro tipo de cabeceras de la respuesta generará un error. -To grant JavaScript access to any other response header, the server must send the `Access-Control-Expose-Headers` header. It contains a comma-separated list of unsafe header names that should be made accessible. +Para permitir a JavaScript acceso a cualquier otra cabecera de respuesta, el servidor debe incluir la cabecera `Access-Control-Expose-Headers`. Este campo contiene una lista separada por comas de las cabeceras inseguras que podrán ser accesibles. -For example: +Por ejemplo: ```http 200 OK @@ -193,32 +193,32 @@ Access-Control-Expose-Headers: Content-Encoding,API-Key */!* ``` -With such an `Access-Control-Expose-Headers` header, the script is allowed to read the `Content-Encoding` and `API-Key` headers of the response. +Con tal cabecera, `Access-Control-Expose-Headers`, el script tendrá permitido acceder a los valores de las cabeceras `Content-Encoding` y `API-Key` de la respuesta. -## "Unsafe" requests +## Solicitudes "inseguras" -We can use any HTTP-method: not just `GET/POST`, but also `PATCH`, `DELETE` and others. +Podemos utilizar cualquier método HTTP: no únicamente `GET/POST`, sino también `PATCH`, `DELETE` y otros. -Some time ago no one could even imagine that a webpage could make such requests. So there may still exist webservices that treat a non-standard method as a signal: "That's not a browser". They can take it into account when checking access rights. +Hace algún tiempo nadie podía siquiera imaginar que un sitio web pudiera realizar ese tipo de solicitudes. Por lo que aún existen servicios web que cuando reciben un método no estándar los consideran como una señal de que: "Del otro lado no hay un navegador". Ellos pueden tener en cuenta esto cuando revisan los derechos de acceso. -So, to avoid misunderstandings, any "unsafe" request -- that couldn't be done in the old times, the browser does not make such requests right away. First, it sends a preliminary, so-called "preflight" request, to ask for permission. +Por lo tanto, para evitar malentendidos, cualquier solicitud "insegura" (Estas que no podían ser realizadas en los viejos tiempos), no será realizada por el navegador en forma directa. Antes, enviará una solicitud preliminar llamada solicitud de "pre-vuelo", solicitando que se le concedan los permisos. -A preflight request uses the method `OPTIONS`, no body and three headers: +Una solicitud de "pre-vuelo" utiliza el método `OPTIONS`, sin contenido en el cuerpo y con tres cabeceras: -- `Access-Control-Request-Method` header has the method of the unsafe request. -- `Access-Control-Request-Headers` header provides a comma-separated list of its unsafe HTTP-headers. -- `Origin` header tells from where the request came. (such as `https://javascript.info`) +- `Access-Control-Request-Method`, cabecera que contiene el método de la solicitud "insegura". +- `Access-Control-Request-Headers` provee una lista separada por comas de las cabeceras inseguras de la solicitud. +- `Origin` cabecera que informa de dónde viene la solicitud. (como `https://javascript.info`) -If the server agrees to serve the requests, then it should respond with empty body, status 200 and headers: +Si el servidor está de acuerdo con lo solicitado, entonces responderá con el código de estado 200 y un cuerpo vacío: -- `Access-Control-Allow-Origin` must be either `*` or the requesting origin, such as `https://javascript.info`, to allow it. -- `Access-Control-Allow-Methods` must have the allowed method. -- `Access-Control-Allow-Headers` must have a list of allowed headers. -- Additionally, the header `Access-Control-Max-Age` may specify a number of seconds to cache the permissions. So the browser won't have to send a preflight for subsequent requests that satisfy given permissions. +- `Access-Control-Allow-Origin` debe ser `*` o el origen de la solicitud, tal como `https://javascript.info`, para permitir el acceso. +- `Access-Control-Allow-Methods` contiene el método permitido. +- `Access-Control-Allow-Headers` contiene un listado de las cabeceras permitidas. +- Además, la cabecera `Access-Control-Max-Age` puede especificar el número máximo de segundos que puede recordar los permisos. Por lo que el navegador no necesita volver a requerirlos en las próximas solicitudes. ![](xhr-preflight.svg) -Let's see how it works step-by-step on the example of a cross-origin `PATCH` request (this method is often used to update data): +Vamos a ver cómo funciona paso a paso, mediante un ejemplo para una solicitud de origen cruzado `PATCH` (este método suele utilizarse para actualizar datos): ```js let response = await fetch('https://site.com/service.json', { @@ -230,14 +230,14 @@ let response = await fetch('https://site.com/service.json', { }); ``` -There are three reasons why the request is unsafe (one is enough): -- Method `PATCH` -- `Content-Type` is not one of: `application/x-www-form-urlencoded`, `multipart/form-data`, `text/plain`. -- "Unsafe" `API-Key` header. +Hay tres motivos por los cuales esta solicitud no es segura (una es suficiente): +- Método `PATCH` +- `Content-Type` no es del tipo: `application/x-www-form-urlencoded`, `multipart/form-data`, `text/plain`. +- Cabecera `API-Key` "insegura". -### Step 1 (preflight request) +### Paso 1 (solicitud de pre-vuelo) -Prior to sending such a request, the browser, on its own, sends a preflight request that looks like this: +Antes de enviar una solicitud de este tipo, el navegador envía una solicitud de pre-vuelo que se ve de este modo: ```http OPTIONS /service.json @@ -247,25 +247,25 @@ Access-Control-Request-Method: PATCH Access-Control-Request-Headers: Content-Type,API-Key ``` -- Method: `OPTIONS`. -- The path -- exactly the same as the main request: `/service.json`. -- Cross-origin special headers: - - `Origin` -- the source origin. - - `Access-Control-Request-Method` -- requested method. - - `Access-Control-Request-Headers` -- a comma-separated list of "unsafe" headers. +- Método: `OPTIONS`. +- El path -- exactamente el mismo que el de la solicitud principal: `/service.json`. +- Cabeceras especiales de origen cruzado (Cross-origin): + - `Origin` -- el origen de la fuente. + - `Access-Control-Request-Method` -- método solicitado. + - `Access-Control-Request-Headers` -- listado separado por comas de las cabeceras "inseguras". -### Step 2 (preflight response) +### Paso 2 (solicitud de pre-vuelo) -The server should respond with status 200 and the headers: +El servidor debe responder con el código de estado 200 y las cabeceras: - `Access-Control-Allow-Origin: https://javascript.info` - `Access-Control-Allow-Methods: PATCH` - `Access-Control-Allow-Headers: Content-Type,API-Key`. -That allows future communication, otherwise an error is triggered. +Esto permitirá la comunicación futura, de otro modo se disparará un error. -If the server expects other methods and headers in the future, it makes sense to allow them in advance by adding them to the list. +Si el servidor espera otro método y cabeceras en el futuro, tiene sentido permitirlos por adelantado agregándolos a la lista. -For example, this response also allows `PUT`, `DELETE` and additional headers: +Por ejemplo, esta respuesta habilita además los métodos `PUT`, `DELETE` y otras cabeceras: ```http 200 OK @@ -275,15 +275,15 @@ Access-Control-Allow-Headers: API-Key,Content-Type,If-Modified-Since,Cache-Contr Access-Control-Max-Age: 86400 ``` -Now the browser can see that `PATCH` is in `Access-Control-Allow-Methods` and `Content-Type,API-Key` are in the list `Access-Control-Allow-Headers`, so it sends out the main request. +Ahora el navegador puede ver que `PATCH` se encuentra dentro de la cabecera `Access-Control-Allow-Methods` y `Content-Type,API-Key` dentro de la lista `Access-Control-Allow-Headers`, por lo que permitirá enviar la solicitud principal. -If there's the header `Access-Control-Max-Age` with a number of seconds, then the preflight permissions are cached for the given time. The response above will be cached for 86400 seconds (one day). Within this timeframe, subsequent requests will not cause a preflight. Assuming that they fit the cached allowances, they will be sent directly. +Si se encuentra con una cabecera `Access-Control-Max-Age` con determinada cantidad de segundos, entonces los permisos son almacenados en el caché por ese determinado tiempo. La solicitud anterior será cacheada por 86400 segundos (un día). Durante ese marco de tiempo, las solicitudes siguientes no requerirán la solicitud de pre-vuelo. Asumiendo que están dentro de lo permitido en la respuesta cacheada, serán enviadas de forma directa. -### Step 3 (actual request) +### Paso 3 (solicitud real) -When the preflight is successful, the browser now makes the main request. The process here is the same as for safe requests. +Una vez el pre-vuelo se realiza de forma satisfactoria, el navegador realiza la solicitud principal. El algoritmo aquí es el mismo que el utilizado para una solicitud segura. -The main request has the `Origin` header (because it's cross-origin): +La solicitud principal tiene la cabecera `Origin` (ya que se trata de una solicitud de origen cruzado): ```http PATCH /service.json @@ -293,37 +293,37 @@ API-Key: secret Origin: https://javascript.info ``` -### Step 4 (actual response) +### Paso 4 (respuesta real) -The server should not forget to add `Access-Control-Allow-Origin` to the main response. A successful preflight does not relieve from that: +El server no debe olvidar agregar la cabecera `Access-Control-Allow-Origin` a la respuesta principal. Un pre-vuelo exitoso no lo libera de esto: ```http Access-Control-Allow-Origin: https://javascript.info ``` -Then JavaScript is able to read the main server response. +Entonces JavaScript es capaz de leer la respuesta principal del servidor. ```smart -Preflight request occurs "behind the scenes", it's invisible to JavaScript. +La solicitud de pre-vuelo ocurre "detrás de escena", es invisible a JavaScript. -JavaScript only gets the response to the main request or an error if there's no server permission. +JavaScript únicamente obtiene la respuesta a la solicitud principal o un error en caso de que el servidor no otorgue la autorización. ``` -## Credentials +## Credenciales -A cross-origin request initiated by JavaScript code by default does not bring any credentials (cookies or HTTP authentication). +Una solicitud de origen cruzado realizada por código JavaScript, por defecto no provee ningún tipo de credenciales (cookies o autenticación HTTP). -That's uncommon for HTTP-requests. Usually, a request to `http://site.com` is accompanied by all cookies from that domain. Cross-origin requests made by JavaScript methods on the other hand are an exception. +Esto es poco común para solicitudes HTTP. Usualmente una solicitud a un sitio `http://site.com` es acompañada por todas las cookies de ese dominio. Pero una solicitud de origen cruzado realizada por métodos de JavaScript son una excepción. -For example, `fetch('http://another.com')` does not send any cookies, even those (!) that belong to `another.com` domain. +Por ejemplo, `fetch('http://another.com')` no enviará ninguna cookie, ni siquiera (!) esas que pertenecen al dominio `another.com`. -Why? +¿Por qué? -That's because a request with credentials is much more powerful than without them. If allowed, it grants JavaScript the full power to act on behalf of the user and access sensitive information using their credentials. +El motivo de esto es que una solicitud con credenciales es mucho más poderosa que sin ellas. Si se permitiera, esto garantizaría a JavaScript el completo poder de actuar en representación del usuario y de acceder a información sensible utilizando sus credenciales. -Does the server really trust the script that much? Then it must explicitly allow requests with credentials with an additional header. +¿En verdad el servidor confía lo suficiente en el script? En ese caso el servidor debera enviar explicitamente que permite solicitudes con credenciales mediante otra cabecera especial. -To send credentials in `fetch`, we need to add the option `credentials: "include"`, like this: +Para permitir el envío de credenciales en `fetch`, necesitamos agregar la opción `credentials: "include"`, de este modo: ```js fetch('http://another.com', { @@ -331,11 +331,11 @@ fetch('http://another.com', { }); ``` -Now `fetch` sends cookies originating from `another.com` with request to that site. +Ahora `fetch` envía cookies originadas desde `another.com` con las solicitudes a ese sitio. -If the server agrees to accept the request *with credentials*, it should add a header `Access-Control-Allow-Credentials: true` to the response, in addition to `Access-Control-Allow-Origin`. +Si el servidor está de acuerdo en aceptar solicitudes *con credenciales*, debe agregar la cabecera `Access-Control-Allow-Credentials: true` a la respuesta, además de `Access-Control-Allow-Origin`. -For example: +Por ejemplo: ```http 200 OK @@ -343,42 +343,42 @@ Access-Control-Allow-Origin: https://javascript.info Access-Control-Allow-Credentials: true ``` -Please note: `Access-Control-Allow-Origin` is prohibited from using a star `*` for requests with credentials. Like shown above, it must provide the exact origin there. That's an additional safety measure, to ensure that the server really knows who it trusts to make such requests. +Cabe destacar que: `Access-Control-Allow-Origin` no se puede utilizar con un asterisco `*` para solicitudes con credenciales. Tal como se muestra a arriba debe proveer el origen exacto. Esto es una medida adicional de seguridad, para asegurar de que el servidor conozca exactamente en quién confiar para que le envíe este tipo de solicitudes. -## Summary +## Resumen -From the browser point of view, there are two kinds of cross-origin requests: "safe" and all the others. +Desde el punto de vista del navegador, existen dos tipos de solicitudes de origen cruzado: solicitudes "seguras" y todas las demás. -"Safe" requests must satisfy the following conditions: -- Method: GET, POST or HEAD. -- Headers -- we can set only: +[Solicitudes seguras](http://www.w3.org/TR/cors/#terminology) deben cumplir las siguientes condiciones: +- Método: GET, POST o HEAD. +- Cabeceras -- solo podemos establecer: - `Accept` - `Accept-Language` - `Content-Language` - - `Content-Type` to the value `application/x-www-form-urlencoded`, `multipart/form-data` or `text/plain`. + - `Content-Type` con el valor `application/x-www-form-urlencoded`, `multipart/form-data` o `text/plain`. -The essential difference is that safe requests were doable since ancient times using `<form>` or `<script>` tags, while unsafe were impossible for browsers for a long time. +La diferencia esencial es que las solicitudes seguras eran posibles desde los viejos tiempos utilizando las etiquetas `<form>` o `<script>`, mientras que las solicitudes "inseguras" fueron imposibles para el navegador durante mucho tiempo. -So, the practical difference is that safe requests are sent right away, with the `Origin` header, while for the other ones the browser makes a preliminary "preflight" request, asking for permission. +Por lo tanto, en la práctica, la diferencia se encuentra en que las solicitudes seguras son realizadas de forma directa, utilizando la cabecera `Origin`, mientras que para las otras el navegador realiza una solicitud extra de "pre-vuelo" para requerir la autorización. -**For safe requests:** +**Para una solicitud segura:** -- → The browser sends the `Origin` header with the origin. -- ← For requests without credentials (not sent by default), the server should set: - - `Access-Control-Allow-Origin` to `*` or same value as `Origin` -- ← For requests with credentials, the server should set: - - `Access-Control-Allow-Origin` to same value as `Origin` - - `Access-Control-Allow-Credentials` to `true` +- → El navegador envía una cabecera `Origin` con el origen. +- ← Para solicitudes sin credenciales (no enviadas por defecto), el servidor debe establecer: + - `Access-Control-Allow-Origin` como `*` o el mismo valor que en `Origin`. +- ← Para solicitudes con credenciales, el servidor deberá establecer: + - `Access-Control-Allow-Origin` con el mismo valor que en `Origin`. + - `Access-Control-Allow-Credentials` en `true` -Additionally, to grant JavaScript access to any response headers except `Cache-Control`, `Content-Language`, `Content-Type`, `Expires`, `Last-Modified` or `Pragma`, the server should list the allowed ones in `Access-Control-Expose-Headers` header. +Adicionalmente, para garantizar a JavaScript acceso a cualquier cabecera de la respuesta, con excepción de `Cache-Control`, `Content-Language`, `Content-Type`, `Expires`, `Last-Modified` o `Pragma`, el servidor debe agregarlas como permitidas en la lista de la cabecera `Access-Control-Expose-Headers`. -**For unsafe requests, a preliminary "preflight" request is issued before the requested one:** +**Para solicitudes inseguras, se utiliza una solicitud preliminar "pre-vuelo" antes de la solicitud principal:** -- → The browser sends an `OPTIONS` request to the same URL, with the headers: - - `Access-Control-Request-Method` has requested method. - - `Access-Control-Request-Headers` lists unsafe requested headers. -- ← The server should respond with status 200 and the headers: - - `Access-Control-Allow-Methods` with a list of allowed methods, - - `Access-Control-Allow-Headers` with a list of allowed headers, - - `Access-Control-Max-Age` with a number of seconds to cache the permissions. -- Then the actual request is sent, and the previous "safe" scheme is applied. +- → El navegador envía una solicitud del tipo `OPTIONS` a la misma URL, con las cabeceras: + - `Access-Control-Request-Method` con el método requerido. + - `Access-Control-Request-Headers` listado de las cabeceras inseguras. +- ← El servidor debe responder con el código de estado 200 y las cabeceras: + - `Access-Control-Allow-Methods` con la lista de todos los métodos permitidos, + - `Access-Control-Allow-Headers` con una lista de cabeceras permitidas, + - `Access-Control-Max-Age` con los segundos en los que se podrá almacenar la autorización en caché. +- Tras lo cual la solicitud es enviada, y se aplica el esquema previo "seguro". diff --git a/5-network/05-fetch-crossorigin/cors-gmail-messages.svg b/5-network/05-fetch-crossorigin/cors-gmail-messages.svg index c24aac140..013b80dbb 100644 --- a/5-network/05-fetch-crossorigin/cors-gmail-messages.svg +++ b/5-network/05-fetch-crossorigin/cors-gmail-messages.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="651" height="285" viewBox="0 0 651 285"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="network" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="cors-gmail-messages.svg"><path id="Rectangle-1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M11 48h298v199H11z"/><g id="<script>-let-respons" fill-rule="nonzero" transform="translate(15.937 58.259)"><path id="<script>" fill="#7E7C7B" d="M5.339 9.099l-.745.752L0 6.016 4.594 2.18l.745.758-3.726 3.063L5.34 9.099zm7.95-1.23a1.634 1.634 0 01-.458 1.158 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.637 4.637 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199V8.511c.4.114.8.2 1.196.26.397.059.79.088 1.183.088.57 0 .991-.077 1.264-.232.274-.155.41-.376.41-.663a.812.812 0 00-.064-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.859-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.451-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.588-1.265c.214-.195.503-.358.868-.488.364-.13.82-.195 1.367-.195.269 0 .567.015.896.044.328.03.67.081 1.025.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.872-.065c-.296 0-.545.023-.748.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.023.235.071.335.048.1.137.197.267.29.13.094.311.187.543.28.233.094.536.196.91.305.405.118.747.243 1.025.372.278.13.504.275.677.435.173.159.297.339.372.54.075.2.113.428.113.683zm7.595 1.62c-.31.118-.628.205-.954.262a5.845 5.845 0 01-1.008.086c-1.085 0-1.92-.294-2.505-.882-.586-.588-.879-1.447-.879-2.577 0-.542.084-1.035.253-1.477a3.142 3.142 0 011.805-1.863c.424-.17.89-.256 1.401-.256.356 0 .688.025.998.075.31.05.606.133.889.247v1.134a3.77 3.77 0 00-.906-.338 4.248 4.248 0 00-.954-.106c-.305 0-.593.058-.864.174-.271.116-.51.284-.715.503a2.38 2.38 0 00-.485.8c-.119.314-.178.67-.178 1.066 0 .83.202 1.45.605 1.863.404.412.963.618 1.678.618a3.897 3.897 0 001.818-.438v1.108zm2.645-6.611h1.087l.034 1.265c.406-.488.806-.841 1.2-1.06a2.436 2.436 0 011.193-.328c.71 0 1.25.23 1.617.69.367.46.536 1.144.509 2.051h-1.203c.013-.601-.074-1.038-.263-1.309-.19-.271-.466-.407-.83-.407-.16 0-.321.029-.483.086a1.947 1.947 0 00-.499.273 4.47 4.47 0 00-.543.482 8.898 8.898 0 00-.616.711v4.41H23.53V2.877zm9.755.984h-2.03v-.984h3.233V8.75h2.044v.991h-5.503V8.75h2.256V3.862zM33.701 0c.132 0 .255.024.37.072a.893.893 0 01.297.201.919.919 0 01.27.663.96.96 0 01-.27.663.893.893 0 01-.298.202.944.944 0 01-.369.072.944.944 0 01-.369-.072.893.893 0 01-.297-.201.96.96 0 01-.27-.663.919.919 0 01.27-.664.893.893 0 01.297-.201.944.944 0 01.37-.072zM44.51 6.187c0 .61-.086 1.143-.257 1.6-.17.455-.406.833-.707 1.134a2.9 2.9 0 01-1.066.677c-.41.15-.855.225-1.333.225-.22 0-.437-.011-.653-.034a4.952 4.952 0 01-.66-.116v2.87h-1.19V2.879h1.06l.075 1.148c.342-.469.707-.798 1.094-.987.387-.19.807-.284 1.258-.284.392 0 .736.082 1.032.246.296.164.545.395.745.694.2.298.351.658.451 1.08.1.421.15.892.15 1.412zm-1.217.054c0-.36-.026-.69-.079-.991a2.586 2.586 0 00-.25-.772 1.37 1.37 0 00-.437-.503 1.104 1.104 0 00-.635-.181c-.15 0-.303.024-.458.072a1.857 1.857 0 00-.482.239 3.59 3.59 0 00-.527.444 7 7 0 00-.591.687v3.33c.219.09.449.162.69.215.242.052.479.078.711.078.643 0 1.147-.217 1.511-.652.365-.436.547-1.09.547-1.966zm8.58 3.405a5.793 5.793 0 01-.835.146 8.52 8.52 0 01-.875.045c-.861 0-1.504-.195-1.928-.585-.423-.39-.635-.987-.635-1.794V3.876h-1.921v-.998h1.92V.99l1.19-.307v2.194h3.083v.998h-3.083v3.486c0 .492.131.86.393 1.104.262.244.648.366 1.159.366.219 0 .458-.017.718-.051.26-.034.53-.088.813-.16v1.025zm2.501-6.707l.738-.758 4.594 3.835-4.594 3.835-.738-.752 3.726-3.07-3.726-3.09z"/><path id="let" fill="#1C85B5" d="M2.495 33.06H.465v-.978h3.233v8.668h2.044v.991H.24v-.991h2.256v-7.69zm11.156 4.887c0 .169-.002.31-.006.424a6.37 6.37 0 01-.021.321h-4.82c0 .702.197 1.241.589 1.617.392.376.957.564 1.695.564.2 0 .4-.008.601-.024.201-.016.395-.037.582-.065.186-.027.365-.058.536-.092.171-.034.33-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.042-.694 2.857 2.857 0 01-.602-1.114 5.273 5.273 0 01-.194-1.494c0-.483.069-.94.208-1.37a3.44 3.44 0 01.608-1.135c.267-.326.594-.585.981-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.271.225.37.15.68.364.933.64.253.275.445.61.575 1.005.13.394.194.835.194 1.322zm-1.237-.17a2.593 2.593 0 00-.089-.838 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.571-.427 1.916 1.916 0 00-.793-.154c-.26 0-.497.05-.71.15-.215.1-.4.242-.555.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.177.84h3.61zm8.668 3.869a5.793 5.793 0 01-.834.146 8.52 8.52 0 01-.875.045c-.861 0-1.504-.195-1.928-.585-.424-.39-.635-.987-.635-1.794v-3.582h-1.921v-.998h1.92V32.99l1.19-.307v2.194h3.083v.998h-3.083v3.486c0 .492.131.86.393 1.104.262.244.648.366 1.159.366.219 0 .458-.017.718-.051.26-.034.53-.088.813-.16v1.025z"/><path id="response" fill="#181717" d="M31.227 34.878h1.086l.035 1.265c.405-.488.805-.841 1.2-1.06a2.436 2.436 0 011.192-.328c.711 0 1.25.23 1.617.69.367.46.537 1.144.51 2.051h-1.204c.014-.601-.074-1.038-.263-1.309-.19-.271-.466-.407-.83-.407-.16 0-.32.029-.483.086a1.947 1.947 0 00-.499.273 4.47 4.47 0 00-.543.482 8.898 8.898 0 00-.615.711v4.41h-1.203v-6.864zm13.213 3.07c0 .168-.002.31-.006.423a6.37 6.37 0 01-.02.321h-4.82c0 .702.196 1.241.588 1.617.392.376.957.564 1.695.564.2 0 .401-.008.602-.024.2-.016.394-.037.58-.065.187-.027.366-.058.537-.092.171-.034.33-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.042-.694 2.857 2.857 0 01-.602-1.114 5.273 5.273 0 01-.194-1.494c0-.483.07-.94.208-1.37a3.44 3.44 0 01.609-1.135c.266-.326.593-.585.98-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.271.225.37.15.68.364.933.64.253.275.445.61.575 1.005.13.394.194.835.194 1.322zm-1.237-.172a2.593 2.593 0 00-.089-.837 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.57-.427 1.916 1.916 0 00-.794-.154c-.26 0-.497.05-.71.15-.215.1-.4.242-.555.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.177.84h3.61zm8.572 2.092a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.637 4.637 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.401.114.8.2 1.197.26.396.059.79.088 1.182.088.57 0 .991-.077 1.265-.232.273-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.859-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.45-.56 1.649 1.649 0 01-.165-.759 1.744 1.744 0 01.588-1.265c.214-.195.504-.358.868-.488.365-.13.82-.195 1.367-.195.27 0 .568.015.896.044.328.03.67.081 1.025.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.871-.065c-.297 0-.546.023-.749.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.072.335.048.1.136.197.266.29.13.094.311.187.544.28.232.094.535.196.909.305.405.118.747.243 1.025.372.278.13.504.275.677.435.173.159.297.339.373.54.075.2.112.428.112.683zm8.128-1.681c0 .61-.085 1.143-.256 1.6-.17.455-.407.833-.708 1.134a2.9 2.9 0 01-1.066.677c-.41.15-.854.225-1.333.225-.219 0-.436-.011-.653-.034a4.952 4.952 0 01-.66-.116v2.87h-1.189v-9.665h1.06l.075 1.148c.342-.469.706-.798 1.094-.987.387-.19.806-.284 1.257-.284.392 0 .736.082 1.033.246.296.164.544.395.745.694.2.298.35.658.45 1.08.101.421.151.892.151 1.412zm-1.216.054c0-.36-.027-.69-.08-.991a2.586 2.586 0 00-.249-.772 1.37 1.37 0 00-.437-.503 1.104 1.104 0 00-.636-.181c-.15 0-.303.024-.458.072a1.857 1.857 0 00-.482.239 3.59 3.59 0 00-.526.444 7 7 0 00-.591.687v3.33c.218.09.448.162.69.215.242.052.478.078.71.078.643 0 1.147-.217 1.512-.652.364-.436.547-1.09.547-1.966zm9.023.014a4.55 4.55 0 01-.226 1.466c-.15.445-.366.825-.649 1.142a2.913 2.913 0 01-1.032.738 3.447 3.447 0 01-1.381.263c-.492 0-.933-.076-1.323-.229a2.617 2.617 0 01-.991-.673 2.98 2.98 0 01-.622-1.1c-.144-.438-.215-.942-.215-1.511 0-.534.075-1.02.225-1.46.15-.44.367-.818.65-1.135a2.913 2.913 0 011.032-.738 3.447 3.447 0 011.38-.263c.493 0 .934.076 1.323.229.39.153.72.376.992.67.27.294.478.66.622 1.097.143.437.215.939.215 1.504zm-1.217.055c0-.424-.047-.795-.14-1.111a2.237 2.237 0 00-.4-.793 1.65 1.65 0 00-.632-.479 2.077 2.077 0 00-.83-.16c-.356 0-.66.07-.913.208-.253.14-.46.325-.622.557a2.4 2.4 0 00-.356.81 4.06 4.06 0 00-.113.968c0 .423.047.795.14 1.114.094.319.227.584.4.796.174.212.383.372.63.479.245.107.523.16.833.16.356 0 .66-.07.913-.208.253-.14.46-.325.622-.557a2.4 2.4 0 00.355-.81 4.09 4.09 0 00.113-.974zm2.94-3.432h1.06l.047 1.107c.2-.237.394-.434.581-.591.187-.157.37-.284.55-.38a2.06 2.06 0 01.55-.201c.188-.039.381-.058.582-.058.706 0 1.24.208 1.603.625.362.417.543 1.045.543 1.884v4.477h-1.19V37.36c0-.537-.1-.935-.3-1.192-.2-.258-.499-.387-.896-.387-.145 0-.288.022-.427.065a1.557 1.557 0 00-.434.226c-.15.107-.313.252-.489.434-.175.182-.372.41-.59.683v4.553h-1.19v-6.863zm13.131 4.99a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.637 4.637 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.401.114.8.2 1.197.26.396.059.79.088 1.182.088.57 0 .991-.077 1.265-.232.273-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.858-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.452-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.588-1.265c.214-.195.504-.358.868-.488.365-.13.82-.195 1.367-.195.27 0 .568.015.896.044.328.03.67.081 1.025.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.871-.065c-.297 0-.546.023-.749.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.072.335.048.1.136.197.266.29.13.094.311.187.544.28.232.094.535.196.909.305.406.118.747.243 1.025.372.278.13.504.275.677.435.173.159.297.339.373.54.075.2.112.428.112.683zm8.06-1.92c0 .168-.002.31-.007.423a6.37 6.37 0 01-.02.321h-4.82c0 .702.196 1.241.588 1.617.392.376.957.564 1.696.564.2 0 .4-.008.601-.024.2-.016.394-.037.581-.065.187-.027.366-.058.537-.092.17-.034.329-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.042-.694 2.857 2.857 0 01-.602-1.114 5.273 5.273 0 01-.195-1.494c0-.483.07-.94.209-1.37a3.44 3.44 0 01.608-1.135c.267-.326.594-.585.981-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.27.225.37.15.681.364.934.64.253.275.444.61.574 1.005.13.394.195.835.195 1.322zm-1.237-.172a2.593 2.593 0 00-.09-.837 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.57-.427 1.916 1.916 0 00-.793-.154c-.26 0-.497.05-.711.15-.214.1-.399.242-.554.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.178.84h3.61z"/><path id="=" fill="#DBAF88" d="M105.916 37.325h-5.879V36.32h5.879v1.005zm0 2.38h-5.879v-1.006h5.879v1.005z"/><path id="await" fill="#1C85B5" d="M120.025 41.741l-.027-.923c-.374.37-.753.636-1.138.8-.385.164-.79.246-1.214.246-.391 0-.726-.05-1.004-.15a1.883 1.883 0 01-.687-.414 1.603 1.603 0 01-.397-.618 2.289 2.289 0 01-.126-.773c0-.688.256-1.227.769-1.617.512-.39 1.27-.584 2.273-.584h1.422v-.602c0-.405-.13-.73-.39-.974-.26-.243-.656-.365-1.19-.365-.387 0-.769.043-1.145.13-.376.086-.764.21-1.165.369v-1.074a7.36 7.36 0 011.087-.3 7.6 7.6 0 01.642-.1c.224-.025.45-.037.677-.037.415 0 .789.045 1.121.137.333.09.614.23.844.417.23.186.407.421.53.704.123.282.185.615.185.998v4.73h-1.067zm-.13-3.124h-1.51c-.296 0-.552.03-.766.09-.214.058-.39.143-.526.252-.137.11-.238.24-.304.393-.067.153-.1.325-.1.516 0 .132.021.259.062.38.041.12.107.227.198.32.091.094.21.168.356.223.146.055.323.082.533.082.273 0 .587-.083.94-.25.353-.166.726-.429 1.118-.789v-1.217zm9.776-3.74l-.998 6.864h-1.443l-.99-2.87-.199-.698-.226.738-.95 2.83h-1.401l-.991-6.863h1.162l.574 4.662.123 1.04.294-.91.998-3.083h.855l1.073 3.042.307.91.103-.965.533-4.696h1.176zm5.749 6.864l-.027-.923c-.374.37-.754.636-1.139.8-.385.164-.79.246-1.213.246-.392 0-.727-.05-1.005-.15a1.883 1.883 0 01-.687-.414 1.603 1.603 0 01-.396-.618 2.289 2.289 0 01-.127-.773c0-.688.257-1.227.77-1.617.512-.39 1.27-.584 2.272-.584h1.422v-.602c0-.405-.13-.73-.39-.974-.26-.243-.656-.365-1.19-.365-.386 0-.768.043-1.144.13-.376.086-.765.21-1.166.369v-1.074a7.36 7.36 0 011.087-.3 7.6 7.6 0 01.643-.1c.223-.025.449-.037.677-.037.414 0 .788.045 1.12.137.333.09.615.23.845.417.23.186.407.421.53.704.123.282.184.615.184.998v4.73h-1.066zm-.13-3.124h-1.51c-.297 0-.552.03-.766.09-.215.058-.39.143-.527.252-.136.11-.238.24-.304.393-.066.153-.099.325-.099.516 0 .132.02.259.062.38.04.12.107.227.198.32.09.094.21.168.355.223.146.055.324.082.533.082.274 0 .587-.083.94-.25.354-.166.726-.429 1.118-.789v-1.217zm5.756-2.755h-2.03v-.984h3.233v5.872h2.044v.991h-5.503v-.991h2.256v-4.888zm.417-3.862c.132 0 .255.024.369.072a.893.893 0 01.297.201.919.919 0 01.27.663.96.96 0 01-.27.663.893.893 0 01-.297.202.944.944 0 01-.37.072.944.944 0 01-.368-.072.893.893 0 01-.298-.201.96.96 0 01-.27-.663.919.919 0 01.27-.664.893.893 0 01.298-.201.944.944 0 01.369-.072zm10.473 9.646a5.793 5.793 0 01-.834.146 8.52 8.52 0 01-.875.045c-.862 0-1.504-.195-1.928-.585-.424-.39-.636-.987-.636-1.794v-3.582h-1.92v-.998h1.92V32.99l1.19-.307v2.194h3.083v.998h-3.083v3.486c0 .492.13.86.393 1.104.262.244.648.366 1.158.366.219 0 .458-.017.718-.051.26-.034.531-.088.814-.16v1.025z"/><path id="fetch" fill="#181717" d="M167.89 33.19c-.624-.133-1.161-.199-1.613-.199-1.07 0-1.606.56-1.606 1.682v1.203h3.008v.991h-3.008v4.874h-1.21v-4.874h-2.208v-.991h2.208v-1.135c0-1.827.952-2.741 2.857-2.741.474 0 .998.055 1.573.164v1.025zm7.404 4.757c0 .169-.002.31-.007.424a6.37 6.37 0 01-.02.321h-4.82c0 .702.196 1.241.588 1.617.392.376.957.564 1.695.564.201 0 .402-.008.602-.024.2-.016.394-.037.581-.065.187-.027.366-.058.537-.092.17-.034.329-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.042-.694 2.857 2.857 0 01-.602-1.114 5.273 5.273 0 01-.195-1.494c0-.483.07-.94.209-1.37a3.44 3.44 0 01.608-1.135c.267-.326.594-.585.981-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.27.225.37.15.681.364.934.64.253.275.444.61.574 1.005.13.394.195.835.195 1.322zm-1.237-.17a2.593 2.593 0 00-.09-.838 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.57-.427 1.916 1.916 0 00-.793-.154c-.26 0-.497.05-.711.15-.215.1-.4.242-.554.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.178.84h3.61zm8.668 3.869a5.793 5.793 0 01-.834.146 8.52 8.52 0 01-.875.045c-.862 0-1.504-.195-1.928-.585-.424-.39-.636-.987-.636-1.794v-3.582h-1.92v-.998h1.92V32.99l1.19-.307v2.194h3.083v.998h-3.083v3.486c0 .492.13.86.393 1.104.262.244.648.366 1.158.366.22 0 .458-.017.718-.051.26-.034.531-.088.814-.16v1.025zm7.499-.158c-.31.119-.628.206-.954.263a5.845 5.845 0 01-1.008.086c-1.085 0-1.92-.294-2.506-.882-.585-.588-.878-1.447-.878-2.577 0-.542.084-1.035.253-1.477a3.142 3.142 0 011.805-1.863c.423-.17.89-.256 1.4-.256.356 0 .69.025.999.075.31.05.606.133.889.247v1.134a3.77 3.77 0 00-.906-.338 4.248 4.248 0 00-.954-.106c-.305 0-.593.058-.864.174-.272.116-.51.284-.715.503a2.38 2.38 0 00-.485.8c-.119.314-.178.67-.178 1.066 0 .83.202 1.45.605 1.863.403.412.963.618 1.678.618a3.897 3.897 0 001.818-.438v1.108zm7.881.253h-1.189V37.36c0-.528-.1-.924-.297-1.186-.199-.262-.482-.393-.851-.393-.16 0-.31.022-.448.065a1.557 1.557 0 00-.434.226 3.73 3.73 0 00-.492.434c-.178.182-.383.41-.616.683v4.553h-1.19v-9.659h1.19v2.796l-.04 1.08c.186-.223.37-.411.55-.564.18-.153.36-.277.54-.373.18-.095.363-.164.55-.205.187-.04.38-.061.581-.061.684 0 1.212.208 1.586.625.374.417.56 1.045.56 1.884v4.477z"/><path id="(" fill="#7E7C7B" d="M204.395 44.612c-2.101-1.946-3.152-4.097-3.152-6.453 0-.551.056-1.102.168-1.65.111-.55.291-1.1.54-1.652a9.028 9.028 0 01.984-1.654c.408-.551.904-1.098 1.487-1.64l.69.704c-1.768 1.745-2.652 3.68-2.652 5.803 0 1.058.223 2.074.67 3.05.447.974 1.107 1.895 1.982 2.76l-.717.732z"/><path id="'https://gmail.com/messages'" fill="#478964" d="M19.093 46.082l-.198 3.206h-1.176l-.192-3.206h1.566zm9.673 9.66h-1.19v-4.383c0-.528-.099-.924-.297-1.186-.198-.262-.482-.393-.851-.393-.16 0-.309.022-.448.065a1.557 1.557 0 00-.434.226 3.73 3.73 0 00-.492.434c-.178.182-.383.41-.616.683v4.553H23.25v-9.659h1.19v2.796l-.042 1.08c.187-.223.37-.411.55-.564.18-.153.36-.277.54-.373.18-.095.364-.164.551-.205.187-.04.38-.061.581-.061.684 0 1.212.208 1.586.625.374.417.56 1.045.56 1.884v4.477zm7.71-.096a5.793 5.793 0 01-.833.146 8.52 8.52 0 01-.875.045c-.862 0-1.504-.195-1.928-.585-.424-.39-.636-.987-.636-1.794v-3.582h-1.92v-.998h1.92V46.99l1.19-.307v2.194h3.083v.998h-3.083v3.486c0 .492.13.86.393 1.104.262.244.648.366 1.158.366.22 0 .458-.017.718-.051.26-.034.531-.088.814-.16v1.025zm7.698 0a5.793 5.793 0 01-.834.146 8.52 8.52 0 01-.875.045c-.861 0-1.504-.195-1.928-.585-.424-.39-.636-.987-.636-1.794v-3.582h-1.92v-.998h1.92V46.99l1.19-.307v2.194h3.083v.998H41.09v3.486c0 .492.13.86.393 1.104.262.244.648.366 1.159.366.218 0 .458-.017.717-.051.26-.034.531-.088.814-.16v1.025zm8.032-3.46c0 .611-.085 1.144-.256 1.6-.171.456-.407.834-.708 1.135a2.9 2.9 0 01-1.066.677c-.41.15-.855.225-1.333.225-.219 0-.437-.011-.653-.034a4.952 4.952 0 01-.66-.116v2.87h-1.19v-9.665h1.06l.076 1.148c.341-.469.706-.798 1.093-.987.388-.19.807-.284 1.258-.284.392 0 .736.082 1.032.246.297.164.545.395.745.694.201.298.351.658.452 1.08.1.421.15.892.15 1.412zm-1.217.055c0-.36-.026-.69-.078-.991a2.586 2.586 0 00-.25-.772 1.37 1.37 0 00-.437-.503 1.104 1.104 0 00-.636-.181c-.15 0-.303.024-.458.072a1.857 1.857 0 00-.482.239 3.59 3.59 0 00-.526.444 7 7 0 00-.592.687v3.33c.219.09.45.162.69.215.242.052.48.078.712.078.642 0 1.146-.217 1.51-.652.365-.436.547-1.09.547-1.966zm8.484 1.627a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.493.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.637 4.637 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.402.114.8.2 1.197.26.396.059.79.088 1.182.088.57 0 .992-.077 1.265-.232.273-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.858-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.452-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.588-1.265c.214-.195.504-.358.868-.488.365-.13.82-.195 1.368-.195.268 0 .567.015.895.044.328.03.67.081 1.025.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.871-.065c-.296 0-.546.023-.749.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.072.335.048.1.137.197.267.29.13.094.31.187.543.28.232.094.535.196.91.305.405.118.747.243 1.025.372.278.13.503.275.676.435.173.159.298.339.373.54.075.2.113.428.113.683zm5.017-5.113c.142 0 .276.028.404.085a1.107 1.107 0 01.564.564.978.978 0 01.085.404.971.971 0 01-.085.406 1.12 1.12 0 01-.23.332 1.058 1.058 0 01-.738.308c-.146 0-.281-.028-.407-.083a1.07 1.07 0 01-.557-.557 1.006 1.006 0 01-.082-.406 1.058 1.058 0 01.308-.738 1.12 1.12 0 01.331-.23.971.971 0 01.407-.085zm0 5.01c.142 0 .276.03.404.086a1.107 1.107 0 01.564.564.978.978 0 01.085.403.971.971 0 01-.085.407 1.12 1.12 0 01-.23.332 1.058 1.058 0 01-.738.307c-.146 0-.281-.027-.407-.082a1.07 1.07 0 01-.557-.557 1.006 1.006 0 01-.082-.407 1.058 1.058 0 01.308-.738 1.12 1.12 0 01.331-.229.971.971 0 01.407-.085zm10.46-7.683l-4.69 11.129h-1.135l4.69-11.129h1.134zm7.696 0l-4.689 11.129h-1.135l4.69-11.129h1.134zm7.124 3.767c.127.16.226.345.297.557.07.212.106.44.106.687 0 .355-.065.68-.195.974-.13.294-.313.546-.55.755a2.54 2.54 0 01-.851.489c-.33.116-.694.174-1.09.174-.288 0-.557-.03-.807-.092a2.064 2.064 0 01-.595-.229c-.087.128-.16.248-.219.362a.817.817 0 00-.089.383c0 .173.084.317.25.43.166.115.386.176.66.185l1.804.069c.342.009.658.052.947.13.29.077.538.189.745.335.207.145.37.325.485.54.117.214.175.462.175.745 0 .305-.066.594-.198.868a1.985 1.985 0 01-.612.721c-.276.207-.626.373-1.05.496-.423.123-.927.184-1.51.184-.556 0-1.03-.044-1.419-.133-.39-.089-.71-.212-.96-.37a1.49 1.49 0 01-.547-.56 1.51 1.51 0 01-.171-.714c0-.333.077-.624.232-.875.155-.25.395-.492.718-.725a1.133 1.133 0 01-.52-.475 1.322 1.322 0 01-.164-.632c0-.296.07-.567.21-.814.138-.246.303-.478.495-.697a3.57 3.57 0 01-.23-.307 1.91 1.91 0 01-.28-.701 2.536 2.536 0 01-.037-.462c0-.355.065-.68.195-.974.13-.294.312-.545.547-.755.234-.21.517-.373.847-.489.33-.116.696-.174 1.097-.174.169 0 .33.011.486.034.155.023.291.052.41.089h2.488v.97h-1.1zm-4.13 6.883c0 .324.17.56.506.708.338.148.807.222 1.409.222.378 0 .696-.034.953-.102.258-.069.465-.159.622-.27.158-.112.27-.24.339-.383.068-.144.102-.29.102-.441 0-.278-.114-.483-.342-.615-.227-.133-.576-.21-1.045-.233l-1.791-.061c-.15.1-.275.198-.373.294a1.292 1.292 0 00-.229.29 1.127 1.127 0 00-.116.294 1.309 1.309 0 00-.034.297zm.363-5.612c0 .219.036.42.11.602.072.182.175.337.307.465.132.127.288.226.468.297.18.07.38.106.598.106.237 0 .448-.04.633-.12.184-.08.34-.188.464-.324.126-.137.222-.294.288-.472.066-.178.099-.362.099-.554 0-.219-.037-.42-.11-.601a1.316 1.316 0 00-.307-.465 1.404 1.404 0 00-.469-.298c-.18-.07-.379-.106-.598-.106-.237 0-.447.041-.632.123-.185.082-.34.19-.465.325s-.22.29-.287.468c-.066.178-.1.363-.1.554zm11.416 4.621v-4.928c0-.215-.008-.39-.024-.527a1.2 1.2 0 00-.075-.325.348.348 0 00-.13-.167.43.43 0 00-.448.034c-.077.055-.16.144-.25.267a4.216 4.216 0 00-.293.488c-.107.203-.234.453-.38.749v4.41h-1.087v-4.8c0-.25-.008-.453-.024-.608a1.42 1.42 0 00-.075-.362.34.34 0 00-.133-.178.431.431 0 00-.43.02.988.988 0 00-.243.247c-.09.118-.188.28-.298.485-.11.205-.239.467-.39.786v4.41h-1.093v-6.864h.91l.054 1.306c.118-.26.233-.481.345-.663.112-.183.227-.33.345-.441.119-.112.245-.194.38-.247.134-.052.283-.078.447-.078.37 0 .65.12.841.362.192.242.287.615.287 1.121.11-.237.217-.447.322-.632a2.31 2.31 0 01.338-.465c.12-.125.254-.22.4-.287.146-.066.314-.1.506-.1.861 0 1.292.664 1.292 1.99v4.997h-1.094zm7.212 0l-.027-.923c-.374.37-.754.636-1.139.8-.385.164-.79.246-1.213.246-.392 0-.727-.05-1.005-.15a1.883 1.883 0 01-.687-.414 1.603 1.603 0 01-.396-.618 2.289 2.289 0 01-.127-.773c0-.688.256-1.227.77-1.617.512-.39 1.27-.584 2.272-.584h1.422v-.602c0-.405-.13-.73-.39-.974-.26-.243-.656-.365-1.19-.365-.386 0-.768.043-1.144.13-.376.086-.765.21-1.166.369v-1.074a7.36 7.36 0 011.087-.3 7.6 7.6 0 01.643-.1c.223-.025.449-.037.677-.037.414 0 .788.045 1.12.137.333.09.615.23.845.417.23.186.407.421.53.704.123.282.184.615.184.998v4.73h-1.066zm-.13-3.124h-1.51c-.297 0-.552.03-.766.09-.215.058-.39.143-.527.252-.136.11-.238.24-.304.393-.066.153-.1.325-.1.516 0 .132.021.259.062.38.041.12.108.227.199.32.09.094.21.168.355.223.146.055.324.082.533.082.274 0 .587-.083.94-.25.353-.166.726-.429 1.118-.789v-1.217zm5.756-2.755h-2.03v-.984h3.233v5.872h2.044v.991H108v-.991h2.256v-4.888zm.417-3.862c.132 0 .255.024.369.072a.893.893 0 01.297.201.919.919 0 01.27.663.96.96 0 01-.27.663.893.893 0 01-.297.202.944.944 0 01-.37.072.944.944 0 01-.368-.072.893.893 0 01-.298-.201.96.96 0 01-.27-.663.919.919 0 01.27-.664.893.893 0 01.298-.201.944.944 0 01.369-.072zm7.28 1.06h-2.03v-.978h3.233v8.668h2.044v.991h-5.503v-.991h2.256v-7.69zm8.06 6.528a1.113 1.113 0 01.803.335c.102.105.183.227.243.366.059.139.088.288.088.447 0 .155-.03.301-.088.438a1.148 1.148 0 01-.605.601c-.14.06-.286.09-.441.09-.16 0-.308-.03-.445-.09a1.148 1.148 0 01-.601-.601 1.088 1.088 0 01-.09-.438 1.178 1.178 0 01.332-.813 1.12 1.12 0 01.804-.335zm10.329 1.9c-.31.119-.628.206-.954.263a5.845 5.845 0 01-1.008.086c-1.085 0-1.92-.294-2.506-.882-.585-.588-.878-1.447-.878-2.577 0-.542.084-1.035.253-1.477.169-.442.406-.82.71-1.134.306-.315.67-.558 1.095-.728.424-.171.89-.257 1.401-.257.356 0 .688.025.998.075.31.05.606.133.889.247v1.134a3.77 3.77 0 00-.906-.338 4.248 4.248 0 00-.954-.106c-.305 0-.593.058-.864.174-.272.116-.51.284-.715.503a2.38 2.38 0 00-.485.8c-.119.314-.178.67-.178 1.066 0 .83.202 1.45.605 1.863.404.412.963.618 1.678.618a3.897 3.897 0 001.818-.438v1.108zm8.34-3.233a4.55 4.55 0 01-.226 1.466c-.15.445-.367.825-.65 1.142a2.913 2.913 0 01-1.032.738 3.447 3.447 0 01-1.38.263c-.493 0-.934-.076-1.323-.229a2.617 2.617 0 01-.991-.673 2.98 2.98 0 01-.623-1.1c-.143-.438-.215-.942-.215-1.511 0-.534.075-1.02.226-1.46.15-.44.367-.818.65-1.135a2.913 2.913 0 011.031-.738 3.447 3.447 0 011.381-.263c.492 0 .933.076 1.323.229.39.153.72.376.991.67.271.294.479.66.622 1.097.144.437.216.939.216 1.504zm-1.217.055c0-.424-.047-.795-.14-1.111a2.237 2.237 0 00-.4-.793 1.65 1.65 0 00-.633-.479 2.077 2.077 0 00-.83-.16c-.356 0-.66.07-.913.208-.253.14-.46.325-.622.557a2.4 2.4 0 00-.355.81 4.06 4.06 0 00-.113.968c0 .423.047.795.14 1.114.094.319.227.584.4.796.173.212.383.372.629.479.246.107.524.16.834.16.355 0 .66-.07.912-.208.253-.14.46-.325.623-.557a2.4 2.4 0 00.355-.81 4.09 4.09 0 00.113-.974zm7.834 3.431v-4.928c0-.215-.008-.39-.024-.527a1.2 1.2 0 00-.075-.325.348.348 0 00-.13-.167.43.43 0 00-.448.034c-.077.055-.16.144-.25.267a4.216 4.216 0 00-.293.488 30.3 30.3 0 00-.38.749v4.41h-1.087v-4.8c0-.25-.008-.453-.024-.608a1.42 1.42 0 00-.075-.362.34.34 0 00-.133-.178.431.431 0 00-.43.02.988.988 0 00-.243.247c-.09.118-.188.28-.298.485-.11.205-.24.467-.39.786v4.41h-1.093v-6.864h.909l.055 1.306c.118-.26.233-.481.345-.663.111-.183.227-.33.345-.441.119-.112.245-.194.38-.247.134-.052.283-.078.447-.078.37 0 .65.12.841.362.191.242.287.615.287 1.121.11-.237.217-.447.321-.632a2.31 2.31 0 01.339-.465c.12-.125.254-.22.4-.287.146-.066.314-.1.506-.1.86 0 1.292.664 1.292 1.99v4.997H151.3zm8.32-9.659l-4.69 11.129h-1.135l4.69-11.129h1.134zm7.074 9.66v-4.93c0-.214-.008-.39-.024-.526a1.2 1.2 0 00-.075-.325.348.348 0 00-.13-.167.43.43 0 00-.448.034c-.077.055-.16.144-.249.267a4.216 4.216 0 00-.294.488c-.107.203-.233.453-.38.749v4.41h-1.086v-4.8c0-.25-.008-.453-.024-.608a1.42 1.42 0 00-.075-.362.34.34 0 00-.134-.178.431.431 0 00-.43.02.988.988 0 00-.243.247c-.089.118-.188.28-.297.485-.11.205-.24.467-.39.786v4.41h-1.094v-6.864h.91l.054 1.306c.119-.26.234-.481.345-.663.112-.183.227-.33.346-.441.118-.112.245-.194.379-.247.134-.052.284-.078.448-.078.369 0 .65.12.84.362.192.242.288.615.288 1.121.109-.237.216-.447.321-.632a2.31 2.31 0 01.338-.465c.121-.125.254-.22.4-.287.146-.066.315-.1.506-.1.861 0 1.292.664 1.292 1.99v4.997h-1.094zm8.6-3.795c0 .169-.002.31-.007.424a6.37 6.37 0 01-.02.321h-4.82c0 .702.196 1.241.588 1.617.392.376.957.564 1.695.564.201 0 .402-.008.602-.024.2-.016.394-.037.581-.065.187-.027.366-.058.537-.092.17-.034.329-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.042-.694 2.857 2.857 0 01-.602-1.114 5.273 5.273 0 01-.195-1.494c0-.483.07-.94.209-1.37a3.44 3.44 0 01.608-1.135c.267-.326.594-.585.981-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.27.225.37.15.681.364.934.64.253.275.444.61.574 1.005.13.394.195.835.195 1.322zm-1.237-.17a2.593 2.593 0 00-.09-.838 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.57-.427 1.916 1.916 0 00-.793-.154c-.26 0-.497.05-.711.15-.215.1-.4.242-.554.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.178.84h3.61zm8.572 2.091a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.637 4.637 0 01-.657.048 12.3 12.3 0 01-1.281-.061 8.646 8.646 0 01-1.145-.199v-1.093c.4.114.8.2 1.196.26.397.059.79.088 1.183.088.57 0 .99-.077 1.264-.232.274-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.235-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.981-.328 6.963 6.963 0 01-.858-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.451-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.587-1.265c.215-.195.504-.358.869-.488.364-.13.82-.195 1.367-.195.269 0 .567.015.895.044.328.03.67.081 1.026.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.872-.065c-.296 0-.546.023-.748.068a1.553 1.553 0 00-.493.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.072.335.048.1.137.197.267.29.13.094.31.187.543.28.233.094.536.196.91.305.405.118.747.243 1.025.372.278.13.503.275.677.435.173.159.297.339.372.54.075.2.113.428.113.683zm7.697 0a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.637 4.637 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.401.114.8.2 1.196.26.397.059.791.088 1.183.088.57 0 .991-.077 1.265-.232.273-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.859-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.451-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.588-1.265c.214-.195.503-.358.868-.488.364-.13.82-.195 1.367-.195.269 0 .567.015.896.044.328.03.67.081 1.025.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.872-.065c-.296 0-.545.023-.748.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.071.335.048.1.137.197.267.29.13.094.311.187.543.28.233.094.536.196.91.305.405.118.747.243 1.025.372.278.13.504.275.677.435.173.159.297.339.372.54.076.2.113.428.113.683zm6.672 1.873l-.027-.923c-.374.37-.753.636-1.138.8-.386.164-.79.246-1.214.246-.392 0-.727-.05-1.005-.15a1.883 1.883 0 01-.687-.414 1.603 1.603 0 01-.396-.618 2.289 2.289 0 01-.127-.773c0-.688.257-1.227.77-1.617.512-.39 1.27-.584 2.272-.584h1.422v-.602c0-.405-.13-.73-.39-.974-.26-.243-.656-.365-1.189-.365-.387 0-.769.043-1.145.13-.376.086-.764.21-1.165.369v-1.074a7.36 7.36 0 011.086-.3 7.6 7.6 0 01.643-.1c.223-.025.449-.037.677-.037.414 0 .788.045 1.12.137.334.09.615.23.845.417.23.186.407.421.53.704.123.282.184.615.184.998v4.73h-1.066zm-.13-3.124h-1.51c-.297 0-.552.03-.766.09-.214.058-.39.143-.527.252-.136.11-.238.24-.304.393-.066.153-.099.325-.099.516 0 .132.02.259.062.38.04.12.107.227.198.32.091.094.21.168.355.223.146.055.324.082.534.082.273 0 .586-.083.94-.25.353-.166.725-.429 1.117-.789v-1.217zm8.36-2.768c.128.16.227.345.298.557.07.212.106.44.106.687 0 .355-.065.68-.195.974-.13.294-.313.546-.55.755a2.54 2.54 0 01-.851.489c-.33.116-.694.174-1.09.174-.288 0-.557-.03-.807-.092a2.064 2.064 0 01-.595-.229c-.087.128-.16.248-.219.362a.817.817 0 00-.089.383c0 .173.083.317.25.43.166.115.386.176.66.185l1.804.069c.342.009.658.052.947.13.29.077.538.189.745.335.207.145.37.325.485.54.117.214.175.462.175.745 0 .305-.066.594-.198.868a1.985 1.985 0 01-.612.721c-.276.207-.626.373-1.05.496-.423.123-.927.184-1.51.184-.556 0-1.03-.044-1.419-.133-.39-.089-.71-.212-.96-.37a1.49 1.49 0 01-.547-.56 1.51 1.51 0 01-.171-.714c0-.333.077-.624.232-.875.155-.25.395-.492.718-.725a1.133 1.133 0 01-.52-.475 1.322 1.322 0 01-.164-.632c0-.296.07-.567.21-.814.138-.246.303-.478.495-.697a3.57 3.57 0 01-.23-.307 1.91 1.91 0 01-.28-.701 2.536 2.536 0 01-.037-.462c0-.355.065-.68.195-.974.13-.294.312-.545.546-.755.235-.21.518-.373.848-.489.33-.116.696-.174 1.097-.174.169 0 .33.011.486.034.155.023.291.052.41.089h2.488v.97h-1.1zm-4.128 6.883c0 .324.168.56.505.708.338.148.807.222 1.409.222.378 0 .696-.034.953-.102.258-.069.465-.159.622-.27.158-.112.27-.24.339-.383.068-.144.102-.29.102-.441 0-.278-.114-.483-.342-.615-.227-.133-.576-.21-1.045-.233l-1.791-.061c-.15.1-.275.198-.373.294a1.292 1.292 0 00-.229.29 1.127 1.127 0 00-.116.294 1.309 1.309 0 00-.034.297zm.362-5.612c0 .219.036.42.11.602.072.182.175.337.307.465.132.127.288.226.468.297.18.07.38.106.598.106.237 0 .448-.04.633-.12.184-.08.34-.188.464-.324.126-.137.222-.294.288-.472.066-.178.099-.362.099-.554 0-.219-.037-.42-.11-.601a1.316 1.316 0 00-.307-.465 1.404 1.404 0 00-.469-.298c-.18-.07-.379-.106-.598-.106-.237 0-.447.041-.632.123-.185.082-.34.19-.465.325s-.22.29-.287.468c-.066.178-.1.363-.1.554zm12.318.827c0 .169-.002.31-.007.424a6.372 6.372 0 01-.02.321h-4.82c0 .702.197 1.241.588 1.617.392.376.958.564 1.696.564.2 0 .4-.008.601-.024.2-.016.395-.037.581-.065.187-.027.366-.058.537-.092.17-.034.33-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.042-.694 2.857 2.857 0 01-.602-1.114 5.273 5.273 0 01-.195-1.494c0-.483.07-.94.209-1.37a3.44 3.44 0 01.608-1.135c.267-.326.594-.585.981-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.271.225.37.15.68.364.933.64.253.275.445.61.574 1.005.13.394.195.835.195 1.322zm-1.237-.17a2.593 2.593 0 00-.089-.838 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.571-.427 1.916 1.916 0 00-.793-.154c-.26 0-.497.05-.711.15-.214.1-.399.242-.554.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.177.84h3.609zm8.572 2.091a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.637 4.637 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.401.114.8.2 1.196.26.397.059.791.088 1.183.088.57 0 .991-.077 1.265-.232.273-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.859-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.451-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.588-1.265c.214-.195.503-.358.868-.488s.82-.195 1.367-.195c.269 0 .568.015.896.044.328.03.67.081 1.025.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.872-.065c-.296 0-.545.023-.748.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.071.335.048.1.137.197.267.29.13.094.311.187.544.28.232.094.535.196.909.305.405.118.747.243 1.025.372.278.13.504.275.677.435.173.159.297.339.372.54.076.2.113.428.113.683zm5.804-7.786l-.198 3.206h-1.176l-.191-3.206h1.565z"/><path id=");" fill="#7E7C7B" d="M1.559 59.563c2.1 1.945 3.151 4.11 3.151 6.494 0 .492-.05 1.002-.15 1.53-.1.53-.272 1.07-.513 1.624a9.296 9.296 0 01-.974 1.685c-.408.57-.922 1.142-1.542 1.716l-.69-.704c.888-.88 1.552-1.798 1.99-2.755.437-.957.655-1.96.655-3.007 0-2.17-.881-4.12-2.645-5.852l.718-.731zm6.91 11.58c.233.009.459-.012.677-.062a2.02 2.02 0 00.578-.222c.166-.098.3-.22.4-.366a.86.86 0 00.15-.499.95.95 0 00-.099-.465 2.238 2.238 0 00-.222-.328 2.438 2.438 0 01-.222-.321.91.91 0 01-.1-.458.915.915 0 01.223-.588.857.857 0 01.287-.212 1.12 1.12 0 01.875.017c.148.066.278.168.39.304.111.137.2.308.266.513.066.205.1.447.1.725 0 .378-.07.741-.209 1.09a2.657 2.657 0 01-.622.926c-.276.27-.62.483-1.032.643-.413.16-.892.24-1.44.24v-.937zm2.14-8.388c.142 0 .276.028.404.085a1.107 1.107 0 01.564.564.978.978 0 01.085.404.971.971 0 01-.085.406 1.12 1.12 0 01-.23.332 1.058 1.058 0 01-.738.308c-.145 0-.281-.028-.406-.083a1.07 1.07 0 01-.557-.557 1.006 1.006 0 01-.083-.406 1.058 1.058 0 01.308-.738 1.12 1.12 0 01.332-.23.971.971 0 01.406-.085z"/><path id="let" fill="#1C85B5" d="M2.495 117.06H.465v-.978h3.233v8.668h2.044v.991H.24v-.991h2.256v-7.69zm11.156 4.887c0 .169-.002.31-.006.424a6.37 6.37 0 01-.021.321h-4.82c0 .702.197 1.241.589 1.617.392.376.957.564 1.695.564.2 0 .4-.008.601-.024.201-.016.395-.037.582-.065.186-.027.365-.058.536-.092.171-.034.33-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.042-.694 2.857 2.857 0 01-.602-1.114 5.273 5.273 0 01-.194-1.494c0-.483.069-.94.208-1.37a3.44 3.44 0 01.608-1.135c.267-.326.594-.585.981-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.271.225.37.15.68.364.933.64.253.275.445.61.575 1.005.13.394.194.835.194 1.322zm-1.237-.17a2.593 2.593 0 00-.089-.838 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.571-.427 1.916 1.916 0 00-.793-.154c-.26 0-.497.05-.71.15-.215.1-.4.242-.555.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.177.84h3.61zm8.668 3.869a5.793 5.793 0 01-.834.146 8.52 8.52 0 01-.875.045c-.861 0-1.504-.195-1.928-.585-.424-.39-.635-.987-.635-1.794v-3.582h-1.921v-.998h1.92v-1.887l1.19-.307v2.194h3.083v.998h-3.083v3.486c0 .492.131.86.393 1.104.262.244.648.366 1.159.366.219 0 .458-.017.718-.051.26-.034.53-.088.813-.16v1.025z"/><path id="messages" fill="#181717" d="M35.84 125.741v-4.928c0-.215-.007-.39-.023-.527a1.2 1.2 0 00-.075-.325.348.348 0 00-.13-.167.43.43 0 00-.448.034c-.077.055-.16.144-.25.267a4.216 4.216 0 00-.293.488c-.107.203-.234.453-.38.749v4.41h-1.087v-4.8c0-.25-.008-.453-.024-.608a1.42 1.42 0 00-.075-.362.34.34 0 00-.133-.178.431.431 0 00-.43.02.988.988 0 00-.243.247c-.09.118-.188.28-.298.485-.11.205-.24.467-.39.786v4.41h-1.093v-6.864h.909l.055 1.306c.118-.26.233-.481.345-.663.112-.183.227-.33.345-.441.119-.112.245-.194.38-.247.134-.052.283-.078.447-.078.37 0 .65.12.841.362.191.242.287.615.287 1.121.11-.237.217-.447.321-.632a2.31 2.31 0 01.339-.465c.12-.125.254-.22.4-.287.146-.066.314-.1.506-.1.86 0 1.292.664 1.292 1.99v4.997H35.84zm8.6-3.794c0 .169-.002.31-.006.424a6.37 6.37 0 01-.02.321h-4.82c0 .702.196 1.241.588 1.617.392.376.957.564 1.695.564.2 0 .401-.008.602-.024.2-.016.394-.037.58-.065.187-.027.366-.058.537-.092.171-.034.33-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.042-.694 2.857 2.857 0 01-.602-1.114 5.273 5.273 0 01-.194-1.494c0-.483.07-.94.208-1.37a3.44 3.44 0 01.609-1.135c.266-.326.593-.585.98-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.271.225.37.15.68.364.933.64.253.275.445.61.575 1.005.13.394.194.835.194 1.322zm-1.237-.17a2.593 2.593 0 00-.089-.838 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.57-.427 1.916 1.916 0 00-.794-.154c-.26 0-.497.05-.71.15-.215.1-.4.242-.555.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.177.84h3.61zm8.572 2.091a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.638 4.638 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.401.114.8.2 1.197.26.396.059.79.088 1.182.088.57 0 .991-.077 1.265-.232.273-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.859-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.45-.56 1.649 1.649 0 01-.165-.759 1.744 1.744 0 01.588-1.265c.214-.195.504-.358.868-.488.365-.13.82-.195 1.367-.195.27 0 .568.015.896.044.328.03.67.081 1.025.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.871-.065c-.297 0-.546.023-.749.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.072.335.048.1.136.197.266.29.13.094.311.187.544.28.232.094.535.196.909.305.405.118.747.243 1.025.372.278.13.504.275.677.435.173.159.297.339.373.54.075.2.112.428.112.683zm7.698 0a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.493.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.638 4.638 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.402.114.8.2 1.197.26.396.059.79.088 1.182.088.57 0 .992-.077 1.265-.232.273-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.858-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.452-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.588-1.265c.214-.195.504-.358.868-.488.365-.13.82-.195 1.368-.195.268 0 .567.015.895.044.328.03.67.081 1.025.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.871-.065c-.296 0-.546.023-.749.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.072.335.048.1.137.197.267.29.13.094.31.187.543.28.232.094.535.196.91.305.405.118.747.243 1.025.372.278.13.503.275.676.435.173.159.298.339.373.54.075.2.113.428.113.683zm6.672 1.873l-.028-.923c-.374.37-.753.636-1.138.8-.385.164-.79.246-1.213.246-.392 0-.727-.05-1.005-.15a1.883 1.883 0 01-.687-.414 1.603 1.603 0 01-.397-.618 2.289 2.289 0 01-.126-.773c0-.688.256-1.227.769-1.617.513-.39 1.27-.584 2.273-.584h1.422v-.602c0-.405-.13-.73-.39-.974-.26-.243-.656-.365-1.19-.365-.387 0-.768.043-1.144.13-.376.086-.765.21-1.166.369v-1.074a7.36 7.36 0 011.087-.3 7.6 7.6 0 01.642-.1c.224-.025.45-.037.677-.037.415 0 .789.045 1.121.137.333.09.614.23.845.417.23.186.406.421.53.704.122.282.184.615.184.998v4.73h-1.066zm-.13-3.124h-1.511c-.296 0-.552.03-.766.09-.214.058-.39.143-.526.252-.137.11-.238.24-.304.393-.066.153-.1.325-.1.516 0 .132.021.259.062.38.041.12.107.227.198.32.092.094.21.168.356.223.146.055.323.082.533.082.273 0 .587-.083.94-.25.353-.166.726-.429 1.118-.789v-1.217zm8.36-2.768c.128.16.227.345.297.557.071.212.106.44.106.687 0 .355-.065.68-.195.974-.13.294-.313.546-.55.755a2.54 2.54 0 01-.85.489c-.331.116-.695.174-1.091.174-.287 0-.556-.03-.807-.092a2.064 2.064 0 01-.595-.229c-.086.128-.16.248-.218.362a.817.817 0 00-.09.383c0 .173.084.317.25.43.167.115.387.176.66.185l1.805.069c.341.009.657.052.946.13.29.077.538.189.746.335.207.145.369.325.485.54.116.214.174.462.174.745 0 .305-.066.594-.198.868a1.985 1.985 0 01-.612.721c-.276.207-.625.373-1.05.496-.423.123-.927.184-1.51.184-.556 0-1.029-.044-1.418-.133-.39-.089-.71-.212-.96-.37a1.49 1.49 0 01-.548-.56 1.51 1.51 0 01-.17-.714c0-.333.077-.624.232-.875.155-.25.394-.492.718-.725a1.133 1.133 0 01-.52-.475 1.322 1.322 0 01-.164-.632c0-.296.07-.567.209-.814.139-.246.304-.478.495-.697a3.57 3.57 0 01-.229-.307 1.91 1.91 0 01-.28-.701 2.536 2.536 0 01-.038-.462c0-.355.065-.68.195-.974.13-.294.312-.545.547-.755.235-.21.517-.373.848-.489.33-.116.696-.174 1.097-.174.168 0 .33.011.485.034.155.023.292.052.41.089h2.489v.97h-1.101zm-4.129 6.883c0 .324.169.56.506.708.337.148.807.222 1.408.222.378 0 .696-.034.954-.102.257-.069.465-.159.622-.27.157-.112.27-.24.338-.383.069-.144.103-.29.103-.441 0-.278-.114-.483-.342-.615-.228-.133-.577-.21-1.046-.233l-1.791-.061c-.15.1-.275.198-.373.294a1.292 1.292 0 00-.229.29 1.127 1.127 0 00-.116.294 1.309 1.309 0 00-.034.297zm.362-5.612c0 .219.037.42.11.602.073.182.175.337.307.465.133.127.289.226.469.297.18.07.379.106.598.106.237 0 .448-.04.632-.12.185-.08.34-.188.465-.324.125-.137.221-.294.287-.472.066-.178.1-.362.1-.554 0-.219-.037-.42-.11-.601a1.316 1.316 0 00-.308-.465 1.404 1.404 0 00-.468-.298c-.18-.07-.38-.106-.598-.106-.237 0-.448.041-.633.123-.184.082-.339.19-.464.325-.126.135-.221.29-.287.468-.067.178-.1.363-.1.554zm12.319.827c0 .169-.003.31-.007.424a6.37 6.37 0 01-.02.321h-4.82c0 .702.196 1.241.588 1.617.392.376.957.564 1.695.564.2 0 .401-.008.602-.024.2-.016.394-.037.58-.065.188-.027.367-.058.538-.092.17-.034.329-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.589 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.043-.694 2.857 2.857 0 01-.601-1.114 5.273 5.273 0 01-.195-1.494c0-.483.07-.94.208-1.37a3.44 3.44 0 01.609-1.135c.266-.326.593-.585.98-.776a2.94 2.94 0 011.32-.287c.479 0 .902.075 1.272.225.369.15.68.364.933.64.253.275.444.61.574 1.005.13.394.195.835.195 1.322zm-1.238-.17a2.593 2.593 0 00-.088-.838 1.763 1.763 0 00-.339-.653 1.576 1.576 0 00-.57-.427 1.916 1.916 0 00-.794-.154c-.26 0-.496.05-.71.15-.215.1-.4.242-.554.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.178.84h3.61zm8.573 2.091a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.599.259c-.212.068-.43.119-.652.15a4.638 4.638 0 01-.657.048 12.3 12.3 0 01-1.281-.061 8.646 8.646 0 01-1.145-.199v-1.093c.4.114.8.2 1.196.26.396.059.79.088 1.182.088.57 0 .992-.077 1.265-.232.274-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.858-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.452-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.588-1.265c.214-.195.504-.358.868-.488.365-.13.82-.195 1.368-.195.269 0 .567.015.895.044.328.03.67.081 1.026.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.872-.065c-.296 0-.546.023-.749.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.072.335.048.1.137.197.267.29.13.094.31.187.543.28.232.094.536.196.91.305.405.118.747.243 1.025.372.278.13.503.275.676.435.174.159.298.339.373.54.075.2.113.428.113.683z"/><path id="=" fill="#DBAF88" d="M105.916 121.325h-5.879v-1.005h5.879v1.005zm0 2.38h-5.879v-1.006h5.879v1.005z"/><path id="await" fill="#1C85B5" d="M120.025 125.741l-.027-.923c-.374.37-.753.636-1.138.8-.385.164-.79.246-1.214.246-.391 0-.726-.05-1.004-.15a1.883 1.883 0 01-.687-.414 1.603 1.603 0 01-.397-.618 2.289 2.289 0 01-.126-.773c0-.688.256-1.227.769-1.617.512-.39 1.27-.584 2.273-.584h1.422v-.602c0-.405-.13-.73-.39-.974-.26-.243-.656-.365-1.19-.365-.387 0-.769.043-1.145.13-.376.086-.764.21-1.165.369v-1.074a7.36 7.36 0 011.087-.3 7.6 7.6 0 01.642-.1c.224-.025.45-.037.677-.037.415 0 .789.045 1.121.137.333.09.614.23.844.417.23.186.407.421.53.704.123.282.185.615.185.998v4.73h-1.067zm-.13-3.124h-1.51c-.296 0-.552.03-.766.09-.214.058-.39.143-.526.252-.137.11-.238.24-.304.393-.067.153-.1.325-.1.516 0 .132.021.259.062.38.041.12.107.227.198.32.091.094.21.168.356.223.146.055.323.082.533.082.273 0 .587-.083.94-.25.353-.166.726-.429 1.118-.789v-1.217zm9.776-3.74l-.998 6.864h-1.443l-.99-2.87-.199-.698-.226.738-.95 2.83h-1.401l-.991-6.863h1.162l.574 4.662.123 1.04.294-.91.998-3.083h.855l1.073 3.042.307.91.103-.965.533-4.696h1.176zm5.749 6.864l-.027-.923c-.374.37-.754.636-1.139.8-.385.164-.79.246-1.213.246-.392 0-.727-.05-1.005-.15a1.883 1.883 0 01-.687-.414 1.603 1.603 0 01-.396-.618 2.289 2.289 0 01-.127-.773c0-.688.257-1.227.77-1.617.512-.39 1.27-.584 2.272-.584h1.422v-.602c0-.405-.13-.73-.39-.974-.26-.243-.656-.365-1.19-.365-.386 0-.768.043-1.144.13-.376.086-.765.21-1.166.369v-1.074a7.36 7.36 0 011.087-.3 7.6 7.6 0 01.643-.1c.223-.025.449-.037.677-.037.414 0 .788.045 1.12.137.333.09.615.23.845.417.23.186.407.421.53.704.123.282.184.615.184.998v4.73h-1.066zm-.13-3.124h-1.51c-.297 0-.552.03-.766.09-.215.058-.39.143-.527.252-.136.11-.238.24-.304.393-.066.153-.099.325-.099.516 0 .132.02.259.062.38.04.12.107.227.198.32.09.094.21.168.355.223.146.055.324.082.533.082.274 0 .587-.083.94-.25.354-.166.726-.429 1.118-.789v-1.217zm5.756-2.755h-2.03v-.984h3.233v5.872h2.044v.991h-5.503v-.991h2.256v-4.888zm.417-3.862c.132 0 .255.024.369.072a.893.893 0 01.297.201.919.919 0 01.27.663.96.96 0 01-.27.663.893.893 0 01-.297.202.944.944 0 01-.37.072.944.944 0 01-.368-.072.893.893 0 01-.298-.201.96.96 0 01-.27-.663.919.919 0 01.27-.664.893.893 0 01.298-.201.944.944 0 01.369-.072zm10.473 9.646a5.793 5.793 0 01-.834.146 8.52 8.52 0 01-.875.045c-.862 0-1.504-.195-1.928-.585-.424-.39-.636-.987-.636-1.794v-3.582h-1.92v-.998h1.92v-1.887l1.19-.307v2.194h3.083v.998h-3.083v3.486c0 .492.13.86.393 1.104.262.244.648.366 1.158.366.219 0 .458-.017.718-.051.26-.034.531-.088.814-.16v1.025z"/><path id="response" fill="#181717" d="M162.08 118.878h1.087l.034 1.265c.406-.488.806-.841 1.2-1.06a2.436 2.436 0 011.193-.328c.71 0 1.25.23 1.616.69.367.46.537 1.144.51 2.051h-1.203c.013-.601-.074-1.038-.264-1.309-.189-.271-.466-.407-.83-.407-.16 0-.32.029-.482.086a1.947 1.947 0 00-.5.273 4.47 4.47 0 00-.543.482 8.898 8.898 0 00-.615.711v4.41h-1.203v-6.864zm13.214 3.07c0 .168-.002.31-.007.423a6.37 6.37 0 01-.02.321h-4.82c0 .702.196 1.241.588 1.617.392.376.957.564 1.695.564.201 0 .402-.008.602-.024.2-.016.394-.037.581-.065.187-.027.366-.058.537-.092.17-.034.329-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.042-.694 2.857 2.857 0 01-.602-1.114 5.273 5.273 0 01-.195-1.494c0-.483.07-.94.209-1.37a3.44 3.44 0 01.608-1.135c.267-.326.594-.585.981-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.27.225.37.15.681.364.934.64.253.275.444.61.574 1.005.13.394.195.835.195 1.322zm-1.237-.172a2.593 2.593 0 00-.09-.837 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.57-.427 1.916 1.916 0 00-.793-.154c-.26 0-.497.05-.711.15-.215.1-.4.242-.554.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.178.84h3.61zm8.572 2.092a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.638 4.638 0 01-.657.048 12.3 12.3 0 01-1.281-.061 8.646 8.646 0 01-1.145-.199v-1.093c.4.114.8.2 1.196.26.397.059.79.088 1.183.088.57 0 .99-.077 1.264-.232.274-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.235-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.981-.328 6.963 6.963 0 01-.858-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.451-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.587-1.265c.215-.195.504-.358.869-.488.364-.13.82-.195 1.367-.195.269 0 .567.015.895.044.328.03.67.081 1.026.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.872-.065c-.296 0-.546.023-.748.068a1.553 1.553 0 00-.493.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.072.335.048.1.137.197.267.29.13.094.31.187.543.28.233.094.536.196.91.305.405.118.747.243 1.025.372.278.13.503.275.677.435.173.159.297.339.372.54.075.2.113.428.113.683zm8.128-1.681c0 .61-.086 1.143-.257 1.6-.17.455-.406.833-.707 1.134a2.9 2.9 0 01-1.066.677c-.41.15-.855.225-1.333.225-.22 0-.437-.011-.653-.034a4.952 4.952 0 01-.66-.116v2.87h-1.19v-9.665h1.06l.075 1.148c.342-.469.707-.798 1.094-.987.387-.19.807-.284 1.258-.284.392 0 .736.082 1.032.246.296.164.545.395.745.694.2.298.351.658.451 1.08.1.421.15.892.15 1.412zm-1.217.054c0-.36-.026-.69-.079-.991a2.586 2.586 0 00-.25-.772 1.37 1.37 0 00-.437-.503 1.104 1.104 0 00-.635-.181c-.15 0-.303.024-.458.072a1.857 1.857 0 00-.482.239 3.59 3.59 0 00-.527.444 7 7 0 00-.59.687v3.33c.218.09.448.162.69.215.241.052.478.078.71.078.643 0 1.147-.217 1.511-.652.365-.436.547-1.09.547-1.966zm9.023.014a4.55 4.55 0 01-.225 1.466c-.15.445-.367.825-.65 1.142a2.913 2.913 0 01-1.032.738 3.447 3.447 0 01-1.38.263c-.493 0-.934-.076-1.323-.229a2.617 2.617 0 01-.992-.673 2.98 2.98 0 01-.622-1.1c-.143-.438-.215-.942-.215-1.511 0-.534.075-1.02.226-1.46.15-.44.366-.818.649-1.135a2.913 2.913 0 011.032-.738 3.447 3.447 0 011.381-.263c.492 0 .933.076 1.323.229.39.153.72.376.991.67.271.294.479.66.622 1.097.144.437.215.939.215 1.504zm-1.216.055c0-.424-.047-.795-.14-1.111a2.237 2.237 0 00-.4-.793 1.65 1.65 0 00-.633-.479 2.077 2.077 0 00-.83-.16c-.356 0-.66.07-.913.208-.253.14-.46.325-.622.557a2.4 2.4 0 00-.355.81 4.06 4.06 0 00-.113.968c0 .423.047.795.14 1.114.093.319.227.584.4.796.173.212.383.372.629.479.246.107.524.16.834.16.355 0 .66-.07.912-.208.253-.14.46-.325.622-.557a2.4 2.4 0 00.356-.81 4.09 4.09 0 00.113-.974zm2.94-3.432h1.059l.048 1.107c.2-.237.394-.434.58-.591.187-.157.37-.284.55-.38a2.06 2.06 0 01.551-.201c.187-.039.38-.058.581-.058.707 0 1.241.208 1.603.625.363.417.544 1.045.544 1.884v4.477h-1.19v-4.382c0-.537-.1-.935-.3-1.192-.201-.258-.5-.387-.896-.387-.146 0-.288.022-.427.065a1.557 1.557 0 00-.434.226c-.15.107-.314.252-.49.434-.175.182-.372.41-.59.683v4.553h-1.19v-6.863zm13.131 4.99a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.638 4.638 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.4.114.8.2 1.196.26.397.059.79.088 1.183.088.57 0 .99-.077 1.264-.232.274-.155.41-.376.41-.663a.812.812 0 00-.064-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.981-.328 6.963 6.963 0 01-.858-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.451-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.588-1.265c.214-.195.503-.358.868-.488.364-.13.82-.195 1.367-.195.269 0 .567.015.895.044.329.03.67.081 1.026.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.872-.065c-.296 0-.546.023-.748.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.083.346c0 .123.024.235.072.335.048.1.137.197.267.29.13.094.31.187.543.28.233.094.536.196.91.305.405.118.747.243 1.025.372.278.13.503.275.677.435.173.159.297.339.372.54.075.2.113.428.113.683zm8.06-1.92c0 .168-.003.31-.007.423a6.37 6.37 0 01-.02.321h-4.82c0 .702.196 1.241.588 1.617.392.376.957.564 1.695.564.2 0 .401-.008.602-.024.2-.016.394-.037.58-.065.188-.027.366-.058.537-.092.171-.034.33-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.517-.24a2.597 2.597 0 01-1.043-.694 2.857 2.857 0 01-.601-1.114 5.273 5.273 0 01-.195-1.494c0-.483.07-.94.208-1.37a3.44 3.44 0 01.609-1.135c.266-.326.593-.585.98-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.271.225.37.15.68.364.933.64.253.275.445.61.575 1.005.13.394.195.835.195 1.322zm-1.238-.172a2.593 2.593 0 00-.089-.837 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.57-.427 1.916 1.916 0 00-.794-.154c-.26 0-.497.05-.71.15-.215.1-.4.242-.554.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.178.84h3.61z"/><path id="." fill="#7E7C7B" d="M226.078 123.588a1.113 1.113 0 01.803.335c.103.105.184.227.243.366.06.139.089.288.089.447 0 .155-.03.301-.089.438a1.148 1.148 0 01-.605.601c-.139.06-.286.09-.44.09-.16 0-.308-.03-.445-.09a1.148 1.148 0 01-.602-.601 1.088 1.088 0 01-.089-.438 1.178 1.178 0 01.332-.813 1.12 1.12 0 01.803-.335z"/><path id="json" fill="#181717" d="M235.724 118.878v6.74c0 .479-.069.904-.205 1.275a2.557 2.557 0 01-.588.94 2.485 2.485 0 01-.94.581c-.372.132-.79.198-1.255.198a5.24 5.24 0 01-.987-.092 4.354 4.354 0 01-.872-.256v-1.135a4.512 4.512 0 001.976.472c.52 0 .927-.164 1.223-.493.296-.328.445-.81.445-1.449v-5.797h-3.364v-.984h4.567zm-.718-2.878c.132 0 .255.024.369.072a.893.893 0 01.297.201.96.96 0 01.27.664.919.919 0 01-.27.663.893.893 0 01-.297.201.944.944 0 01-.37.072.944.944 0 01-.368-.072.893.893 0 01-.298-.201.919.919 0 01-.27-.663.96.96 0 01.27-.663.893.893 0 01.298-.202.944.944 0 01.369-.072zm9.201 7.868a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.638 4.638 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.4.114.8.2 1.196.26.397.059.79.088 1.183.088.57 0 .991-.077 1.264-.232.274-.155.41-.376.41-.663a.812.812 0 00-.064-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.981-.328 6.963 6.963 0 01-.858-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.451-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.588-1.265c.214-.195.503-.358.868-.488.364-.13.82-.195 1.367-.195.269 0 .567.015.895.044.329.03.67.081 1.026.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.872-.065c-.296 0-.545.023-.748.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.023.235.071.335.048.1.137.197.267.29.13.094.31.187.543.28.233.094.536.196.91.305.405.118.747.243 1.025.372.278.13.504.275.677.435.173.159.297.339.372.54.075.2.113.428.113.683zm8.237-1.613a4.55 4.55 0 01-.225 1.466c-.15.445-.367.825-.65 1.142a2.913 2.913 0 01-1.032.738 3.447 3.447 0 01-1.38.263c-.493 0-.934-.076-1.324-.229a2.617 2.617 0 01-.99-.673 2.98 2.98 0 01-.623-1.1c-.143-.438-.215-.942-.215-1.511 0-.534.075-1.02.225-1.46.15-.44.367-.818.65-1.135a2.913 2.913 0 011.032-.738 3.447 3.447 0 011.381-.263c.492 0 .933.076 1.323.229.39.153.72.376.99.67.272.294.48.66.623 1.097.144.437.215.939.215 1.504zm-1.216.055c0-.424-.047-.795-.14-1.111a2.237 2.237 0 00-.4-.793 1.65 1.65 0 00-.633-.479 2.077 2.077 0 00-.83-.16c-.356 0-.66.07-.913.208-.253.14-.46.325-.622.557a2.4 2.4 0 00-.356.81 4.06 4.06 0 00-.112.968c0 .423.046.795.14 1.114.093.319.227.584.4.796.173.212.383.372.629.479.246.107.524.16.834.16.355 0 .66-.07.912-.208.253-.14.46-.325.622-.557a2.4 2.4 0 00.356-.81 4.09 4.09 0 00.113-.974zm2.939-3.432h1.06l.047 1.107c.2-.237.395-.434.581-.591.187-.157.37-.284.55-.38a2.06 2.06 0 01.551-.201c.187-.039.38-.058.581-.058.706 0 1.24.208 1.603.625.362.417.544 1.045.544 1.884v4.477h-1.19v-4.382c0-.537-.1-.935-.3-1.192-.201-.258-.5-.387-.896-.387-.146 0-.288.022-.427.065a1.557 1.557 0 00-.434.226c-.15.107-.314.252-.49.434-.175.182-.372.41-.59.683v4.553h-1.19v-6.863z"/><path id="();" fill="#7E7C7B" d="M265.973 128.612c-2.101-1.946-3.152-4.097-3.152-6.453 0-.551.056-1.102.168-1.65.111-.55.291-1.1.54-1.652a9.028 9.028 0 01.984-1.654c.408-.551.904-1.098 1.487-1.64l.69.704c-1.768 1.745-2.652 3.68-2.652 5.803 0 1.058.223 2.074.67 3.05.447.974 1.107 1.895 1.982 2.76l-.717.732zm4.99-13.05c2.1 1.946 3.151 4.111 3.151 6.495 0 .492-.05 1.002-.15 1.53-.1.53-.271 1.07-.513 1.624a9.296 9.296 0 01-.974 1.685c-.408.57-.922 1.142-1.541 1.716l-.69-.704c.888-.88 1.55-1.798 1.988-2.755.438-.957.657-1.96.657-3.007 0-2.17-.882-4.12-2.646-5.852l.718-.731zm6.911 11.58c.232.01.458-.01.677-.06a2.02 2.02 0 00.577-.223c.167-.098.3-.22.4-.366a.86.86 0 00.15-.499.95.95 0 00-.098-.465 2.238 2.238 0 00-.223-.328 2.438 2.438 0 01-.222-.321.91.91 0 01-.099-.458.915.915 0 01.222-.588.857.857 0 01.287-.212 1.12 1.12 0 01.875.017c.149.066.278.168.39.304.112.137.2.308.267.513.066.205.099.447.099.725 0 .378-.07.741-.209 1.09a2.657 2.657 0 01-.622.926c-.276.27-.62.483-1.032.643-.412.16-.892.24-1.439.24v-.937zm2.14-8.387c.14 0 .275.028.403.085a1.107 1.107 0 01.564.564.978.978 0 01.085.404.971.971 0 01-.085.406 1.12 1.12 0 01-.229.332 1.058 1.058 0 01-.738.308c-.146 0-.282-.028-.407-.083a1.07 1.07 0 01-.557-.557 1.006 1.006 0 01-.082-.406 1.058 1.058 0 01.308-.738 1.12 1.12 0 01.33-.23.971.971 0 01.408-.085z"/><path id="..." fill="#DBAF88" d="M2.857 137.588a1.113 1.113 0 01.804.335c.102.105.183.227.242.366.06.139.09.288.09.447 0 .155-.03.301-.09.438a1.148 1.148 0 01-.605.601c-.139.06-.286.09-.44.09-.16 0-.308-.03-.445-.09a1.148 1.148 0 01-.601-.601 1.088 1.088 0 01-.09-.438 1.178 1.178 0 01.332-.813 1.12 1.12 0 01.803-.335zm7.698 0a1.113 1.113 0 01.803.335c.102.105.183.227.243.366.059.139.088.288.088.447 0 .155-.03.301-.088.438a1.148 1.148 0 01-.605.601c-.14.06-.286.09-.441.09-.16 0-.308-.03-.445-.09a1.148 1.148 0 01-.601-.601 1.088 1.088 0 01-.09-.438 1.178 1.178 0 01.332-.813 1.12 1.12 0 01.804-.335zm7.697 0a1.113 1.113 0 01.803.335c.103.105.184.227.243.366.06.139.089.288.089.447 0 .155-.03.301-.09.438a1.148 1.148 0 01-.604.601c-.14.06-.286.09-.441.09-.16 0-.308-.03-.444-.09a1.148 1.148 0 01-.602-.601 1.088 1.088 0 01-.089-.438 1.178 1.178 0 01.332-.813 1.12 1.12 0 01.803-.335z"/><path id="</script>" fill="#7E7C7B" d="M5.339 171.099l-.745.752L0 168.016l4.594-3.835.745.758-3.726 3.063 3.726 3.097zm8.032-9.017l-4.69 11.129H7.548l4.69-11.129h1.134zm7.615 7.786a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.638 4.638 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.401.114.8.2 1.196.26.397.059.791.088 1.183.088.57 0 .991-.077 1.265-.232.273-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.859-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.45-.56 1.649 1.649 0 01-.165-.759 1.744 1.744 0 01.588-1.265c.214-.195.503-.358.868-.488s.82-.195 1.367-.195c.27 0 .568.015.896.044.328.03.67.081 1.025.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.871-.065c-.297 0-.546.023-.749.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.072.335.047.1.136.197.266.29.13.094.311.187.544.28.232.094.535.196.909.305.405.118.747.243 1.025.372.278.13.504.275.677.435.173.159.297.339.373.54.075.2.112.428.112.683zm7.595 1.62c-.31.119-.628.206-.954.263a5.845 5.845 0 01-1.008.086c-1.085 0-1.92-.294-2.505-.882-.586-.588-.879-1.447-.879-2.577 0-.542.085-1.035.253-1.477a3.142 3.142 0 011.805-1.863c.424-.17.89-.256 1.401-.256.356 0 .688.025.998.075.31.05.607.133.89.247v1.134a3.771 3.771 0 00-.907-.338 4.248 4.248 0 00-.953-.106c-.306 0-.594.058-.865.174-.271.116-.51.284-.714.503a2.38 2.38 0 00-.486.8c-.118.314-.178.67-.178 1.066 0 .83.202 1.45.605 1.863.404.412.963.618 1.679.618a3.897 3.897 0 001.818-.438v1.108zm2.646-6.61h1.086l.035 1.265c.405-.488.805-.841 1.2-1.06a2.436 2.436 0 011.192-.328c.711 0 1.25.23 1.617.69.367.46.537 1.144.51 2.051h-1.204c.014-.601-.074-1.038-.263-1.309-.19-.271-.466-.407-.83-.407-.16 0-.32.029-.483.086a1.947 1.947 0 00-.499.273 4.47 4.47 0 00-.543.482 8.898 8.898 0 00-.615.711v4.41h-1.203v-6.864zm9.754.984h-2.03v-.984h3.234v5.872h2.044v.991h-5.503v-.991h2.255v-4.888zm.417-3.862c.133 0 .256.024.37.072a.893.893 0 01.297.201.919.919 0 01.27.663.96.96 0 01-.27.663.893.893 0 01-.297.202.944.944 0 01-.37.072.944.944 0 01-.369-.072.893.893 0 01-.297-.201.96.96 0 01-.27-.663.919.919 0 01.27-.664.893.893 0 01.297-.201.944.944 0 01.37-.072zm10.808 6.187c0 .61-.085 1.143-.256 1.6-.171.455-.407.833-.708 1.134a2.9 2.9 0 01-1.066.677c-.41.15-.855.225-1.333.225-.219 0-.437-.011-.653-.034a4.952 4.952 0 01-.66-.116v2.87h-1.19v-9.665h1.06l.076 1.148c.341-.469.706-.798 1.093-.987.388-.19.807-.284 1.258-.284.392 0 .736.082 1.032.246.297.164.545.395.745.694.201.298.351.658.452 1.08.1.421.15.892.15 1.412zm-1.217.054c0-.36-.026-.69-.078-.991a2.586 2.586 0 00-.25-.772 1.37 1.37 0 00-.437-.503 1.104 1.104 0 00-.636-.181c-.15 0-.303.024-.458.072a1.857 1.857 0 00-.482.239 3.59 3.59 0 00-.526.444 7 7 0 00-.592.687v3.33c.219.09.45.162.69.215.242.052.48.078.712.078.642 0 1.146-.217 1.51-.652.365-.436.547-1.09.547-1.966zm8.58 3.405a5.793 5.793 0 01-.835.146 8.52 8.52 0 01-.875.045c-.861 0-1.504-.195-1.927-.585-.424-.39-.636-.987-.636-1.794v-3.582h-1.921v-.998h1.92v-1.887l1.19-.307v2.194h3.083v.998h-3.083v3.486c0 .492.131.86.393 1.104.262.244.649.366 1.16.366.218 0 .457-.017.717-.051.26-.034.53-.088.813-.16v1.025zm2.501-6.707l.739-.758 4.593 3.835-4.593 3.835-.739-.752 3.726-3.07-3.726-3.09z"/></g><text id="evil.com" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="16" font-weight="bold"><tspan x="129" y="36">evil.com</tspan></text><path id="Rectangle-1-Copy" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M494 48h140v199H494z"/><text id="got-the-cookie?-okay" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal"><tspan x="508.465" y="133">got the cookie?</tspan> <tspan x="544.852" y="155">okay!</tspan></text><text id="gmail.com" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="16" font-weight="bold"><tspan x="528" y="36">gmail.com</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M308.215 100.812l1.487.202 158.667 21.513 1.075-7.927 17.551 11.967-20.104 6.861 1.075-7.928-158.668-21.514-1.486-.201.403-2.973z"/><path id="Line-Copy" fill="#C06334" fill-rule="nonzero" d="M491.796 151.82l.383 2.976-1.488.192L332.54 175.34l1.021 7.935-20.057-6.997 17.632-11.847 1.021 7.934 158.152-20.354 1.487-.191z"/><text id="GET-/messages" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal" transform="rotate(7 408.585 101.617)"><tspan x="352.085" y="107.617">GET /messages</tspan></text><text id="cookie:-user=John" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal" transform="rotate(7 402.573 127.8)"><tspan x="336.073" y="133.8">cookie: user=John</tspan></text><text id="{"messages":-[...]}" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal" transform="rotate(-7 408.333 176.276)"><tspan x="342.833" y="182.276">{"messages": [...]}</tspan></text></g></g></svg> \ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" width="651" height="285" viewBox="0 0 651 285"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="network" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="cors-gmail-messages.svg"><path id="Rectangle-1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M11 48h298v199H11z"/><g id="<script>-let-respons" fill-rule="nonzero" transform="translate(15.937 58.259)"><path id="<script>" fill="#7E7C7B" d="M5.339 9.099l-.745.752L0 6.016 4.594 2.18l.745.758-3.726 3.063L5.34 9.099zm7.95-1.23a1.634 1.634 0 01-.458 1.158 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.637 4.637 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199V8.511c.4.114.8.2 1.196.26.397.059.79.088 1.183.088.57 0 .991-.077 1.264-.232.274-.155.41-.376.41-.663a.812.812 0 00-.064-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.859-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.451-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.588-1.265c.214-.195.503-.358.868-.488.364-.13.82-.195 1.367-.195.269 0 .567.015.896.044.328.03.67.081 1.025.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.872-.065c-.296 0-.545.023-.748.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.023.235.071.335.048.1.137.197.267.29.13.094.311.187.543.28.233.094.536.196.91.305.405.118.747.243 1.025.372.278.13.504.275.677.435.173.159.297.339.372.54.075.2.113.428.113.683zm7.595 1.62c-.31.118-.628.205-.954.262a5.845 5.845 0 01-1.008.086c-1.085 0-1.92-.294-2.505-.882-.586-.588-.879-1.447-.879-2.577 0-.542.084-1.035.253-1.477a3.142 3.142 0 011.805-1.863c.424-.17.89-.256 1.401-.256.356 0 .688.025.998.075.31.05.606.133.889.247v1.134a3.77 3.77 0 00-.906-.338 4.248 4.248 0 00-.954-.106c-.305 0-.593.058-.864.174-.271.116-.51.284-.715.503a2.38 2.38 0 00-.485.8c-.119.314-.178.67-.178 1.066 0 .83.202 1.45.605 1.863.404.412.963.618 1.678.618a3.897 3.897 0 001.818-.438v1.108zm2.645-6.611h1.087l.034 1.265c.406-.488.806-.841 1.2-1.06a2.436 2.436 0 011.193-.328c.71 0 1.25.23 1.617.69.367.46.536 1.144.509 2.051h-1.203c.013-.601-.074-1.038-.263-1.309-.19-.271-.466-.407-.83-.407-.16 0-.321.029-.483.086a1.947 1.947 0 00-.499.273 4.47 4.47 0 00-.543.482 8.898 8.898 0 00-.616.711v4.41H23.53V2.877zm9.755.984h-2.03v-.984h3.233V8.75h2.044v.991h-5.503V8.75h2.256V3.862zM33.701 0c.132 0 .255.024.37.072a.893.893 0 01.297.201.919.919 0 01.27.663.96.96 0 01-.27.663.893.893 0 01-.298.202.944.944 0 01-.369.072.944.944 0 01-.369-.072.893.893 0 01-.297-.201.96.96 0 01-.27-.663.919.919 0 01.27-.664.893.893 0 01.297-.201.944.944 0 01.37-.072zM44.51 6.187c0 .61-.086 1.143-.257 1.6-.17.455-.406.833-.707 1.134a2.9 2.9 0 01-1.066.677c-.41.15-.855.225-1.333.225-.22 0-.437-.011-.653-.034a4.952 4.952 0 01-.66-.116v2.87h-1.19V2.879h1.06l.075 1.148c.342-.469.707-.798 1.094-.987.387-.19.807-.284 1.258-.284.392 0 .736.082 1.032.246.296.164.545.395.745.694.2.298.351.658.451 1.08.1.421.15.892.15 1.412zm-1.217.054c0-.36-.026-.69-.079-.991a2.586 2.586 0 00-.25-.772 1.37 1.37 0 00-.437-.503 1.104 1.104 0 00-.635-.181c-.15 0-.303.024-.458.072a1.857 1.857 0 00-.482.239 3.59 3.59 0 00-.527.444 7 7 0 00-.591.687v3.33c.219.09.449.162.69.215.242.052.479.078.711.078.643 0 1.147-.217 1.511-.652.365-.436.547-1.09.547-1.966zm8.58 3.405a5.793 5.793 0 01-.835.146 8.52 8.52 0 01-.875.045c-.861 0-1.504-.195-1.928-.585-.423-.39-.635-.987-.635-1.794V3.876h-1.921v-.998h1.92V.99l1.19-.307v2.194h3.083v.998h-3.083v3.486c0 .492.131.86.393 1.104.262.244.648.366 1.159.366.219 0 .458-.017.718-.051.26-.034.53-.088.813-.16v1.025zm2.501-6.707l.738-.758 4.594 3.835-4.594 3.835-.738-.752 3.726-3.07-3.726-3.09z"/><path id="let" fill="#1C85B5" d="M2.495 33.06H.465v-.978h3.233v8.668h2.044v.991H.24v-.991h2.256v-7.69zm11.156 4.887c0 .169-.002.31-.006.424a6.37 6.37 0 01-.021.321h-4.82c0 .702.197 1.241.589 1.617.392.376.957.564 1.695.564.2 0 .4-.008.601-.024.201-.016.395-.037.582-.065.186-.027.365-.058.536-.092.171-.034.33-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.042-.694 2.857 2.857 0 01-.602-1.114 5.273 5.273 0 01-.194-1.494c0-.483.069-.94.208-1.37a3.44 3.44 0 01.608-1.135c.267-.326.594-.585.981-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.271.225.37.15.68.364.933.64.253.275.445.61.575 1.005.13.394.194.835.194 1.322zm-1.237-.17a2.593 2.593 0 00-.089-.838 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.571-.427 1.916 1.916 0 00-.793-.154c-.26 0-.497.05-.71.15-.215.1-.4.242-.555.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.177.84h3.61zm8.668 3.869a5.793 5.793 0 01-.834.146 8.52 8.52 0 01-.875.045c-.861 0-1.504-.195-1.928-.585-.424-.39-.635-.987-.635-1.794v-3.582h-1.921v-.998h1.92V32.99l1.19-.307v2.194h3.083v.998h-3.083v3.486c0 .492.131.86.393 1.104.262.244.648.366 1.159.366.219 0 .458-.017.718-.051.26-.034.53-.088.813-.16v1.025z"/><path id="response" fill="#181717" d="M31.227 34.878h1.086l.035 1.265c.405-.488.805-.841 1.2-1.06a2.436 2.436 0 011.192-.328c.711 0 1.25.23 1.617.69.367.46.537 1.144.51 2.051h-1.204c.014-.601-.074-1.038-.263-1.309-.19-.271-.466-.407-.83-.407-.16 0-.32.029-.483.086a1.947 1.947 0 00-.499.273 4.47 4.47 0 00-.543.482 8.898 8.898 0 00-.615.711v4.41h-1.203v-6.864zm13.213 3.07c0 .168-.002.31-.006.423a6.37 6.37 0 01-.02.321h-4.82c0 .702.196 1.241.588 1.617.392.376.957.564 1.695.564.2 0 .401-.008.602-.024.2-.016.394-.037.58-.065.187-.027.366-.058.537-.092.171-.034.33-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.042-.694 2.857 2.857 0 01-.602-1.114 5.273 5.273 0 01-.194-1.494c0-.483.07-.94.208-1.37a3.44 3.44 0 01.609-1.135c.266-.326.593-.585.98-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.271.225.37.15.68.364.933.64.253.275.445.61.575 1.005.13.394.194.835.194 1.322zm-1.237-.172a2.593 2.593 0 00-.089-.837 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.57-.427 1.916 1.916 0 00-.794-.154c-.26 0-.497.05-.71.15-.215.1-.4.242-.555.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.177.84h3.61zm8.572 2.092a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.637 4.637 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.401.114.8.2 1.197.26.396.059.79.088 1.182.088.57 0 .991-.077 1.265-.232.273-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.859-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.45-.56 1.649 1.649 0 01-.165-.759 1.744 1.744 0 01.588-1.265c.214-.195.504-.358.868-.488.365-.13.82-.195 1.367-.195.27 0 .568.015.896.044.328.03.67.081 1.025.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.871-.065c-.297 0-.546.023-.749.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.072.335.048.1.136.197.266.29.13.094.311.187.544.28.232.094.535.196.909.305.405.118.747.243 1.025.372.278.13.504.275.677.435.173.159.297.339.373.54.075.2.112.428.112.683zm8.128-1.681c0 .61-.085 1.143-.256 1.6-.17.455-.407.833-.708 1.134a2.9 2.9 0 01-1.066.677c-.41.15-.854.225-1.333.225-.219 0-.436-.011-.653-.034a4.952 4.952 0 01-.66-.116v2.87h-1.189v-9.665h1.06l.075 1.148c.342-.469.706-.798 1.094-.987.387-.19.806-.284 1.257-.284.392 0 .736.082 1.033.246.296.164.544.395.745.694.2.298.35.658.45 1.08.101.421.151.892.151 1.412zm-1.216.054c0-.36-.027-.69-.08-.991a2.586 2.586 0 00-.249-.772 1.37 1.37 0 00-.437-.503 1.104 1.104 0 00-.636-.181c-.15 0-.303.024-.458.072a1.857 1.857 0 00-.482.239 3.59 3.59 0 00-.526.444 7 7 0 00-.591.687v3.33c.218.09.448.162.69.215.242.052.478.078.71.078.643 0 1.147-.217 1.512-.652.364-.436.547-1.09.547-1.966zm9.023.014a4.55 4.55 0 01-.226 1.466c-.15.445-.366.825-.649 1.142a2.913 2.913 0 01-1.032.738 3.447 3.447 0 01-1.381.263c-.492 0-.933-.076-1.323-.229a2.617 2.617 0 01-.991-.673 2.98 2.98 0 01-.622-1.1c-.144-.438-.215-.942-.215-1.511 0-.534.075-1.02.225-1.46.15-.44.367-.818.65-1.135a2.913 2.913 0 011.032-.738 3.447 3.447 0 011.38-.263c.493 0 .934.076 1.323.229.39.153.72.376.992.67.27.294.478.66.622 1.097.143.437.215.939.215 1.504zm-1.217.055c0-.424-.047-.795-.14-1.111a2.237 2.237 0 00-.4-.793 1.65 1.65 0 00-.632-.479 2.077 2.077 0 00-.83-.16c-.356 0-.66.07-.913.208-.253.14-.46.325-.622.557a2.4 2.4 0 00-.356.81 4.06 4.06 0 00-.113.968c0 .423.047.795.14 1.114.094.319.227.584.4.796.174.212.383.372.63.479.245.107.523.16.833.16.356 0 .66-.07.913-.208.253-.14.46-.325.622-.557a2.4 2.4 0 00.355-.81 4.09 4.09 0 00.113-.974zm2.94-3.432h1.06l.047 1.107c.2-.237.394-.434.581-.591.187-.157.37-.284.55-.38a2.06 2.06 0 01.55-.201c.188-.039.381-.058.582-.058.706 0 1.24.208 1.603.625.362.417.543 1.045.543 1.884v4.477h-1.19V37.36c0-.537-.1-.935-.3-1.192-.2-.258-.499-.387-.896-.387-.145 0-.288.022-.427.065a1.557 1.557 0 00-.434.226c-.15.107-.313.252-.489.434-.175.182-.372.41-.59.683v4.553h-1.19v-6.863zm13.131 4.99a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.637 4.637 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.401.114.8.2 1.197.26.396.059.79.088 1.182.088.57 0 .991-.077 1.265-.232.273-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.858-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.452-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.588-1.265c.214-.195.504-.358.868-.488.365-.13.82-.195 1.367-.195.27 0 .568.015.896.044.328.03.67.081 1.025.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.871-.065c-.297 0-.546.023-.749.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.072.335.048.1.136.197.266.29.13.094.311.187.544.28.232.094.535.196.909.305.406.118.747.243 1.025.372.278.13.504.275.677.435.173.159.297.339.373.54.075.2.112.428.112.683zm8.06-1.92c0 .168-.002.31-.007.423a6.37 6.37 0 01-.02.321h-4.82c0 .702.196 1.241.588 1.617.392.376.957.564 1.696.564.2 0 .4-.008.601-.024.2-.016.394-.037.581-.065.187-.027.366-.058.537-.092.17-.034.329-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.042-.694 2.857 2.857 0 01-.602-1.114 5.273 5.273 0 01-.195-1.494c0-.483.07-.94.209-1.37a3.44 3.44 0 01.608-1.135c.267-.326.594-.585.981-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.27.225.37.15.681.364.934.64.253.275.444.61.574 1.005.13.394.195.835.195 1.322zm-1.237-.172a2.593 2.593 0 00-.09-.837 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.57-.427 1.916 1.916 0 00-.793-.154c-.26 0-.497.05-.711.15-.214.1-.399.242-.554.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.178.84h3.61z"/><path id="=" fill="#DBAF88" d="M105.916 37.325h-5.879V36.32h5.879v1.005zm0 2.38h-5.879v-1.006h5.879v1.005z"/><path id="await" fill="#1C85B5" d="M120.025 41.741l-.027-.923c-.374.37-.753.636-1.138.8-.385.164-.79.246-1.214.246-.391 0-.726-.05-1.004-.15a1.883 1.883 0 01-.687-.414 1.603 1.603 0 01-.397-.618 2.289 2.289 0 01-.126-.773c0-.688.256-1.227.769-1.617.512-.39 1.27-.584 2.273-.584h1.422v-.602c0-.405-.13-.73-.39-.974-.26-.243-.656-.365-1.19-.365-.387 0-.769.043-1.145.13-.376.086-.764.21-1.165.369v-1.074a7.36 7.36 0 011.087-.3 7.6 7.6 0 01.642-.1c.224-.025.45-.037.677-.037.415 0 .789.045 1.121.137.333.09.614.23.844.417.23.186.407.421.53.704.123.282.185.615.185.998v4.73h-1.067zm-.13-3.124h-1.51c-.296 0-.552.03-.766.09-.214.058-.39.143-.526.252-.137.11-.238.24-.304.393-.067.153-.1.325-.1.516 0 .132.021.259.062.38.041.12.107.227.198.32.091.094.21.168.356.223.146.055.323.082.533.082.273 0 .587-.083.94-.25.353-.166.726-.429 1.118-.789v-1.217zm9.776-3.74l-.998 6.864h-1.443l-.99-2.87-.199-.698-.226.738-.95 2.83h-1.401l-.991-6.863h1.162l.574 4.662.123 1.04.294-.91.998-3.083h.855l1.073 3.042.307.91.103-.965.533-4.696h1.176zm5.749 6.864l-.027-.923c-.374.37-.754.636-1.139.8-.385.164-.79.246-1.213.246-.392 0-.727-.05-1.005-.15a1.883 1.883 0 01-.687-.414 1.603 1.603 0 01-.396-.618 2.289 2.289 0 01-.127-.773c0-.688.257-1.227.77-1.617.512-.39 1.27-.584 2.272-.584h1.422v-.602c0-.405-.13-.73-.39-.974-.26-.243-.656-.365-1.19-.365-.386 0-.768.043-1.144.13-.376.086-.765.21-1.166.369v-1.074a7.36 7.36 0 011.087-.3 7.6 7.6 0 01.643-.1c.223-.025.449-.037.677-.037.414 0 .788.045 1.12.137.333.09.615.23.845.417.23.186.407.421.53.704.123.282.184.615.184.998v4.73h-1.066zm-.13-3.124h-1.51c-.297 0-.552.03-.766.09-.215.058-.39.143-.527.252-.136.11-.238.24-.304.393-.066.153-.099.325-.099.516 0 .132.02.259.062.38.04.12.107.227.198.32.09.094.21.168.355.223.146.055.324.082.533.082.274 0 .587-.083.94-.25.354-.166.726-.429 1.118-.789v-1.217zm5.756-2.755h-2.03v-.984h3.233v5.872h2.044v.991h-5.503v-.991h2.256v-4.888zm.417-3.862c.132 0 .255.024.369.072a.893.893 0 01.297.201.919.919 0 01.27.663.96.96 0 01-.27.663.893.893 0 01-.297.202.944.944 0 01-.37.072.944.944 0 01-.368-.072.893.893 0 01-.298-.201.96.96 0 01-.27-.663.919.919 0 01.27-.664.893.893 0 01.298-.201.944.944 0 01.369-.072zm10.473 9.646a5.793 5.793 0 01-.834.146 8.52 8.52 0 01-.875.045c-.862 0-1.504-.195-1.928-.585-.424-.39-.636-.987-.636-1.794v-3.582h-1.92v-.998h1.92V32.99l1.19-.307v2.194h3.083v.998h-3.083v3.486c0 .492.13.86.393 1.104.262.244.648.366 1.158.366.219 0 .458-.017.718-.051.26-.034.531-.088.814-.16v1.025z"/><path id="fetch" fill="#181717" d="M167.89 33.19c-.624-.133-1.161-.199-1.613-.199-1.07 0-1.606.56-1.606 1.682v1.203h3.008v.991h-3.008v4.874h-1.21v-4.874h-2.208v-.991h2.208v-1.135c0-1.827.952-2.741 2.857-2.741.474 0 .998.055 1.573.164v1.025zm7.404 4.757c0 .169-.002.31-.007.424a6.37 6.37 0 01-.02.321h-4.82c0 .702.196 1.241.588 1.617.392.376.957.564 1.695.564.201 0 .402-.008.602-.024.2-.016.394-.037.581-.065.187-.027.366-.058.537-.092.17-.034.329-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.042-.694 2.857 2.857 0 01-.602-1.114 5.273 5.273 0 01-.195-1.494c0-.483.07-.94.209-1.37a3.44 3.44 0 01.608-1.135c.267-.326.594-.585.981-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.27.225.37.15.681.364.934.64.253.275.444.61.574 1.005.13.394.195.835.195 1.322zm-1.237-.17a2.593 2.593 0 00-.09-.838 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.57-.427 1.916 1.916 0 00-.793-.154c-.26 0-.497.05-.711.15-.215.1-.4.242-.554.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.178.84h3.61zm8.668 3.869a5.793 5.793 0 01-.834.146 8.52 8.52 0 01-.875.045c-.862 0-1.504-.195-1.928-.585-.424-.39-.636-.987-.636-1.794v-3.582h-1.92v-.998h1.92V32.99l1.19-.307v2.194h3.083v.998h-3.083v3.486c0 .492.13.86.393 1.104.262.244.648.366 1.158.366.22 0 .458-.017.718-.051.26-.034.531-.088.814-.16v1.025zm7.499-.158c-.31.119-.628.206-.954.263a5.845 5.845 0 01-1.008.086c-1.085 0-1.92-.294-2.506-.882-.585-.588-.878-1.447-.878-2.577 0-.542.084-1.035.253-1.477a3.142 3.142 0 011.805-1.863c.423-.17.89-.256 1.4-.256.356 0 .69.025.999.075.31.05.606.133.889.247v1.134a3.77 3.77 0 00-.906-.338 4.248 4.248 0 00-.954-.106c-.305 0-.593.058-.864.174-.272.116-.51.284-.715.503a2.38 2.38 0 00-.485.8c-.119.314-.178.67-.178 1.066 0 .83.202 1.45.605 1.863.403.412.963.618 1.678.618a3.897 3.897 0 001.818-.438v1.108zm7.881.253h-1.189V37.36c0-.528-.1-.924-.297-1.186-.199-.262-.482-.393-.851-.393-.16 0-.31.022-.448.065a1.557 1.557 0 00-.434.226 3.73 3.73 0 00-.492.434c-.178.182-.383.41-.616.683v4.553h-1.19v-9.659h1.19v2.796l-.04 1.08c.186-.223.37-.411.55-.564.18-.153.36-.277.54-.373.18-.095.363-.164.55-.205.187-.04.38-.061.581-.061.684 0 1.212.208 1.586.625.374.417.56 1.045.56 1.884v4.477z"/><path id="(" fill="#7E7C7B" d="M204.395 44.612c-2.101-1.946-3.152-4.097-3.152-6.453 0-.551.056-1.102.168-1.65.111-.55.291-1.1.54-1.652a9.028 9.028 0 01.984-1.654c.408-.551.904-1.098 1.487-1.64l.69.704c-1.768 1.745-2.652 3.68-2.652 5.803 0 1.058.223 2.074.67 3.05.447.974 1.107 1.895 1.982 2.76l-.717.732z"/><path id="'https://gmail.com/messages'" fill="#478964" d="M19.093 46.082l-.198 3.206h-1.176l-.192-3.206h1.566zm9.673 9.66h-1.19v-4.383c0-.528-.099-.924-.297-1.186-.198-.262-.482-.393-.851-.393-.16 0-.309.022-.448.065a1.557 1.557 0 00-.434.226 3.73 3.73 0 00-.492.434c-.178.182-.383.41-.616.683v4.553H23.25v-9.659h1.19v2.796l-.042 1.08c.187-.223.37-.411.55-.564.18-.153.36-.277.54-.373.18-.095.364-.164.551-.205.187-.04.38-.061.581-.061.684 0 1.212.208 1.586.625.374.417.56 1.045.56 1.884v4.477zm7.71-.096a5.793 5.793 0 01-.833.146 8.52 8.52 0 01-.875.045c-.862 0-1.504-.195-1.928-.585-.424-.39-.636-.987-.636-1.794v-3.582h-1.92v-.998h1.92V46.99l1.19-.307v2.194h3.083v.998h-3.083v3.486c0 .492.13.86.393 1.104.262.244.648.366 1.158.366.22 0 .458-.017.718-.051.26-.034.531-.088.814-.16v1.025zm7.698 0a5.793 5.793 0 01-.834.146 8.52 8.52 0 01-.875.045c-.861 0-1.504-.195-1.928-.585-.424-.39-.636-.987-.636-1.794v-3.582h-1.92v-.998h1.92V46.99l1.19-.307v2.194h3.083v.998H41.09v3.486c0 .492.13.86.393 1.104.262.244.648.366 1.159.366.218 0 .458-.017.717-.051.26-.034.531-.088.814-.16v1.025zm8.032-3.46c0 .611-.085 1.144-.256 1.6-.171.456-.407.834-.708 1.135a2.9 2.9 0 01-1.066.677c-.41.15-.855.225-1.333.225-.219 0-.437-.011-.653-.034a4.952 4.952 0 01-.66-.116v2.87h-1.19v-9.665h1.06l.076 1.148c.341-.469.706-.798 1.093-.987.388-.19.807-.284 1.258-.284.392 0 .736.082 1.032.246.297.164.545.395.745.694.201.298.351.658.452 1.08.1.421.15.892.15 1.412zm-1.217.055c0-.36-.026-.69-.078-.991a2.586 2.586 0 00-.25-.772 1.37 1.37 0 00-.437-.503 1.104 1.104 0 00-.636-.181c-.15 0-.303.024-.458.072a1.857 1.857 0 00-.482.239 3.59 3.59 0 00-.526.444 7 7 0 00-.592.687v3.33c.219.09.45.162.69.215.242.052.48.078.712.078.642 0 1.146-.217 1.51-.652.365-.436.547-1.09.547-1.966zm8.484 1.627a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.493.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.637 4.637 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.402.114.8.2 1.197.26.396.059.79.088 1.182.088.57 0 .992-.077 1.265-.232.273-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.858-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.452-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.588-1.265c.214-.195.504-.358.868-.488.365-.13.82-.195 1.368-.195.268 0 .567.015.895.044.328.03.67.081 1.025.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.871-.065c-.296 0-.546.023-.749.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.072.335.048.1.137.197.267.29.13.094.31.187.543.28.232.094.535.196.91.305.405.118.747.243 1.025.372.278.13.503.275.676.435.173.159.298.339.373.54.075.2.113.428.113.683zm5.017-5.113c.142 0 .276.028.404.085a1.107 1.107 0 01.564.564.978.978 0 01.085.404.971.971 0 01-.085.406 1.12 1.12 0 01-.23.332 1.058 1.058 0 01-.738.308c-.146 0-.281-.028-.407-.083a1.07 1.07 0 01-.557-.557 1.006 1.006 0 01-.082-.406 1.058 1.058 0 01.308-.738 1.12 1.12 0 01.331-.23.971.971 0 01.407-.085zm0 5.01c.142 0 .276.03.404.086a1.107 1.107 0 01.564.564.978.978 0 01.085.403.971.971 0 01-.085.407 1.12 1.12 0 01-.23.332 1.058 1.058 0 01-.738.307c-.146 0-.281-.027-.407-.082a1.07 1.07 0 01-.557-.557 1.006 1.006 0 01-.082-.407 1.058 1.058 0 01.308-.738 1.12 1.12 0 01.331-.229.971.971 0 01.407-.085zm10.46-7.683l-4.69 11.129h-1.135l4.69-11.129h1.134zm7.696 0l-4.689 11.129h-1.135l4.69-11.129h1.134zm7.124 3.767c.127.16.226.345.297.557.07.212.106.44.106.687 0 .355-.065.68-.195.974-.13.294-.313.546-.55.755a2.54 2.54 0 01-.851.489c-.33.116-.694.174-1.09.174-.288 0-.557-.03-.807-.092a2.064 2.064 0 01-.595-.229c-.087.128-.16.248-.219.362a.817.817 0 00-.089.383c0 .173.084.317.25.43.166.115.386.176.66.185l1.804.069c.342.009.658.052.947.13.29.077.538.189.745.335.207.145.37.325.485.54.117.214.175.462.175.745 0 .305-.066.594-.198.868a1.985 1.985 0 01-.612.721c-.276.207-.626.373-1.05.496-.423.123-.927.184-1.51.184-.556 0-1.03-.044-1.419-.133-.39-.089-.71-.212-.96-.37a1.49 1.49 0 01-.547-.56 1.51 1.51 0 01-.171-.714c0-.333.077-.624.232-.875.155-.25.395-.492.718-.725a1.133 1.133 0 01-.52-.475 1.322 1.322 0 01-.164-.632c0-.296.07-.567.21-.814.138-.246.303-.478.495-.697a3.57 3.57 0 01-.23-.307 1.91 1.91 0 01-.28-.701 2.536 2.536 0 01-.037-.462c0-.355.065-.68.195-.974.13-.294.312-.545.547-.755.234-.21.517-.373.847-.489.33-.116.696-.174 1.097-.174.169 0 .33.011.486.034.155.023.291.052.41.089h2.488v.97h-1.1zm-4.13 6.883c0 .324.17.56.506.708.338.148.807.222 1.409.222.378 0 .696-.034.953-.102.258-.069.465-.159.622-.27.158-.112.27-.24.339-.383.068-.144.102-.29.102-.441 0-.278-.114-.483-.342-.615-.227-.133-.576-.21-1.045-.233l-1.791-.061c-.15.1-.275.198-.373.294a1.292 1.292 0 00-.229.29 1.127 1.127 0 00-.116.294 1.309 1.309 0 00-.034.297zm.363-5.612c0 .219.036.42.11.602.072.182.175.337.307.465.132.127.288.226.468.297.18.07.38.106.598.106.237 0 .448-.04.633-.12.184-.08.34-.188.464-.324.126-.137.222-.294.288-.472.066-.178.099-.362.099-.554 0-.219-.037-.42-.11-.601a1.316 1.316 0 00-.307-.465 1.404 1.404 0 00-.469-.298c-.18-.07-.379-.106-.598-.106-.237 0-.447.041-.632.123-.185.082-.34.19-.465.325s-.22.29-.287.468c-.066.178-.1.363-.1.554zm11.416 4.621v-4.928c0-.215-.008-.39-.024-.527a1.2 1.2 0 00-.075-.325.348.348 0 00-.13-.167.43.43 0 00-.448.034c-.077.055-.16.144-.25.267a4.216 4.216 0 00-.293.488c-.107.203-.234.453-.38.749v4.41h-1.087v-4.8c0-.25-.008-.453-.024-.608a1.42 1.42 0 00-.075-.362.34.34 0 00-.133-.178.431.431 0 00-.43.02.988.988 0 00-.243.247c-.09.118-.188.28-.298.485-.11.205-.239.467-.39.786v4.41h-1.093v-6.864h.91l.054 1.306c.118-.26.233-.481.345-.663.112-.183.227-.33.345-.441.119-.112.245-.194.38-.247.134-.052.283-.078.447-.078.37 0 .65.12.841.362.192.242.287.615.287 1.121.11-.237.217-.447.322-.632a2.31 2.31 0 01.338-.465c.12-.125.254-.22.4-.287.146-.066.314-.1.506-.1.861 0 1.292.664 1.292 1.99v4.997h-1.094zm7.212 0l-.027-.923c-.374.37-.754.636-1.139.8-.385.164-.79.246-1.213.246-.392 0-.727-.05-1.005-.15a1.883 1.883 0 01-.687-.414 1.603 1.603 0 01-.396-.618 2.289 2.289 0 01-.127-.773c0-.688.256-1.227.77-1.617.512-.39 1.27-.584 2.272-.584h1.422v-.602c0-.405-.13-.73-.39-.974-.26-.243-.656-.365-1.19-.365-.386 0-.768.043-1.144.13-.376.086-.765.21-1.166.369v-1.074a7.36 7.36 0 011.087-.3 7.6 7.6 0 01.643-.1c.223-.025.449-.037.677-.037.414 0 .788.045 1.12.137.333.09.615.23.845.417.23.186.407.421.53.704.123.282.184.615.184.998v4.73h-1.066zm-.13-3.124h-1.51c-.297 0-.552.03-.766.09-.215.058-.39.143-.527.252-.136.11-.238.24-.304.393-.066.153-.1.325-.1.516 0 .132.021.259.062.38.041.12.108.227.199.32.09.094.21.168.355.223.146.055.324.082.533.082.274 0 .587-.083.94-.25.353-.166.726-.429 1.118-.789v-1.217zm5.756-2.755h-2.03v-.984h3.233v5.872h2.044v.991H108v-.991h2.256v-4.888zm.417-3.862c.132 0 .255.024.369.072a.893.893 0 01.297.201.919.919 0 01.27.663.96.96 0 01-.27.663.893.893 0 01-.297.202.944.944 0 01-.37.072.944.944 0 01-.368-.072.893.893 0 01-.298-.201.96.96 0 01-.27-.663.919.919 0 01.27-.664.893.893 0 01.298-.201.944.944 0 01.369-.072zm7.28 1.06h-2.03v-.978h3.233v8.668h2.044v.991h-5.503v-.991h2.256v-7.69zm8.06 6.528a1.113 1.113 0 01.803.335c.102.105.183.227.243.366.059.139.088.288.088.447 0 .155-.03.301-.088.438a1.148 1.148 0 01-.605.601c-.14.06-.286.09-.441.09-.16 0-.308-.03-.445-.09a1.148 1.148 0 01-.601-.601 1.088 1.088 0 01-.09-.438 1.178 1.178 0 01.332-.813 1.12 1.12 0 01.804-.335zm10.329 1.9c-.31.119-.628.206-.954.263a5.845 5.845 0 01-1.008.086c-1.085 0-1.92-.294-2.506-.882-.585-.588-.878-1.447-.878-2.577 0-.542.084-1.035.253-1.477.169-.442.406-.82.71-1.134.306-.315.67-.558 1.095-.728.424-.171.89-.257 1.401-.257.356 0 .688.025.998.075.31.05.606.133.889.247v1.134a3.77 3.77 0 00-.906-.338 4.248 4.248 0 00-.954-.106c-.305 0-.593.058-.864.174-.272.116-.51.284-.715.503a2.38 2.38 0 00-.485.8c-.119.314-.178.67-.178 1.066 0 .83.202 1.45.605 1.863.404.412.963.618 1.678.618a3.897 3.897 0 001.818-.438v1.108zm8.34-3.233a4.55 4.55 0 01-.226 1.466c-.15.445-.367.825-.65 1.142a2.913 2.913 0 01-1.032.738 3.447 3.447 0 01-1.38.263c-.493 0-.934-.076-1.323-.229a2.617 2.617 0 01-.991-.673 2.98 2.98 0 01-.623-1.1c-.143-.438-.215-.942-.215-1.511 0-.534.075-1.02.226-1.46.15-.44.367-.818.65-1.135a2.913 2.913 0 011.031-.738 3.447 3.447 0 011.381-.263c.492 0 .933.076 1.323.229.39.153.72.376.991.67.271.294.479.66.622 1.097.144.437.216.939.216 1.504zm-1.217.055c0-.424-.047-.795-.14-1.111a2.237 2.237 0 00-.4-.793 1.65 1.65 0 00-.633-.479 2.077 2.077 0 00-.83-.16c-.356 0-.66.07-.913.208-.253.14-.46.325-.622.557a2.4 2.4 0 00-.355.81 4.06 4.06 0 00-.113.968c0 .423.047.795.14 1.114.094.319.227.584.4.796.173.212.383.372.629.479.246.107.524.16.834.16.355 0 .66-.07.912-.208.253-.14.46-.325.623-.557a2.4 2.4 0 00.355-.81 4.09 4.09 0 00.113-.974zm7.834 3.431v-4.928c0-.215-.008-.39-.024-.527a1.2 1.2 0 00-.075-.325.348.348 0 00-.13-.167.43.43 0 00-.448.034c-.077.055-.16.144-.25.267a4.216 4.216 0 00-.293.488 30.3 30.3 0 00-.38.749v4.41h-1.087v-4.8c0-.25-.008-.453-.024-.608a1.42 1.42 0 00-.075-.362.34.34 0 00-.133-.178.431.431 0 00-.43.02.988.988 0 00-.243.247c-.09.118-.188.28-.298.485-.11.205-.24.467-.39.786v4.41h-1.093v-6.864h.909l.055 1.306c.118-.26.233-.481.345-.663.111-.183.227-.33.345-.441.119-.112.245-.194.38-.247.134-.052.283-.078.447-.078.37 0 .65.12.841.362.191.242.287.615.287 1.121.11-.237.217-.447.321-.632a2.31 2.31 0 01.339-.465c.12-.125.254-.22.4-.287.146-.066.314-.1.506-.1.86 0 1.292.664 1.292 1.99v4.997H151.3zm8.32-9.659l-4.69 11.129h-1.135l4.69-11.129h1.134zm7.074 9.66v-4.93c0-.214-.008-.39-.024-.526a1.2 1.2 0 00-.075-.325.348.348 0 00-.13-.167.43.43 0 00-.448.034c-.077.055-.16.144-.249.267a4.216 4.216 0 00-.294.488c-.107.203-.233.453-.38.749v4.41h-1.086v-4.8c0-.25-.008-.453-.024-.608a1.42 1.42 0 00-.075-.362.34.34 0 00-.134-.178.431.431 0 00-.43.02.988.988 0 00-.243.247c-.089.118-.188.28-.297.485-.11.205-.24.467-.39.786v4.41h-1.094v-6.864h.91l.054 1.306c.119-.26.234-.481.345-.663.112-.183.227-.33.346-.441.118-.112.245-.194.379-.247.134-.052.284-.078.448-.078.369 0 .65.12.84.362.192.242.288.615.288 1.121.109-.237.216-.447.321-.632a2.31 2.31 0 01.338-.465c.121-.125.254-.22.4-.287.146-.066.315-.1.506-.1.861 0 1.292.664 1.292 1.99v4.997h-1.094zm8.6-3.795c0 .169-.002.31-.007.424a6.37 6.37 0 01-.02.321h-4.82c0 .702.196 1.241.588 1.617.392.376.957.564 1.695.564.201 0 .402-.008.602-.024.2-.016.394-.037.581-.065.187-.027.366-.058.537-.092.17-.034.329-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.042-.694 2.857 2.857 0 01-.602-1.114 5.273 5.273 0 01-.195-1.494c0-.483.07-.94.209-1.37a3.44 3.44 0 01.608-1.135c.267-.326.594-.585.981-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.27.225.37.15.681.364.934.64.253.275.444.61.574 1.005.13.394.195.835.195 1.322zm-1.237-.17a2.593 2.593 0 00-.09-.838 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.57-.427 1.916 1.916 0 00-.793-.154c-.26 0-.497.05-.711.15-.215.1-.4.242-.554.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.178.84h3.61zm8.572 2.091a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.637 4.637 0 01-.657.048 12.3 12.3 0 01-1.281-.061 8.646 8.646 0 01-1.145-.199v-1.093c.4.114.8.2 1.196.26.397.059.79.088 1.183.088.57 0 .99-.077 1.264-.232.274-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.235-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.981-.328 6.963 6.963 0 01-.858-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.451-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.587-1.265c.215-.195.504-.358.869-.488.364-.13.82-.195 1.367-.195.269 0 .567.015.895.044.328.03.67.081 1.026.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.872-.065c-.296 0-.546.023-.748.068a1.553 1.553 0 00-.493.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.072.335.048.1.137.197.267.29.13.094.31.187.543.28.233.094.536.196.91.305.405.118.747.243 1.025.372.278.13.503.275.677.435.173.159.297.339.372.54.075.2.113.428.113.683zm7.697 0a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.637 4.637 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.401.114.8.2 1.196.26.397.059.791.088 1.183.088.57 0 .991-.077 1.265-.232.273-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.859-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.451-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.588-1.265c.214-.195.503-.358.868-.488.364-.13.82-.195 1.367-.195.269 0 .567.015.896.044.328.03.67.081 1.025.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.872-.065c-.296 0-.545.023-.748.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.071.335.048.1.137.197.267.29.13.094.311.187.543.28.233.094.536.196.91.305.405.118.747.243 1.025.372.278.13.504.275.677.435.173.159.297.339.372.54.076.2.113.428.113.683zm6.672 1.873l-.027-.923c-.374.37-.753.636-1.138.8-.386.164-.79.246-1.214.246-.392 0-.727-.05-1.005-.15a1.883 1.883 0 01-.687-.414 1.603 1.603 0 01-.396-.618 2.289 2.289 0 01-.127-.773c0-.688.257-1.227.77-1.617.512-.39 1.27-.584 2.272-.584h1.422v-.602c0-.405-.13-.73-.39-.974-.26-.243-.656-.365-1.189-.365-.387 0-.769.043-1.145.13-.376.086-.764.21-1.165.369v-1.074a7.36 7.36 0 011.086-.3 7.6 7.6 0 01.643-.1c.223-.025.449-.037.677-.037.414 0 .788.045 1.12.137.334.09.615.23.845.417.23.186.407.421.53.704.123.282.184.615.184.998v4.73h-1.066zm-.13-3.124h-1.51c-.297 0-.552.03-.766.09-.214.058-.39.143-.527.252-.136.11-.238.24-.304.393-.066.153-.099.325-.099.516 0 .132.02.259.062.38.04.12.107.227.198.32.091.094.21.168.355.223.146.055.324.082.534.082.273 0 .586-.083.94-.25.353-.166.725-.429 1.117-.789v-1.217zm8.36-2.768c.128.16.227.345.298.557.07.212.106.44.106.687 0 .355-.065.68-.195.974-.13.294-.313.546-.55.755a2.54 2.54 0 01-.851.489c-.33.116-.694.174-1.09.174-.288 0-.557-.03-.807-.092a2.064 2.064 0 01-.595-.229c-.087.128-.16.248-.219.362a.817.817 0 00-.089.383c0 .173.083.317.25.43.166.115.386.176.66.185l1.804.069c.342.009.658.052.947.13.29.077.538.189.745.335.207.145.37.325.485.54.117.214.175.462.175.745 0 .305-.066.594-.198.868a1.985 1.985 0 01-.612.721c-.276.207-.626.373-1.05.496-.423.123-.927.184-1.51.184-.556 0-1.03-.044-1.419-.133-.39-.089-.71-.212-.96-.37a1.49 1.49 0 01-.547-.56 1.51 1.51 0 01-.171-.714c0-.333.077-.624.232-.875.155-.25.395-.492.718-.725a1.133 1.133 0 01-.52-.475 1.322 1.322 0 01-.164-.632c0-.296.07-.567.21-.814.138-.246.303-.478.495-.697a3.57 3.57 0 01-.23-.307 1.91 1.91 0 01-.28-.701 2.536 2.536 0 01-.037-.462c0-.355.065-.68.195-.974.13-.294.312-.545.546-.755.235-.21.518-.373.848-.489.33-.116.696-.174 1.097-.174.169 0 .33.011.486.034.155.023.291.052.41.089h2.488v.97h-1.1zm-4.128 6.883c0 .324.168.56.505.708.338.148.807.222 1.409.222.378 0 .696-.034.953-.102.258-.069.465-.159.622-.27.158-.112.27-.24.339-.383.068-.144.102-.29.102-.441 0-.278-.114-.483-.342-.615-.227-.133-.576-.21-1.045-.233l-1.791-.061c-.15.1-.275.198-.373.294a1.292 1.292 0 00-.229.29 1.127 1.127 0 00-.116.294 1.309 1.309 0 00-.034.297zm.362-5.612c0 .219.036.42.11.602.072.182.175.337.307.465.132.127.288.226.468.297.18.07.38.106.598.106.237 0 .448-.04.633-.12.184-.08.34-.188.464-.324.126-.137.222-.294.288-.472.066-.178.099-.362.099-.554 0-.219-.037-.42-.11-.601a1.316 1.316 0 00-.307-.465 1.404 1.404 0 00-.469-.298c-.18-.07-.379-.106-.598-.106-.237 0-.447.041-.632.123-.185.082-.34.19-.465.325s-.22.29-.287.468c-.066.178-.1.363-.1.554zm12.318.827c0 .169-.002.31-.007.424a6.372 6.372 0 01-.02.321h-4.82c0 .702.197 1.241.588 1.617.392.376.958.564 1.696.564.2 0 .4-.008.601-.024.2-.016.395-.037.581-.065.187-.027.366-.058.537-.092.17-.034.33-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.042-.694 2.857 2.857 0 01-.602-1.114 5.273 5.273 0 01-.195-1.494c0-.483.07-.94.209-1.37a3.44 3.44 0 01.608-1.135c.267-.326.594-.585.981-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.271.225.37.15.68.364.933.64.253.275.445.61.574 1.005.13.394.195.835.195 1.322zm-1.237-.17a2.593 2.593 0 00-.089-.838 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.571-.427 1.916 1.916 0 00-.793-.154c-.26 0-.497.05-.711.15-.214.1-.399.242-.554.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.177.84h3.609zm8.572 2.091a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.637 4.637 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.401.114.8.2 1.196.26.397.059.791.088 1.183.088.57 0 .991-.077 1.265-.232.273-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.859-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.451-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.588-1.265c.214-.195.503-.358.868-.488s.82-.195 1.367-.195c.269 0 .568.015.896.044.328.03.67.081 1.025.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.872-.065c-.296 0-.545.023-.748.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.071.335.048.1.137.197.267.29.13.094.311.187.544.28.232.094.535.196.909.305.405.118.747.243 1.025.372.278.13.504.275.677.435.173.159.297.339.372.54.076.2.113.428.113.683zm5.804-7.786l-.198 3.206h-1.176l-.191-3.206h1.565z"/><path id=");" fill="#7E7C7B" d="M1.559 59.563c2.1 1.945 3.151 4.11 3.151 6.494 0 .492-.05 1.002-.15 1.53-.1.53-.272 1.07-.513 1.624a9.296 9.296 0 01-.974 1.685c-.408.57-.922 1.142-1.542 1.716l-.69-.704c.888-.88 1.552-1.798 1.99-2.755.437-.957.655-1.96.655-3.007 0-2.17-.881-4.12-2.645-5.852l.718-.731zm6.91 11.58c.233.009.459-.012.677-.062a2.02 2.02 0 00.578-.222c.166-.098.3-.22.4-.366a.86.86 0 00.15-.499.95.95 0 00-.099-.465 2.238 2.238 0 00-.222-.328 2.438 2.438 0 01-.222-.321.91.91 0 01-.1-.458.915.915 0 01.223-.588.857.857 0 01.287-.212 1.12 1.12 0 01.875.017c.148.066.278.168.39.304.111.137.2.308.266.513.066.205.1.447.1.725 0 .378-.07.741-.209 1.09a2.657 2.657 0 01-.622.926c-.276.27-.62.483-1.032.643-.413.16-.892.24-1.44.24v-.937zm2.14-8.388c.142 0 .276.028.404.085a1.107 1.107 0 01.564.564.978.978 0 01.085.404.971.971 0 01-.085.406 1.12 1.12 0 01-.23.332 1.058 1.058 0 01-.738.308c-.145 0-.281-.028-.406-.083a1.07 1.07 0 01-.557-.557 1.006 1.006 0 01-.083-.406 1.058 1.058 0 01.308-.738 1.12 1.12 0 01.332-.23.971.971 0 01.406-.085z"/><path id="let" fill="#1C85B5" d="M2.495 117.06H.465v-.978h3.233v8.668h2.044v.991H.24v-.991h2.256v-7.69zm11.156 4.887c0 .169-.002.31-.006.424a6.37 6.37 0 01-.021.321h-4.82c0 .702.197 1.241.589 1.617.392.376.957.564 1.695.564.2 0 .4-.008.601-.024.201-.016.395-.037.582-.065.186-.027.365-.058.536-.092.171-.034.33-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.042-.694 2.857 2.857 0 01-.602-1.114 5.273 5.273 0 01-.194-1.494c0-.483.069-.94.208-1.37a3.44 3.44 0 01.608-1.135c.267-.326.594-.585.981-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.271.225.37.15.68.364.933.64.253.275.445.61.575 1.005.13.394.194.835.194 1.322zm-1.237-.17a2.593 2.593 0 00-.089-.838 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.571-.427 1.916 1.916 0 00-.793-.154c-.26 0-.497.05-.71.15-.215.1-.4.242-.555.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.177.84h3.61zm8.668 3.869a5.793 5.793 0 01-.834.146 8.52 8.52 0 01-.875.045c-.861 0-1.504-.195-1.928-.585-.424-.39-.635-.987-.635-1.794v-3.582h-1.921v-.998h1.92v-1.887l1.19-.307v2.194h3.083v.998h-3.083v3.486c0 .492.131.86.393 1.104.262.244.648.366 1.159.366.219 0 .458-.017.718-.051.26-.034.53-.088.813-.16v1.025z"/><path id="messages" fill="#181717" d="M35.84 125.741v-4.928c0-.215-.007-.39-.023-.527a1.2 1.2 0 00-.075-.325.348.348 0 00-.13-.167.43.43 0 00-.448.034c-.077.055-.16.144-.25.267a4.216 4.216 0 00-.293.488c-.107.203-.234.453-.38.749v4.41h-1.087v-4.8c0-.25-.008-.453-.024-.608a1.42 1.42 0 00-.075-.362.34.34 0 00-.133-.178.431.431 0 00-.43.02.988.988 0 00-.243.247c-.09.118-.188.28-.298.485-.11.205-.24.467-.39.786v4.41h-1.093v-6.864h.909l.055 1.306c.118-.26.233-.481.345-.663.112-.183.227-.33.345-.441.119-.112.245-.194.38-.247.134-.052.283-.078.447-.078.37 0 .65.12.841.362.191.242.287.615.287 1.121.11-.237.217-.447.321-.632a2.31 2.31 0 01.339-.465c.12-.125.254-.22.4-.287.146-.066.314-.1.506-.1.86 0 1.292.664 1.292 1.99v4.997H35.84zm8.6-3.794c0 .169-.002.31-.006.424a6.37 6.37 0 01-.02.321h-4.82c0 .702.196 1.241.588 1.617.392.376.957.564 1.695.564.2 0 .401-.008.602-.024.2-.016.394-.037.58-.065.187-.027.366-.058.537-.092.171-.034.33-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.042-.694 2.857 2.857 0 01-.602-1.114 5.273 5.273 0 01-.194-1.494c0-.483.07-.94.208-1.37a3.44 3.44 0 01.609-1.135c.266-.326.593-.585.98-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.271.225.37.15.68.364.933.64.253.275.445.61.575 1.005.13.394.194.835.194 1.322zm-1.237-.17a2.593 2.593 0 00-.089-.838 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.57-.427 1.916 1.916 0 00-.794-.154c-.26 0-.497.05-.71.15-.215.1-.4.242-.555.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.177.84h3.61zm8.572 2.091a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.638 4.638 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.401.114.8.2 1.197.26.396.059.79.088 1.182.088.57 0 .991-.077 1.265-.232.273-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.859-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.45-.56 1.649 1.649 0 01-.165-.759 1.744 1.744 0 01.588-1.265c.214-.195.504-.358.868-.488.365-.13.82-.195 1.367-.195.27 0 .568.015.896.044.328.03.67.081 1.025.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.871-.065c-.297 0-.546.023-.749.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.072.335.048.1.136.197.266.29.13.094.311.187.544.28.232.094.535.196.909.305.405.118.747.243 1.025.372.278.13.504.275.677.435.173.159.297.339.373.54.075.2.112.428.112.683zm7.698 0a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.493.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.638 4.638 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.402.114.8.2 1.197.26.396.059.79.088 1.182.088.57 0 .992-.077 1.265-.232.273-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.858-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.452-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.588-1.265c.214-.195.504-.358.868-.488.365-.13.82-.195 1.368-.195.268 0 .567.015.895.044.328.03.67.081 1.025.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.871-.065c-.296 0-.546.023-.749.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.072.335.048.1.137.197.267.29.13.094.31.187.543.28.232.094.535.196.91.305.405.118.747.243 1.025.372.278.13.503.275.676.435.173.159.298.339.373.54.075.2.113.428.113.683zm6.672 1.873l-.028-.923c-.374.37-.753.636-1.138.8-.385.164-.79.246-1.213.246-.392 0-.727-.05-1.005-.15a1.883 1.883 0 01-.687-.414 1.603 1.603 0 01-.397-.618 2.289 2.289 0 01-.126-.773c0-.688.256-1.227.769-1.617.513-.39 1.27-.584 2.273-.584h1.422v-.602c0-.405-.13-.73-.39-.974-.26-.243-.656-.365-1.19-.365-.387 0-.768.043-1.144.13-.376.086-.765.21-1.166.369v-1.074a7.36 7.36 0 011.087-.3 7.6 7.6 0 01.642-.1c.224-.025.45-.037.677-.037.415 0 .789.045 1.121.137.333.09.614.23.845.417.23.186.406.421.53.704.122.282.184.615.184.998v4.73h-1.066zm-.13-3.124h-1.511c-.296 0-.552.03-.766.09-.214.058-.39.143-.526.252-.137.11-.238.24-.304.393-.066.153-.1.325-.1.516 0 .132.021.259.062.38.041.12.107.227.198.32.092.094.21.168.356.223.146.055.323.082.533.082.273 0 .587-.083.94-.25.353-.166.726-.429 1.118-.789v-1.217zm8.36-2.768c.128.16.227.345.297.557.071.212.106.44.106.687 0 .355-.065.68-.195.974-.13.294-.313.546-.55.755a2.54 2.54 0 01-.85.489c-.331.116-.695.174-1.091.174-.287 0-.556-.03-.807-.092a2.064 2.064 0 01-.595-.229c-.086.128-.16.248-.218.362a.817.817 0 00-.09.383c0 .173.084.317.25.43.167.115.387.176.66.185l1.805.069c.341.009.657.052.946.13.29.077.538.189.746.335.207.145.369.325.485.54.116.214.174.462.174.745 0 .305-.066.594-.198.868a1.985 1.985 0 01-.612.721c-.276.207-.625.373-1.05.496-.423.123-.927.184-1.51.184-.556 0-1.029-.044-1.418-.133-.39-.089-.71-.212-.96-.37a1.49 1.49 0 01-.548-.56 1.51 1.51 0 01-.17-.714c0-.333.077-.624.232-.875.155-.25.394-.492.718-.725a1.133 1.133 0 01-.52-.475 1.322 1.322 0 01-.164-.632c0-.296.07-.567.209-.814.139-.246.304-.478.495-.697a3.57 3.57 0 01-.229-.307 1.91 1.91 0 01-.28-.701 2.536 2.536 0 01-.038-.462c0-.355.065-.68.195-.974.13-.294.312-.545.547-.755.235-.21.517-.373.848-.489.33-.116.696-.174 1.097-.174.168 0 .33.011.485.034.155.023.292.052.41.089h2.489v.97h-1.101zm-4.129 6.883c0 .324.169.56.506.708.337.148.807.222 1.408.222.378 0 .696-.034.954-.102.257-.069.465-.159.622-.27.157-.112.27-.24.338-.383.069-.144.103-.29.103-.441 0-.278-.114-.483-.342-.615-.228-.133-.577-.21-1.046-.233l-1.791-.061c-.15.1-.275.198-.373.294a1.292 1.292 0 00-.229.29 1.127 1.127 0 00-.116.294 1.309 1.309 0 00-.034.297zm.362-5.612c0 .219.037.42.11.602.073.182.175.337.307.465.133.127.289.226.469.297.18.07.379.106.598.106.237 0 .448-.04.632-.12.185-.08.34-.188.465-.324.125-.137.221-.294.287-.472.066-.178.1-.362.1-.554 0-.219-.037-.42-.11-.601a1.316 1.316 0 00-.308-.465 1.404 1.404 0 00-.468-.298c-.18-.07-.38-.106-.598-.106-.237 0-.448.041-.633.123-.184.082-.339.19-.464.325-.126.135-.221.29-.287.468-.067.178-.1.363-.1.554zm12.319.827c0 .169-.003.31-.007.424a6.37 6.37 0 01-.02.321h-4.82c0 .702.196 1.241.588 1.617.392.376.957.564 1.695.564.2 0 .401-.008.602-.024.2-.016.394-.037.58-.065.188-.027.367-.058.538-.092.17-.034.329-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.589 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.043-.694 2.857 2.857 0 01-.601-1.114 5.273 5.273 0 01-.195-1.494c0-.483.07-.94.208-1.37a3.44 3.44 0 01.609-1.135c.266-.326.593-.585.98-.776a2.94 2.94 0 011.32-.287c.479 0 .902.075 1.272.225.369.15.68.364.933.64.253.275.444.61.574 1.005.13.394.195.835.195 1.322zm-1.238-.17a2.593 2.593 0 00-.088-.838 1.763 1.763 0 00-.339-.653 1.576 1.576 0 00-.57-.427 1.916 1.916 0 00-.794-.154c-.26 0-.496.05-.71.15-.215.1-.4.242-.554.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.178.84h3.61zm8.573 2.091a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.599.259c-.212.068-.43.119-.652.15a4.638 4.638 0 01-.657.048 12.3 12.3 0 01-1.281-.061 8.646 8.646 0 01-1.145-.199v-1.093c.4.114.8.2 1.196.26.396.059.79.088 1.182.088.57 0 .992-.077 1.265-.232.274-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.858-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.452-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.588-1.265c.214-.195.504-.358.868-.488.365-.13.82-.195 1.368-.195.269 0 .567.015.895.044.328.03.67.081 1.026.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.872-.065c-.296 0-.546.023-.749.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.072.335.048.1.137.197.267.29.13.094.31.187.543.28.232.094.536.196.91.305.405.118.747.243 1.025.372.278.13.503.275.676.435.174.159.298.339.373.54.075.2.113.428.113.683z"/><path id="=" fill="#DBAF88" d="M105.916 121.325h-5.879v-1.005h5.879v1.005zm0 2.38h-5.879v-1.006h5.879v1.005z"/><path id="await" fill="#1C85B5" d="M120.025 125.741l-.027-.923c-.374.37-.753.636-1.138.8-.385.164-.79.246-1.214.246-.391 0-.726-.05-1.004-.15a1.883 1.883 0 01-.687-.414 1.603 1.603 0 01-.397-.618 2.289 2.289 0 01-.126-.773c0-.688.256-1.227.769-1.617.512-.39 1.27-.584 2.273-.584h1.422v-.602c0-.405-.13-.73-.39-.974-.26-.243-.656-.365-1.19-.365-.387 0-.769.043-1.145.13-.376.086-.764.21-1.165.369v-1.074a7.36 7.36 0 011.087-.3 7.6 7.6 0 01.642-.1c.224-.025.45-.037.677-.037.415 0 .789.045 1.121.137.333.09.614.23.844.417.23.186.407.421.53.704.123.282.185.615.185.998v4.73h-1.067zm-.13-3.124h-1.51c-.296 0-.552.03-.766.09-.214.058-.39.143-.526.252-.137.11-.238.24-.304.393-.067.153-.1.325-.1.516 0 .132.021.259.062.38.041.12.107.227.198.32.091.094.21.168.356.223.146.055.323.082.533.082.273 0 .587-.083.94-.25.353-.166.726-.429 1.118-.789v-1.217zm9.776-3.74l-.998 6.864h-1.443l-.99-2.87-.199-.698-.226.738-.95 2.83h-1.401l-.991-6.863h1.162l.574 4.662.123 1.04.294-.91.998-3.083h.855l1.073 3.042.307.91.103-.965.533-4.696h1.176zm5.749 6.864l-.027-.923c-.374.37-.754.636-1.139.8-.385.164-.79.246-1.213.246-.392 0-.727-.05-1.005-.15a1.883 1.883 0 01-.687-.414 1.603 1.603 0 01-.396-.618 2.289 2.289 0 01-.127-.773c0-.688.257-1.227.77-1.617.512-.39 1.27-.584 2.272-.584h1.422v-.602c0-.405-.13-.73-.39-.974-.26-.243-.656-.365-1.19-.365-.386 0-.768.043-1.144.13-.376.086-.765.21-1.166.369v-1.074a7.36 7.36 0 011.087-.3 7.6 7.6 0 01.643-.1c.223-.025.449-.037.677-.037.414 0 .788.045 1.12.137.333.09.615.23.845.417.23.186.407.421.53.704.123.282.184.615.184.998v4.73h-1.066zm-.13-3.124h-1.51c-.297 0-.552.03-.766.09-.215.058-.39.143-.527.252-.136.11-.238.24-.304.393-.066.153-.099.325-.099.516 0 .132.02.259.062.38.04.12.107.227.198.32.09.094.21.168.355.223.146.055.324.082.533.082.274 0 .587-.083.94-.25.354-.166.726-.429 1.118-.789v-1.217zm5.756-2.755h-2.03v-.984h3.233v5.872h2.044v.991h-5.503v-.991h2.256v-4.888zm.417-3.862c.132 0 .255.024.369.072a.893.893 0 01.297.201.919.919 0 01.27.663.96.96 0 01-.27.663.893.893 0 01-.297.202.944.944 0 01-.37.072.944.944 0 01-.368-.072.893.893 0 01-.298-.201.96.96 0 01-.27-.663.919.919 0 01.27-.664.893.893 0 01.298-.201.944.944 0 01.369-.072zm10.473 9.646a5.793 5.793 0 01-.834.146 8.52 8.52 0 01-.875.045c-.862 0-1.504-.195-1.928-.585-.424-.39-.636-.987-.636-1.794v-3.582h-1.92v-.998h1.92v-1.887l1.19-.307v2.194h3.083v.998h-3.083v3.486c0 .492.13.86.393 1.104.262.244.648.366 1.158.366.219 0 .458-.017.718-.051.26-.034.531-.088.814-.16v1.025z"/><path id="response" fill="#181717" d="M162.08 118.878h1.087l.034 1.265c.406-.488.806-.841 1.2-1.06a2.436 2.436 0 011.193-.328c.71 0 1.25.23 1.616.69.367.46.537 1.144.51 2.051h-1.203c.013-.601-.074-1.038-.264-1.309-.189-.271-.466-.407-.83-.407-.16 0-.32.029-.482.086a1.947 1.947 0 00-.5.273 4.47 4.47 0 00-.543.482 8.898 8.898 0 00-.615.711v4.41h-1.203v-6.864zm13.214 3.07c0 .168-.002.31-.007.423a6.37 6.37 0 01-.02.321h-4.82c0 .702.196 1.241.588 1.617.392.376.957.564 1.695.564.201 0 .402-.008.602-.024.2-.016.394-.037.581-.065.187-.027.366-.058.537-.092.17-.034.329-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.042-.694 2.857 2.857 0 01-.602-1.114 5.273 5.273 0 01-.195-1.494c0-.483.07-.94.209-1.37a3.44 3.44 0 01.608-1.135c.267-.326.594-.585.981-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.27.225.37.15.681.364.934.64.253.275.444.61.574 1.005.13.394.195.835.195 1.322zm-1.237-.172a2.593 2.593 0 00-.09-.837 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.57-.427 1.916 1.916 0 00-.793-.154c-.26 0-.497.05-.711.15-.215.1-.4.242-.554.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.178.84h3.61zm8.572 2.092a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.638 4.638 0 01-.657.048 12.3 12.3 0 01-1.281-.061 8.646 8.646 0 01-1.145-.199v-1.093c.4.114.8.2 1.196.26.397.059.79.088 1.183.088.57 0 .99-.077 1.264-.232.274-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.235-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.981-.328 6.963 6.963 0 01-.858-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.451-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.587-1.265c.215-.195.504-.358.869-.488.364-.13.82-.195 1.367-.195.269 0 .567.015.895.044.328.03.67.081 1.026.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.872-.065c-.296 0-.546.023-.748.068a1.553 1.553 0 00-.493.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.072.335.048.1.137.197.267.29.13.094.31.187.543.28.233.094.536.196.91.305.405.118.747.243 1.025.372.278.13.503.275.677.435.173.159.297.339.372.54.075.2.113.428.113.683zm8.128-1.681c0 .61-.086 1.143-.257 1.6-.17.455-.406.833-.707 1.134a2.9 2.9 0 01-1.066.677c-.41.15-.855.225-1.333.225-.22 0-.437-.011-.653-.034a4.952 4.952 0 01-.66-.116v2.87h-1.19v-9.665h1.06l.075 1.148c.342-.469.707-.798 1.094-.987.387-.19.807-.284 1.258-.284.392 0 .736.082 1.032.246.296.164.545.395.745.694.2.298.351.658.451 1.08.1.421.15.892.15 1.412zm-1.217.054c0-.36-.026-.69-.079-.991a2.586 2.586 0 00-.25-.772 1.37 1.37 0 00-.437-.503 1.104 1.104 0 00-.635-.181c-.15 0-.303.024-.458.072a1.857 1.857 0 00-.482.239 3.59 3.59 0 00-.527.444 7 7 0 00-.59.687v3.33c.218.09.448.162.69.215.241.052.478.078.71.078.643 0 1.147-.217 1.511-.652.365-.436.547-1.09.547-1.966zm9.023.014a4.55 4.55 0 01-.225 1.466c-.15.445-.367.825-.65 1.142a2.913 2.913 0 01-1.032.738 3.447 3.447 0 01-1.38.263c-.493 0-.934-.076-1.323-.229a2.617 2.617 0 01-.992-.673 2.98 2.98 0 01-.622-1.1c-.143-.438-.215-.942-.215-1.511 0-.534.075-1.02.226-1.46.15-.44.366-.818.649-1.135a2.913 2.913 0 011.032-.738 3.447 3.447 0 011.381-.263c.492 0 .933.076 1.323.229.39.153.72.376.991.67.271.294.479.66.622 1.097.144.437.215.939.215 1.504zm-1.216.055c0-.424-.047-.795-.14-1.111a2.237 2.237 0 00-.4-.793 1.65 1.65 0 00-.633-.479 2.077 2.077 0 00-.83-.16c-.356 0-.66.07-.913.208-.253.14-.46.325-.622.557a2.4 2.4 0 00-.355.81 4.06 4.06 0 00-.113.968c0 .423.047.795.14 1.114.093.319.227.584.4.796.173.212.383.372.629.479.246.107.524.16.834.16.355 0 .66-.07.912-.208.253-.14.46-.325.622-.557a2.4 2.4 0 00.356-.81 4.09 4.09 0 00.113-.974zm2.94-3.432h1.059l.048 1.107c.2-.237.394-.434.58-.591.187-.157.37-.284.55-.38a2.06 2.06 0 01.551-.201c.187-.039.38-.058.581-.058.707 0 1.241.208 1.603.625.363.417.544 1.045.544 1.884v4.477h-1.19v-4.382c0-.537-.1-.935-.3-1.192-.201-.258-.5-.387-.896-.387-.146 0-.288.022-.427.065a1.557 1.557 0 00-.434.226c-.15.107-.314.252-.49.434-.175.182-.372.41-.59.683v4.553h-1.19v-6.863zm13.131 4.99a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.638 4.638 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.4.114.8.2 1.196.26.397.059.79.088 1.183.088.57 0 .99-.077 1.264-.232.274-.155.41-.376.41-.663a.812.812 0 00-.064-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.981-.328 6.963 6.963 0 01-.858-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.451-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.588-1.265c.214-.195.503-.358.868-.488.364-.13.82-.195 1.367-.195.269 0 .567.015.895.044.329.03.67.081 1.026.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.872-.065c-.296 0-.546.023-.748.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.083.346c0 .123.024.235.072.335.048.1.137.197.267.29.13.094.31.187.543.28.233.094.536.196.91.305.405.118.747.243 1.025.372.278.13.503.275.677.435.173.159.297.339.372.54.075.2.113.428.113.683zm8.06-1.92c0 .168-.003.31-.007.423a6.37 6.37 0 01-.02.321h-4.82c0 .702.196 1.241.588 1.617.392.376.957.564 1.695.564.2 0 .401-.008.602-.024.2-.016.394-.037.58-.065.188-.027.366-.058.537-.092.171-.034.33-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.517-.24a2.597 2.597 0 01-1.043-.694 2.857 2.857 0 01-.601-1.114 5.273 5.273 0 01-.195-1.494c0-.483.07-.94.208-1.37a3.44 3.44 0 01.609-1.135c.266-.326.593-.585.98-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.271.225.37.15.68.364.933.64.253.275.445.61.575 1.005.13.394.195.835.195 1.322zm-1.238-.172a2.593 2.593 0 00-.089-.837 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.57-.427 1.916 1.916 0 00-.794-.154c-.26 0-.497.05-.71.15-.215.1-.4.242-.554.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.178.84h3.61z"/><path id="." fill="#7E7C7B" d="M226.078 123.588a1.113 1.113 0 01.803.335c.103.105.184.227.243.366.06.139.089.288.089.447 0 .155-.03.301-.089.438a1.148 1.148 0 01-.605.601c-.139.06-.286.09-.44.09-.16 0-.308-.03-.445-.09a1.148 1.148 0 01-.602-.601 1.088 1.088 0 01-.089-.438 1.178 1.178 0 01.332-.813 1.12 1.12 0 01.803-.335z"/><path id="json" fill="#181717" d="M235.724 118.878v6.74c0 .479-.069.904-.205 1.275a2.557 2.557 0 01-.588.94 2.485 2.485 0 01-.94.581c-.372.132-.79.198-1.255.198a5.24 5.24 0 01-.987-.092 4.354 4.354 0 01-.872-.256v-1.135a4.512 4.512 0 001.976.472c.52 0 .927-.164 1.223-.493.296-.328.445-.81.445-1.449v-5.797h-3.364v-.984h4.567zm-.718-2.878c.132 0 .255.024.369.072a.893.893 0 01.297.201.96.96 0 01.27.664.919.919 0 01-.27.663.893.893 0 01-.297.201.944.944 0 01-.37.072.944.944 0 01-.368-.072.893.893 0 01-.298-.201.919.919 0 01-.27-.663.96.96 0 01.27-.663.893.893 0 01.298-.202.944.944 0 01.369-.072zm9.201 7.868a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.638 4.638 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.4.114.8.2 1.196.26.397.059.79.088 1.183.088.57 0 .991-.077 1.264-.232.274-.155.41-.376.41-.663a.812.812 0 00-.064-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.981-.328 6.963 6.963 0 01-.858-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.451-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.588-1.265c.214-.195.503-.358.868-.488.364-.13.82-.195 1.367-.195.269 0 .567.015.895.044.329.03.67.081 1.026.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.872-.065c-.296 0-.545.023-.748.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.023.235.071.335.048.1.137.197.267.29.13.094.31.187.543.28.233.094.536.196.91.305.405.118.747.243 1.025.372.278.13.504.275.677.435.173.159.297.339.372.54.075.2.113.428.113.683zm8.237-1.613a4.55 4.55 0 01-.225 1.466c-.15.445-.367.825-.65 1.142a2.913 2.913 0 01-1.032.738 3.447 3.447 0 01-1.38.263c-.493 0-.934-.076-1.324-.229a2.617 2.617 0 01-.99-.673 2.98 2.98 0 01-.623-1.1c-.143-.438-.215-.942-.215-1.511 0-.534.075-1.02.225-1.46.15-.44.367-.818.65-1.135a2.913 2.913 0 011.032-.738 3.447 3.447 0 011.381-.263c.492 0 .933.076 1.323.229.39.153.72.376.99.67.272.294.48.66.623 1.097.144.437.215.939.215 1.504zm-1.216.055c0-.424-.047-.795-.14-1.111a2.237 2.237 0 00-.4-.793 1.65 1.65 0 00-.633-.479 2.077 2.077 0 00-.83-.16c-.356 0-.66.07-.913.208-.253.14-.46.325-.622.557a2.4 2.4 0 00-.356.81 4.06 4.06 0 00-.112.968c0 .423.046.795.14 1.114.093.319.227.584.4.796.173.212.383.372.629.479.246.107.524.16.834.16.355 0 .66-.07.912-.208.253-.14.46-.325.622-.557a2.4 2.4 0 00.356-.81 4.09 4.09 0 00.113-.974zm2.939-3.432h1.06l.047 1.107c.2-.237.395-.434.581-.591.187-.157.37-.284.55-.38a2.06 2.06 0 01.551-.201c.187-.039.38-.058.581-.058.706 0 1.24.208 1.603.625.362.417.544 1.045.544 1.884v4.477h-1.19v-4.382c0-.537-.1-.935-.3-1.192-.201-.258-.5-.387-.896-.387-.146 0-.288.022-.427.065a1.557 1.557 0 00-.434.226c-.15.107-.314.252-.49.434-.175.182-.372.41-.59.683v4.553h-1.19v-6.863z"/><path id="();" fill="#7E7C7B" d="M265.973 128.612c-2.101-1.946-3.152-4.097-3.152-6.453 0-.551.056-1.102.168-1.65.111-.55.291-1.1.54-1.652a9.028 9.028 0 01.984-1.654c.408-.551.904-1.098 1.487-1.64l.69.704c-1.768 1.745-2.652 3.68-2.652 5.803 0 1.058.223 2.074.67 3.05.447.974 1.107 1.895 1.982 2.76l-.717.732zm4.99-13.05c2.1 1.946 3.151 4.111 3.151 6.495 0 .492-.05 1.002-.15 1.53-.1.53-.271 1.07-.513 1.624a9.296 9.296 0 01-.974 1.685c-.408.57-.922 1.142-1.541 1.716l-.69-.704c.888-.88 1.55-1.798 1.988-2.755.438-.957.657-1.96.657-3.007 0-2.17-.882-4.12-2.646-5.852l.718-.731zm6.911 11.58c.232.01.458-.01.677-.06a2.02 2.02 0 00.577-.223c.167-.098.3-.22.4-.366a.86.86 0 00.15-.499.95.95 0 00-.098-.465 2.238 2.238 0 00-.223-.328 2.438 2.438 0 01-.222-.321.91.91 0 01-.099-.458.915.915 0 01.222-.588.857.857 0 01.287-.212 1.12 1.12 0 01.875.017c.149.066.278.168.39.304.112.137.2.308.267.513.066.205.099.447.099.725 0 .378-.07.741-.209 1.09a2.657 2.657 0 01-.622.926c-.276.27-.62.483-1.032.643-.412.16-.892.24-1.439.24v-.937zm2.14-8.387c.14 0 .275.028.403.085a1.107 1.107 0 01.564.564.978.978 0 01.085.404.971.971 0 01-.085.406 1.12 1.12 0 01-.229.332 1.058 1.058 0 01-.738.308c-.146 0-.282-.028-.407-.083a1.07 1.07 0 01-.557-.557 1.006 1.006 0 01-.082-.406 1.058 1.058 0 01.308-.738 1.12 1.12 0 01.33-.23.971.971 0 01.408-.085z"/><path id="..." fill="#DBAF88" d="M2.857 137.588a1.113 1.113 0 01.804.335c.102.105.183.227.242.366.06.139.09.288.09.447 0 .155-.03.301-.09.438a1.148 1.148 0 01-.605.601c-.139.06-.286.09-.44.09-.16 0-.308-.03-.445-.09a1.148 1.148 0 01-.601-.601 1.088 1.088 0 01-.09-.438 1.178 1.178 0 01.332-.813 1.12 1.12 0 01.803-.335zm7.698 0a1.113 1.113 0 01.803.335c.102.105.183.227.243.366.059.139.088.288.088.447 0 .155-.03.301-.088.438a1.148 1.148 0 01-.605.601c-.14.06-.286.09-.441.09-.16 0-.308-.03-.445-.09a1.148 1.148 0 01-.601-.601 1.088 1.088 0 01-.09-.438 1.178 1.178 0 01.332-.813 1.12 1.12 0 01.804-.335zm7.697 0a1.113 1.113 0 01.803.335c.103.105.184.227.243.366.06.139.089.288.089.447 0 .155-.03.301-.09.438a1.148 1.148 0 01-.604.601c-.14.06-.286.09-.441.09-.16 0-.308-.03-.444-.09a1.148 1.148 0 01-.602-.601 1.088 1.088 0 01-.089-.438 1.178 1.178 0 01.332-.813 1.12 1.12 0 01.803-.335z"/><path id="</script>" fill="#7E7C7B" d="M5.339 171.099l-.745.752L0 168.016l4.594-3.835.745.758-3.726 3.063 3.726 3.097zm8.032-9.017l-4.69 11.129H7.548l4.69-11.129h1.134zm7.615 7.786a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.638 4.638 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.401.114.8.2 1.196.26.397.059.791.088 1.183.088.57 0 .991-.077 1.265-.232.273-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.859-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.45-.56 1.649 1.649 0 01-.165-.759 1.744 1.744 0 01.588-1.265c.214-.195.503-.358.868-.488s.82-.195 1.367-.195c.27 0 .568.015.896.044.328.03.67.081 1.025.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.871-.065c-.297 0-.546.023-.749.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.072.335.047.1.136.197.266.29.13.094.311.187.544.28.232.094.535.196.909.305.405.118.747.243 1.025.372.278.13.504.275.677.435.173.159.297.339.373.54.075.2.112.428.112.683zm7.595 1.62c-.31.119-.628.206-.954.263a5.845 5.845 0 01-1.008.086c-1.085 0-1.92-.294-2.505-.882-.586-.588-.879-1.447-.879-2.577 0-.542.085-1.035.253-1.477a3.142 3.142 0 011.805-1.863c.424-.17.89-.256 1.401-.256.356 0 .688.025.998.075.31.05.607.133.89.247v1.134a3.771 3.771 0 00-.907-.338 4.248 4.248 0 00-.953-.106c-.306 0-.594.058-.865.174-.271.116-.51.284-.714.503a2.38 2.38 0 00-.486.8c-.118.314-.178.67-.178 1.066 0 .83.202 1.45.605 1.863.404.412.963.618 1.679.618a3.897 3.897 0 001.818-.438v1.108zm2.646-6.61h1.086l.035 1.265c.405-.488.805-.841 1.2-1.06a2.436 2.436 0 011.192-.328c.711 0 1.25.23 1.617.69.367.46.537 1.144.51 2.051h-1.204c.014-.601-.074-1.038-.263-1.309-.19-.271-.466-.407-.83-.407-.16 0-.32.029-.483.086a1.947 1.947 0 00-.499.273 4.47 4.47 0 00-.543.482 8.898 8.898 0 00-.615.711v4.41h-1.203v-6.864zm9.754.984h-2.03v-.984h3.234v5.872h2.044v.991h-5.503v-.991h2.255v-4.888zm.417-3.862c.133 0 .256.024.37.072a.893.893 0 01.297.201.919.919 0 01.27.663.96.96 0 01-.27.663.893.893 0 01-.297.202.944.944 0 01-.37.072.944.944 0 01-.369-.072.893.893 0 01-.297-.201.96.96 0 01-.27-.663.919.919 0 01.27-.664.893.893 0 01.297-.201.944.944 0 01.37-.072zm10.808 6.187c0 .61-.085 1.143-.256 1.6-.171.455-.407.833-.708 1.134a2.9 2.9 0 01-1.066.677c-.41.15-.855.225-1.333.225-.219 0-.437-.011-.653-.034a4.952 4.952 0 01-.66-.116v2.87h-1.19v-9.665h1.06l.076 1.148c.341-.469.706-.798 1.093-.987.388-.19.807-.284 1.258-.284.392 0 .736.082 1.032.246.297.164.545.395.745.694.201.298.351.658.452 1.08.1.421.15.892.15 1.412zm-1.217.054c0-.36-.026-.69-.078-.991a2.586 2.586 0 00-.25-.772 1.37 1.37 0 00-.437-.503 1.104 1.104 0 00-.636-.181c-.15 0-.303.024-.458.072a1.857 1.857 0 00-.482.239 3.59 3.59 0 00-.526.444 7 7 0 00-.592.687v3.33c.219.09.45.162.69.215.242.052.48.078.712.078.642 0 1.146-.217 1.51-.652.365-.436.547-1.09.547-1.966zm8.58 3.405a5.793 5.793 0 01-.835.146 8.52 8.52 0 01-.875.045c-.861 0-1.504-.195-1.927-.585-.424-.39-.636-.987-.636-1.794v-3.582h-1.921v-.998h1.92v-1.887l1.19-.307v2.194h3.083v.998h-3.083v3.486c0 .492.131.86.393 1.104.262.244.649.366 1.16.366.218 0 .457-.017.717-.051.26-.034.53-.088.813-.16v1.025zm2.501-6.707l.739-.758 4.593 3.835-4.593 3.835-.739-.752 3.726-3.07-3.726-3.09z"/></g><text id="evil.com" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="16" font-weight="bold"><tspan x="129" y="36">evil.com</tspan></text><path id="Rectangle-1-Copy" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M494 48h140v199H494z"/><text id="got-the-cookie?-okay" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal"><tspan x="507.965" y="133">tiene la cookie?</tspan> <tspan x="552.352" y="155">Ok!</tspan></text><text id="gmail.com" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="16" font-weight="bold"><tspan x="528" y="36">gmail.com</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M308.215 100.812l1.487.202 158.667 21.513 1.075-7.927 17.551 11.967-20.104 6.861 1.075-7.928-158.668-21.514-1.486-.201.403-2.973z"/><path id="Line-Copy" fill="#C06334" fill-rule="nonzero" d="M491.796 151.82l.383 2.976-1.488.192L332.54 175.34l1.021 7.935-20.057-6.997 17.632-11.847 1.021 7.934 158.152-20.354 1.487-.191z"/><text id="GET-/messages" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal" transform="rotate(7 408.585 101.617)"><tspan x="352.085" y="107.617">GET /messages</tspan></text><text id="cookie:-user=John" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal" transform="rotate(7 402.573 127.8)"><tspan x="336.073" y="133.8">cookie: user=John</tspan></text><text id="{"messages":-[...]}" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal" transform="rotate(-7 408.333 176.276)"><tspan x="342.833" y="182.276">{"messages": [...]}</tspan></text></g></g></svg> \ No newline at end of file diff --git a/5-network/05-fetch-crossorigin/xhr-another-domain.svg b/5-network/05-fetch-crossorigin/xhr-another-domain.svg index 2ed70febd..452ae118c 100644 --- a/5-network/05-fetch-crossorigin/xhr-another-domain.svg +++ b/5-network/05-fetch-crossorigin/xhr-another-domain.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="633" height="411" viewBox="0 0 633 411"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="network" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="xhr-another-domain.svg"><path id="Rectangle-227" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M1 16h128v64H1z"/><text id="JavaScript" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="35" y="50">JavaScript</tspan></text><path id="Rectangle-228" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M261 16h128v64H261z"/><text id="Browser" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="296" y="50">Browser</tspan></text><path id="Rectangle-229" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M504 16h128v64H504z"/><text id="Server" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="544" y="50">Server</tspan></text><path id="Line" stroke="#7E7C7B" stroke-linecap="square" d="M66 81v320M326 81v320M569 81v320"/><path id="Line" fill="#C06334" fill-rule="nonzero" d="M312 133l14 7-14 7v-6H66v-2h246v-6z"/><text id="Origin:-https://java" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="354.313" y="205">Origin: https://javascript.info</tspan></text><text id="HTTP-request" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="401.015" y="174">HTTP-request</tspan></text><text id="fetch()" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="171" y="131">fetch()</tspan></text><text id="HTTP-response" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="398.419" y="250">HTTP-response</tspan></text><text id="Access-Control-Allow" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="347" y="287">Access-Control-Allow-Origin: * </tspan> <tspan x="364.141" y="305">(or https://javascript.info)</tspan></text><text id="if-the-header-allows" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="85" y="325">if the header allows, then success,</tspan></text><text id="otherwise-fail" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="148" y="353">otherwise fail</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M554 180l14 7-14 7v-6H328v-2h226v-6z"/><path id="Line-2" fill="#C06334" fill-rule="nonzero" d="M340 258v6h228v2H340v6l-14-7 14-7z"/><path id="Line-3" fill="#C06334" fill-rule="nonzero" d="M80 326v6h247v2H80v6l-14-7 14-7z"/></g></g></svg> \ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" width="633" height="411" viewBox="0 0 633 411"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="network" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="xhr-another-domain.svg"><path id="Rectangle-227" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M1 16h128v64H1z"/><text id="JavaScript" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="35" y="50">JavaScript</tspan></text><path id="Rectangle-228" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M261 16h128v64H261z"/><text id="Browser" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="287" y="50">Navegador</tspan></text><path id="Rectangle-229" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M504 16h128v64H504z"/><text id="Server" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="537.5" y="50">Servidor</tspan></text><path id="Line" stroke="#7E7C7B" stroke-linecap="square" d="M66 81v320M326 81v320M569 81v320"/><path id="Line" fill="#C06334" fill-rule="nonzero" d="M312 133l14 7-14 7v-6H66v-2h246v-6z"/><text id="Origin:-https://java" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="354.313" y="205">Origin: https://javascript.info</tspan></text><text id="HTTP-request" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="398.515" y="174">Solicitud HTTP</tspan></text><text id="fetch()" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="171" y="131">fetch()</tspan></text><text id="HTTP-response" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="394.919" y="250">Respuesta HTTP</tspan></text><text id="Access-Control-Allow" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="347" y="287">Access-Control-Allow-Origin: * </tspan> <tspan x="364.141" y="305">(or https://javascript.info)</tspan></text><text id="if-the-header-allows" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="55.5" y="325">si el encabezado lo permite, entonces éxito</tspan></text><text id="otherwise-fail" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="128" y="353">de lo contrario fallo</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M554 180l14 7-14 7v-6H328v-2h226v-6z"/><path id="Line-2" fill="#C06334" fill-rule="nonzero" d="M340 258v6h228v2H340v6l-14-7 14-7z"/><path id="Line-3" fill="#C06334" fill-rule="nonzero" d="M80 326v6h247v2H80v6l-14-7 14-7z"/></g></g></svg> \ No newline at end of file diff --git a/5-network/05-fetch-crossorigin/xhr-preflight.svg b/5-network/05-fetch-crossorigin/xhr-preflight.svg index 049572cee..5cb3b5651 100644 --- a/5-network/05-fetch-crossorigin/xhr-preflight.svg +++ b/5-network/05-fetch-crossorigin/xhr-preflight.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="620" height="633" viewBox="0 0 620 633"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="network" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="xhr-preflight.svg"><path id="Rectangle-227" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M1 16h128v64H1z"/><text id="JavaScript" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="33" y="50">JavaScript</tspan></text><path id="Rectangle-228" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M240 16h128v64H240z"/><text id="Browser" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="278" y="48">Browser</tspan></text><path id="Rectangle-229" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M491 16h128v64H491z"/><text id="Server" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="534" y="50">Server</tspan></text><path id="Line" stroke="#7E7C7B" stroke-linecap="square" d="M66 82v520M305 82v520M556 82v520"/><path id="Line" fill="#C06334" fill-rule="nonzero" d="M291 133l14 7-14 7-.001-6H67v-2h223.999l.001-6z"/><text id="fetch()" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="166" y="131">fetch()</tspan></text><text id="OPTIONS" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="400" y="173">OPTIONS</tspan></text><text id="Origin-Access-Contro" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="406.721" y="198">Origin</tspan> <tspan x="321.866" y="216">Access-Control-Request-Method</tspan> <tspan x="320" y="234">Access-Control-Request-Headers</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M319 285.5v6h237v2H319v6l-14-7 14-7z"/><text id="200-OK" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="407" y="285">200 OK</tspan></text><path id="Line-4" fill="#C06334" fill-rule="nonzero" d="M542 173l14 7-14 7-.001-6H305v-2h236.999l.001-6z"/><path id="Line-2" fill="#C06334" fill-rule="nonzero" d="M319 495v6h237v2H319v6l-14-7 14-7z"/><path id="Line-3" fill="#C06334" fill-rule="nonzero" d="M90.5 544.5v8h214v3h-214v8l-19-9.5 19-9.5z"/><text id="Access-Control-Allow" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="336" y="522">Access-Control-Allow-Origin</tspan></text><text id="Main-HTTP-response" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="355" y="495">Main HTTP-response</tspan></text><text id="otherwise-error" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="130" y="570">otherwise error</tspan></text><text id="if-allowed:-success," fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="115.5" y="545">if allowed: success,</tspan></text><path id="Line-5" fill="#C06334" fill-rule="nonzero" d="M542 429l14 7-14 7-.001-6H304v-2h237.999l.001-6z"/><text id="Origin" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="405" y="454">Origin</tspan></text><text id="Main-HTTP-request" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="360" y="429">Main HTTP-request</tspan></text><text id="preflight" fill="#7E7C7B" font-family="OpenSans-Regular, Open Sans" font-size="22" font-weight="normal"><tspan x="210.145" y="186">preflight</tspan></text><text id="if-allowed" fill="#7E7C7B" font-family="OpenSans-Regular, Open Sans" font-size="22" font-weight="normal"><tspan x="198.338" y="441">if allowed</tspan></text><text id="1" fill="#181717" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal"><tspan x="570.139" y="186">1</tspan></text><text id="2" fill="#181717" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal"><tspan x="570.139" y="302">2</tspan></text><text id="3" fill="#181717" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal"><tspan x="570.139" y="443">3</tspan></text><text id="4" fill="#181717" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal"><tspan x="570.139" y="509">4</tspan></text><text id="Access-Control-Allow" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"> <tspan x="340.89" y="311.33">Access-Control-Allow-Origin</tspan> <tspan x="332" y="329.33">Access-Control-Allow-Methods</tspan> <tspan x="333.473" y="347.33">Access-Control-Allow-Headers</tspan> <tspan x="352.443" y="365.33">Access-Control-Max-Age</tspan></text></g></g></svg> \ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" width="620" height="633" viewBox="0 0 620 633"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="network" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="xhr-preflight.svg"><path id="Rectangle-227" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M1 16h128v64H1z"/><text id="JavaScript" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="33" y="50">JavaScript</tspan></text><path id="Rectangle-228" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M240 16h128v64H240z"/><text id="Browser" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="269" y="48">Navegador</tspan></text><path id="Rectangle-229" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M491 16h128v64H491z"/><text id="Server" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="527.5" y="50">Servidor</tspan></text><path id="Line" stroke="#7E7C7B" stroke-linecap="square" d="M66 82v520M305 82v520M556 82v520"/><path id="Line" fill="#C06334" fill-rule="nonzero" d="M291 133l14 7-14 7-.001-6H67v-2h223.999l.001-6z"/><text id="fetch()" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="166" y="131">fetch()</tspan></text><text id="OPTIONS" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="400" y="173">OPTIONS</tspan></text><text id="Origin-Access-Contro" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="406.721" y="198">Origin</tspan> <tspan x="321.866" y="216">Access-Control-Request-Method</tspan> <tspan x="320" y="234">Access-Control-Request-Headers</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M319 285.5v6h237v2H319v6l-14-7 14-7z"/><text id="200-OK" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="407" y="285">200 OK</tspan></text><path id="Line-4" fill="#C06334" fill-rule="nonzero" d="M542 173l14 7-14 7-.001-6H305v-2h236.999l.001-6z"/><path id="Line-2" fill="#C06334" fill-rule="nonzero" d="M319 495v6h237v2H319v6l-14-7 14-7z"/><path id="Line-3" fill="#C06334" fill-rule="nonzero" d="M90.5 544.5v8h214v3h-214v8l-19-9.5 19-9.5z"/><text id="Access-Control-Allow" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="336" y="522">Access-Control-Allow-Origin</tspan></text><text id="Main-HTTP-response" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="340.5" y="495">Principal respuesta HTTP</tspan></text><text id="otherwise-error" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="114" y="570">de lo contrario error</tspan></text><text id="if-allowed:-success," fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="101.5" y="545">si está permitido: éxito,</tspan></text><path id="Line-5" fill="#C06334" fill-rule="nonzero" d="M542 429l14 7-14 7-.001-6H304v-2h237.999l.001-6z"/><text id="Origin" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="405" y="454">Origin</tspan></text><text id="Main-HTTP-request" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="345.5" y="429">Principal solicitud HTTP</tspan></text><text id="preflight" fill="#7E7C7B" font-family="OpenSans-Regular, Open Sans" font-size="22" font-weight="normal"><tspan x="70.14500000000001" y="186">antes del lanzamiento</tspan></text><text id="if-allowed" fill="#7E7C7B" font-family="OpenSans-Regular, Open Sans" font-size="22" font-weight="normal"><tspan x="144.338" y="441">si es permitido</tspan></text><text id="1" fill="#181717" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal"><tspan x="570.139" y="186">1</tspan></text><text id="2" fill="#181717" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal"><tspan x="570.139" y="302">2</tspan></text><text id="3" fill="#181717" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal"><tspan x="570.139" y="443">3</tspan></text><text id="4" fill="#181717" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal"><tspan x="570.139" y="509">4</tspan></text><text id="Access-Control-Allow" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"> <tspan x="340.917" y="308.33">Access-Control-Allow-Origin</tspan> <tspan x="335.366" y="326.33">Access-Control-Allow-Method</tspan> <tspan x="333.5" y="344.33">Access-Control-Allow-Headers</tspan> <tspan x="352.47" y="362.33">Access-Control-Max-Age</tspan></text></g></g></svg> \ No newline at end of file diff --git a/5-network/06-fetch-api/article.md b/5-network/06-fetch-api/article.md index 5f55c78ef..7f5af403c 100644 --- a/5-network/06-fetch-api/article.md +++ b/5-network/06-fetch-api/article.md @@ -1,119 +1,119 @@ # Fetch API -So far, we know quite a bit about `fetch`. +Hasta ahora, sabemos bastante sobre `fetch`. -Let's see the rest of API, to cover all its abilities. +Veamos el resto de API, para cubrir todas sus capacidades. ```smart -Please note: most of these options are used rarely. You may skip this chapter and still use `fetch` well. +Ten en cuenta: la mayoría de estas opciones se utilizan con poca frecuencia. Puedes saltarte este capítulo y seguir utilizando bien `fetch`. -Still, it's good to know what `fetch` can do, so if the need arises, you can return and read the details. +Aún así, es bueno saber lo que puede hacer `fetch`, por lo que si surge la necesidad, puedes regresar y leer los detalles. ``` -Here's the full list of all possible `fetch` options with their default values (alternatives in comments): +Aquí está la lista completa de todas las posibles opciones de `fetch` con sus valores predeterminados (alternativas en los comentarios): ```js let promise = fetch(url, { method: "GET", // POST, PUT, DELETE, etc. headers: { - // the content type header value is usually auto-set - // depending on the request body + // el valor del encabezado Content-Type generalmente se establece automáticamente + // dependiendo del cuerpo de la solicitud "Content-Type": "text/plain;charset=UTF-8" }, - body: undefined, // string, FormData, Blob, BufferSource, or URLSearchParams - referrer: "about:client", // or "" to send no Referer header, - // or an url from the current origin + body: undefined, // string, FormData, Blob, BufferSource, o URLSearchParams + referrer: "about:client", // o "" para no enviar encabezado de Referrer, + // o una URL del origen actual referrerPolicy: "strict-origin-when-cross-origin", // no-referrer-when-downgrade, no-referrer, origin, same-origin... mode: "cors", // same-origin, no-cors credentials: "same-origin", // omit, include - cache: "default", // no-store, reload, no-cache, force-cache, or only-if-cached + cache: "default", // no-store, reload, no-cache, force-cache, o only-if-cached redirect: "follow", // manual, error - integrity: "", // a hash, like "sha256-abcdef1234567890" + integrity: "", // un hash, como "sha256-abcdef1234567890" keepalive: false, // true - signal: undefined, // AbortController to abort request + signal: undefined, // AbortController para cancelar la solicitud window: window // null }); ``` -An impressive list, right? +Una lista impresionante, ¿verdad? -We fully covered `method`, `headers` and `body` in the chapter <info:fetch>. +Cubrimos completamente `method`, `headers` y `body` en el capítulo <info:fetch>. -The `signal` option is covered in <info:fetch-abort>. +La opción `signal` está cubierta en <info:fetch-abort>. -Now let's explore the remaining capabilities. +Ahora exploremos el resto de capacidades. ## referrer, referrerPolicy -These options govern how `fetch` sets the HTTP `Referer` header. +Estas opciones gobiernan cómo `fetch` establece el encabezado HTTP `Referer`. -Usually that header is set automatically and contains the url of the page that made the request. In most scenarios, it's not important at all, sometimes, for security purposes, it makes sense to remove or shorten it. +Por lo general, ese encabezado se establece automáticamente y contiene la URL de la página que realizó la solicitud. En la mayoría de los escenarios, no es importante en absoluto, a veces, por motivos de seguridad, tiene sentido eliminarlo o acortarlo. -**The `referrer` option allows to set any `Referer` (within the current origin) or remove it.** +**La opción `referrer` permite establecer cualquier `Referer` (dentro del origen actual) o eliminarlo.** -To send no referrer, set an empty string: +Para no enviar ningún referrer, establece un string vacío: ```js fetch('/page', { *!* - referrer: "" // no Referer header + referrer: "" // sin encabezado Referrer */!* }); ``` -To set another url within the current origin: +Para establecer otra URL dentro del origen actual: ```js fetch('/page', { - // assuming we're on https://javascript.info - // we can set any Referer header, but only within the current origin + // asumiendo que estamos en https://javascript.info + // podemos establecer cualquier encabezado Referer, pero solo dentro del origen actual *!* referrer: "https://javascript.info/anotherpage" */!* }); ``` -**The `referrerPolicy` option sets general rules for `Referer`.** +**La opción `referrerPolicy` establece reglas generales para `Referer`.** -Requests are split into 3 types: +Las solicitudes se dividen en 3 tipos: -1. Request to the same origin. -2. Request to another origin. -3. Request from HTTPS to HTTP (from safe to unsafe protocol). +1. Solicitud al mismo origen. +2. Solicitud a otro origen. +3. Solicitud de HTTPS a HTTP (de protocolo seguro a no seguro). -Unlike the `referrer` option that allows to set the exact `Referer` value, `referrerPolicy` tells the browser general rules for each request type. +A diferencia de la opción `referrer` que permite establecer el valor exacto de `Referer`, `referrerPolicy` indica al navegador las reglas generales para cada tipo de solicitud. -Possible values are described in the [Referrer Policy specification](https://w3c.github.io/webappsec-referrer-policy/): +Los valores posibles se describen en la [Especificación de la política Referrer](https://w3c.github.io/webappsec-referrer-policy/): -- **`"strict-origin-when-cross-origin"`** -- the default value: for same-origin send the full `Referer`, for cross-origin send only the origin, unless it's HTTPS→HTTP request, then send nothing. -- **`"no-referrer-when-downgrade"`** -- full `Referer` is always sent, unless we send a request from HTTPS to HTTP (to the less secure protocol). -- **`"no-referrer"`** -- never send `Referer`. -- **`"origin"`** -- only send the origin in `Referer`, not the full page URL, e.g. only `http://site.com` instead of `http://site.com/path`. -- **`"origin-when-cross-origin"`** -- send the full `Referer` to the same origin, but only the origin part for cross-origin requests (as above). -- **`"same-origin"`** -- send the full `Referer` to the same origin, but no `Referer` for cross-origin requests. -- **`"strict-origin"`** -- send only the origin, not the `Referer` for HTTPS→HTTP requests. -- **`"unsafe-url"`** -- always send the full url in `Referer`, even for HTTPS→HTTP requests. +- **`"strict-origin-when-cross-origin"`** -- El valor predeterminado. Para el mismo origen, envía el `Referer` completo. Para el envío cross-origin envía solo el origen, a menos que sea una solicitud HTTPS→HTTP, entonces no envía nada. +- **`"no-referrer-when-downgrade"`** -- el `Referer` completo se envía siempre, a menos que enviemos una solicitud de HTTPS a HTTP (a un protocolo menos seguro). +- **`"no-referrer"`** -- nunca envía `Referer`. +- **`"origin"`** -- solo envía el origen en `Referer`, no la URL de la página completa. Por ejemplo, solo `http://site.com` en lugar de `http://site.com/path`. +- **`"origin-when-cross-origin"`** -- envía el `Referrer` completo al mismo origen, pero solo la parte de origen para solicitudes cross-origin (como se indica arriba). +- **`"same-origin"`** -- envía un `Referer` completo al mismo origen, pero no un `Referer` para solicitudes cross-origin. +- **`"strict-origin"`** -- envía solo el origen, no envía `Referer` para solicitudes HTTPS→HTTP. +- **`"unsafe-url"`** -- envía siempre la URL completa en `Referer`, incluso para solicitudes HTTPS→HTTP. -Here's a table with all combinations: +Aquí hay una tabla con todas las combinaciones: -| Value | To same origin | To another origin | HTTPS→HTTP | +| Valor | Al mismo origen | A otro origen | HTTPS→HTTP | |-------|----------------|-------------------|------------| | `"no-referrer"` | - | - | - | -| `"no-referrer-when-downgrade"` | full | full | - | -| `"origin"` | origin | origin | origin | -| `"origin-when-cross-origin"` | full | origin | origin | -| `"same-origin"` | full | - | - | -| `"strict-origin"` | origin | origin | - | -| `"strict-origin-when-cross-origin"` or `""` (default) | full | origin | - | -| `"unsafe-url"` | full | full | full | +| `"no-referrer-when-downgrade"` | completo | completo | - | +| `"origin"` | origen | origen | origen | +| `"origin-when-cross-origin"` | completo | origen | origen | +| `"same-origin"` | completo | - | - | +| `"strict-origin"` | origen | origen | - | +| `"strict-origin-when-cross-origin"` or `""` (predeterminado) | completo | origen | - | +| `"unsafe-url"` | completo | completo | completo | -Let's say we have an admin zone with a URL structure that shouldn't be known from outside of the site. +Digamos que tenemos una zona de administración con una estructura de URL que no debería conocerse desde fuera del sitio. -If we send a `fetch`, then by default it always sends the `Referer` header with the full url of our page (except when we request from HTTPS to HTTP, then no `Referer`). +Si enviamos un `fetch`, entonces de forma predeterminada siempre envía el encabezado `Referer` con la URL completa de nuestra página (excepto cuando solicitamos de HTTPS a HTTP, entonces no hay `Referer`). -E.g. `Referer: https://javascript.info/admin/secret/paths`. +Por ejemplo, `Referer: https://javascript.info/admin/secret/paths`. -If we'd like other websites know only the origin part, not the URL-path, we can set the option: +Si queremos que otros sitios web solo conozcan la parte del origen, no la ruta de la URL, podemos configurar la opción: ```js fetch('https://another.com/page', { @@ -122,66 +122,66 @@ fetch('https://another.com/page', { }); ``` -We can put it to all `fetch` calls, maybe integrate into JavaScript library of our project that does all requests and uses `fetch` inside. +Podemos ponerlo en todas las llamadas `fetch`, tal vez integrarlo en la biblioteca JavaScript de nuestro proyecto que hace todas las solicitudes y que usa `fetch` por dentro. -Its only difference compared to the default behavior is that for requests to another origin `fetch` sends only the origin part of the URL (e.g. `https://javascript.info`, without path). For requests to our origin we still get the full `Referer` (maybe useful for debugging purposes). +Su única diferencia en comparación con el comportamiento predeterminado es que para las solicitudes a otro origen, `fetch` envía solo la parte de origen de la URL (por ejemplo, `https://javascript.info`, sin ruta). Para las solicitudes a nuestro origen, todavía obtenemos el `Referer` completo (quizás útil para fines de depuración). -```smart header="Referrer policy is not only for `fetch`" -Referrer policy, described in the [specification](https://w3c.github.io/webappsec-referrer-policy/), is not just for `fetch`, but more global. +```smart header="La política Referrer no es solo para `fetch`" +La política Referrer, descrita en la [especificación](https://w3c.github.io/webappsec-referrer-policy/), no es solo para `fetch`, sino más global. -In particular, it's possible to set the default policy for the whole page using the `Referrer-Policy` HTTP header, or per-link, with `<a rel="noreferrer">`. +En particular, es posible establecer la política predeterminada para toda la página utilizando el encabezado HTTP `Referrer-Policy`, o por enlace, con `<a rel="noreferrer">`. ``` ## mode -The `mode` option is a safe-guard that prevents occasional cross-origin requests: +La opción `mode` es una protección que evita solicitudes cross-origin ocasionales: -- **`"cors"`** -- the default, cross-origin requests are allowed, as described in <info:fetch-crossorigin>, -- **`"same-origin"`** -- cross-origin requests are forbidden, -- **`"no-cors"`** -- only safe cross-origin requests are allowed. +- **`"cors"`** -- por defecto, se permiten las solicitudes cross-origin predeterminadas, como se describe en <info:fetch-crossorigin>, +- **`"same-origin"`** -- las solicitudes cross-origin están prohibidas, +- **`"no-cors"`** -- solo se permiten solicitudes cross-origin seguras. -This option may be useful when the URL for `fetch` comes from a 3rd-party, and we want a "power off switch" to limit cross-origin capabilities. +Esta opción puede ser útil cuando la URL de `fetch` proviene de un tercero y queremos un "interruptor de apagado" para limitar las capacidades cross-origin. ## credentials -The `credentials` option specifies whether `fetch` should send cookies and HTTP-Authorization headers with the request. +La opción `credentials` especifica si `fetch` debe enviar cookies y encabezados de autorización HTTP con la solicitud. -- **`"same-origin"`** -- the default, don't send for cross-origin requests, -- **`"include"`** -- always send, requires `Access-Control-Allow-Credentials` from cross-origin server in order for JavaScript to access the response, that was covered in the chapter <info:fetch-crossorigin>, -- **`"omit"`** -- never send, even for same-origin requests. +- **`"same-origin"`** -- el valor predeterminado, no enviar solicitudes cross-origin, +- **`"include"`** -- enviar siempre, requiere `Access-Control-Allow-Credentials` del servidor cross-origin para que JavaScript acceda a la respuesta, que se cubrió en el capítulo <info:fetch-crossorigin>, +- **`"omit"`** -- nunca enviar, incluso para solicitudes del mismo origen. ## cache -By default, `fetch` requests make use of standard HTTP-caching. That is, it respects the `Expires` and `Cache-Control` headers, sends `If-Modified-Since` and so on. Just like regular HTTP-requests do. +De forma predeterminada, las solicitudes `fetch` utilizan el almacenamiento en caché HTTP estándar. Es decir, respeta los encabezados `Expires`, `Cache-Control`, envía `If-Modified-Since`, y así sucesivamente. Al igual que lo hacen las solicitudes HTTP habituales. -The `cache` options allows to ignore HTTP-cache or fine-tune its usage: +Las opciones de `cache` permiten ignorar el caché HTTP o ajustar su uso: -- **`"default"`** -- `fetch` uses standard HTTP-cache rules and headers, -- **`"no-store"`** -- totally ignore HTTP-cache, this mode becomes the default if we set a header `If-Modified-Since`, `If-None-Match`, `If-Unmodified-Since`, `If-Match`, or `If-Range`, -- **`"reload"`** -- don't take the result from HTTP-cache (if any), but populate the cache with the response (if the response headers permit this action), -- **`"no-cache"`** -- create a conditional request if there is a cached response, and a normal request otherwise. Populate HTTP-cache with the response, -- **`"force-cache"`** -- use a response from HTTP-cache, even if it's stale. If there's no response in HTTP-cache, make a regular HTTP-request, behave normally, -- **`"only-if-cached"`** -- use a response from HTTP-cache, even if it's stale. If there's no response in HTTP-cache, then error. Only works when `mode` is `"same-origin"`. +- **`"default"`** -- `fetch` utiliza reglas y encabezados de caché HTTP estándar, +- **`"no-store"`** -- ignoramos por completo el caché HTTP, este modo se convierte en el predeterminado si configuramos un encabezado `If-Modified-Since`, `If-None-Match`, `If-Unmodified-Since`, `If-Match` o `If-Range`, +- **`"reload"`** -- no toma el resultado del caché HTTP (si corresponde), pero completa el caché con la respuesta (si los encabezados de respuesta lo permiten), +- **`"no-cache"`** -- crea una solicitud condicional si hay una respuesta en caché y una solicitud normal en caso contrario. Llena el caché HTTP con la respuesta, +- **`"force-cache"`** -- usa una respuesta del caché HTTP, incluso si está obsoleta. Si no hay respuesta en el caché HTTP, hace una solicitud HTTP regular, se comporta normalmente, +- **`"only-if-cached"`** -- usa una respuesta del caché HTTP, incluso si está obsoleta. Si no hay respuesta en el caché HTTP, entonces envía un error. Solo funciona cuando `mode` es `"same-origin"`. ## redirect -Normally, `fetch` transparently follows HTTP-redirects, like 301, 302 etc. +Normalmente, `fetch` sigue de forma transparente las redirecciones HTTP, como 301, 302, etc. -The `redirect` option allows to change that: +La opción `redirect` permite cambiar eso: -- **`"follow"`** -- the default, follow HTTP-redirects, -- **`"error"`** -- error in case of HTTP-redirect, -- **`"manual"`** -- allows to process HTTP-redirects manually. In case of redirect, we'll get a special response object, with `response.type="opaqueredirect"` and zeroed/empty status and most other properies. +- **`"follow"`** -- el predeterminado, sigue las redirecciones HTTP, +- **`"error"`** -- error en caso de redireccionamiento HTTP, +- **`"manual"`** -- permite procesar redireccionamiento HTTP manualmente. En caso de redireccionamiento obtendremos un objeto response especial, con `response.type="opaqueredirect"` y cero o vacío en la mayor parte de las demás propiedades. ## integrity -The `integrity` option allows to check if the response matches the known-ahead checksum. +La opción `integrity` permite comprobar si la respuesta coincide con el known-ahead checksum. -As described in the [specification](https://w3c.github.io/webappsec-subresource-integrity/), supported hash-functions are SHA-256, SHA-384, and SHA-512, there might be others depending on the browser. +Como se describe en la [especificación](https://w3c.github.io/webappsec-subresource-integrity/), las funciones hash admitidas son SHA-256, SHA-384 y SHA-512. Puede haber otras dependiendo de un navegador. -For example, we're downloading a file, and we know that its SHA-256 checksum is "abcdef" (a real checksum is longer, of course). +Por ejemplo, estamos descargando un archivo y sabemos que su checksum SHA-256 es "abcdef" (un checksum real es más largo, por supuesto). -We can put it in the `integrity` option, like this: +Lo podemos poner en la opción `integrity`, así: ```js fetch('http://site.com/file', { @@ -189,17 +189,17 @@ fetch('http://site.com/file', { }); ``` -Then `fetch` will calculate SHA-256 on its own and compare it with our string. In case of a mismatch, an error is triggered. +Luego, `fetch` calculará SHA-256 por sí solo y lo comparará con nuestro string. En caso de discrepancia, se activa un error. ## keepalive -The `keepalive` option indicates that the request may "outlive" the webpage that initiated it. +La opción `keepalive` indica que la solicitud puede "vivir más allá" de la página web que la inició. -For example, we gather statistics on how the current visitor uses our page (mouse clicks, page fragments he views), to analyze and improve the user experience. +Por ejemplo, recopilamos estadísticas sobre cómo el visitante actual usa nuestra página (clics del mouse, fragmentos de página que ve), para analizar y mejorar la experiencia del usuario. -When the visitor leaves our page -- we'd like to save the data to our server. +Cuando el visitante abandona nuestra página, nos gustaría guardar los datos en nuestro servidor. -We can use the `window.onunload` event for that: +Podemos usar el evento `window.onunload` para eso: ```js run window.onunload = function() { @@ -213,12 +213,13 @@ window.onunload = function() { }; ``` -Normally, when a document is unloaded, all associated network requests are aborted. But the `keepalive` option tells the browser to perform the request in the background, even after it leaves the page. So this option is essential for our request to succeed. +Normalmente, cuando se descarga un documento, se cancelan todas las solicitudes de red asociadas. Pero la opción `keepalive` le dice al navegador que realice la solicitud en segundo plano, incluso después de salir de la página. Por tanto, esta opción es fundamental para que nuestra solicitud tenga éxito. -It has a few limitations: -- We can't send megabytes: the body limit for `keepalive` requests is 64KB. - - If we need to gather a lot of statistics about the visit, we should send it out regularly in packets, so that there won't be a lot left for the last `onunload` request. - - This limit applies to all `keepalive` requests together. In other words, we can perform multiple `keepalive` requests in parallel, but the sum of their body lengths should not exceed 64KB. -- We can't handle the server response if the document is unloaded. So in our example `fetch` will succeed due to `keepalive`, but subsequent functions won't work. - - In most cases, such as sending out statistics, it's not a problem, as the server just accepts the data and usually sends an empty response to such requests. +Tiene algunas limitaciones: + +- No podemos enviar megabytes: el límite de cuerpo para las solicitudes `keepalive` es de 64 KB. + - Si necesitamos recopilar muchas estadísticas sobre la visita, deberíamos enviarlas regularmente en paquetes, de modo que no quede mucho para la última solicitud `onunload`. + - Este límite se aplica a todas las solicitudes `keepalive` juntas. En otras palabras, podemos realizar múltiples solicitudes `keepalive` en paralelo, pero la suma de las longitudes de sus cuerpos no debe exceder los 64 KB. +- No podemos manejar la respuesta del servidor si el documento no está cargado. Entonces, en nuestro ejemplo, `fetch` tendrá éxito debido a `keepalive`, pero las funciones posteriores no funcionarán. + - En la mayoría de los casos, como enviar estadísticas, no es un problema, ya que el servidor simplemente acepta los datos y generalmente envía una respuesta vacía a tales solicitudes. diff --git a/5-network/06-fetch-api/post.view/server.js b/5-network/06-fetch-api/post.view/server.js index 5e182a2ba..53bf01401 100644 --- a/5-network/06-fetch-api/post.view/server.js +++ b/5-network/06-fetch-api/post.view/server.js @@ -17,7 +17,7 @@ function accept(req, res) { chunks.push(data); length += data.length; - // Too much POST data, kill the connection! + // Demasiados datos POST, ¡cancela la conexión! if (length > 1e6) { request.connection.destroy(); } diff --git a/5-network/07-url/article.md b/5-network/07-url/article.md index 58b3ab1ae..949b8ea67 100644 --- a/5-network/07-url/article.md +++ b/5-network/07-url/article.md @@ -1,28 +1,28 @@ -# URL objects +# Objetos URL -The built-in [URL](https://url.spec.whatwg.org/#api) class provides a convenient interface for creating and parsing URLs. +La clase [URL](https://url.spec.whatwg.org/#api) incorporada brinda una interfaz conveniente para crear y analizar URLs. -There are no networking methods that require exactly a `URL` object, strings are good enough. So technically we don't have to use `URL`. But sometimes it can be really helpful. +No hay métodos de networking que requieran exactamente un objeto `URL`, los strings son suficientemente buenos para eso. Así que técnicamente no tenemos que usar `URL`. Pero a veces puede ser realmente útil. -## Creating a URL +## Creando una URL -The syntax to create a new `URL` object: +La sintaxis para crear un nuevo objeto `URL` es: ```js new URL(url, [base]) ``` -- **`url`** -- the full URL or only path (if base is set, see below), -- **`base`** -- an optional base URL: if set and `url` argument has only path, then the URL is generated relative to `base`. +- **`url`** -- La URL completa o ruta única (si se establece base, mira a continuación), +- **`base`** - una URL base opcional: si se establece y el argumento `url` solo tiene una ruta, entonces la URL se genera relativa a `base`. -For example: +Por ejemplo: ```js let url = new URL('https://javascript.info/profile/admin'); ``` -These two URLs are same: +Estas dos URLs son las mismas: ```js run let url1 = new URL('https://javascript.info/profile/admin'); @@ -32,7 +32,7 @@ alert(url1); // https://javascript.info/profile/admin alert(url2); // https://javascript.info/profile/admin ``` -We can easily create a new URL based on the path relative to an existing URL: +Fácilmente podemos crear una nueva URL basada en la ruta relativa a una URL existente: ```js run let url = new URL('https://javascript.info/profile/admin'); @@ -41,7 +41,7 @@ let newUrl = new URL('tester', url); alert(newUrl); // https://javascript.info/profile/tester ``` -The `URL` object immediately allows us to access its components, so it's a nice way to parse the url, e.g.: +El objeto `URL` inmediatamente nos permite acceder a sus componentes, por lo que es una buena manera de analizar la url, por ej.: ```js run let url = new URL('https://javascript.info/url'); @@ -51,79 +51,79 @@ alert(url.host); // javascript.info alert(url.pathname); // /url ``` -Here's the cheatsheet for URL components: +Aquí está la hoja de trucos para los componentes URL: ![](url-object.svg) -- `href` is the full url, same as `url.toString()` -- `protocol` ends with the colon character `:` -- `search` - a string of parameters, starts with the question mark `?` -- `hash` starts with the hash character `#` -- there may be also `user` and `password` properties if HTTP authentication is present: `http://login:password@site.com` (not painted above, rarely used). +- `href` es la url completa, igual que `url.toString()` +- `protocol` acaba con el carácter dos puntos `:` +- `search` - un string de parámetros, comienza con el signo de interrogación `?` +- `hash` comienza con el carácter de hash `#` +- También puede haber propiedades `user` y `password` si la autenticación HTTP esta presente: `http://login:password@site.com` (no mostrados arriba, raramente usados) -```smart header="We can pass `URL` objects to networking (and most other) methods instead of a string" -We can use a `URL` object in `fetch` or `XMLHttpRequest`, almost everywhere where a URL-string is expected. +```smart header="Podemos pasar objetos `URL` a métodos de red (y la mayoría de los demás) en lugar de un string" +Podemos usar un objeto `URL` en `fetch` o `XMLHttpRequest`, casi en todas partes donde se espera un URL-string. -Generally, the `URL` object can be passed to any method instead of a string, as most methods will perform the string conversion, that turns a `URL` object into a string with full URL. +Generalmente, un objeto `URL` puede pasarse a cualquier método en lugar de un string, ya que la mayoría de métodos llevarán a cabo la conversión del string, eso convierte un objeto `URL` en un string con URL completa. ``` -## SearchParams "?..." +## Parámetros de búsqueda "?..." -Let's say we want to create a url with given search params, for instance, `https://google.com/search?query=JavaScript`. +Digamos que queremos crear una url con determinados parámetros de búsqueda, por ejemplo, `https://google.com/search?query=JavaScript`. -We can provide them in the URL string: +Podemos proporcionarlos en el string URL: ```js new URL('https://google.com/search?query=JavaScript') ``` -...But parameters need to be encoded if they contain spaces, non-latin letters, etc (more about that below). +...Pero los parámetros necesitan estar codificados si contienen espacios, letras no latinas, entre otros (Más sobre eso debajo). -So there's a URL property for that: `url.searchParams`, an object of type [URLSearchParams](https://url.spec.whatwg.org/#urlsearchparams). +Por lo que existe una propiedad URL para eso: `url.searchParams`, un objeto de tipo [URLSearchParams](https://url.spec.whatwg.org/#urlsearchparams). -It provides convenient methods for search parameters: +Esta proporciona métodos convenientes para los parámetros de búsqueda: -- **`append(name, value)`** -- add the parameter by `name`, -- **`delete(name)`** -- remove the parameter by `name`, -- **`get(name)`** -- get the parameter by `name`, -- **`getAll(name)`** -- get all parameters with the same `name` (that's possible, e.g. `?user=John&user=Pete`), -- **`has(name)`** -- check for the existence of the parameter by `name`, -- **`set(name, value)`** -- set/replace the parameter, -- **`sort()`** -- sort parameters by name, rarely needed, -- ...and it's also iterable, similar to `Map`. +- **`append(name, value)`** -- añade el parámetro por `name`, +- **`delete(name)`** -- elimina el parámetro por `name`, +- **`get(name)`** -- obtiene el parámetro por `name`, +- **`getAll(name)`** -- obtiene todos los parámetros con el mismo `name` (Eso es posible, por ej. `?user=John&user=Pete`), +- **`has(name)`** -- comprueba la existencia del parámetro por `name`, +- **`set(name, value)`** -- establece/reemplaza el parámetro, +- **`sort()`** -- ordena parámetros por `name`, raramente necesitado, +- ...y además es iterable, similar a `Map`. -An example with parameters that contain spaces and punctuation marks: +Un ejemplo con parámetros que contienen espacios y signos de puntuación: ```js run let url = new URL('https://google.com/search'); -url.searchParams.set('q', 'test me!'); // added parameter with a space and ! +url.searchParams.set('q', 'test me!'); // Parámetro añadido con un espacio y ! alert(url); // https://google.com/search?q=test+me%21 -url.searchParams.set('tbs', 'qdr:y'); // added parameter with a colon : +url.searchParams.set('tbs', 'qdr:y'); // Parámetro añadido con dos puntos : -// parameters are automatically encoded +// Los parámetros son automáticamente codificados alert(url); // https://google.com/search?q=test+me%21&tbs=qdr%3Ay -// iterate over search parameters (decoded) +// Iterar sobre los parametros de búsqueda (Decodificados) for(let [name, value] of url.searchParams) { alert(`${name}=${value}`); // q=test me!, then tbs=qdr:y } ``` -## Encoding +## Codificación -There's a standard [RFC3986](https://tools.ietf.org/html/rfc3986) that defines which characters are allowed in URLs and which are not. +Existe un estándar [RFC3986](https://tools.ietf.org/html/rfc3986) que define cuales caracteres son permitidos en URLs y cuales no. -Those that are not allowed, must be encoded, for instance non-latin letters and spaces - replaced with their UTF-8 codes, prefixed by `%`, such as `%20` (a space can be encoded by `+`, for historical reasons, but that's an exception). +Esos que no son permitidos, deben ser codificados, por ejemplo letras no latinas y espacios - reemplazados con sus códigos UTF-8, con el prefijo `%`, tal como `%20` (un espacio puede ser codificado con `+`, por razones históricas, pero esa es una excepción). -The good news is that `URL` objects handle all that automatically. We just supply all parameters unencoded, and then convert the `URL` to string: +La buena noticia es que los objetos `URL` manejan todo eso automáticamente. Nosotros sólo proporcionamos todos los parámetros sin codificar, y luego convertimos la `URL` a string: ```js run -// using some cyrillic characters for this example +// Usando algunos caracteres cirílicos para este ejemplo let url = new URL('https://ru.wikipedia.org/wiki/Тест'); @@ -131,50 +131,50 @@ url.searchParams.set('key', 'ъ'); alert(url); //https://ru.wikipedia.org/wiki/%D0%A2%D0%B5%D1%81%D1%82?key=%D1%8A ``` -As you can see, both `Тест` in the url path and `ъ` in the parameter are encoded. +Como puedes ver, ambos `Тест` en la ruta url y `ъ` en el parámetro están codificados. -The URL became longer, because each cyrillic letter is represented with two bytes in UTF-8, so there are two `%..` entities. +La URL se alarga, ya que cada letra cirílica es representada con dos bytes en UTF-8, por lo que hay dos entidades `%..`. -### Encoding strings +### Codificando strings -In old times, before `URL` objects appeared, people used strings for URLs. +En los viejos tiempos, antes de que los objetos `URL` aparecieran, la gente usaba strings para las URL. -As of now, `URL` objects are often more convenient, but strings can still be used as well. In many cases using a string makes the code shorter. +A partir de ahora, los objetos `URL` son frecuentemente más convenientes, pero también aún pueden usarse los strings. En muchos casos usando un string se acorta el código. -If we use a string though, we need to encode/decode special characters manually. +Aunque si usamos un string, necesitamos codificar/decodificar caracteres especiales manualmente. -There are built-in functions for that: +Existen funciones incorporadas para eso: -- [encodeURI](mdn:/JavaScript/Reference/Global_Objects/encodeURI) - encodes URL as a whole. -- [decodeURI](mdn:/JavaScript/Reference/Global_Objects/decodeURI) - decodes it back. -- [encodeURIComponent](mdn:/JavaScript/Reference/Global_Objects/encodeURIComponent) - encodes a URL component, such as a search parameter, or a hash, or a pathname. -- [decodeURIComponent](mdn:/JavaScript/Reference/Global_Objects/decodeURIComponent) - decodes it back. +- [encodeURI](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI) - Codifica la URL como un todo. +- [decodeURI](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURI) - La decodifica de vuelta. +- [encodeURIComponent](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent) - Codifica un componente URL, como un parametro de busqueda, un hash, o un pathname. +- [decodeURIComponent](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent) - La decodifica de vuelta. -A natural question is: "What's the difference between `encodeURIComponent` and `encodeURI`? When we should use either?" +Una pregunta natural es: "¿Cuál es la diferencia entre `encodeURIComponent` y `encodeURI`?¿Cuándo deberíamos usar una u otra? -That's easy to understand if we look at the URL, that's split into components in the picture above: +Eso es fácil de entender si miramos a la URL, que está separada en componentes en la imagen de arriba: ``` https://site.com:8080/path/page?p1=v1&p2=v2#hash ``` -As we can see, characters such as `:`, `?`, `=`, `&`, `#` are allowed in URL. +Como podemos ver, caracteres tales como `:`, `?`, `=`, `&`, `#` son admitidos en URL. -...On the other hand, if we look at a single URL component, such as a search parameter, these characters must be encoded, not to break the formatting. +...Por otra parte, si miramos a un único componente URL, como un parámetro de búsqueda, estos caracteres deben estar codificados, para no romper el formateo. -- `encodeURI` encodes only characters that are totally forbidden in URL. -- `encodeURIComponent` encodes same characters, and, in addition to them, characters `#`, `$`, `&`, `+`, `,`, `/`, `:`, `;`, `=`, `?` and `@`. +- `encodeURI` Codifica solo caracteres que están totalmente prohibidos en URL +- `encodeURIComponent` Codifica los mismos caracteres, y, en adición a ellos, los caracteres `#`, `$`, `&`, `+`, `,`, `/`, `:`, `;`, `=`, `?` y `@`. -So, for a whole URL we can use `encodeURI`: +Entonces, para una URL completa podemos usar `encodeURI`: ```js run -// using cyrillic characters in url path +// Usando caracteres cirílicos en el path URL let url = encodeURI('http://site.com/привет'); alert(url); // http://site.com/%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82 ``` -...While for URL parameters we should use `encodeURIComponent` instead: +...Mientras que para parámetros URL deberíamos usar `encodeURIComponent` en su lugar: ```js run let music = encodeURIComponent('Rock&Roll'); @@ -183,7 +183,7 @@ let url = `https://google.com/search?q=${music}`; alert(url); // https://google.com/search?q=Rock%26Roll ``` -Compare it with `encodeURI`: +Compáralo con `encodeURI`: ```js run let music = encodeURI('Rock&Roll'); @@ -192,26 +192,26 @@ let url = `https://google.com/search?q=${music}`; alert(url); // https://google.com/search?q=Rock&Roll ``` -As we can see, `encodeURI` does not encode `&`, as this is a legit character in URL as a whole. +Como podemos ver, `encodeURI` no codifica `&`, ya que este es un carácter legítimo en la URL como un todo. -But we should encode `&` inside a search parameter, otherwise, we get `q=Rock&Roll` - that is actually `q=Rock` plus some obscure parameter `Roll`. Not as intended. +Pero debemos codificar `&` dentro de un parámetro de búsqueda, de otra manera, obtendremos `q=Rock&Roll`- que es realmente `q=Rock` más algún parámetro `Roll` oscuro. No según lo previsto. -So we should use only `encodeURIComponent` for each search parameter, to correctly insert it in the URL string. The safest is to encode both name and value, unless we're absolutely sure that it has only allowed characters. +Así que debemos usar solo `encodeURIComponent`para cada parámetro de búsqueda, para insertarlo correctamente en el string URL. Lo más seguro es codificar tanto nombre como valor, a menos que estemos absolutamente seguros de que solo haya admitido caracteres -````smart header="Encoding difference compared to `URL`" -Classes [URL](https://url.spec.whatwg.org/#url-class) and [URLSearchParams](https://url.spec.whatwg.org/#interface-urlsearchparams) are based on the latest URI specification: [RFC3986](https://tools.ietf.org/html/rfc3986), while `encode*` functions are based on the obsolete version [RFC2396](https://www.ietf.org/rfc/rfc2396.txt). +````smart header="Diferencia de codificación comparado con `URL`" +Las clases [URL](https://url.spec.whatwg.org/#url-class) y [URLSearchParams](https://url.spec.whatwg.org/#interface-urlsearchparams) están basadas en la especificación URI mas reciente: [RFC3986](https://tools.ietf.org/html/rfc3986), mientras que las funciones `encode*` están basadas en la versión obsoleta [RFC2396](https://www.ietf.org/rfc/rfc2396.txt). -There are a few differences, e.g. IPv6 addresses are encoded differently: +Existen algunas diferencias, por ej. las direcciones IPv6 se codifican de otra forma: ```js run -// valid url with IPv6 address +// Url válida con dirección IPv6 let url = 'http://[2607:f8b0:4005:802::1007]/'; alert(encodeURI(url)); // http://%5B2607:f8b0:4005:802::1007%5D/ alert(new URL(url)); // http://[2607:f8b0:4005:802::1007]/ ``` -As we can see, `encodeURI` replaced square brackets `[...]`, that's not correct, the reason is: IPv6 urls did not exist at the time of RFC2396 (August 1998). +Como podemos ver, `encodeURI` reemplazó los corchetes `[...]`, eso es incorrecto, la razón es: las urls IPv6 no existían en el tiempo de RFC2396 (August 1998). -Such cases are rare, `encode*` functions work well most of the time. +Tales casos son raros, las funciones `encode*` mayormente funcionan bien. ```` diff --git a/5-network/08-xmlhttprequest/article.md b/5-network/08-xmlhttprequest/article.md index 43d816cab..4c3997412 100644 --- a/5-network/08-xmlhttprequest/article.md +++ b/5-network/08-xmlhttprequest/article.md @@ -1,160 +1,160 @@ # XMLHttpRequest -`XMLHttpRequest` is a built-in browser object that allows to make HTTP requests in JavaScript. +`XMLHttpRequest` es un objeto nativo del navegador que permite hacer solicitudes HTTP desde JavaScript. -Despite having the word "XML" in its name, it can operate on any data, not only in XML format. We can upload/download files, track progress and much more. +A pesar de tener la palabra "XML" en su nombre, se puede operar sobre cualquier dato, no solo en formato XML. Podemos cargar y descargar archivos, dar seguimiento y mucho más. -Right now, there's another, more modern method `fetch`, that somewhat deprecates `XMLHttpRequest`. +Ahora hay un método más moderno `fetch` que en algún sentido hace obsoleto a `XMLHttpRequest`. -In modern web-development `XMLHttpRequest` is used for three reasons: +En el desarrollo web moderno `XMLHttpRequest` se usa por tres razones: -1. Historical reasons: we need to support existing scripts with `XMLHttpRequest`. -2. We need to support old browsers, and don't want polyfills (e.g. to keep scripts tiny). -3. We need something that `fetch` can't do yet, e.g. to track upload progress. +1. Razones históricas: necesitamos soportar scripts existentes con `XMLHttpRequest`. +2. Necesitamos soportar navegadores viejos, y no queremos `polyfills` (p.ej. para mantener los scripts pequeños). +3. Necesitamos hacer algo que `fetch` no puede todavía, ej. rastrear el progreso de subida. -Does that sound familiar? If yes, then all right, go on with `XMLHttpRequest`. Otherwise, please head on to <info:fetch>. +¿Te suena familiar? Si es así, está bien, adelante con `XMLHttpRequest`. De otra forma, por favor, dirígete a <info:fetch>. -## The basics +## Lo básico -XMLHttpRequest has two modes of operation: synchronous and asynchronous. +XMLHttpRequest tiene dos modos de operación: sincrónica y asíncrona. -Let's see the asynchronous first, as it's used in the majority of cases. +Veamos primero la asíncrona, ya que es utilizada en la mayoría de los casos. -To do the request, we need 3 steps: +Para hacer la petición, necesitamos seguir 3 pasos: -1. Create `XMLHttpRequest`: +1. Crear el objeto `XMLHttpRequest`: ```js let xhr = new XMLHttpRequest(); ``` - The constructor has no arguments. + El constructor no tiene argumentos. -2. Initialize it, usually right after `new XMLHttpRequest`: +2. Inicializarlo, usualmente justo después de `new XMLHttpRequest`: ```js xhr.open(method, URL, [async, user, password]) ``` - This method specifies the main parameters of the request: + Este método especifica los parámetros principales para la petición: - - `method` -- HTTP-method. Usually `"GET"` or `"POST"`. - - `URL` -- the URL to request, a string, can be [URL](info:url) object. - - `async` -- if explicitly set to `false`, then the request is synchronous, we'll cover that a bit later. - - `user`, `password` -- login and password for basic HTTP auth (if required). + - `method` -- método HTTP. Usualmente `"GET"` o `"POST"`. + - `URL` -- la URL a solicitar, una cadena, puede ser un objeto [URL](info:url). + - `async` -- si se asigna explícitamente a `false`, entonces la petición será asincrónica. Cubriremos esto un poco más adelante. + - `user`, `password` -- usuario y contraseña para autenticación HTTP básica (si se requiere). - Please note that `open` call, contrary to its name, does not open the connection. It only configures the request, but the network activity only starts with the call of `send`. + Por favor, toma en cuenta que la llamada a `open`, contrario a su nombre, no abre la conexión. Solo configura la solicitud, pero la actividad de red solo empieza con la llamada del método `send`. -3. Send it out. +3. Enviar. ```js xhr.send([body]) ``` - This method opens the connection and sends the request to server. The optional `body` parameter contains the request body. + Este método abre la conexión y envía ka solicitud al servidor. El parámetro adicional `body` contiene el cuerpo de la solicitud. - Some request methods like `GET` do not have a body. And some of them like `POST` use `body` to send the data to the server. We'll see examples of that later. + Algunos métodos como `GET` no tienen un cuerpo. Y otros como `POST` usan el parámetro `body` para enviar datos al servidor. Vamos a ver unos ejemplos de eso más tarde. -4. Listen to `xhr` events for response. +4. Escuchar los eventos de respuesta `xhr`. - These three events are the most widely used: - - `load` -- when the request is complete (even if HTTP status is like 400 or 500), and the response is fully downloaded. - - `error` -- when the request couldn't be made, e.g. network down or invalid URL. - - `progress` -- triggers periodically while the response is being downloaded, reports how much has been downloaded. + Estos son los tres eventos más comúnmente utilizados: + - `load` -- cuando la solicitud está; completa (incluso si el estado HTTP es 400 o 500), y la respuesta se descargó por completo. + - `error` -- cuando la solicitud no pudo ser realizada satisfactoriamente, ej. red caída o una URL inválida. + - `progress` -- se dispara periódicamente mientras la respuesta está siendo descargada, reporta cuánto se ha descargado. ```js xhr.onload = function() { - alert(`Loaded: ${xhr.status} ${xhr.response}`); + alert(`Cargado: ${xhr.status} ${xhr.response}`); }; - xhr.onerror = function() { // only triggers if the request couldn't be made at all - alert(`Network Error`); + xhr.onerror = function() { // solo se activa si la solicitud no se puede realizar + alert(`Error de red`); }; - xhr.onprogress = function(event) { // triggers periodically - // event.loaded - how many bytes downloaded - // event.lengthComputable = true if the server sent Content-Length header - // event.total - total number of bytes (if lengthComputable) - alert(`Received ${event.loaded} of ${event.total}`); + xhr.onprogress = function(event) { // se dispara periódicamente + // event.loaded - cuántos bytes se han descargado + // event.lengthComputable = devuelve true si el servidor envía la cabecera Content-Length (longitud del contenido) + // event.total - número total de bytes (si `lengthComputable` es `true`) + alert(`Recibido ${event.loaded} of ${event.total}`); }; ``` -Here's a full example. The code below loads the URL at `/article/xmlhttprequest/example/load` from the server and prints the progress: +Aquí un ejemplo completo. El siguiente código carga la URL en `/article/xmlhttprequest/example/load` desde el servidor e imprime el progreso: ```js run -// 1. Create a new XMLHttpRequest object +// 1. Crea un nuevo objeto XMLHttpRequest let xhr = new XMLHttpRequest(); -// 2. Configure it: GET-request for the URL /article/.../load +// 2. Configuración: solicitud GET para la URL /article/.../load xhr.open('GET', '/article/xmlhttprequest/example/load'); -// 3. Send the request over the network +// 3. Envía la solicitud a la red xhr.send(); -// 4. This will be called after the response is received +// 4. Esto se llamará después de que la respuesta se reciba xhr.onload = function() { - if (xhr.status != 200) { // analyze HTTP status of the response - alert(`Error ${xhr.status}: ${xhr.statusText}`); // e.g. 404: Not Found - } else { // show the result - alert(`Done, got ${xhr.response.length} bytes`); // response is the server response + if (xhr.status != 200) { // analiza el estado HTTP de la respuesta + alert(`Error ${xhr.status}: ${xhr.statusText}`); // ej. 404: No encontrado + } else { // muestra el resultado + alert(`Hecho, obtenidos ${xhr.response.length} bytes`); // Respuesta del servidor } }; xhr.onprogress = function(event) { if (event.lengthComputable) { - alert(`Received ${event.loaded} of ${event.total} bytes`); + alert(`Recibidos ${event.loaded} de ${event.total} bytes`); } else { - alert(`Received ${event.loaded} bytes`); // no Content-Length + alert(`Recibidos ${event.loaded} bytes`); // sin Content-Length } }; xhr.onerror = function() { - alert("Request failed"); + alert("Solicitud fallida"); }; ``` -Once the server has responded, we can receive the result in the following `xhr` properties: +Una vez el servidor haya respondido, podemos recibir el resultado en las siguientes propiedades de `xhr`: `status` -: HTTP status code (a number): `200`, `404`, `403` and so on, can be `0` in case of a non-HTTP failure. +: Código del estado HTTP (un número): `200`, `404`, `403` y así por el estilo, puede ser `0` en caso de una falla no HTTP. `statusText` -: HTTP status message (a string): usually `OK` for `200`, `Not Found` for `404`, `Forbidden` for `403` and so on. +: Mensaje del estado HTTP (una cadena): usualmente `OK` para `200`, `Not Found` para `404`, `Forbidden` para `403` y así por el estilo. -`response` (old scripts may use `responseText`) -: The server response body. +`response` (scripts antiguos deben usar `responseText`) +: El cuerpo de la respuesta del servidor. -We can also specify a timeout using the corresponding property: +También podemos especificar un tiempo límite usando la propiedad correspondiente: ```js -xhr.timeout = 10000; // timeout in ms, 10 seconds +xhr.timeout = 10000; // límite de tiempo en milisegundos, 10 segundos ``` -If the request does not succeed within the given time, it gets canceled and `timeout` event triggers. +Si la solicitud no es realizada con éxito dentro del tiempo dado, se cancela y el evento `timeout` se activa. -````smart header="URL search parameters" -To add parameters to URL, like `?name=value`, and ensure the proper encoding, we can use [URL](info:url) object: +````smart header="Parámetros de búsqueda URL" +Para agregar los parámetros a la URL, como `?nombre=valor`, y asegurar la codificación adecuada, podemos utilizar un objeto [URL](info:url): ```js let url = new URL('https://google.com/search'); -url.searchParams.set('q', 'test me!'); +url.searchParams.set('q', 'pruébame!'); -// the parameter 'q' is encoded +// el parámetro 'q' está codificado xhr.open('GET', url); // https://google.com/search?q=test+me%21 ``` ```` -## Response Type +## Tipo de respuesta -We can use `xhr.responseType` property to set the response format: +Podemos usar la propiedad `xhr.responseType` para asignar el formato de la respuesta: -- `""` (default) -- get as string, -- `"text"` -- get as string, -- `"arraybuffer"` -- get as `ArrayBuffer` (for binary data, see chapter <info:arraybuffer-binary-arrays>), -- `"blob"` -- get as `Blob` (for binary data, see chapter <info:blob>), -- `"document"` -- get as XML document (can use XPath and other XML methods) or HTML document (based on the MIME type of the received data), -- `"json"` -- get as JSON (parsed automatically). +- `""` (default) -- obtiene una cadena, +- `"text"` -- obtiene una cadena, +- `"arraybuffer"` -- obtiene un `ArrayBuffer` (para datos binarios, ve el capítulo <info:arraybuffer-binary-arrays>), +- `"blob"` -- obtiene un `Blob` (para datos binarios, ver el capítulo <info:blob>), +- `"document"` -- obtiene un documento XML (puede usar XPath y otros métodos XML) o un documento HTML (en base al tipo MIME del dato recibido), +- `"json"` -- obtiene un JSON (automáticamente analizado). -For example, let's get the response as JSON: +Por ejemplo, obtengamos una respuesta como JSON: ```js run let xhr = new XMLHttpRequest(); @@ -167,67 +167,67 @@ xhr.responseType = 'json'; xhr.send(); -// the response is {"message": "Hello, world!"} +// la respuesta es {"message": "Hola, Mundo!"} xhr.onload = function() { let responseObj = xhr.response; - alert(responseObj.message); // Hello, world! + alert(responseObj.message); // Hola, Mundo! }; ``` ```smart -In the old scripts you may also find `xhr.responseText` and even `xhr.responseXML` properties. +En los scripts antiguos puedes encontrar también las propiedades `xhr.responseText` e incluso `xhr.responseXML`. -They exist for historical reasons, to get either a string or XML document. Nowadays, we should set the format in `xhr.responseType` and get `xhr.response` as demonstrated above. +Existen por razones históricas, para obtener ya sea una cadena o un documento XML. Hoy en día, debemos seleccionar el formato en `xhr.responseType` y obtener `xhr.response` como se demuestra debajo. ``` -## Ready states +## Estados -`XMLHttpRequest` changes between states as it progresses. The current state is accessible as `xhr.readyState`. +`XMLHttpRequest` cambia entre estados a medida que avanza. El estado actual es accesible como `xhr.readyState`. -All states, as in [the specification](https://xhr.spec.whatwg.org/#states): +Todos los estados, como en [la especificación](https://xhr.spec.whatwg.org/#states): ```js -UNSENT = 0; // initial state -OPENED = 1; // open called -HEADERS_RECEIVED = 2; // response headers received -LOADING = 3; // response is loading (a data packet is received) -DONE = 4; // request complete +UNSENT = 0; // estado inicial +OPENED = 1; // llamada abierta +HEADERS_RECEIVED = 2; // cabeceras de respuesta recibidas +LOADING = 3; // la respuesta está cargando (un paquete de datos es recibido) +DONE = 4; // solicitud completa ``` -An `XMLHttpRequest` object travels them in the order `0` -> `1` -> `2` -> `3` -> ... -> `3` -> `4`. State `3` repeats every time a data packet is received over the network. +Un objeto `XMLHttpRequest` escala en orden `0` -> `1` -> `2` -> `3` -> ... -> `3` -> `4`. El estado `3` se repite cada vez que un paquete de datos se recibe a través de la red. -We can track them using `readystatechange` event: +Podemos seguirlos usando el evento `readystatechange`: ```js xhr.onreadystatechange = function() { if (xhr.readyState == 3) { - // loading + // cargando } if (xhr.readyState == 4) { - // request finished + // solicitud finalizada } }; ``` -You can find `readystatechange` listeners in really old code, it's there for historical reasons, as there was a time when there were no `load` and other events. Nowadays, `load/error/progress` handlers deprecate it. +Puedes encontrar oyentes del evento `readystatechange` en código realmente viejo, está ahí por razones históricas, había un tiempo cuando no existían `load` y otros eventos. Hoy en día los manipuladores `load/error/progress` lo hacen obsoleto. -## Aborting request +## Abortando solicitudes -We can terminate the request at any time. The call to `xhr.abort()` does that: +Podemos terminar la solicitud en cualquier momento. La llamada a `xhr.abort()` hace eso: ```js -xhr.abort(); // terminate the request +xhr.abort(); // termina la solicitud ``` -That triggers `abort` event, and `xhr.status` becomes `0`. +Este dispara el evento `abort`, y el `xhr.status` se convierte en `0`. -## Synchronous requests +## Solicitudes sincrónicas -If in the `open` method the third parameter `async` is set to `false`, the request is made synchronously. +Si en el método `open` el tercer parámetro `async` se asigna como `false`, la solicitud se hace sincrónicamente. -In other words, JavaScript execution pauses at `send()` and resumes when the response is received. Somewhat like `alert` or `prompt` commands. +En otras palabras, la ejecución de JavaScript se pausa en el `send()` y se reanuda cuando la respuesta es recibida. Algo como los comandos `alert` o `prompt`. -Here's the rewritten example, the 3rd parameter of `open` is `false`: +Aquí está el ejemplo reescrito, el tercer parámetro de `open` es `false`: ```js let xhr = new XMLHttpRequest(); @@ -241,68 +241,68 @@ try { } else { alert(xhr.response); } -} catch(err) { // instead of onerror - alert("Request failed"); +} catch(err) { // en lugar de onerror + alert("Solicitud fallida"); } ``` -It might look good, but synchronous calls are used rarely, because they block in-page JavaScript till the loading is complete. In some browsers it becomes impossible to scroll. If a synchronous call takes too much time, the browser may suggest to close the "hanging" webpage. +Puede verse bien, pero las llamadas sincrónicas son rara vez utilizadas porque bloquean todo el JavaScript de la página hasta que la carga está completa. En algunos navegadores se hace imposible hacer scroll. Si una llamada síncrona toma mucho tiempo, el navegador puede sugerir cerrar el sitio web "colgado". -Many advanced capabilities of `XMLHttpRequest`, like requesting from another domain or specifying a timeout, are unavailable for synchronous requests. Also, as you can see, no progress indication. +Algunas capacidades avanzadas de `XMLHttpRequest`, como solicitar desde otro dominio o especificar un tiempo límite, no están disponibles para solicitudes síncronas. Tampoco, como puedes ver, la indicación de progreso. -Because of all that, synchronous requests are used very sparingly, almost never. We won't talk about them any more. +La razón de esto es que las solicitudes sincrónicas son utilizadas muy escasamente, casi nunca. No hablaremos más sobre ellas. -## HTTP-headers +## Cabeceras HTTP -`XMLHttpRequest` allows both to send custom headers and read headers from the response. +`XMLHttpRequest` permite tanto enviar cabeceras personalizadas como leer cabeceras de la respuesta. -There are 3 methods for HTTP-headers: +Existen 3 métodos para las cabeceras HTTP: `setRequestHeader(name, value)` -: Sets the request header with the given `name` and `value`. +: Asigna la cabecera de la solicitud con los valores `name` y `value` provistos. - For instance: + Por ejemplo: ```js xhr.setRequestHeader('Content-Type', 'application/json'); ``` - ```warn header="Headers limitations" - Several headers are managed exclusively by the browser, e.g. `Referer` and `Host`. - The full list is [in the specification](https://xhr.spec.whatwg.org/#the-setrequestheader()-method). + ```warn header="Limitaciones de cabeceras" + Muchas cabeceras se administran exclusivamente por el navegador, ej. `Referer` y `Host`. + La lista completa está [en la especificación](https://xhr.spec.whatwg.org/#the-setrequestheader()-method). - `XMLHttpRequest` is not allowed to change them, for the sake of user safety and correctness of the request. + `XMLHttpRequest` no está permitido cambiarlos, por motivos de seguridad del usuario y la exactitud de la solicitud. ``` - ````warn header="Can't remove a header" - Another peculiarity of `XMLHttpRequest` is that one can't undo `setRequestHeader`. + ````warn header="No se pueden eliminar cabeceras" + Otra peculiaridad de `XMLHttpRequest` es que no puede deshacer un `setRequestHeader`. - Once the header is set, it's set. Additional calls add information to the header, don't overwrite it. + Una vez que una cabecera es asignada, ya está asignada. Llamadas adicionales agregan información a la cabecera, no la sobreescriben. - For instance: + Por ejemplo: ```js xhr.setRequestHeader('X-Auth', '123'); xhr.setRequestHeader('X-Auth', '456'); - // the header will be: + // la cabecera será: // X-Auth: 123, 456 ``` ```` `getResponseHeader(name)` -: Gets the response header with the given `name` (except `Set-Cookie` and `Set-Cookie2`). +: Obtiene la cabecera de la respuesta con el `name` dado (excepto `Set-Cookie` y `Set-Cookie2`). - For instance: + Por ejemplo: ```js xhr.getResponseHeader('Content-Type') ``` `getAllResponseHeaders()` -: Returns all response headers, except `Set-Cookie` and `Set-Cookie2`. +: Devuelve todas las cabeceras de la respuesta, excepto por `Set-Cookie` y `Set-Cookie2`. - Headers are returned as a single line, e.g.: + Las cabeceras se devuelven como una sola línea, ej.: ```http Cache-Control: max-age=31536000 @@ -311,11 +311,11 @@ There are 3 methods for HTTP-headers: Date: Sat, 08 Sep 2012 16:53:16 GMT ``` - The line break between headers is always `"\r\n"` (doesn't depend on OS), so we can easily split it into individual headers. The separator between the name and the value is always a colon followed by a space `": "`. That's fixed in the specification. + El salto de línea entre las cabeceras siempre es un `"\r\n"` (independiente del SO), así podemos dividirlas en cabeceras individuales. El separador entre el nombre y el valor siempre es dos puntos seguido de un espacio `": "`. Eso quedó establecido en la especificación. - So, if we want to get an object with name/value pairs, we need to throw in a bit JS. + Así, si queremos obtener un objeto con pares nombre/valor, necesitamos tratarlas con un poco de JS. - Like this (assuming that if two headers have the same name, then the latter one overwrites the former one): + Como esto (asumiendo que si dos cabeceras tienen el mismo nombre, entonces el último sobreescribe al primero): ```js let headers = xhr @@ -330,23 +330,23 @@ There are 3 methods for HTTP-headers: // headers['Content-Type'] = 'image/png' ``` -## POST, FormData +## POST, Formularios -To make a POST request, we can use the built-in [FormData](mdn:api/FormData) object. +Para hacer una solicitud POST, podemos utilizar el objeto [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData) nativo. -The syntax: +La sintaxis: ```js -let formData = new FormData([form]); // creates an object, optionally fill from <form> -formData.append(name, value); // appends a field +let formData = new FormData([form]); // crea un objeto, opcionalmente se completa con un <form> +formData.append(name, value); // añade un campo ``` -We create it, optionally fill from a form, `append` more fields if needed, and then: +Lo creamos, opcionalmente lleno desde un formulario, `append` (agrega) más campos si se necesitan, y entonces: -1. `xhr.open('POST', ...)` – use `POST` method. -2. `xhr.send(formData)` to submit the form to the server. +1. `xhr.open('POST', ...)` – se utiliza el método `POST`. +2. `xhr.send(formData)` para enviar el formulario al servidor. -For instance: +Por ejemplo: ```html run refresh <form name="person"> @@ -355,13 +355,13 @@ For instance: </form> <script> - // pre-fill FormData from the form + // pre llenado del objeto FormData desde el formulario let formData = new FormData(document.forms.person); - // add one more field + // agrega un campo más formData.append("middle", "Lee"); - // send it out + // lo enviamos let xhr = new XMLHttpRequest(); xhr.open("POST", "/article/xmlhttprequest/post/user"); xhr.send(formData); @@ -370,11 +370,11 @@ For instance: </script> ``` -The form is sent with `multipart/form-data` encoding. +El formulario fue enviado con codificación `multipart/form-data`. -Or, if we like JSON more, then `JSON.stringify` and send as a string. +O, si nos gusta más JSON, entonces, un `JSON.stringify` y lo enviamos como un string. -Just don't forget to set the header `Content-Type: application/json`, many server-side frameworks automatically decode JSON with it: +Solo no te olvides de asignar la cabecera `Content-Type: application/json`, muchos frameworks del lado del servidor decodifican automáticamente JSON con este: ```js let xhr = new XMLHttpRequest(); @@ -390,29 +390,29 @@ xhr.setRequestHeader('Content-type', 'application/json; charset=utf-8'); xhr.send(json); ``` -The `.send(body)` method is pretty omnivore. It can send almost any `body`, including `Blob` and `BufferSource` objects. +El método `.send(body)` es bastante omnívoro. Puede enviar casi cualquier `body`, incluyendo objetos `Blob` y `BufferSource`. -## Upload progress +## Progreso de carga -The `progress` event triggers only on the downloading stage. +El evento `progress` se dispara solo en la fase de descarga. -That is: if we `POST` something, `XMLHttpRequest` first uploads our data (the request body), then downloads the response. +Esto es: si hacemos un `POST` de algo, `XMLHttpRequest` primero sube nuestros datos (el cuerpo de la respuesta), entonces descarga la respuesta. -If we're uploading something big, then we're surely more interested in tracking the upload progress. But `xhr.onprogress` doesn't help here. +Si estamos subiendo algo grande, entonces seguramente estaremos interesados en rastrear el progreso de nuestra carga. Pero `xhr.onprogress` no ayuda aquí. -There's another object, without methods, exclusively to track upload events: `xhr.upload`. +Hay otro objeto, sin métodos, exclusivamente para rastrear los eventos de subida: `xhr.upload`. -It generates events, similar to `xhr`, but `xhr.upload` triggers them solely on uploading: +Este genera eventos similares a `xhr`, pero `xhr.upload` se dispara solo en las subidas: -- `loadstart` -- upload started. -- `progress` -- triggers periodically during the upload. -- `abort` -- upload aborted. -- `error` -- non-HTTP error. -- `load` -- upload finished successfully. -- `timeout` -- upload timed out (if `timeout` property is set). -- `loadend` -- upload finished with either success or error. +- `loadstart` -- carga iniciada. +- `progress` -- se dispara periódicamente durante la subida. +- `abort` -- carga abortada. +- `error` -- error no HTTP. +- `load` -- carga finalizada con éxito. +- `timeout` -- carga caducada (si la propiedad `timeout` está asignada). +- `loadend` -- carga finalizada con éxito o error. -Example of handlers: +Ejemplos de manejadores: ```js xhr.upload.onprogress = function(event) { @@ -424,11 +424,11 @@ xhr.upload.onload = function() { }; xhr.upload.onerror = function() { - alert(`Error during the upload: ${xhr.status}`); + alert(`Error durante la carga: ${xhr.status}`); }; ``` -Here's a real-life example: file upload with progress indication: +Aquí un ejemplo de la vida real: indicación del progreso de subida de un archivo: ```html run <input type="file" onchange="upload(this.files[0])"> @@ -437,17 +437,17 @@ Here's a real-life example: file upload with progress indication: function upload(file) { let xhr = new XMLHttpRequest(); - // track upload progress + // rastrea el progreso de la subida *!* xhr.upload.onprogress = function(event) { console.log(`Uploaded ${event.loaded} of ${event.total}`); }; */!* - // track completion: both successful or not + // seguimiento completado: sea satisfactorio o no xhr.onloadend = function() { if (xhr.status == 200) { - console.log("success"); + console.log("Logrado"); } else { console.log("error " + this.status); } @@ -459,11 +459,11 @@ function upload(file) { </script> ``` -## Cross-origin requests +## Solicitudes de origen cruzado (Cross-origin) -`XMLHttpRequest` can make cross-origin requests, using the same CORS policy as [fetch](info:fetch-crossorigin). +`XMLHttpRequest` puede hacer solicitudes de origen cruzado, utilizando la misma política CORS que se [solicita](info:fetch-crossorigin). -Just like `fetch`, it doesn't send cookies and HTTP-authorization to another origin by default. To enable them, set `xhr.withCredentials` to `true`: +Tal como `fetch`, no envía cookies ni autorización HTTP a otro origen por omisión. Para activarlas, asigna `xhr.withCredentials` como `true`: ```js let xhr = new XMLHttpRequest(); @@ -475,12 +475,12 @@ xhr.open('POST', 'http://anywhere.com/request'); ... ``` -See the chapter <info:fetch-crossorigin> for details about cross-origin headers. +Ve el capítulo <info:fetch-crossorigin> para detalles sobre las cabeceras de origen cruzado. -## Summary +## Resumen -Typical code of the GET-request with `XMLHttpRequest`: +Codificación típica de la solicitud GET con `XMLHttpRequest`: ```js let xhr = new XMLHttpRequest(); @@ -490,39 +490,39 @@ xhr.open('GET', '/my/url'); xhr.send(); xhr.onload = function() { - if (xhr.status != 200) { // HTTP error? - // handle error + if (xhr.status != 200) { // error HTTP? + // maneja el error alert( 'Error: ' + xhr.status); return; } - // get the response from xhr.response + // obtiene la respuesta de xhr.response }; xhr.onprogress = function(event) { - // report progress + // reporta progreso alert(`Loaded ${event.loaded} of ${event.total}`); }; xhr.onerror = function() { - // handle non-HTTP error (e.g. network down) + // manejo de un error no HTTP (ej. red caída) }; ``` -There are actually more events, the [modern specification](https://xhr.spec.whatwg.org/#events) lists them (in the lifecycle order): +De hecho hay más eventos, la [especificación moderna](https://xhr.spec.whatwg.org/#events) los lista (en el orden del ciclo de vida): -- `loadstart` -- the request has started. -- `progress` -- a data packet of the response has arrived, the whole response body at the moment is in `response`. -- `abort` -- the request was canceled by the call `xhr.abort()`. -- `error` -- connection error has occurred, e.g. wrong domain name. Doesn't happen for HTTP-errors like 404. -- `load` -- the request has finished successfully. -- `timeout` -- the request was canceled due to timeout (only happens if it was set). -- `loadend` -- triggers after `load`, `error`, `timeout` or `abort`. +- `loadstart` -- la solicitud ha empezado. +- `progress` -- un paquete de datos de la respuesta ha llegado, el cuerpo completo de la respuesta al momento está en `response`. +- `abort` -- la solicitud ha sido cancelada por la llamada de `xhr.abort()`. +- `error` -- un error de conexión ha ocurrido, ej. nombre de dominio incorrecto. No pasa con errores HTTP como 404. +- `load` -- la solicitud se ha completado satisfactoriamente. +- `timeout` -- la solicitud fue cancelada debido a que caducó (solo pasa si fue configurado). +- `loadend` -- se dispara después de `load`, `error`, `timeout` o `abort`. -The `error`, `abort`, `timeout`, and `load` events are mutually exclusive. Only one of them may happen. +Los eventos `error`, `abort`, `timeout`, y `load` son mutuamente exclusivos. Solo uno de ellos puede pasar. -The most used events are load completion (`load`), load failure (`error`), or we can use a single `loadend` handler and check the properties of the request object `xhr` to see what happened. +Los eventos más usados son la carga terminada (`load`), falla de carga (`error`), o podemos usar un solo manejador `loadend` y comprobar las propiedades del objeto solicitado `xhr` para ver qué ha pasado. -We've already seen another event: `readystatechange`. Historically, it appeared long ago, before the specification settled. Nowadays, there's no need to use it, we can replace it with newer events, but it can often be found in older scripts. +Ya hemos visto otro evento: `readystatechange`. Históricamente, apareció hace mucho tiempo, antes de que la especificación fuera publicada. Hoy en día no es necesario usarlo; podemos reemplazarlo con eventos más nuevos, pero puede ser encontrado a menudo en scripts viejos. -If we need to track uploading specifically, then we should listen to same events on `xhr.upload` object. +Si necesitamos rastrear específicamente, entonces debemos escuchar a los mismos eventos en el objeto `xhr.upload`. diff --git a/5-network/08-xmlhttprequest/phones-async.view/index.html b/5-network/08-xmlhttprequest/phones-async.view/index.html index dff72ba3d..2c255cc05 100644 --- a/5-network/08-xmlhttprequest/phones-async.view/index.html +++ b/5-network/08-xmlhttprequest/phones-async.view/index.html @@ -5,7 +5,7 @@ </head> <body> - <button onclick="loadPhones()" id="button">Load phones.json!</button> + <button onclick="loadPhones()" id="button">Cargar phones.json!</button> <script> function loadPhones() { @@ -21,19 +21,19 @@ xhr.onreadystatechange = function() { if (xhr.readyState != 4) return; - button.innerHTML = 'Complete!'; + button.innerHTML = '¡Finalizado!'; if (xhr.status != 200) { - // handle error + // manejar error alert(xhr.status + ': ' + xhr.statusText); } else { - // show result + // mostrar resultado alert(xhr.responseText); } } - button.innerHTML = 'Loading...'; + button.innerHTML = 'Cargando...'; button.disabled = true; } </script> diff --git a/5-network/08-xmlhttprequest/phones.view/index.html b/5-network/08-xmlhttprequest/phones.view/index.html index 02e1fb967..b76c572fc 100644 --- a/5-network/08-xmlhttprequest/phones.view/index.html +++ b/5-network/08-xmlhttprequest/phones.view/index.html @@ -5,7 +5,7 @@ </head> <body> - <button onclick="loadPhones()">Load phones.json!</button> + <button onclick="loadPhones()">Carga phones.json!</button> <script> function loadPhones() { @@ -15,10 +15,10 @@ xhr.send(); if (xhr.status != 200) { - // handle error + // manejar error alert('Error ' + xhr.status + ': ' + xhr.statusText); } else { - // show result + // mostrar resultado alert(xhr.responseText); } } diff --git a/5-network/09-resume-upload/article.md b/5-network/09-resume-upload/article.md index b0aa447d6..92c73a71f 100644 --- a/5-network/09-resume-upload/article.md +++ b/5-network/09-resume-upload/article.md @@ -1,36 +1,36 @@ -# Resumable file upload +# Carga de archivos reanudable -With `fetch` method it's fairly easy to upload a file. +Con el método `fetch` es bastante fácil cargar un archivo. -How to resume the upload after lost connection? There's no built-in option for that, but we have the pieces to implement it. +¿Cómo reanudar la carga de un archivo despues de perder la conexión? No hay una opción incorporada para eso, pero tenemos las piezas para implementarlo. -Resumable uploads should come with upload progress indication, as we expect big files (if we may need to resume). So, as `fetch` doesn't allow to track upload progress, we'll use [XMLHttpRequest](info:xmlhttprequest). +Las cargas reanudables deberían venir con indicación de progreso, ya que esperamos archivos grandes (Si necesitamos reanudar). Entonces, ya que `fetch` no permite rastrear el progreso de carga, usaremos [XMLHttpRequest](info:xmlhttprequest). -## Not-so-useful progress event +## Evento de progreso poco útil -To resume upload, we need to know how much was uploaded till the connection was lost. +Para reanudar la carga, necesitamos saber cuánto fue cargado hasta la pérdida de la conexión. -There's `xhr.upload.onprogress` to track upload progress. +Disponemos de `xhr.upload.onprogress` para rastrear el progreso de carga. -Unfortunately, it won't help us to resume the upload here, as it triggers when the data is *sent*, but was it received by the server? The browser doesn't know. +Desafortunadamente, esto no nos ayudará a reanudar la descarga, Ya que se origina cuando los datos son *enviados*, ¿pero fue recibida por el servidor? el navegador no lo sabe. -Maybe it was buffered by a local network proxy, or maybe the remote server process just died and couldn't process them, or it was just lost in the middle and didn't reach the receiver. +Tal vez fue almacenada por un proxy de la red local, o quizá el proceso del servidor remoto solo murió y no pudo procesarla, o solo se perdió en el medio y no alcanzó al receptor. -That's why this event is only useful to show a nice progress bar. +Es por eso que este evento solo es útil para mostrar una barra de progreso bonita. -To resume upload, we need to know *exactly* the number of bytes received by the server. And only the server can tell that, so we'll make an additional request. +Para reanudar una carga, necesitamos saber *exactamente* el número de bytes recibidos por el servidor. Y eso solo lo sabe el servidor, por lo tanto haremos una solicitud adicional. -## Algorithm +## Algoritmos -1. First, create a file id, to uniquely identify the file we're going to upload: +1. Primero, crear un archivo id, para únicamente identificar el archivo que vamos a subir: ```js let fileId = file.name + '-' + file.size + '-' + file.lastModified; ``` - That's needed for resume upload, to tell the server what we're resuming. + Eso es necesario para reanudar la carga, para decirle al servidor lo que estamos reanudando. - If the name or the size or the last modification date changes, then there'll be another `fileId`. + Si el nombre o tamaño de la última fecha de modificación cambia, entonces habrá otro `fileId`. -2. Send a request to the server, asking how many bytes it already has, like this: +2. Envía una solicitud al servidor, preguntando cuántos bytes tiene, así: ```js let response = await fetch('status', { headers: { @@ -38,45 +38,45 @@ To resume upload, we need to know *exactly* the number of bytes received by the } }); - // The server has that many bytes + // El servidor tiene tanta cantidad de bytes let startByte = +await response.text(); ``` - This assumes that the server tracks file uploads by `X-File-Id` header. Should be implemented at server-side. + Esto asume que el servidor rastrea archivos cargados por el encabezado `X-File-Id`. Debe ser implementado en el lado del servidor. - If the file doesn't yet exist at the server, then the server response should be `0` + Si el archivo no existe aún en el servidor, entonces su respuesta debe ser `0`. -3. Then, we can use `Blob` method `slice` to send the file from `startByte`: +3. Entonces, podemos usar el método `Blob` `slice` para enviar el archivo desde `startByte`: ```js xhr.open("POST", "upload"); - // File id, so that the server knows which file we upload + // Archivo, de modo que el servidor sepa qué archivo subimos xhr.setRequestHeader('X-File-Id', fileId); - // The byte we're resuming from, so the server knows we're resuming + // El byte desde el que estamos reanudando, así el servidor sabe que estamos reanudando xhr.setRequestHeader('X-Start-Byte', startByte); xhr.upload.onprogress = (e) => { console.log(`Uploaded ${startByte + e.loaded} of ${startByte + e.total}`); }; - // file can be from input.files[0] or another source + // El archivo puede ser de input.files[0] u otra fuente xhr.send(file.slice(startByte)); ``` - Here we send the server both file id as `X-File-Id`, so it knows which file we're uploading, and the starting byte as `X-Start-Byte`, so it knows we're not uploading it initially, but resuming. + Aquí enviamos al servidor ambos archivos id como `X-File-Id`, para que de esa manera sepa que archivos estamos cargando, y el byte inicial como `X-Start-Byte`, para que sepa que no lo estamos cargando inicialmente, si no reanudándolo. - The server should check its records, and if there was an upload of that file, and the current uploaded size is exactly `X-Start-Byte`, then append the data to it. + El servidor debe verificar sus registros, y si hubo una carga de ese archivo, y si el tamaño de carga actual es exactamente `X-Start-Byte`, entonces agregarle los datos. -Here's the demo with both client and server code, written on Node.js. +Aquí esta la demostración con el código tanto del cliente como del servidor, escrito en Node.js. -It works only partially on this site, as Node.js is behind another server named Nginx, that buffers uploads, passing them to Node.js when fully complete. +Esto funciona solo parcialmente en este sitio, ya que Node.js esta detrás de otro servidor llamado Nginx, que almacena cargas, pasándolas a Node.js cuando esta completamente lleno. -But you can download it and run locally for the full demonstration: +Pero puedes cargarlo y ejecutarlo localmente para la demostración completa: [codetabs src="upload-resume" height=200] -As we can see, modern networking methods are close to file managers in their capabilities -- control over headers, progress indicator, sending file parts, etc. +Como podemos ver, los métodos de red modernos estan cerca de los gestores de archivos en sus capacidades -- control sobre header, indicador de progreso, enviar partes de archivos, etc. -We can implement resumable upload and much more. +Podemos implementar la carga reanudable y mucho mas. diff --git a/5-network/10-long-polling/article.md b/5-network/10-long-polling/article.md index e9d8abe39..60bca22ed 100644 --- a/5-network/10-long-polling/article.md +++ b/5-network/10-long-polling/article.md @@ -1,98 +1,98 @@ -# Long polling +# Sondeo largo -Long polling is the simplest way of having persistent connection with server, that doesn't use any specific protocol like WebSocket or Server Sent Events. +El "sondeo largo" es la forma más sencilla de tener una conexión persistente con el servidor. No utiliza ningún protocolo específico como "WebSocket" o "SSE". -Being very easy to implement, it's also good enough in a lot of cases. +Es muy fácil de implementar, y es suficientemente bueno en muchos casos. -## Regular Polling +## Sondeo regular -The simplest way to get new information from the server is periodic polling. That is, regular requests to the server: "Hello, I'm here, do you have any information for me?". For example, once every 10 seconds. +La forma más sencilla de obtener información nueva desde el servidor es un sondeo periódico. Es decir, solicitudes regulares al servidor: "Hola, estoy aquí, ¿tienes información para mí?". Por ejemplo, una vez cada 10 segundos. -In response, the server first takes a notice to itself that the client is online, and second - sends a packet of messages it got till that moment. +En respuesta, el servidor primero se da cuenta de que el cliente está en línea, y segundo, envía un paquete con los mensajes que recibió hasta ese momento. -That works, but there are downsides: -1. Messages are passed with a delay up to 10 seconds (between requests). -2. Even if there are no messages, the server is bombed with requests every 10 seconds, even if the user switched somewhere else or is asleep. That's quite a load to handle, speaking performance-wise. +Esto funciona, pero tiene sus desventajas: +1. Los mensajes desde el servidor se transmiten con un retraso de hasta 10 segundos (el tiempo entre solicitudes de nuestro ejemplo). +2. El servidor es bombardeado con solicitudes cada 10 segundos aunque no haya mensajes, incluso si el usuario cambió a otro lugar o está dormido. En términos de rendimiento, esto es bastante difícil de manejar. -So, if we're talking about a very small service, the approach may be viable, but generally, it needs an improvement. +Entonces: si hablamos de un servicio muy pequeño, este enfoque es viable. Pero en general, se necesita algo mejor. -## Long polling +## Sondeo largo -So-called "long polling" is a much better way to poll the server. +El llamado "sondeo largo" es una forma mucho mejor de sondear el servidor. -It's also very easy to implement, and delivers messages without delays. +También es muy fácil de implementar, y envía los mensajes sin demoras. -The flow: +El flujo es: -1. A request is sent to the server. -2. The server doesn't close the connection until it has a message to send. -3. When a message appears - the server responds to the request with it. -4. The browser makes a new request immediately. +1. El navegador envía una solicitud al servidor. +2. El servidor mantiene la conexión abierta mientras no tenga mensajes para enviar. +3. Cuando aparece un mensaje, el servidor responde a la solicitud con dicho mensaje y cierra la conexión. +4. El navegador recibe el mensaje y de inmediato realiza una nueva solicitud. -This situation, where the browser has sent a request and keeps a pending connection with the server, is standard for this method. Only when a message is delivered, the connection is closed and reestablished. +Esta situación, en la que el navegador envió una solicitud y se mantiene abierta una conexión con el servidor, es estándar para este método. En cuanto se entrega un mensaje, la conexión se cierra y restablece. ![](long-polling.svg) -If the connection is lost, because of, say, a network error, the browser immediately sends a new request. +Si se pierde la conexión (debido a un error de red, por ejemplo), el navegador envía inmediatamente una nueva solicitud. -A sketch of client-side `subscribe` function that makes long requests: +Este es el esquema, del lado del cliente, de una función de suscripción que realiza solicitudes largas: ```js async function subscribe() { let response = await fetch("/subscribe"); if (response.status == 502) { - // Status 502 is a connection timeout error, - // may happen when the connection was pending for too long, - // and the remote server or a proxy closed it - // let's reconnect - await subscribe(); + // El estado 502 es un error de "tiempo de espera agotado" en la conexión, + // puede suceder cuando la conexión estuvo pendiente durante demasiado tiempo, + // y el servidor remoto o un proxy la cerró + // vamos a reconectarnos + subscribe(); } else if (response.status != 200) { - // An error - let's show it + // Un error : vamos a mostrarlo showMessage(response.statusText); - // Reconnect in one second + // Vuelve a conectar en un segundo await new Promise(resolve => setTimeout(resolve, 1000)); - await subscribe(); + subscribe(); } else { - // Get and show the message + // Recibe y muestra el mensaje let message = await response.text(); showMessage(message); - // Call subscribe() again to get the next message - await subscribe(); + // Llama a subscribe () nuevamente para obtener el siguiente mensaje + subscribe(); } } subscribe(); ``` -As you can see, `subscribe` function makes a fetch, then waits for the response, handles it and calls itself again. +Como puedes ver, la función `subscribe` realiza una búsqueda, espera la respuesta, la maneja, y se llama a sí misma nuevamente. -```warn header="Server should be ok with many pending connections" -The server architecture must be able to work with many pending connections. +```warn header="El servidor debe ser capaz de mantener muchas conexiones pendientes" +La arquitectura del servidor debe poder funcionar bien con muchas conexiones pendientes. -Certain server architectures run one process per connection, resulting in there being as many processes as there are connections, while each process consumes quite a bit of memory. So, too many connections will just consume it all. +Algunas arquitecturas de servidor ejecutan un proceso por conexión, resultando en que habrá tantos procesos como conexiones, y cada proceso requiere bastante memoria. Demasiadas conexiones la consumirán toda. -That's often the case for backends written in languages like PHP and Ruby. +Este suele ser el caso de los backends escritos en lenguajes como PHP y Ruby. -Servers written using Node.js usually don't have such problems. +Los servidores escritos con Node.js generalmente no tienen este problema. -That said, it isn't a programming language issue. Most modern languages, including PHP and Ruby allow to implement a proper backend. Just please make sure that your server architecture works fine with many simultaneous connections. +Dicho esto, no es un problema del lenguaje sino de la implementación. La mayoría de los lenguajes modernos, incluyendo PHP y Ruby, permiten la implementación de un backend adecuado. Por favor, asegúrate de que la arquitectura del servidor funcione bien con múltiples conexiones simultáneas. ``` -## Demo: a chat +## Demostración: un chat -Here's a demo chat, you can also download it and run locally (if you're familiar with Node.js and can install modules): +Este es un chat de demostración, que también puedes descargar y ejecutar localmente (si estás familiarizado con Node.js y puedes instalar módulos): [codetabs src="longpoll" height=500] -Browser code is in `browser.js`. +El código del navegador está en `browser.js`. -## Area of usage +## Área de uso -Long polling works great in situations when messages are rare. +El sondeo largo funciona muy bien en situaciones en las que los mensajes son escasos. -If messages come very often, then the chart of requesting-receiving messages, painted above, becomes saw-like. +Pero si los mensajes llegan con mucha frecuencia, entonces el gráfico de arriba, mensajes solicitados/recibidos, se vuelve en forma de "diente de sierra". -Every message is a separate request, supplied with headers, authentication overhead, and so on. +Cada mensaje es una solicitud separada: provista de encabezados, sobrecarga de autenticación, etc. -So, in this case, another method is preferred, such as [Websocket](info:websocket) or [Server Sent Events](info:server-sent-events). +En este caso se prefieren otros métodos, como [Websocket](info:websocket), o [SSE](info:server-sent-events) (Eventos enviados por el servidor). diff --git a/5-network/10-long-polling/long-polling.svg b/5-network/10-long-polling/long-polling.svg index 045ef371f..ee7965dc9 100644 --- a/5-network/10-long-polling/long-polling.svg +++ b/5-network/10-long-polling/long-polling.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="521" height="320" viewBox="0 0 521 320"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="network" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="long-polling.svg"><text id="Browser" fill="#181717" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal"><tspan x="27" y="74">Browser</tspan></text><text id="Server" fill="#181717" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal"><tspan x="31" y="226">Server</tspan></text><path id="Line" fill="#181717" fill-rule="nonzero" d="M450.81 75.82l.435.244 8 4.5.775.436-.775.436-8 4.5-.436.245-.49-.872.436-.245 6.336-3.564H66.5v-1h390.591l-6.336-3.564-.436-.245.49-.872zM450.81 200.32l.435.244 8 4.5.775.436-.775.436-8 4.5-.436.245-.49-.872.436-.245L457.09 206H65.5v-1h391.591l-6.336-3.564-.436-.245.49-.872z"/><text id="request" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold" transform="rotate(76 113.291 143.266)"><tspan x="83.891" y="146.266">request</tspan></text><text id="connection-hangs" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="126" y="227">connection</tspan> <tspan x="142.519" y="245">hangs</tspan></text><text id="connection-breaks-en" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="12" font-weight="normal"><tspan x="186.09" y="264">connection breaks</tspan> <tspan x="196.479" y="282">end of request</tspan></text><path id="Line-3" fill="#C06334" fill-rule="nonzero" d="M90.233 80.79l.238.972 27.169 110.901 5.828-1.427L120 206.5l-10.13-11.932 5.827-1.429L88.529 82.238l-.238-.971 1.942-.476z"/><text id="data" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold" transform="rotate(-76 206.38 143.266)"><tspan x="189.58" y="146.266">data</tspan></text><path id="Line-2" fill="#C06334" fill-rule="nonzero" d="M233.226 82l3.107 15.341-5.794-1.565-29.574 109.485-.26.965-1.931-.521.26-.966L228.61 95.255l-5.792-1.565L233.226 82z"/><path id="Line-2-Copy" fill="#C06334" fill-rule="nonzero" d="M377.226 82l3.107 15.341-5.794-1.565-29.574 109.485-.26.965-1.931-.521.26-.966L372.61 95.255l-5.792-1.565L377.226 82z"/><text id="request-copy" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold" transform="rotate(76 260.291 143.266)"><tspan x="230.891" y="146.266">request</tspan></text><text id="connection-hangs-copy" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="270" y="227">connection</tspan> <tspan x="286.519" y="245">hangs</tspan></text><path id="Line-3-Copy-3" fill="#C06334" fill-rule="nonzero" d="M237.233 80.79l.238.972 27.169 110.901 5.828-1.427L267 206.5l-10.13-11.932 5.827-1.429-27.168-110.901-.238-.971 1.942-.476z"/><text id="request-copy-2" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold" transform="rotate(76 404.291 143.266)"><tspan x="374.891" y="146.266">request</tspan></text><path id="Line-3-Copy-4" fill="#C06334" fill-rule="nonzero" d="M381.233 80.79l.238.972 27.169 110.901 5.828-1.427L411 206.5l-10.13-11.932 5.827-1.429-27.168-110.901-.238-.971 1.942-.476z"/><text id="data-copy" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold" transform="rotate(-76 350.38 143.266)"><tspan x="333.58" y="146.266">data</tspan></text><path id="Line" stroke="#7E7C7B" stroke-dasharray="3,3" stroke-linecap="square" d="M235 40.75v201"/><text id="connection-breaks-en-copy" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="12" font-weight="normal"><tspan x="330.09" y="264">connection breaks</tspan> <tspan x="340.479" y="282">end of request</tspan></text><path id="Line-Copy" stroke="#7E7C7B" stroke-dasharray="3,3" stroke-linecap="square" d="M379 40.75v201"/></g></g></svg> \ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" width="521" height="320" viewBox="0 0 521 320"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="network" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="long-polling.svg"><text id="Browser" fill="#181717" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal"><tspan x="17" y="74">Navegador</tspan></text><text id="Server" fill="#181717" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal"><tspan x="23.5" y="226">Servidor</tspan></text><path id="Line" fill="#181717" fill-rule="nonzero" d="M450.81 75.82l.435.244 8 4.5.775.436-.775.436-8 4.5-.436.245-.49-.872.436-.245 6.336-3.564H66.5v-1h390.591l-6.336-3.564-.436-.245.49-.872zM450.81 200.32l.435.244 8 4.5.775.436-.775.436-8 4.5-.436.245-.49-.872.436-.245L457.09 206H65.5v-1h391.591l-6.336-3.564-.436-.245.49-.872z"/><text id="request" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold" transform="rotate(76 113.291 143.266)"><tspan x="75.891" y="146.266">solicitud</tspan></text><text id="connection-hangs" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="133" y="227">conexión</tspan> <tspan x="131.519" y="245">se cuelga</tspan></text><text id="connection-breaks-en" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="12" font-weight="normal"><tspan x="177.59" y="264">se rompe la conexión</tspan> <tspan x="190.479" y="282">fin de la solicitud</tspan></text><path id="Line-3" fill="#C06334" fill-rule="nonzero" d="M90.233 80.79l.238.972 27.169 110.901 5.828-1.427L120 206.5l-10.13-11.932 5.827-1.429L88.529 82.238l-.238-.971 1.942-.476z"/><text id="data" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold" transform="rotate(-76 206.38 143.266)"><tspan x="161.58" y="146.266">información</tspan></text><path id="Line-2" fill="#C06334" fill-rule="nonzero" d="M233.226 82l3.107 15.341-5.794-1.565-29.574 109.485-.26.965-1.931-.521.26-.966L228.61 95.255l-5.792-1.565L233.226 82z"/><path id="Line-2-Copy" fill="#C06334" fill-rule="nonzero" d="M377.226 82l3.107 15.341-5.794-1.565-29.574 109.485-.26.965-1.931-.521.26-.966L372.61 95.255l-5.792-1.565L377.226 82z"/><text id="request-copy" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold" transform="rotate(76 260.291 143.266)"><tspan x="222.891" y="146.266">solicitud</tspan></text><text id="connection-hangs-copy" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="277" y="227">conexión</tspan> <tspan x="275.519" y="245">se cuelga</tspan></text><path id="Line-3-Copy-3" fill="#C06334" fill-rule="nonzero" d="M237.233 80.79l.238.972 27.169 110.901 5.828-1.427L267 206.5l-10.13-11.932 5.827-1.429-27.168-110.901-.238-.971 1.942-.476z"/><text id="request-copy-2" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold" transform="rotate(76 404.291 143.266)"><tspan x="366.891" y="146.266">solicitud</tspan></text><path id="Line-3-Copy-4" fill="#C06334" fill-rule="nonzero" d="M381.233 80.79l.238.972 27.169 110.901 5.828-1.427L411 206.5l-10.13-11.932 5.827-1.429-27.168-110.901-.238-.971 1.942-.476z"/><text id="data-copy" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold" transform="rotate(-76 350.38 143.266)"><tspan x="305.58" y="146.266">información</tspan></text><path id="Line" stroke="#7E7C7B" stroke-dasharray="3,3" stroke-linecap="square" d="M235 40.75v201"/><text id="connection-breaks-en-copy" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="12" font-weight="normal"><tspan x="321.59" y="264">se rompe la conexión</tspan> <tspan x="334.479" y="282">fin de la solicitud</tspan></text><path id="Line-Copy" stroke="#7E7C7B" stroke-dasharray="3,3" stroke-linecap="square" d="M379 40.75v201"/></g></g></svg> \ No newline at end of file diff --git a/5-network/10-long-polling/longpoll.view/browser.js b/5-network/10-long-polling/longpoll.view/browser.js index 3a66aa5c6..f73ec95e4 100644 --- a/5-network/10-long-polling/longpoll.view/browser.js +++ b/5-network/10-long-polling/longpoll.view/browser.js @@ -1,4 +1,4 @@ -// Sending messages, a simple POST +// Envío de mensajes, un simple POST function PublishForm(form, url) { function sendMessage(message) { @@ -18,7 +18,7 @@ function PublishForm(form, url) { }; } -// Receiving messages with long polling +// Recibir mensajes con sondeo largo function SubscribePane(elem, url) { function showMessage(message) { @@ -31,18 +31,18 @@ function SubscribePane(elem, url) { let response = await fetch(url); if (response.status == 502) { - // Connection timeout - // happens when the connection was pending for too long - // let's reconnect + // El tiempo de conexión expiró + // sucede cuando la conexión estuvo pendiente durante demasiado tiempo + // vamos a reconectarnos await subscribe(); } else if (response.status != 200) { - // Show Error + // Mostrar Error showMessage(response.statusText); - // Reconnect in one second + // Volver a conectar en un segundo await new Promise(resolve => setTimeout(resolve, 1000)); await subscribe(); } else { - // Got message + // Tengo un mensaje let message = await response.text(); showMessage(message); await subscribe(); diff --git a/5-network/10-long-polling/longpoll.view/index.html b/5-network/10-long-polling/longpoll.view/index.html index 7452c1838..fafe9922b 100644 --- a/5-network/10-long-polling/longpoll.view/index.html +++ b/5-network/10-long-polling/longpoll.view/index.html @@ -1,11 +1,11 @@ <!DOCTYPE html> <script src="browser.js"></script> -All visitors of this page will see messages of each other. +Todos los visitantes de esta página verán mensajes entre ellos. <form name="publish"> <input type="text" name="message" /> - <input type="submit" value="Send" /> + <input type="submit" value="Enviar" /> </form> <div id="subscribe"> @@ -13,6 +13,6 @@ <script> new PublishForm(document.forms.publish, 'publish'); - // random url parameter to avoid any caching issues + // parámetro de url aleatorio para evitar problemas de almacenamiento en caché new SubscribePane(document.getElementById('subscribe'), 'subscribe?random=' + Math.random()); </script> diff --git a/5-network/10-long-polling/longpoll.view/server.js b/5-network/10-long-polling/longpoll.view/server.js index c3903e375..13697a7fc 100644 --- a/5-network/10-long-polling/longpoll.view/server.js +++ b/5-network/10-long-polling/longpoll.view/server.js @@ -34,28 +34,28 @@ function publish(message) { function accept(req, res) { let urlParsed = url.parse(req.url, true); - // new client wants messages + // El nuevo cliente quiere mensajes if (urlParsed.pathname == '/subscribe') { onSubscribe(req, res); return; } - // sending a message + // enviando un mensaje if (urlParsed.pathname == '/publish' && req.method == 'POST') { - // accept POST + // aceptar POST req.setEncoding('utf8'); let message = ''; req.on('data', function(chunk) { message += chunk; }).on('end', function() { - publish(message); // publish it to everyone + publish(message); // publicarlo para todos res.end("ok"); }); return; } - // the rest is static + // el resto es estático fileServer.serve(req, res); } @@ -71,7 +71,7 @@ function close() { if (!module.parent) { http.createServer(accept).listen(8080); - console.log('Server running on port 8080'); + console.log('Servidor que se ejecuta en el puerto 8080'); } else { exports.accept = accept; diff --git a/5-network/11-websocket/article.md b/5-network/11-websocket/article.md index 268b674f0..d1b618a54 100644 --- a/5-network/11-websocket/article.md +++ b/5-network/11-websocket/article.md @@ -1,57 +1,57 @@ # WebSocket -The `WebSocket` protocol, described in the specification [RFC 6455](https://datatracker.ietf.org/doc/html/rfc6455), provides a way to exchange data between browser and server via a persistent connection. The data can be passed in both directions as "packets", without breaking the connection and the need of additional HTTP-requests. +El protocolo `WebSocket`, descrito en la especificación [RFC 6455](https://datatracker.ietf.org/doc/html/rfc6455), brinda una forma de intercambiar datos entre el navegador y el servidor por medio de una conexión persistente. Los datos pueden ser pasados en ambas direcciones como paquetes "packets", sin cortar la conexión y sin pedidos adicionales de HTTP "HTTP-requests". -WebSocket is especially great for services that require continuous data exchange, e.g. online games, real-time trading systems and so on. +WebSocket es especialmente bueno para servicios que requieren intercambio de información continua, por ejemplo juegos en línea, sistemas de negocios en tiempo real, entre otros. -## A simple example +## Un ejemplo simple -To open a websocket connection, we need to create `new WebSocket` using the special protocol `ws` in the url: +Para abrir una conexión websocket, necesitamos crearla `new WebSocket` usando el protocolo especial `ws` en la url: ```js let socket = new WebSocket("*!*ws*/!*://javascript.info"); ``` -There's also encrypted `wss://` protocol. It's like HTTPS for websockets. +También hay una versión encriptada `wss://`. Equivale al HTTPS para los websockets. -```smart header="Always prefer `wss://`" -The `wss://` protocol is not only encrypted, but also more reliable. +```smart header="Siempre dé preferencia a `wss://`" +El protocolo `wss://` no solamente está encriptado, también es más confiable. -That's because `ws://` data is not encrypted, visible for any intermediary. Old proxy servers do not know about WebSocket, they may see "strange" headers and abort the connection. +Esto es porque los datos en `ws://` no están encriptados y son visibles para cualquier intermediario. Entonces los servidores proxy viejos que no reconocen el protocolo WebSocket podrían interpretar los datos como cabeceras "extrañas" y abortar la conexión. -On the other hand, `wss://` is WebSocket over TLS, (same as HTTPS is HTTP over TLS), the transport security layer encrypts the data at the sender and decrypts it at the receiver. So data packets are passed encrypted through proxies. They can't see what's inside and let them through. +En cambio `wss://` es WebSocket sobre TLS (al igual que HTTPS es HTTP sobre TLS), la seguridad de la capa de transporte encripta los datos en el envío y los desencripta en el destino. Así, los paquetes de datos pasan encriptados a través de los proxy, estos servidores no pueden ver lo que hay dentro y los dejan pasar. ``` -Once the socket is created, we should listen to events on it. There are totally 4 events: -- **`open`** -- connection established, -- **`message`** -- data received, -- **`error`** -- websocket error, -- **`close`** -- connection closed. +Una vez que el socket es creado, debemos escuchar los eventos que ocurren en él. Hay en total 4 eventos: +- **`open`** -- conexión establecida, +- **`message`** -- datos recibidos, +- **`error`** -- error en websocket, +- **`close`** -- conexión cerrada. -...And if we'd like to send something, then `socket.send(data)` will do that. +...Y si queremos enviar algo, `socket.send(data)` lo hará. -Here's an example: +Aquí un ejemplo: ```js run let socket = new WebSocket("wss://javascript.info/article/websocket/demo/hello"); socket.onopen = function(e) { - alert("[open] Connection established"); - alert("Sending to server"); - socket.send("My name is John"); + alert("[open] Conexión establecida"); + alert("Enviando al servidor"); + socket.send("Mi nombre es John"); }; socket.onmessage = function(event) { - alert(`[message] Data received from server: ${event.data}`); + alert(`[message] Datos recibidos del servidor: ${event.data}`); }; socket.onclose = function(event) { if (event.wasClean) { - alert(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`); + alert(`[close] Conexión cerrada limpiamente, código=${event.code} motivo=${event.reason}`); } else { - // e.g. server process killed or network down - // event.code is usually 1006 in this case - alert('[close] Connection died'); + // ej. El proceso del servidor se detuvo o la red está caída + // event.code es usualmente 1006 en este caso + alert('[close] La conexión se cayó'); } }; @@ -60,23 +60,23 @@ socket.onerror = function(error) { }; ``` -For demo purposes, there's a small server [server.js](demo/server.js) written in Node.js, for the example above, running. It responds with "Hello from server, John", then waits 5 seconds and closes the connection. +Para propósitos de demostración, tenemos un pequeño servidor [server.js](demo/server.js), escrito en Node.js, ejecutándose para el ejemplo de arriba. Este responde con "Hello from server, John", espera 5 segundos, y cierra la conexión. -So you'll see events `open` -> `message` -> `close`. +Entonces verás los eventos `open` -> `message` -> `close`. -That's actually it, we can talk WebSocket already. Quite simple, isn't it? +Eso es realmente todo, ya podemos conversar con WebSocket. Bastante simple, ¿no es cierto? -Now let's talk more in-depth. +Ahora hablemos más en profundidad. -## Opening a websocket +## Abriendo un websocket -When `new WebSocket(url)` is created, it starts connecting immediately. +Cuando se crea `new WebSocket(url)`, comienza la conexión de inmediato. -During the connection, the browser (using headers) asks the server: "Do you support Websocket?" And if the server replies "yes", then the talk continues in WebSocket protocol, which is not HTTP at all. +Durante la conexión, el navegador (usando cabeceras o "header") le pregunta al servidor: "¿Soportas Websockets?" y si si el servidor responde "Sí", la comunicación continúa en el protocolo WebSocket, que no es HTTP en absoluto. ![](websocket-handshake.svg) -Here's an example of browser headers for a request made by `new WebSocket("wss://javascript.info/chat")`. +Aquí hay un ejemplo de cabeceras de navegador para una petición hecha por `new WebSocket("wss://javascript.info/chat")`. ``` GET /chat @@ -88,17 +88,17 @@ Sec-WebSocket-Key: Iv8io/9s+lYFgZWcXczP8Q== Sec-WebSocket-Version: 13 ``` -- `Origin` -- the origin of the client page, e.g. `https://javascript.info`. WebSocket objects are cross-origin by nature. There are no special headers or other limitations. Old servers are unable to handle WebSocket anyway, so there are no compatibility issues. But the `Origin` header is important, as it allows the server to decide whether or not to talk WebSocket with this website. -- `Connection: Upgrade` -- signals that the client would like to change the protocol. -- `Upgrade: websocket` -- the requested protocol is "websocket". -- `Sec-WebSocket-Key` -- a random browser-generated key, used to ensure that the server supports WebSocket protocol. It's random to prevent proxies from caching any following communication. -- `Sec-WebSocket-Version` -- WebSocket protocol version, 13 is the current one. +- `Origin` -- La página de origen del cliente, ej. `https://javascript.info`. Los objetos WebSocket son cross-origin por naturaleza. No existen las cabeceras especiales ni otras limitaciones. De cualquier manera los servidores viejos son incapaces de manejar WebSocket, asi que no hay problemas de compatibilidad. Pero la cabecera `Origin` es importante, pues habilita al servidor decidir si permite o no la comunicación WebSocket con el sitio web. +- `Connection: Upgrade` -- señaliza que el cliente quiere cambiar el protocolo. +- `Upgrade: websocket` -- el protocolo requerido es "websocket". +- `Sec-WebSocket-Key` -- una clave de aleatoria generada por el navegador, usada para asegurar que el servidor soporta el protocolo WebSocket. Es aleatoria para evitar que servidores proxy almacenen en cache la comunicación que sigue. +- `Sec-WebSocket-Version` -- Versión del protocolo WebSocket, 13 es la actual. -```smart header="WebSocket handshake can't be emulated" -We can't use `XMLHttpRequest` or `fetch` to make this kind of HTTP-request, because JavaScript is not allowed to set these headers. +```smart header="El intercambio WebSocket no puede ser emulado" +No podemos usar `XMLHttpRequest` o `fetch` para hacer este tipo de peticiones HTTP, porque JavaScript no tiene permitido establecer esas cabeceras. ``` -If the server agrees to switch to WebSocket, it should send code 101 response: +Si el servidor concede el cambio a WebSocket, envía como respuesta el código 101: ``` 101 Switching Protocols @@ -107,29 +107,29 @@ Connection: Upgrade Sec-WebSocket-Accept: hsBlbuDTkk24srzEOTBUlZAlC2g= ``` -Here `Sec-WebSocket-Accept` is `Sec-WebSocket-Key`, recoded using a special algorithm. Upon seeing it, the browser understands that the server really does support the WebSocket protocol. +Aquí `Sec-WebSocket-Accept` es `Sec-WebSocket-Key`, recodificado usando un algoritmo especial. Al verlo, el navegador entiende que el servidor realmente soporta el protocolo WebSocket. -Afterwards, the data is transferred using the WebSocket protocol, we'll see its structure ("frames") soon. And that's not HTTP at all. +A continuación los datos son transferidos usando el protocolo WebSocket. Pronto veremos su estructura ("frames", marcos o cuadros en español). Y no es HTTP en absoluto. -### Extensions and subprotocols +### Extensiones y subprotocolos -There may be additional headers `Sec-WebSocket-Extensions` and `Sec-WebSocket-Protocol` that describe extensions and subprotocols. +Puede tener las cabeceras adicionales `Sec-WebSocket-Extensions` y `Sec-WebSocket-Protocol` que describen extensiones y subprotocolos. -For instance: +Por ejemplo: -- `Sec-WebSocket-Extensions: deflate-frame` means that the browser supports data compression. An extension is something related to transferring the data, functionality that extends the WebSocket protocol. The header `Sec-WebSocket-Extensions` is sent automatically by the browser, with the list of all extensions it supports. +- `Sec-WebSocket-Extensions: deflate-frame` significa que el navegador soporta compresión de datos. una extensión es algo relacionado a la transferencia de datos, funcionalidad que extiende el protocolo WebSocket. La cabecera `Sec-WebSocket-Extensions` es enviada automáticamente por el navegador, con la lista de todas las extensiones que soporta. -- `Sec-WebSocket-Protocol: soap, wamp` means that we'd like to transfer not just any data, but the data in [SOAP](https://en.wikipedia.org/wiki/SOAP) or WAMP ("The WebSocket Application Messaging Protocol") protocols. WebSocket subprotocols are registered in the [IANA catalogue](https://www.iana.org/assignments/websocket/websocket.xml). So, this header describes the data formats that we're going to use. +- `Sec-WebSocket-Protocol: soap, wamp` significa que queremos transferir no cualquier dato, sino datos en protocolos [SOAP](https://es.wikipedia.org/wiki/Simple_Object_Access_Protocol) o WAMP ("The WebSocket Application Messaging Protocol"). Los subprotocolos de WebSocket están registrados en el [catálogo IANA](https://www.iana.org/assignments/websocket/websocket.xml). Entonces, esta cabecera describe los formatos de datos que vamos a usar. - This optional header is set using the second parameter of `new WebSocket`. That's the array of subprotocols, e.g. if we'd like to use SOAP or WAMP: + Esta cabecera opcional se establece usando el segundo parámetro de `new WebSocket`, que es el array de subprotocolos. Por ejemplo, si queremos usar SOAP o WAMP: ```js let socket = new WebSocket("wss://javascript.info/chat", ["soap", "wamp"]); ``` -The server should respond with a list of protocols and extensions that it agrees to use. +El servidor debería responder con una lista de protocolos o extensiones que acepta usar. -For example, the request: +Por ejemplo, la petición: ``` GET /chat @@ -145,7 +145,7 @@ Sec-WebSocket-Protocol: soap, wamp */!* ``` -Response: +Respuesta: ``` 101 Switching Protocols @@ -158,49 +158,49 @@ Sec-WebSocket-Protocol: soap */!* ``` -Here the server responds that it supports the extension "deflate-frame", and only SOAP of the requested subprotocols. +Aquí el servidor responde que soporta la extensión "deflate-frame", y únicamente SOAP de los subprotocolos solicitados. -## Data transfer +## Transferencia de datos -WebSocket communication consists of "frames" -- data fragments, that can be sent from either side, and can be of several kinds: +La comunicación WebSocket consiste de "frames" (cuadros) de fragmentos de datos, que pueden ser enviados de ambos lados y pueden ser de varias clases: -- "text frames" -- contain text data that parties send to each other. -- "binary data frames" -- contain binary data that parties send to each other. -- "ping/pong frames" are used to check the connection, sent from the server, the browser responds to these automatically. -- there's also "connection close frame" and a few other service frames. +- "text frames" -- contiene datos de texto que las partes se mandan entre sí. +- "binary data frames" -- contiene datos binarios que las partes se mandan entre sí. +- "ping/pong frames" son usados para testear la conexión; enviados desde el servidor, el navegador responde automáticamente. +- También existe "connection close frame", y algunos otros frames de servicio. -In the browser, we directly work only with text or binary frames. +En el navegador, trabajamos directamente solamente con frames de texto y binarios. -**WebSocket `.send()` method can send either text or binary data.** +**El método WebSocket `.send()` puede enviar tanto datos de texto como binarios.** -A call `socket.send(body)` allows `body` in string or a binary format, including `Blob`, `ArrayBuffer`, etc. No settings are required: just send it out in any format. +Una llamada `socket.send(body)` permite en `body` datos en formato string o binarios, incluyendo `Blob`, `ArrayBuffer`, etc. No se requiere configuración: simplemente se envían en cualquier formato. -**When we receive the data, text always comes as string. And for binary data, we can choose between `Blob` and `ArrayBuffer` formats.** +**Cuando recibimos datos, el texto siempre viene como string. Y para datos binarios, podemos elegir entre los formatos `Blob` y `ArrayBuffer`.** -That's set by `socket.binaryType` property, it's `"blob"` by default, so binary data comes as `Blob` objects. +Esto se establece en la propiedad `socket.binaryType`, que es `"blob"` por defecto y entonces los datos binarios vienen como objetos `Blob`. -[Blob](info:blob) is a high-level binary object, it directly integrates with `<a>`, `<img>` and other tags, so that's a sane default. But for binary processing, to access individual data bytes, we can change it to `"arraybuffer"`: +[Blob](info:blob) es un objeto binario de alto nivel que se integra directamente con `<a>`, `<img>` y otras etiquetas, así que es una opción predeterminada saludable. Pero para procesamiento binario, para acceder a bytes individuales, podemos cambiarlo a `"arraybuffer"`: ```js socket.binaryType = "arraybuffer"; socket.onmessage = (event) => { - // event.data is either a string (if text) or arraybuffer (if binary) + // event.data puede ser string (si es texto) o arraybuffer (si es binario) }; ``` -## Rate limiting +## Limitaciones de velocidad -Imagine, our app is generating a lot of data to send. But the user has a slow network connection, maybe on a mobile internet, outside of a city. +Supongamos que nuestra app está generando un montón de datos para enviar. Pero el usuario tiene una conexión de red lenta, posiblemente internet móvil fuera de la ciudad. -We can call `socket.send(data)` again and again. But the data will be buffered (stored) in memory and sent out only as fast as network speed allows. +Podemos llamar `socket.send(data)` una y otra vez. Pero los datos serán acumulados en memoria (en un "buffer") y enviados solamente tan rápido como la velocidad de la red lo permita. -The `socket.bufferedAmount` property stores how many bytes remain buffered at this moment, waiting to be sent over the network. +La propiedad `socket.bufferedAmount` registra cuántos bytes quedan almacenados ("buffered") hasta el momento esperando a ser enviados a la red. -We can examine it to see whether the socket is actually available for transmission. +Podemos examinarla para ver si el "socket" está disponible para transmitir. ```js -// every 100ms examine the socket and send more data -// only if all the existing data was sent out +// examina el socket cada 100ms y envía más datos +// solamente si todos los datos existentes ya fueron enviados setInterval(() => { if (socket.bufferedAmount == 0) { socket.send(moreData()); @@ -209,25 +209,25 @@ setInterval(() => { ``` -## Connection close +## Cierre de conexión -Normally, when a party wants to close the connection (both browser and server have equal rights), they send a "connection close frame" with a numeric code and a textual reason. +Normalmente, cuando una parte quiere cerrar la conexión (servidor o navegador, ambos tienen el mismo derecho), envía un "frame de cierre de conexión" con un código numérico y un texto con el motivo. -The method for that is: +El método para eso es: ```js socket.close([code], [reason]); ``` -- `code` is a special WebSocket closing code (optional) -- `reason` is a string that describes the reason of closing (optional) +- `code` es un código especial de cierre de WebSocket (opcional) +- `reason` es un string que describe el motivo de cierre (opcional) -Then the other party in the `close` event handler gets the code and the reason, e.g.: +Entonces el manejador del evento `close` de la otra parte obtiene el código y el motivo, por ejemplo: ```js -// closing party: +// la parte que hace el cierre: socket.close(1000, "Work complete"); -// the other party +// la otra parte: socket.onclose = event => { // event.code === 1000 // event.reason === "Work complete" @@ -235,47 +235,47 @@ socket.onclose = event => { }; ``` -Most common code values: +Los códigos más comunes: -- `1000` -- the default, normal closure (used if no `code` supplied), -- `1006` -- no way to set such code manually, indicates that the connection was lost (no close frame). +- `1000` -- cierre normal. Es el predeterminado (usado si no se proporciona `code`), +- `1006` -- no hay forma de establecerlo manualmente, indica que la conexión se perdió (no hay frame de cierre). -There are other codes like: +Hay otros códigos como: -- `1001` -- the party is going away, e.g. server is shutting down, or a browser leaves the page, -- `1009` -- the message is too big to process, -- `1011` -- unexpected error on server, -- ...and so on. +- `1001` -- una parte se va, por ejemplo el server se está apagando, o el navegador deja la página, +- `1009` -- el mensaje es demasiado grande para procesar, +- `1011` -- error inesperado en el servidor, +- ...y así. -The full list can be found in [RFC6455, §7.4.1](https://tools.ietf.org/html/rfc6455#section-7.4.1). +La lista completa puede encontrarse en [RFC6455, §7.4.1](https://tools.ietf.org/html/rfc6455#section-7.4.1). -WebSocket codes are somewhat like HTTP codes, but different. In particular, codes lower than `1000` are reserved, there'll be an error if we try to set such a code. +Los códigos de WebSocket son como los que hay de HTTP, pero diferentes. En particular, los códigos menores a `1000` son reservados, habrá un error si tratamos de establecerlos. ```js -// in case connection is broken +// en caso de conexión que se rompe socket.onclose = event => { // event.code === 1006 // event.reason === "" - // event.wasClean === false (no closing frame) + // event.wasClean === false (no hay un frame de cierre) }; ``` -## Connection state +## Estado de la conexión -To get connection state, additionally there's `socket.readyState` property with values: +Para obtener el estado (state) de la conexión, tenemos la propiedad `socket.readyState` con valores: -- **`0`** -- "CONNECTING": the connection has not yet been established, -- **`1`** -- "OPEN": communicating, -- **`2`** -- "CLOSING": the connection is closing, -- **`3`** -- "CLOSED": the connection is closed. +- **`0`** -- "CONNECTING": la conexión aún no fue establecida, +- **`1`** -- "OPEN": comunicando, +- **`2`** -- "CLOSING": la conexión se está cerrando, +- **`3`** -- "CLOSED": la conexión está cerrada. -## Chat example +## Ejemplo Chat -Let's review a chat example using browser WebSocket API and Node.js WebSocket module <https://github.com/websockets/ws>. We'll pay the main attention to the client side, but the server is also simple. +Revisemos un ejemplo de chat usando la API WebSocket del navegador y el módulo WebSocket de Node.js <https://github.com/websockets/ws>. Prestaremos atención al lado del cliente, pero el servidor es igual de simple. -HTML: we need a `<form>` to send messages and a `<div>` for incoming messages: +HTML: necesitamos un `<form>` para enviar mensajes y un `<div>` para los mensajes entrantes: ```html <!-- message form --> @@ -288,17 +288,17 @@ HTML: we need a `<form>` to send messages and a `<div>` for incoming messages: <div id="messages"></div> ``` -From JavaScript we want three things: -1. Open the connection. -2. On form submission -- `socket.send(message)` for the message. -3. On incoming message -- append it to `div#messages`. +De JavaScript queremos tres cosas: +1. Abrir la conexión. +2. Ante el "submit" del form, enviar `socket.send(message)` el mensaje. +3. Al llegar un mensaje, agregarlo a `div#messages`. -Here's the code: +Aquí el código: ```js let socket = new WebSocket("wss://javascript.info/article/websocket/chat/ws"); -// send message from the form +// enviar el mensaje del form document.forms.publish.onsubmit = function() { let outgoingMessage = this.message.value; @@ -306,7 +306,7 @@ document.forms.publish.onsubmit = function() { return false; }; -// message received - show the message in div#messages +// mensaje recibido - muestra el mensaje en div#messages socket.onmessage = function(event) { let message = event.data; @@ -316,14 +316,14 @@ socket.onmessage = function(event) { } ``` -Server-side code is a little bit beyond our scope. Here we'll use Node.js, but you don't have to. Other platforms also have their means to work with WebSocket. +El código de servidor está fuera de nuestro objetivo. Aquí usaremos Node.js, pero no necesitas hacerlo. Otras plataformas también tienen sus formas de trabajar con WebSocket. -The server-side algorithm will be: +El algoritmo de lado de servidor será: -1. Create `clients = new Set()` -- a set of sockets. -2. For each accepted websocket, add it to the set `clients.add(socket)` and set `message` event listener to get its messages. -3. When a message is received: iterate over clients and send it to everyone. -4. When a connection is closed: `clients.delete(socket)`. +1. Crear `clients = new Set()` -- un conjunto de sockets. +2. Para cada websocket aceptado, sumarlo al conjunto `clients.add(socket)` y establecer un "event listener" `message` para obtener sus mensajes. +3. Cuando un mensaje es recibido: iterar sobre los clientes y enviarlo a todos ellos. +4. Cuando una conexión se cierra: `clients.delete(socket)`. ```js const ws = new require('ws'); @@ -332,8 +332,8 @@ const wss = new ws.Server({noServer: true}); const clients = new Set(); http.createServer((req, res) => { - // here we only handle websocket connections - // in real project we'd have some other code here to handle non-websocket requests + // aquí solo manejamos conexiones websocket + // en proyectos reales tendremos también algún código para manejar peticiones no websocket wss.handleUpgrade(req, req.socket, Buffer.alloc(0), onSocketConnect); }); @@ -341,7 +341,7 @@ function onSocketConnect(ws) { clients.add(ws); ws.on('message', function(message) { - message = message.slice(0, 50); // max message length will be 50 + message = message.slice(0, 50); // la longitud máxima del mensaje será 50 for(let client of clients) { client.send(message); @@ -355,34 +355,34 @@ function onSocketConnect(ws) { ``` -Here's the working example: +Aquí está el ejemplo funcionando: [iframe src="chat" height="100" zip] -You can also download it (upper-right button in the iframe) and run it locally. Just don't forget to install [Node.js](https://nodejs.org/en/) and `npm install ws` before running. +Puedes descargarlo (botón arriba/derecha en el iframe) y ejecutarlo localmente. No olvides instalar [Node.js](https://nodejs.org/en/) y `npm install ws` antes de hacerlo. -## Summary +## Resumen -WebSocket is a modern way to have persistent browser-server connections. +WebSocket es la forma moderna de tener conexiones persistentes entre navegador y servidor . -- WebSockets don't have cross-origin limitations. -- They are well-supported in browsers. -- Can send/receive strings and binary data. +- Los WebSockets no tienen limitaciones "cross-origin". +- Están muy bien soportados en los navegadores. +- Pueden enviar y recibir datos string y binarios. -The API is simple. +La API es simple. -Methods: +Métodos: - `socket.send(data)`, - `socket.close([code], [reason])`. -Events: +Eventos: - `open`, - `message`, - `error`, - `close`. -WebSocket by itself does not include reconnection, authentication and many other high-level mechanisms. So there are client/server libraries for that, and it's also possible to implement these capabilities manually. +El WebSocket por sí mismo no incluye reconexión, autenticación ni otros mecanismos de alto nivel. Hay librerías cliente/servidor para eso, y también es posible implementar esas capacidades manualmente. -Sometimes, to integrate WebSocket into existing projects, people run a WebSocket server in parallel with the main HTTP-server, and they share a single database. Requests to WebSocket use `wss://ws.site.com`, a subdomain that leads to the WebSocket server, while `https://site.com` goes to the main HTTP-server. +A veces, para integrar WebSocket a un proyecto existente, se ejecuta un servidor WebSocket en paralelo con el servidor HTTP principal compartiendo la misma base de datos. Las peticiones a WebSocket usan `wss://ws.site.com`, un subdominio que se dirige al servidor de WebSocket mientras que `https://site.com` va al servidor HTTP principal. -Surely, other ways of integration are also possible. +Seguro, otras formas de integración también son posibles. diff --git a/5-network/11-websocket/chat.view/index.html b/5-network/11-websocket/chat.view/index.html index 3dd4c24cd..d421db2cd 100644 --- a/5-network/11-websocket/chat.view/index.html +++ b/5-network/11-websocket/chat.view/index.html @@ -14,7 +14,7 @@ let socket = new WebSocket(url); -// send message from the form +// envío de mensaje desde el form document.forms.publish.onsubmit = function() { let outgoingMessage = this.message.value; @@ -22,7 +22,7 @@ return false; }; -// handle incoming messages +// manejo de mensajes entrantes socket.onmessage = function(event) { let incomingMessage = event.data; showMessage(incomingMessage); @@ -30,7 +30,7 @@ socket.onclose = event => console.log(`Closed ${event.code}`); -// show message in div#messages +// mostrar mensaje en div#messages function showMessage(message) { let messageElem = document.createElement('div'); messageElem.textContent = message; diff --git a/5-network/11-websocket/websocket-handshake.svg b/5-network/11-websocket/websocket-handshake.svg index 96c2cd3ef..3056139f1 100644 --- a/5-network/11-websocket/websocket-handshake.svg +++ b/5-network/11-websocket/websocket-handshake.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="429" height="348" viewBox="0 0 429 348"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="network" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="websocket-handshake.svg"><path id="Rectangle-227" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M2 16h128v64H2z"/><text id="Browser" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="40" y="49">Browser</tspan></text><path id="Rectangle-228" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M298 16h128v64H298z"/><text id="Server" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="340" y="49">Server</tspan></text><path id="Line" stroke="#7E7C7B" stroke-linecap="square" d="M67 81v250.5M363 81v251.5"/><path id="Line" fill="#C06334" fill-rule="nonzero" d="M349 133l14 7-14 7v-6H68v-2h281v-6z"/><path id="Line-Copy" fill="#C06334" fill-rule="nonzero" d="M83 204v6h281v2H83v6l-14-7 14-7z"/><text id="HTTP-request" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="172.015" y="130">HTTP-request</tspan></text><text id=""Hey,-server,-let's" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="102.605" y="161">"Hey, server, let's talk WebSocket?"</tspan></text><text id="HTTP-response-"Okay!" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="166.419" y="204">HTTP-response</tspan> <tspan x="191.972" y="226">"Okay!"</tspan></text><path id="Line-Copy-2" fill="#C06334" fill-rule="nonzero" d="M81 272v6h2v2h-2v6l-14-7 14-7zm268 0l14 7-14 7v-14zm-254 6v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm-126 0v2h-4v-2h4zm138 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm-126 0v2h-4v-2h4z" transform="matrix(-1 0 0 1 430 0)"/><text id="WebSocket-protocol" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="151.604" y="272">WebSocket protocol</tspan></text></g></g></svg> \ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" width="429" height="348" viewBox="0 0 429 348"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="network" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="websocket-handshake.svg"><path id="Rectangle-227" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M2 16h128v64H2z"/><text id="Browser" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="31" y="49">Navegador</tspan></text><path id="Rectangle-228" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M298 16h128v64H298z"/><text id="Server" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="333.5" y="49">Servidor</tspan></text><path id="Line" stroke="#7E7C7B" stroke-linecap="square" d="M67 81v250.5M363 81v251.5"/><path id="Line" fill="#C06334" fill-rule="nonzero" d="M349 133l14 7-14 7v-6H68v-2h281v-6z"/><path id="Line-Copy" fill="#C06334" fill-rule="nonzero" d="M83 204v6h281v2H83v6l-14-7 14-7z"/><text id="HTTP-request" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="169.515" y="130">Solicitud HTTP</tspan></text><text id=""Hey,-server,-let's" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="89.605" y="161">"Hola servidor, hablamos WebSocket?"</tspan></text><text id="HTTP-response-"Okay!" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="162.919" y="204">Respuesta HTTP</tspan> <tspan x="205.472" y="226">Ok!</tspan></text><path id="Line-Copy-2" fill="#C06334" fill-rule="nonzero" d="M81 272v6h2v2h-2v6l-14-7 14-7zm268 0l14 7-14 7v-14zm-260 6v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4z"/><text id="WebSocket-protocol" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="148.104" y="272">Protocolo WebSocket</tspan></text></g></g></svg> \ No newline at end of file diff --git a/5-network/12-server-sent-events/article.md b/5-network/12-server-sent-events/article.md index c59d671a4..2fd2c4345 100644 --- a/5-network/12-server-sent-events/article.md +++ b/5-network/12-server-sent-events/article.md @@ -1,80 +1,80 @@ -# Server Sent Events +# Eventos enviados por el servidor -The [Server-Sent Events](https://html.spec.whatwg.org/multipage/comms.html#the-eventsource-interface) specification describes a built-in class `EventSource`, that keeps connection with the server and allows to receive events from it. +La especificación de los [Eventos enviados por el servidor](https://html.spec.whatwg.org/multipage/comms.html#the-eventsource-interface) describe una clase incorporada `EventSource`, que mantiene la conexión con el servidor y permite recibir eventos de él. -Similar to `WebSocket`, the connection is persistent. +Similar a `WebSocket`, la conexión es persistente. -But there are several important differences: +Pero existen varias diferencias importantes: | `WebSocket` | `EventSource` | |-------------|---------------| -| Bi-directional: both client and server can exchange messages | One-directional: only server sends data | -| Binary and text data | Only text | -| WebSocket protocol | Regular HTTP | +| Bidireccional: tanto el cliente como el servidor pueden intercambiar mensajes | Unidireccional: solo el servidor envía datos | +| Datos binarios y de texto | Solo texto | +| Protocolo WebSocket | HTTP regular | -`EventSource` is a less-powerful way of communicating with the server than `WebSocket`. +`EventSource` es una forma menos poderosa de comunicarse con el servidor que `WebSocket`. -Why should one ever use it? +¿Por qué debería uno usarlo? -The main reason: it's simpler. In many applications, the power of `WebSocket` is a little bit too much. +El motivo principal: es más sencillo. En muchas aplicaciones, el poder de `WebSocket` es demasiado. -We need to receive a stream of data from server: maybe chat messages or market prices, or whatever. That's what `EventSource` is good at. Also it supports auto-reconnect, something we need to implement manually with `WebSocket`. Besides, it's a plain old HTTP, not a new protocol. +Necesitamos recibir un flujo de datos del servidor: tal vez mensajes de chat o precios de mercado, o lo que sea. Para eso es bueno `EventSource`. También admite la reconexión automática, algo que debemos implementar manualmente con `WebSocket`. Además, es HTTP común, no un protocolo nuevo. -## Getting messages +## Recibir mensajes -To start receiving messages, we just need to create `new EventSource(url)`. +Para comenzar a recibir mensajes, solo necesitamos crear un `new EventSource(url)`. -The browser will connect to `url` and keep the connection open, waiting for events. +El navegador se conectará a la `url` y mantendrá la conexión abierta, esperando eventos. -The server should respond with status 200 and the header `Content-Type: text/event-stream`, then keep the connection and write messages into it in the special format, like this: +El servidor debe responder con el estado 200 y el encabezado `Content-Type:text/event-stream`, entonces mantener la conexión y escribir mensajes en el formato especial, así: ``` -data: Message 1 +data: Mensaje 1 -data: Message 2 +data: Mensaje 2 -data: Message 3 -data: of two lines +data: Mensaje 3 +data: de dos líneas ``` -- A message text goes after `data:`, the space after the colon is optional. -- Messages are delimited with double line breaks `\n\n`. -- To send a line break `\n`, we can immediately send one more `data:` (3rd message above). +- Un mensaje de texto va después de `data:`, el espacio después de los dos puntos es opcional. +- Los mensajes están delimitados con saltos de línea dobles `\n\n`. +- Para enviar un salto de línea `\n`, podemos enviar inmediatamente un `data:` (tercer mensaje arriba) más. -In practice, complex messages are usually sent JSON-encoded. Line-breaks are encoded as `\n` within them, so multiline `data:` messages are not necessary. +En la práctica, los mensajes complejos generalmente se envían codificados en JSON. Los saltos de línea están codificados así `\n` dentro de los mensajes, por lo que los mensajes `data:` multilínea no son necesarios. -For instance: +Por ejemplo: ```js -data: {"user":"John","message":"First line*!*\n*/!* Second line"} +data: {"user":"John","message":"Primera línea*!*\n*/!* Segunda línea"} ``` -...So we can assume that one `data:` holds exactly one message. +... Entonces podemos asumir que un `data:` contiene exactamente un mensaje. -For each such message, the `message` event is generated: +Para cada uno de estos mensajes, se genera el evento `message`: ```js let eventSource = new EventSource("/events/subscribe"); eventSource.onmessage = function(event) { - console.log("New message", event.data); - // will log 3 times for the data stream above + console.log("Nuevo mensaje", event.data); + // registrará apuntes 3 veces para el flujo de datos anterior }; -// or eventSource.addEventListener('message', ...) +// o eventSource.addEventListener('message', ...) ``` -### Cross-origin requests +### Solicitudes Cross-origin -`EventSource` supports cross-origin requests, like `fetch` and any other networking methods. We can use any URL: +`EventSource` admite solicitudes cross-origin, como `fetch` o cualquier otro método de red. Podemos utilizar cualquier URL: ```js let source = new EventSource("https://another-site.com/events"); ``` -The remote server will get the `Origin` header and must respond with `Access-Control-Allow-Origin` to proceed. +El servidor remoto obtendrá el encabezado `Origin` y debe responder con `Access-Control-Allow-Origin` para continuar. -To pass credentials, we should set the additional option `withCredentials`, like this: +Para pasar las credenciales, debemos configurar la opción adicional `withCredentials`, así: ```js let source = new EventSource("https://another-site.com/events", { @@ -82,30 +82,30 @@ let source = new EventSource("https://another-site.com/events", { }); ``` -Please see the chapter <info:fetch-crossorigin> for more details about cross-origin headers. +Consulte el capítulo <info:fetch-crossorigin> para obtener más detalles sobre los encabezados cross-origin. -## Reconnection +## Reconexión -Upon creation, `new EventSource` connects to the server, and if the connection is broken -- reconnects. +Tras la creación con `new EventSource`, el cliente se conecta al servidor y, si la conexión se interrumpe, se vuelve a conectar. -That's very convenient, as we don't have to care about it. +Eso es muy conveniente, ya que no tenemos que preocuparnos por eso. -There's a small delay between reconnections, a few seconds by default. +Hay un pequeño retraso entre las reconexiones, unos segundos por defecto. -The server can set the recommended delay using `retry:` in response (in milliseconds): +El servidor puede establecer la demora recomendada usando `retry:` dentro de la respuesta (en milisegundos): ```js retry: 15000 -data: Hello, I set the reconnection delay to 15 seconds +data: Hola, configuré el retraso de reconexión en 15 segundos ``` -The `retry:` may come both together with some data, or as a standalone message. +El `retry:` puede venir junto con algunos datos, o como un mensaje independiente. -The browser should wait that many milliseconds before reconnecting. Or longer, e.g. if the browser knows (from OS) that there's no network connection at the moment, it may wait until the connection appears, and then retry. +El navegador debe esperar esa cantidad de milisegundos antes de volver a conectarse. O más, por ejemplo: si el navegador sabe (desde el sistema operativo) que no hay conexión de red en este momento, puede esperar hasta que aparezca la conexión y luego volver a intentarlo. -- If the server wants the browser to stop reconnecting, it should respond with HTTP status 204. -- If the browser wants to close the connection, it should call `eventSource.close()`: +- Si el servidor desea que el navegador deje de volver a conectarse, debería responder con el estado HTTP 204. +- Si el navegador quiere cerrar la conexión, debe llamar a `eventSource.close()`: ```js let eventSource = new EventSource(...); @@ -113,159 +113,159 @@ let eventSource = new EventSource(...); eventSource.close(); ``` -Also, there will be no reconnection if the response has an incorrect `Content-Type` or its HTTP status differs from 301, 307, 200 and 204. In such cases the `"error"` event will be emitted, and the browser won't reconnect. +Además, no habrá reconexión si la respuesta tiene un `Content-Type` incorrecto o su estado HTTP difiere de 301, 307, 200 y 204. En tales casos, se emitirá el evento `"error"` y el navegador no se volverá a conectar. ```smart -When a connection is finally closed, there's no way to "reopen" it. If we'd like to connect again, just create a new `EventSource`. +Cuando una conexión finalmente se cierra, no hay forma de "reabrirla". Si queremos conectarnos de nuevo, simplemente crea un nuevo `EventSource`. ``` -## Message id +## ID del mensaje -When a connection breaks due to network problems, either side can't be sure which messages were received, and which weren't. +Cuando una conexión se interrumpe debido a problemas de red, ninguna de las partes puede estar segura de qué mensajes se recibieron y cuáles no. -To correctly resume the connection, each message should have an `id` field, like this: +Para reanudar correctamente la conexión, cada mensaje debe tener un campo `id`, así: ``` -data: Message 1 +data: Mensaje 1 id: 1 -data: Message 2 +data: Mensaje 2 id: 2 -data: Message 3 -data: of two lines +data: Mensaje 3 +data: de dos líneas id: 3 ``` -When a message with `id:` is received, the browser: +Cuando se recibe un mensaje con `id:`, el navegador: -- Sets the property `eventSource.lastEventId` to its value. -- Upon reconnection sends the header `Last-Event-ID` with that `id`, so that the server may re-send following messages. +- Establece la propiedad `eventSource.lastEventId` a su valor. +- Tras la reconexión, el navegador envía el encabezado `Last-Event-ID` con ese `id`, para que el servidor pueda volver a enviar los siguientes mensajes. -```smart header="Put `id:` after `data:`" -Please note: the `id` is appended below message `data` by the server, to ensure that `lastEventId` is updated after the message is received. +```smart header="Pon `id:` después de `data:`" +Ten en cuenta: el `id` es adjuntado debajo del mensaje `data` por el servidor, para garantizar que `lastEventId` se actualice después de recibir el mensaje. ``` -## Connection status: readyState +## Estado de conexión: readyState -The `EventSource` object has `readyState` property, that has one of three values: +El objeto `EventSource` tiene la propiedad `readyState`, que tiene uno de tres valores: ```js no-beautify -EventSource.CONNECTING = 0; // connecting or reconnecting -EventSource.OPEN = 1; // connected -EventSource.CLOSED = 2; // connection closed +EventSource.CONNECTING = 0; // conectando o reconectando +EventSource.OPEN = 1; // conectado +EventSource.CLOSED = 2; // conexión cerrada ``` -When an object is created, or the connection is down, it's always `EventSource.CONNECTING` (equals `0`). +Cuando se crea un objeto, o la conexión no funciona, siempre es `EventSource.CONNECTING` (es igual a `0`). -We can query this property to know the state of `EventSource`. +Podemos consultar esta propiedad para conocer el estado de `EventSource`. -## Event types +## Tipos de eventos -By default `EventSource` object generates three events: +Por defecto, el objeto `EventSource` genera tres eventos: -- `message` -- a message received, available as `event.data`. -- `open` -- the connection is open. -- `error` -- the connection could not be established, e.g. the server returned HTTP 500 status. +- `message` -- un mensaje recibido, disponible como `event.data`. +- `open` -- la conexión está abierta. +- `error` -- no se pudo establecer la conexión, por ejemplo, el servidor devolvió el estado HTTP 500. -The server may specify another type of event with `event: ...` at the event start. +El servidor puede especificar otro tipo de evento con `event: ...` al inicio del evento. -For example: +Por ejemplo: ``` event: join data: Bob -data: Hello +data: Hola event: leave data: Bob ``` -To handle custom events, we must use `addEventListener`, not `onmessage`: +Para manejar eventos personalizados, debemos usar `addEventListener`, no `onmessage`: ```js eventSource.addEventListener('join', event => { - alert(`Joined ${event.data}`); + alert(`Se unió ${event.data}`); }); eventSource.addEventListener('message', event => { - alert(`Said: ${event.data}`); + alert(`Dijo: ${event.data}`); }); eventSource.addEventListener('leave', event => { - alert(`Left ${event.data}`); + alert(`Salió ${event.data}`); }); ``` -## Full example +## Ejemplo completo -Here's the server that sends messages with `1`, `2`, `3`, then `bye` and breaks the connection. +Aquí está el servidor que envía mensajes con `1`, `2`, `3`, luego `bye` y cierra la conexión. -Then the browser automatically reconnects. +Luego, el navegador se vuelve a conectar automáticamente. [codetabs src="eventsource"] -## Summary +## Resumen -`EventSource` object automatically establishes a persistent connection and allows the server to send messages over it. +El objeto `EventSource` establece automáticamente una conexión persistente y permite al servidor enviar mensajes a través de él. -It offers: -- Automatic reconnect, with tunable `retry` timeout. -- Message ids to resume events, the last received identifier is sent in `Last-Event-ID` header upon reconnection. -- The current state is in the `readyState` property. +Ofrece: +- Reconexión automática, con tiempo de espera de `reintento` ajustable. +- IDs en cada mensaje para reanudar los eventos, el último identificador recibido se envía en el encabezado `Last-Event-ID` al volver a conectarse. +- El estado actual está en la propiedad `readyState`. -That makes `EventSource` a viable alternative to `WebSocket`, as the latter is more low-level and lacks such built-in features (though they can be implemented). +Eso hace que `EventSource` sea una alternativa viable a `WebSocket`, ya que es de un nivel más bajo y carece de esas características integradas (aunque se pueden implementar). -In many real-life applications, the power of `EventSource` is just enough. +En muchas aplicaciones de la vida real, el poder de `EventSource` es suficiente. -Supported in all modern browsers (not IE). +Compatible con todos los navegadores modernos (no IE). -The syntax is: +La sintaxis es: ```js let source = new EventSource(url, [credentials]); ``` -The second argument has only one possible option: `{ withCredentials: true }`, it allows sending cross-origin credentials. +El segundo argumento tiene solo una opción posible: `{withCredentials: true}`, permite enviar credenciales de cross-origin. -Overall cross-origin security is same as for `fetch` and other network methods. +La seguridad general de cross-origin es la misma que para `fetch` y otros métodos de red. -### Properties of an `EventSource` object +### Propiedades de un objeto `EventSource` `readyState` -: The current connection state: either `EventSource.CONNECTING (=0)`, `EventSource.OPEN (=1)` or `EventSource.CLOSED (=2)`. +: El estado de conexión actual: `EventSource.CONNECTING (=0)`, `EventSource.OPEN (=1)` o `EventSource.CLOSED (=2)`. `lastEventId` -: The last received `id`. Upon reconnection the browser sends it in the header `Last-Event-ID`. +: El último `id` recibido. Tras la reconexión, el navegador lo envía en el encabezado `Last-Event-ID`. -### Methods +### Métodos `close()` -: Closes the connection. +: Cierra la conexión. -### Events +### Eventos `message` -: Message received, the data is in `event.data`. +: Mensaje recibido, los datos están en `event.data`. `open` -: The connection is established. +: Se establece la conexión. `error` -: In case of an error, including both lost connection (will auto-reconnect) and fatal errors. We can check `readyState` to see if the reconnection is being attempted. +: En caso de error, se incluyen tanto la pérdida de conexión (se reconectará automáticamente) como los errores fatales. Podemos comprobar `readyState` para ver si se está intentando la reconexión. -The server may set a custom event name in `event:`. Such events should be handled using `addEventListener`, not `on<event>`. +El servidor puede establecer un nombre de evento personalizado en `event:`. Tales eventos deben manejarse usando `addEventListener`, no `on<evento>`. -### Server response format +### Formato de respuesta del servidor -The server sends messages, delimited by `\n\n`. +El servidor envía mensajes, delimitados por `\n\n`. -A message may have following fields: +Un mensaje puede tener los siguientes campos: -- `data:` -- message body, a sequence of multiple `data` is interpreted as a single message, with `\n` between the parts. -- `id:` -- renews `lastEventId`, sent in `Last-Event-ID` on reconnect. -- `retry:` -- recommends a retry delay for reconnections in ms. There's no way to set it from JavaScript. -- `event:` -- event name, must precede `data:`. +- `data:` -- cuerpo del mensaje, una secuencia de múltiples `datos` se interpreta como un solo mensaje, con `\n` entre las partes. +- `id:` -- renueva `lastEventId`, enviado en el encabezado `Last-Event-ID` al volver a conectarse. +- `retry:` -- recomienda una demora de reintento para las reconexiones en milisegundos. No hay forma de configurarlo desde JavaScript. +- `event:` -- nombre del evento, debe preceder a `data:`. -A message may include one or more fields in any order, but `id:` usually goes the last. +Un mensaje puede incluir uno o más campos en cualquier orden, pero `id:` suele ser el último. diff --git a/5-network/12-server-sent-events/eventsource.view/index.html b/5-network/12-server-sent-events/eventsource.view/index.html index 795b07ebb..ca526be98 100644 --- a/5-network/12-server-sent-events/eventsource.view/index.html +++ b/5-network/12-server-sent-events/eventsource.view/index.html @@ -2,38 +2,38 @@ <script> let eventSource; -function start() { // when "Start" button pressed +function start() { // cuando se presiona el botón "Inicio" if (!window.EventSource) { - // IE or an old browser - alert("The browser doesn't support EventSource."); + // IE o un navegador antiguo + alert("El navegador no es compatible con EventSource."); return; } eventSource = new EventSource('digits'); eventSource.onopen = function(e) { - log("Event: open"); + log("Evento: open"); }; eventSource.onerror = function(e) { - log("Event: error"); + log("Evento: error"); if (this.readyState == EventSource.CONNECTING) { - log(`Reconnecting (readyState=${this.readyState})...`); + log(`Reconectando (readyState=${this.readyState})...`); } else { - log("Error has occured."); + log("Un error ha ocurrido."); } }; eventSource.addEventListener('bye', function(e) { - log("Event: bye, data: " + e.data); + log("Evento: bye, datos: " + e.data); }); eventSource.onmessage = function(e) { - log("Event: message, data: " + e.data); + log("Evento: message, datos: " + e.data); }; } -function stop() { // when "Stop" button pressed +function stop() { // cuando se presiona el botón "Detener" eventSource.close(); log("eventSource.close()"); } @@ -44,7 +44,7 @@ } </script> -<button onclick="start()">Start</button> Press the "Start" to begin. +<button onclick="start()">Inicio</button> Presiona "Inicio" para comenzar. <div id="logElem" style="margin: 6px 0"></div> -<button onclick="stop()">Stop</button> "Stop" to finish. +<button onclick="stop()">Detener</button> "Detener" para terminar. diff --git a/5-network/index.md b/5-network/index.md index 31f86f092..61a0f7fb8 100644 --- a/5-network/index.md +++ b/5-network/index.md @@ -1,2 +1,2 @@ -# Network requests +# Solicitudes de red diff --git a/6-data-storage/01-cookie/article.md b/6-data-storage/01-cookie/article.md index 1b9e93414..c2b771cf6 100644 --- a/6-data-storage/01-cookie/article.md +++ b/6-data-storage/01-cookie/article.md @@ -1,81 +1,81 @@ # Cookies, document.cookie -Cookies are small strings of data that are stored directly in the browser. They are a part of the HTTP protocol, defined by the [RFC 6265](https://tools.ietf.org/html/rfc6265) specification. +Las cookies son pequeñas cadenas de datos que se almacenan directamente en el navegador. Son parte del protocolo HTTP, definido por la especificación [RFC 6265](https://tools.ietf.org/html/rfc6265). -Cookies are usually set by a web server using the response `Set-Cookie` HTTP header. Then, the browser automatically adds them to (almost) every request to the same domain using the `Cookie` HTTP header. +Las cookies generalmente se establecen desde un servidor web utilizando la cabecera de respuesta HTTP `Set-Cookie`. Luego, el navegador los agrega automáticamente a (casi) toda solicitud al mismo dominio usando la cabecera HTTP `Cookie`. -One of the most widespread use cases is authentication: +Uno de los casos de uso más difundidos es la autenticación: -1. Upon sign-in, the server uses the `Set-Cookie` HTTP header in the response to set a cookie with a unique "session identifier". -2. Next time the request is sent to the same domain, the browser sends the cookie over the net using the `Cookie` HTTP header. -3. So the server knows who made the request. +1. Al iniciar sesión, el servidor usa la cabecera HTTP `Set-Cookie` en respuesta para establecer una cookie con un "identificador de sesión" único. +2. Al enviar la siguiente solicitud al mismo dominio, el navegador envía la cookie usando la cabecera HTTP `Cookie`. +3. Así el servidor sabe quién hizo la solicitud. -We can also access cookies from the browser, using `document.cookie` property. +También podemos acceder a las cookies desde el navegador usando la propiedad `document.cookie`. -There are many tricky things about cookies and their attributes. In this chapter, we'll cover them in detail. +Hay muchas complejidades en las cookies y sus atributos. En este artículo las veremos en detalle. -## Reading from document.cookie +## Leyendo a document.cookie ```online -Does your browser store any cookies from this site? Let's see: +¿Puede tu navegador almacenar cookies de este sitio? Veamos: ``` ```offline -Assuming you're on a website, it's possible to see the cookies from it, like this: +Asumiendo que estás en un sitio web, es posible ver sus cookies así: ``` ```js run -// At javascript.info, we use Google Analytics for statistics, -// so there should be some cookies +// En javascript.info, usamos Google Analytics para estadísticas, +// así que debería haber algunas cookies alert( document.cookie ); // cookie1=value1; cookie2=value2;... ``` -The value of `document.cookie` consists of `name=value` pairs, delimited by `; `. Each one is a separate cookie. +El valor de `document.cookie` consiste de pares `name=value` delimitados por `; `. Cada uno es una cookie separada. -To find a particular cookie, we can split `document.cookie` by `; `, and then find the right name. We can use either a regular expression or array functions to do that. +Para encontrar una cookie particular, podemos separar `document.cookie` por `; ` y encontrar el nombre correcto. Podemos usar tanto una expresión regular como funciones de array para ello. -We leave it as an exercise for the reader. Also, at the end of the chapter, you'll find helper functions to manipulate cookies. +Lo dejamos como ejercicio para el lector. Al final del artículo encontrarás funciones de ayuda para manipular cookies. -## Writing to document.cookie +## Escribiendo en document.cookie -We can write to `document.cookie`. But it's not a data property, it's an [accessor (getter/setter)](info:property-accessors). An assignment to it is treated specially. +Podemos escribir en `document.cookie`. Pero no es una propiedad de datos, es un [accessor (getter/setter)](info:property-accessors). Una asignación a él se trata especialmente. -**A write operation to `document.cookie` updates only the cookie mentioned in it and doesn't touch other cookies.** +**Una operación de escritura a `document.cookie` actualiza solo las cookies mencionadas en ella, y no toca las demás.** -For instance, this call sets a cookie with the name `user` and value `John`: +Por ejemplo, este llamado establece una cookie con el nombre `user` y el valor `John`: ```js run -document.cookie = "user=John"; // update only cookie named 'user' -alert(document.cookie); // show all cookies +document.cookie = "user=John"; // modifica solo la cookie llamada 'user' +alert(document.cookie); // muestra todas las cookies ``` -If you run it, you will likely see multiple cookies. That's because the `document.cookie=` operation does not overwrite all cookies. It only sets the mentioned cookie `user`. +Si lo ejecutas, probablemente verás múltiples cookies. Esto es porque la operación `document.cookie=` no sobrescribe todas las cookies. Solo configura la cookie mencionada `user`. -Technically, name and value can have any characters. To keep the valid formatting, they should be escaped using a built-in `encodeURIComponent` function: +Técnicamente, nombre y valor pueden tener cualquier carácter. Pero para mantener un formato válido, los caracteres especiales deben escaparse usando la función integrada `encodeURIComponent`: ```js run -// special characters (spaces) need encoding +// los caracteres especiales (espacios), necesitan codificarse let name = "my name"; let value = "John Smith" -// encodes the cookie as my%20name=John%20Smith +// codifica la cookie como my%20name=John%20Smith document.cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value); alert(document.cookie); // ...; my%20name=John%20Smith ``` -```warn header="Limitations" -There are a few limitations: -- You can only set/update a single cookie at a time using `document.cookie`. -- The `name=value` pair, after `encodeURIComponent`, should not exceed 4KB. So we can't store anything huge in a cookie. -- The total number of cookies per domain is limited to around 20+, the exact limit depends on the browser. +```warn header="Limitaciones" +Hay algunas limitaciones: +- Solo puedes establecer/modificar una cookie a la vez usando `document.cookie`. +- El par `name=value`, después del `encodeURIComponent`, no debe exceder 4KB. Así que no podemos almacenar algo enorme en una cookie. +- La cantidad total de cookies por dominio está limitada a alrededor de más de 20, el límite exacto depende del navegador. ``` -Cookies have several attributes, many of which are important and should be set. +Las cookies tienen varios atributos, muchos de ellos importantes y deberían ser configurados. -The attributes are listed after `key=value`, delimited by `;`, like this: +Las atributos son listados después de `key=value`, delimitadas por un `;`: ```js run document.cookie = "user=John; path=/; expires=Tue, 19 Jan 2038 03:14:07 GMT" @@ -85,89 +85,89 @@ document.cookie = "user=John; path=/; expires=Tue, 19 Jan 2038 03:14:07 GMT" - **`domain=site.com`** -A domain defines where the cookie is accessible. In practice though, there are limitations. We can't set any domain. +Un dominio define dónde la cookie es accesible. Aunque en la práctica hay limitaciones y no podemos configurar cualquier dominio. -**There's no way to let a cookie be accessible from another 2nd-level domain, so `other.com` will never receive a cookie set at `site.com`.** +**No hay forma de hacer que una cookie sea accesible desde otro dominio de segundo nivel, entonces `other.com` nunca recibirá una cookie establecida en `site.com`.** -It's a safety restriction, to allow us to store sensitive data in cookies that should be available only on one site. +Es una restricción de seguridad, para permitirnos almacenar datos sensibles en cookies que deben estar disponibles para un único sitio solamente. -By default, a cookie is accessible only at the domain that set it. +De forma predeterminada, una cookie solo es accesible en el dominio que la establece. -Please note, by default, a cookie is not shared with a subdomain, such as `forum.site.com`. +Pero toma nota: de forma predeterminada, una cookie tampoco es compartida por un subdominio (como `forum.site.com`). ```js -// if we set a cookie at site.com website... +// en site.com document.cookie = "user=John" -// ...we won't see it at forum.site.com +// en forum.site.com alert(document.cookie); // no user ``` -...But this can be changed. If we'd like to allow subdomains like `forum.site.com` to get a cookie set at `site.com`, that's possible. +... aunque esto puede cambiarse. Si queremos permitir que un subdominio como `forum.site.com` obtenga una cookie establecida por `site.com`, es posible hacerlo. -For that to happen, when setting a cookie at `site.com`, we should explicitly set the `domain` attribute to the root domain: `domain=site.com`. Then all subdomains will see such a cookie. +Para ello, cuando establecemos una cookie en `site.com`, debemos establecer explícitamente la raíz del dominio en el atributo `domain`: `domain=site.com`. Así todos los subdominios verán la cookie. -For example: +Por ejemplo: ```js -// at site.com -// make the cookie accessible on any subdomain *.site.com: +// en site.com +// hacer la cookie accesible en cualquier subdominio *.site.com: document.cookie = "user=John; *!*domain=site.com*/!*" -// later +// ...luego -// at forum.site.com -alert(document.cookie); // has cookie user=John +// en forum.site.com +alert(document.cookie); // tiene la cookie user=John ``` -```warn header="Legacy syntax" -Historically, `domain=.site.com` (with a dot before `site.com`) used to work the same way, allowing access to the cookie from subdomains. Leading dots in domain names are now ignored, but some browsers may decline to set the cookie containing such dots. +```warn header="Sintaxis heredada" +Históricamente, `domain=.site.com` (con un punto antes de `site.com`) se usaba para este propósito, permitir el acceso a la cookie desde los subdominios. Actualmente, los puntos al inicio de nombres de dominio se ignoran, pero algunos navegadores podrían rechazar la configuración de cookies que los contengan. ``` -To summarize, the `domain` attribute allows to make a cookie accessible at subdomains. +Resumiendo, el atributo `domain` permite que las cookies sean accesibles en los subdominios. -## path +## path - **`path=/mypath`** -The URL path prefix must be absolute. It makes the cookie accessible for pages under that path. By default, it's the current path. +La ruta URL del prefijo `path` debe ser absoluta. Esto hace que la cookie sea accesible a las páginas bajo esa ruta. De forma predeterminada, es la ruta actual. -If a cookie is set with `path=/admin`, it's visible on pages `/admin` and `/admin/something`, but not at `/home`, `/home/admin` or `/`. +Si una cookie se establece con `path=/admin`, será visible en las páginas `/admin` y `/admin/something`, pero no en `/home`, `/adminpage`, o `/`. -Usually, we should set `path` to the root: `path=/` to make the cookie accessible from all website pages. If this attribute is not set the default is calculated using [this method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#path_default_value). +Lo usual es establecer `path` en la raíz: `path=/`, para hacer la cookie accesible a todas las páginas del sitio web. Si este atributo no se establece, el predeterminado es calculado unsando [este método](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#path_default_value). ## expires, max-age -By default, if a cookie doesn't have one of these attributes, it disappears when the browser/tab is closed. Such cookies are called "session cookies" +De forma predeterminada, una cookie desaparece cuando el navegador se cierra. Tales cookies se denominan "cookies de sesión". -To let cookies survive a browser close, we can set either the `expires` or `max-age` attribute. `max-Age` has precedence if both are set. +Para que las cookies sobrevivan al cierre del navegador, podemos establecer uno de estos atrubutos: `expires` o `max-age`. - **`expires=Tue, 19 Jan 2038 03:14:07 GMT`** -The cookie expiration date defines the time when the browser will automatically delete it (according to the browser's time zone). +La fecha de expiración define el momento en que el navegador la borrará automáticamente (según la zona horaria del navegador). -The date must be exactly in this format, in the GMT timezone. We can use `date.toUTCString` to get it. For instance, we can set the cookie to expire in 1 day: +La fecha debe estar exactamente en ese formato, en el huso horario GMT. Podemos obtenerlo con `date.toUTCString`. Por ejemplo, podemos configurar que la cookie expire en un día: ```js -// +1 day from now +// +1 día desde ahora let date = new Date(Date.now() + 86400e3); date = date.toUTCString(); document.cookie = "user=John; expires=" + date; ``` -If we set `expires` to a date in the past, the cookie is deleted. +Si establecemos `expires` en una fecha en el pasado, la cookie es eliminada. - **`max-age=3600`** -It's an alternative to `expires` and specifies the cookie's expiration in seconds from the current moment. +`max-age` es una alternativa a `expires`, y especifica la expiración de la cookie en segundos desde el momento actual. -If set to zero or a negative value, the cookie is deleted: +Si la configuramos a cero, o a un valor negativo, la cookie se elimina: ```js -// cookie will die in +1 hour from now +// la cookie morirá en +1 hora a partir de ahora document.cookie = "user=John; max-age=3600"; -// delete cookie (let it expire right now) +// borra la cookie (la hacemos expirar ya) document.cookie = "user=John; max-age=0"; ``` @@ -175,125 +175,125 @@ document.cookie = "user=John; max-age=0"; - **`secure`** -The cookie should be transferred only over HTTPS. +La cookie debe ser transferida solamente a través de HTTPS. -**By default, if we set a cookie at `http://site.com`, then it also appears at `https://site.com` and vice versa.** +**De forma predeterminada, si establecemos una cookie en `http://site.com`, entonces también aparece en `https://site.com` y viceversa.** -That is, cookies are domain-based, they do not distinguish between the protocols. +Esto es, las cookies están basadas en el dominio, no distinguen entre protocolos. -With this attribute, if a cookie is set by `https://site.com`, then it doesn't appear when the same site is accessed by HTTP, as `http://site.com`. So if a cookie has sensitive content that should never be sent over unencrypted HTTP, the `secure` flag is the right thing. +Con el atributo `secure`, si una cookie se establece para `https://site.com`, entonces no aparecerá cuando el mismo sitio sea accedido por HTTP, como `http://site.com`. Entonces, si una cookie tiene información sensible que nunca debe ser enviada sobre HTTP sin encriptar, debe configurarse `secure`. ```js -// assuming we're on https:// now -// set the cookie to be secure (only accessible over HTTPS) +// asumiendo que estamos en https:// ahora +// configuramos la cookie para ser segura (solo accesible sobre HTTPS) document.cookie = "user=John; secure"; ``` ## samesite -This is another security attribute `samesite`. It's designed to protect from so-called XSRF (cross-site request forgery) attacks. +Este es otro atributo de seguridad. `samesite` está diseñado para protección contra los ataques XSRF (cross-site request forgery, falsificación de solicitud entre sitios). -To understand how it works and when it's useful, let's take a look at XSRF attacks. +Para entender cómo funciona y su utilidad, veamos primero los ataques XSRF. -### XSRF attack +### ataque XSRF -Imagine, you are logged into the site `bank.com`. That is: you have an authentication cookie from that site. Your browser sends it to `bank.com` with every request so that it recognizes you and performs all sensitive financial operations. +Imagina que tienes una sesión en el sitio `bank.com`. Esto es: tienes una cookie de autenticación para ese sitio. Tu navegador lo envía a `bank.com` en cada solicitud, así aquel te reconoce y ejecuta todas las operaciones financieras sensibles. -Now, while browsing the web in another window, you accidentally come to another site `evil.com`. That site has JavaScript code that submits a form `<form action="https://bank.com/pay">` to `bank.com` with fields that initiate a transaction to the hacker's account. +Ahora, mientras navegas la red en otra ventana, accidentalmente entras en otro sitio `evil.com`. Este sitio tiene código JavaScript que envía un formulario `<form action="https://bank.com/pay">` a `bank.com` con los campos que inician una transacción hacia la cuenta del hacker. -The browser sends cookies every time you visit the site `bank.com`, even if the form was submitted from `evil.com`. So the bank recognizes you and performs the payment. +El navegador envía cookies cada vez que visitas el sitio `bank.com`, incluso si el form fue enviado desde `evil.com`. Entonces el banco te reconoce y realmente ejecuta el pago. ![](cookie-xsrf.svg) -This is a so-called "Cross-Site Request Forgery" (in short, XSRF) attack. +Ese es el ataque llamado "Cross-Site Request Forgery" (XSRF). -Real banks are protected from it of course. All forms generated by `bank.com` have a special field, a so-called "XSRF protection token", that an evil page can't generate or extract from a remote page. It can submit a form there, but can't get the data back. The site `bank.com` checks for such a token in every form it receives. +Los bancos reales están protegidos contra esto por supuesto. Todos los formularios generados por `bank.com` tienen un campo especial, llamado "token de protección XSRF", que una página maliciosa no puede generar o extraer desde una página remota. Puede enviar el form, pero no obtiene respuesta a la solicitud. El sitio `bank.com` verifica tal token en cada form que recibe. -Such a protection takes time to implement though. We need to ensure that every form has the required token field, and we must also check all requests. +Tal protección toma tiempo para implementarla. Necesitamos asegurarnos de que cada form tiene dicho campo token, y debemos verificar todas las solicitudes. -### Use cookie samesite attribute +### Uso del atributo samesite -The cookie `samesite` attribute provides another way to protect from such attacks, that (in theory) should not require "xsrf protection tokens". +El atributo `samesite` brinda otra forma de protegerse de tales ataques, que (en teoría) no requiere el "token de protección XSRF". -It has two possible values: +Tiene dos valores posibles: - **`samesite=strict`** -A cookie with `samesite=strict` is never sent if the user comes from outside the same site. +Una cookie con `samesite=strict` nunca es enviada si el usuario viene desde fuera del mismo sitio. -In other words, whether a user follows a link from their email, submits a form from `evil.com`, or does any operation that originates from another domain, the cookie is not sent. +En otras palabras, si el usuario sigue un enlace desde su correo, envía un form desde `evil.com`, o hace cualquier operación originada desde otro dominio, la cookie no será enviada. -If authentication cookies have the `samesite=strict` attribute, then an XSRF attack has no chance of succeeding, because a submission from `evil.com` comes without cookies. So `bank.com` will not recognize the user and will not proceed with the payment. +Cuando las cookies de autenticación tienen el atributo `samesite=strict`, un ataque XSRF no tiene posibilidad de éxito, porque el envío de `evil.com` llega sin cookies. Así `bank.com` no reconoce el usuario y no procederá con el pago. -The protection is quite reliable. Only operations that come from `bank.com` will send the `samesite=strict` cookie, e.g. a form submission from another page at `bank.com`. +Esta protección es muy confiable. Solo las operaciones que provienen de `bank.com` enviarán la cookie `samesite=strict`, por ejemplo, el envío de un form desde otra página en `bank.com`. -Although, there's a small inconvenience. +Aunque hay un pequeño inconveniente. -When a user follows a legitimate link to `bank.com`, like from their notes, they'll be surprised that `bank.com` does not recognize them. Indeed, `samesite=strict` cookies are not sent in that case. +Cuando el usuario sigue un enlace legítimo a `bank.com`, por ejemplo desde sus propio correo, será sorprendido con que `bank.com` no lo reconoce. Efectivamente, las cookies `samesite=strict` no son enviadas en ese caso. -We could work around that by using two cookies: one for "general recognition", only to say: "Hello, John", and the other one for data-changing operations with `samesite=strict`. Then, a person coming from outside of the site will see a welcome, but payments must be initiated from the bank's website, for the second cookie to be sent. +Podemos sortear esto usando dos cookies: una para el "reconocimiento general", con el solo propósito de decir: "Hola, John", y la otra para operaciones de datos con `samesite=strict`. Entonces, la persona que venga desde fuera del sitio llega a la página de bienvenida, pero los pagos serían iniciados desde dentro del sitio web del banco, entonces la segunda cookie sí será enviada. -- **`samesite=lax` (same as `samesite` without value)** +- **`samesite=lax` (es lo mismo que `samesite` sin un valor)** -A more relaxed approach that also protects from XSRF and doesn't break the user experience. +Un enfoque más laxo que también protege de ataques XSRF y no afecta la experiencia de usuario. -Lax mode, just like `strict`, forbids the browser to send cookies when coming from outside the site, but adds an exception. +El modo `lax`, como `strict`, prohíbe al navegador enviar cookies cuando viene desde fuera del sitio, pero agrega una excepción. -A `samesite=lax` cookie is sent if both of these conditions are true: -1. The HTTP method is "safe" (e.g. GET, but not POST). +Una cookie `samesite=lax` es enviada si se cumplen dos condiciones: +1. El método HTTP es seguro (por ejemplo GET, pero no POST). - The full list of safe HTTP methods is in the [RFC7231 specification](https://tools.ietf.org/html/rfc7231#section-4.2.1). These are the methods that should be used for reading, but not writing the data. They must not perform any data-changing operations. Following a link is always GET, the safe method. + La lista completa de métodos seguros HTTP está en la especificación [RFC7231](https://tools.ietf.org/html/rfc7231#section-4.2.1). Son métodos que deben ser usados para leer, pero no escribir datos. No debem ejecutar ninguna operación de alteración de datos. Seguir un enlace es siempre GET, el método seguro. -2. The operation performs a top-level navigation (changes URL in the browser address bar). +2. La operación ejecuta una navegación del más alto nivel (cambia la URL en la barra de dirección del navegador). - This is usually true, but if the navigation is performed in an `<iframe>`, then it is not top-level. Additionally, JavaScript methods for network requests do not perform any navigation. + Generalmente esto es verdad, pero si la navegación es ejecutada dentro de un `<iframe>`, entonces no es de alto nivel. Además, los métodos JavaScript para solicitudes de red no ejecutan ninguna navegación. -So, what `samesite=lax` does, is to allow the most common "go to URL" operation to have cookies. E.g. opening a website link from notes that satisfy these conditions. +Entonces, lo que hace `samesite=lax` es permitir la operación más común "ir a URL" para obtener cookies. Por ejemplo, abrir un sitio desde el link en una agenda sí satisface estas condiciones. -But anything more complicated, like a network request from another site or a form submission, loses cookies. +Pero cualquier cosa más complicada, como solicitudes de red desde otro sitio, o un "form submit", pierde las cookies. -If that's fine for you, then adding `samesite=lax` will probably not break the user experience and add protection. +Si esto es adecuado para ti, entonces agregar `samesite=lax` probablemente no dañe la experiencia de usuario y agrega protección. -Overall, `samesite` is a great attribute. +Por sobre todo, `samesite` es un atributo excelente. -There's a drawback: +Tiene una importante debilidad: -- `samesite` is ignored (not supported) by very old browsers, the year 2017 or so. +- `samesite` es ignorado (no soportado) por navegadores viejos, de hasta alrededor de 2017. -**So if we solely rely on `samesite` to provide protection, then old browsers will be vulnerable.** +**Así que si solo confiamos en `samesite` para brindar protección, habrá navegadores que serán vulnerables.** -But we can use `samesite` together with other protection measures, like xsrf tokens, to add a layer of defence and then, in the future, when old browsers die out, we'll probably be able to drop xsrf tokens. +Pero podemos usar `samesite` junto con otras medidas de protección, como los tokens xsrf, para agregar una capa adicional de defensa. En el futuro, cuando los viejos navegadores mueran, probablemente podamos descartar la necesidad de tokens xsrf. ## httpOnly -This attribute has nothing to do with JavaScript, but we have to mention it for completeness. +Est atributo no tiene nada que ver con JavaScript, pero debemos mencionarlo para completar la guía. -The web server uses the `Set-Cookie` header to set a cookie. Also, it may set the `httpOnly` attribute. +El servidor web usa la cabecera `Set-Cookie` para establecer la cookie. También puede establecer el atributo `httpOnly`. -This attribute forbids any JavaScript access to the cookie. We can't see such a cookie or manipulate it using `document.cookie`. +Este atributo impide a JavaScript todo acceso a la cookie. No podemos ver ni manipular tal cookie usando `document.cookie`. -This is used as a precautionary measure, to protect from certain attacks when a hacker injects his own JavaScript code into a page and waits for a user to visit that page. That shouldn't be possible at all, hackers should not be able to inject their code into our site, but there may be bugs that let them do it. +Esto es usado como medida de precaución, para proteger de ciertos ataques donde el hacker inyecta su propio código JavaScript en una página y espera que el usuario visite esa página. Esto no debería ser posible en absoluto, los hackers no deberían poder insertar su código en nuestro sitio, pero puede haber bugs que les permitan hacerlo. -Normally, if such a thing happens, and a user visits a web-page with a hacker's JavaScript code, then that code executes and gains access to `document.cookie` with user cookies containing authentication information. That's bad. +Normalmente, si eso sucede y el usuario visita una página web con el código JavaScript del hacker, entonces ese código se ejecuta y gana acceso a `document.cookie` con las cookies del usuario conteniendo información de autenticación. Eso es malo. -But if a cookie is `httpOnly`, then `document.cookie` doesn't see it, so it is protected. +Pero si una cookie es `httpOnly`, `document.cookie` no la ve y está protegida. -## Appendix: Cookie functions +## Apéndice: Funciones de cookies -Here's a small set of functions to work with cookies, more convenient than a manual modification of `document.cookie`. +Aquí hay un pequeño conjunto de funciones para trabajar con cookies, más conveniente que la modificación manual de `document.cookie`. -There exist many cookie libraries for that, so these are for demo purposes. Fully working though. +Existen muchas librerías de cookies para eso, asi que estas son para demostración solamente. Aunque completamente funcionales. ### getCookie(name) -The shortest way to access a cookie is to use a [regular expression](info:regular-expressions). +La forma más corta de acceder a una cookie es usar una [expresión regular](info:regular-expressions). -The function `getCookie(name)` returns the cookie with the given `name`: +La función `getCookie(name)` devuelve la cookie con el nombre `name` dado: ```js -// returns the cookie with the given name, -// or undefined if not found +// devuelve la cookie con el nombre dado, +// o undefined si no la encuentra function getCookie(name) { let matches = document.cookie.match(new RegExp( "(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)" @@ -302,20 +302,20 @@ function getCookie(name) { } ``` -Here `new RegExp` is generated dynamically, to match `; name=<value>`. +Aquí `new RegExp` se genera dinámicamente para coincidir `; name=<value>`. -Please note that a cookie value is encoded, so `getCookie` uses a built-in `decodeURIComponent` function to decode it. +Nota que el valor de una cookie está codificado, entonces `getCookie` usa una función integrada `decodeURIComponent` para decodificarla. ### setCookie(name, value, attributes) -Sets the cookie's `name` to the given `value` with `path=/` by default (can be modified to add other defaults): +Establece el nombre de la cookie `name` al valor dado `value`, con la ruta por defecto `path=/`, y puede ser modificada para agregar otros valores predeterminados: ```js run function setCookie(name, value, attributes = {}) { attributes = { path: '/', - // add other defaults here if necessary + // agregar otros valores predeterminados si es necesario ...attributes }; @@ -336,13 +336,13 @@ function setCookie(name, value, attributes = {}) { document.cookie = updatedCookie; } -// Example of use: +// Ejemplo de uso: setCookie('user', 'John', {secure: true, 'max-age': 3600}); ``` ### deleteCookie(name) -To delete a cookie, we can call it with a negative expiration date: +Para borrar una cookie, podemos llamarla con una fecha de expiración negativa: ```js function deleteCookie(name) { @@ -352,87 +352,87 @@ function deleteCookie(name) { } ``` -```warn header="Updating or deleting must use same path and domain" -Please note: when we update or delete a cookie, we should use exactly the same path and domain attributes as when we set it. +```warn header="La modificación o eliminación debe usar la misma ruta y dominio" +Por favor nota que cuando alteramos o borramos una cookie debemos usar exactamente el mismo "path" y "domain" que cuando la establecimos. ``` -Together: [cookie.js](cookie.js). +Completo: [cookie.js](cookie.js). -## Appendix: Third-party cookies +## Apéndice: Cookies de terceros -A cookie is called "third-party" if it's placed by a domain other than the page the user is visiting. +Una cookie es llamada "third-party" o "de terceros" si es colocada por un dominio distinto al de la página que el usuario está visitando. -For instance: -1. A page at `site.com` loads a banner from another site: `<img src="https://ads.com/banner.png">`. -2. Along with the banner, the remote server at `ads.com` may set the `Set-Cookie` header with a cookie like `id=1234`. Such a cookie originates from the `ads.com` domain, and will only be visible at `ads.com`: +Por ejemplo: +1. Una página en `site.com` carga un banner desde otro sitio: `<img src="https://ads.com/banner.png">`. +2. Junto con el banner, el servidor remoto en `ads.com` puede configurar la cabecera `Set-Cookie` con una cookie como `id=1234`. Tal cookie tiene origen en el dominio `ads.com`, y será visible solamente en `ads.com`: ![](cookie-third-party.svg) -3. Next time when `ads.com` is accessed, the remote server gets the `id` cookie and recognizes the user: +3. La próxima vez que se accede a `ads.com`, el servidor remoto obtiene la cookie `id` y reconoce al usuario: ![](cookie-third-party-2.svg) -4. What's even more important is, when the user moves from `site.com` to another site `other.com`, which also has a banner, then `ads.com` gets the cookie, as it belongs to `ads.com`, thus recognizing the visitor and tracking him as he moves between sites: +4. Lo que es más importante aquí, cuando el usuario cambia de `site.com` a otro sitio `other.com` que también tiene un banner, entonces `ads.com` obtiene la cookie porque pertenece a `ads.com`, reconociendo al visitante y su movimiento entre sitios: ![](cookie-third-party-3.svg) -Third-party cookies are traditionally used for tracking and ads services, due to their nature. They are bound to the originating domain, so `ads.com` can track the same user between different sites, if they all access it. +Las cookies de terceros son tradicionalmente usados para rastreo y servicios de publicidad (ads) debido a su naturaleza. Ellas están vinculadas al dominio de origen, entonces `ads.com` puede rastrear al mismo usuario a través de diferentes sitios si ellos los acceden. -Naturally, some people don't like being tracked, so browsers allow them to disable such cookies. +Naturalmente, a algunos no les gusta ser seguidos, así que los navegadores permiten deshabilitar tales cookies. -Also, some modern browsers employ special policies for such cookies: -- Safari does not allow third-party cookies at all. -- Firefox comes with a "black list" of third-party domains where it blocks third-party cookies. +Además, algunos navegadores modernos emplean políticas especiales para tales cookies: +- Safari no permite cookies de terceros en absoluto. +- Firefox viene con una "lista negra" de dominios de terceros y bloquea las cookies de tales orígenes. ```smart -If we load a script from a third-party domain, like `<script src="https://google-analytics.com/analytics.js">`, and that script uses `document.cookie` to set a cookie, then such cookie is not third-party. +Si cargamos un script desde un dominio de terceros, como `<script src="https://google-analytics.com/analytics.js">`, y ese script usa `document.cookie` para configurar una cookie, tal cookie no es "de terceros". -If a script sets a cookie, then no matter where the script came from -- the cookie belongs to the domain of the current webpage. +Si un script configura una cookie, no importa de dónde viene el script: la cookie pertenece al dominio de la página web actual. ``` -## Appendix: GDPR +## Apéndice: GDPR -This topic is not related to JavaScript at all, it is just something to keep in mind when setting cookies. +Este tópico no está relacionado a JavaScript en absoluto, solo es algo para tener en mente cuando configuramos cookies. -There's a legislation in Europe called GDPR, that enforces a set of rules for websites to respect the users' privacy. One of these rules is to require explicit permission for tracking cookies from the user. +Hay una legislación en Europa llamada GDPR. Es un conjunto de reglas que fuerza a los sitios web a respetar la privacidad del usuario. Una de estas reglas es requerir el permiso explícito del usuario para el uso de cookies de seguimiento. -Please note, that's only about tracking/identifying/authorizing cookies. +Nota que esto solo se refiere a cookies de seguimiento, identificación y autorización. -So, if we set a cookie that just saves some information, but neither tracks nor identifies the user, then we are free to do it. +Así que si queremos configurar una cookie que solo guarda alguna información pero no hace seguimiento ni identificación del usuario, somos libres de hacerlo. -But if we are going to set a cookie with an authentication session or a tracking ID, then a user must allow that. +Pero si vamos a configurar una cookie con una sesión de autenticación o un id de seguimiento, el usuario debe dar su permiso. -Websites generally have two variants of complying with GDPR. You are likely to have seen them both on the web: +Los sitios web generalmente tienen dos variantes para cumplir con el GDPR. Debes de haberlas visto en la web: -1. If a website wants to set tracking cookies only for authenticated users. +1. Si un sitio web quiere establecer cookies de seguimiento solo para usuarios autenticados. - To do so, the registration form should have a checkbox like "accept the privacy policy" (that describes how cookies are used), the user must check it, and then the website is free to set auth cookies. + Para hacerlo, el form de registro debe tener un checkbox como: "aceptar la política de privacidad" (que describe cómo las cookies son usadas), el usuario debe marcarlo, entonces el sitio web es libre para establecer cookies de autenticación. -2. If a website wants to set tracking cookies for everyone. +2. Si un sitio web quiere establecer cookies de seguimiento a todo visitante. - To do so legally, a website shows a modal "splash screen" for newcomers and requires them to agree to the cookies. Then the website can set them and let people see the content. That can be disturbing for new visitors though. No one likes to see such "must-click" modal splash screens instead of the content. But GDPR requires an explicit agreement. + Para hacerlo legalmente, el sitio web muestra un mensaje del tipo "pantalla de bienvenida (splash screen)" a los recién llegados que les pide aceptar las cookies. Entonces el sitio web puede configurarlas y les deja ver el contenido. Esto puede ser molesto para el visitante. A nadie le gusta que aparezca una pantalla modal con la obligación de cliquear en ella en lugar del contenido. Pero el GDPR requiere el acuerdo explícito. -GDPR is not only about cookies, it is about other privacy-related issues too, but that is beyond our scope. +El GDPR no trata solo de cookies, también es acerca de otros problemas relacionados a la privacidad, pero eso va más allá de nuestro objetivo. -## Summary +## Resumen -`document.cookie` provides access to cookies. -- Write operations modify only the cookie mentioned in it. -- Name/value must be encoded. -- One cookie may not exceed 4KB in size. The number of cookies allowed on a domain is around 20+ (varies by browser). +`document.cookie` brinda acceso a las cookies. +- la operación de escritura modifica solo cookies mencionadas en ella. +- nombre y valor deben estar codificados. +- Una cookie no debe exceder los 4 KB. El número de cookies está limitado a alrededor de más de 20 por sitio (depende del navegador). -Cookie attributes: -- `path=/`, by default current path, makes the cookie visible only under that path. -- `domain=site.com`, by default a cookie is visible on the current domain only. If the domain is set explicitly, the cookie becomes visible on subdomains. -- `expires` or `max-age` sets the cookie expiration time. Without them, the cookie dies when the browser is closed. -- `secure` makes the cookie HTTPS-only. -- `samesite` forbids the browser to send the cookie with requests coming from outside the site. This helps to prevent XSRF attacks. +Atributos de Cookie: +- `path=/`, por defecto la ruta actual, hace la cookie visible solo bajo esa ruta. +- `domain=site.com`, por defecto una cookie es visible solo en el dominio actual. Si el domino se establece explícitamente, la cookie se hace visible a los subdominios. +- `expires` o `max-age` configuran el tiempo de expiración de la cookie. Sin ellas la cookie muere cuando el navegador se cierra. +- `secure` hace la cookie solo para HTTPS. +- `samesite` prohíbe al navegador enviar la cookie a solicitudes que vengan desde fuera del sitio. Esto ayuda a prevenir ataques XSRF. -Additionally: -- The browser may forbid third-party cookies, e.g. Safari does that by default. There is also work in progress to implement this in Chrome. -- When setting a tracking cookie for EU citizens, GDPR requires to ask for permission. +Adicionalmente: +- El navegador puede prohibir las cookies de terceros. Por ejemplo, Safari lo hace por defecto. +- Cuando se configuran cookies de seguimiento para ciudadanos de la UE, la regulación GDPR requiere la autorización del usuario. diff --git a/6-data-storage/01-cookie/cookie-xsrf.svg b/6-data-storage/01-cookie/cookie-xsrf.svg index 961a8078f..55cd08e47 100644 --- a/6-data-storage/01-cookie/cookie-xsrf.svg +++ b/6-data-storage/01-cookie/cookie-xsrf.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="668" height="166" viewBox="0 0 668 166"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="data-storage" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="cookie-xsrf.svg"><path id="Rectangle-1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M38 48h282v88H38z"/><text id="<form-action="https:" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="48" y="69"><form action="https://bank.com/pay"></tspan> <tspan x="48" y="88"> ....</tspan> <tspan x="48" y="107"></form></tspan></text><text id="evil.com" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="149" y="36">evil.com</tspan></text><path id="Rectangle-1-Copy" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M505 48h140v88H505z"/><text id="got-the-cookie?-okay" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="526.469" y="108">got the cookie?</tspan> <tspan x="558.308" y="127">okay!</tspan></text><text id="bank.com" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="543" y="36">bank.com</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M321.643 67.375l.991.134 163.127 22.119.806-5.946L499.5 92.5l-14.814 5.055.806-5.945-163.126-22.12-.991-.133.268-1.982z"/><text id="POST-/pay" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" transform="rotate(7 409.576 67.26)"><tspan x="376.076" y="72.76">POST /pay</tspan></text><text id="cookie:-user=John" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" transform="rotate(7 407.076 90.26)"><tspan x="349.076" y="95.76">cookie: user=John</tspan></text></g></g></svg> \ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" width="668" height="166" viewBox="0 0 668 166"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="data-storage" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="cookie-xsrf.svg"><path id="Rectangle-1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M38 48h282v88H38z"/><text id="<form-action="https:" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="48" y="69"><form action="https://bank.com/pay"></tspan> <tspan x="48" y="88"> ....</tspan> <tspan x="48" y="107"></form></tspan></text><text id="evil.com" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="149" y="36">evil.com</tspan></text><path id="Rectangle-1-Copy" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M505 48h140v88H505z"/><text id="got-the-cookie?-okay" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="518.969" y="108">obtuvo la cookie?</tspan> <tspan x="564.308" y="127">Ok!</tspan></text><text id="bank.com" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="543" y="36">bank.com</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M321.643 67.375l.991.134 163.127 22.119.806-5.946L499.5 92.5l-14.814 5.055.806-5.945-163.126-22.12-.991-.133.268-1.982z"/><text id="POST-/pay" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" transform="rotate(7 409.576 67.26)"><tspan x="376.076" y="72.76">POST /pay</tspan></text><text id="cookie:-user=John" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" transform="rotate(7 407.076 90.26)"><tspan x="349.076" y="95.76">cookie: user=John</tspan></text></g></g></svg> \ No newline at end of file diff --git a/6-data-storage/02-localstorage/1-form-autosave/task.md b/6-data-storage/02-localstorage/1-form-autosave/task.md index 2b147d79c..76ed702f2 100644 --- a/6-data-storage/02-localstorage/1-form-autosave/task.md +++ b/6-data-storage/02-localstorage/1-form-autosave/task.md @@ -1,10 +1,10 @@ -# Autosave a form field +# Guardar automáticamente un campo de formulario -Create a `textarea` field that "autosaves" its value on every change. +Crea un campo `textarea` que "autoguarde" sus valores en cada cambio. -So, if the user accidentally closes the page, and opens it again, he'll find his unfinished input at place. +Entonces, si el usuario cierra accidentalmente la página y la abre de nuevo, encontrará su entrada inacabada en su lugar. -Like this: +Como esto: [iframe src="solution" height=120] diff --git a/6-data-storage/02-localstorage/article.md b/6-data-storage/02-localstorage/article.md index a99bcb653..74c30c373 100644 --- a/6-data-storage/02-localstorage/article.md +++ b/6-data-storage/02-localstorage/article.md @@ -1,84 +1,83 @@ # LocalStorage, sessionStorage -Web storage objects `localStorage` and `sessionStorage` allow to save key/value pairs in the browser. +Los objetos de almacenaje web `localStorage` y `sessionStorage` permiten guardar pares de clave/valor en el navegador. -What's interesting about them is that the data survives a page refresh (for `sessionStorage`) and even a full browser restart (for `localStorage`). We'll see that very soon. +Lo que es interesante sobre ellos es que los datos sobreviven a una recarga de página (en el caso de `sessionStorage`) y hasta un reinicio completo de navegador (en el caso de `localStorage`). Lo veremos en breve. -We already have cookies. Why additional objects? +Ya tenemos cookies. ¿Por qué tener objetos adicionales? -- Unlike cookies, web storage objects are not sent to server with each request. Because of that, we can store much more. Most modern browsers allow at least 5 megabytes of data (or more) and have settings to configure that. -- Also unlike cookies, the server can't manipulate storage objects via HTTP headers. Everything's done in JavaScript. -- The storage is bound to the origin (domain/protocol/port triplet). That is, different protocols or subdomains infer different storage objects, they can't access data from each other. +- Al contrario que las cookies, los objetos de almacenaje web no se envían al servidor en cada petición. Debido a esto, podemos almacenar mucha más información. La mayoría de los navegadores modernos permiten almacenar, como mínimo, 5 megabytes de datos y tienen opciones para configurar estos límites. +- También diferente de las cookies es que el servidor no puede manipular los objetos de almacenaje via cabeceras HTTP, todo se hace via JavaScript. +- El almacenaje está vinculado al orígen (al triplete dominio/protocolo/puerto). Esto significa que distintos protocolos o subdominios tienen distintos objetos de almacenaje, no pueden acceder a otros datos que no sean los suyos. -Both storage objects provide the same methods and properties: +Ambos objetos de almacenaje proveen los mismos métodos y propiedades: -- `setItem(key, value)` -- store key/value pair. -- `getItem(key)` -- get the value by key. -- `removeItem(key)` -- remove the key with its value. -- `clear()` -- delete everything. -- `key(index)` -- get the key on a given position. -- `length` -- the number of stored items. +- `setItem(clave, valor)` -- almacenar un par clave/valor. +- `getItem(clave)` -- obtener el valor por medio de la clave. +- `removeItem(clave)` -- eliminar la clave y su valor. +- `clear()` -- borrar todo. +- `key(índice)` -- obtener la clave de una posición dada. +- `length` -- el número de ítems almacenados. -As you can see, it's like a `Map` collection (`setItem/getItem/removeItem`), but also allows access by index with `key(index)`. +Como puedes ver, es como una colección `Map` (`setItem/getItem/removeItem`), pero también permite el acceso a través de index con `key(index)`. -Let's see how it works. +Vamos a ver cómo funciona. -## localStorage demo +## Demo de localStorage -The main features of `localStorage` are: +Las principales funcionalidades de `localStorage` son: -- Shared between all tabs and windows from the same origin. -- The data does not expire. It remains after the browser restart and even OS reboot. - -For instance, if you run this code... +- Es compartido entre todas las pestañas y ventanas del mismo origen. +- Los datos no expiran. Persisten a los reinicios de navegador y hasta del sistema operativo. +Por ejemplo, si ejecutas éste código... ```js run localStorage.setItem('test', 1); ``` -...And close/open the browser or just open the same page in a different window, then you can get it like this: +... y cierras/abres el navegador, o simplemente abres la misma página en otra ventana, puedes coger el ítem que hemos guardado de este modo: ```js run alert( localStorage.getItem('test') ); // 1 ``` -We only have to be on the same origin (domain/port/protocol), the url path can be different. +Solo tenemos que estar en el mismo dominio/puerto/protocolo, la url puede ser distinta. -The `localStorage` is shared between all windows with the same origin, so if we set the data in one window, the change becomes visible in another one. +`localStorage` es compartido por toda las ventanas del mismo origen, de modo que si guardamos datos en una ventana, el cambio es visible en la otra. -## Object-like access +## Acceso tipo Objeto -We can also use a plain object way of getting/setting keys, like this: +También podemos utilizar un modo de acceder/guardar claves del mismo modo que se hace con objetos, así: ```js run -// set key +// guarda una clave localStorage.test = 2; -// get key +// coge una clave alert( localStorage.test ); // 2 -// remove key +// borra una clave delete localStorage.test; ``` -That's allowed for historical reasons, and mostly works, but generally not recommended, because: +Esto se permite por razones históricas, y principalmente funciona, pero en general no se recomienda por dos motivos: -1. If the key is user-generated, it can be anything, like `length` or `toString`, or another built-in method of `localStorage`. In that case `getItem/setItem` work fine, while object-like access fails: +1. Si la clave es generada por el usuario, puede ser cualquier cosa, como `length` o `toString`, u otro método propio de `localStorage`. En este caso `getItem/setItem` funcionan correctamente, pero el acceso de simil-objeto falla; ```js run let key = 'length'; - localStorage[key] = 5; // Error, can't assign length + localStorage[key] = 5; // Error, no se puede asignar 'length' ``` + +2. Existe un evento `storage`, que se dispara cuando modificamos los datos. Este evento no se dispara si utilizamos el acceso tipo objeto. Lo veremos más tarde en este capítulo. -2. There's a `storage` event, it triggers when we modify the data. That event does not happen for object-like access. We'll see that later in this chapter. - -## Looping over keys +## Iterando sobre las claves -As we've seen, the methods provide "get/set/remove by key" functionality. But how to get all saved values or keys? +Los métodos proporcionan la funcionalidad get / set / remove. ¿Pero cómo conseguimos todas las claves o valores guardados? -Unfortunately, storage objects are not iterable. +Desafortunadamente, los objetos de almacenaje no son iterables. -One way is to loop over them as over an array: +Una opción es utilizar iteración sobre un array: ```js run for(let i=0; i<localStorage.length; i++) { @@ -87,29 +86,29 @@ for(let i=0; i<localStorage.length; i++) { } ``` -Another way is to use `for key in localStorage` loop, just as we do with regular objects. +Otra opción es utilizar el loop específico para objetos `for key in localStorage` tal como hacemos en objetos comunes. -It iterates over keys, but also outputs few built-in fields that we don't need: +Esta opción itera sobre las claves, pero también devuelve campos propios de `localStorage` que no necesitamos: ```js run -// bad try +// mal intento for(let key in localStorage) { - alert(key); // shows getItem, setItem and other built-in stuff + alert(key); // muestra getItem, setItem y otros campos que no nos interesan } ``` -...So we need either to filter fields from the prototype with `hasOwnProperty` check: +... De modo que necesitamos o bien filtrar campos des del prototipo con la validación `hasOwnProperty`: ```js run for(let key in localStorage) { if (!localStorage.hasOwnProperty(key)) { - continue; // skip keys like "setItem", "getItem" etc + continue; // se salta claves como "setItem", "getItem" etc } alert(`${key}: ${localStorage.getItem(key)}`); } ``` -...Or just get the "own" keys with `Object.keys` and then loop over them if needed: +... O simplemente acceder a las claves "propias" con `Object.keys` y iterar sobre éstas si es necesario: ```js run let keys = Object.keys(localStorage); @@ -118,90 +117,90 @@ for(let key of keys) { } ``` -The latter works, because `Object.keys` only returns the keys that belong to the object, ignoring the prototype. +Esta última opción funciona, ya que `Object.keys` solo devuelve las claves que pertenecen al objeto, ignorando el prototipo. -## Strings only +## Solo strings -Please note that both key and value must be strings. +Hay que tener en cuenta que tanto la clave como el valor deben ser strings. -If they were any other type, like a number, or an object, they would get converted to a string automatically: +Si fueran de cualquier otro tipo, como un número o un objeto, se convertirían a cadena de texto automáticamente: ```js run localStorage.user = {name: "John"}; alert(localStorage.user); // [object Object] ``` -We can use `JSON` to store objects though: +A pesar de eso, podemos utilizar `JSON` para almacenar objetos: ```js run localStorage.user = JSON.stringify({name: "John"}); -// sometime later +// en algún momento más tarde let user = JSON.parse( localStorage.user ); alert( user.name ); // John ``` -Also it is possible to stringify the whole storage object, e.g. for debugging purposes: +También es posible pasar a texto todo el objeto de almacenaje, por ejemplo para debugear: ```js run -// added formatting options to JSON.stringify to make the object look nicer +// se ha añadido opciones de formato a JSON.stringify para que el objeto se lea mejor alert( JSON.stringify(localStorage, null, 2) ); ``` ## sessionStorage -The `sessionStorage` object is used much less often than `localStorage`. +El objeto `sessionStorage` se utiliza mucho menos que `localStorage`. -Properties and methods are the same, but it's much more limited: +Las propiedades y métodos son los mismos, pero es mucho más limitado: -- The `sessionStorage` exists only within the current browser tab. - - Another tab with the same page will have a different storage. - - But it is shared between iframes in the same tab (assuming they come from the same origin). -- The data survives page refresh, but not closing/opening the tab. +- `sessionStorage` solo existe dentro de la pestaña actual del navegador. + - Otra pestaña con la misma página tendrá un almacenaje distinto. + - Pero se comparte entre iframes en la pestaña (asumiendo que tengan el mismo orígen). +- Los datos sobreviven un refresco de página, pero no cerrar/abrir la pestaña. -Let's see that in action. +Vamos a verlo en acción. -Run this code... +Ejecuta éste código... ```js run sessionStorage.setItem('test', 1); ``` -...Then refresh the page. Now you can still get the data: +... Y recarga la página. Aún puedes acceder a los datos: ```js run -alert( sessionStorage.getItem('test') ); // after refresh: 1 +alert( sessionStorage.getItem('test') ); // después de la recarga: 1 ``` -...But if you open the same page in another tab, and try again there, the code above returns `null`, meaning "nothing found". +... Pero si abres la misma página en otra pestaña, y lo intentas de nuevo, el código anterior devuelve `null`, que significa que no se ha encontrado nada. -That's exactly because `sessionStorage` is bound not only to the origin, but also to the browser tab. For that reason, `sessionStorage` is used sparingly. +Esto es exactamente porque `sessionStorage` no está vinculado solamente al orígen, sino también a la pestaña del navegador. Por ésta razón `sessionStorage` se usa relativamente poco. -## Storage event +## Evento storage -When the data gets updated in `localStorage` or `sessionStorage`, [storage](https://html.spec.whatwg.org/multipage/webstorage.html#the-storageevent-interface) event triggers, with properties: +Cuando los datos se actualizan en `localStorage` o en `sessionStorage`, se dispara el evento [storage](https://html.spec.whatwg.org/multipage/webstorage.html#the-storageevent-interface) con las propiedades: -- `key` – the key that was changed (`null` if `.clear()` is called). -- `oldValue` – the old value (`null` if the key is newly added). -- `newValue` – the new value (`null` if the key is removed). -- `url` – the url of the document where the update happened. -- `storageArea` – either `localStorage` or `sessionStorage` object where the update happened. +- `key` – la clave que ha cambiado, (`null` si se llama `.clear()`). +- `oldValue` – el anterior valor (`null` si se añade una clave). +- `newValue` – el nuevo valor (`null` si se borra una clave). +- `url` – la url del documento donde ha pasado la actualización. +- `storageArea` – bien el objeto `localStorage` o `sessionStorage`, donde se ha producido la actualización. -The important thing is: the event triggers on all `window` objects where the storage is accessible, except the one that caused it. +El hecho importante es: el evento se dispara en todos los objetos `window` donde el almacenaje es accesible, excepto en el que lo ha causado. -Let's elaborate. +Vamos a desarrollarlo. -Imagine, you have two windows with the same site in each. So `localStorage` is shared between them. +Imagina que tienes dos ventanas con el mismo sitio en cada una, de modo que `localStorage` es compartido entre ellas. ```online -You might want to open this page in two browser windows to test the code below. +Quizá quieras abrir ésta página en dos ventanas distintas para probar el código que sigue. ``` -If both windows are listening for `window.onstorage`, then each one will react on updates that happened in the other one. +Si ambas ventanas están escuchando el evento `window.onstorage`, cada una reaccionará a las actualizaciones que pasen en la otra. ```js run -// triggers on updates made to the same storage from other documents -window.onstorage = event => { // can also use window.addEventListener('storage', event => { +// se dispara en actualizaciones hechas en el mismo almacenaje, desde otros documentos +window.onstorage = event => { // también puede usar window.addEventListener('storage', event => { if (event.key != 'now') return; alert(event.key + ':' + event.newValue + " at " + event.url); }; @@ -209,41 +208,41 @@ window.onstorage = event => { // can also use window.addEventListener('storage', localStorage.setItem('now', Date.now()); ``` -Please note that the event also contains: `event.url` -- the url of the document where the data was updated. +Hay que tener en cuenta que el evento también contiene: `event.url` -- la url del documento en que se actualizaron los datos. -Also, `event.storageArea` contains the storage object -- the event is the same for both `sessionStorage` and `localStorage`, so `event.storageArea` references the one that was modified. We may even want to set something back in it, to "respond" to a change. +También que `event.storageArea` contiene el objeto de almacenaje -- el evento es el mismo para `sessionStorage` y `localStorage` --, de modo que `storageArea` referencia el que se modificó. Podemos hasta querer cambiar datos en él, para "responder" a un cambio. -**That allows different windows from the same origin to exchange messages.** +**Esto permite que distintas ventanas del mismo orígen puedan intercambiar mensajes.** -Modern browsers also support [Broadcast channel API](mdn:/api/Broadcast_Channel_API), the special API for same-origin inter-window communication, it's more full featured, but less supported. There are libraries that polyfill that API, based on `localStorage`, that make it available everywhere. +Los navegadores modernos también soportan la [API de Broadcast channel API](https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API), la API específica para la comunicación entre ventanas del mismo orígen. Es más completa, pero tiene menos soporte. Hay librerías que añaden polyfills para ésta API basados en `localStorage` para que se pueda utilizar en cualquier entorno. -## Summary +## Resumen -Web storage objects `localStorage` and `sessionStorage` allow to store key/value pairs in the browser. +Los objetos de almacenaje web `localStorage` y `sessionStorage` permiten guardar pares de clave/valor en el navegador. -- Both `key` and `value` must be strings. -- The limit is 5mb+, depends on the browser. -- They do not expire. -- The data is bound to the origin (domain/port/protocol). +- Tanto la `clave` como el `valor` deben ser strings. +- El límite es de más de 5mb+, dependiendo del navegador. +- No expiran. +- Los datos están vinculados al origen (dominio/puerto/protocolo). | `localStorage` | `sessionStorage` | |----------------|------------------| -| Shared between all tabs and windows with the same origin | Visible within a browser tab, including iframes from the same origin | -| Survives browser restart | Survives page refresh (but not tab close) | +| Compartida entre todas las pestañas y ventanas que tengan el mismo orígen | Accesible en una pestaña del navegador, incluyendo iframes del mismo origen | +| Sobrevive a reinicios del navegador | Muere al cerrar la pestaña | API: -- `setItem(key, value)` -- store key/value pair. -- `getItem(key)` -- get the value by key. -- `removeItem(key)` -- remove the key with its value. -- `clear()` -- delete everything. -- `key(index)` -- get the key number `index`. -- `length` -- the number of stored items. -- Use `Object.keys` to get all keys. -- We access keys as object properties, in that case `storage` event isn't triggered. +- `setItem(clave, valor)` -- guarda pares clave/valor. +- `getItem(clave)` -- coge el valor de una clave. +- `removeItem(clave)` -- borra una clave con su valor. +- `clear()` -- borra todo. +- `key(índice)` -- coge la clave en una posición determinada. +- `length` -- el número de ítems almacenados. +- Utiliza `Object.keys` para conseguir todas las claves. +- Puede utilizar las claves como propiedades de objetor, pero en ese caso el evento `storage` no se dispara -Storage event: +Evento storage: -- Triggers on `setItem`, `removeItem`, `clear` calls. -- Contains all the data about the operation (`key/oldValue/newValue`), the document `url` and the storage object `storageArea`. -- Triggers on all `window` objects that have access to the storage except the one that generated it (within a tab for `sessionStorage`, globally for `localStorage`). +- Se dispara en las llamadas a `setItem`, `removeItem`, `clear`. +- Contiene todos los datos relativos a la operación (`key/oldValue/newValue`), la `url` del documento y el objeto de almacenaje. +- Se dispara en todos los objetos `window` que tienen acceso al almacenaje excepto el que ha generado el evento (en una pestaña en el caso de `sessionStorage` o globalmente en el caso de `localStorage`). diff --git a/6-data-storage/03-indexeddb/article.md b/6-data-storage/03-indexeddb/article.md index 43344e487..f4a46a863 100644 --- a/6-data-storage/03-indexeddb/article.md +++ b/6-data-storage/03-indexeddb/article.md @@ -5,63 +5,63 @@ libs: # IndexedDB -IndexedDB is a database that is built into a browser, much more powerful than `localStorage`. +IndexedDB es una base de datos construida dentro del navegador, mucho más potente que `localStorage`. -- Stores almost any kind of values by keys, multiple key types. -- Supports transactions for reliability. -- Supports key range queries, indexes. -- Can store much bigger volumes of data than `localStorage`. +- Almacena casi todo tipo de valores por claves, tipos de clave múltiple. +- Soporta transacciones para confiabilidad. +- Soporta consultas de rango por clave, e índices. +- Puede almacenar mucho mayor volumen de datos que `localStorage`. -That power is usually excessive for traditional client-server apps. IndexedDB is intended for offline apps, to be combined with ServiceWorkers and other technologies. +Toda esta potencia es normalmente excesiva para las aplicaciones cliente-servidor tradicionales. IndexedDB está previsto para aplicaciones fuera de línea, para ser combinado con ServiceWorkers y otras tecnologías. -The native interface to IndexedDB, described in the specification <https://www.w3.org/TR/IndexedDB>, is event-based. +La interfaz nativa de IndexedDB, descrita en la <https://www.w3.org/TR/IndexedDB>, está basada en eventos. -We can also use `async/await` with the help of a promise-based wrapper, like <https://github.com/jakearchibald/idb>. That's pretty convenient, but the wrapper is not perfect, it can't replace events for all cases. So we'll start with events, and then, after we gain an understanding of IndexedDB, we'll use the wrapper. +También podemos usar `async/await` con la ayuda de un contenedor basado en promesas, como idb <https://github.com/jakearchibald/idb>. Sin embargo, hay que tener en cuenta que el contenedor no es perfecto y no puede reemplazar a los eventos en todos los casos. Así que comenzaremos con eventos y, cuando hayamos avanzado en el entendimiento de IndexedDB, usaremos el contenedor. -```smart header="Where's the data?" -Technically, the data is usually stored in the visitor's home directory, along with browser settings, extensions, etc. +```smart header="¿Dónde están los datos?" +Técnicamente, los datos son almacenados bajo el directorio raíz del usuario junto con la configuración personal del navegador, extensiones, etc. -Different browsers and OS-level users have each their own independant storage. +Navegadores y usuarios diferentes tendrán cada uno su propio almacenamiento independiente. ``` -## Open database +## Apertura de una base de datos, "open" -To start working with IndexedDB, we first need to `open` (connect to) a database. +Para empezar a trabajar con IndexedDB, primero necesitamos conectarnos o "abrir" (`open`) una base de datos. -The syntax: +La sintaxis: ```js let openRequest = indexedDB.open(name, version); ``` -- `name` -- a string, the database name. -- `version` -- a positive integer version, by default `1` (explained below). +- `name` -- un string, el nombre de la base de datos. +- `version` -- un entero positivo, predeterminado en `1` (explicado más abajo). -We can have many databases with different names, but all of them exist within the current origin (domain/protocol/port). Different websites can't access each other's databases. +Podemos tener muchas bases de datos con nombres diferentes, pero todas ellas existen dentro del mismo origen (dominio/protocolo/puerto). Un sitio web no puede acceder bases de datos de otro. -The call returns `openRequest` object, we should listen to events on it: -- `success`: database is ready, there's the "database object" in `openRequest.result`, we should use it for further calls. -- `error`: opening failed. -- `upgradeneeded`: database is ready, but its version is outdated (see below). +La llamada devuelve un objeto `openRequest`, debemos escuchar en él los eventos: +- `success`: la base de datos está lista. Hay un "objeto database" en `openRequest.result` que habremos de usar en las llamadas subsiguientes. +- `error`: Apertura fallida. +- `upgradeneeded`: La base de datos está lista, pero su versión es obsoleta (ver abajo). -**IndexedDB has a built-in mechanism of "schema versioning", absent in server-side databases.** +**IndexedDB tiene incorporado un mecanismo de "versión de esquema", ausente en bases de datos de servidor.** -Unlike server-side databases, IndexedDB is client-side, the data is stored in the browser, so we, developers, don't have full-time access to it. So, when we have published a new version of our app, and the user visits our webpage, we may need to update the database. +A diferencia de las bases de datos del lado del servidor, IndexedDB se ejecuta en el lado del cliente y los datos son almacenados en el navegador, así que nosotros, desarrolladores, no tenemos acceso permanente a esas bases. Entonces, cuando publicamos una nueva versión de nuestra app y el usuario visita nuestra página web, podemos necesitar actualizar la estructura de su base de datos. -If the local database version is less than specified in `open`, then a special event `upgradeneeded` is triggered, and we can compare versions and upgrade data structures as needed. +Si la versión de la base es menor a la especificada en `open`, entonces se dispara un evento especial `upgradeneeded` (actualización-requerida), donde podemos comparar versiones y hacer la actualización de la estructura de datos que se necesite. -The `upgradeneeded` event also triggers when the database doesn't yet exist (technically, its version is `0`), so we can perform the initialization. +El evento `upgradeneeded` también se dispara cuando la base aún no existe (técnicamente, su versión es `0`), lo cual nos permite llevar a cabo su inicialización. -Let's say we published the first version of our app. +Digamos que publicamos la primera versión de nuestra app. -Then we can open the database with version `1` and perform the initialization in an `upgradeneeded` handler like this: +Entonces podemos abrir la base con version `1` y hacer la inicialización en un manejador `upgradeneeded`: ```js let openRequest = indexedDB.open("store", *!*1*/!*); openRequest.onupgradeneeded = function() { - // triggers if the client had no database - // ...perform initialization... + // se dispara si el cliente no tiene la base de datos + // ...ejecuta la inicialización... }; openRequest.onerror = function() { @@ -70,68 +70,68 @@ openRequest.onerror = function() { openRequest.onsuccess = function() { let db = openRequest.result; - // continue working with database using db object + // continúa trabajando con la base de datos usando el objeto db }; ``` -Then, later, we publish the 2nd version. +Luego, más tarde, publicamos la segunda versión. -We can open it with version `2` and perform the upgrade like this: +Podemos abrirla con version `2` y ejecutar la actualización así: ```js let openRequest = indexedDB.open("store", *!*2*/!*); openRequest.onupgradeneeded = function(event) { - // the existing database version is less than 2 (or it doesn't exist) + // la versión de la base existente es menor que 2 (o ni siquiera existe) let db = openRequest.result; - switch(event.oldVersion) { // existing db version + switch(event.oldVersion) { // versión de db existente case 0: - // version 0 means that the client had no database - // perform initialization + // version 0 significa que el cliente no tiene base de datos + // ejecutar inicialización case 1: - // client had version 1 - // update + // el cliente tiene la versión 1 + // actualizar } }; ``` -Please note: as our current version is `2`, the `onupgradeneeded` handler has a code branch for version `0`, suitable for users that are accessing for the first time and have no database, and also for version `1`, for upgrades. +Tenlo en cuenta: como nuestra versión actual es `2`, el manejador `onupgradeneeded` tiene una rama de código para la versión `0`, adecuada para usuarios que acceden por primera vez y no tienen una base de datos, y otra rama para la versión `1`, para su actualización. -And then, only if `onupgradeneeded` handler finishes without errors, `openRequest.onsuccess` triggers, and the database is considered successfully opened. +Entonces, y solamente si el manejador de `onupgradeneeded` finaliza sin errores, se dispara el evento `openRequest.onsuccess` y se considera que la base de datos fue abierta con éxito. -To delete a database: +Para borrar una base de datos: ```js let deleteRequest = indexedDB.deleteDatabase(name) -// deleteRequest.onsuccess/onerror tracks the result +// deleteRequest.onsuccess/onerror rastrea el resultado ``` -```warn header="We can't open a database using an older open call version" -If the current user database has a higher version than in the `open` call, e.g. the existing DB version is `3`, and we try to `open(...2)`, then that's an error, `openRequest.onerror` triggers. +```warn header="No se puede abrir una base de datos usando una versión más vieja de open" +Si la base del usuario tiene una versión mayor que el `open` que la abre, por ejemplo: la base existente tiene versión `3` e intentamos `open(...2)`, se producirá un error que disparará `openRequest.onerror`. -That's rare, but such a thing may happen when a visitor loads outdated JavaScript code, e.g. from a proxy cache. So the code is old, but his database is new. +Es una situación rara, pero puede ocurrir cuando un visitante carga código JavaScript viejo (por ejemplo desde un caché proxy). Así el código es viejo, pero la base de datos nueva. -To protect from errors, we should check `db.version` and suggest a page reload. Use proper HTTP caching headers to avoid loading the old code, so that you'll never have such problems. +Para prevenir errores, debemos verificar `db.version` y sugerir la recarga de página. Usa cabeceras HTTP de caché apropiadas para evitar la carga de código viejo, así nunca tendrás tales problemas. ``` -### Parallel update problem +### El problema de la actualización paralela -As we're talking about versioning, let's tackle a small related problem. +Hablando de versionado, encaremos un pequeño problema relacionado. -Let's say: -1. A visitor opened our site in a browser tab, with database version `1`. -2. Then we rolled out an update, so our code is newer. -3. And then the same visitor opens our site in another tab. +Supongamos que: +1. Un visitante, en una pestaña de su navegador, abrió nuestro sitio con un base de datos con la versión `1`. +2. Luego publicamos una actualización, así que nuestro código es más reciente. +3. Y el mismo visitante abre nuestro sitio en otra pestaña. -So there's a tab with an open connection to DB version `1`, while the second one attempts to update it to version `2` in its `upgradeneeded` handler. +Entonces hay una primera pestaña con una conexión abierta a una base con versión `1`, mientras la segunda intenta actualizarla a la versión `2` en su manejador `upgradeneeded`. -The problem is that a database is shared between two tabs, as it's the same site, same origin. And it can't be both version `1` and `2`. To perform the update to version `2`, all connections to version 1 must be closed, including the one in the first tab. +El problema es que la misma base está compartida entre las dos pestañas, por ser del mismo sitio y origen. Y no puede ser versión `1` y `2` al mismo tiempo. Para ejecutar la actualización a la versión `2`, todas las conexiones a la versión 1 deben ser cerradas, incluyendo las de la primera pestaña. -In order to organize that, the `versionchange` event triggers on the "outdated" database object. We should listen for it and close the old database connection (and probably suggest a page reload, to load the updated code). +Para detectar estas situaciones, se dispara automáticamente el evento `versionchange` (cambio-de-versión) en el objeto de base de datos. Debemos escuchar dicho evento y cerrar la conexión vieja (y probablemente sugerir una recarga de página, para cargar el código actualizado). -If we don't listen for the `versionchange` event and don't close the old connection, then the second, new connection won't be made. The `openRequest` object will emit the `blocked` event instead of `success`. So the second tab won't work. +Si no escuchamos el evento `versionchange` y no cerramos la conexión vieja, entonces la segunda y más nueva no se podrá hacer. El objeto `openRequest` emitirá el evento `blocked` en lugar de `success`. Entonces la segunda pestaña no funcionará. -Here's the code to correctly handle the parallel upgrade. It installs the `onversionchange` handler, that triggers if the current database connection becomes outdated (db version is updated elsewhere) and closes the connection. +Aquí tenemos el código para manejar correctamente la actualización paralela. Este instala un manejador `onversionchange` que se dispara si la conexión actual queda obsoleta y la cierra (la versión se actualiza en algún otro lado): ```js let openRequest = indexedDB.open("store", 2); @@ -145,152 +145,152 @@ openRequest.onsuccess = function() { *!* db.onversionchange = function() { db.close(); - alert("Database is outdated, please reload the page.") + alert("La base de datos está desactualizada, por favor recargue la página.") }; */!* - // ...the db is ready, use it... + // ...la base db está lista, úsala... }; *!* openRequest.onblocked = function() { - // this event shouldn't trigger if we handle onversionchange correctly + // este evento no debería dispararse si hemos manejado onversionchange correctamente - // it means that there's another open connection to the same database - // and it wasn't closed after db.onversionchange triggered for it + // significa que hay otra conexión abierta a la misma base + // que no fue cerrada después de que se disparó db.onversionchange }; */!* ``` -...In other words, here we do two things: +Aquí hacemos dos cosas: -1. The `db.onversionchange` listener informs us about a parallel update attempt, if the current database version becomes outdated. -2. The `openRequest.onblocked` listener informs us about the opposite situation: there's a connection to an outdated version elsewhere, and it doesn't close, so the newer connection can't be made. +1. La escucha a `db.onversionchange` nos informa de un intento de actualización paralela si la conexión actual se volvió obsoleta. +2. La escucha a `openRequest.onblocked` nos informa de la situación opuesta: hay una conexión obsoleta en algún otro lugar que no fue cerrada y por eso la conexión nueva no se pudo realizar. -We can handle things more gracefully in `db.onversionchange`, prompt the visitor to save the data before the connection is closed and so on. +Podemos manejar las cosas más suavemente en `db.onversionchange`, como pedirle al visitante que guarde los datos antes de cerrar la conexión. -Or, an alternative approach would be to not close the database in `db.onversionchange`, but instead use the `onblocked` handler (in the new tab) to alert the visitor, tell him that the newer version can't be loaded until they close other tabs. +Como alternativa podríamos no cerrar la base en `db.onversionchange` sino usar `onblocked` de la nueva pestaña para advertirle que no puede crear una nueva conexión hasta que cierre las viejas. -These update collisions happen rarely, but we should at least have some handling for them, at least an `onblocked` handler, to prevent our script from dying silently. +Estas colisiones ocurren raramente, pero deberíamos tener algún manejo de ella, como mínimo un manejador `onblocked` para evitar que nuestro script muera silenciosamente. -## Object store +## Almacén de objetos, "store" -To store something in IndexedDB, we need an *object store*. +Para almacenar algo en IndexedDB, necesitamos un "almacén de objetos" *object store*. -An object store is a core concept of IndexedDB. Counterparts in other databases are called "tables" or "collections". It's where the data is stored. A database may have multiple stores: one for users, another one for goods, etc. +Un almacén de objetos es un concepto central de IndexedDB. Equivale a lo que en otras bases de datos se denominan "tablas" o "colecciones". Es donde los datos son almacenados. Una base de datos puede tener múltiples almacenes: uno para usuarios, otro para bienes, etc. -Despite being named an "object store", primitives can be stored too. +A pesar de llamarse "almacén de objetos", también puede almacenar tipos primitivos. -**We can store almost any value, including complex objects.** +**Podemos almacenar casi cualquier valor, incluyendo objetos complejos.** -IndexedDB uses the [standard serialization algorithm](https://www.w3.org/TR/html53/infrastructure.html#section-structuredserializeforstorage) to clone-and-store an object. It's like `JSON.stringify`, but more powerful, capable of storing much more datatypes. +IndexedDB usa el [algoritmo de serialización estándar](https://www.w3.org/TR/html53/infrastructure.html#section-structuredserializeforstorage) para clonar-y-almacenar un objeto. Es como `JSON.stringify`; pero más poderoso, capaz de almacenar muchos tipos de datos más. -An example of an object that can't be stored: an object with circular references. Such objects are not serializable. `JSON.stringify` also fails for such objects. +Hay objetos que no pueden ser almacenados, por ejemplo los que tienen referencias circulares. Tales objetos no son serializables. `JSON.stringify` también falla con ellos. -**There must be a unique `key` for every value in the store.** +**Debe haber una clave `key` única para cada valor del almacén.** -A key must be one of these types - number, date, string, binary, or array. It's a unique identifier, so we can search/remove/update values by the key. +Una clave debe ser de uno de estos tipos: number, date, string, binary, o array. Es un identificador único, así podemos buscar/borrar/modificar valores por medio de la clave. ![](indexeddb-structure.svg) -As we'll see very soon, we can provide a key when we add a value to the store, similar to `localStorage`. But when we store objects, IndexedDB allows setting up an object property as the key, which is much more convenient. Or we can auto-generate keys. +Como veremos pronto, cuando agregamos un valor al almacén podemos proporcionarle una clave, de forma similar a `localStorage`. Pero cuando lo que almacenamos son objetos, IndexedDB permite asignar una propiedad del objeto como clave, lo que es mucho más conveniente. También podemos usar claves que se generan automáticamente. -But we need to create an object store first. +Pero primero, necesitamos crear el almacén de objetos. -The syntax to create an object store: +La sintaxis para crear un almacén de objetos u "object store": ```js db.createObjectStore(name[, keyOptions]); ``` -Please note, the operation is synchronous, no `await` needed. +Ten en cuenta que esta operación es sincrónica, no requiere `await`. -- `name` is the store name, e.g. `"books"` for books, -- `keyOptions` is an optional object with one of two properties: - - `keyPath` -- a path to an object property that IndexedDB will use as the key, e.g. `id`. - - `autoIncrement` -- if `true`, then the key for a newly stored object is generated automatically, as an ever-incrementing number. +- `name` es el nombre del almacén, por ejemplo `"books"`, +- `keyOptions` es un objeto opcional con una de estas dos propiedades: + - `keyPath` -- la ruta a una propiedad del objeto que IndexedDB usará como clave, por ejemplo `id`. + - `autoIncrement` -- si es `true`, la clave para el objeto nuevo que se almacene se generará automáticamente con un número autoincremental. -If we don't supply `keyOptions`, then we'll need to provide a key explicitly later, when storing an object. +Si no establecemos `keyOptions`, necesitaremos proporcionar una clave explícitamente más tarde: al momento de almacenar un objeto. -For instance, this object store uses `id` property as the key: +Por ejemplo, este objeto usa la propiedad `id` como clave: ```js db.createObjectStore('books', {keyPath: 'id'}); ``` -**An object store can only be created/modified while updating the DB version, in `upgradeneeded` handler.** +**Un almacén de objetos solo puede ser creado o modificado durante la actualización de su versión, esto es, en el manejador `upgradeneeded`.** -That's a technical limitation. Outside of the handler we'll be able to add/remove/update the data, but object stores can only be created/removed/altered during a version update. +Esto es una limitación técnica. Fuera del manejador podremos agregar/borrar/modificar los datos, pero los almacenes de objetos solo pueden ser creados/borrados/alterados durante la actualización de versión. -To perform a database version upgrade, there are two main approaches: +Para hacer una actualización de base de datos, hay principalmente dos enfoques: -1. We can implement per-version upgrade functions: from 1 to 2, from 2 to 3, from 3 to 4 etc. Then, in `upgradeneeded` we can compare versions (e.g. old 2, now 4) and run per-version upgrades step by step, for every intermediate version (2 to 3, then 3 to 4). -2. Or we can just examine the database: get a list of existing object stores as `db.objectStoreNames`. That object is a [DOMStringList](https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#domstringlist) that provides `contains(name)` method to check for existance. And then we can do updates depending on what exists and what doesn't. +1. Podemos implementar una función de actualización por versión: desde 1 a 2, de 2 a 3, de 3 a 4, etc. Así en `upgradeneeded` podemos comparar versiones (ejemplo: vieja 2, ahora 4) y ejecutar actualizaciones por versión paso a paso para cada versión intermedia (en el ejemplo: 2 a 3, luego 3 a 4). +2. O podemos simplemente examinar la base y alterarla en un paso. Obtenemos una lista de los almacenes existentes como `db.objectStoreNames`. Este objeto es un [DOMStringList](https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#domstringlist) que brinda el método `contains(name)` para chequear existencias. Y podemos entonces hacer actualizaciones dependiendo de lo que existe y lo que no. -For small databases the second variant may be simpler. +En bases de datos pequeñas la segunda variante puede ser más simple. -Here's the demo of the second approach: +Aquí hay un demo del segundo enfoque: ```js let openRequest = indexedDB.open("db", 2); -// create/upgrade the database without version checks +// crea/actualiza la base de datos sin chequeo de versiones openRequest.onupgradeneeded = function() { let db = openRequest.result; - if (!db.objectStoreNames.contains('books')) { // if there's no "books" store - db.createObjectStore('books', {keyPath: 'id'}); // create it + if (!db.objectStoreNames.contains('books')) { // si no hay un almacén de libros ("books"), + db.createObjectStore('books', {keyPath: 'id'}); // crearlo } }; ``` -To delete an object store: +Para borrar un almacén de objetos: ```js db.deleteObjectStore('books') ``` -## Transactions +## Transacciones -The term "transaction" is generic, used in many kinds of databases. +El término transacción es genérico, usado por muchos tipos de bases de datos. -A transaction is a group of operations, that should either all succeed or all fail. +Una transacción es un grupo de operaciones cuyos resultados están vinculados: todas deben ser exitosas o todas fallar. -For instance, when a person buys something, we need to: +Por ejemplo, cuando una persona compra algo, necesitamos: -1. Subtract the money from their account. -2. Add the item to their inventory. +1. Restar el dinero de su cuenta personal. +2. Agregar el ítem a su inventario. -It would be pretty bad if we complete the 1st operation, and then something goes wrong, e.g. lights out, and we fail to do the 2nd. Both should either succeed (purchase complete, good!) or both fail (at least the person kept their money, so they can retry). +Sería muy malo que si se completara la primera operación y algo saliera mal (como un corte de luz), fallara la segunda. Ambas deberían ser exitosas (compra completa, ¡bien!) o ambas fallar (al menos la persona mantuvo su dinero y puede reintentar). -Transactions can guarantee that. +Las transacciones garantizan eso. -**All data operations must be made within a transaction in IndexedDB.** +**Todas las operaciones deben ser hechas dentro de una transacción en IndexedDB.** -To start a transaction: +Para iniciar una transacción: ```js db.transaction(store[, type]); ``` -- `store` is a store name that the transaction is going to access, e.g. `"books"`. Can be an array of store names if we're going to access multiple stores. -- `type` – a transaction type, one of: - - `readonly` -- can only read, the default. - - `readwrite` -- can only read and write the data, but not create/remove/alter object stores. +- `store` – el nombre de almacén al que la transacción va a acceder, por ejemplo `"books"`. Puede ser un array de nombres de almacenes si vamos a acceder a múltiples almacenes. +- `type` – el tipo de transacción, uno de estos dos: + - `readonly` -- solo puede leer (es el predeterminado). + - `readwrite` -- puede leer o escribir datos (pero no crear/quitar/alterar almacenes de objetos). -There's also `versionchange` transaction type: such transactions can do everything, but we can't create them manually. IndexedDB automatically creates a `versionchange` transaction when opening the database, for `upgradeneeded` handler. That's why it's a single place where we can update the database structure, create/remove object stores. +También existe el tipo de transacción `versionchange`: tal transacción puede hacer de todo, pero no podemos crearla nosotros a mano. IndexedDB la crea automáticamente cuando abre la base de datos para el manejador `upgradeneeded`. Por ello, es el único lugar donde podemos actualizar la estructura de base de datos, crear o quitar almacenes de objetos. -```smart header="Why are there different types of transactions?" -Performance is the reason why transactions need to be labeled either `readonly` and `readwrite`. +```smart header="¿Por qué hay diferentes tipos de transacciones?" +El rendimiento es la razón por la que necesitamos identificar las transacciones como `readonly` (lectura solamente) o `readwrite` (lectura y escritura). -Many `readonly` transactions are able to access the same store concurrently, but `readwrite` transactions can't. A `readwrite` transaction "locks" the store for writing. The next transaction must wait before the previous one finishes before accessing the same store. +Muchas transacciones `readonly` pueden leer en un mismo almacén concurrentemente, en cambio las transacciones de escritura `readwrite`, no. Una transacción `readwrite` bloquea el almacén para escribir en él. La siguiente transacción debe esperar a que la anterior termine antes de acceder al mismo almacén. ``` -After the transaction is created, we can add an item to the store, like this: +Una vez que la transacción ha sido creada, podemos agregar un ítem al almacén: ```js let transaction = db.transaction("books", "readwrite"); // (1) -// get an object store to operate on it +// obtiene un almacén de objetos para operar con él *!* let books = transaction.objectStore("books"); // (2) */!* @@ -306,7 +306,7 @@ let request = books.add(book); // (3) */!* request.onsuccess = function() { // (4) - console.log("Book added to the store", request.result); + console.log("Libro agregado al almacén", request.result); }; request.onerror = function() { @@ -314,43 +314,43 @@ request.onerror = function() { }; ``` -There were basically four steps: +Básicamente, hay cuatro pasos: -1. Create a transaction, mentioning all the stores it's going to access, at `(1)`. -2. Get the store object using `transaction.objectStore(name)`, at `(2)`. -3. Perform the request to the object store `books.add(book)`, at `(3)`. -4. ...Handle request success/error `(4)`, then we can make other requests if needed, etc. +1. Crea una transacción, mencionando todos los almacenes a los que irá a acceder, en `(1)`. +2. Obtiene el almacén usando `transaction.objectStore(name)`, en `(2)`. +3. Ejecuta lo petición al almacén `books.add(book)`, en `(3)`. +4. ...Maneja el éxito o fracaso de la petición `(4)`, a continuación podemos hacer otras peticiones si lo necesitamos, etc. -Object stores support two methods to store a value: +Los almacenes de objetos soportan dos métodos para almacenar un valor: - **put(value, [key])** - Add the `value` to the store. The `key` is supplied only if the object store did not have `keyPath` or `autoIncrement` option. If there's already a value with the same key, it will be replaced. + Agrega `value` al almacén. La clave `key` debe ser suministrada solo si al almacén no se le asignó la opción `keyPath` o `autoIncrement`. Si ya hay un valor con la misma clave, este será reemplazado. - **add(value, [key])** - Same as `put`, but if there's already a value with the same key, then the request fails, and an error with the name `"ConstraintError"` is generated. + Lo mismo que `put`, pero si ya hay un valor con la misma clave, la petición falla y se genera un error con el nombre `"ConstraintError"`. -Similar to opening a database, we can send a request: `books.add(book)`, and then wait for `success/error` events. +Al igual que al abrir una base de datos, podemos enviar una petición: `books.add(book)` y quedar a la espera de los eventos `success/error`. -- The `request.result` for `add` is the key of the new object. -- The error is in `request.error` (if any). +- El resultado `request.result` de `add` es la clave del nuevo objeto. +- El error, si lo hay, está en `request.error`. -## Transactions' autocommit +## Commit, culminación automática de las transacciones -In the example above we started the transaction and made `add` request. But as we stated previously, a transaction may have multiple associated requests, that must either all succeed or all fail. How do we mark the transaction as finished, with no more requests to come? +En el ejemplo anterior, empezamos la transacción e hicimos una petición `add`. Pero, como explicamos antes, una transacción puede tener muchas peticiones asociadas, que deben todas ser exitosas o todas fallar. ¿Cómo marcamos que una transacción se da por finalizada, que no tendrá más peticiones asociadas? -The short answer is: we don't. +Respuesta corta: no lo hacemos. -In the next version 3.0 of the specification, there will probably be a manual way to finish the transaction, but right now in 2.0 there isn't. +En la siguiente versión 3.0 de la especificación, probablemente haya una manera de finalizarla manualmente, pero ahora mismo en la 2.0 no la hay. -**When all transaction requests are finished, and the [microtasks queue](info:microtask-queue) is empty, it is committed automatically.** +**Cuando todas las peticiones de una transacción terminaron y la [cola de microtareas](info:microtask-queue) está vacía, se hace un commit (consumación) automático.** -Usually, we can assume that a transaction commits when all its requests are complete, and the current code finishes. +De forma general, podemos asumir que una transacción se consuma cuando todas sus peticiones fueron completadas y el código actual finaliza. -So, in the example above no special call is needed to finish the transaction. +Entonces, en el ejemplo anterior no se necesita una llamada especial para finalizar la transacción. -Transactions auto-commit principle has an important side effect. We can't insert an async operation like `fetch`, `setTimeout` in the middle of a transaction. IndexedDB will not keep the transaction waiting till these are done. +El principio de auto-commit de las transacciones tiene un efecto colateral importante. No podemos insertar una operación asincrónica como `fetch`, `setTimeout` en mitad de una transacción. IndexedDB no mantendrá la transacción esperando a que terminen. -In the code below, `request2` in the line `(*)` fails, because the transaction is already committed, and can't make any request in it: +En el siguiente código, `request2` en la línea `(*)` falla, porque la transacción ya está finalizada y no podemos hacer más peticiones sobre ella: ```js let request1 = books.add(book); @@ -367,54 +367,54 @@ request1.onsuccess = function() { }; ``` -That's because `fetch` is an asynchronous operation, a macrotask. Transactions are closed before the browser starts doing macrotasks. +Esto es porque `fetch` es una operación asincrónica, una macrotarea. Las transacciones se cierran antes de que el navegador comience con las macrotareas. -Authors of IndexedDB spec believe that transactions should be short-lived. Mostly for performance reasons. +Los autores de la especificación de IndexedDB creen que las transacciones deben ser de corta vida. Mayormente por razones de rendimiento. -Notably, `readwrite` transactions "lock" the stores for writing. So if one part of the application initiated `readwrite` on `books` object store, then another part that wants to do the same has to wait: the new transaction "hangs" till the first one is done. That can lead to strange delays if transactions take a long time. +Es de notar que las transacciones `readwrite` "traban" los almacenes para escritura. Entonces si una parte de la aplicación inició `readwrite` en el almacén `books`, cuando otra parte quiera hacer lo mismo tendrá que esperar: la nueva transacción "se cuelga" hasta que la primera termine. Esto puede llevar a extraños retardos si las transacciones toman un tiempo largo. -So, what to do? +Entonces, ¿qué hacer? -In the example above we could make a new `db.transaction` right before the new request `(*)`. +En el ejemplo de arriba podemos hacer una nueva `db.transaction` justo antes de la nueva petición `(*)`. -But it will be even better, if we'd like to keep the operations together, in one transaction, to split apart IndexedDB transactions and "other" async stuff. +Pero, si queremos mantener las operaciones juntas en una transacción, será mucho mejor separar las transacciones IndexedDB de la parte asincrónica. -First, make `fetch`, prepare the data if needed, afterwards create a transaction and perform all the database requests, it'll work then. +Primero, hacer `fetch` y preparar todos los datos que fueran necesarios y, solo entonces, crear una transacción y ejecutar todas las peticiones de base de datos. Así, funcionaría. -To detect the moment of successful completion, we can listen to `transaction.oncomplete` event: +Para detectar el momento de finalización exitosa, podemos escuchar al evento `transaction.oncomplete`: ```js let transaction = db.transaction("books", "readwrite"); -// ...perform operations... +// ...ejecutar las operaciones... transaction.oncomplete = function() { - console.log("Transaction is complete"); + console.log("Transacción completa"); }; ``` -Only `complete` guarantees that the transaction is saved as a whole. Individual requests may succeed, but the final write operation may go wrong (e.g. I/O error or something). +Solo `complete` garantiza que la transacción fue guardada como un todo. Las peticiones individuales pueden ser exitosas, pero la operación final de escritura puede ir mal (por ejemplo por un error de Entrada/Salida u otra cosa). -To manually abort the transaction, call: +Para abortar una transacción manualmente: ```js transaction.abort(); ``` -That cancels all modification made by the requests in it and triggers `transaction.onabort` event. +Esto cancela todas las modificaciones hechas por las peticiones y dispara el evento `transaction.onabort`. -## Error handling +## Manejo de error -Write requests may fail. +Las peticiones de escritura pueden fallar. -That's to be expected, not only because of possible errors at our side, but also for reasons not related to the transaction itself. For instance, the storage quota may be exceeded. So we must be ready to handle such case. +Esto es esperable, no solo por posibles errores de nuestro lado, sino también por razones no relacionadas con la transacción en si misma. Por ejemplo, la cuota de almacenamiento podría haberse exedido. Por tanto, debemos estar preparados para manejar tal caso. -**A failed request automatically aborts the transaction, canceling all its changes.** +**Una petición fallida automáticamente aborta la transacción, cancelando todos sus cambios.** -In some situations, we may want to handle the failure (e.g. try another request), without canceling existing changes, and continue the transaction. That's possible. The `request.onerror` handler is able to prevent the transaction abort by calling `event.preventDefault()`. +En algunas situaciones, podemos querer manejar el fallo (por ejemplo, intentar otra petición) sin cancelar los cambios en curso, y continuar la transacción. Eso es posible. El manejador `request.onerror` es capaz de evitar el aborto de la transacción llamando a `event.preventDefault()`. -In the example below a new book is added with the same key (`id`) as the existing one. The `store.add` method generates a `"ConstraintError"` in that case. We handle it without canceling the transaction: +En el ejemplo que sigue, un libro nuevo es agregado con la misma clave (`id`) que otro existente. El método `store.add` genera un `"ConstraintError"` en ese caso. Lo manejamos sin cancelar la transacción: ```js let transaction = db.transaction("books", "readwrite"); @@ -424,14 +424,14 @@ let book = { id: 'js', price: 10 }; let request = transaction.objectStore("books").add(book); request.onerror = function(event) { - // ConstraintError occurs when an object with the same id already exists + // ConstraintError ocurre cuando un objeto con el mismo id ya existe if (request.error.name == "ConstraintError") { - console.log("Book with such id already exists"); // handle the error - event.preventDefault(); // don't abort the transaction - // use another key for the book? + console.log("Ya existe un libro con ese id"); // manejo del error + event.preventDefault(); // no abortar la transacción + // ¿usar otra clave para el libro? } else { - // unexpected error, can't handle it - // the transaction will abort + // error inesperado, no podemos manejarlo + // la transacción se abortará } }; @@ -440,126 +440,126 @@ transaction.onabort = function() { }; ``` -### Event delegation +### Delegación de eventos -Do we need onerror/onsuccess for every request? Not every time. We can use event delegation instead. +¿Necesitamos onerror/onsuccess en cada petición? No siempre. En su lugar podemos usar la delegación de eventos. -**IndexedDB events bubble: `request` -> `transaction` -> `database`.** +**Propagación de eventos IndexedDB: `request` -> `transaction` -> `database`.** -All events are DOM events, with capturing and bubbling, but usually only bubbling stage is used. +Todos los eventos son eventos DOM, con captura y propagación, pero generalmente solo se usa el escenario de la propagación. -So we can catch all errors using `db.onerror` handler, for reporting or other purposes: +Así que podemos capturar todos los errores usando el manejador `db.onerror`, para reportarlos u otros propósitos: ```js db.onerror = function(event) { - let request = event.target; // the request that caused the error + let request = event.target; // la petición (request) que causó el error console.log("Error", request.error); }; ``` -...But what if an error is fully handled? We don't want to report it in that case. +...Pero ¿y si el error fue completamente manejado? No queremos elevarlo en ese caso. -We can stop the bubbling and hence `db.onerror` by using `event.stopPropagation()` in `request.onerror`. +Podemos detener la propagación y en consecuencia `db.onerror` usando `event.stopPropagation()` en `request.onerror`. ```js request.onerror = function(event) { if (request.error.name == "ConstraintError") { - console.log("Book with such id already exists"); // handle the error - event.preventDefault(); // don't abort the transaction - event.stopPropagation(); // don't bubble error up, "chew" it + console.log("Ya existe un libro con ese id"); // manejo de error + event.preventDefault(); // no abortar la transacción + event.stopPropagation(); // no propagar el error } else { - // do nothing - // transaction will be aborted - // we can take care of error in transaction.onabort + // no hacer nada + // la transacción será abortada + // podemos encargarnos del error en transaction.onabort } }; ``` -## Searching +## Búsquedas -There are two main types of search in an object store: +Hay dos maneras principales de buscar en un almacén de objetos: -1. By a key value or a key range. In our "books" storage that would be a value or range of values of `book.id`. -2. By another object field, e.g. `book.price`. This required an additional data structure, named "index". +1. Por clave o por rango de claves. En nuestro almacén "books", puede ser por un valor o por un rango de valores de `book.id`. +2. Por algún otro campo del objeto, por ejemplo `book.price`. Esto requiere una estructura de datos adicional llamada índice "index". -### By key +### Por clave -First let's deal with the first type of search: by key. +Veamos el primer tipo de búsqueda: por clave. -Searching methods support both exact key values and so-called "ranges of values" -- [IDBKeyRange](https://www.w3.org/TR/IndexedDB/#keyrange) objects that specify an acceptable "key range". +Los métodos de búsqueda soportan tanto las claves exactas como las denominadas "consultas por rango" que son objetos [IDBKeyRange](https://www.w3.org/TR/IndexedDB/#keyrange) que especifican un "rango de claves" aceptable. -`IDBKeyRange` objects are created using following calls: +Los objetos `IDBKeyRange` son creados con las siguientes llamadas: -- `IDBKeyRange.lowerBound(lower, [open])` means: `≥lower` (or `>lower` if `open` is true) -- `IDBKeyRange.upperBound(upper, [open])` means: `≤upper` (or `<upper` if `open` is true) -- `IDBKeyRange.bound(lower, upper, [lowerOpen], [upperOpen])` means: between `lower` and `upper`. If the open flags is true, the corresponding key is not included in the range. -- `IDBKeyRange.only(key)` -- a range that consists of only one `key`, rarely used. +- `IDBKeyRange.lowerBound(lower, [open])` significa: `≥ lower` (o `> lower` si `open` es true) +- `IDBKeyRange.upperBound(upper, [open])` significa: `≤ upper` (o `< upper` si `open` es true) +- `IDBKeyRange.bound(lower, upper, [lowerOpen], [upperOpen])` significa: entre `lower` y `upper`. Si el indicador "open" es true, la clave correspondiente no es incluida en el rango. +- `IDBKeyRange.only(key)` -- es un rango compuesto solamente por una clave `key`, es raramente usado. -We'll see practical examples of using them very soon. +Veremos ejemplos prácticos de uso muy pronto. -To perform the actual search, there are following methods. They accept a `query` argument that can be either an exact key or a key range: +Para efectuar la búsqueda, existen los siguientes métodos. Ellos aceptan un argumento `query` que puede ser una clave exacta o un rango de claves: -- `store.get(query)` -- search for the first value by a key or a range. -- `store.getAll([query], [count])` -- search for all values, limit by `count` if given. -- `store.getKey(query)` -- search for the first key that satisfies the query, usually a range. -- `store.getAllKeys([query], [count])` -- search for all keys that satisfy the query, usually a range, up to `count` if given. -- `store.count([query])` -- get the total count of keys that satisfy the query, usually a range. +- `store.get(query)` -- busca el primer valor, por clave o por rango. +- `store.getAll([query], [count])` -- busca todos los valores, limitado a la cantidad `count` si esta se especifica. +- `store.getKey(query)` -- busca la primera clave que satisface la consulta, usualmente un rango. +- `store.getAllKeys([query], [count])` -- busca todas las claves que satisfacen la consulta, usualmente un rango, hasta la cantidad `count` si es suministrada. +- `store.count([query])` -- obtiene la cantidad de claves que satisfacen la consulta, usualmente un rango. -For instance, we have a lot of books in our store. Remember, the `id` field is the key, so all these methods can search by `id`. +Por ejemplo, tenemos un montón de libros en nuestro almacén. Recuerda, el campo `id` es la clave, así que todos estos métodos pueden buscar por `id`. -Request examples: +Ejemplos de peticiones: ```js -// get one book +// obtiene un libro books.get('js') -// get books with 'css' <= id <= 'html' +// obtiene libros con: 'css' <= id <= 'html' books.getAll(IDBKeyRange.bound('css', 'html')) -// get books with id < 'html' +// obtiene libros con id < 'html' books.getAll(IDBKeyRange.upperBound('html', true)) -// get all books +// obtiene todos los libros books.getAll() -// get all keys, where id > 'js' +// obtiene todas las claves donde id > 'js' books.getAllKeys(IDBKeyRange.lowerBound('js', true)) ``` -```smart header="Object store is always sorted" -An object store sorts values by key internally. +```smart header="El almacén de objetos siempre está ordenado" +El almacén internamente guarda los valores ordenados por clave. -So requests that return many values always return them in sorted by key order. +Entonces, en las peticiones que devuelvan varios valores, estos siempre estarán ordenados por la clave. ``` -### By a field using an index +## Buscando por cualquier campo con un índice -To search by other object fields, we need to create an additional data structure named "index". +Para buscar por otro campo del objeto, necesitamos crear una estructura de datos adicional llamada "índice (index)". -An index is an "add-on" to the store that tracks a given object field. For each value of that field, it stores a list of keys for objects that have that value. There will be a more detailed picture below. +Un índice es un agregado al almacén que rastrea un campo determinado del objeto dado. Para cada valor de ese campo, almacena una lista de claves de objetos que tienen ese valor. Veremos una imagen más detallada abajo. -The syntax: +La sintaxis: ```js objectStore.createIndex(name, keyPath, [options]); ``` -- **`name`** -- index name, -- **`keyPath`** -- path to the object field that the index should track (we're going to search by that field), -- **`option`** -- an optional object with properties: - - **`unique`** -- if true, then there may be only one object in the store with the given value at the `keyPath`. The index will enforce that by generating an error if we try to add a duplicate. - - **`multiEntry`** -- only used if the value on `keyPath` is an array. In that case, by default, the index will treat the whole array as the key. But if `multiEntry` is true, then the index will keep a list of store objects for each value in that array. So array members become index keys. +- **`name`** -- nombre del índice, +- **`keyPath`** -- ruta al campo del objeto que el índice debe seguir (vamos a buscar por ese campo), +- **`option`** -- un objeto opcional con las propiedades: + - **`unique`** -- si es true, un valor no podrá repetirse en el índice. Solamente puede haber un único objeto en el almacén con un valor dado de su `keyPath`. El índice forzará esto generando un error si intentamos agregar un duplicado. + - **`multiEntry`** -- solo se usa si el valor en `keyPath` es un array. En ese caso, de manera predeterminada, el índice tratará el array completo como clave. Pero si `multiEntry` es true, entonces el índice mantendrá una lista de objetos almacenados para cada valor en ese array. Así los miembros del array se vuelven claves de ese índice. -In our example, we store books keyed by `id`. +En nuestro ejemplo, almacenamos libros usando la propiedad `id` como clave. -Let's say we want to search by `price`. +Digamos que queremos buscar por precio `price`. -First, we need to create an index. It must be done in `upgradeneeded`, just like an object store: +Primero necesitamos crear un índice. Esto debe hacerse en `upgradeneeded`, al igual que hacíamos la creación del almacén de objetos. ```js openRequest.onupgradeneeded = function() { - // we must create the index here, in versionchange transaction + // debemos crear el índice aquí, en la transacción versionchange let books = db.createObjectStore('books', {keyPath: 'id'}); *!* let index = books.createIndex('price_idx', 'price'); @@ -567,19 +567,19 @@ openRequest.onupgradeneeded = function() { }; ``` -- The index will track `price` field. -- The price is not unique, there may be multiple books with the same price, so we don't set `unique` option. -- The price is not an array, so `multiEntry` flag is not applicable. +- El índice hará seguimiento del campo `price`. +- El precio no es único, puede haber múltiples libros con el mismo precio así que no establecemos la opción `unique`. +- El precio no es un array, entonces el indicador `multiEntry` no es aplicable. -Imagine that our `inventory` has 4 books. Here's the picture that shows exactly what the `index` is: +Imagine que nuestro `inventory` tiene 4 libros. Aquí la imagen muestra exactamente lo que es el `índice`: ![](indexeddb-index.svg) -As said, the index for each value of `price` (second argument) keeps the list of keys that have that price. +Como se dijo, el índice para cada valor de `price` (segundo argumento) mantiene la lista de claves que tienen ese precio. -The index keeps itself up to date automatically, we don't have to care about it. +El índice se mantiene actualizado automáticamente, no necesitamos preocuparnos de eso. -Now, when we want to search for a given price, we simply apply the same search methods to the index: +Ahora, cuando queremos buscar por un determinado precio, simplemente aplicamos el mismo método de búsqueda al índice: ```js let transaction = db.transaction("books"); // readonly @@ -592,39 +592,39 @@ let request = priceIndex.getAll(10); request.onsuccess = function() { if (request.result !== undefined) { - console.log("Books", request.result); // array of books with price=10 + console.log("Books", request.result); // array de libros con precio = 10 } else { - console.log("No such books"); + console.log("No hay libros así"); } }; ``` -We can also use `IDBKeyRange` to create ranges and looks for cheap/expensive books: +También podemos usar `IDBKeyRange` para crear rangos y vistas de libros baratos/caros: ```js -// find books where price <= 5 +// encontrar libros donde price <= 5 let request = priceIndex.getAll(IDBKeyRange.upperBound(5)); ``` -Indexes are internally sorted by the tracked object field, `price` in our case. So when we do the search, the results are also sorted by `price`. +Los índices están ordenados internamente por el campo del índice, en nuestro caso `price`. Entonces cuando hacemos la búsqueda, los resultados también estarán ordenados por `price`. -## Deleting from store +## Borrando del almacén -The `delete` method looks up values to delete by a query, the call format is similar to `getAll`: +El método `delete` (eliminar) busca a través de una consulta valores para borrarlos. El formato de la llamada es similar a `getAll`: -- **`delete(query)`** -- delete matching values by query. +- **`delete(query)`** -- elimina valores coincidentes con una consulta (query). -For instance: +Por ejemplo: ```js -// delete the book with id='js' +// borra el libro cuyo id='js' books.delete('js'); ``` -If we'd like to delete books based on a price or another object field, then we should first find the key in the index, and then call `delete`: +Si queremos borrar libros basados en un precio u otro campo del objeto, debemos primero encontrar la clave en el índice, luego llamar a `delete` con dicha clave: ```js -// find the key where price = 5 +// encuentra la clave donde price = 5 let request = priceIndex.getKey(5); request.onsuccess = function() { @@ -633,44 +633,44 @@ request.onsuccess = function() { }; ``` -To delete everything: +Para borrar todo: ```js -books.clear(); // clear the storage. +books.clear(); // clear "limpia" el almacén. ``` -## Cursors +## Cursores -Methods like `getAll/getAllKeys` return an array of keys/values. +Métodos como `getAll/getAllKeys` devuelven un array de claves/valores. -But an object storage can be huge, bigger than the available memory. Then `getAll` will fail to get all records as an array. +Pero un almacén de objetos puede ser enorme, incluso más que la memoria disponible. Entonces `getAll` fallaría al tratar de llenar de registros el array. -What to do? +¿Qué hacer? -Cursors provide the means to work around that. +Los cursores brindan los medios para manejar esta situación. -**A *cursor* is a special object that traverses the object storage, given a query, and returns one key/value at a time, thus saving memory.** +**Un *cursor* es un objeto especial que, dada una consulta, recorre el almacén y devuelve un solo par clave/valor cada vez, ahorrando así memoria.** -As an object store is sorted internally by key, a cursor walks the store in key order (ascending by default). +Como un almacén está ordenado internamente por clave, un cursor lo recorre en el orden de la clave (ascendente de forma predeterminada). -The syntax: +La sintaxis: ```js -// like getAll, but with a cursor: +// como getAll, pero con un cursor: let request = store.openCursor(query, [direction]); -// to get keys, not values (like getAllKeys): store.openKeyCursor +// para obtener las claves y no sus valores (como getAllKeys): store.openKeyCursor ``` -- **`query`** is a key or a key range, same as for `getAll`. -- **`direction`** is an optional argument, which order to use: - - `"next"` -- the default, the cursor walks up from the record with the lowest key. - - `"prev"` -- the reverse order: down from the record with the biggest key. - - `"nextunique"`, `"prevunique"` -- same as above, but skip records with the same key (only for cursors over indexes, e.g. for multiple books with price=5 only the first one will be returned). +- **`query`** (consulta) es una clave o un rango de claves, al igual que para `getAll`. +- **`direction`** es un argumento opcional, el orden que se va a usar: + - `"next"` -- el predeterminado: el cursor recorre en orden ascendente comenzando por la clave más baja. + - `"prev"` -- el orden inverso: decrece comenzando con el registro con la clave más alta. + - `"nextunique"`, `"prevunique"` -- igual que las anteriores, pero saltando los registros con la misma clave (válido solo para cursores sobre índices; por ejemplo, de múltiples libros con price=5, solamente el primero será devuelto). -**The main difference of the cursor is that `request.onsuccess` triggers multiple times: once for each result.** +**La diferencia principal del cursor es que `request.onsuccess` se dispara múltiples veces: una por cada resultado.** -Here's an example of how to use a cursor: +Aquí hay un ejemplo de cómo usar un cursor: ```js let transaction = db.transaction("books"); @@ -678,63 +678,63 @@ let books = transaction.objectStore("books"); let request = books.openCursor(); -// called for each book found by the cursor +// llamado por cada libro encontrado por el cursor request.onsuccess = function() { let cursor = request.result; if (cursor) { - let key = cursor.key; // book key (id field) - let value = cursor.value; // book object + let key = cursor.key; // clave del libro (el campo id) + let value = cursor.value; // el objeto libro console.log(key, value); cursor.continue(); } else { - console.log("No more books"); + console.log("No hay más libros"); } }; ``` -The main cursor methods are: +Los principales métodos de cursor son: -- `advance(count)` -- advance the cursor `count` times, skipping values. -- `continue([key])` -- advance the cursor to the next value in range matching (or immediately after `key` if given). +- `advance(count)` -- avanza el cursor `count` veces, saltando valores. +- `continue([key])` -- avanza el cursor al siguiente valor en el rango o, si se provee la clave `key`, al valor inmediatamente posterior a `key`. -Whether there are more values matching the cursor or not -- `onsuccess` gets called, and then in `result` we can get the cursor pointing to the next record, or `undefined`. +El evento `onsuccess` será llamado haya o no más valores coincidentes, y en `result` obtenemos el cursor apuntando al siguiente registro o `undefined`. -In the example above the cursor was made for the object store. +En el ejemplo anterior, el cursor fue hecho sobre el almacén de objetos. -But we also can make a cursor over an index. As we remember, indexes allow to search by an object field. Cursors over indexes do precisely the same as over object stores -- they save memory by returning one value at a time. +Pero también podemos hacerlo sobre un índice. Recordamos, los índices nos permiten buscar por los campos del objeto. Los cursores sobre índices hacen precisamente lo mismo que sobre el almacén de objetos: ahorran memoria al devolver un solo valor cada vez. -For cursors over indexes, `cursor.key` is the index key (e.g. price), and we should use `cursor.primaryKey` property for the object key: +Para cursores sobre índices, `cursor.key` es la clave del índice (es decir "price"), y debemos usar la propiedad `cursor.primaryKey` para la clave del objeto: ```js let request = priceIdx.openCursor(IDBKeyRange.upperBound(5)); -// called for each record +// es llamado para cada registro request.onsuccess = function() { let cursor = request.result; if (cursor) { - let primaryKey = cursor.primaryKey; // next object store key (id field) - let value = cursor.value; // next object store object (book object) - let key = cursor.key; // next index key (price) + let primaryKey = cursor.primaryKey; // la siguiente clave (campo id) del almacén + let value = cursor.value; // el siguiente objeto (objeto book) del almacén + let key = cursor.key; // siguiente clave del índice (price) console.log(key, value); cursor.continue(); } else { - console.log("No more books"); + console.log("No hay más libros"); } }; ``` -## Promise wrapper +## Contenedor promisificador -Adding `onsuccess/onerror` to every request is quite a cumbersome task. Sometimes we can make our life easier by using event delegation, e.g. set handlers on the whole transactions, but `async/await` is much more convenient. +Agregar `onsuccess/onerror` a cada petición es una tarea agobiante. A veces podemos hacernos la vida más fácil usando delegación de eventos (por ejemplo, estableciendo manejadores para las transacciones completas), pero `async/await` es mucho más conveniente. -Let's use a thin promise wrapper <https://github.com/jakearchibald/idb> further in this chapter. It creates a global `idb` object with [promisified](info:promisify) IndexedDB methods. +Usemos en adelante para este capítulo un contenedor (wrapper) liviano que añade promesas <https://github.com/jakearchibald/idb>. Este contenedor crea un objeto global `idb` con métodos IndexedDB [promisificados](info:promisify). -Then, instead of `onsuccess/onerror` we can write like this: +Entonces, en lugar de `onsuccess/onerror`, podemos escribir: ```js let db = await idb.openDB('store', 1, db => { if (db.oldVersion == 0) { - // perform the initialization + // ejecuta la inicialización db.createObjectStore('books', {keyPath: 'id'}); } }); @@ -754,31 +754,31 @@ try { } ``` -So we have all the sweet "plain async code" and "try..catch" stuff. +Así tenemos todo lo dulce de "código async plano" y "try..catch". -### Error handling +### Manejo de Error -If we don't catch an error, then it falls through, till the closest outer `try..catch`. +Si no atrapamos un error, este se propaga hasta el `try..catch` externo más cercano. -An uncaught error becomes an "unhandled promise rejection" event on `window` object. +Un error no atrapado se vuelve un evento "rechazo de promesa no manejado" sobre el objeto `window`. -We can handle such errors like this: +Podemos manejar tales errores así: ```js window.addEventListener('unhandledrejection', event => { - let request = event.target; // IndexedDB native request object - let error = event.reason; // Unhandled error object, same as request.error - ...report about the error... + let request = event.target; // objeto request nativo de IndexedDB + let error = event.reason; // objeto error no manejado, igual que request.error + ...reportar el error... }); ``` -### "Inactive transaction" pitfall +### La trampa "transacción inactiva" -As we already know, a transaction auto-commits as soon as the browser is done with the current code and microtasks. So if we put a *macrotask* like `fetch` in the middle of a transaction, then the transaction won't wait for it to finish. It just auto-commits. So the next request in it would fail. +Como sabemos, una transacción se autofinaliza tan pronto como el navegador termina el código actual y las microtareas. Por tanto, si ponemos una *macrotarea* como `fetch` en el medio de una transacción, la transacción no esperará a que termine. Simplemente se autofinaliza. Así la siguiente petición fallaría. -For a promise wrapper and `async/await` the situation is the same. +Para el contenedor de promisificación y `async/await` la situación es la misma. -Here's an example of `fetch` in the middle of the transaction: +Este es un ejemplo de `fetch` en el medio de una transacción: ```js let transaction = db.transaction("inventory", "readwrite"); @@ -791,50 +791,50 @@ await fetch(...); // (*) await inventory.add({ id: 'js', price: 10, created: new Date() }); // Error ``` -The next `inventory.add` after `fetch` `(*)` fails with an "inactive transaction" error, because the transaction is already committed and closed at that time. +El `inventory.add` que sigue a `fetch` `(*)` falla con el error "transacción inactiva", porque la transacción se autocompletó y, llegado ese momento, ya está cerrada. -The workaround is the same as when working with native IndexedDB: either make a new transaction or just split things apart. +La forma de sortear esto es la misma que con el IndexedDB nativo: Hacer una nueva transacción o simplemente partir las cosas. -1. Prepare the data and fetch all that's needed first. -2. Then save in the database. +1. Preparar los datos y buscar todo lo que sea necesario primero. +2. Solo entonces, grabar en la base de datos. -### Getting native objects +### Obtener objetos nativos -Internally, the wrapper performs a native IndexedDB request, adding `onerror/onsuccess` to it, and returns a promise that rejects/resolves with the result. +Internamente, el contenedor ejecuta una petición IndexedDB nativa, agregándole `onerror/onsuccess` y devolviendo una promesa que rechaza/resuelve con el resultado. -That works fine most of the time. The examples are at the lib page <https://github.com/jakearchibald/idb>. +Esto funciona bien la mayor parte del tiempo. Los ejemplos están en la página lib de idb <https://github.com/jakearchibald/idb>. -In few rare cases, when we need the original `request` object, we can access it as `promise.request` property of the promise: +En algunos raros casos necesitamos el objeto `request` original. Podemos accederlo con la propiedad `promise.request` de la promesa: ```js -let promise = books.add(book); // get a promise (don't await for its result) +let promise = books.add(book); // obtiene una promesa (no espera por su resultado) -let request = promise.request; // native request object -let transaction = request.transaction; // native transaction object +let request = promise.request; // objeto request nativo +let transaction = request.transaction; // objeto transaction nativo -// ...do some native IndexedDB voodoo... +// ...hace algún vudú IndexedDB... -let result = await promise; // if still needed +let result = await promise; // si aún se necesita ``` -## Summary +## Resumen -IndexedDB can be thought of as a "localStorage on steroids". It's a simple key-value database, powerful enough for offline apps, yet simple to use. +IndexedDB puede considerarse como "localStorage con esteroides". Es una simple base de datos de clave-valor, suficientemente poderosa para apps fuera de línea y fácil de usar. -The best manual is the specification, [the current one](https://www.w3.org/TR/IndexedDB-2/) is 2.0, but few methods from [3.0](https://w3c.github.io/IndexedDB/) (it's not much different) are partially supported. +El mejor manual es la especificación, [la actual](https://www.w3.org/TR/IndexedDB-2/) es 2.0, pero algunos métodos de [3.0](https://w3c.github.io/IndexedDB/) (no muy diferente) están soportados parcialmente. -The basic usage can be described with a few phrases: +El uso básico puede ser descrito en pocas frases: -1. Get a promise wrapper like [idb](https://github.com/jakearchibald/idb). -2. Open a database: `idb.openDb(name, version, onupgradeneeded)` - - Create object storages and indexes in `onupgradeneeded` handler or perform version update if needed. -3. For requests: - - Create transaction `db.transaction('books')` (readwrite if needed). - - Get the object store `transaction.objectStore('books')`. -4. Then, to search by a key, call methods on the object store directly. - - To search by an object field, create an index. -5. If the data does not fit in memory, use a cursor. +1. Obtenga un contenedor promisificador como [idb](https://github.com/jakearchibald/idb). +2. Abra la base de datos: `idb.openDb(name, version, onupgradeneeded)` + - Cree almacenes de objetos e índices en el manejador `onupgradeneeded` o ejecute la actualización de versión cuando sea necesario. +3. Para peticiones: + - Cree una transacción `db.transaction('books')` (readwrite si es necesario). + - Obtenga el almacén de objetos `transaction.objectStore('books')`. +4. Entonces, para buscar por clave, llame métodos sobre el almacén directamente. + - Para buscar por un campo de objeto, cree un índice. +5. Si los datos son demasiados para la memoria, use un cursor. -Here's a small demo app: +Una pequeña app de demo: [codetabs src="books" current="index.html"] diff --git a/6-data-storage/index.md b/6-data-storage/index.md index a08c05b58..c6586f0e7 100644 --- a/6-data-storage/index.md +++ b/6-data-storage/index.md @@ -1,2 +1,2 @@ -# Storing data in the browser +# Almacenando datos en el navegador diff --git a/7-animation/1-bezier-curve/article.md b/7-animation/1-bezier-curve/article.md index 63b9a03ce..007e847f3 100644 --- a/7-animation/1-bezier-curve/article.md +++ b/7-animation/1-bezier-curve/article.md @@ -1,207 +1,205 @@ -# Bezier curve +# Curva de Bézier -Bezier curves are used in computer graphics to draw shapes, for CSS animation and in many other places. +Las curvas de Bézier se utilizan en gráficos por ordenador para dibujar formas, para animación CSS y en muchos otros lugares. -They are a very simple thing, worth to study once and then feel comfortable in the world of vector graphics and advanced animations. +En realidad, son algo muy sencillo, vale la pena estudiarlos una vez y luego sentirse cómodo en el mundo de los gráficos vectoriales y las animaciones avanzadas. -```smart header="Some theory, please" -This article provides a theoretical, but very needed insight into what Bezier curves are, while [the next one](info:css-animations#bezier-curve) shows how we can use them for CSS animations. +```smart header="Un poco de teoría, por favor" +Este artículo brinda una base teórica, pero muy necesaria, de lo que son las curvas Bezier; mientras que el [próximo](info:css-animations#bezier-curve) muestra cómo podemos usarlas en animaciones CSS. -Please take your time to read and understand the concept, it'll serve you well. +Por favor toma tu tiempo en leer y entender el concepto, te servirá bien. ``` -## Control points +## Puntos de Control -A [bezier curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve) is defined by control points. +Una [curva de Bézier](https://es.wikipedia.org/wiki/Curva_de_B%C3%A9zier) está definida por puntos de control. -There may be 2, 3, 4 or more. +Puede haber 2, 3, 4 o más. -For instance, two points curve: +Por ejemplo, curva de dos puntos: ![](bezier2.svg) -Three points curve: +Curva de tres puntos: ![](bezier3.svg) -Four points curve: +Curva de cuatro puntos: ![](bezier4.svg) -If you look closely at these curves, you can immediately notice: +Si observas detenidamente estas curvas, puedes notar inmediatamente que: -1. **Points are not always on curve.** That's perfectly normal, later we'll see how the curve is built. -2. **The curve order equals the number of points minus one**. -For two points we have a linear curve (that's a straight line), for three points -- quadratic curve (parabolic), for four points -- cubic curve. -3. **A curve is always inside the [convex hull](https://en.wikipedia.org/wiki/Convex_hull) of control points:** +1. **Los puntos no siempre están en la curva.** Eso es perfectamente normal, luego veremos cómo se construye la curva. +2. **El orden de la curva es igual al número de puntos menos uno**. +Para dos puntos tenemos una curva lineal (que es una línea recta), para tres puntos -- curva cuadrática (parabólica), para cuatro puntos -- curva cúbica. +3. **Una curva siempre está dentro del [casco convexo](https://en.wikipedia.org/wiki/Convex_hull) de los puntos de control:** ![](bezier4-e.svg) ![](bezier3-e.svg) -Because of that last property, in computer graphics it's possible to optimize intersection tests. If convex hulls do not intersect, then curves do not either. So checking for the convex hulls intersection first can give a very fast "no intersection" result. Checking the intersection of convex hulls is much easier, because they are rectangles, triangles and so on (see the picture above), much simpler figures than the curve. +Debido a esa última propiedad, en gráficos por ordenador es posible optimizar las pruebas de intersección. Si los cascos convexos no se intersecan, las curvas tampoco. Por tanto, comprobar primero la intersección de los cascos convexos puede dar un resultado "sin intersección" muy rápido. La comprobación de la intersección o los cascos convexos es mucho más fácil, porque son rectángulos, triángulos, etc. (vea la imagen de arriba), figuras mucho más simples que la curva. -**The main value of Bezier curves for drawing -- by moving the points the curve is changing *in intuitively obvious way*.** +**El valor principal de las curvas de Bézier para dibujar: al mover los puntos, la curva cambia *de manera intuitiva*.** -Try to move control points using a mouse in the example below: +Intenta mover los puntos de control con el ratón en el siguiente ejemplo: [iframe src="demo.svg?nocpath=1&p=0,0,0.5,0,0.5,1,1,1" height=370] -**As you can notice, the curve stretches along the tangential lines 1 -> 2 and 3 -> 4.** +**Como puedes observar, la curva se extiende a lo largo de las lineas tangenciales 1 -> 2 y 3 -> 4.** -After some practice it becomes obvious how to place points to get the needed curve. And by connecting several curves we can get practically anything. +Después de algo de práctica, se vuelve obvio cómo colocar puntos para obtener la curva necesaria. Y al conectar varias curvas podemos obtener prácticamente cualquier cosa. -Here are some examples: +Aquí tenemos algunos ejemplos: ![](bezier-car.svg) ![](bezier-letter.svg) ![](bezier-vase.svg) -## De Casteljau's algorithm +## Algoritmo de De Casteljau +Hay una fórmula matemática para las curvas de Bézier, pero la veremos un poco más tarde, porque el +[algoritmo de De Casteljau](https://es.wikipedia.org/wiki/Algoritmo_de_De_Casteljau) es idéntico a la definición matemática y muestra visualmente cómo se construye. -There's a mathematical formula for Bezier curves, but let's cover it a bit later, because -[De Casteljau's algorithm](https://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm) is identical to the mathematical definition and visually shows how it is constructed. +Primero veamos el ejemplo de los 3 puntos. -First let's see the 3-points example. +Aquí está la demostración, y la explicación a continuación. -Here's the demo, and the explanation follow. - -Control points (1,2 and 3) can be moved by the mouse. Press the "play" button to run it. +Los puntos de control (1,2 y 3) se pueden mover con el ratón. Presiona el botón "play" para ejecutarlo. [iframe src="demo.svg?p=0,0,0.5,1,1,0&animate=1" height=370] -**De Casteljau's algorithm of building the 3-point bezier curve:** +**El algoritmo de De Casteljau para construir la curva de Bézier de 3 puntos:** -1. Draw control points. In the demo above they are labeled: `1`, `2`, `3`. -2. Build segments between control points 1 -> 2 -> 3. In the demo above they are <span style="color:#825E28">brown</span>. -3. The parameter `t` moves from `0` to `1`. In the example above the step `0.05` is used: the loop goes over `0, 0.05, 0.1, 0.15, ... 0.95, 1`. +1. Dibujar puntos de control. En la demostración anterior están etiquetados: `1`, `2`, `3`. +2. Construir segmentos entre los puntos de control 1 -> 2 -> 3. En la demo anterior son <span style="color:#825E28">marrones</span>. +3. El parámetro `t` se mueve de `0` a `1`. En el ejemplo de arriba se usa el paso `0.05`: el bucle pasa por `0, 0.05, 0.1, 0.15, ... 0.95, 1`. - For each of these values of `t`: + Para cada uno de estos valores de `t`: - - On each <span style="color:#825E28">brown</span> segment we take a point located on the distance proportional to `t` from its beginning. As there are two segments, we have two points. + - En cada segmento <span style="color:#825E28">marrón</span> tomamos un punto ubicado en la distancia proporcional a `t` desde su comienzo. Como hay dos segmentos, tenemos dos puntos. - For instance, for `t=0` -- both points will be at the beginning of segments, and for `t=0.25` -- on the 25% of segment length from the beginning, for `t=0.5` -- 50%(the middle), for `t=1` -- in the end of segments. + Por ejemplo, para `t=0` -- ambos puntos estarán al comienzo de los segmentos, y para `t=0.25` -- en el 25% de la longitud del segmento desde el comienzo, para `t=0.5` -- 50%(el medio), for `t=1` -- al final de los segmentos. - - Connect the points. On the picture below the connecting segment is painted <span style="color:#167490">blue</span>. + - Conecta los puntos. En la imagen de abajo el segmento de conexión está pintado de <span style="color:#167490">azul</span>. -| For `t=0.25` | For `t=0.5` | +| Para `t=0.25` | Para `t=0.5` | | ------------------------ | ---------------------- | | ![](bezier3-draw1.svg) | ![](bezier3-draw2.svg) | -4. Now in the <span style="color:#167490">blue</span> segment take a point on the distance proportional to the same value of `t`. That is, for `t=0.25` (the left picture) we have a point at the end of the left quarter of the segment, and for `t=0.5` (the right picture) -- in the middle of the segment. On pictures above that point is <span style="color:red">red</span>. +4. Ahora, en el segmento <span style="color:#167490">azul</span>, toma un punto en la distancia proporcional al mismo valor de `t`. Es decir, para `t=0.25` (la imagen de la izquierda) tenemos un punto al final del cuarto izquierdo del segmento, y para `t=0.5` (la imagen de la derecha) -- en la mitad del segmento. En las imágenes de arriba ese punto es <span style="color:red">rojo</span>. -5. As `t` runs from `0` to `1`, every value of `t` adds a point to the curve. The set of such points forms the Bezier curve. It's red and parabolic on the pictures above. +5. Como `t` va de `0` a `1`, cada valor de `t` añade un punto a la curva. El conjunto de tales puntos forma la curva de Bézier. Es rojo y parabólico en las imágenes de arriba. -That was a process for 3 points. But the same is for 4 points. +Este fue el proceso para 3 puntos. Sería lo mismo para 4 puntos. -The demo for 4 points (points can be moved by a mouse): +La demo para 4 puntos (los puntos se pueden mover con el ratón): [iframe src="demo.svg?p=0,0,0.5,0,0.5,1,1,1&animate=1" height=370] -The algorithm for 4 points: +El algoritmo para 4 puntos: -- Connect control points by segments: 1 -> 2, 2 -> 3, 3 -> 4. There will be 3 <span style="color:#825E28">brown</span> segments. -- For each `t` in the interval from `0` to `1`: - - We take points on these segments on the distance proportional to `t` from the beginning. These points are connected, so that we have two <span style="color:#0A0">green segments</span>. - - On these segments we take points proportional to `t`. We get one <span style="color:#167490">blue segment</span>. - - On the blue segment we take a point proportional to `t`. On the example above it's <span style="color:red">red</span>. -- These points together form the curve. +- Conectar puntos de control por segmentos: 1 -> 2, 2 -> 3, 3 -> 4. Habrá 3 segmentos <span style="color:#825E28">marrones</span>. +- Para cada `t` en el intervalo de `0` a `1`: + - Tomamos puntos en estos segmentos en la distancia proporcional a `t` desde el principio. Estos puntos están conectados, por lo que tenemos dos <span style="color:#0A0">segmentos verdes</span>. + - En estos segmentos tomamos puntos proporcionales a `t`. Obtenemos un <span style="color:#167490">segmento azul</span>. + - En el segmento azul tomamos un punto proporcional a `t`. En el ejemplo anterior es <span style="color:red">rojo</span>. +- Estos puntos juntos forman la curva. -The algorithm is recursive and can be generalized for any number of control points. +El algoritmo es recursivo y se puede generalizar para cualquier número de puntos de control. -Given N of control points: +Dados N de puntos de control: -1. We connect them to get initially N-1 segments. -2. Then for each `t` from `0` to `1`, we take a point on each segment on the distance proportional to `t` and connect them. There will be N-2 segments. -3. Repeat step 2 until there is only one point. +1. Los conectamos para obtener inicialmente N-1 segmentos. +2. Entonces, para cada `t` de `0` a `1`, tomamos un punto en cada segmento en la distancia proporcional a `t` y los conectamos. Habrá N-2 segmentos. +3. Repetimos el paso 2 hasta que solo quede un punto. -These points make the curve. +Estos puntos forman la curva. ```online -**Run and pause examples to clearly see the segments and how the curve is built.** +**Ejecuta y pausa los ejemplos para ver claramente los segmentos y cómo se construye la curva.** ``` -A curve that looks like `y=1/t`: +Una curva que se parece a `y=1/t`: [iframe src="demo.svg?p=0,0,0,0.75,0.25,1,1,1&animate=1" height=370] -Zig-zag control points also work fine: +Los puntos de control en zig-zag también funcionan bien: [iframe src="demo.svg?p=0,0,1,0.5,0,0.5,1,1&animate=1" height=370] -Making a loop is possible: +Es posible hacer un bucle: [iframe src="demo.svg?p=0,0,1,0.5,0,1,0.5,0&animate=1" height=370] -A non-smooth Bezier curve (yeah, that's possible too): +Una curva de Bézier no suave (sí, eso también es posible): [iframe src="demo.svg?p=0,0,1,1,0,1,1,0&animate=1" height=370] ```online -If there's something unclear in the algorithm description, please look at the live examples above to see how -the curve is built. +Si hay algo que no está claro en la descripción del algoritmo, los ejemplos en vivo de arriba muestran cómo se construye la curva. ``` -As the algorithm is recursive, we can build Bezier curves of any order, that is: using 5, 6 or more control points. But in practice many points are less useful. Usually we take 2-3 points, and for complex lines glue several curves together. That's simpler to develop and calculate. +Como el algoritmo es recursivo, podemos construir curvas de Bézier de cualquier orden, es decir, usando 5, 6 o más puntos de control. Pero en la práctica muchos puntos son menos útiles. Por lo general, tomamos 2-3 puntos, y para líneas complejas pegamos varias curvas juntas. Eso es más simple de desarrollar y calcular. -```smart header="How to draw a curve *through* given points?" -To specify a Bezier curve, control points are used. As we can see, they are not on the curve, except the first and the last ones. +```smart header="¿Cómo dibujar una curva *a través de* puntos dados?" +Para especificar una curva de Bézier se utilizan puntos de control. Como podemos ver, no están en la curva, excepto el primero y el último. -Sometimes we have another task: to draw a curve *through several points*, so that all of them are on a single smooth curve. That task is called [interpolation](https://en.wikipedia.org/wiki/Interpolation), and here we don't cover it. +A veces tenemos otra tarea: dibujar una curva *a traves de varios puntos*, de modo que todos ellos estén en una sola curva suave. Esta tarea se llama [interpolación](https://es.wikipedia.org/wiki/Interpolaci%C3%B3n), y aquí no la cubrimos. -There are mathematical formulas for such curves, for instance [Lagrange polynomial](https://en.wikipedia.org/wiki/Lagrange_polynomial). In computer graphics [spline interpolation](https://en.wikipedia.org/wiki/Spline_interpolation) is often used to build smooth curves that connect many points. +Hay fórmulas matemáticas para tales curvas, por ejemplo el [polinomio de Lagrange](https://es.wikipedia.org/wiki/Interpolaci%C3%B3n_polin%C3%B3mica_de_Lagrange). En gráficos por ordenador la [interpolación de spline](https://en.wikipedia.org/wiki/Spline_interpolation) se usa a menudo para construir curvas suaves que conectan muchos puntos. ``` -## Maths +## Matemáticas -A Bezier curve can be described using a mathematical formula. +Una curva de Bézier se puede describir usando una fórmula matemática. -As we saw -- there's actually no need to know it, most people just draw the curve by moving points with a mouse. But if you're into maths -- here it is. +Como vimos, en realidad no hay necesidad de saberlo, la mayoría de la gente simplemente dibuja la curva moviendo los puntos con un mouse. Pero si te gustan las matemáticas, aquí están. -Given the coordinates of control points <code>P<sub>i</sub></code>: the first control point has coordinates <code>P<sub>1</sub> = (x<sub>1</sub>, y<sub>1</sub>)</code>, the second: <code>P<sub>2</sub> = (x<sub>2</sub>, y<sub>2</sub>)</code>, and so on, the curve coordinates are described by the equation that depends on the parameter `t` from the segment `[0,1]`. +Dadas las coordenadas de los puntos de control <code>P<sub>i</sub></code>: el primer punto de control tiene las coordenadas <code>P<sub>1</sub> = (x<sub>1</sub>, y<sub>1</sub>)</code>, el segundo: <code>P<sub>2</sub> = (x<sub>2</sub>, y<sub>2</sub>)</code>, y así sucesivamente, las coordenadas de la curva se describen mediante la ecuación que depende del parámetro `t` del segmento `[0,1]`. -- The formula for a 2-points curve: +- La fórmula para una curva de 2 puntos: <code>P = (1-t)P<sub>1</sub> + tP<sub>2</sub></code> -- For 3 control points: +- Para 3 puntos de control: <code>P = (1−t)<sup>2</sup>P<sub>1</sub> + 2(1−t)tP<sub>2</sub> + t<sup>2</sup>P<sub>3</sub></code> -- For 4 control points: +- Para 4 puntos de control: <code>P = (1−t)<sup>3</sup>P<sub>1</sub> + 3(1−t)<sup>2</sup>tP<sub>2</sub> +3(1−t)t<sup>2</sup>P<sub>3</sub> + t<sup>3</sup>P<sub>4</sub></code> -These are vector equations. In other words, we can put `x` and `y` instead of `P` to get corresponding coordinates. +Estas son las ecuaciones vectoriales. En otras palabras, podemos poner `x` e `y` en lugar de `P` para obtener las coordenadas correspondientes. -For instance, the 3-point curve is formed by points `(x,y)` calculated as: +Por ejemplo, la curva de 3 puntos está formada por puntos `(x, y)` calculados como: - <code>x = (1−t)<sup>2</sup>x<sub>1</sub> + 2(1−t)tx<sub>2</sub> + t<sup>2</sup>x<sub>3</sub></code> - <code>y = (1−t)<sup>2</sup>y<sub>1</sub> + 2(1−t)ty<sub>2</sub> + t<sup>2</sup>y<sub>3</sub></code> -Instead of <code>x<sub>1</sub>, y<sub>1</sub>, x<sub>2</sub>, y<sub>2</sub>, x<sub>3</sub>, y<sub>3</sub></code> we should put coordinates of 3 control points, and then as `t` moves from `0` to `1`, for each value of `t` we'll have `(x,y)` of the curve. +En lugar de <code>x<sub>1</sub>, y<sub>1</sub>, x<sub>2</sub>, y<sub>2</sub>, x<sub>3</sub>, y<sub>3</sub></code> deberíamos poner coordenadas de 3 puntos de control, y luego a medida que te `t` se mueve de `0` a `1`, para cada valor de `t` tendremos `(x,y)` de la curva. -For instance, if control points are `(0,0)`, `(0.5, 1)` and `(1, 0)`, the equations become: +Por ejemplo, si los puntos de control son `(0,0)`, `(0.5, 1)` y `(1, 0)`, las ecuaciones se convierten en: - <code>x = (1−t)<sup>2</sup> * 0 + 2(1−t)t * 0.5 + t<sup>2</sup> * 1 = (1-t)t + t<sup>2</sup> = t</code> - <code>y = (1−t)<sup>2</sup> * 0 + 2(1−t)t * 1 + t<sup>2</sup> * 0 = 2(1-t)t = –2t<sup>2</sup> + 2t</code> -Now as `t` runs from `0` to `1`, the set of values `(x,y)` for each `t` forms the curve for such control points. +Ahora como `t` se ejecuta desde `0` a `1`, el conjunto de valores `(x,y)` para cada `t` forman la curva para dichos puntos de control. -## Summary +## Resumen -Bezier curves are defined by their control points. +Las curvas de Bézier se definen por sus puntos de control. -We saw two definitions of Bezier curves: +Vimos dos definiciones de curvas de Bézier: -1. Using a drawing process: De Casteljau's algorithm. -2. Using a mathematical formulas. +1. Utilizando un proceso de dibujo: el algoritmo de De Casteljau. +2. Utilizando una fórmula matemática. -Good properties of Bezier curves: +Buenas propiedades de las curvas de Bezier: -- We can draw smooth lines with a mouse by moving control points. -- Complex shapes can be made of several Bezier curves. +- Podemos dibujar líneas suaves con un ratón moviendo los puntos de control. +- Las formas complejas se pueden construir con varias curvas Bezier. -Usage: +Uso: -- In computer graphics, modeling, vector graphic editors. Fonts are described by Bezier curves. -- In web development -- for graphics on Canvas and in the SVG format. By the way, "live" examples above are written in SVG. They are actually a single SVG document that is given different points as parameters. You can open it in a separate window and see the source: [demo.svg](demo.svg?p=0,0,1,0.5,0,0.5,1,1&animate=1). -- In CSS animation to describe the path and speed of animation. +- En gráficos por ordenador, modelado, editores gráficos vectoriales. Las fuentes están descritas por curvas de Bézier. +- En desarrollo web - para gráficos en Canvas y en formato SVG. Por cierto, los ejemplos "en vivo" de arriba están escritos en SVG. En realidad, son un solo documento SVG que recibe diferentes puntos como parámetros. Puede abrirlo en una ventana separada y ver el código fuente: [demo.svg](demo.svg?p=0,0,1,0.5,0,0.5,1,1&animate=1). +- En animación CSS para describir la trayectoria y la velocidad de la animación. diff --git a/7-animation/2-css-animations/1-animate-logo-css/solution.md b/7-animation/2-css-animations/1-animate-logo-css/solution.md index 7aa7ca728..04991519a 100644 --- a/7-animation/2-css-animations/1-animate-logo-css/solution.md +++ b/7-animation/2-css-animations/1-animate-logo-css/solution.md @@ -1,17 +1,17 @@ -CSS to animate both `width` and `height`: +CSS para animar tanto `width` como `height`: ```css -/* original class */ +/* clase original */ #flyjet { transition: all 3s; } -/* JS adds .growing */ +/* JS añade .growing */ #flyjet.growing { width: 400px; height: 240px; } ``` -Please note that `transitionend` triggers two times -- once for every property. So if we don't perform an additional check then the message would show up 2 times. +Ten en cuenta que `transitionend` se dispara dos veces, una para cada propiedad. Entonces, si no realizamos una verificación adicional, el mensaje aparecería 2 veces. diff --git a/7-animation/2-css-animations/1-animate-logo-css/solution.view/index.html b/7-animation/2-css-animations/1-animate-logo-css/solution.view/index.html index d77f25e28..e09729180 100644 --- a/7-animation/2-css-animations/1-animate-logo-css/solution.view/index.html +++ b/7-animation/2-css-animations/1-animate-logo-css/solution.view/index.html @@ -27,12 +27,12 @@ <img id="flyjet" src="https://en.js.cx/clipart/flyjet.jpg"> <script> - let ended = false; // will change to true after the animation finishes + let ended = false; // cambiará a true cuando la animación finalice flyjet.onclick = function() { flyjet.addEventListener('transitionend', function() { - if (!ended) { // check to show the message only once + if (!ended) { // verificación para que el mensaje solo se muestre una vez ended = true; alert('Done!'); } diff --git a/7-animation/2-css-animations/1-animate-logo-css/task.md b/7-animation/2-css-animations/1-animate-logo-css/task.md index ed10d4ace..8fbb6c0e0 100644 --- a/7-animation/2-css-animations/1-animate-logo-css/task.md +++ b/7-animation/2-css-animations/1-animate-logo-css/task.md @@ -2,13 +2,13 @@ importance: 5 --- -# Animate a plane (CSS) +# Animar un avión (CSS) -Show the animation like on the picture below (click the plane): +Muestra la animación como en la imagen a continuación (haz clic en el avión): [iframe src="solution" height=300] -- The picture grows on click from `40x24px` to `400x240px` (10 times larger). -- The animation takes 3 seconds. -- At the end output: "Done!". -- During the animation process, there may be more clicks on the plane. They shouldn't "break" anything. +- La imagen crece al hacer clic de `40x24px` a `400x240px` (10 veces más grande). +- La animación dura 3 segundos. +- Al final muestra: "¡Listo!". +- Durante el proceso de animación, puede haber más clics en el avión. No deberían "romper" nada. \ No newline at end of file diff --git a/7-animation/2-css-animations/2-animate-logo-bezier-css/solution.md b/7-animation/2-css-animations/2-animate-logo-bezier-css/solution.md index 88105399c..6a15092ae 100644 --- a/7-animation/2-css-animations/2-animate-logo-bezier-css/solution.md +++ b/7-animation/2-css-animations/2-animate-logo-bezier-css/solution.md @@ -1,7 +1,7 @@ -We need to choose the right Bezier curve for that animation. It should have `y>1` somewhere for the plane to "jump out". +Necesitamos elegir la curva de Bézier correcta para esa animación. Debe tener `y>1` en algún punto para que el avión "salte". -For instance, we can take both control points with `y>1`, like: `cubic-bezier(0.25, 1.5, 0.75, 1.5)`. +Por ejemplo, podemos tomar ambos puntos de control con `y>1`, como: `cubic-bezier(0.25, 1.5, 0.75, 1.5)`. -The graph: +La gráfica: ![](bezier-up.svg) diff --git a/7-animation/2-css-animations/2-animate-logo-bezier-css/task.md b/7-animation/2-css-animations/2-animate-logo-bezier-css/task.md index 18e63b480..c713b202d 100644 --- a/7-animation/2-css-animations/2-animate-logo-bezier-css/task.md +++ b/7-animation/2-css-animations/2-animate-logo-bezier-css/task.md @@ -2,12 +2,12 @@ importance: 5 --- -# Animate the flying plane (CSS) +# Animar el avión volando (CSS) -Modify the solution of the previous task <info:task/animate-logo-css> to make the plane grow more than its original size 400x240px (jump out), and then return to that size. +Modifica la solución de la tarea anterior <info:task/animate-logo-css> para hacer que el avión crezca más que su tamaño original 400x240px (saltar fuera), y luego vuelva a ese tamaño. -Here's how it should look (click on the plane): +Así es como debería verse (haz clic en el avión): [iframe src="solution" height=350] -Take the solution of the previous task as the source. +Toma la solución de la tarea anterior como punto de partida. diff --git a/7-animation/2-css-animations/3-animate-circle/task.md b/7-animation/2-css-animations/3-animate-circle/task.md index 83bbb3e84..0856bc50a 100644 --- a/7-animation/2-css-animations/3-animate-circle/task.md +++ b/7-animation/2-css-animations/3-animate-circle/task.md @@ -2,15 +2,15 @@ importance: 5 --- -# Animated circle +# Círculo animado -Create a function `showCircle(cx, cy, radius)` that shows an animated growing circle. +Crea una función `showCircle(cx, cy, radius)` que muestre un círculo animado creciendo. -- `cx,cy` are window-relative coordinates of the center of the circle, -- `radius` is the radius of the circle. +- `cx,cy` son coordenadas relativas a la ventana del centro del círculo, +- `radius` es el radio del círculo. -Click the button below to see how it should look like: +Haz clic en el botón de abajo para ver cómo debería verse: [iframe src="solution" height=260] -The source document has an example of a circle with right styles, so the task is precisely to do the animation right. +El documento fuente tiene un ejemplo de un círculo con estilos correctos, por lo que la tarea es precisamente hacer la animación correctamente. diff --git a/7-animation/2-css-animations/4-animate-circle-callback/solution.view/index.html b/7-animation/2-css-animations/4-animate-circle-callback/solution.view/index.html index 64746e85f..e35e883d7 100644 --- a/7-animation/2-css-animations/4-animate-circle-callback/solution.view/index.html +++ b/7-animation/2-css-animations/4-animate-circle-callback/solution.view/index.html @@ -22,14 +22,14 @@ <body> - <button onclick="go()">Click me</button> + <button onclick="go()">Pruébame</button> <script> function go() { showCircle(150, 150, 100, div => { div.classList.add('message-ball'); - div.append("Hello, world!"); + div.append("Hola, mundo!"); }); } diff --git a/7-animation/2-css-animations/4-animate-circle-callback/task.md b/7-animation/2-css-animations/4-animate-circle-callback/task.md index 4a20ca604..1ee33b56f 100644 --- a/7-animation/2-css-animations/4-animate-circle-callback/task.md +++ b/7-animation/2-css-animations/4-animate-circle-callback/task.md @@ -1,25 +1,25 @@ -# Animated circle with callback +# Círculo animado con función de callback -In the task <info:task/animate-circle> an animated growing circle is shown. +En la tarea <info:task/animate-circle> se muestra un círculo creciente animado. -Now let's say we need not just a circle, but to show a message inside it. The message should appear *after* the animation is complete (the circle is fully grown), otherwise it would look ugly. +Ahora digamos que necesitamos no solo un círculo, sino mostrar un mensaje dentro de él. El mensaje debería aparecer *después* de que la animación esté completa (el círculo es desarrollado completamente), de lo contrario se vería feo. -In the solution of the task, the function `showCircle(cx, cy, radius)` draws the circle, but gives no way to track when it's ready. +En la solución de la tarea, la función `showCircle(cx, cy, radius)` dibuja el círculo, pero no hay forma de saber cuando lo termina. -Add a callback argument: `showCircle(cx, cy, radius, callback)` to be called when the animation is complete. The `callback` should receive the circle `<div>` as an argument. +Agrega un argumento callback: `showCircle(cx, cy, radius, callback)` que se llamará cuando se complete la animación. El `callback` debería recibir el círculo `<div>` como argumento. -Here's the example: +Aqui el ejemplo: ```js showCircle(150, 150, 100, div => { div.classList.add('message-ball'); - div.append("Hello, world!"); + div.append("Hola, mundo!"); }); ``` -Demo: +Demostración: [iframe src="solution" height=260] -Take the solution of the task <info:task/animate-circle> as the base. +Toma la solución de la tarea <info:task/animate-circle> como base. diff --git a/7-animation/2-css-animations/article.md b/7-animation/2-css-animations/article.md index a6a41eaeb..e63907875 100644 --- a/7-animation/2-css-animations/article.md +++ b/7-animation/2-css-animations/article.md @@ -1,16 +1,16 @@ -# CSS-animations +# Animaciones CSS -CSS animations make it possible to do simple animations without JavaScript at all. +Las animaciones CSS permiten hacer animaciones simples sin JavaScript en absoluto. -JavaScript can be used to control CSS animations and make them even better, with little code. +Se puede utilizar JavaScript para controlar la animación CSS y mejorarla con un poco de código. -## CSS transitions [#css-transition] +## Transiciones CSS [#css-transition] -The idea of CSS transitions is simple. We describe a property and how its changes should be animated. When the property changes, the browser paints the animation. +La idea de las transiciones CSS es simple. Describimos una propiedad y cómo se deberían animar sus cambios. Cuando la propiedad cambia, el navegador pinta la animación. -That is, all we need is to change the property, and the fluid transition will be done by the browser. +Es decir: todo lo que necesitamos es cambiar la propiedad, y la transición fluida la hará el navegador. -For instance, the CSS below animates changes of `background-color` for 3 seconds: +Por ejemplo, el CSS a continuación anima los cambios de `background-color` durante 3 segundos: ```css .animated { @@ -19,12 +19,12 @@ For instance, the CSS below animates changes of `background-color` for 3 seconds } ``` -Now if an element has `.animated` class, any change of `background-color` is animated during 3 seconds. +Ahora, si un elemento tiene la clase `.animated`, cualquier cambio de `background-color` es animado durante 3 segundos. -Click the button below to animate the background: +Haz clic en el botón de abajo para animar el fondo: ```html run autorun height=60 -<button id="color">Click me</button> +<button id="color">Haz clic en mi</button> <style> #color { @@ -40,19 +40,19 @@ Click the button below to animate the background: </script> ``` -There are 4 properties to describe CSS transitions: +Hay 4 propiedades para describir las transiciones CSS: - `transition-property` - `transition-duration` - `transition-timing-function` - `transition-delay` -We'll cover them in a moment, for now let's note that the common `transition` property allows declaring them together in the order: `property duration timing-function delay`, as well as animating multiple properties at once. +Las cubriremos en un momento, por ahora tengamos en cuenta que la propiedad común `transition` permite declararlas juntas en el orden: `property duration timing-function delay`, y también animar múltiples propiedades a la vez. -For instance, this button animates both `color` and `font-size`: +Por ejemplo, este botón anima tanto `color` como `font-size`: ```html run height=80 autorun no-beautify -<button id="growing">Click me</button> +<button id="growing">Haz clic en mi</button> <style> #growing { @@ -70,29 +70,29 @@ growing.onclick = function() { </script> ``` -Now, let's cover animation properties one by one. +Ahora cubramos las propiedades de animación una por una. ## transition-property -In `transition-property`, we write a list of properties to animate, for instance: `left`, `margin-left`, `height`, `color`. Or we could write `all`, which means "animate all properties". +En `transition-property` escribimos una lista de propiedades para animar, por ejemplo: `left`, `margin-left`, `height`, `color`. O podemos escribir `all`, que significa "animar todas las propiedades". -Do note that, there are properties which can not be animated. However, [most of the generally used properties are animatable](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animated_properties). +No todas las propiedades pueden ser animadas, pero sí [la mayoría de las generalmente usadas](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animated_properties). ## transition-duration -In `transition-duration` we can specify how long the animation should take. The time should be in [CSS time format](https://www.w3.org/TR/css3-values/#time): in seconds `s` or milliseconds `ms`. +En `transition-duration` podemos especificar cuánto tiempo debe durar la animación. El tiempo debe estar en [formato de tiempo CSS](https://www.w3.org/TR/css3-values/#time): en segundos `s` o milisegundos `ms`. ## transition-delay -In `transition-delay` we can specify the delay *before* the animation. For instance, if `transition-delay` is `1s` and `transition-duration` is `2s`, then the animation starts 1 second after the property change and the total duration will be 2 seconds. +En `transition-delay` podemos especificar el retraso *antes* de la animación. Por ejemplo, si `transition-delay` es `1s` y `transition-duration` es `2s`, la animación comienza después de 1 segundo tras el cambio de la propiedad y la duración total será de 2 segundos. -Negative values are also possible. Then the animation is shown immediately, but the starting point of the animation will be after given value (time). For example, if `transition-delay` is `-1s` and `transition-duration` is `2s`, then animation starts from the halfway point and total duration will be 1 second. +Los valores negativos también son posibles. De esta manera la animación comienza inmediatamente, pero el punto de inicio de la animación será el del valor dado (tiempo). Por ejemplo, si `transition-delay` es `-1s` y `transition-duration` es `2s`, entonces la animación comienza desde la mitad y la duración total será de 1 segundo. -Here the animation shifts numbers from `0` to `9` using CSS `translate` property: +Aquí la animación cambia los números de `0` a `9` usando la propiedad CSS `translate`: [codetabs src="digits"] -The `transform` property is animated like this: +La propiedad `transform` se anima así: ```css #stripe.animate { @@ -102,25 +102,25 @@ The `transform` property is animated like this: } ``` -In the example above JavaScript adds the class `.animate` to the element -- and the animation starts: +En el ejemplo anterior, JavaScript agrega la clase `.animate` al elemento, y comienza la animación: ```js stripe.classList.add('animate'); ``` -We could also start it from somewhere in the middle of the transition, from an exact number, e.g. corresponding to the current second, using a negative `transition-delay`. +También podemos comenzar "desde el medio", desde el número exacto, p. ej. correspondiente al segundo actual, usando el negativo `transition-delay`. -Here if you click the digit -- it starts the animation from the current second: +Aquí, si haces clic en el dígito, comienza la animación desde el segundo actual: [codetabs src="digits-negative-delay"] -JavaScript does it with an extra line: +JavaScript lo hace con una línea extra: ```js stripe.onclick = function() { let sec = new Date().getSeconds() % 10; *!* - // for instance, -3s here starts the animation from the 3rd second + // por ejemplo, -3s aquí comienza la animación desde el 3er segundo stripe.style.transitionDelay = '-' + sec + 's'; */!* stripe.classList.add('animate'); @@ -129,60 +129,60 @@ stripe.onclick = function() { ## transition-timing-function -The timing function describes how the animation process is distributed along its timeline. Will it start slowly and then go fast, or vice versa. +La función de temporización describe cómo se distribuye el proceso de animación a lo largo del tiempo. Comenzará lentamente y luego irá rápido o viceversa. -It appears to be the most complicated property at first. But it becomes very simple if we devote a bit time to it. +Es la propiedad más complicada a primera vista. Pero se vuelve muy simple si le dedicamos un poco de tiempo. -That property accepts two kinds of values: a Bezier curve or steps. Let's start with the curve, as it's used more often. +Esa propiedad acepta dos tipos de valores: una curva de Bézier o pasos. Comencemos por la curva, ya que se usa con más frecuencia. -### Bezier curve +### Curva de Bézier -The timing function can be set as a [Bezier curve](/bezier-curve) with 4 control points that satisfy the conditions: +La función de temporización se puede establecer como una [curva de Bézier](/bezier-curve) con 4 puntos de control que satisfacen las condiciones: -1. First control point: `(0,0)`. -2. Last control point: `(1,1)`. -3. For intermediate points, the values of `x` must be in the interval `0..1`, `y` can be anything. +1. Primer punto de control: `(0,0)`. +2. Último punto de control: `(1,1)`. +3. Para los puntos intermedios, los valores de `x` deben estar en el intervalo `0..1`, `y` puede ser cualquier cosa. -The syntax for a Bezier curve in CSS: `cubic-bezier(x2, y2, x3, y3)`. Here we need to specify only 2nd and 3rd control points, because the 1st one is fixed to `(0,0)` and the 4th one is `(1,1)`. +La sintaxis de una curva de Bézier en CSS: `cubic-bezier(x2, y2, x3, y3)`. Aquí necesitamos especificar solo los puntos de control segundo y tercero, porque el primero está fijado a `(0,0)` y el cuarto es `(1,1)`. -The timing function describes how fast the animation process goes. +La función de temporización determina qué tan rápido ocurre el proceso de animación. -- The `x` axis is the time: `0` -- the start, `1` -- the end of `transition-duration`. -- The `y` axis specifies the completion of the process: `0` -- the starting value of the property, `1` -- the final value. +- El eje `x` es el tiempo: `0` -- el momento inicial, `1` -- el último momento de `transition-duration`. +- El eje `y` especifica la finalización del proceso: `0` -- el valor inicial de la propiedad, `1` -- el valor final. -The simplest variant is when the animation goes uniformly, with the same linear speed. That can be specified by the curve `cubic-bezier(0, 0, 1, 1)`. +La variante más simple es cuando la animación es uniforme, con la misma velocidad lineal. Eso puede especificarse mediante la curva `cubic-bezier(0, 0, 1, 1)`. -Here's how that curve looks: +Así es como se ve esa curva: ![](bezier-linear.svg) -...As we can see, it's just a straight line. As the time (`x`) passes, the completion (`y`) of the animation steadily goes from `0` to `1`. +... Como podemos ver, es solo una línea recta. A medida que pasa el tiempo (`x`), la finalización (`y`) de la animación pasa constantemente de `0` a` 1`. -The train in the example below goes from left to right with the permanent speed (click it): +El tren, en el ejemplo a continuación, va de izquierda a derecha con velocidad constante (haz clic en él): [codetabs src="train-linear"] -The CSS `transition` is based on that curve: +La `transition` de CSS se basa en esa curva: ```css .train { left: 0; transition: left 5s cubic-bezier(0, 0, 1, 1); - /* click on a train sets left to 450px, thus triggering the animation */ + /* el clic en un tren establece left a 450px, disparando la animación */ } ``` -...And how can we show a train slowing down? +... ¿Y cómo podemos mostrar un tren desacelerando? -We can use another Bezier curve: `cubic-bezier(0.0, 0.5, 0.5 ,1.0)`. +Podemos usar otra curva de Bézier: `cubic-bezier(0.0, 0.5, 0.5, 1.0)`. -The graph: +La gráfica: ![](train-curve.svg) -As we can see, the process starts fast: the curve soars up high, and then slower and slower. +Como podemos ver, el proceso comienza rápido: la curva se eleva mucho, y luego más y más despacio. -Here's the timing function in action (click the train): +Aquí está la función de temporización en acción (haz clic en el tren): [codetabs src="train"] @@ -191,90 +191,89 @@ CSS: .train { left: 0; transition: left 5s cubic-bezier(0, .5, .5, 1); - /* click on a train sets left to 450px, thus triggering the animation */ + /* el clic en un tren establece left a 450px, disparando la animación */ } ``` -There are several built-in curves: `linear`, `ease`, `ease-in`, `ease-out` and `ease-in-out`. +Hay varias curvas incorporadas: `linear`,` ease`, `ease-in`,` ease-out` y `ease-in-out`. -The `linear` is a shorthand for `cubic-bezier(0, 0, 1, 1)` -- a straight line, which we described above. +La `linear` es una abreviatura de `cubic-bezier(0, 0, 1, 1) ` -- una línea recta, como ya vimos. -Other names are shorthands for the following `cubic-bezier`: +Otros nombres son abreviaturas para la siguiente `cubic-bezier`: | <code>ease</code><sup>*</sup> | <code>ease-in</code> | <code>ease-out</code> | <code>ease-in-out</code> | |-------------------------------|----------------------|-----------------------|--------------------------| | <code>(0.25, 0.1, 0.25, 1.0)</code> | <code>(0.42, 0, 1.0, 1.0)</code> | <code>(0, 0, 0.58, 1.0)</code> | <code>(0.42, 0, 0.58, 1.0)</code> | | ![ease, figure](ease.svg) | ![ease-in, figure](ease-in.svg) | ![ease-out, figure](ease-out.svg) | ![ease-in-out, figure](ease-in-out.svg) | -`*` -- by default, if there's no timing function, `ease` is used. +`*` -- por defecto, si no hay una función de temporización, se utiliza `ease`. -So we could use `ease-out` for our slowing down train: +Por lo tanto, podríamos usar `ease-out` para nuestro tren desacelerando: ```css .train { left: 0; transition: left 5s ease-out; - /* same as transition: left 5s cubic-bezier(0, .5, .5, 1); */ + /* igual que transition: left 5s cubic-bezier(0, .5, .5, 1); */ } ``` -But it looks a bit differently. +Pero se ve un poco diferente. -**A Bezier curve can make the animation exceed its range.** +**Una curva de Bézier puede hacer que la animación exceda su rango.** -The control points on the curve can have any `y` coordinates: even negative or huge ones. Then the Bezier curve would also extend very low or high, making the animation go beyond its normal range. +Los puntos de control en la curva pueden tener cualquier coordenada `y`: incluso negativa o enorme. Entonces la curva de Bézier también saltaría muy bajo o alto, haciendo que la animación vaya más allá de su rango normal. -In the example below the animation code is: +En el siguiente ejemplo, el código de animación es: ```css .train { left: 100px; transition: left 5s cubic-bezier(.5, -1, .5, 2); - /* click on a train sets left to 450px */ + /* clic en un tren establece left a 400px */ } ``` -The property `left` should animate from `100px` to `400px`. +La propiedad `left` debería animarse de `100px` a `400px`. -But if you click the train, you'll see that: +Pero si haces clic en el tren, verás que: -- First, the train goes *back*: `left` becomes less than `100px`. -- Then it goes forward, a little bit farther than `400px`. -- And then back again -- to `400px`. +- Primero, el tren va *atrás*: `left` llega a ser menor que `100px`. +- Luego avanza, un poco más allá de `400px`. +- Y luego de vuelve a `400px`. [codetabs src="train-over"] -Why it happens is pretty obvious if we look at the graph of the given Bezier curve: +¿Por qué sucede? es bastante obvio si miramos la gráfica de la curva de Bézier dada: ![](bezier-train-over.svg) -We moved the `y` coordinate of the 2nd point below zero, and for the 3rd point we made it over `1`, so the curve goes out of the "regular" quadrant. The `y` is out of the "standard" range `0..1`. +Movimos la coordenada `y` del segundo punto por debajo de cero, y para el tercer punto lo colocamos sobre `1`, de modo que la curva sale del cuadrante "regular". La `y` está fuera del rango "estándar" `0..1`. -As we know, `y` measures "the completion of the animation process". The value `y = 0` corresponds to the starting property value and `y = 1` -- the ending value. So values `y<0` move the property beyond the starting `left` and `y>1` -- past the final `left`. +Como sabemos, `y` mide "la finalización del proceso de animación". El valor `y = 0` corresponde al valor inicial de la propiedad e `y = 1` al valor final. Por lo tanto, los valores `y<0` mueven la propiedad por debajo del `left` inicial e `y>1` por encima del `left` final. -That's a "soft" variant for sure. If we put `y` values like `-99` and `99` then the train would jump out of the range much more. +Esa es una variante "suave" sin duda. Si ponemos valores `y` como `-99` y `99`, entonces el tren saltaría mucho más fuera del rango. -But how do we make a Bezier curve for a specific task? There are many tools. +Pero, ¿cómo hacer la curva de Bézier para una tarea específica? Hay muchas herramientas. -- For instance, we can do it on the site <https://cubic-bezier.com>. -- Browser developer tools also have special support for Bezier curves in CSS: - 1. Open the developer tools with `key:F12` (Mac: `key:Cmd+Opt+I`). - 2. Select the `Elements` tab, then pay attention to the `Styles` sub-panel at the right side. - 3. CSS properties with a word `cubic-bezier` will have an icon before this word. - 4. Click this icon to edit the curve. +- Por ejemplo, podemos hacerlo en el sitio <http://cubic-bezier.com/>. +- El navegador también tiene soporte especial para curvas Bezier en CSS: + 1. Abre las herramientas de desarrollador con `key:F12` (Mac: `key:Cmd+Opt+I`). + 2. Selecciona la pestaña `Elementos` y presta atención al subpanel `Estilos`. + 3. Las propiedades CSS que contengan la palabra `cubic-bezier` tendrán un icono antes de esta palabra. + 4. Haz clic en este icono para editar la curva. +### Pasos -### Steps +La función de temporización `steps(number of steps[, start/end])` permite dividir la animación en múltiples pasos. -The timing function `steps(number of steps[, start/end])` allows splitting an transition into multiple steps. +Veamos eso en un ejemplo con dígitos. -Let's see that in an example with digits. - -Here's a list of digits, without any animations, just as a source: +Aquí tenemos una lista de dígitos, sin animaciones, solo como fuente: [codetabs src="step-list"] -In the HTML, a stripe of digits is enclosed into a fixed-length `<div id="digits">`: +En el HTML, una línea de dígitos está encerrada en un div de largo fijo`<div id="digits">`: ```html <div id="digit"> @@ -282,13 +281,13 @@ In the HTML, a stripe of digits is enclosed into a fixed-length `<div id="digits </div> ``` -The `#digit` div has a fixed width and a border, so it looks like a red window. +El div `#digit`tiene ancho fijo y un borde, entonces se ve como una ventana roja. -We'll make a timer: the digits will appear one by one, in a discrete way. +Haremos un temporizador: los dígitos aparecerán uno por uno, de una manera discreta. -To achieve that, we'll hide the `#stripe` outside of `#digit` using `overflow: hidden`, and then shift the `#stripe` to the left step-by-step. +Para lograr esto, ocultaremos el `#stripe` fuera de `#digit` usando `overflow: hidden`, y luego desplazamos el `#stripe` a la izquierda paso a paso. -There will be 9 steps, a step-move for each digit: +Habrá 9 pasos, un paso para cada dígito: ```css #stripe.animate { @@ -297,60 +296,60 @@ There will be 9 steps, a step-move for each digit: } ``` -The first argument of `steps(9, start)` is the number of steps. The transform will be split into 9 parts (10% each). The time interval is automatically divided into 9 parts as well, so `transition: 9s` gives us 9 seconds for the whole animation – 1 second per digit. +El primer argumento de `steps(9, start)` es el número de pasos. La transformación se dividirá en 9 partes (10% cada una). El intervalo de tiempo también se divide automáticamente en 9 partes, por lo que `transition: 9s` nos da 9 segundos para toda la animación: 1 segundo por dígito. -The second argument is one of two words: `start` or `end`. +El segundo argumento es una de dos palabras: `start` o `end`. -The `start` means that in the beginning of animation we need to make the first step immediately. +El `start` significa que al comienzo de la animación debemos hacer el primer paso de inmediato. -In action: +En acción: [codetabs src="step"] -A click on the digit changes it to `1` (the first step) immediately, and then changes in the beginning of the next second. +Un clic en el dígito lo cambia a `1` (el primer paso) inmediatamente, y luego cambia al comienzo del siguiente segundo. -The process is progressing like this: +El proceso está progresando así: -- `0s` -- `-10%` (first change in the beginning of the 1st second, immediately) +- `0s` -- `-10%` (primer cambio al comienzo del primer segundo, inmediatamente) - `1s` -- `-20%` - ... - `8s` -- `-90%` -- (the last second shows the final value). +- (el último segundo muestra el valor final). -Here, the first change was immediate because of `start` in the `steps`. +Aquí el primer cambio fue inmediato por el`start` en el `steps` -The alternative value `end` would mean that the change should be applied not in the beginning, but at the end of each second. +El valor alternativo 'end' haría que el cambio se aplicara no al principio sino al final de cada segundo. -So the process for `steps(9, end)` would go like this: +Entonces el proceso para `steps(9, end)` sería así: -- `0s` -- `0` (during the first second nothing changes) -- `1s` -- `-10%` (first change at the end of the 1st second) +- `0s` -- `0` (durante el primer segundo nada cambia) +- `1s` -- `-10%` (primer cambio al final del primer segundo) - `2s` -- `-20%` - ... - `9s` -- `-90%` -Here's `steps(9, end)` in action (note the pause before the first digit change): +Aquí está el `step(9, end)` en acción (observa la pausa antes del primer cambio de dígitos): [codetabs src="step-end"] -There are also some pre-defined shorthands for `steps(...)`: +También hay algunas formas abreviadas predefinidas para `steps(...)`: -- `step-start` -- is the same as `steps(1, start)`. That is, the animation starts immediately and takes 1 step. So it starts and finishes immediately, as if there were no animation. -- `step-end` -- the same as `steps(1, end)`: make the animation in a single step at the end of `transition-duration`. +- `step-start` -- es lo mismo que` steps(1, start)`. Es decir, la animación comienza de inmediato y toma 1 paso. Entonces comienza y termina inmediatamente, como si no hubiera animación. +- `step-end` -- lo mismo que `steps(1, end)`: realiza la animación en un solo paso al final de `transition-duration`. -These values are rarely used, as they represent not a real animation, but rather a single-step change. We mention them here for completeness. +Estos valores rara vez se usan porque no representan una verdadera animación sino un cambio de un solo paso. -## Event: "transitionend" +## Evento transitionend -When the CSS animation finishes, the `transitionend` event triggers. +Cuando finaliza la animación CSS, se dispara el evento `transitionend`. -It is widely used to do an action after the animation is done. Also we can join animations. +Es ampliamente utilizado para hacer una acción después de que se realiza la animación. También podemos unir animaciones. -For instance, the ship in the example below starts to sail there and back when clicked, each time farther and farther to the right: +Por ejemplo, el barco a continuación comienza a navegar ida y vuelta al hacer clic, cada vez más y más a la derecha: [iframe src="boat" height=300 edit link] -The animation is initiated by the function `go` that re-runs each time the transition finishes, and flips the direction: +La animación se inicia mediante la función `go` que se vuelve a ejecutar cada vez que finaliza la transición y cambia la dirección: ```js boat.onclick = function() { @@ -359,11 +358,11 @@ boat.onclick = function() { function go() { if (times % 2) { - // sail to the right + // navegar a la derecha boat.classList.remove('back'); boat.style.marginLeft = 100 * times + 200 + 'px'; } else { - // sail to the left + // navegar a la izquierda boat.classList.add('back'); boat.style.marginLeft = 100 * times - 200 + 'px'; } @@ -379,40 +378,40 @@ boat.onclick = function() { }; ``` -The event object for `transitionend` has a few specific properties: +El objeto de evento para `transitionend` tiene pocas propiedades específicas: `event.propertyName` -: The property that has finished animating. Can be good if we animate multiple properties simultaneously. +: La propiedad que ha terminado de animarse. Puede ser bueno si animamos múltiples propiedades simultáneamente. `event.elapsedTime` -: The time (in seconds) that the animation took, without `transition-delay`. +: El tiempo (en segundos) que duró la animación, sin `transition-delay`. -## Keyframes +## Fotogramas clave (Keyframes) -We can join multiple simple animations together using the `@keyframes` CSS rule. +Podemos unir múltiples animaciones simples juntas usando la regla CSS `@keyframes`. -It specifies the "name" of the animation and rules - what, when and where to animate. Then using the `animation` property, we can attach the animation to the element and specify additional parameters for it. +Especifica el "nombre" de la animación y las reglas: qué, cuándo y dónde animar. Luego, usando la propiedad `animation`, adjuntamos la animación al elemento y especificamos parámetros adicionales para él. -Here's an example with explanations: +Aquí tenemos un ejemplo con explicaciones: ```html run height=60 autorun="no-epub" no-beautify <div class="progress"></div> <style> *!* - @keyframes go-left-right { /* give it a name: "go-left-right" */ - from { left: 0px; } /* animate from left: 0px */ - to { left: calc(100% - 50px); } /* animate to left: 100%-50px */ + @keyframes go-left-right { /* dale un nombre: "go-left-right" */ + from { left: 0px; } /* animar desde la izquierda: 0px */ + to { left: calc(100% - 50px); } /* animar a la izquierda: 100%-50px */ } */!* .progress { *!* animation: go-left-right 3s infinite alternate; - /* apply the animation "go-left-right" to the element - duration 3 seconds - number of times: infinite - alternate direction every time + /* aplicar la animación "go-left-right" al elemento + duración 3 segundos + número de veces: infinitas + alternar la dirección cada vez */ */!* @@ -425,45 +424,45 @@ Here's an example with explanations: </style> ``` -There are many articles about `@keyframes` and a [detailed specification](https://drafts.csswg.org/css-animations/). +Hay muchos artículos sobre `@keyframes` y una [especificación detallada](https://drafts.csswg.org/css-animations/). -You probably won't need `@keyframes` often, unless everything is in constant motion on your sites. +Probablemente no necesitarás `@keyframes` a menudo, a menos que todo esté en constante movimiento en tus sitios. ## Performance -Most CSS properties can be animated, because most of them are numeric values. For instance, `width`, `color`, `font-size` are all numbers. When you animate them, the browser gradually changes these numbers frame by frame, creating a smooth effect. +La mayoría de las propiedades CSS pueden ser animadas, porque la mayoría son valores numéricos. Por ejemplo `width`, `color`, `font-size` son todas números. Cuando las animamos, el navegador cambia estos valores gradualmente grama por grama, creando un efecto suave. -However, not all animations will look as smooth as you'd like, because different CSS properties cost differently to change. +Sin embargo, no todas las animaciones se verán tan suaves como quisieras, porque diferentes propiedades CSS tienen diferente costo para cambiar. -In more technical details, when there's a style change, the browser goes through 3 steps to render the new look: +En detalles más técnicos, cuando hay un cambio de estilo, el navegador atraviesa 3 pasos para renderizar la nueva vista: -1. **Layout**: re-compute the geometry and position of each element, then -2. **Paint**: re-compute how everything should look like at their places, including background, colors, -3. **Composite**: render the final results into pixels on screen, apply CSS transforms if they exist. +1. **Layout**: (diagrama) recalcula la geometría y posición de cada elemento, luego +2. **Paint**: (dibuja) recalcula cómo debe verse todo en sus lugares, incluyendo background, colores, +3. **Composite**: (render) despliega el resultado final en pixels de la pantalla, aplicando transformaciones CSS si existen. -During a CSS animation, this process repeats every frame. However, CSS properties that never affect geometry or position, such as `color`, may skip the Layout step. If a `color` changes, the browser doesn't calculate any new geometry, it goes to Paint -> Composite. And there are few properties that directly go to Composite. You can find a longer list of CSS properties and which stages they trigger at <https://csstriggers.com>. +Durante una animación CSS , este proceso se repite para cada frame. Sin embargo las propiedades CSS que nunca afectan geometría o posición, como `color`, pueden saltar el paso "Layout". Si un `color` cambia, el navegador no recalcula geometría, va a Paint -> Composite. Y hay unas pocas propiedades que saltan directo a "Composite". Puedes encontrar la lista de propiedades CSS y cuáles estados disparan en <https://csstriggers.com>. -The calculations may take time, especially on pages with many elements and a complex layout. And the delays are actually visible on most devices, leading to "jittery", less fluid animations. +Los cálculos pueden tomar un tiempo, especialmente en páginas con muchos elementos y diagramación compleja. Y los retrasos pueden ser notorios en muchos dispositivos, provocando "jitter": animaciones irregulares, menos fluidas. -Animations of properties that skip the Layout step are faster. It's even better if Paint is skipped too. +La animación de propiedades que salten el paso "Layout" son más rápidas. Mucho mejor si el paso "Paint" se salta también. -The `transform` property is a great choice, because: -- CSS transforms affect the target element box as a whole (rotate, flip, stretch, shift it). -- CSS transforms never affect neighbour elements. +La propiedad `transform` es una excelente opción porque: +- CSS transform afecta el elemento objetivo como un todo (rotar, tornar, estirar, desplazar). +- CSS transform nunca afecta a los elementos vecinos. -...So browsers apply `transform` "on top" of existing Layout and Paint calculations, in the Composite stage. +...entonces los navegadores aplican `transform` "por encima" de "Layout" y "Paint" ya calculados, en el paso "Composite". -In other words, the browser calculates the Layout (sizes, positions), paints it with colors, backgrounds, etc at the Paint stage, and then applies `transform` to element boxes that need it. +En otras palabras, el navegador calcula la diagramación en la etapa Layout (tamaños, posiciones); lo dibuja con colores, backgrounds, etc., en la etapa "Paint"; y luego aplica `transform` a los elementos que lo necesitan. -Changes (animations) of the `transform` property never trigger Layout and Paint steps. More than that, the browser leverages the graphics accelerator (a special chip on the CPU or graphics card) for CSS transforms, thus making them very efficient. +Cambios (animaciones) de la propiedad `transform` nunca disparan los pasos Layout y Paint. Aún más, el navegador delega las transformaciones CSS en el acelerador gráfico (un chip especial en la CPU o placa gráfica), haciéndolas muy eficientes. -Luckily, the `transform` property is very powerful. By using `transform` on an element, you could rotate and flip it, stretch and shrink it, move it around, and [much more](https://developer.mozilla.org/docs/Web/CSS/transform#syntax). So instead of `left/margin-left` properties we can use `transform: translateX(…)`, use `transform: scale` for increasing element size, etc. +Afortunadamente la propiedad `transform` es muy poderosa. Usando `transform` en un elemento, puedes rotarlo, darlo vuelta, estirarlo o comprimirlo, desplazarlo y [mucho más](https://developer.mozilla.org/docs/Web/CSS/transform#syntax). Así que en lugar de las propiedades `left/margin-left` podemos usar `transform: translateX(…)`, o usar `transform: scale` para incrementar su tamaño, etc. -The `opacity` property also never triggers Layout (also skips Paint in Mozilla Gecko). We can use it for show/hide or fade-in/fade-out effects. +La propiedad `opacity` tampoco dispara "Layout" (también se salta "Paint" en Gecko de Mozilla). Podemos usarlo para efectos de mostrar/ocultar o desvanecer/aparecer. -Paring `transform` with `opacity` can usually solve most of our needs, providing fluid, good-looking animations. +Aparear `transform` con `opacity` puede usualmente resolver la mayoría de nuestras necesidades brindando animaciones vistosas y fluidas. -For example, here clicking on the `#boat` element adds the class with `transform: translateX(300px)` and `opacity: 0`, thus making it move `300px` to the right and disappear: +Aquí, por ejemplo, un clic en el elemento `#boat` le agrega la clase con `transform: translateX(300)` y `opacity: 0`, haciendo que se mueva `300px` a la derecha y desaparezca: ```html run height=260 autorun no-beautify <img src="https://js.cx/clipart/boat.png" id="boat"> @@ -484,7 +483,7 @@ For example, here clicking on the `#boat` element adds the class with `transform </script> ``` -Here's a more complex example, with `@keyframes`: +Un ejemplo más complejo con `@keyframes`: ```html run height=80 autorun no-beautify <h2 onclick="this.classList.toggle('animated')">click me to start / stop</h2> @@ -510,23 +509,23 @@ Here's a more complex example, with `@keyframes`: </style> ``` -## Summary +## Resumen -CSS animations allow smoothly (or step-by-step) animated changes of one or multiple CSS properties. +Las animaciones CSS permiten animar, suavemente o por pasos, los cambios de una o varias propiedades CSS. -They are good for most animation tasks. We're also able to use JavaScript for animations, the next chapter is devoted to that. +Son buenas para la mayoría de las tareas de animación. También podemos usar JavaScript para animaciones, el siguiente capítulo está dedicado a eso. -Limitations of CSS animations compared to JavaScript animations: +Limitaciones de las animaciones CSS en comparación con las animaciones JavaScript: -```compare plus="CSS animations" minus="JavaScript animations" -+ Simple things done simply. -+ Fast and lightweight for CPU. -- JavaScript animations are flexible. They can implement any animation logic, like an "explosion" of an element. -- Not just property changes. We can create new elements in JavaScript as part of the animation. +```comparar más="Animaciones CSS" menos="Animaciones JavaScript" ++ Cosas simples hechas simplemente. ++ Rápido y ligero para la CPU. +- Las animaciones de JavaScript son flexibles. Pueden implementar cualquier lógica de animación, como una "explosión" de un elemento. +- No solo cambios de propiedad. Podemos crear nuevos elementos en JavaScript para fines de animación. ``` -In early examples in this chapter, we animate `font-size`, `left`, `width`, `height`, etc. In real life projects, we should use `transform: scale()` and `transform: translate()` for better performance. +En los ejemplos de este artículo animamos `font-size`, `left`, `width`, `height`, etc. En proyectos de la vida real es preferible usar `transform: scale()` y `transform: translate()` para obtener mejor performance. -The majority of animations can be implemented using CSS as described in this chapter. And the `transitionend` event allows JavaScript to be run after the animation, so it integrates fine with the code. +La mayoría de las animaciones se pueden implementar usando CSS como se describe en este capítulo. Y el evento `transitionend` permite ejecutar JavaScript después de la animación, por lo que se integra bien con el código. -But in the next chapter we'll do some JavaScript animations to cover more complex cases. +Pero en el próximo capítulo haremos algunas animaciones en JavaScript para cubrir casos más complejos. diff --git a/7-animation/2-css-animations/boat.view/index.html b/7-animation/2-css-animations/boat.view/index.html index 60b704262..4bfcfc7eb 100644 --- a/7-animation/2-css-animations/boat.view/index.html +++ b/7-animation/2-css-animations/boat.view/index.html @@ -13,7 +13,7 @@ <script> boat.onclick = function() { - this.onclick = null; // only the first click should start the animation + this.onclick = null; // solo el primer clic debe comenzar la animación let times = 1; diff --git a/7-animation/2-css-animations/digits-negative-delay.view/index.html b/7-animation/2-css-animations/digits-negative-delay.view/index.html index dbeefaae2..482b38d7d 100644 --- a/7-animation/2-css-animations/digits-negative-delay.view/index.html +++ b/7-animation/2-css-animations/digits-negative-delay.view/index.html @@ -8,7 +8,7 @@ <body> - Click below to animate: + Haz clic a continuación para animar: <div id="digit"><div id="stripe">0123456789</div></div> <script src="script.js"></script> diff --git a/7-animation/2-css-animations/digits.view/index.html b/7-animation/2-css-animations/digits.view/index.html index a156d8189..a459f0f4c 100644 --- a/7-animation/2-css-animations/digits.view/index.html +++ b/7-animation/2-css-animations/digits.view/index.html @@ -8,7 +8,7 @@ <body> - Click below to animate: + Haz clic a continuación para animar: <div id="digit"><div id="stripe">0123456789</div></div> diff --git a/7-animation/2-css-animations/step-end.view/index.html b/7-animation/2-css-animations/step-end.view/index.html index 2c8df7275..0f4f85e03 100644 --- a/7-animation/2-css-animations/step-end.view/index.html +++ b/7-animation/2-css-animations/step-end.view/index.html @@ -8,7 +8,7 @@ <body> - Click below to animate: + Haz clic a continuación para animar: <div id="digit"><div id="stripe">0123456789</div></div> diff --git a/7-animation/2-css-animations/step.view/index.html b/7-animation/2-css-animations/step.view/index.html index 2c8df7275..0f4f85e03 100644 --- a/7-animation/2-css-animations/step.view/index.html +++ b/7-animation/2-css-animations/step.view/index.html @@ -8,7 +8,7 @@ <body> - Click below to animate: + Haz clic a continuación para animar: <div id="digit"><div id="stripe">0123456789</div></div> diff --git a/7-animation/3-js-animation/1-animate-ball/solution.md b/7-animation/3-js-animation/1-animate-ball/solution.md index 0dc67b8bd..9a9c1d518 100644 --- a/7-animation/3-js-animation/1-animate-ball/solution.md +++ b/7-animation/3-js-animation/1-animate-ball/solution.md @@ -1,10 +1,10 @@ -To bounce we can use CSS property `top` and `position:absolute` for the ball inside the field with `position:relative`. +Para rebotar podemos usar la propiedad CSS `top` y `position:absolute` para la pelota dentro del campo con `position:relative`. -The bottom coordinate of the field is `field.clientHeight`. The CSS `top` property refers to the upper edge of the ball. So it should go from `0` till `field.clientHeight - ball.clientHeight`, that's the final lowest position of the upper edge of the ball. +La coordenada inferior del campo es `field.clientHeight`. La propiedad CSS `top` se refiere al borde superior de la bola. Por lo tanto, debe ir desde `0` hasta `field.clientHeight - ball.clientHeight`, que es la posición final más baja del borde superior de la pelota. -To get the "bouncing" effect we can use the timing function `bounce` in `easeOut` mode. +Para obtener el efecto de "rebote", podemos usar la función de sincronización `bounce` en el modo `easeOut`. -Here's the final code for the animation: +Aquí está el código final de la animación: ```js let to = field.clientHeight - ball.clientHeight; diff --git a/7-animation/3-js-animation/1-animate-ball/task.md b/7-animation/3-js-animation/1-animate-ball/task.md index 903e82384..a49eeaf14 100644 --- a/7-animation/3-js-animation/1-animate-ball/task.md +++ b/7-animation/3-js-animation/1-animate-ball/task.md @@ -2,8 +2,8 @@ importance: 5 --- -# Animate the bouncing ball +# Animar la pelota que rebota -Make a bouncing ball. Click to see how it should look: +Haz una pelota que rebote. Haz clic para ver cómo debería verse: [iframe height=250 src="solution"] diff --git a/7-animation/3-js-animation/2-animate-ball-hops/solution.md b/7-animation/3-js-animation/2-animate-ball-hops/solution.md index d47387e30..f0eb38946 100644 --- a/7-animation/3-js-animation/2-animate-ball-hops/solution.md +++ b/7-animation/3-js-animation/2-animate-ball-hops/solution.md @@ -1,18 +1,18 @@ -In the task <info:task/animate-ball> we had only one property to animate. Now we need one more: `elem.style.left`. +En la tarea <info:task/animate-ball> solo teníamos una propiedad para animar. Ahora necesitamos una más: `elem.style.left`. -The horizontal coordinate changes by another law: it does not "bounce", but gradually increases shifting the ball to the right. +La coordenada horizontal cambia por otra ley: no "rebota", sino que aumenta gradualmente desplazando la pelota hacia la derecha. -We can write one more `animate` for it. +Podemos escribir una `animate` más para ello. -As the time function we could use `linear`, but something like `makeEaseOut(quad)` looks much better. +Como función de tiempo podríamos usar `linear`, pero algo como `makeEaseOut(quad)` se ve mucho mejor. -The code: +El código: ```js let height = field.clientHeight - ball.clientHeight; let width = 100; -// animate top (bouncing) +// animate top (rebotando) animate({ duration: 2000, timing: makeEaseOut(bounce), @@ -21,7 +21,7 @@ animate({ } }); -// animate left (moving to the right) +// animate left (moviéndose a la derecha) animate({ duration: 2000, timing: makeEaseOut(quad), diff --git a/7-animation/3-js-animation/2-animate-ball-hops/task.md b/7-animation/3-js-animation/2-animate-ball-hops/task.md index fdc26c8da..b8b8069a9 100644 --- a/7-animation/3-js-animation/2-animate-ball-hops/task.md +++ b/7-animation/3-js-animation/2-animate-ball-hops/task.md @@ -2,12 +2,12 @@ importance: 5 --- -# Animate the ball bouncing to the right +# Animar la pelota rebotando hacia la derecha -Make the ball bounce to the right. Like this: +Haz que la pelota rebote hacia la derecha. Así: [iframe height=250 src="solution"] -Write the animation code. The distance to the left is `100px`. +Escribe el código de la animación. La distancia a la izquierda es `100px`. -Take the solution of the previous task <info:task/animate-ball> as the source. +Toma la solución de la tarea anterior <info:task/animate-ball> como fuente. diff --git a/7-animation/3-js-animation/article.md b/7-animation/3-js-animation/article.md index b85e91e21..55fe43ed1 100644 --- a/7-animation/3-js-animation/article.md +++ b/7-animation/3-js-animation/article.md @@ -1,63 +1,63 @@ -# JavaScript animations +# Animaciones JavaScript -JavaScript animations can handle things that CSS can't. +Las animaciones de JavaScript pueden manejar cosas que CSS no puede. -For instance, moving along a complex path, with a timing function different from Bezier curves, or an animation on a canvas. +Por ejemplo, moverse a lo largo de una ruta compleja, con una función de sincronización diferente a las curvas de Bézier, o una animación en un canvas. -## Using setInterval +## Usando setInterval -An animation can be implemented as a sequence of frames -- usually small changes to HTML/CSS properties. +Una animación se puede implementar como una secuencia de frames, generalmente pequeños cambios en las propiedades de HTML/CSS. -For instance, changing `style.left` from `0px` to `100px` moves the element. And if we increase it in `setInterval`, changing by `2px` with a tiny delay, like 50 times per second, then it looks smooth. That's the same principle as in the cinema: 24 frames per second is enough to make it look smooth. +Por ejemplo, cambiar `style.left` de `0px` a `100px` mueve el elemento. Y si lo aumentamos en `setInterval`, cambiando en `2px` con un pequeño retraso, como 50 veces por segundo, entonces se ve suave. Ese es el mismo principio que en el cine: 24 frames por segundo son suficientes para que se vea suave. -The pseudo-code can look like this: +El pseudocódigo puede verse así: ```js let timer = setInterval(function() { if (animation complete) clearInterval(timer); else increase style.left by 2px -}, 20); // change by 2px every 20ms, about 50 frames per second +}, 20); // cambiar en 2px cada 20ms, aproximadamente 50 frames por segundo ``` -More complete example of the animation: +Ejemplo más completo de la animación: ```js -let start = Date.now(); // remember start time +let start = Date.now(); // recordar la hora de inicio let timer = setInterval(function() { - // how much time passed from the start? + // ¿Cuánto tiempo pasó desde el principio? let timePassed = Date.now() - start; if (timePassed >= 2000) { - clearInterval(timer); // finish the animation after 2 seconds + clearInterval(timer); // terminar la animación después de 2 segundos return; } - // draw the animation at the moment timePassed + // dibujar la animación en el momento timePassed draw(timePassed); }, 20); -// as timePassed goes from 0 to 2000 -// left gets values from 0px to 400px +// mientras timePassed va de 0 a 2000 +// left obtiene valores de 0px a 400px function draw(timePassed) { train.style.left = timePassed / 5 + 'px'; } ``` -Click for the demo: +Haz clic para ver la demostración: [codetabs height=200 src="move"] -## Using requestAnimationFrame +## Usando requestAnimationFrame -Let's imagine we have several animations running simultaneously. +Imaginemos que tenemos varias animaciones ejecutándose simultáneamente. -If we run them separately, then even though each one has `setInterval(..., 20)`, then the browser would have to repaint much more often than every `20ms`. +Si las ejecutamos por separado, aunque cada una tenga `setInterval (..., 20)`, el navegador tendría que volver a pintar con mucha más frecuencia que cada `20ms`. -That's because they have different starting time, so "every 20ms" differs between different animations. The intervals are not aligned. So we'll have several independent runs within `20ms`. +Eso es porque tienen un tiempo de inicio diferente, por lo que "cada 20ms" difiere entre las diferentes animaciones. Los intervalos no están alineados. Así que tendremos varias ejecuciones independientes dentro de `20ms`. -In other words, this: +En otras palabras, esto: ```js setInterval(function() { @@ -67,40 +67,40 @@ setInterval(function() { }, 20) ``` -...Is lighter than three independent calls: +...Es más ligero que tres llamadas independientes: ```js -setInterval(animate1, 20); // independent animations -setInterval(animate2, 20); // in different places of the script +setInterval(animate1, 20); // animaciones independientes +setInterval(animate2, 20); // en diferentes lugares del script setInterval(animate3, 20); ``` -These several independent redraws should be grouped together, to make the redraw easier for the browser and hence load less CPU load and look smoother. +Estos varios redibujos independientes deben agruparse para facilitar el redibujado al navegador y, por lo tanto, cargar menos CPU y verse más fluido. -There's one more thing to keep in mind. Sometimes CPU is overloaded, or there are other reasons to redraw less often (like when the browser tab is hidden), so we really shouldn't run it every `20ms`. +Hay una cosa más a tener en cuenta. A veces, cuando el CPU está sobrecargado, o hay otras razones para volver a dibujar con menos frecuencia (como cuando la pestaña del navegador está oculta), no deberíamos ejecutarlo cada `20ms`. -But how do we know about that in JavaScript? There's a specification [Animation timing](https://www.w3.org/TR/animation-timing/) that provides the function `requestAnimationFrame`. It addresses all these issues and even more. +Pero, ¿cómo sabemos eso en JavaScript? Hay una especificación [Sincronización de animación](https://www.w3.org/TR/animation-timing/) que proporciona la función `requestAnimationFrame`. Aborda todos estos problemas y aún más. -The syntax: +La sintaxis: ```js let requestId = requestAnimationFrame(callback) ``` -That schedules the `callback` function to run in the closest time when the browser wants to do animation. +Eso programa la función `callback` para que se ejecute en el tiempo más cercano cuando el navegador quiera hacer una animación. -If we do changes in elements in `callback` then they will be grouped together with other `requestAnimationFrame` callbacks and with CSS animations. So there will be one geometry recalculation and repaint instead of many. +Si hacemos cambios en los elementos dentro de `callback`, entonces se agruparán con otros callbacks de `requestAnimationFrame` y con animaciones CSS. Así que habrá un recálculo y repintado de geometría en lugar de muchos. -The returned value `requestId` can be used to cancel the call: +El valor devuelto `requestId` se puede utilizar para cancelar la llamada: ```js -// cancel the scheduled execution of callback +// cancelar la ejecución programada del callback cancelAnimationFrame(requestId); ``` -The `callback` gets one argument -- the time passed from the beginning of the page load in milliseconds. This time can also be obtained by calling [performance.now()](mdn:api/Performance/now). +El `callback` obtiene un argumento: el tiempo transcurrido desde el inicio de la carga de la página en microsegundos. Este tiempo también se puede obtener llamando a [performance.now()](https://developer.mozilla.org/es/docs/Web/API/Performance/now). -Usually `callback` runs very soon, unless the CPU is overloaded or the laptop battery is almost discharged, or there's another reason. +Por lo general, el `callback` se ejecuta muy pronto, a menos que el CPU esté sobrecargado o la batería de la laptop esté casi descargada, o haya otra razón. -The code below shows the time between first 10 runs for `requestAnimationFrame`. Usually it's 10-20ms: +El siguiente código muestra el tiempo entre las primeras 10 ejecuciones de `requestAnimationFrame`. Por lo general, son 10-20ms: ```html run height=40 refresh <script> @@ -116,9 +116,9 @@ The code below shows the time between first 10 runs for `requestAnimationFrame`. </script> ``` -## Structured animation +## Animación estructurada -Now we can make a more universal animation function based on `requestAnimationFrame`: +Ahora podemos hacer una función de animación más universal basada en `requestAnimationFrame`: ```js function animate({timing, draw, duration}) { @@ -126,14 +126,14 @@ function animate({timing, draw, duration}) { let start = performance.now(); requestAnimationFrame(function animate(time) { - // timeFraction goes from 0 to 1 + // timeFraction va de 0 a 1 let timeFraction = (time - start) / duration; if (timeFraction > 1) timeFraction = 1; - // calculate the current animation state + // calcular el estado actual de la animación let progress = timing(timeFraction) - draw(progress); // draw it + draw(progress); // dibujar if (timeFraction < 1) { requestAnimationFrame(animate); @@ -143,15 +143,15 @@ function animate({timing, draw, duration}) { } ``` -Function `animate` accepts 3 parameters that essentially describes the animation: +La función `animate` acepta 3 parámetros que básicamente describen la animación: `duration` -: Total time of animation. Like, `1000`. +: Tiempo total de animación. Como: `1000`. `timing(timeFraction)` -: Timing function, like CSS-property `transition-timing-function` that gets the fraction of time that passed (`0` at start, `1` at the end) and returns the animation completion (like `y` on the Bezier curve). +: Función de sincronización, como la propiedad CSS `transition-timing-function` que obtiene la fracción de tiempo que pasó (`0` al inicio, `1` al final) y devuelve la finalización de la animación (como `y` en la curva de Bézier). - For instance, a linear function means that the animation goes on uniformly with the same speed: + Por ejemplo, una función lineal significa que la animación continúa uniformemente con la misma velocidad: ```js function linear(timeFraction) { @@ -159,33 +159,33 @@ Function `animate` accepts 3 parameters that essentially describes the animation } ``` - Its graph: + Su gráfico: ![](linear.svg) - That's just like `transition-timing-function: linear`. There are more interesting variants shown below. + Eso es como `transition-timing-function: linear`. A continuación se muestran variantes más interesantes. `draw(progress)` -: The function that takes the animation completion state and draws it. The value `progress=0` denotes the beginning animation state, and `progress=1` -- the end state. +: La función que toma el estado de finalización de la animación y la dibuja. El valor `progress=0` denota el estado inicial de la animación y `progress=1` -- el estado final. - This is that function that actually draws out the animation. + Esta es la función que realmente dibuja la animación. - It can move the element: + Puede mover el elemento: ```js function draw(progress) { train.style.left = progress + 'px'; } ``` - ...Or do anything else, we can animate anything, in any way. + ...O hacer cualquier otra cosa, podemos animar cualquier cosa, de cualquier forma. -Let's animate the element `width` from `0` to `100%` using our function. +Vamos a animar el elemento `width` de `0` a `100%` usando nuestra función. -Click on the element for the demo: +Haz clic en el elemento de la demostración: [codetabs height=60 src="width"] -The code for it: +El código para ello: ```js animate({ @@ -199,19 +199,19 @@ animate({ }); ``` -Unlike CSS animation, we can make any timing function and any drawing function here. The timing function is not limited by Bezier curves. And `draw` can go beyond properties, create new elements for like fireworks animation or something. +A diferencia de la animación CSS, aquí podemos hacer cualquier función de sincronización y cualquier función de dibujo. La función de sincronización no está limitada por las curvas de Bézier. Y `draw` puede ir más allá de las propiedades, crear nuevos elementos para la animación de fuegos artificiales o algo así. -## Timing functions +## Funciones de sincronización -We saw the simplest, linear timing function above. +Vimos arriba la función de sincronización lineal más simple. -Let's see more of them. We'll try movement animations with different timing functions to see how they work. +Veamos más de ellas. Intentaremos animaciones de movimiento con diferentes funciones de sincronización para ver cómo funcionan. -### Power of n +### Potencia de n -If we want to speed up the animation, we can use `progress` in the power `n`. +Si queremos acelerar la animación, podemos usar `progress` en la potencia `n`. -For instance, a parabolic curve: +Por ejemplo, una curva parabólica: ```js function quad(timeFraction) { @@ -219,27 +219,27 @@ function quad(timeFraction) { } ``` -The graph: +La gráfica: ![](quad.svg) -See in action (click to activate): +Velo en acción (haz clic para activar): [iframe height=40 src="quad" link] -...Or the cubic curve or even greater `n`. Increasing the power makes it speed up faster. +...O la curva cúbica o incluso mayor `n`. Aumentar la potencia hace que se acelere más rápido. -Here's the graph for `progress` in the power `5`: +Aquí está el gráfico de `progress` en la potencia `5`: ![](quint.svg) -In action: +En acción: [iframe height=40 src="quint" link] -### The arc +### El arco -Function: +Función: ```js function circ(timeFraction) { @@ -247,19 +247,19 @@ function circ(timeFraction) { } ``` -The graph: +La gráfica: ![](circ.svg) [iframe height=40 src="circ" link] -### Back: bow shooting +### Back: tiro con arco -This function does the "bow shooting". First we "pull the bowstring", and then "shoot". +Esta función realiza el "tiro con arco". Primero "tiramos de la cuerda del arco", y luego "disparamos". -Unlike previous functions, it depends on an additional parameter `x`, the "elasticity coefficient". The distance of "bowstring pulling" is defined by it. +A diferencia de las funciones anteriores, depende de un parámetro adicional `x`, el "coeficiente de elasticidad". La distancia de "tirar de la cuerda del arco" está definida por él. -The code: +El código: ```js function back(x, timeFraction) { @@ -271,15 +271,15 @@ function back(x, timeFraction) { ![](back.svg) -For animation we use it with a specific value of `x`. Example for `x = 1.5`: +Para la animación lo usamos con un valor específico de `x`. Ejemplo de `x = 1.5`: [iframe height=40 src="back" link] -### Bounce +### Rebotar -Imagine we are dropping a ball. It falls down, then bounces back a few times and stops. +Imagina que dejamos caer una pelota. Se cae, luego rebota unas cuantas veces y se detiene. -The `bounce` function does the same, but in the reverse order: "bouncing" starts immediately. It uses few special coefficients for that: +La función `bounce` hace lo mismo, pero en orden inverso: el "rebote" comienza inmediatamente. Utiliza algunos coeficientes especiales para eso: ```js function bounce(timeFraction) { @@ -291,13 +291,13 @@ function bounce(timeFraction) { } ``` -In action: +En acción: [iframe height=40 src="bounce" link] -### Elastic animation +### Animación elástica -One more "elastic" function that accepts an additional parameter `x` for the "initial range". +Una función "elástica" más que acepta un parámetro adicional `x` para el "rango inicial". ```js function elastic(x, timeFraction) { @@ -305,31 +305,31 @@ function elastic(x, timeFraction) { } ``` -**The graph for `x=1.5`:** +**La gráfica para `x=1.5`:** ![](elastic.svg) -In action for `x=1.5`: +En acción para `x=1.5`: [iframe height=40 src="elastic" link] -## Reversal: ease* +## Inversión: ease* -So we have a collection of timing functions. Their direct application is called "easeIn". +Entonces tenemos una colección de funciones de sincronización. Su aplicación directa se llama "easyIn". -Sometimes we need to show the animation in the reverse order. That's done with the "easeOut" transform. +A veces necesitamos mostrar la animación en orden inverso. Eso se hace con la transformación "easyOut". ### easeOut -In the "easeOut" mode the `timing` function is put into a wrapper `timingEaseOut`: +En el modo "easyOut", la función `de sincronización` se coloca en un wrapper `timingEaseOut`: ```js timingEaseOut(timeFraction) = 1 - timing(1 - timeFraction) ``` -In other words, we have a "transform" function `makeEaseOut` that takes a "regular" timing function and returns the wrapper around it: +En otras palabras, tenemos una función de "transformación" `makeEaseOut` que toma una función de sincronización "regular" y devuelve el wrapper envolviéndola: ```js -// accepts a timing function, returns the transformed variant +// acepta una función de sincronización, devuelve la variante transformada function makeEaseOut(timing) { return function(timeFraction) { return 1 - timing(1 - timeFraction); @@ -337,42 +337,42 @@ function makeEaseOut(timing) { } ``` -For instance, we can take the `bounce` function described above and apply it: +Por ejemplo, podemos tomar la función `bounce` descrita anteriormente y aplicarla: ```js let bounceEaseOut = makeEaseOut(bounce); ``` -Then the bounce will be not in the beginning, but at the end of the animation. Looks even better: +Entonces el rebote no estará al principio, sino al final de la animación. Se ve aún mejor: [codetabs src="bounce-easeout"] -Here we can see how the transform changes the behavior of the function: +Aquí podemos ver cómo la transformación cambia el comportamiento de la función: ![](bounce-inout.svg) -If there's an animation effect in the beginning, like bouncing -- it will be shown at the end. +Si hay un efecto de animación al principio, como rebotar, se mostrará al final. -In the graph above the <span style="color:#EE6B47">regular bounce</span> has the red color, and the <span style="color:#62C0DC">easeOut bounce</span> is blue. +En el gráfico anterior, el <span style="color:#EE6B47">rebote regular</span> tiene el color rojo y el <span style="color:#62C0DC">rebote easyOut</span> es azul. -- Regular bounce -- the object bounces at the bottom, then at the end sharply jumps to the top. -- After `easeOut` -- it first jumps to the top, then bounces there. +- Rebote regular: el objeto rebota en la parte inferior y luego, al final, salta bruscamente hacia la parte superior. +- Después de `easyOut` -- primero salta a la parte superior, luego rebota allí. ### easeInOut -We also can show the effect both in the beginning and the end of the animation. The transform is called "easeInOut". +También podemos mostrar el efecto tanto al principio como al final de la animación. La transformación se llama "easyInOut". -Given the timing function, we calculate the animation state like this: +Dada la función de tiempo, calculamos el estado de la animación de la siguiente manera: ```js -if (timeFraction <= 0.5) { // first half of the animation +if (timeFraction <= 0.5) { // primera mitad de la animación return timing(2 * timeFraction) / 2; -} else { // second half of the animation +} else { // segunda mitad de la animación return (2 - timing(2 * (1 - timeFraction))) / 2; } ``` -The wrapper code: +El código wrapper: ```js function makeEaseInOut(timing) { @@ -387,37 +387,37 @@ function makeEaseInOut(timing) { bounceEaseInOut = makeEaseInOut(bounce); ``` -In action, `bounceEaseInOut`: +En acción, `bounceEaseInOut`: [codetabs src="bounce-easeinout"] -The "easeInOut" transform joins two graphs into one: `easeIn` (regular) for the first half of the animation and `easeOut` (reversed) -- for the second part. +La transformación "easyInOut" une dos gráficos en uno: `easyIn` (regular) para la primera mitad de la animación y `easyOut` (invertido) -- para la segunda parte. -The effect is clearly seen if we compare the graphs of `easeIn`, `easeOut` and `easeInOut` of the `circ` timing function: +El efecto se ve claramente si comparamos las gráficas de `easyIn`, `easyOut` y `easyInOut` de la función de sincronización `circ`: ![](circ-ease.svg) -- <span style="color:#EE6B47">Red</span> is the regular variant of `circ` (`easeIn`). -- <span style="color:#8DB173">Green</span> -- `easeOut`. -- <span style="color:#62C0DC">Blue</span> -- `easeInOut`. +- <span style="color:#EE6B47">Rojo</span> es la variante regular de `circ` (`easeIn`). +- <span style="color:#8DB173">Verde</span> -- `easeOut`. +- <span style="color:#62C0DC">Azul</span> -- `easeInOut`. -As we can see, the graph of the first half of the animation is the scaled down `easeIn`, and the second half is the scaled down `easeOut`. As a result, the animation starts and finishes with the same effect. +Como podemos ver, el gráfico de la primera mitad de la animación es el `easyIn` reducido y la segunda mitad es el `easyOut` reducido. Como resultado, la animación comienza y termina con el mismo efecto. -## More interesting "draw" +## "Dibujar" más interesante -Instead of moving the element we can do something else. All we need is to write the proper `draw`. +En lugar de mover el elemento podemos hacer otra cosa. Todo lo que necesitamos es escribir la función `draw` adecuada. -Here's the animated "bouncing" text typing: +Aquí está la escritura de texto animada "rebotando": [codetabs src="text"] -## Summary +## Resumen -For animations that CSS can't handle well, or those that need tight control, JavaScript can help. JavaScript animations should be implemented via `requestAnimationFrame`. That built-in method allows to setup a callback function to run when the browser will be preparing a repaint. Usually that's very soon, but the exact time depends on the browser. +Para animaciones que CSS no puede manejar bien, o aquellas que necesitan un control estricto, JavaScript puede ayudar. Las animaciones de JavaScript deben implementarse a través de `requestAnimationFrame`. Ese método integrado permite configurar una función callback para que se ejecute cuando el navegador esté preparando un repintado. Por lo general, es muy pronto, pero el tiempo exacto depende del navegador. -When a page is in the background, there are no repaints at all, so the callback won't run: the animation will be suspended and won't consume resources. That's great. +Cuando una página está en segundo plano, no se repinta en absoluto, por lo que el callback no se ejecutará: la animación se suspenderá y no consumirá recursos. Eso es genial. -Here's the helper `animate` function to setup most animations: +Aquí está la función auxiliar `animate` para configurar la mayoría de las animaciones: ```js function animate({timing, draw, duration}) { @@ -425,14 +425,14 @@ function animate({timing, draw, duration}) { let start = performance.now(); requestAnimationFrame(function animate(time) { - // timeFraction goes from 0 to 1 + // timeFraction va de 0 a 1 let timeFraction = (time - start) / duration; if (timeFraction > 1) timeFraction = 1; - // calculate the current animation state + // calcular el estado actual de la animación let progress = timing(timeFraction); - draw(progress); // draw it + draw(progress); // dibujar if (timeFraction < 1) { requestAnimationFrame(animate); @@ -442,14 +442,14 @@ function animate({timing, draw, duration}) { } ``` -Options: +Opciones: -- `duration` -- the total animation time in ms. -- `timing` -- the function to calculate animation progress. Gets a time fraction from 0 to 1, returns the animation progress, usually from 0 to 1. -- `draw` -- the function to draw the animation. +- `duration` -- el tiempo total de animación en ms. +- `timing` -- la función para calcular el progreso de la animación. Obtiene una fracción de tiempo de 0 a 1, devuelve el progreso de la animación, generalmente de 0 a 1. +- `draw` -- la función para dibujar la animación. -Surely we could improve it, add more bells and whistles, but JavaScript animations are not applied on a daily basis. They are used to do something interesting and non-standard. So you'd want to add the features that you need when you need them. +Seguramente podríamos mejorarlo, agregar más campanas y silbidos, pero las animaciones de JavaScript no se aplican a diario. Se utilizan para hacer algo interesante y no estándar. Por lo tanto, querrás agregar las funciones que necesitas cuando las necesites. -JavaScript animations can use any timing function. We covered a lot of examples and transformations to make them even more versatile. Unlike CSS, we are not limited to Bezier curves here. +Las animaciones JavaScript pueden utilizar cualquier función de sincronización. Cubrimos muchos ejemplos y transformaciones para hacerlos aún más versátiles. A diferencia de CSS, aquí no estamos limitados a las curvas de Bézier. -The same is true about `draw`: we can animate anything, not just CSS properties. +Lo mismo ocurre con `draw`: podemos animar cualquier cosa, no solo propiedades CSS. diff --git a/7-animation/3-js-animation/move-raf.view/index.html b/7-animation/3-js-animation/move-raf.view/index.html index bf6e0f6e4..6523f4785 100644 --- a/7-animation/3-js-animation/move-raf.view/index.html +++ b/7-animation/3-js-animation/move-raf.view/index.html @@ -26,18 +26,18 @@ let start = performance.now(); requestAnimationFrame(function animate(time) { - // how much time passed from the start? + // ¿Cuánto tiempo pasó desde el principio? let timePassed = time - start; if (timePassed > duration) timePassed = duration; - // progress is from 0 to 1, the fraction of time that passed + // el progreso es de 0 a 1, la fracción de tiempo que pasó let progress = duration / timePassed; - // draw the animation progress + // dibuja el progreso de la animación draw(progress); - // if time is not up - schedule one more run + // si el tiempo no se acaba, programa una ejecución más if (timePassed < duration) { requestAnimationFrame(animate); } diff --git a/7-animation/3-js-animation/text.view/index.html b/7-animation/3-js-animation/text.view/index.html index 4947e4cd4..95e9b058c 100644 --- a/7-animation/3-js-animation/text.view/index.html +++ b/7-animation/3-js-animation/text.view/index.html @@ -10,13 +10,13 @@ <body> - <textarea id="textExample" rows="5" cols="60">He took his vorpal sword in hand: -Long time the manxome foe he sought— -So rested he by the Tumtum tree, -And stood awhile in thought. + <textarea id="textExample" rows="5" cols="60">Tomó su espada vorpal en mano: +Hace mucho tiempo que el enemigo manxome buscaba— +Así descansó junto al árbol Tumtum, +Y se quedó un rato en el pensamiento. </textarea> - <button onclick="animateText(textExample)">Run the animated typing!</button> + <button onclick="animateText(textExample)">¡Ejecuta la escritura animada!</button> <script> function animateText(textArea) { diff --git a/7-animation/index.md b/7-animation/index.md index 37bc847ad..a73bc19a0 100644 --- a/7-animation/index.md +++ b/7-animation/index.md @@ -1,3 +1,3 @@ -# Animation +# Animaciones -CSS and JavaScript animations. +Animaciones con CSS y JavaScript. diff --git a/8-web-components/1-webcomponents-intro/article.md b/8-web-components/1-webcomponents-intro/article.md index c3522dea9..a36fb6fa6 100644 --- a/8-web-components/1-webcomponents-intro/article.md +++ b/8-web-components/1-webcomponents-intro/article.md @@ -1,76 +1,76 @@ -# From the orbital height +# Desde la altura orbital -This section describes a set of modern standards for "web components". +En esta sección se describe un conjunto de normas modernas para los "web components". -As of now, these standards are under development. Some features are well-supported and integrated into the modern HTML/DOM standard, while others are yet in draft stage. You can try examples in any browser, Google Chrome is probably the most up to date with these features. Guess, that's because Google fellows are behind many of the related specifications. +En la actualidad, estos estándares están en desarrollo. Algunas características están bien apoyadas e integradas en el standard moderno HTML/DOM, mientras que otras están aún en fase de borrador. Puedes probar algunos ejemplos en cualquier navegador, Google Chrome es probablemente el que más actualizado esté con estas características. Suponemos que eso se debe a que los compañeros de Google están detrás de muchas de las especificaciones relacionadas. -## What's common between... +## Lo que es común entre... -The whole component idea is nothing new. It's used in many frameworks and elsewhere. +La idea del componente completo no es nada nuevo. Se usa en muchos frameworks y en otros lugares. -Before we move to implementation details, take a look at this great achievement of humanity: +Antes de pasar a los detalles de implementación, echemos un vistazo a este gran logro de la humanidad: ![](satellite.jpg) -That's the International Space Station (ISS). +Esa es la Estación Espacial Internacional (EEI). -And this is how it's made inside (approximately): +Y así es como se ha montado (aproximadamente): ![](satellite-expanded.jpg) -The International Space Station: -- Consists of many components. -- Each component, in its turn, has many smaller details inside. -- The components are very complex, much more complicated than most websites. -- Components are developed internationally, by teams from different countries, speaking different languages. +La Estación Espacial Internacional: +- Está formada por muchos componentes. +- Cada componente, a su vez, tiene muchos detalles más pequeños en su interior. +- Los componentes son muy complejos, mucho más complicados que la mayoría de los sitios web. +- Los componentes han sido desarrollados internacionalmente, por equipos de diferentes países, que hablan diferentes idiomas. -...And this thing flies, keeps humans alive in space! +...Y esta cosa vuela, ¡mantiene a los humanos vivos en el espacio! -How are such complex devices created? +¿Cómo se crean dispositivos tan complejos? -Which principles could we borrow to make our development same-level reliable and scalable? Or, at least, close to it? +¿Qué principios podríamos tomar prestados para que nuestro desarrollo sea fiable y escalable a ese nivel? ¿O, al menos, cerca de él? -## Component architecture +## Arquitectura de componentes -The well known rule for developing complex software is: don't make complex software. +La regla más conocida para desarrollar software complejo es: no hacer software complejo. -If something becomes complex -- split it into simpler parts and connect in the most obvious way. +Si algo se vuelve complejo -- divídelo en partes más simples y conéctalas de la manera más obvia. -**A good architect is the one who can make the complex simple.** +**Un buen arquitecto es el que puede hacer lo complejo simple.** -We can split user interface into visual components: each of them has own place on the page, can "do" a well-described task, and is separate from the others. +Podemos dividir la interfaz de usuario en componentes visuales: cada uno de ellos tiene su propio lugar en la página, puede "hacer" una tarea bien descrita, y está separado de los demás. -Let's take a look at a website, for example Twitter. +Echemos un vistazo a un sitio web, por ejemplo Twitter. -It naturally splits into components: +Naturalmente está divido en componentes: ![](web-components-twitter.svg) -1. Top navigation. -2. User info. -3. Follow suggestions. -4. Submit form. -5. (and also 6, 7) -- messages. +1. Navegación superior. +2. Información usuario. +3. Sugerencias de seguimiento. +4. Envío de formulario. +5. (y también 6, 7) -- mensajes. -Components may have subcomponents, e.g. messages may be parts of a higher-level "message list" component. A clickable user picture itself may be a component, and so on. +Los componentes pueden tener subcomponentes, p.ej. los mensajes pueden ser parte de un componente "lista de mensajes" de nivel superior. Una imagen de usuario en sí puede ser un componente, y así sucesivamente. -How do we decide, what is a component? That comes from intuition, experience and common sense. Usually it's a separate visual entity that we can describe in terms of what it does and how it interacts with the page. In the case above, the page has blocks, each of them plays its own role, it's logical to make these components. +¿Cómo decidimos qué es un componente? Eso viene de la intuición, la experiencia y el sentido común. Normalmente es una entidad visual separada que podemos describir en términos de lo que hace y cómo interactúa con la página. En el caso anterior, la página tiene bloques, cada uno de ellos juega su propio papel, es lógico crear esos componentes. -A component has: -- Its own JavaScript class. -- DOM structure, managed solely by its class, outside code doesn't access it ("encapsulation" principle). -- CSS styles, applied to the component. -- API: events, class methods etc, to interact with other components. +Un componente tiene: +- Su propia clase de JavaScript. +- La estructura DOM, gestionada únicamente por su clase, el código externo no accede a ella (principio de "encapsulación"). +- Estilos CSS, aplicados al componente. +- API: eventos, métodos de clase etc, para interactuar con otros componentes. -Once again, the whole "component" thing is nothing special. +Una vez más, todo el asunto del "componente" no es nada especial. -There exist many frameworks and development methodologies to build them, each with its own bells and whistles. Usually, special CSS classes and conventions are used to provide "component feel" -- CSS scoping and DOM encapsulation. +Existen muchos frameworks y metodologías de desarrollos para construirlos, cada uno con sus propias características y reglas. Normalmente, se utilizan clases y convenciones CSS para proporcionar la "sensación de componente" -- alcance de CSS y encapsulación de DOM. -"Web components" provide built-in browser capabilities for that, so we don't have to emulate them any more. +"Web components" proporcionan capacidades de navegación incorporadas para eso, así que ya no tenemos que emularlos. -- [Custom elements](https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements) -- to define custom HTML elements. -- [Shadow DOM](https://dom.spec.whatwg.org/#shadow-trees) -- to create an internal DOM for the component, hidden from the others. -- [CSS Scoping](https://drafts.csswg.org/css-scoping/) -- to declare styles that only apply inside the Shadow DOM of the component. -- [Event retargeting](https://dom.spec.whatwg.org/#retarget) and other minor stuff to make custom components better fit the development. +- [Custom elements](https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements) -- para definir elementos HTML personalizados. +- [Shadow DOM](https://dom.spec.whatwg.org/#shadow-trees) -- para crear un DOM interno para el componente, oculto a los demás componentes. +- [CSS Scoping](https://drafts.csswg.org/css-scoping/) -- para declarar estilos que sólo se aplican dentro del Shadow DOM del componente. +- [Event retargeting](https://dom.spec.whatwg.org/#retarget) y otras cosas menores para hacer que los componentes se ajusten mejor al desarrollo. -In the next chapter we'll go into details of "Custom Elements" -- the fundamental and well-supported feature of web components, good on its own. +En el próximo capítulo entraremos en detalles en los "Custom Elements" -- la característica fundamental y bien soportada de los componentes web, buena por sí misma. diff --git a/8-web-components/2-custom-elements/1-live-timer/solution.md b/8-web-components/2-custom-elements/1-live-timer/solution.md index a9eacc880..478712c90 100644 --- a/8-web-components/2-custom-elements/1-live-timer/solution.md +++ b/8-web-components/2-custom-elements/1-live-timer/solution.md @@ -1,4 +1,4 @@ -Please note: -1. We clear `setInterval` timer when the element is removed from the document. That's important, otherwise it continues ticking even if not needed any more. And the browser can't clear the memory from this element and referenced by it. -2. We can access current date as `elem.date` property. All class methods and properties are naturally element methods and properties. +Por favor ten en cuenta: +1. Borramos el temporizador `setInterval` cuando el elemento es quitado del documento. Esto es importante, de otro modo continuará ejecutando aunque no se lo necesite más, y el navegador no puede liberar la memoria asignada a este elemento. +2. Podemos acceder a la fecha actual con la propiedad `elem.date`. Todos los métodos y propiedades de clase son naturalmente métodos y propiedades del elemento. diff --git a/8-web-components/2-custom-elements/1-live-timer/solution.view/live-timer.js b/8-web-components/2-custom-elements/1-live-timer/solution.view/live-timer.js index a53d72e00..4b5e1e2ab 100644 --- a/8-web-components/2-custom-elements/1-live-timer/solution.view/live-timer.js +++ b/8-web-components/2-custom-elements/1-live-timer/solution.view/live-timer.js @@ -24,7 +24,7 @@ class LiveTimer extends HTMLElement { } disconnectedCallback() { - clearInterval(this.timer); // important to let the element be garbage-collected + clearInterval(this.timer); // importante para hacer el elemento disponible al recolector de basura } } diff --git a/8-web-components/2-custom-elements/1-live-timer/source.view/index.html b/8-web-components/2-custom-elements/1-live-timer/source.view/index.html index 878120241..1bb4be8e7 100644 --- a/8-web-components/2-custom-elements/1-live-timer/source.view/index.html +++ b/8-web-components/2-custom-elements/1-live-timer/source.view/index.html @@ -1,8 +1,8 @@ <!doctype html> -<!-- don't modify this --> +<!-- No modificar esto --> <script src="time-formatted.js"></script> -<!-- your code here: --> +<!-- tu código aquí: --> <script src="live-timer.js"></script> <live-timer id="elem"></live-timer> diff --git a/8-web-components/2-custom-elements/1-live-timer/source.view/live-timer.js b/8-web-components/2-custom-elements/1-live-timer/source.view/live-timer.js index e2fe2b69f..6e46b8bc4 100644 --- a/8-web-components/2-custom-elements/1-live-timer/source.view/live-timer.js +++ b/8-web-components/2-custom-elements/1-live-timer/source.view/live-timer.js @@ -1,6 +1,6 @@ class LiveTimer extends HTMLElement { - /* your code here */ + /* tu código aquí */ } diff --git a/8-web-components/2-custom-elements/1-live-timer/task.md b/8-web-components/2-custom-elements/1-live-timer/task.md index 1feb7490a..7748a45ea 100644 --- a/8-web-components/2-custom-elements/1-live-timer/task.md +++ b/8-web-components/2-custom-elements/1-live-timer/task.md @@ -1,14 +1,14 @@ -# Live timer element +# Elemento reloj dinámico -We already have `<time-formatted>` element to show a nicely formatted time. +Ya tenemos un elemento `<time-formatted>` para mostrar la hora agradablemente formateada. -Create `<live-timer>` element to show the current time: -1. It should use `<time-formatted>` internally, not duplicate its functionality. -2. Ticks (updates) every second. -3. For every tick, a custom event named `tick` should be generated, with the current date in `event.detail` (see chapter <info:dispatch-events>). +Crea el elemento `<live-timer>` para mostrar la hora actual: +1. Internamente debe usar `<time-formatted>`, no duplicar su funcionalidad. +2. Aactualiza (¡tic!) cada segundo. +3. Por cada tic, se debe generar un evento personalizado llamado `tick` con la fecha actual en `event.detail` (ver artículo <info:dispatch-events>). -Usage: +Uso: ```html <live-timer id="elem"></live-timer> diff --git a/8-web-components/2-custom-elements/article.md b/8-web-components/2-custom-elements/article.md index a84ed1192..5a01a0ad1 100644 --- a/8-web-components/2-custom-elements/article.md +++ b/8-web-components/2-custom-elements/article.md @@ -1,81 +1,81 @@ -# Custom elements +# Elementos personalizados -We can create custom HTML elements, described by our class, with its own methods and properties, events and so on. +Podemos crear elementos HTML personalizados con nuestras propias clases; con sus propios métodos, propiedades, eventos y demás. -Once a custom element is defined, we can use it on par with built-in HTML elements. +Una vez que definimos el elemento personalizado, podemos usarlo a la par de elementos HTML nativos. -That's great, as HTML dictionary is rich, but not infinite. There are no `<easy-tabs>`, `<sliding-carousel>`, `<beautiful-upload>`... Just think of any other tag we might need. +Esto es grandioso, porque el el diccionario HTML es rico, pero no infinito. No hay `<aletas-faciles>`, `<gira-carrusel>`, `<bella-descarga>`... Solo piensa en cualquier otra etiqueta que puedas necesitar. -We can define them with a special class, and then use as if they were always a part of HTML. +Podemos definirlos con una clase especial, y luego usarlos como si siempre hubieran sido parte del HTML. -There are two kinds of custom elements: +Hay dos clases de elementos personalizados: -1. **Autonomous custom elements** -- "all-new" elements, extending the abstract `HTMLElement` class. -2. **Customized built-in elements** -- extending built-in elements, like a customized button, based on `HTMLButtonElement` etc. +1. **Elementos personalizados autónomos** -- son elementos "todo-nuevo", extensiones de la clase abstracta `HTMLElement`. +2. **Elementos nativos personalizados** -- son extensiones de elementos nativos, por ejemplo un botón personalizado basado en `HTMLButtonElement`. -First we'll cover autonomous elements, and then move to customized built-in ones. +Primero cubriremos los elementos autónomos, luego pasaremos a la personalización de elementos nativos. -To create a custom element, we need to tell the browser several details about it: how to show it, what to do when the element is added or removed to page, etc. +Para crear un elemento personalizado, necesitamos decirle al navegador varios detalles acerca de él: cómo mostrarlo, qué hacer cuando el elemento es agregado o quitado de la página, etc. -That's done by making a class with special methods. That's easy, as there are only few methods, and all of them are optional. +Eso se logra creando una clase con métodos especiales. Es fácil, son unos pocos métodos y todos ellos son opcionales. -Here's a sketch with the full list: +Este es el esquema con la lista completa: ```js class MyElement extends HTMLElement { constructor() { super(); - // element created + // elemento creado } connectedCallback() { - // browser calls this method when the element is added to the document - // (can be called many times if an element is repeatedly added/removed) + // el navegador llama a este método cuando el elemento es agregado al documento + // (puede ser llamado varias veces si un elemento es agregado y quitado repetidamente) } disconnectedCallback() { - // browser calls this method when the element is removed from the document - // (can be called many times if an element is repeatedly added/removed) + // el navegador llama a este método cuando el elemento es quitado del documento + // (puede ser llamado varias veces si un elemento es agregado y quitado repetidamente) } static get observedAttributes() { - return [/* array of attribute names to monitor for changes */]; + return [/* array de nombres de atributos a los que queremos monitorear por cambios */]; } attributeChangedCallback(name, oldValue, newValue) { - // called when one of attributes listed above is modified + // es llamado cuando uno de los atributos listados arriba es modificado } adoptedCallback() { - // called when the element is moved to a new document - // (happens in document.adoptNode, very rarely used) + // es llamado cuando el elemento es movido a un nuevo documento + // (ocurre en document.adoptNode, muy raramente usado) } - // there can be other element methods and properties + // puede haber otros métodos y propiedades de elemento } ``` -After that, we need to register the element: +Después de ello, necesitamos registrar el elemento: ```js -// let the browser know that <my-element> is served by our new class +// hacer saber al navegador que <my-element> es servido por nuestra nueva clase customElements.define("my-element", MyElement); ``` -Now for any HTML elements with tag `<my-element>`, an instance of `MyElement` is created, and the aforementioned methods are called. We also can `document.createElement('my-element')` in JavaScript. +A partir de ello, para cada elemento HTML con la etiqueta `<my-element>` se crea una instancia de `MyElement` y se llaman los métodos mencionados. También podemos insertarlo con JavaScript: `document.createElement('my-element')`. -```smart header="Custom element name must contain a hyphen `-`" -Custom element name must have a hyphen `-`, e.g. `my-element` and `super-button` are valid names, but `myelement` is not. +```smart header="Los nombres de los elementos personalizados deben incluir un guion `-`" +Los elemento personalizados deben incluir un guion corto `-` en su nombre. Por ejemplo, `my-element` y `super-button` son nombres válidos, pero `myelement` no lo es. -That's to ensure that there are no name conflicts between built-in and custom HTML elements. +Esto se hace para asegurar que no haya conflicto de nombres entre los elementos nativos y los personalizados. ``` -## Example: "time-formatted" +## Ejemplo: "time-formatted" -For example, there already exists `<time>` element in HTML, for date/time. But it doesn't do any formatting by itself. +Ya existe un elemento `<time>` en HTML para presentar fecha y hora, pero este no hace ningún formateo por sí mismo. -Let's create `<time-formatted>` element that displays the time in a nice, language-aware format: +Construyamos el elemento `<time-formatted>` que muestre la hora en un bonito formato y reconozca la configuración de lengua local: ```html run height=50 autorun="no-epub" @@ -115,43 +115,43 @@ customElements.define("time-formatted", TimeFormatted); // (2) ></time-formatted> ``` -1. The class has only one method `connectedCallback()` -- the browser calls it when `<time-formatted>` element is added to page (or when HTML parser detects it), and it uses the built-in [Intl.DateTimeFormat](mdn:/JavaScript/Reference/Global_Objects/DateTimeFormat) data formatter, well-supported across the browsers, to show a nicely formatted time. -2. We need to register our new element by `customElements.define(tag, class)`. -3. And then we can use it everywhere. +1. La clase tiene un solo método, `connectedCallback()`, que es llamado por el navegador cuando se agrega el elemento `<time-formatted>` a la página o cuando el analizador HTML lo detecta. Este método usa el formateador de datos nativo [Intl.DateTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat), bien soportado por los navegadores, para mostrar una agradable hora formateada. +2. Necesitamos registrar nuestro nuevo elemento con `customElements.define(tag, class)`. +3. Y podremos usarlo por doquier. -```smart header="Custom elements upgrade" -If the browser encounters any `<time-formatted>` elements before `customElements.define`, that's not an error. But the element is yet unknown, just like any non-standard tag. +```smart header="Actualización de elementos personalizados" +Si el navegador encuentra algún `<time-formatted>` antes de `customElements.define`, no es un error. Pero el elemento es todavía desconocido, como cualquier etiqueta no estándar. -Such "undefined" elements can be styled with CSS selector `:not(:defined)`. +Tal elemento "undefined" puede ser estilizado con el selector CSS `:not(:defined)`. -When `customElement.define` is called, they are "upgraded": a new instance of `TimeFormatted` -is created for each, and `connectedCallback` is called. They become `:defined`. +Una vez que `customElement.define` es llamado, estos elementos son "actualizados": para cada elemento, una nueva instancia de `TimeFormatted` +es creada y `connectedCallback` es llamado. Se vuelven `:defined`. -To get the information about custom elements, there are methods: -- `customElements.get(name)` -- returns the class for a custom element with the given `name`, -- `customElements.whenDefined(name)` -- returns a promise that resolves (without value) when a custom element with the given `name` becomes defined. +Para obtener información acerca de los elementos personalizados, tenemos los métodos: +- `customElements.get(name)` -- devuelve la clase del elemento personalizado con el `name` dado, +- `customElements.whenDefined(name)` -- devuelve una promesa que se resuelve (sin valor) cuando un elemento personalizado con el `name` dado se vuelve `defined`. ``` -```smart header="Rendering in `connectedCallback`, not in `constructor`" -In the example above, element content is rendered (created) in `connectedCallback`. +```smart header="Renderizado en `connectedCallback`, no en el `constructor`" +En el ejemplo de arriba, el contenido del elemento es renderizado (construido) en `connectedCallback`. -Why not in the `constructor`? +¿Por qué no en el `constructor`? -The reason is simple: when `constructor` is called, it's yet too early. The element is created, but the browser did not yet process/assign attributes at this stage: calls to `getAttribute` would return `null`. So we can't really render there. +La razón es simple: cuando el `constructor` es llamado, es aún demasiado pronto. El elemento es creado, pero el navegador aún no procesó ni asignó atributos en este estado, entonces las llamadas a `getAttribute` devolverían `null`. Así que no podemos renderizar ahora. -Besides, if you think about it, that's better performance-wise -- to delay the work until it's really needed. +Por otra parte, si lo piensas, es más adecuado en términos de performance: demorar el trabajo hasta que realmente se lo necesite. -The `connectedCallback` triggers when the element is added to the document. Not just appended to another element as a child, but actually becomes a part of the page. So we can build detached DOM, create elements and prepare them for later use. They will only be actually rendered when they make it into the page. +El `connectedCallback` se dispara cuando el elemento es agregado al documento. No apenas agregado a otro elemento como hijo, sino cuando realmente se vuelve parte de la página. Así podemos construir un DOM separado, crear elementos y prepararlos para uso futuro. Ellos serán realmente renderizados una vez que estén dentro de la página. ``` -## Observing attributes +## Observando atributos -In the current implementation of `<time-formatted>`, after the element is rendered, further attribute changes don't have any effect. That's strange for an HTML element. Usually, when we change an attribute, like `a.href`, we expect the change to be immediately visible. So let's fix this. +En la implementación actual de `<time-formatted>`, después de que el elemento fue renderizado, cambios posteriores en sus atributos no tendrán ningún efecto. Eso es extraño para un elemento HTML, porque cuando cambiamos un atributo (como en `a.href`) esperamos que dicho cambio sea visible de inmediato. Corrijamos esto. -We can observe attributes by providing their list in `observedAttributes()` static getter. For such attributes, `attributeChangedCallback` is called when they are modified. It doesn't trigger for other, unlisted attributes (that's for performance reasons). +Podemos observar atributos suministrando la lista de ellos al getter estático `observedAttributes()`. Cuando esos atributos son modificados, se dispara `attributeChangedCallback`. No se dispara para los atributos no incluidos en la lista, por razones de performance. -Here's a new `<time-formatted>`, that auto-updates when attributes change: +A continuación, el nuevo `<time-formatted>` que se actualiza cuando los atributos cambian: ```html run autorun="no-epub" height=50 <script> @@ -208,19 +208,19 @@ setInterval(() => elem.setAttribute('datetime', new Date()), 1000); // (5) </script> ``` -1. The rendering logic is moved to `render()` helper method. -2. We call it once when the element is inserted into page. -3. For a change of an attribute, listed in `observedAttributes()`, `attributeChangedCallback` triggers. -4. ...and re-renders the element. -5. At the end, we can easily make a live timer. +1. La lógica de renderizado fue movida al método ayudante `render()`. +2. Lo llamamos una vez cuando el elemento es insertado en la página. +3. Al cambiar un atributo listado en `observedAttributes()`, se dispara `attributeChangedCallback`. +4. ...y se re-renderiza el elemento. +5. Como resultado, ahora podemos crear un reloj dinámico con facilidad. -## Rendering order +## Orden de renderizado -When HTML parser builds the DOM, elements are processed one after another, parents before children. E.g. if we have `<outer><inner></inner></outer>`, then `<outer>` element is created and connected to DOM first, and then `<inner>`. +Cuando el "parser" construye el DOM, los elementos son procesados uno tras otro, padres antes que hijos. Por ejemplo si tenemos `<outer><inner></inner></outer>`, el elemento `<outer>` es creado y conectado al DOM primero, y luego `<inner>`. -That leads to important consequences for custom elements. +Esto lleva a consecuencias importantes para los elementos personalizados. -For example, if a custom element tries to access `innerHTML` in `connectedCallback`, it gets nothing: +Por ejemplo, si un elemento personalizado trata de acceder a `innerHTML` en `connectedCallback`, no obtiene nada: ```html run height=40 <script> @@ -228,7 +228,7 @@ customElements.define('user-info', class extends HTMLElement { connectedCallback() { *!* - alert(this.innerHTML); // empty (*) + alert(this.innerHTML); // vacío (*) */!* } @@ -240,15 +240,15 @@ customElements.define('user-info', class extends HTMLElement { */!* ``` -If you run it, the `alert` is empty. +Si lo ejecutas, el `alert` estará vacío. -That's exactly because there are no children on that stage, the DOM is unfinished. HTML parser connected the custom element `<user-info>`, and is going to proceed to its children, but just didn't yet. +Esto es porque no hay hijos en aquel estadio, pues el DOM no está finalizado. Se conectó el elemento personalizado `<user-info>` y está por proceder con sus hijos, pero no lo hizo aún. -If we'd like to pass information to custom element, we can use attributes. They are available immediately. +Si queremos pasar información al elemento personalizado, podemos usar atributos. Estos están disponibles inmediatamente. -Or, if we really need the children, we can defer access to them with zero-delay `setTimeout`. +O, si realmente necesitamos acceder a los hijos, podemos demorar el acceso a ellos con un `setTimeout` de tiempo cero. -This works: +Esto funciona: ```html run height=40 <script> @@ -268,13 +268,13 @@ customElements.define('user-info', class extends HTMLElement { */!* ``` -Now the `alert` in line `(*)` shows "John", as we run it asynchronously, after the HTML parsing is complete. We can process children if needed and finish the initialization. +Ahora el `alert` en la línea `(*)` muestra "John" porque lo corremos asincrónicamente, después de que el armado HTML está completo. Podemos procesar los hijos si lo necesitamos y finalizar la inicialización. -On the other hand, this solution is also not perfect. If nested custom elements also use `setTimeout` to initialize themselves, then they queue up: the outer `setTimeout` triggers first, and then the inner one. +Por otro lado, la solución tampoco es perfecta. Si los elementos anidados también usan `setTimeout` para inicializarse, entonces van a la cola: el `setTimeout` externo se dispara primero y luego el interno. -So the outer element finishes the initialization before the inner one. +Como consecuencia, el elemento externo termina la inicialización antes que el interno. -Let's demonstrate that on example: +Demostrémoslo con un ejemplo: ```html run height=0 <script> @@ -293,50 +293,50 @@ customElements.define('user-info', class extends HTMLElement { */!* ``` -Output order: +Orden de salida: -1. outer connected. -2. inner connected. -3. outer initialized. -4. inner initialized. +1. outer conectado. +2. inner conectado. +3. outer inicializado. +4. inner inicializado. -We can clearly see that the outer element finishes initialization `(3)` before the inner one `(4)`. +Claramente vemos que el elemento finaliza su inicialización `(3)` antes que el interno `(4)`. -There's no built-in callback that triggers after nested elements are ready. If needed, we can implement such thing on our own. For instance, inner elements can dispatch events like `initialized`, and outer ones can listen and react on them. +No existe un callback nativo que se dispare después de que los elementos anidados estén listos. Si es necesario, podemos implementarlo nosotros mismos. Por ejemplo, los elementos internos pueden disparar eventos como `initialized`, y los externos pueden escucharlos para reaccionar a ellos. -## Customized built-in elements +## Elementos nativos personalizados -New elements that we create, such as `<time-formatted>`, don't have any associated semantics. They are unknown to search engines, and accessibility devices can't handle them. +Los elementos nuevos que creamos, tales como `<time-formatted>`, no tienen ninguna semántica asociada. Para los motores de búsqueda son desconocidos, y los dispositivos de accesibilidad tampoco pueden manejarlos. -But such things can be important. E.g, a search engine would be interested to know that we actually show a time. And if we're making a special kind of button, why not reuse the existing `<button>` functionality? +Pero estas cosas son importantes. Por ejemplo, un motor de búsqueda podría estar interesado en saber que realmente mostramos la hora. y si hacemos una clase especial de botón, ¿por qué no reusar la funcionalidad ya existente de `<button>`? -We can extend and customize built-in HTML elements by inheriting from their classes. +Podemos extender y personalizar elementos HTML nativos, heredando desde sus clases. -For example, buttons are instances of `HTMLButtonElement`, let's build upon it. +Por ejemplo, los botones son instancias de `HTMLButtonElement`, construyamos sobre ello. -1. Extend `HTMLButtonElement` with our class: +1. Extender `HTMLButtonElement` con nuestra clase: ```js - class HelloButton extends HTMLButtonElement { /* custom element methods */ } + class HelloButton extends HTMLButtonElement { /* métodos de elemento personalizado */ } ``` -2. Provide the third argument to `customElements.define`, that specifies the tag: +2. Ponemos el tercer argumento de `customElements.define`, el cual especifica la etiqueta: ```js customElements.define('hello-button', HelloButton, *!*{extends: 'button'}*/!*); ``` - There may be different tags that share the same DOM-class, that's why specifying `extends` is needed. + Puede haber diferentes etiquetas que comparten la misma clase DOM, por eso se necesita especificar `extends`. -3. At the end, to use our custom element, insert a regular `<button>` tag, but add `is="hello-button"` to it: +3. Por último, para usar nuestro elemento personalizado, insertamos una etiqueta común `<button>`, pero le agregamos `is="hello-button"`: ```html <button is="hello-button">...</button> ``` -Here's a full example: +El ejemplo completo: ```html run autorun="no-epub" <script> -// The button that says "hello" on click +// El botón que dice "hello" al hacer clic class HelloButton extends HTMLButtonElement { *!* constructor() { @@ -360,20 +360,20 @@ customElements.define('hello-button', HelloButton, {extends: 'button'}); */!* ``` -Our new button extends the built-in one. So it keeps the same styles and standard features like `disabled` attribute. +Nuestro nuevo botón extiende el 'button' nativo. Así mantenemos los mismos estilos y características estándar, como por ejemplo el atributo `disabled`. -## References +## Referencias -- HTML Living Standard: <https://html.spec.whatwg.org/#custom-elements>. -- Compatiblity: <https://caniuse.com/#feat=custom-elementsv1>. +- HTML estándar vivo: <https://html.spec.whatwg.org/#custom-elements>. +- Compatibilidad: <https://caniuse.com/#feat=custom-elementsv1>. -## Summary +## Resumen -Custom elements can be of two types: +Los elementos personalizados pueden ser de dos tipos: -1. "Autonomous" -- new tags, extending `HTMLElement`. +1. "Autónomos" -- son etiquetas nuevas, se crean extendiendo `HTMLElement`. - Definition scheme: + Esquema de definición: ```js class MyElement extends HTMLElement { @@ -388,13 +388,13 @@ Custom elements can be of two types: /* <my-element> */ ``` -2. "Customized built-in elements" -- extensions of existing elements. +2. "Elementos nativos personalizados" -- se crean extendiendo elementos ya existentes. - Requires one more `.define` argument, and `is="..."` in HTML: + Requiere un argumento más `.define`, y `is="..."` en HTML: ```js class MyButton extends HTMLButtonElement { /*...*/ } customElements.define('my-button', MyElement, {extends: 'button'}); /* <button is="my-button"> */ ``` -Custom elements are well-supported among browsers. There's a polyfill <https://github.com/webcomponents/polyfills/tree/master/packages/webcomponentsjs>. +Los elementos personalizados tienen muy buen soporte entre los navegadores. Existe un polyfill <https://github.com/webcomponents/polyfills/tree/master/packages/webcomponentsjs>. diff --git a/8-web-components/3-shadow-dom/article.md b/8-web-components/3-shadow-dom/article.md index 92614f777..1b9687fb2 100644 --- a/8-web-components/3-shadow-dom/article.md +++ b/8-web-components/3-shadow-dom/article.md @@ -1,32 +1,32 @@ # Shadow DOM -Shadow DOM serves for encapsulation. It allows a component to have its very own "shadow" DOM tree, that can't be accidentally accessed from the main document, may have local style rules, and more. +Shadow DOM sirve para el encapsulamiento. Le permite a un componente tener su propio árbol DOM oculto, que no puede ser accedido por accidente desde el documento principal, puede tener reglas de estilo locales, y más. -## Built-in shadow DOM +## Shadow DOM incorporado -Did you ever think how complex browser controls are created and styled? +¿Alguna vez pensó cómo los controles complejos del navegador se crean y se les aplica estilo? -Such as `<input type="range">`: +Tales como `<input type="range">`: <p> <input type="range"> </p> -The browser uses DOM/CSS internally to draw them. That DOM structure is normally hidden from us, but we can see it in developer tools. E.g. in Chrome, we need to enable in Dev Tools "Show user agent shadow DOM" option. +El navegador usa DOM/CSS internamente para dibujarlos. Esa estructura DOM normalmente está oculta para nosotros, pero podemos verla con herramientas de desarrollo. Por ejemplo, en Chrome, necesitamos habilitar la opción "Show user agent shadow DOM" en las herramientas de desarrollo. -Then `<input type="range">` looks like this: +Entonces `<input type="range">` se ve algo así: ![](shadow-dom-range.png) -What you see under `#shadow-root` is called "shadow DOM". +Lo que ves bajo `#shadow-root` se llama "shadow DOM". -We can't get built-in shadow DOM elements by regular JavaScript calls or selectors. These are not regular children, but a powerful encapsulation technique. +No podemos obtener los elementos de shadow DOM incorporados con llamadas normales a JavaScript o selectores. Estos no son hijos normales sino una poderosa técnica de encapsulamiento. -In the example above, we can see a useful attribute `pseudo`. It's non-standard, exists for historical reasons. We can use it style subelements with CSS, like this: +En el ejemplo de abajo podemos ver un útil atributo `pseudo`. No es estándar, existe por razones históricas. Podemos usarlo para aplicar estilo a subelementos con CSS como aquí: ```html run autorun <style> -/* make the slider track red */ +/* hace el control deslizable rojo */ input::-webkit-slider-runnable-track { background: red; } @@ -35,22 +35,22 @@ input::-webkit-slider-runnable-track { <input type="range"> ``` -Once again, `pseudo` is a non-standard attribute. Chronologically, browsers first started to experiment with internal DOM structures to implement controls, and then, after time, shadow DOM was standardized to allow us, developers, to do the similar thing. +De nuevo: `pseudo` no es un atributo estándar. Cronológicamente, los navegadores primero comenzaron a experimentar con estructuras DOM internas para implementar controles, y luego, con el tiempo, fue estandarizado shadow DOM que nos permite, a nosotros desarrolladores, hacer algo similar. -Further on, we'll use the modern shadow DOM standard, covered by [DOM spec](https://dom.spec.whatwg.org/#shadow-trees) and other related specifications. +Seguidamente usaremos el moderno estándar shadow DOM cubierto en la [especificación DOM](https://dom.spec.whatwg.org/#shadow-trees). -## Shadow tree +## Shadow tree (árbol oculto) -A DOM element can have two types of DOM subtrees: +Un elemento DOM puede tener dos tipos de subárboles DOM: -1. Light tree -- a regular DOM subtree, made of HTML children. All subtrees that we've seen in previous chapters were "light". -2. Shadow tree -- a hidden DOM subtree, not reflected in HTML, hidden from prying eyes. +1. Light tree -- un subárbol normal, hecho de hijos HTML. Todos los subárboles vistos en capítulos previos eran "light". +2. Shadow tree -- un subárbol shadow DOM, no reflejado en HTML, oculto a la vista. -If an element has both, then the browser renders only the shadow tree. But we can setup a kind of composition between shadow and light trees as well. We'll see the details later in the chapter <info:slots-composition>. +Si un elemento tiene ambos, el navegador solamente construye el árbol shadow. Pero también podemos establecer un tipo de composición entre árboles shadow y light. Veremos los detalles en el capítulo <info:slots-composition>. -Shadow tree can be used in Custom Elements to hide component internals and apply component-local styles. +El árbol shadow puede ser usado en elementos personalizados para ocultar los componentes internos y aplicarles estilos locales. -For example, this `<show-hello>` element hides its internal DOM in shadow tree: +Por ejemplo, este elemento `<show-hello>` oculta su DOM interno en un shadow tree: ```html run autorun height=60 <script> @@ -67,46 +67,46 @@ customElements.define('show-hello', class extends HTMLElement { <show-hello name="John"></show-hello> ``` -That's how the resulting DOM looks in Chrome dev tools, all the content is under "#shadow-root": +Así es como el DOM resultante se ve en las herramientas de desarrollador de Chrome, todo el contenido está bajo "#shadow-root": ![](shadow-dom-say-hello.png) -First, the call to `elem.attachShadow({mode: …})` creates a shadow tree. +Primero, el llamado a `elem.attachShadow({mode: …})` crea un árbol shadow. -There are two limitations: -1. We can create only one shadow root per element. -2. The `elem` must be either a custom element, or one of: "article", "aside", "blockquote", "body", "div", "footer", "h1..h6", "header", "main" "nav", "p", "section", or "span". Other elements, like `<img>`, can't host shadow tree. +Hay dos limitaciones: +1. Podemos crear solamente una raíz shadow por elemento. +2. `elem` debe ser: o bien un elemento personalizado, o uno de: "article", "aside", "blockquote", "body", "div", "footer", "h1..h6", "header", "main" "nav", "p", "section", o "span". Otros elementos, como `<img>`, no pueden contener un árbol shadow. -The `mode` option sets the encapsulation level. It must have any of two values: -- `"open"` -- the shadow root is available as `elem.shadowRoot`. +La opción `mode` establece el nivel de encapsulamiento. Debe tener uno de estos dos valores: +- `"open"` -- Abierto: la raíz shadow está disponible como `elem.shadowRoot`. - Any code is able to access the shadow tree of `elem`. -- `"closed"` -- `elem.shadowRoot` is always `null`. + Todo código puede acceder el árbol shadow de `elem`. +- `"closed"` -- Cerrado: `elem.shadowRoot` siempre es `null`. - We can only access the shadow DOM by the reference returned by `attachShadow` (and probably hidden inside a class). Browser-native shadow trees, such as `<input type="range">`, are closed. There's no way to access them. + Solamente podemos acceder al shadow DOM por medio de la referencia devuelta por `attachShadow` (y probablemente oculta dentro de un class). Árboles shadow nativos del navegador, tales como `<input type="range">`, son "closed". No hay forma de accederlos. -The [shadow root](https://dom.spec.whatwg.org/#shadowroot), returned by `attachShadow`, is like an element: we can use `innerHTML` or DOM methods, such as `append`, to populate it. +La raíz [shadow root](https://dom.spec.whatwg.org/#shadowroot), devuelta por `attachShadow`, es como un elemento: podemos usar `innerHTML` o métodos DOM tales como `append` para llenarlo. -The element with a shadow root is called a "shadow tree host", and is available as the shadow root `host` property: +El elemento con una raíz shadow es llamado "shadow tree host" (anfitrión de árbol shadow), y está disponible como la propiedad `host` de shadow root: ```js -// assuming {mode: "open"}, otherwise elem.shadowRoot is null +// asumimos {mode: "open"}, de otra forma elem.shadowRoot sería null alert(elem.shadowRoot.host === elem); // true ``` -## Encapsulation +## Encapsulamiento -Shadow DOM is strongly delimited from the main document: +Shadow DOM está fuertemente delimitado del documento principal "main document": -1. Shadow DOM elements are not visible to `querySelector` from the light DOM. In particular, Shadow DOM elements may have ids that conflict with those in the light DOM. They must be unique only within the shadow tree. -2. Shadow DOM has own stylesheets. Style rules from the outer DOM don't get applied. +1. Los elementos Shadow DOM no son visibles para `querySelector` desde el DOM visible (light DOM). En particular, los elementos Shadow DOM pueden tener ids en conflicto con aquellos en el DOM visible. Estos deben ser únicos solamente dentro del árbol shadow. +2. El Shadow DOM tiene stylesheets propios. Las reglas de estilo del exterior DOM no se le aplican. -For example: +Por ejemplo: ```html run untrusted height=40 <style> *!* - /* document style won't apply to the shadow tree inside #elem (1) */ + /* document style no será aplicado al árbol shadow dentro de #elem (1) */ */!* p { color: red; } </style> @@ -116,7 +116,7 @@ For example: <script> elem.attachShadow({mode: 'open'}); *!* - // shadow tree has its own style (2) + // el árbol shadow tiene su propio style (2) */!* elem.shadowRoot.innerHTML = ` <style> p { font-weight: bold; } </style> @@ -124,34 +124,34 @@ For example: `; *!* - // <p> is only visible from queries inside the shadow tree (3) + // <p> solo es visible en consultas "query" dentro del árbol shadow (3) */!* alert(document.querySelectorAll('p').length); // 0 alert(elem.shadowRoot.querySelectorAll('p').length); // 1 </script> ``` -1. The style from the document does not affect the shadow tree. -2. ...But the style from the inside works. -3. To get elements in shadow tree, we must query from inside the tree. +1. El estilo del documento no afecta al árbol shadow. +2. ...Pero el estilo interno funciona. +3. Para obtener los elementos en el árbol shadow, debemos buscarlos (query) desde dentro del árbol. -## References +## Referencias - DOM: <https://dom.spec.whatwg.org/#shadow-trees> -- Compatibility: <https://caniuse.com/#feat=shadowdomv1> -- Shadow DOM is mentioned in many other specifications, e.g. [DOM Parsing](https://w3c.github.io/DOM-Parsing/#the-innerhtml-mixin) specifies that shadow root has `innerHTML`. +- Compatibilidad: <https://caniuse.com/#feat=shadowdomv1> +- Shadow DOM es mencionado en muchas otras especificaciones, por ejemplo [DOM Parsing](https://w3c.github.io/DOM-Parsing/#the-innerhtml-mixin) especifica que el shadow root tiene `innerHTML`. -## Summary +## Resumen -Shadow DOM is a way to create a component-local DOM. +El Shadow DOM es una manera de crear un DOM de componentes locales. -1. `shadowRoot = elem.attachShadow({mode: open|closed})` -- creates shadow DOM for `elem`. If `mode="open"`, then it's accessible as `elem.shadowRoot` property. -2. We can populate `shadowRoot` using `innerHTML` or other DOM methods. +1. `shadowRoot = elem.attachShadow({mode: open|closed})` -- crea shadow DOM para `elem`. Si `mode="open"`, será accesible con la propiedad `elem.shadowRoot`. +2. Podemos llenar `shadowRoot` usando `innerHTML` u otros métodos DOM. -Shadow DOM elements: -- Have their own ids space, -- Invisible to JavaScript selectors from the main document, such as `querySelector`, -- Use styles only from the shadow tree, not from the main document. +Los elementos de Shadow DOM: +- Tienen su propio espacio de ids, +- Son invisibles a los selectores JavaScript desde el documento principal tales como `querySelector`, +- Usan style solo desde dentro del árbol shadow, no desde el documento principal. -Shadow DOM, if exists, is rendered by the browser instead of so-called "light DOM" (regular children). In the chapter <info:slots-composition> we'll see how to compose them. +El Shadow DOM, si existe, es construido por el navegador en lugar del DOM visible llamado "light DOM" (hijo regular). En el capítulo <info:slots-composition> veremos cómo se componen. diff --git a/8-web-components/4-template-element/article.md b/8-web-components/4-template-element/article.md index 5499c4edc..3b537a951 100644 --- a/8-web-components/4-template-element/article.md +++ b/8-web-components/4-template-element/article.md @@ -1,24 +1,24 @@ -# Template element +# Elemento template -A built-in `<template>` element serves as a storage for HTML markup templates. The browser ignores its contents, only checks for syntax validity, but we can access and use it in JavaScript, to create other elements. +El elemento incorporado `<template>` sirve como almacenamiento para plantillas de markup de HTML. El navegador ignora su contenido, solo verifica la validez de la sintaxis, pero podemos acceder a él y usarlo en JavaScript para crear otros elementos. -In theory, we could create any invisible element somewhere in HTML for HTML markup storage purposes. What's special about `<template>`? +En teoría, podríamos crear cualquier elemento invisible en algún lugar de HTML par fines de almacenamiento de HTML markup. ¿Qué hay de especial en `<template>`? -First, its content can be any valid HTML, even if it normally requires a proper enclosing tag. +En primer lugar, su contenido puede ser cualquier HTML válido, incluso si normalmente requiere una etiqueta adjunta adecuada. -For example, we can put there a table row `<tr>`: +Por ejemplo, podemos poner una fila de tabla `<tr>`: ```html <template> <tr> - <td>Contents</td> + <td>Contenidos</td> </tr> </template> ``` -Usually, if we try to put `<tr>` inside, say, a `<div>`, the browser detects the invalid DOM structure and "fixes" it, adds `<table>` around. That's not what we want. On the other hand, `<template>` keeps exactly what we place there. +Normalmente, si intentamos poner `<tr>` dentro, digamos, de un `<div>`, el navegador detecta la estructura DOM como inválida y la “arregla”, y añade un `<table>` alrededor. Eso no es lo que queremos. Sin embargo, `<template>` mantiene exactamente lo que ponemos allí. -We can put styles and scripts into `<template>` as well: +También podemos poner estilos y scripts dentro de `<template>`: ```html <template> @@ -26,45 +26,45 @@ We can put styles and scripts into `<template>` as well: p { font-weight: bold; } </style> <script> - alert("Hello"); + alert("Hola"); </script> </template> ``` -The browser considers `<template>` content "out of the document": styles are not applied, scripts are not executed, `<video autoplay>` is not run, etc. +El navegador considera al contenido `<template>` “fuera del documento”: Los estilos no son aplicados, los scripts no son ejecutados, `<video autoplay>` no es ejecutado, etc. -The content becomes live (styles apply, scripts run etc) when we insert it into the document. +El contenido cobra vida (estilos aplicados, scripts, etc) cuando los insertamos dentro del documento. -## Inserting template +## Insertando template -The template content is available in its `content` property as a [DocumentFragment](info:modifying-document#document-fragment) -- a special type of DOM node. +El contenido template está disponible en su propiedad `content` como un [DocumentFragment](info:modifying-document#document-fragment): un tipo especial de nodo DOM. -We can treat it as any other DOM node, except one special property: when we insert it somewhere, its children are inserted instead. +Podemos tratarlo como a cualquier otro nodo DOM, excepto por una propiedad especial: cuando lo insertamos en algún lugar, sus hijos son insertados en su lugar. -For example: +Por ejemplo: ```html run <template id="tmpl"> <script> - alert("Hello"); + alert("Hola"); </script> - <div class="message">Hello, world!</div> + <div class="message">¡Hola mundo!</div> </template> <script> let elem = document.createElement('div'); *!* - // Clone the template content to reuse it multiple times + // Clona el contenido de la plantilla para reutilizarlo múltiples veces elem.append(tmpl.content.cloneNode(true)); */!* document.body.append(elem); - // Now the script from <template> runs + // Ahora el script de <template> se ejecuta </script> ``` -Let's rewrite a Shadow DOM example from the previous chapter using `<template>`: +Reescribamos un ejemplo de Shadow DOM del capítulo anterior usando `<template>`: ```html run untrusted autorun="no-epub" height=60 <template id="tmpl"> @@ -72,7 +72,7 @@ Let's rewrite a Shadow DOM example from the previous chapter using `<template>`: <p id="message"></p> </template> -<div id="elem">Click me</div> +<div id="elem">Haz clic sobre mi</div> <script> elem.onclick = function() { @@ -82,14 +82,14 @@ Let's rewrite a Shadow DOM example from the previous chapter using `<template>`: elem.shadowRoot.append(tmpl.content.cloneNode(true)); // (*) */!* - elem.shadowRoot.getElementById('message').innerHTML = "Hello from the shadows!"; + elem.shadowRoot.getElementById('message').innerHTML = "¡Saludos desde las sombras!"; }; </script> ``` -In the line `(*)` when we clone and insert `tmpl.content`, as its `DocumentFragment`, its children (`<style>`, `<p>`) are inserted instead. +En la línea `(*)`, cuando clonamos e insertamos `tmpl.content` como su `DocumentFragment`, sus hijos (`<style>`, `<p>`) se insertan en su lugar. -They form the shadow DOM: +Ellos forman el shadow DOM: ```html <div id="elem"> @@ -99,18 +99,18 @@ They form the shadow DOM: </div> ``` -## Summary +## Resumen -To summarize: +Para resumir: -- `<template>` content can be any syntactically correct HTML. -- `<template>` content is considered "out of the document", so it doesn't affect anything. -- We can access `template.content` from JavaScript, clone it to reuse in a new component. +- El contenido `<template>` puede ser cualquier HTML sintácticamente correcto. +- El contenido `<template>` es considerado “fuera del documento”, para que no afecte a nada. +- Podemos acceder a `template.content` desde JavaScript, y clonarlo para reusarlo en un nuevo componente. -The `<template>` tag is quite unique, because: +La etiqueta `<template>` es bastante única, ya que: -- The browser checks HTML syntax inside it (as opposed to using a template string inside a script). -- ...But still allows use of any top-level HTML tags, even those that don't make sense without proper wrappers (e.g. `<tr>`). -- The content becomes interactive: scripts run, `<video autoplay>` plays etc, when inserted into the document. +- El navegador comprueba la sintaxis HTML dentro de él (lo opuesto a usar una plantilla string dentro de un script). +- ...Pero aún permite el uso de cualquier etiqueta HTML de alto nivel, incluso aquellas que no tienen sentido sin un envoltorio adecuado (por ej.`<tr>`). +- El contenido se vuelve interactivo cuando es insertado en el documento: los scripts se ejecutan, `<video autoplay>` se reproduce, etc. -The `<template>` element does not feature any iteration mechanisms, data binding or variable substitutions, but we can implement those on top of it. +El elemento `<template>` no ofrece ningún mecanismo de iteración, enlazamiento de datos o sustitución de variables, pero podemos implementar los que están por encima. diff --git a/8-web-components/5-slots-composition/article.md b/8-web-components/5-slots-composition/article.md index c41e26e05..3d9b8ab25 100644 --- a/8-web-components/5-slots-composition/article.md +++ b/8-web-components/5-slots-composition/article.md @@ -1,33 +1,33 @@ -# Shadow DOM slots, composition +# Shadow DOM slots, composición -Many types of components, such as tabs, menus, image galleries, and so on, need the content to render. +Muchos tipos de componentes; como pestañas, menús, galerías de imágenes, etc., necesitan renderizar contenido. -Just like built-in browser `<select>` expects `<option>` items, our `<custom-tabs>` may expect the actual tab content to be passed. And a `<custom-menu>` may expect menu items. +Al igual que el `<select>` nativo del navegador espera elementos de `<option>`, nuestros `<custom-tabs>` pueden esperar que se pase el contenido real de la pestaña. Y un `<custom-menu>` puede esperar elementos de menú. -The code that makes use of `<custom-menu>` can look like this: +El código que hace uso de `<custom-menu>` puede verse así: ```html <custom-menu> - <title>Candy menu - Lollipop - Fruit Toast - Cup Cake + Menú de dulces + Paletas + Tostada de frutas + Magdalenas ``` -...Then our component should render it properly, as a nice menu with given title and items, handle menu events, etc. +...Entonces nuestro componente debería renderizar correctamente, como un agradable menú con un título y elementos dados, manejar eventos de menú, etc. -How to implement it? +¿Cómo implementarlo? -We could try to analyze the element content and dynamically copy-rearrange DOM nodes. That's possible, but if we're moving elements to shadow DOM, then CSS styles from the document do not apply in there, so the visual styling may be lost. Also that requires some coding. +Podríamos intentar analizar el contenido del elemento y copiar y reorganizar dinámicamente los nodos del DOM. Esto es posible, pero si estamos moviendo elementos al shadow DOM, entonces los estilos CSS del documento no se aplican allí, por lo que se puede perder el estilo visual. También eso requiere algo de programación. -Luckily, we don't have to. Shadow DOM supports `` elements, that are automatically filled by the content from light DOM. +Afortunadamente, no tenemos que hacerlo. Shadow DOM soporta elementos ``, que se llenan automáticamente con el contenido del light DOM. -## Named slots +## Slots con nombres -Let's see how slots work on a simple example. +Veamos cómo funcionan los slots en un ejemplo simple. -Here, `` shadow DOM provides two slots, filled from light DOM: +Aquí, el shadow DOM `` proporciona dos slots, que se llenan desde el light DOM: ```html run autorun="no-epub" untrusted height=80 ``` -The menu rendering updates each time without our intervention. +La representación del menú se actualiza cada vez sin nuestra intervención.. -There are two `slotchange` events here: +Hay dos eventos `slotchange` aquí: -1. At initialization: +1. En la inicialización: - `slotchange: title` triggers immediately, as the `slot="title"` from the light DOM gets into the corresponding slot. -2. After 1 second: + `slotchange: title` se dispara inmediatamente, cuando el `slot="title"` desde el light DOM entra en el slot correspondiente. +2. Después de 1 segundo: - `slotchange: item` triggers, when a new `
  • ` is added. + `slotchange: item` se activa, cuando se agrega un nuevo `
  • `. -Please note: there's no `slotchange` event after 2 seconds, when the content of `slot="title"` is modified. That's because there's no slot change. We modify the content inside the slotted element, that's another thing. +Observa que no hay ningún evento `slotchange` después de 2 segundos, cuando se modifica el contenido de `slot = "title"`. Eso es porque no hay cambio en el slot. Modificamos el contenido dentro del elemento esloteado, eso es otra cosa. -If we'd like to track internal modifications of light DOM from JavaScript, that's also possible using a more generic mechanism: [MutationObserver](info:mutation-observer). +Si quisiéramos rastrear las modificaciones internas del Light DOM desde JavaScript, eso también es posible usando un mecanismo más genérico: [MutationObserver](info:mutation-observer). ## Slot API -Finally, let's mention the slot-related JavaScript methods. +Finalmente, mencionemos los métodos JavaScript relacionados con los slots. -As we've seen before, JavaScript looks at the "real" DOM, without flattening. But, if the shadow tree has `{mode: 'open'}`, then we can figure out which elements assigned to a slot and, vice-versa, the slot by the element inside it: +Como hemos visto antes, JavaScript busca en el DOM "real", sin aplanar. Pero, si el shadow tree tiene `{mode: 'open'}`, podemos averiguar qué elementos hay asignados a un slot y, viceversa, averiguar el slot por el elemento dentro de el: -- `node.assignedSlot` -- returns the `` element that the `node` is assigned to. -- `slot.assignedNodes({flatten: true/false})` -- DOM nodes, assigned to the slot. The `flatten` option is `false` by default. If explicitly set to `true`, then it looks more deeply into the flattened DOM, returning nested slots in case of nested components and the fallback content if no node assigned. -- `slot.assignedElements({flatten: true/false})` -- DOM elements, assigned to the slot (same as above, but only element nodes). +- `node.assignedSlot` -- retorna el elemento `` al que está asignado el `nodo`. +- `slot.assignedNodes({flatten: true/false})` -- Nodos DOM, asignados al slot. La opción `flatten` es `false` por defecto. Si se establece explícitamente a `true`, entonces mira más profundamente en el flattened DOM, retornando slots anidadas en caso de componentes anidados y el contenido de respaldo si ningún node está asignado. +- `slot.assignedElements({flatten: true/false})` -- Elementos DOM, asignados al slot (igual que arriba, pero solo nodos de elementos). -These methods are useful when we need not just show the slotted content, but also track it in JavaScript. +Estos métodos son útiles cuando no solo necesitamos mostrar el contenido esloteado, sino también rastrearlo en JavaScript. -For example, if `` component wants to know, what it shows, then it could track `slotchange` and get the items from `slot.assignedElements`: +Por ejemplo, si el componente `` quiere saber qué muestra, entonces podría rastrear `slotchange` y obtener los elementos de `slot.assignedElements`: ```html run untrusted height=120 - Candy menu -
  • Lollipop
  • -
  • Fruit Toast
  • + Menú de dulces +
  • Paletas
  • +
  • Tostada de frutas
  • ``` -## Summary +## Resumen -Usually, if an element has shadow DOM, then its light DOM is not displayed. Slots allow to show elements from light DOM in specified places of shadow DOM. +Por lo general, si un elemento tiene shadow DOM, no se muestra su light DOM. Los slots permiten mostrar elementos del light DOM en lugares específicos del shadow DOM. -There are two kinds of slots: +Hay dos tipos de slots: -- Named slots: `...` -- gets light children with `slot="X"`. -- Default slot: the first `` without a name (subsequent unnamed slots are ignored) -- gets unslotted light children. -- If there are many elements for the same slot -- they are appended one after another. -- The content of `` element is used as a fallback. It's shown if there are no light children for the slot. +- Named slots: `...` -- consigue los light children con `slot="X"`. +- Default slot: el primer `` sin un nombre (los slots subsiguientes sin nombre se ignoran) -- obtiene light children sin slotear. +- Si hay muchos elementos para el mismo slot, se añaden uno tras otro. +- El contenido del elemento `` se utiliza como respaldo. Se muestra si no hay light children para el slot. -The process of rendering slotted elements inside their slots is called "composition". The result is called a "flattened DOM". +El proceso de renderizar elementos sloteados dentro de sus slots se llama "composición". El resultado se denomina "flattened DOM". -Composition does not really move nodes, from JavaScript point of view the DOM is still same. +La composición no mueve realmente los nodos, desde el punto de vista de JavaScript, el DOM sigue siendo el mismo. -JavaScript can access slots using methods: -- `slot.assignedNodes/Elements()` -- returns nodes/elements inside the `slot`. -- `node.assignedSlot` -- the reverse property, returns slot by a node. +JavaScript puede acceder a los slots mediante estos métodos: +- `slot.assignedNodes/Elements()` -- retorna nodos/elementos dentro del `slot`. +- `node.assignedSlot` -- la propiedad inversa, retorna el slot por un nodo. -If we'd like to know what we're showing, we can track slot contents using: -- `slotchange` event -- triggers the first time a slot is filled, and on any add/remove/replace operation of the slotted element, but not its children. The slot is `event.target`. -- [MutationObserver](info:mutation-observer) to go deeper into slot content, watch changes inside it. +Si queremos saber, podemos rastrear el contenido de los slots usando: +- `slotchange` event -- se activa la primera vez que se llena un slot, y en cualquier operación de agregar/quitar/reemplazar del elemento esloteado, pero no sus hijos. El slot es `event.target`. +- [MutationObserver](info:mutation-observer) para profundizar en el contenido del slot, observar los cambios en su interior. -Now, as we know how to show elements from light DOM in shadow DOM, let's see how to style them properly. The basic rule is that shadow elements are styled inside, and light elements -- outside, but there are notable exceptions. +Ahora que, como sabemos cómo mostrar elementos del light DOM en el shadow DOM, veamos cómo diseñarlos correctamente. La regla básica es que los elementos shadow se diseñan en el interior y los elementos light se diseñan afuera, pero hay notables excepciones. -We'll see the details in the next chapter. +Veremos los detalles en el próximo capítulo. diff --git a/8-web-components/6-shadow-dom-style/article.md b/8-web-components/6-shadow-dom-style/article.md index 98e246a7f..3744e9dc0 100644 --- a/8-web-components/6-shadow-dom-style/article.md +++ b/8-web-components/6-shadow-dom-style/article.md @@ -1,21 +1,21 @@ -# Shadow DOM styling +# Estilo Shadow DOM -Shadow DOM may include both ` ``` -...Then the `` would be without padding. +...Entonces el `` estaría sin padding. -It's very convenient, as we can setup "default" component styles in its `:host` rule, and then easily override them in the document. +Es muy conveniente, ya que podemos configurar estilos de componentes "predeterminados" en su regla `:host`, y luego sobreescribirlos fácilmente en el documento. -The exception is when a local property is labelled `!important`, for such properties, local styles take precedence. +La excepción es cuando una propiedad local está etiquetada como `!important`. Para tales propiedades, los estilos locales tienen prioridad. ## :host(selector) -Same as `:host`, but applied only if the shadow host matches the `selector`. +Igual que `:host`, pero se aplica solo si el shadow host coincide con el `selector`. -For example, we'd like to center the `` only if it has `centered` attribute: +Por ejemplo, nos gustaría centrar el `` solo si tiene el atributo `centered`: ```html run autorun="no-epub" untrusted height=80