Desenvolupament web dinàmic frontend amb javascript
Conceptes¶
Sessió 3: Treballar amb elements HTML¶
Objectiu Sessió 3
L'objectiu serà dinamitzar i generar el contingut de la interfície segons la interacció de l'usuari. Modificarem el frontend gestionant els elements html que formen part de la interfície.
Dinamitzar interfície¶
Com a experiència d’usuari ens pot interessar de dinamitzar els elements que es mostren a la interfície. Podrem crear-ne de nous, així com eliminar, moure o substituir-los a dins de la interfície. D’aquesta manera el dinamisme del contingut serà màxim.
Exemples
Crea i afegeix un element nou al DOM en resposta a un esdeveniment, com ara un clic:
Javascript
HTMLPermet eliminar elements amb un botó específic per a cada element.
Javascript
HTMLIdeal per afegir o eliminar camps en un formulari segons necessitats de l'usuari.
Javascript
document.getElementById("addField").addEventListener("click", function() {
const newField = document.createElement("input");
newField.type = "text";
newField.placeholder = "Camp nou";
document.getElementById("formFields").appendChild(newField);
});
document.getElementById("removeField").addEventListener("click", function() {
const fields = document.getElementById("formFields");
if (fields.lastElementChild) {
fields.removeChild(fields.lastElementChild);
}
});
Afegir i esborrar elements¶
Segons l'especificació del DOM, tot el que hi ha en un document HTML es representa com un node d'un tipus concret:
- el document sencer és el node de tipus document
- cada element HTML és un node de tipus element
- el text que hi ha dins dels elements HTML són nodes de tipus text
- cada atribut d'un element és un node de tipus atribut
- tots els comentaris són nodes de tipus comentari
Relació entre nodes¶
Els nodes estan relacionats jeràrquicament:
- el node inicial s'anomena node arrel
- cada node té exactament un sol pare, excepte el node arrel que no en té cap
- un node pot tenir qualsevol nombre de nodes fills
- els nodes germans (siblings) són aquells que tenen el mateix pare
Podem utilitzar les següents propietats per navegar entre nodes amb JavaScript:
Podem utilitzar les següents propietats per navegar entre nodes amb JavaScript:
Exemple Entenem els elements
Tenim aquest resultat:
<html>
<body>
<button id="btn">appendChild()</button>
<div id="div1">
<p id="p1">Primer paràgraf</p>
<p id="p2">Segon paràgraf</p>
</div>
<script>
// ens connectem al div1
const ele = document.getElementById("div1");
// recorrem tots els nodes fills del div1
console.log(ele);
ele.childNodes.forEach(element => {
console.log(element);
});
</script>
</body>
</html>
Tenim un codi HTML
amb div i paràgrafs amb un id="p1"
a dins
Tenim un botó <button id="btn">appendChild()</button>
Tenim un SCRIPT
que mostra a la consola els elements fills que conté el DOM a partir del div1
Crear nous elements HTML (nodes)¶
Per afegir elements al DOM cal fer-ho en dues passes:
- Crear el node del tipus d'element que volem, utilitzant el mètode
createElement()
- Associar-lo com a fill d'un element ja existent amb
appendChild()
oinsertBefore()
Afegir un element amb appendChild()
El mètode appendChild
afegeix un node nou com a últim element fill dins d'un altre node.
Exemple appendchild
Tenim aquest resultat:
<button id="btn">appendChild()</button>
<div id="div1">
<p id="p1">Primer paràgraf</p>
<p id="p2">Segon paràgraf</p>
</div>
<script>
document.getElementById("btn").onclick = function () {
var nou = document.createElement("p");
nou.innerHTML = "Paràgraf nou";
nou.style.color = "red";
document.getElementById("div1").appendChild(nou);
}
</script>
Tenim un codi HTML
amb div i paràgrafs amb un id="p1"
a dins
Tenim un botó <button id="btn">appendChild()</button>
que programem l'esdevenimnet click, fent:
- Creem un nou element paràgraf amb
var nou = document.createElement("p");
- Modifiquem propietats com el text a mostrar i l'estil
nou.innerHTML = "Paràgraf nou";
inou.style.color = "red";
- L'afegim al final amb
document.getElementById("div1").appendChild(nou);
Afegir un element amb insertBefore()
El mètode insertBefore
afegeix un node nou com a element fill situant-lo just davant d'un altre node ja existent.
Exemple insertBefore
Tenim aquest resultat:
<button id="btn">insertBefore()</button>
<div id="div1">
<p id="p1">Primer paràgraf</p>
<p id="p2">Segon paràgraf</p>
</div>
<script>
document.getElementById("btn").onclick = function () {
var nou = document.createElement("p");
nou.innerHTML = "Paràgraf nou";
nou.style.color = "red";
posicio = document.getElementById("p2");
document.getElementById("div1").insertBefore(nou, posicio);
}
</script>
Tenim un codi HTML
amb div i paràgrafs amb un id="p1"
a dins
Tenim un botó <button id="btn">insertBefore()</button>
que programem l'esdevenimnet click, fent:
- Creem un nou element paràgraf amb
var nou = document.createElement("p");
- Modifiquem propietats com el text a mostrar i l'estil
nou.innerHTML = "Paràgraf nou";
inou.style.color = "red";
- Obtenim l'element
p2
- L'afegim abans de la posició del element amb el div2 amb
document.getElementById("div1").insertBefore(nou, posicio);
Moure un element¶
Si utilitzem appendChild
o insertBefore
sobre un node ja existent enlloc d'un de nou, el desplaçarà a la nova posició.
Per indicar el node a moure, el passem com a paràmetre del mètode utilitzat.
Exemple Moure un element
Tenim aquest resultat:
<button id="btn">moure element</button>
<div id="div1">
<p id="p1">Primer paràgraf</p>
<p id="p2">Segon paràgraf</p>
</div>
<hr>
<div id="div2">
<p id="p3">Tercer paràgraf</p>
<p id="p4">Quart paràgraf</p>
</div>
<script>
document.getElementById("btn").onclick = function () {
canvi = document.getElementById("p4");
document.getElementById("div1").appendChild(canvi);
}
</script>
Tenim un codi HTML
amb div i paràgrafs amb un id="p1" ...
a dins
Tenim un botó <button id="btn">moure element</button>
que programem l'esdevenimnet click, fent:
- Obtenim l'element
p4
,canvi = document.getElementById("p4");
- L'afegim a dins del div1 amb
document.getElementById("div1").appendChild(canvi);
Eliminar elements HTML existents¶
Podem eliminar un element invocant el mètode remove
del mateix element.
Sintaxi
node.remove()
Exemple Eliminar un element
Tenim aquest resultat:
Tenim un codi HTML
amb div i paràgrafs amb un id="p1" ...
a dins
Tenim un botó <button id="btn">remove()</button>
que programem l'esdevenimnet click, fent:
- Eliminem l'element amb id p2 amb remove
document.getElementById("p2").remove();
També podem eliminar un element del DOM és a través del seu node pare utilitzant el mètode removeChild
.
Sintaxi
node.removeChild( node )
Retorna el node eliminat, o null
si no existeix
Exemple Eliminar un element fill
Tenim aquest resultat:
Tenim un codi HTML
amb div i paràgrafs amb un id="p1" ...
a dins
Tenim un botó <button id="btn">removeChild()</button>
que programem l'esdevenimnet click, fent:
- Seleccionem l'element a eliminar amb id "p2":
eliminar = document.getElementById("p2");
- Eliminem l'element fill del pare id="div1" amb removeChild
document.getElementById("div1").removeChild(eliminar);
Substituir elements HTML¶
Amb el mètode replaceChild
podem reemplaçar un node fill d'un element per un altre node.
Exemple Substutuir un element
Tenim aquest resultat:
<button id="btn">replaceChild()</button>
<div id="div1">
<p id="p1">Primer paràgraf</p>
<p id="p2">Segon paràgraf</p>
</div>
<script>
document.getElementById("btn").onclick = function () {
var nou = document.createElement("p");
nou.innerHTML = "Paràgraf nou";
substituir = document.getElementById("p1");
document.getElementById("div1").replaceChild(nou, substituir);
}
</script>
Tenim un codi HTML
amb div i paràgrafs amb un id="p1" ...
a dins
Tenim un botó <button id="btn">replaceChild()</button>
que programem l'esdevenimnet click, fent:
- Creem un nou element:
var nou = document.createElement("p");
- Assignem el valor innerHTML:
nou.innerHTML = "Paràgraf nou";
- Seleccionem l'elemnt a substituir:
substituir = document.getElementById("p1");
- Substituim l'element fill del pare id="div1" amb replaceChild
document.getElementById("div1").replaceChild(nou, substituir);
Atenció
Si el node nou és un element ja existent al DOM enlloc d'un node nou, es desplaçarà de la seva nova posició i substituirà al node vell.
Gestió d'esdeveniments¶
Esdeveniment¶
Un esdeveniment és un mecanisme que desencadena l'execució de codi quan té lloc una acció determinada. Cada acció té associada un gestor (handler) que fa de lligam entre un element HTML i la pròpia acció.
Exemple: el gestor de l'esdeveniment click (fer clic) és onclick
Podem enllaçar esdeveniments a elements HTML utiltzant un d'aquests mecanismes:
Atributs HTML d'esdeveniment¶
Dins del codi HTML, a la declaració d'un element li assignem un atribut amb el nom de l'esdeveniment començat per on. El gestor de l'esdeveniment s'assigna en el moment de crear l'element HTML.
<button onclick="mostraData()">Calendari</button>
<script>
function mostraData() {
/// codi de la funció
}
</script>
Funcions d'esdeveniment¶
En un bloc de codi Javascript, assignant una funció al mètode corresponent de l'objecte. Localitzant la referència d'un element dins del DOM, li podem assignar una funció a l'esdeveniment:
Amb una funció amb nom
function mostraData() {
/// codi de la funció
}
document.getElementById("boto1").onclick = mostraData
Recordatori
Com que el que volem fer és assignar la funció a l'esdeveniment, i no pas executar-la immediatament, no s'ha de posar els ( ) després del nom de la funció en fer l'assignació
Amb una funció anònima
Si assignem una nova funció al mateix esdeveniment d'un element HTML, aquesta sobreescriu l'anterior i només s'executarà la darrera assignada:
function funcioUsuari1() { alert("S'executa la funció 1"); }
function funcioUsuari2() { alert("S'executa la funció 2"); }
document.getElementById("boto1").onclick = funcioUsuari1;
document.getElementById("boto1").onclick = funcioUsuari2;
La funció addEventListener¶
En un bloc de codi Javascript, utilitzant el mètode genèric addEventListener
de l'objecte.
Localitzant la referència d'un element i assignant-li amb el mètode addEventListener
.
En aquest cas, el nom de l'esdeveniment no porta el prefix on
.
Amb una funció amb nom
function funcioUsuari() {
/// codi de la funció
}
document.getElementById("boto1").addEventListener( "click", funcioUsuari );
Amb una funció anònima
document.getElementById("boto1").addEventListener( "click", function () {
/// codi de la funció
} );
Amb aquesta sintaxi s'admet assignar més d'una funció al mateix esdeveniment. En aquest cas, s'executaran totes en produir-se l'esdeveniment i en l'ordre en que s'han assignat a l'element:
function funcioUsuari1() { alert("S'executa la funció 1"); }
function funcioUsuari2() { alert("S'executa la funció 2"); }
document.getElementById("boto1").addEventListener( "click", funcioUsuari1 );
document.getElementById("boto1").addEventListener( "click", funcioUsuari2 );
Desvincular esdeveniments¶
Podem desvincular completament un esdeveniment d'un element assignant-li com a funció el valor undefined.
Si els hem vinculat amb addEventListener
, podem desvincular esdeveniments selectivament amb removeEventListener
.
Només podem fer-lo amb les funcions amb nom.
// Assignem una funció a l'esdeveniment click
document.getElementById("boto1").addEventListener( "click", funcioUsuari );
// Desvinculem la funció i ja no s'executarà en fer clic
document.getElementById("boto1").removeEventListener( "click", funcioUsuari );
Tipus d'esdeveniments¶
Hi ha molts tipus d'esdeveniments, cadascun vinculat a un grup d'accions relacionat. Els més comuns són els de ratolí, teclat, pàgina i formulari, però també n'hi ha per multimèdia, animacions, portapapers, etc.
La llista completa es pot trobar a la pàgina de referència de la MDN.
Els més habituals són els següents:
Esdeveniments ratoli¶
Són aquells que reaccionen quan es fa alguna acció amb el punter o amb els botons del ratolí.
Tingueu en compte que...
Tot i que habitualment el botó principal és l'esquerre i el botó de menú contextual el dret, l'usuari els pot haver canviat a la seva configuració, per exemple si és esquerrà.
onclick
Detecta quan fem un clic amb el botó principal del ratolí sobre l'element que té l'esdeveniment associat.
La funció de retorn porta implícit un paràmetre que ens dóna informació de l'esdeveniment que s'ha produït. Aquest paràmetre és opcional, i normalment s'anomena e per indicar que correspon a un esdeveniment (event), tot i que el nom és arbitrari.
Si el botó té un comportament per defecte associat, el podem cancel·lar invocant el mètode preventDefault d'aquest paràmetre.
ondblclick
Detecta quan fem doble clic amb el botó principal del ratolí sobre l'element que té l'esdeveniment associat.
Igual que amb onclick, la funció de retorn porta implícit un paràmetre que ens dóna informació de l'esdeveniment que s'ha produït.
Tingueu en compte que...
En cas que un element tingui associats alhora els esdeveniments click
i dblclick
, en fer doble click es processaran tots dos, primer el click
i després el dblclick
.
oncontextmenu
Detecta quan fem clic amb el botó de menú contextual del ratolí sobre l'element que té l'esdeveniment associat.
La funció de retorn porta implícit un paràmetre que ens dóna informació de l'esdeveniment que s'ha produït. Aquest paràmetre és opcional, i normalment s'anomena e per indicar que correspon a un esdeveniment (event), tot i que el nom és arbitrari.
Ens pot ser útil per evitar que es desplegui el menú contextual en fer clic amb el botó invocant el mètode preventDefault d'aquest paràmetre.
document.getElementById( "element" ).oncontextmenu = function ( e )
{
// Evitem que s'obri el menú contextual
e.preventDefault();
...
}
Exemple Click
<html>
<head>
<meta charset="UTF-8">
<title>Esdeveniments del ratolí</title>
<style>
body {
margin: 0
}
.zonaTest {
width: 100%;
height: 80px;
border: 1px solid black;
background-color: deepskyblue;
padding: 4px;
box-sizing: border-box;
font-family: monospace;
user-select: none;
}
</style>
</head>
<body>
<div class="zonaTest" id="zona1">Prova de fer clic o doble clic</div>
<script>
botons = ["principal", "central", "secundari"];
document.getElementById("zona1").onclick = function (event) {
this.innerHTML = "Has fet clic amb el botó " + botons[event.button];
}
document.getElementById("zona1").oncontextmenu = function (event) {
event.preventDefault();
this.innerHTML = "Has fet clic amb el botó " + botons[event.button];
}
document.getElementById("zona1").ondblclick = function () {
this.innerHTML = "Has fet doble clic amb el botó " + botons[event.button];
}
</script>
</body>
</html>
onmousedown | onmouseup
Detecten el moment en que es prem (mousedown
) o es deixa anar (mouseup
) qualsevol botó del ratolí sobre l'element que té l'esdeveniment corresponent associat.
La funció de retorn porta implícit un paràmetre que ens dóna informació de l'esdeveniment que s'ha produït.
Aquest paràmetre és opcional, i normalment s'anomena e per indicar que correspon a un esdeveniment (event), tot i que el nom és arbitrari.
Podem determinar amb quin botó s'ha fet clic llegint la propietat button de l'esdeveniment de retorn:
valor | botó |
---|---|
0 | botó principal |
1 | rodeta o botó central |
2 | botó de menú contextual |
Ordre dels esdeveniments en fer clic
Si fem doble clic sobre un element l'ordre en que es disparen els diferents esdeveniments és: 1. onmousedown 2. onmouseup 3. onclick 4. ondblclick
L'esdeveniment onclick
només té lloc per un element si ha rebut just abans un onmousedown
seguit d'un onmouseup
.
document.getElementById( "element" ).onmousedown = function (e) {
this.innerHTML = "Has pressionat el botó " + e.button;
}
Exemple OnMouseDown
<html>
<head>
<meta charset="UTF-8">
<title>Esdeveniments del ratolí</title>
<style>
body {
margin: 0
}
.zonaTest {
width: 100%;
height: 80px;
border: 1px solid black;
background-color: deepskyblue;
padding: 4px;
box-sizing: border-box;
font-family: monospace;
user-select: none;
}
</style>
</head>
<body>
<div class="zonaTest" id="zona1">Prova de fer clic</div>
<script>
botons = ["principal", "central", "secundari"];
document.getElementById("zona1").onmousedown = function (event) {
this.innerHTML = "Has pressionat el botó " + botons[event.button] + " (" + event.button + ")";
}
document.getElementById("zona1").onmouseup = function (event) {
this.innerHTML = "Has deixat anar el botó " + botons[event.button] + " (" + event.button + ")";
}
document.getElementById("zona1").oncontextmenu = function (event) {
event.preventDefault();
}
</script>
</body>
</html>
onmousemove
Detecta el moment en que s'està movent el punter del ratolí pel damunt de l'element que té l'esdeveniment associat. Podem determinar les coordenades del punter llegint les següents propietats de l'esdeveniment de retorn:
eix X | eix Y | posició |
---|---|---|
clientX | clientY | respecte a l'element |
pageX | pageY | respecte a la pàgina |
screenX | screenY | respecte a la pantalla (àrea visible) |
document.getElementById("element").onmousemove = function (e) {
this.innerHTML = "Coordenades: " + e.clientX + ", " + e.clientY;
}
Exemple OnMouseMove
``` html
<head>
<meta charset="UTF-8">
<title>Esdeveniments del ratolí</title>
<style>
body {
margin: 0
}
.zonaTest {
width: 100%;
height: 80px;
border: 1px solid black;
background-color: deepskyblue;
padding: 4px;
box-sizing: border-box;
font-family: monospace;
user-select: none;
}
</style>
</head>
<body>
<div class="zonaTest" id="zona1">Prova de moure el punter aquí dins</div>
<script>
botons = ["principal", "central", "secundari"];
document.getElementById("zona1").onmousemove = function (event) {
this.innerHTML = "El punter està passant per les coordenades " + event.clientX + ":" + event.clientY;
}
</script>
</body>
</html>
onmouseover | onmouseout
Detecten el moment en que el punter de ratolí es situa sobre o surt de sobre l'element que té l'esdeveniment corresponent associat.
document.getElementById( "element" ).onmouseover = function () {...}
document.getElementById( "element" ).onmouseout = function () {...}
Els paràmetres target
i relatedTarget
ens donen informació d'on provenia el punter i a on ha anat a parar:
Per mouseover
event.target – És l'element al qual s'acosta el punter
event.relatedTarget – És l'element d'on prové el punter (passem de relatedTarget
a target
)
Per mouseout
event.target – És l'element que el punter ha abandonat
event.relatedTarget – És l'element que queda sota el punter un cop abandonat l'anterior (passem de target
a relatedTarget
).
document.getElementById(...).onmouseover = function (e) {
let elementActual = e.target
let elementAnterior = e.relatedTarget
}
document.getElementById(...).onmouseout = function (e) {
let elementAbandonat = e.target
let elementActual = e.relatedTarget
}
Exemple OnMouseOver
<html>
<head>
<meta charset="UTF-8">
<title>Esdeveniments del ratolí</title>
<style>
body {
margin: 0
}
.zonaTest {
width: 100%;
height: 80px;
border: 1px solid black;
background-color: deepskyblue;
padding: 2em;
box-sizing: border-box;
font-family: monospace;
user-select: none;
}
#blocTaronja {
border: 1px dashed black;
background-color: orange;
padding: 2em;
}
</style>
</head>
<body>
<div class="zonaTest" id="blocBlau">
<div id="blocTaronja">Prova de fer entrar i sortir el punter</div>
</div>
<script>
botons = ["principal", "central", "secundari"];
document.getElementById("blocTaronja").onmouseover = function (e) {
this.innerHTML = `El punter ha entrat a "${e.target.id}" i abans era a "${e.relatedTarget.id}"`
}
document.getElementById("blocTaronja").onmouseout = function (e) {
this.innerHTML = `El punter ha sortit de "${e.target.id}" i ha entrat a "${e.relatedTarget.id}"`
}
</script>
</body>
</html>
Esdeveniments teclat¶
Són aquells que reaccionen en el moment de prémer una tecla del teclat.
Tots aquests esdeveniments reben com a paràmetre un objecte que conté propietats amb la informació relacionada a l'esdeveniment.
Cancel·lació de tecles
Si la funció associada a un esdeveniment retorna el valor false, la pulsació de tecla no s'envia al control corresponent. Per exemple, dins d'un control <input>
no s'escriuria el caràcter pressionat per l'usuari.
onkeydown
Detecta el moment en que es pressiona una tecla del teclat sobre l'element que té l'esdeveniment corresponent associat.
Podem determinar quina és la tecla premuda llegint les propietats key
o code
de l'esdeveniment de retorn.
Propietat | |
---|---|
key | Retorna un valor que representa un caràcter. Per exemple, retorna valors diferents per lletres majúscules i minúscules encara que s'escriguin amb la mateixa tecla, però el mateix valor per tecles del teclat repetides. |
code | Retorna un valor que representa una tecla física. Per exemple, retornarà sempre el mateix valor per tecles que tinguin múltiples funcions, com ara les del teclat numèric. |
altKey | Retorna un boolà que val true si s'estava prement la tecla Alt en generar l'esdeveniment |
ctrlKey | Retorna un boolà que val true si s'estava prement la tecla Ctrl en generar l'esdeveniment |
shiftKey | Retorna un boolà que val true si s'estava prement la tecla Shift en generar l'esdeveniment |
metaKey | Retorna un boolà que val true si s'estava prement la tecla Meta en generar l'esdeveniment |
Repetició de tecles
Si es manté la tecla pressionada, l'esdeveniment onkeydown
es va repetint contínuament.
document.getElementById( ... ).onkeydown = function ( e )
{
this.value = `Acabes de pressionar la tecla [${e.key}]`
return false;
}
Exemple OnKeyDown
<html>
<head>
<meta charset="UTF-8">
<title>Esdeveniments del teclat</title>
<style>
body {
margin: 0
}
.zonaTest {
width: 100%;
height: 80px;
border: 1px solid black;
background-color: deepskyblue;
padding: 4px;
}
</style>
<body>
<textarea class="zonaTest" id="zona1" spellcheck="false">Prem una tecla dins d'aquest espai</textarea>
<script>
document.getElementById("zona1").onkeydown = function (e) {
this.value = `Acabes de pressionar [${e.key}]\nEl codi de la tecla és [${e.code}]`
if (e.shiftKey && e.key != "Shift") this.value += "+[Shift]"
if (e.altKey && e.key != "Alt") this.value += "+[Alt]"
if (e.ctrlKey && e.key != "Control") this.value += "+[Ctrl]"
if (e.metaKey && e.key != "Meta") this.value += "+[Meta]"
return false;
}
</script>
</body>
</html>
Combinant onkeydown
i return false
a la funció d'esdeveniment, podem detectar tecles que no volem que es premin i filtar-les.
Per exemple en controls de text:
document.getElementById("nomUsuari").onkeydown = function ( e ) {
if ("1234567890".includes( e.key )) {
return false
}
}
Exemple Return False
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Esdeveniment oninput</title>
<style>
body {
font-family: Arial, Helvetica, sans-serif;
}
div {
font-weight: bold;
}
input {
font-size: 2em;
margin-bottom: 0.25em;
}
</style>
</head>
<body>
<input type="text" id="nomUsuari" autocomplete="off">
<div for="nomUsuari">* Aquí no es poden escriure números</div>
<script>
const $ = (e) => document.getElementById(e);
$("nomUsuari").onkeydown = function (e) {
if ("1234567890".includes(e.key)) {
return false
}
}
$("nomUsuari").focus()
</script>
</body>
</html>
Podem fer-ho també amb preventDefault
Si no volem encara finalitzar la funció amb un return
, podem aconseguir el mateix resultat amb aquest codi:
onkeyup
Detecta el moment en que es deixa anar una tecla del teclat sobre l'element que té l'esdeveniment corresponent associat.
Podem determinar quina és la tecla premuda llegint les propietats key
o code
de l'esdeveniment de retorn.
Propietat | |
---|---|
key | Retorna un valor que representa un caràcter. Per exemple, retorna valors diferents per lletres majúscules i minúscules encara que s'escriguin amb la mateixa tecla, però el mateix valor per tecles del teclat repetides. |
code | Retorna un valor que representa una tecla física. Per exemple, retornarà sempre el mateix valor per tecles que tinguin múltiples funcions, com ara les del teclat numèric. |
altKey | Retorna un boolà que val true si s'estava prement la tecla Alt en generar l'esdeveniment |
ctrlKey | Retorna un boolà que val true si s'estava prement la tecla Ctrl en generar l'esdeveniment |
shiftKey | Retorna un boolà que val true si s'estava prement la tecla Shift en generar l'esdeveniment |
metaKey | Retorna un boolà que val true si s'estava prement la tecla Meta en generar l'esdeveniment |
Repetició de tecles
Si es manté la tecla pressionada, l'esdeveniment onkeydown
es va repetint contínuament.
document.getElementById( ... ).onkeyup = function ( e )
{
this.value = `Acabes de deixar anar la tecla [${e.key}]`
return false;
}
Exemple OnKeyUp
<html>
<head>
<meta charset="UTF-8">
<title>Esdeveniments del teclat</title>
<style>
body {
margin: 0
}
.zonaTest {
width: 100%;
height: 80px;
border: 1px solid black;
background-color: deepskyblue;
padding: 4px;
}
</style>
<body>
<textarea class="zonaTest" id="zona1" spellcheck="false">Prem una tecla dins d'aquest espai</textarea>
<script>
document.getElementById("zona1").onkeydown = function (e) {
return false;
}
document.getElementById("zona1").onkeyup = function (e) {
this.value = `Acabes de deixar anar [${e.key}]\nEl codi de la tecla és [${e.code}]`
if (e.shiftKey && e.key != "Shift") this.value += "+[Shift]"
if (e.altKey && e.key != "Alt") this.value += "+[Alt]"
if (e.ctrlKey && e.key != "Control") this.value += "+[Ctrl]"
if (e.metaKey && e.key != "Meta") this.value += "+[Meta]"
return false;
}
</script>
</body>
</html>
Esdeveniments pagina¶
Tenen lloc quan hi ha canvis a la finestra del navegador o bé interacció de l'usuari amb elements d'un formulari
onload
Té lloc quan l'element associat acaba de carregar-se completament.
S'utilitza principalment amb l'element <body>
però és útil per qualsevol element que pugui tenir un temps de càrrega variable (per exemple <img>
)
Exemple OnLoad
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<style>
body {
font-family: Arial, Helvetica, sans-serif;
}
/* Animació per el text d'espera de càrrega */
@keyframes salt {
0% {
top: 0px;
}
100% {
top: -10px;
}
}
/* Contenidor de la foto, el necessitem perquè també contingui el text d'espera */
.foto {
position: relative;
display: inline-block;
width: 320px;
height: 240px;
border: 1px solid black;
margin: 1em;
}
/* Text d'espera */
.foto span {
position: absolute;
font-size: 1.5em;
font-weight: bold;
margin: 3em;
animation: salt 1s linear infinite alternate;
}
/* Imatge a mostrar després de la càrrega */
.foto img {
position: absolute;
width: 320px;
height: 240px;
}
/* Estat inicial de la imatge = invisible */
#img1 {
opacity: 0;
transition: opacity 1s;
}
</style>
</head>
<body>
<!-- Primera imatge, amb el text d'espera i sense atribut SRC -->
<div class="foto" id="foto1">
<span>CARREGANT...</span>
<img id="img1" />
</div>
<!-- Segona imatge, fa la càrrega normal -->
<div class="foto" id="foto2">
<img id="img2" src="./media/forest.jpg" />
</div>
<script>
const $ = (e) => document.getElementById(e);
// Assignem un valor a SRC per inciar la càrrega
$("img1").src = "./media/forest.jpg";
// Event que te lloc quan la imatge ha acabat de carregar
$("img1").onload = function () {
// Fem la imatge visible només quan ja està completament carregada
$("img1").style.opacity = 1;
};
</script>
</body>
</html>
onbeforeunload
S'aplica a l'objecte window
i s'executa just abans de tancar o recarregar la pàgina i quan el document encara és visible.
La seva utilitat principal és fer aparèixer un diàleg de confirmació demanant a l'usuari si realment vol tancar la pàgina, per exemple quan hi ha dades que potser encara no s'han guardat, evitant així que es perdin per error.
Per fer aparèixer el quadre de diàleg és necessari executar el mètode preventDefault()
de l'objecte.
Exemple Click
<!DOCTYPE html>
<html lang="ca">
<head>
<meta charset="UTF-8">
<title>Esdeveniment onbeforeunload</title>
</head>
<body>
<input type="checkbox" id="confirma">
<label for="confirma">Confirma abans de sortir</label>
<script>
const $ = (e) => document.getElementById(e);
window.onbeforeunload = function (e) {
if ($("confirma").checked) {
e.preventDefault()
}
}
</script>
</body>
</html>
onresize
Té lloc quan es modifiquen les dimensions de l'element associat. És útil per forçar el redibuixat d'elements de la pàgina quan canvia la mida de la finestra del navegador.
onscroll
Té lloc quan es mou la barra de desplaçament (en fer scroll) de l'element associat.
Amb les propietats scrollTop
i scrollLeft
podem llegir i modificar el seu desplaçament en píxels, fins i tot encara que les barres de desplaçament estiguin ocultes (overflow: hidden
).
Exemple OnScroll
<!DOCTYPE html>
<html lang="ca">
<head>
<meta charset="UTF-8">
<title>Esdeveniment onscroll</title>
<style>
body {
font-family: 'Courier New', Courier, monospace;
font-weight: bold;
}
.scroll {
height: 600px;
width: 300px;
overflow: auto;
display: inline-block;
margin: 1em;
}
</style>
</head>
<body>
<div>
<div class="scroll" id="scrollDiv1"></div>
<div class="scroll" id="scrollDiv2"></div>
</div>
<div id="posicio"></div>
<script>
const $ = (e) => document.getElementById(e);
for (let i = 1; i <= 150; i++) {
$("scrollDiv1").innerHTML += `Línia ${i}<br>`
}
$("scrollDiv2").innerHTML += $("scrollDiv1").innerHTML
$("scrollDiv1").onscroll = function (e) {
let posY = this.scrollTop
let percent = Math.trunc(posY * 100 / (this.scrollHeight - parseInt(getComputedStyle(this).getPropertyValue("height"))))
$("scrollDiv2").scrollTo(0, posY)
$("posicio").innerHTML = `${posY}px (${percent}%)`
$("scrollDiv2").style.backgroundColor = `rgb(0%,${100 - percent}%,0%)`
$("scrollDiv2").style.color = `rgb(0%,${percent}%,0%)`
}
$("scrollDiv1").onscroll()
</script>
</body>
</html>
Per llegir la posició de scroll de l'objecte window ho hem de fer amb les propietats scrollX
i scrollY
.
onfocus | onblur
Detecten el moment en que l'element que té l'esdeveniment corresponent associat guanya (focus) o perd (blur) el focus, per exemple quan s'ha situat o tret el cursor d'edició de text d'un control <input>
o <textarea>
.
onchange
Té lloc quan canvia el valor d'un element <input>
, <select>
o <textarea>
en aquests casos:
- Quan un element
<input>
o<textarea>
perd el focus i el seu contingut havia canviat - Quan es marca o desmarca un element
<input type="checkbox">
- Quan es marca un element
<input type="radio">
, però no quan es desmarca - Quan l'usuari selecciona un valor d'un desplegable
<select>
, una data d'un<input type="date">
o un fitxer d'un<input type="file">
oninput
Té lloc quan canvia el valor d'un element <input>
, <select>
o <textarea>
.
A diferència de l'esdeveniment onchange, detecta immediatament qualsevol canvi produït.
onselect
Detecta quan s'ha finalitzat de seleccionar text dins del control d'edició que tingui associat.
oncut | oncopy | onpaste
Detecta quan l'usuari talla / copia / enganxa el contingut d'un element al porta-retalls.
Són útils per poder cancel·lar el comportament per defecte utilitzant el mètode preventDefault
.
Cancelació d'esdeveniments¶
Podem impedir que el navegador executi el comportament per defecte d'un esdeveniment si a la funció associada cridem el mètode preventDefault
del paràmetre associat a l'esdeveniment.
Exemple Cancelació d'esdeveniments
<html>
<head>
<meta charset="UTF-8">
<style>
body {
font-family: Arial, Helvetica, sans-serif;
}
div.bloc {
width: 100px;
height: 100px;
display: inline-block;
background-color: deepskyblue;
border: 1px solid black;
margin-right: 1em;
margin-bottom: 0.5em;
padding: 0.5em;
box-sizing: border-box;
}
</style>
</head>
<body>
<div class="bloc" id="espai1">AQUÍ SÍ</div>
<div class="bloc" id="espai2">AQUÍ NO</div>
<div>Prova d'obrir el menú contextual</div>
<script>
document.getElementById("espai2").oncontextmenu = function (e) {
e.preventDefault();
}
</script>
</body>
</html>
Usos habituals:
- cancel·lar el canvi d'URL en clicar un enllaç
- cancel·lar la visualització del menú contextual
- cancel·lar l'enviament d'un formulari en fer "submit"
Atenció
No tots els esdeveniments són cancel·lables. Podem comprovar si un esdeveniment ho és consultant la seva propietat cancelable.
Propagació
Aturar un esdeveniment amb preventDefault
NO cancel·la la seva propagació cap a altres esdeveniments.
Propagació d'esdeveniments (event bubbling)¶
Els esdeveniments que tenen lloc sobre un element, per defecte es propaguen a tots els seus antecessors (aquells dins dels quals estan continguts), començant pel més proper i fins al més allunyat.
Per exemple, en un cas com aquest:
En clicar sobre el div "div2", l'esdeveniment click
tindrà lloc dues vegades, primer per "div2" i just després per "div1" ja que és dins del que està contingut. Per tant s'executarà primer funcio2
i després funcio1
.
Perquè se'n diu event bubbling?
La propagació d'esdeveniments s'anomena "event bubbling" perquè es produeix d'abaix cap amunt, de manera similar a com una bombolla puja dins l'aigua fins arribar a la superfície, que seria l'últim element.
La propagació d'esdeveniments no sempre és desitjable, i la podem aturar invocant el mètode stopPropagation
del paràmetre associat a l'esdeveniment.
element.onmouseover = function (e) {
if ( checkbox.checked )
e.stopPropagation();
this.style.backgroundColor = "orange"
};
element.onclick = function (e) {
if ( checkbox.checked )
e.stopPropagation();
this.style.backgroundColor = "green"
};
Exemple Propagació (event bubbling)
<html>
<head>
<meta charset="UTF-8">
<style>
body {
font-family: Arial, Helvetica, sans-serif;
}
div {
border: 1px solid black;
background-color: lightgray;
padding: 10px;
display: inline-block;
cursor: pointer;
}
</style>
</head>
<body>
<div id="div1">div1
<div id="div2">div2
<div id="div3">div3
<div id="div4">div4
<div id="div5">div5
<div id="div6">div6
</div>
</div>
</div>
</div>
</div>
</div>
<p>
<input type="checkbox" onchange="atura=!atura" id="aturaPropagacio">
<label for="aturaPropagacio">stopPropagation</label>
</p>
<script>
const $ = e => document.getElementById(e)
var atura = false;
for (let i = 1; i <= 6; i++) {
$("div" + i).onmouseover = function (e) {
if (atura) e.stopPropagation();
this.style.backgroundColor = "orange"
};
$("div" + i).onmouseout = function (e) {
if (atura) e.stopPropagation();
this.style.backgroundColor = "lightgray"
};
$("div" + i).onclick = function (e) {
if (atura) e.stopPropagation();
this.style.backgroundColor = "green"
};
}
</script>
</body>
</html>
Exemples¶
Exemple Desplegable
Tenim aquest resultat:
<script>
// Funció que genera el desplegable
function generaDropdown() {
// obtenim el número de l'input
const numberInput = document.getElementById('numberInput').value;
const dropdown = document.getElementById('dropdown');
dropdown.innerHTML = ''; // Netejem el desplegable
// Creem les opcions del desplegable segons el número introduït
for (let i = 1; i <= numberInput; i++) {
const option = document.createElement('option');
option.value = i;
option.textContent = i;
dropdown.appendChild(option); // Afegim l'opció al desplegable
}
}
</script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Generar Desplegable</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
font-size: 14px;
}
label, button, select {
display: block;
margin-top: 5px;
}
input, button, select {
padding: 5px;
}
button {
background-color: #4CAF50;
color: white;
border: none;
cursor: pointer;
}
button:hover {
background-color: #45a049;
}
select, input{
width: 100px;
}
</style>
</head>
<body>
<label for="numberInput">Introdueix un número:</label>
<input type="number" id="numberInput" min="1">
<button onclick="generaDropdown()">Generar Desplegable</button>
<br><br>
<select id="dropdown"></select>
<script>
// Funció que genera el desplegable
function generaDropdown() {
const numberInput = document.getElementById('numberInput').value;
const dropdown = document.getElementById('dropdown');
dropdown.innerHTML = ''; // Netejem el desplegable
// Creem les opcions del desplegable segons el número introduït
for (let i = 1; i <= numberInput; i++) {
const option = document.createElement('option');
option.value = i;
option.textContent = i;
dropdown.appendChild(option); // Afegim l'opció al desplegable
}
}
</script>
</body>
</html>
Tenim un input number per demanar un número.
Tenim un botó amb onclick
a la funció generaDropDown()
Tenim un select
per mostrar el desplegable
Tenim una funció generaDropDown()
:
- obtenim el valor numèric entrat a l'input
- netejem les opcions del select, per si n'hi havia alguna d'anterior
- recorrem el bucle fins al valor:
- creem un element option
- afegim aquest element a les opcions del select amb
dropdown.appendChild(option);
p2-s3-ex9-Desplegable.html p2-s3-ex9-Desplegable.html :fontawesome-solid-download:
Exemple Bombolles
Tenim aquest resultat:
<script>
// Creem el so
pop = new Audio("media/pop.mp3")
// Creem el botó
boto = document.createElement("button")
boto.innerHTML = "Nova bombolla"
boto.onclick = creaBombolla
document.body.appendChild(boto)
// Funció que crea una bombolla individual
function creaBombolla() {
// Triem aleatòriament la mida, posició i transparència de la bombolla
var m = Math.floor(Math.random() * 250) + 50
var x = Math.floor(Math.random() * (window.innerWidth - m))
var y = window.innerHeight
// Transparència = valor aletori entre 0.5 i 1.0 amb només 2 decimals
var t = Math.floor(((Math.random() * 0.5) + 0.5) * 100) / 100
// Creem un div perquè sigui la nova bombolla
var bombolla = document.createElement("div")
// Li apliquem una classe i atributs CSS
bombolla.classList.add("bombolla")
bombolla.style.left = `${x}px`
bombolla.style.top = `${y}px`
bombolla.style.width = `${m}px`
bombolla.style.height = `${m}px`
bombolla.style.opacity = `${t}`
// Associem l'esdeveniment onclick per poder fer explotar la bombolla
bombolla.onclick = function () {
// Quan fem clic, eliminarem l'objecte del DOM i s'esborrarà automàticament
this.remove()
// So d'explotar la bombolla
pop.play()
}
// Afegim la bombolla acabada de crear al DOM com a filla de l'objecte body
document.body.appendChild(bombolla)
}
// Temporitzador per moure les bombolles
function actualitza() {
// Busquem tots els elements del DOM de la classe "bombolla"
llistaBombolles = document.getElementsByClassName("bombolla")
// Recorrem tots els elements
for (bombolla of llistaBombolles) {
// Guardem la coordenada Y i la mida
var top = parseInt(bombolla.style.top)
var height = parseInt(bombolla.style.height)
// Si la bombolla ha desaparegut per la part de dalt de la pantalla
if (top <= -height) {
bombolla.remove()
}
else {
// Altrament fem que la bombolla vagi pujant per la pantalla
bombolla.style.top = Math.floor(top - (height / 100)) + "px"
}
}
// De tant en tant fem aparèixer alguna bombolla aleatòriament
if (Math.random() * 1000 >= 950) creaBombolla()
// Preparar següent redibuixat de pantalla
requestAnimationFrame(actualitza)
}
// Iniciar animació
actualitza()
</script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bombolles</title>
<style>
body {
background-color: white;
overflow: hidden;
}
.bombolla {
background: radial-gradient(rgba(0,0,0,0),rgb(223, 80, 80));
border-radius: 50%;
position: absolute;
cursor: pointer;
}
</style>
</head>
<body>
<script>
// Creem el so
pop = new Audio("media/pop.mp3")
// Creem el botó
boto = document.createElement("button")
boto.innerHTML = "Nova bombolla"
boto.onclick = creaBombolla
document.body.appendChild(boto)
// Funció que crea una bombolla individual
function creaBombolla() {
// Triem aleatòriament la mida, posició i transparència de la bombolla
var m = Math.floor(Math.random() * 200) + 50
var x = Math.floor(Math.random() * (window.innerWidth - m))
var y = window.innerHeight
// Transparència = valor aletori entre 0.5 i 1.0 amb només 2 decimals
var t = Math.floor(((Math.random() * 0.5) + 0.5) * 100) / 100
// Creem un div perquè sigui la nova bombolla
var bombolla = document.createElement("div")
// Li apliquem una classe i atributs CSS
bombolla.classList.add("bombolla")
bombolla.style.left = `${x}px`
bombolla.style.top = `${y}px`
bombolla.style.width = `${m}px`
bombolla.style.height = `${m}px`
bombolla.style.opacity = `${t}`
// Associem l'esdeveniment onclick per poder fer explotar la bombolla
bombolla.onclick = function () {
// Quan fem clic, eliminarem l'objecte del DOM i s'esborrarà automàticament
this.remove()
// So d'explotar la bombolla
pop.play()
}
// Afegim la bombolla acabada de crear al DOM com a filla de l'objecte body
document.body.appendChild(bombolla)
}
// Temporitzador per moure les bombolles
function actualitza() {
// Busquem tots els elements del DOM de la classe "bombolla"
llistaBombolles = document.getElementsByClassName("bombolla")
// Recorrem tots els elements
for (bombolla of llistaBombolles) {
// Guardem la coordenada Y i la mida
var top = parseInt(bombolla.style.top)
var height = parseInt(bombolla.style.height)
// Si la bombolla ha desaparegut per la part de dalt de la pantalla
if (top <= -height) {
bombolla.remove()
}
else {
// Altrament fem que la bombolla vagi pujant per la pantalla
bombolla.style.top = Math.floor(top - (height / 100)) + "px"
}
}
// De tant en tant fem aparèixer alguna bombolla aleatòriament
if (Math.random() * 1000 >= 990) creaBombolla()
// Preparar següent redibuixat de pantalla
requestAnimationFrame(actualitza)
}
// Iniciar animació
actualitza()
</script>
</body>
</html>
Tenim un codi HTML
simple amb un botó i un javascript
per crear elements bombolla.
També programarem un esdeveniment per capturar el click i rebentar bombolles.
Tenim dues funcions javascript
:
-
actualitza():
- obté totes les bombolles per
ClassName
- recorrem les bombolles, per cada una:
- obtenim la posició
- l'eliminem si surt per dalt
- pugem la posició de la bombolla
- de tant en tant en fem apareixer alguna més.
- obté totes les bombolles per
-
creaBombolla():
- calcula la mida, posició x,y i transparència
- aplica l'estil "bombolla"
- aplica les posicions a la pantalla
- assigna l'esdevenimnet
onclick
amb una funció anònima per eliminar la bombolla - aplica el só
- crea l'element al DOM.
La funció requestAnimationFrame(actualitza)
activa la renderització de la interfície de manera automàtica. Optimitzada per a una visió fluïda.
Ara només ens cal fer una primera crida a:
Info
Més endavant aprofundirem amb les funcions !
p2-s3-ex9-Bombolles.html p2-s3-ex9-Bombolles.html :fontawesome-solid-download:
Exemple Esborrem segons estils
Tenim aquest resultat:
<script>
// Funció que posa en pantalla el nombre de caixes indicat
// utilitzant el DOM per crear-les
function posaCaixes( num )
{
// Array de noms de colors que utilitzarem per pintar les caixes
var colors = ["vermell", "verd", "blau", "groc"];
// Iteració segons el nombre de caixes
for ( var i = 1; i <= num; i++ )
{
// Creem l'element de tipus <div>
var caixa = document.createElement( "div" );
// Li assignem el full d'estil corresponent a una caixa
// i un segon estil amb un color de l'array de noms de colors
caixa.classList.add( "caixa", colors[ i % 4 ] );
// Afegim el nom element al document com a fill de l'element "contenidor"
document.getElementById( "contenidor" ).appendChild( caixa );
}
}
// Funció que esborra tots els elements del document que tenen el nom de classe
// que s'ha passat com a paràmetre
function esborrarColor( color )
{
// Localitzem tots els elements amb el nom de classe donat
llista = document.getElementsByClassName( color );
// Mentre quedin elements a la llista (array)
while(llista[ 0 ] != null)
{
// eliminem el primer element de la llista
llista[ 0 ].remove();
}
}
// Inicialment pintem 50 caixes
posaCaixes( 50 );
</script>
<html>
<head>
<meta charset="utf-8">
<title>DOM</title>
<style>
#contenidor {
width: 620px;
}
.caixa {
width: 50px;
height: 50px;
border: 2px solid black;
margin: 4px;
float: left;
}
.vermell { background-color: red; }
.verd { background-color: green; }
.blau { background-color: blue; }
.groc { background-color: yellow; }
</style>
</head>
<body>
<button onClick="esborrarColor('vermell');">Esborrar vermells</button>
<button onClick="esborrarColor('verd');">Esborrar verds</button>
<button onClick="esborrarColor('blau');">Esborrar blaus</button>
<button onClick="esborrarColor('groc');">Esborrar grocs</button>
<button onClick="esborrarColor('caixa');">Esborrar tot</button>
<button onClick="posaCaixes(10);">Afegir-ne 10</button>
<div id="contenidor"></div>
<script>
// Funció que posa en pantalla el nombre de caixes indicat
// utilitzant el DOM per crear-les
function posaCaixes( num )
{
// Array de noms de colors que utilitzarem per pintar les caixes
var colors = ["vermell", "verd", "blau", "groc"];
// Iteració segons el nombre de caixes
for ( var i = 1; i <= num; i++ )
{
// Creem l'element de tipus <div>
var caixa = document.createElement( "div" );
// Li assignem el full d'estil corresponent a una caixa
// i un segon estil amb un color de l'array de noms de colors
caixa.classList.add( "caixa", colors[ i % 4 ] );
// Afegim el nom element al document com a fill de l'element "contenidor"
document.getElementById( "contenidor" ).appendChild( caixa );
}
}
// Funció que esborra tots els elements del document que tenen el nom de classe
// que s'ha passat com a paràmetre
function esborrarColor( color )
{
// Localitzem tots els elements amb el nom de classe donat
llista = document.getElementsByClassName( color );
// Mentre quedin elements a la llista (array)
while(llista[ 0 ] != null)
{
// eliminem el primer element de la llista
// Codi compatible amb tots els navegadors però més llarg, esborrem a través del seu element pare
// llista[ 0 ].parentNode.removeChild( llista[ 0 ] );
// Codi no compatible amb Internet Explorer però més compacte, esborrem a través del propi element
// (https://caniuse.com/#search=remove)
llista[ 0 ].remove();
}
}
// Inicialment pintem 50 caixes
posaCaixes( 50 );
</script>
</body>
</html>
Tenim uns botons amb onclick
a la funció esborrarColor()
Tenim un div
contenidor de més divs
quadres de colors.
Tenim una funció posaCaixes(num)
:
- Definim un array de colors.
- Fem un bucle fins a num
- creem un element
div
- li assignem un estil "caixa"
- l'afegim al
div
contenidor
- creem un element
Tenim una funció esborrarColor( color )
- seleccionem els elements segons la classe color passat
- eliminem aquests elements en bucle
Finalment cridem a la funció de posar-ne 50
arrays
Un array és una estructura repetitiva de dades.
Més endavant treballarem els arrays.
p2-s3-ex9-Esborrem.html p2-s3-ex9-Esborrem.html :fontawesome-solid-download: