Descobrindo dataset! Dados próprios no seu elemento DOM
Em um review sobre Funcionalidade beta no Computaria, o Camilo Micheletto fez um comentário sobre o como eu estava manipulando o DOM:
data-
tem getters e setters em datasetÉ interessante se você tiver mais de uma propriedade
data-*
const attrAtual = foguete.dataset.beta; // Resto inalterado
https://developer.mozilla.org/en-US/docs/Web/HTML/How_to/Use_data_attributes
E, bem, se o mestre falou, vamos seguir, né?
Eu descobri que o uso dos atributos data-*
deveriam ser priorizados perante
atributos sem nome durante um experimento enquanto eu fazia um joguinho em
react usando TypeScript. Usei o campo do elemento para guardar o estado, e na
época usei algo como <td state="occupied"></td>
. E o compilador do TS
reclamou disso no meu TSX, e foi aí que eu descobri o uso do data-*
.
Desde então, tenho usado nas tags o atributo data-*
para guardar o estado do
elemento em questão. E tenho usado bastante disso para manipular a exibição via
seletores CSS (vide
Funcionalidade beta no Computaria e
Carrossel em markdown no GitHub).
Mudanças para o beta
A maior parte das mudanças foram diretas ao ponto:
-const rotate = Number(elementRotate.getAttribute("data-rotation"))
+const rotate = Number(elementRotate.dataset.rotation)
const newRotate = (rotate + delta)%4
-elementRotate.setAttribute("data-rotation", newRotate)
+elementRotate.dataset.rotation = newRotate
Tanto no post, nos scripts do post, como também no meta.js
. MAS… teve um
caso em particular:
betaElement.setAttribute("data-beta-applied", "beta-applied")
Como lidar com o atributo <tag data-beta-applied="beta-applied">
? Pois bem,
experimentei com o elemento.dataset.*
para ver como lidava com isso.
Uma descoberta foi que eu posso criar novos atributos on-the-fly:
const elemento = ...;
elemento.dataset.marm = "ota"
Posso acessar atributos não definidos:
const elemento = ...;
console.log(elemento.dataset.undef)
Para depurar isso de modo visual, que tal usar um dedo de CSS? Usei para
preencher visualmente o pseudoelemento ::after
com o conteúdo
attr(data-marmota)
:
Vê ali:
E abaixo o código para exibir isso:
<style>
.show::after {
content: attr(data-marmota);
}
</style>
<script>
function incrementaMarmota() {
const elemento = document.getElementById("marmotoso")
const marmotaIncrementada = Number(elemento.dataset.marmota) + 1
elemento.dataset.marmota = marmotaIncrementada
}
</script>
<div id="marmotoso" data-marmota="0" class="show">Vê ali: </div>
<button onclick="incrementaMarmota()" md=1>Incrementa o `data-marmota`</button>
Beleza, mas ainda não consegui acessar o data-beta-applied
. Pensei que eu
iria acessar usando elemento.dataset["nome-com-tracos"]
, mas isso não deu
certo.
Então decidi usar o poder do REPL: adicionei no HTML um atributo com traços e pedi o auto-complete do dataset dele. No meu caso, peguei um parágrafo arbitrário e customizei ele:
-<p>Texto</p>
+<p id="aqui" data-marm-ota="123">Texto</p>
Peguei o elemento pelo ID dele e imprimi o dataset
:
const aqui = document.getElementById("aqui")
//undefined
aqui.dataset
//DOMStringMap { marmOta → "123" }
Massa! Isso significa que agora eu consigo acessar o elemento! O que era
data-marm-ota
virou camelCase!
E… eu posso criar atributo assim?
aqui.dataset.outroTeste = "valor"
//"valor"
aqui
//<p id="aqui" data-marm-ota="123" data-outro-teste="valor">Texto</p>
Legal! e… se eu criar o atributo com camel case, como acesso ele no dataset?
<p id="aqui" data-marm-ota="123" data-outro-teste="valor" data-camelCase="abcDef">Texto</p>
Com resultado:
aqui
//<p id="aqui" data-marm-ota="123" data-outro-teste="valor" data-camelcase="abcDef">
Então, usar camelCase nos atributos não tem efeito. Tal qual o onClick
.
Para fazer a alteração que estava restando no meta.js
, só usar o camelCase:
-if (betaElement.hasAttribute("href") && betaElement.getAttribute("data-beta-applied") != "beta-applied") {
- betaElement.setAttribute("data-beta-applied", "beta-applied")
+if (betaElement.hasAttribute("href") && betaElement.dataset.betaApplied != "beta-applied") {
+ betaElement.dataset.betaApplied = "beta-applied"
betaElement.href += window.location.search
}
Girando o carrossel
Para o post do Carrossel em markdown no GitHub, eu coloquei lá para depurar uma manipulação de dataset:
function toggleHighlightCarrosselFalho() {
const spanCarrosselFalho = document.getElementById("carrossel-falho")
if (spanCarrosselFalho.hasAttribute("data-highlight")) {
spanCarrosselFalho.removeAttribute("data-highlight")
} else {
spanCarrosselFalho.setAttribute("data-highlight", "true")
}
}
Seria tudo simples se não fosse o removeAttribute
…
Ok, o que podemos fazer com isso? Setar como nulo? Não adianta, o valor no atributo é convertido em string:
aqui
//<p id="aqui" data-marm-ota="123" data-outro-teste="valor">Texto</p>
aqui.dataset.marmOta = null
//<p id="aqui" data-marm-ota="null" data-outro-teste="valor">Texto</p>
aqui.dataset.marmOta
//"null"
E se eu usar o
delete
?
Esse operador eu quase nunca uso. E o teste foi positivo:
aqui.dataset.marmOta = null
//<p id="aqui" data-marm-ota="null" data-outro-teste="valor">Texto</p>
delete aqui.dataset.marmOta
//<p id="aqui" data-outro-teste="valor">Texto</p>
Ou seja, eu posso reescrever aquela função acima totalmente orientado a dataset agora!
function toggleHighlightCarrosselFalho() {
const spanCarrosselFalho = document.getElementById("carrossel-falho")
if (!!spanCarrosselFalho.dataset.highlight) {
delete spanCarrosselFalho.dataset.highlight
} else {
spanCarrosselFalho.dataset.highlight = "true"
}
}
Focando agora no diff:
function toggleHighlightCarrosselFalho() {
const spanCarrosselFalho = document.getElementById("carrossel-falho")
- if (spanCarrosselFalho.hasAttribute("data-highlight")) {
- spanCarrosselFalho.removeAttribute("data-highlight")
+ if (!!spanCarrosselFalho.dataset.highlight) {
+ delete spanCarrosselFalho.dataset.highlight
} else {
- spanCarrosselFalho.setAttribute("data-highlight", "true")
+ spanCarrosselFalho.dataset.highlight = "true"
}
}