Eventos de JavaScript y respuesta al usuario

 

 

 

  • Clase magistral de CSS moderno avanzado, con Manuel Matuzović
  • Clase magistral de diseño para una interfaz de usuario compleja, con Vitaly Friedman

  • Índice
    1. Conceptos básicos: ¿Qué es un evento?
    2. El último de los conceptos básicos: impedir la ejecución y conseguir el objetivo
    3. Delegación de eventos: es genial. ¡Úselo!
    4. Eventos para detección, transiciones CSS para suavidad
    5. ¿Cuánto tiempo estuvo presionada una tecla?
    6. Trabajar con transiciones CSS (y animaciones)
    7. Velocidad, distancia y ángulo
    8. Eventos de medios
    9. Opciones de entrada
    10. Sal y juega
      1. Otras lecturas

    Los eventos en los navegadores son increíblemente útiles. En este artículo, Christian Heilmann vuelve a los conceptos básicos de los eventos y le anima a empezar a jugar con ellos.

     

    Siempre que la gente me pregunta sobre las cosas más poderosas de JavaScript y DOM, rápidamente llego a los eventos. La razón es que los eventos en los navegadores son increíblemente útiles. Además, desacoplar la funcionalidad de los eventos es una idea poderosa, razón por la cual Node.js se convirtió en un tema tan candente.

    Hoy, volvamos a los conceptos básicos de los eventos y le pongamos de humor para empezar a jugar con ellos, más allá de aplicar controladores de clics a todo o romper la Web con a href=“javascript:void(0)”enlaces o estropear nuestro HTML con onclick=“foo()”controladores en línea (expliqué en detalle en 2005 por qué estas son malas ideas ).

    Nota: este artículo utiliza JavaScript simple y no bibliotecas. Mucho de lo que hablaremos aquí es más fácil de lograr en jQuery, YUI o Dojo, pero comprender los conceptos básicos es importante porque te encontrarás en situaciones en las que no puedes usar una biblioteca pero aún así deberías poder ofrecer una solución sorprendente. .

    Descargo de responsabilidad : la sintaxis de eventos que usaremos aquí es addEventListener() , tal como se define en la especificación " Eventos de nivel DOM 3 ", que funciona en todos los navegadores que se utilizan actualmente, excepto Internet Explorer por debajo de la versión 9. Muchas de las cosas que Sin embargo, lo que mostraremos se puede lograr con jQuery, que también es compatible con navegadores heredados. Ahora que lo pienso, una simple addEventListener()acción DOMContentLoadedes una excelente manera de asegurarse de que su secuencia de comandos no se ejecute en navegadores heredados. Ésto es una cosa buena. Si queremos que la Web evolucione, debemos dejar de proporcionar códigos complejos y exigentes a los navegadores antiguos. Si construye sus soluciones de la manera correcta, entonces IE 6 no necesitará ningún JavaScript para mostrar una solución viable, aunque más simple. Piense en su producto como una escalera mecánica: si su JavaScript no se ejecuta, el sitio web aún debería poder utilizarse como escalera.

    Antes de entrar en los detalles de los eventos y cómo usarlos, consulte algunas demostraciones que utilizan eventos de desplazamiento de una manera inteligente para lograr resultados bastante interesantes:

    • En su búsqueda de un diseñador, Wealthfront Engineering utiliza el contenido desplazable y desplazado a lo largo del eje Z. Esta fue una gran parte del sitio web de Beercamp 2011 . Wealthfront escribió en su blog detalladamente cómo logró esto.
    • Stroll.js adopta un enfoque ligeramente similar, mostrando lo hermosas que pueden ser las transiciones cuando el usuario se desplaza por una lista.
    • jQuery Scroll Path es un complemento para mover contenido a lo largo de una ruta cuando el usuario se desplaza por la página.

    Todo esto se basa en el manejo de eventos y la lectura de lo que nos brinda el navegador. Ahora, veamos cómo repetir lo básico.

    Conceptos básicos: ¿Qué es un evento?

    var log = document.getElementById('log'), i = ’, out = [];for (i in window) { if ( /^on/.test(i)) { out[out.length] = i; }}log.innerHTML = out.join(', ');

    En mi caso, al ejecutar Firefox, aparece esto:

    onmouseenter, onmouseleave, onafterprint, onbeforeprint, onbeforeunload, onhashchange, onmessage, onoffline, ononline, onpopstate, onpagehide, onpageshow, onresize, onunload, ondevicemotion, ondeviceorientation, onabort, onblur, oncanplay, oncanplaythrough, onchange, onclick, oncontextmenu, ondblclick, ondrag, ondragend, ondragenter, ondragleave, ondragover, ondragstart, ondrop, ondurationchange, onemptied, onended, onerror, onfocus, oninput, oninvalid, onkeydown, onkeypress, onkeyup, onload, onloadeddata, onloadedmetadata, onloadstart, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup, onmozfullscreenchange, onmozfullscreenerror, onpause, onplay, onplaying, onprogress, onratechange, onreset, onscroll, onseeked, onseeking, onselect, onshow, onstalled, onsubmit, onsuspend, ontimeupdate, onvolumechange, en espera, oncopy, oncut, onpaste, onbeforescriptexecute, onafterscriptexecute

     

    Hay mucho con lo que jugar y la forma de hacerlo es usando addEventListener():

    element.addEventListener(event, handler, useCapture);

    Por ejemplo:

    var a = document.querySelector('a'); // grab the first link in the documenta.addEventListener('click', ajaxloader, false);

    Es elementel elemento al que aplicamos el controlador; como en “¡Oye tú, enlace! Asegúrate de avisarme cuando te pase algo”. La ajaxloader()función es el detector de eventos; como en “¡Oye tú! Quédese ahí y mantenga los oídos y los ojos bien abiertos en caso de que algo le suceda al enlace”. Establecer en useCapturesignifica falseque nos contentamos con capturar el evento en burbujeo, en lugar de la fase de captura. Este es un tema largo y arduo , bien explicado en Dev.Opera. Digamos que al configurarlo useCaptureen false, estará bien en el 99,7434% de los casos (una aproximación aproximada). En realidad, el parámetro es opcional en todos los navegadores excepto Opera.

    Ahora, la función del controlador de eventos obtiene un objeto como parámetro del evento, que está lleno de propiedades increíbles con las que podemos jugar. Si prueba mi ejemplo , verá lo que hace el siguiente código:

    var log = document.getElementById('log'), out = ’;document.addEventListener('click', logeventinfo, false);document.addEventListener('keypress', logeventinfo, false);function logeventinfo (ev) { log.innerHTML = ’; out = 'ul'; for (var i in ev) { if (typeof ev[i] === 'function' || i === i.toUpperCase()) { continue; } out += 'lispan'+i+'/span: '+ev[i]+'/li'; } log.innerHTML += out + '/ul';}

    Puede asignar varios controladores de eventos al mismo evento o el mismo controlador a varios eventos (como se muestra en esta demostración).

    Esto eves lo que obtenemos del evento. Y (de nuevo, en mi caso, en Firefox) contiene muchas cosas interesantes:

    originalTarget: [object HTMLHtmlElement]type: clicktarget: [object HTMLHtmlElement]currentTarget: [object HTMLDocument]eventPhase: 3bubbles: truecancelable: truetimeStamp: 574553210defaultPrevented: falsewhich: 1rangeParent: [object Text]rangeOffset: 23pageX: 182pageY: 111isChar: falsescreenX: 1016screenY: 572clientX: 182clientY: 111ctrlKey: falseshiftKey: falsealtKey: falsemetaKey: falsebutton: 0relatedTarget: nullmozPressure: 0mozInputSource: 1view: [object Window]detail: 1layerX: 182layerY: 111cancelBubble: falseexplicitOriginalTarget: [object HTMLHtmlElement]isTrusted: trueoriginalTarget: [object HTMLHeadingElement]type: clicktarget: [object HTMLHeadingElement]currentTarget: [object HTMLDocument]eventPhase: 3bubbles: truecancelable: truetimeStamp: 574554192defaultPrevented: falsewhich: 1rangeParent: [object Text]rangeOffset: 0pageX: 1pageY: 18isChar: falsescreenX: 835screenY: 479clientX: 1clientY: 18ctrlKey: falseshiftKey: falsealtKey: falsemetaKey: falsebutton: 0relatedTarget: nullmozPressure: 0mozInputSource: 1view: [object Window]detail: 1layerX: 1layerY: 18cancelBubble: falseexplicitOriginalTarget: [object Text]isTrusted: true

    También difiere de un evento a otro. Intente hacer clic en la demostración y presionar las teclas, y verá que obtiene resultados diferentes. También puede consultar la lista completa de propiedades estándarevent .

     

    El último de los conceptos básicos: impedir la ejecución y conseguir el objetivo

    Dos cosas más son importantes cuando se trata de eventos en el navegador: tenemos que evitar que el navegador lleve a cabo su acción predeterminada para el evento y tenemos que averiguar en qué elemento se disparó el evento. Lo primero se logra con el ev.preventDefault()método y lo segundo se almacena en ev.target.

    Digamos que quiere saber que se ha hecho clic en un enlace, pero no quiere que el navegador lo siga porque tiene una gran idea de qué hacer con ese evento. Puede hacerlo suscribiéndose al evento de clic del enlace y puede evitar que el navegador lo siga llamando a preventDefault(). Aquí está el HTML:

    a href="https://smashingmagazine.com"Smashing, my dear!/aa href="https://smashingmagazine.com"Smashing, my dear!/a

    Y el JavaScript:

    var normal = document.querySelector('.normal'), prevent = document.querySelector('.prevent');prevent.addEventListener('click', function(ev) { alert('fabulous, really!'); ev.preventDefault();}, false);normal.addEventListener('click', function(ev) { alert('fabulous, really!');}, false);

    Nota: document.querySelector()es la forma estándar de obtener un elemento en el DOM. Es lo que $()hace el método en jQuery. Puede leer la especificación del W3C y obtener algunos fragmentos de código explicativos en Mozilla Developer Network (MDN).

    Si ahora hace clic en el enlace, recibirá una alerta. Y cuando presionas el botón “Aceptar”, no pasa nada más; el navegador no va a https://smashingmagazine.com. Sin el preventDefault(), el navegador mostrará la alerta y seguirá el enlace. Pruébalo.

    La forma normal de acceder al elemento en el que se hizo clic o sobre el que se presionó una tecla es usar la thispalabra clave en el controlador. Esto es breve y sencillo, pero en realidad es limitante porque addEventListener()nos brinda algo mejor: el objetivo del evento. También podría resultar confuso porque thises posible que ya esté vinculado a otra cosa, por lo que usarlo ev.currentTargetcomo se indica en la especificación es una apuesta más segura.

    Delegación de eventos: es genial. ¡Úselo!

    Usando la targetpropiedad del objeto de evento, puede averiguar en qué elemento ocurrió el evento.

    Los eventos ocurren al bajar por todo el árbol del documento hasta el elemento con el que interactuó y regresar a la ventana principal. Esto significa que si agrega un controlador de eventos a un elemento, obtendrá todos los elementos secundarios de forma gratuita. Todo lo que necesitas hacer es probar el objetivo del evento y responder en consecuencia. Vea mi ejemplo de una lista :

    ul lia href="https://developer.mozilla.org"MDN/a/li lia href="https://html5doctor.com"HTML5 Doctor/a/li lia href="https://html5rocks.com"HTML5 Rocks/a/li lia href="https://beta.theexpressiveweb.com/"Expressive Web/a/li lia href="https://creativeJS.com/"CreativeJS/a/li/ul

    Pase el mouse sobre la lista en este ejemplo y verá que un controlador de eventos es suficiente para obtener los enlaces, el elemento de la lista y la lista misma. Todo lo que necesitas hacer es comparar el tagNameobjetivo del evento con lo que deseas tener.

     

    var resources = document.querySelector('#resources'), log = document.querySelector('#log');resources.addEventListener('mouseover', showtarget, false);function showtarget(ev) { var target = ev.target; if (target.tagName === 'A') { log.innerHTML = 'A link, with the href:' + target.href; } if (target.tagName === 'LI') { log.innerHTML = 'A list item'; } if (target.tagName === 'UL') { log.innerHTML = 'The list itself'; }}

    Esto significa que puede guardar muchos controladores de eventos, cada uno de los cuales es costoso para el navegador. En lugar de aplicar un controlador de eventos a cada enlace y responder de esa manera, como lo haría la mayoría de las personas en jQuery $(‘a’).click(…)(aunque jQuery onestá bien), puede asignar un único controlador de eventos a la lista y verificar en qué elemento se acaba de hacer clic. Mejores Opiniones y reviews

    El principal beneficio de esto es que eres independiente del HTML. Si agrega más enlaces en una etapa posterior, no es necesario asignar nuevos controladores; el controlador de eventos sabrá automáticamente que hay un nuevo enlace con el que hacer cosas.

    Eventos para detección, transiciones CSS para suavidad

    Si recuerda la lista de propiedades mencionada anteriormente en este artículo, hay muchas cosas que podemos usar. En el pasado, usábamos eventos para efectos de desplazamiento simples, que ahora han sido reemplazados con efectos usando los selectores :hovery CSS. :focusSin embargo, algunas cosas aún no se pueden hacer con CSS; por ejemplo, encontrar la posición del mouse. Con un detector de eventos, esto es bastante sencillo. Primero, definimos un elemento a posicionar, como una pelota. El HTML:

    div/div

    Y el CSS:

    .plot { position:absolute; background:rgb(175,50,50); width: 20px; height: 20px; border-radius: 20px; display: block; top:0; left:0;}

    Luego asignamos un controlador de clic al documento y colocamos la bola en PageXy pageY. Note que necesitamos restar la mitad del ancho de la bola para poder centrarla en el puntero del mouse:

    var plot = document.querySelector('.plot'), offset = plot.offsetWidth / 2;document.addEventListener('click', function(ev) { plot.style.left = (ev.pageX - offset) + 'px'; plot.style.top = (ev.pageY - offset) + 'px';}, false);

    Al hacer clic en cualquier lugar de la pantalla, la pelota se moverá allí. Sin embargo, no es fácil. Si habilitas la casilla de verificación en la demostración , verás que la bola se mueve suavemente. Podríamos animar esto con una biblioteca, pero los navegadores pueden funcionar mejor hoy en día. Todo lo que necesitamos hacer es agregar una transición al CSS y luego el navegador moverá la bola suavemente de una posición a otra. Para lograr esto, definimos una nueva clase nombrada smoothy la aplicamos al gráfico cuando se hace clic en la casilla de verificación en el documento. El CSS:

    .smooth { -webkit-transition: 0.5s; -moz-transition: 0.5s; -ms-transition: 0.5s; -o-transition: 0.5s; transition: 0.5s;}

    El JavaScript:

     

    var cb = document.querySelector('input[type=checkbox]');cb.addEventListener('click', function(ev) { plot.classList.toggle('smooth');}, false);

    La interacción entre eventos CSS y JavaScript siempre ha sido poderosa, pero mejoró aún más en los navegadores más nuevos. Como habrás adivinado, las transiciones y animaciones CSS tienen sus propios eventos.

    ¿Cuánto tiempo estuvo presionada una tecla?

    Como habrá visto anteriormente en la lista de eventos disponibles, los navegadores también nos dan la oportunidad de responder a la entrada del teclado y decirnos cuándo el usuario ha presionado una tecla. Lamentablemente, la gestión de claves en un navegador es difícil de realizar correctamente, como explica en detalle Jan Wolter . Sin embargo, como ejemplo simple, veamos cómo podemos medir en milisegundos cuánto tiempo un usuario ha presionado un botón. Vea esta demostración en tiempo clave para ver un ejemplo. Presione una tecla y verá crecer el campo de salida mientras la tecla está presionada. Una vez que sueltes la tecla, verás la cantidad de milisegundos que la presionaste. El código no es nada complicado:

    var resources = document.querySelector('#resources'), log = document.querySelector('#log'), time = 0;document.addEventListener('keydown', keydown, false);document.addEventListener('keyup', keyup, false);function keydown(ev) { if (time === 0) { time = ev.timeStamp; log.classList.add('animate'); }}function keyup(ev) { if (time !== 0) { log.innerHTML = ev.timeStamp - time; time = 0; log.classList.remove('animate'); }}

    Definimos los elementos que queremos y establecemos el timevalor 0. Luego aplicamos dos controladores de eventos al documento, uno en keydowny otro en keyup.

    En el keydowncontrolador, verificamos si timees 0y, si lo es, configuramos timeel timeStampevento. Asignamos una clase CSS al elemento de salida, que inicia una animación CSS (consulte CSS para saber cómo se hace).

    El keyupcontrolador verifica si timeestá quieto 0(ya que keydownse activa continuamente mientras se presiona la tecla) y calcula la diferencia en las marcas de tiempo si no es así. Volvemos timea 0la clase y la eliminamos para detener la animación.

    Trabajar con transiciones CSS (y animaciones)

    Las transiciones CSS activan un único evento que puedes escuchar en JavaScript llamado transitionend. El objeto de evento tiene entonces dos propiedades: propertyName, que contiene la propiedad a la que se realizó la transición, y elapsedTime, que indica cuánto tiempo tomó.

    Mira la demostración para verlo en acción. El código es bastante simple. Aquí está el CSS:

    .plot { background:rgb(175,50,50); width: 20px; height: 20px; border-radius: 20px; display: block; -webkit-transition: 0.5s; -moz-transition: 0.5s; -ms-transition: 0.5s; -o-transition: 0.5s; transition: 0.5s;}.plot:hover { width: 50px; height: 50px; border-radius: 100px; background: blue;}

    Y el JavaScript:

     

    plot.addEventListener('transitionend', function(ev) { log.innerHTML += ev.propertyName + ':' + ev.elapsedTime + 's ';}, false);

    Sin embargo, esto solo funciona en Firefox en este momento porque Chrome, Safari y Opera tienen eventos con prefijo del proveedor. Como muestra la esencia de David Calhoun , es necesario detectar qué admite el navegador y definir el nombre del evento de esa manera.

    Los eventos de animación CSS funcionan de la misma manera, pero tienes tres eventos en lugar de uno animationstart: animationendy animationiteration. MDN tiene una demostración .

    Velocidad, distancia y ángulo

    Detectar eventos que suceden es una cosa. Si quieres hacer algo hermoso y atractivo con ellos, entonces debes ir más allá y ponerle algo de matemática. Entonces, intentemos usar algunos controladores de mouse para calcular el ángulo, la distancia y la velocidad de movimiento cuando un usuario arrastra un elemento por la pantalla. Mira la demostración primero.

    var plot = document.querySelector('.plot'), log = document.querySelector('output'), offset = plot.offsetWidth / 2, pressed = false, start = 0, x = 0, y = 0, end = 0, ex = 0, ey = 0, mx = 0, my = 0, duration = 0, dist = 0, angle = 0;document.addEventListener('mousedown', onmousedown, false);document.addEventListener('mouseup', onmouseup, false);document.addEventListener('mousemove', onmousemove, false);function onmousedown(ev) { if (start === 0 x === 0 y === 0) { start = ev.timeStamp; x = ev.clientX; y = ev.clientY; moveplot(x, y); pressed = true; }}function onmouseup(ev) { end = ev.timeStamp; duration = end - start; ex = ev.clientX; ey = ev.clientY; mx = ex - x; my = ey - y; dist = Math.sqrt(mx * mx + my * my); start = x = y = 0; pressed = false; angle = Math.atan2( my, mx ) * 180 / Math.PI; log.innerHTML = 'strong' + (dist0) +'/strong pixels in strong'+ duration +'/strong ms ( strong' + twofloat(dist/duration) +'/strong pixels/ms)'+ ' at strong' + twofloat(angle) + '/strong degrees';}function onmousemove (ev) { if (pressed) { moveplot(ev.pageX, ev.pageY); }}function twofloat(val) { return Math.round((val*100))/100;}function moveplot(x, y) { plot.style.left = (x - offset) + 'px'; plot.style.top = (y - offset) + 'px';}

    Bueno, lo admito: aquí están pasando muchas cosas. Pero no es tan difícil como parece. Para ambos onmousedowny onmouseup, leemos la posición del mouse con clientXy clientYy timeStampdel evento. Los eventos del mouse tienen marcas de tiempo que le indican cuándo sucedieron. Cuando el mouse se mueve, todo lo que verificamos es si se ha presionado el botón del mouse (mediante un conjunto booleano en el mousedowncontrolador) y movemos el gráfico con el mouse.

    El resto es geometría; el bueno de Pitágoras , para ser precisos. Obtenemos la velocidad del movimiento comprobando el número de píxeles recorridos en la diferencia de tiempo entre mousedowny mouseup.

    Obtenemos el número de píxeles recorridos como la raíz cuadrada de la suma de los cuadrados de la diferencia entre xey al inicio y al final del movimiento. Y obtenemos el ángulo calculando el arcotangente del triángulo. Todo esto se trata en “ Una mirada rápida a las matemáticas de las animaciones con JavaScript ”; o puedes jugar con el siguiente ejemplo de JSFiddle:

     

    Eventos de medios

    Tanto el vídeo como el audio desencadenan muchos eventos que podemos aprovechar. Los más interesantes son los eventos de tiempo que te indican cuánto tiempo lleva reproduciéndose una canción o película. Una pequeña demostración agradable para ver es la animación de dinosaurios inspirada en MGM en MDN; Grabé un screencast de seis minutos explicando cómo se hace.

    Si desea ver una demostración de todos los eventos en acción, el equipo de JPlayer tiene una excelente página de demostración que muestra eventos multimedia .

    Opciones de entrada

    Tradicionalmente, los navegadores nos permitían interactuar con el mouse y el teclado. Hoy en día esto no es suficiente porque utilizamos hardware que nos ofrece más. La orientación del dispositivo , por ejemplo, permite responder a la inclinación de un teléfono o tableta; los eventos táctiles son una gran novedad en móviles y tabletas; la API Gamepad nos permite leer los controladores de juegos en los navegadores; postMessage nos permite enviar mensajes entre dominios y ventanas del navegador; pageVisibility nos permite reaccionar cuando los usuarios cambian a otra pestaña. Incluso podemos detectar cuando se ha manipulado el objeto histórico de la ventana . Consulte la lista de eventos en el objeto de ventana para encontrar más gemas que quizás no estén listas pero que deberían estar disponibles pronto para que podamos profundizar en ellas.

    Independientemente de lo que suceda a continuación en la compatibilidad con el navegador, puede estar seguro de que los eventos se activarán y podrá escucharlos. El método funciona y realmente es genial.

    Sal y juega

    Y eso es todo. Los acontecimientos no son difíciles; en la mayoría de los casos, sólo necesita suscribirse a ellos y verificar lo que aparece como objeto del evento para ver qué puede hacer con él. Por supuesto, a veces todavía es necesario piratear mucho el navegador, pero, por mi parte, encuentro increíble la cantidad de formas en que podemos interactuar con nuestros usuarios y ver lo que están haciendo. Si quieres ser realmente creativo con esto, deja de pensar en los casos de uso que tenemos ahora y profundiza en el meollo de lo que las distancias, los ángulos, la velocidad y la entrada pueden significar para una interfaz. Si lo piensas bien, jugar a Angry Birds en su mayor medida significa detectar el inicio y el final de un evento táctil y detectar la potencia y la dirección en la que el pájaro debe despegar. Entonces, ¿qué te impide crear algo muy interactivo y genial? ?

    Fuente de la imagen de la portada.

    Otras lecturas

    • Los siete pecados capitales de la implementación de JavaScript
    • Eventos de entrada del navegador: ¿Podemos hacerlo mejor que el clic?
    • Convertir a un trabajador de servicios: un estudio de caso
    • 7 cosas de JavaScript que desearía saber mucho antes en mi carrera

    (al, señor)Explora más en

    • Codificación
    • CSS
    • javascript
    • Programación





    Tal vez te puede interesar:

    1. ¿Deberían abrirse los enlaces en ventanas nuevas?
    2. 24 excelentes tutoriales de AJAX
    3. 70 técnicas nuevas y útiles de AJAX y JavaScript
    4. Más de 45 excelentes recursos y repositorios de fragmentos de código

    Eventos de JavaScript y respuesta al usuario

    Eventos de JavaScript y respuesta al usuario

    Clase magistral de CSS moderno avanzado, con Manuel Matuzović Clase magistral de diseño para una interfaz de usuario compleja, con Vitaly Friedman Índice

    programar

    es

    https://aprendeprogramando.es/static/images/programar-eventos-de-javascript-y-respuesta-al-usuario-802-0.jpg

    2024-05-20

     

    Eventos de JavaScript y respuesta al usuario
    Eventos de JavaScript y respuesta al usuario

    Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente

     

     

    Top 20