5
respostas

[Dúvida] Posição do botão

Tentei colocar o botão no início da syntax, porém ele perde a ação de clicar, não funciona. Como faço para fazer o botão ficar antes do número de quantidade e ainda assim funcionar deletando o elemento?

Screenshot com o exemplo

5 respostas

Oi, Bruno... tudo bem?

Poderia por favor postar o código completo (HTML, CSS e JS) para que possamos analisar e entender como ele está estruturado? Assim teremos uma visão mais clara sobre o problema e podemos trabalhar numa solução específica para o que você precisa executar.

Fico no aguardo,

:)

Segue o HTML:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Mochila de viagem</title>
    <link rel="stylesheet" href="css/style.css">

    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" rel="stylesheet"> 
</head>
<body>
    <main class="conteudo">
        <div class="principal">
            <div class="mochila"></div>
            <form action="" class="adicionar" id="novoItem">
                <label for="item">Nome</label>
                <input type="text" name="nome" id="nome">
                <label for="item">Quantidade</label>
                <input type="number" name="quantidade" id="quantidade">
                <input type="submit" value="Adicionar" class="cadastrar">
            </form>
        </div>

        <ul class="lista" id="lista">
            <!--aqui vão os itens criados com JS-->
        </ul>
    </main>
    <script src="/main.js"></script>
</body>
</html>

JS:

const form = document.getElementById('novoItem')
const lista = document.getElementById('lista')
const itens = JSON.parse(localStorage.getItem('itens')) || []

itens.forEach((elemento) => {
    criaElemento(elemento)
})

//cria ação para obter os dados enviados ao apertar o submit
form.addEventListener('submit', (evento) => {
    evento.preventDefault() //evita que o formulário execute uma ação padrão

    const nome = evento.target.elements['nome']
    const quantidade = evento.target.elements['quantidade']

    const existe = itens.find(elemento => elemento.Nome === nome.value)
    console.log(existe)

    //armazenar no local storage
    const itemAtual = {
        'Nome': nome.value,
        'Quantidade': quantidade.value
    }

    if (existe) {
        itemAtual.id = existe.id
        atualizaElemento(itemAtual)
        itens[itens.findIndex(elemento => elemento.id === existe.id)] = itemAtual
    } else {
        itemAtual.id = itens[itens.length - 1]? itens[itens.length-1].id + 1 : 0
        //obter os valores dos inputs e insere na função de criar elemento
        criaElemento(itemAtual)
        itens.push(itemAtual)
    }

    localStorage.setItem('itens', JSON.stringify(itens))

    //apaga o input depois de enviado os dados
    nome.value = ''
    quantidade.value = ''
})

//função para criar item
function criaElemento(item) {
    const novoItem = document.createElement('li')
    novoItem.classList.add('item')
    novoItem.appendChild(botaoDeleta(item.id))
    const numeroItem = document.createElement('strong')
    numeroItem.innerHTML = item.Quantidade
    numeroItem.dataset.id = item.id
    novoItem.append(numeroItem)
    novoItem.innerHTML += item.Nome

    lista.appendChild(novoItem)
}

function atualizaElemento(item){
    document.querySelector("[data-id='"+item.id+"']").innerHTML = item.Quantidade
}

function botaoDeleta(id) {
    const elementoBotao = document.createElement('button')
    elementoBotao.innerText = 'X'

    elementoBotao.addEventListener("click", function() {
        deletaElemento(this.parentNode, id)
    })

    return elementoBotao
}

function deletaElemento(tag, id){
    tag.remove()
    itens.splice(itens.findIndex(elemento => elemento.id === id), 1)
    localStorage.setItem('itens', JSON.stringify(itens))
}

O trecho que se encontra a inserção do botão é na função

//função para criar item
function criaElemento(item) {
    const novoItem = document.createElement('li')
    novoItem.classList.add('item')
    novoItem.appendChild(botaoDeleta(item.id))
    const numeroItem = document.createElement('strong')
    numeroItem.innerHTML = item.Quantidade
    numeroItem.dataset.id = item.id
    novoItem.append(numeroItem)
    novoItem.innerHTML += item.Nome

    lista.appendChild(novoItem)
}

CSS:

html {
    background: radial-gradient(farthest-corner at right top,#21A09D, #06182D);
    height: 100%;
    font-family: 'Roboto', cursive;
}

.conteudo {
    padding: 10vh 3em 0;
    display: flex;
    align-items: flex-start;
    gap: 2em;
}

.principal {
    flex-grow: 1;
    flex-basis: auto;
    min-width: 600px;
    padding: 4em 2em 2em;
    box-sizing: border-box;
    display: flex;
    background: #C5965F;
    border-radius: 10px;
    gap: 2em;
    box-shadow: 5px 3px 10px black;
}

.mochila {
    min-width: 170px;
    max-width: 170px;
    height: 180px;
    padding-top: 80px;
    background-color: #d84736;
    box-shadow: inset 0 6px 0 2px #e64a3e, 0 -93px 0 -77px #3b1045, 0 -93px 0 -72px #d84736, 0 -93px 0 -68px #3b1045;
    background-image: linear-gradient(to right, transparent 5px, transparent 20px, transparent 1px, transparent 25px, transparent 5px, transparent 20px, transparent 20px, transparent 86px, #c1382e 45px, #c1382e 230px, transparent 25px, transparent 172px );
    background-position: center 254px;
    background-size: 305px 6px;
    background-repeat: repeat-x; 
    border: 5px solid #410c38;
    border-radius: 85px 85px 44px 44px;
    display: inline-block;
    vertical-align: top;
    flex-basis: auto;
    flex-grow: 8;
}

.mochila:before, .mochila:after {
    display: block;
    content: '';
    position: absolute;
  }

.mochila:before {
    height: 75px;
    width: 118px;
    background-image: radial-gradient(circle closest-corner at 13px 29px , #af342c, #af342c 2px, transparent 2px, transparent), radial-gradient(circle closest-corner at 13px 29px , #491033, #491033 4px, transparent 4px, transparent), linear-gradient(to bottom, transparent 10px, transparent 15px, #491033 5px, #491033 18px, transparent 18px, transparent 100px), linear-gradient(to right, #af342c 10px, #af342c 10px, transparent 10px, transparent 16px, #af342c 9px, #af342c 50px), linear-gradient(to bottom, #af342c 8px, #af342c 16px, #491033 8px, #491033 30px, #af342c 20px, #af342c 72px, #af342c 10px, #af342c 57px, #af342c 5%);
    margin-left: 22px;
    margin-top: 58px;
    border: 4px solid #491033;
    border-radius: 11px;
    box-shadow: 0 4px 0 0 #c1382e, inset 0px 4px 1px 0 #c03527;
    content: '||';
    color: transparent;
    font-family: Comic Sans MS;
    font-weight: bold;
    z-index: 1;
    text-shadow: rgb(0, 0, 0) -2px -86px 0px;
    font-size: 14px;
}

.mochila:after {
    margin-left: 67px;
    margin-top: -30px;
    background: #cca16a;
    width:26px;
    height:26px;
    border: 3px solid #491033;
    border-radius: 3px;
    transform: rotate(45deg);
}

.adicionar {
    width: 100%;
}

.adicionar input {
    width: 100%;
    box-sizing: border-box;
    margin: 0.5em 0 1em;
    padding: 1em 0.5em;
    border-radius: 10px;
    border: 2px solid #40112D;
}

.adicionar label {
    display: block;
}

.cadastrar {
    background-color: #40112D;
    color: #FFFFFF;
    cursor: pointer;
}

.lista {
    margin: 0;
    padding: 0;
}

.item {
    list-style: none;
    padding: 0.6em 2em;
    background: #FFFFFF;
    border-radius: 10px;
    margin: 0 0 1em;
    width: 100%;
    box-sizing: border-box;
    display: inline-flex;
    align-items: center;
    gap: 1em;
    font-size: 20px;
    box-shadow: 5px 3px 10px black;
}

.item strong {
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    Background: #C5965F;
    font-weight: 400;
}

Olá Bruno, tudo blz?

Desculpe a demora em responder.

O problema aqui é o comando novoItem.innerHTML += item.Nome da função criaElemento. Como você está criando todos os elementos da sua lista usando o método createElement e, por sua vez, usando o método appendChild para adicionar esse elemento à árvore de elementos da DOM você deve manter esse padrão no caso de dados textuais (que é o caso do nome do item).

Ao usar a propriedade innerHTML para adicionar o nome do item, o javascript acaba fazendo uma substituição capciosa na estrutura HTML: o que era composto por elementos HTML com representação no JavaScript (com eventos adicionados e tudo mais) passa a ser visto apenas como uma string (porque a propriedade innerHTML é uma string com o conteúdo HTML do elemento referido), sendo assim, os elementos adicionados como filhos do objeto novoItem não são mais vistos como objetos representantes de elementos HTML mas apenas como strings com código HTML e aí, perde-se tudo o que foi adicionado a esses elementos que não pode ser traduzido em HTML (o evento click do botão de exclusão por exemplo).

Então, apesar da aparência se manter a mesma, ao usar o comando innerHTML todo o dinamismo adicionado via JavaScript se perde e fica apenas a estrutura HTML crua (sem ações relacionadas), porque tudo foi entendido e convertido numa string com a qual você concatenou o nome do item.

Para manter o evento click adicionado ao elementoBotao quando a função botaoDeleta é chamada, você deve trocar a linha abaixo...

novoItem.innerHTML += item.Nome

Por essa aqui...

novoItem.appendChild(document.createTextNode(item.Nome))

Ou seja, já que você precisa adicionar um texto comum ao elemento novoItem sem perder o dinamismo dos demais elementos HTML já adicionados a ele, você deve criar um nó de texto com o método createTextNode. Assim, o JavaScript mantém a estrutura dos elementos HTML com suas características dinâmicas e faz a adição do texto puro que você precisa a esse item.

Espero ter ajudado!

Qualquer dúvida, só perguntar...

:)