Solucionado (ver solução)
Solucionado
(ver solução)
6
respostas

focar no imput do item a ser editado quando clicar no botão de editar

Eu quero q ao clicar no botão de editar, o cursor foque no item a ser editado, já que fica bem confuso ter q clicar no botão e ai sim clicar no imput para editar. Pra isso, eu montei essa lógica:

    const iconeEditar = document.querySelectorAll("[data-editar]");

    iconeEditar.forEach(i => {

        i.addEventListener("click", evento => {

            const pai = evento.target.parentElement.parentElement;
            const filho = pai.children[0];
            const filhoDoFilho = filho.children[1];
            console.log(pai);
            console.log(filho);
            console.log(filhoDoFilho);
            filhoDoFilho.focus();
            // itemInput = document.querySelector("[data-item]");
            // console.log(itemInput);
            // itemInput.focus();


            itemAEditar = evento.target.parentElement.parentElement.getAttribute("data-value");
        })
    })
}

funcionou sem problemas, mas n me parece a melhor forma de se fazer isso. É possivel notar o codigo comentado onde eu tentei fazer a logica (adicionei um data attribute ao campo do input e referenciei ele em uma variavel) mas por algum motivo ele sempre selecionava o primeiro item da lista, n importando qual icone eu clicasse.

Releve o nome das variaveis, escolhi apenas para reconhecer melhor o q eu estava encontrando com cada um deles.

6 respostas

Eu acabei encontrando uma solução vendo um problema similar postado nesse curso, mas lá a solução foi bem diferente:

// inserir codigo na função mostrarItem(), logo após a checagem do elemento

const inputEdit = document.querySelector(`[data-value="${itemAEditar}"] input[type="text"]`);
            
    if (inputEdit) {
        
        inputEdit.removeAttribute('disabled');
        inputEdit.focus();
        inputEdit.addEventListener('keydown', event => {
                    
            if (event.key === 'Enter') {
                salvarEdicao();
            }
        })
    }

Ainda gostaria de entender melhor a situação da primeira lógica q montei, que funcionou mas com um código meio "verborrágico". Aqui o codigo completo, caso necessário para analizar melhor a dúvida:

let listaDeItens = [];
let itemAEditar;
// let itemInput;

const formulario = document.getElementById("form-itens");
const formularioImput = document.getElementById("receber-item");
const listaDeCompras = document.getElementById("lista-de-itens");
const listaItensComprados = document.getElementById("itens-comprados");

formulario.addEventListener("submit", evento => {

    evento.preventDefault();
    salvarItem();
    mostrarItem();
    formularioImput.focus();
})

function salvarItem() {

    const item = formularioImput.value;
    const checarDuplicado = listaDeItens.some(elemento => elemento.valor.toUpperCase() === item.toUpperCase())

    if(checarDuplicado) {

        alert("Item já existe");
    } else {

        listaDeItens.push({
            valor: item,
            checar: false
        })
    }

    formularioImput.value = "";
}

function mostrarItem() {

    listaDeCompras.innerHTML = "";
    listaItensComprados.innerHTML = "";
    listaDeItens.forEach((elemento, index) => {

        if(elemento.checar) {

            

            listaItensComprados.innerHTML += `
                <li class="item-compra is-flex is-justify-content-space-between" data-value="${index}">
                    <div>
                        <input type="checkbox" checked class="is-clickable" />
                        <span class="itens-comprados is-size-5">${elemento.valor}</span>
                    </div>
                    <div>
                        <i class="fa-solid fa-trash is-clickable deletar" data-deletar></i>
                    </div>
                </li>
            `

        } else {

            listaDeCompras.innerHTML += `
                <li class="item-compra is-flex is-justify-content-space-between" data-value="${index}">
                    <div>
                        <input type="checkbox" class="is-clickable" />
                        <input type="text" class="is-size-5" value="${elemento.valor}" data-item ${index !== Number(itemAEditar) ? "disabled" : ""}></input>
                    </div>
                    <div>
                        ${index == Number(itemAEditar) ? '<button onclick="salvarEdicao()"><i class="fa-regular fa-floppy-disk is-clickable"></i></button>' : '<i class="fa-regular is-clickable fa-pen-to-square editar" data-editar></i>'} <i class="fa-solid fa-trash is-clickable deletar" data-deletar></i>
                    </div>
                </li>
            `;
        }
    });

    const inputEdit = document.querySelector(`[data-value="${itemAEditar}"] input[type="text"]`);
            
    if (inputEdit) {
        
        inputEdit.removeAttribute('disabled');
        inputEdit.focus();
        inputEdit.addEventListener('keydown', event => {
                    
            if (event.key === 'Enter') {
                salvarEdicao();
            }
        })
    }

    const itemCheckbox = document.querySelectorAll('input[type="checkbox"]');

    itemCheckbox.forEach(i => {

        i.addEventListener("click", evento => {

            const itemIndex = evento.target.parentElement.parentElement.getAttribute("data-value");

            listaDeItens[itemIndex].checar = evento.target.checked;

            mostrarItem();
        })
    })

    const deletarItem = document.querySelectorAll("[data-deletar]");

    deletarItem.forEach(i => {

        i.addEventListener("click", evento => {

            const itemIndex = evento.target.parentElement.parentElement.getAttribute("data-value");

            listaDeItens.splice(itemIndex, 1);
            mostrarItem();
        })
    })


    const iconeEditar = document.querySelectorAll("[data-editar]");

    iconeEditar.forEach(i => {

        i.addEventListener("click", evento => {

            // const pai = evento.target.parentElement.parentElement;
            // const filho = pai.children[0];
            // const filhoDoFilho = filho.children[1];
            // console.log(pai);
            // console.log(filho);
            // console.log(filhoDoFilho);
            // filhoDoFilho.focus();
            // itemInput = document.querySelector("[data-item]");
            // console.log(itemInput);
            // itemInput.focus();


            itemAEditar = evento.target.parentElement.parentElement.getAttribute("data-value");
            mostrarItem();
        })
    })
}

function salvarEdicao() {

    const itemEditado = document.querySelector(`[data-value="${itemAEditar}"] input[type="text"]`);
    listaDeItens[itemAEditar].valor = itemEditado.value;
    itemAEditar = -1;
    itemEditado.setAttribute('disabled', true);
    mostrarItem();
}

Oi Lucas, tudo bem?

Primeiramente, é ótimo ver seu entusiasmo em aprimorar a usabilidade de sua aplicação ao editar itens na lista. Vou analisar suas duas abordagens e explicar o funcionamento de cada uma, destacando suas diferenças e o motivo pelo qual uma delas parece mais eficiente.

Primeira Abordagem:

Na primeira abordagem, você utiliza um evento de clique nos ícones de edição para identificar o elemento pai (linha da lista) e, em seguida, acessa o elemento filho (o campo de entrada) para definir o foco. Aqui está um trecho do seu código:

const iconeEditar = document.querySelectorAll("[data-editar]");

iconeEditar.forEach(i => {
    i.addEventListener("click", evento => {
        const pai = evento.target.parentElement.parentElement;
        const filho = pai.children[0];
        const filhoDoFilho = filho.children[1];
        filhoDoFilho.focus();
        itemAEditar = evento.target.parentElement.parentElement.getAttribute("data-value");
    })
})

Esta abordagem funciona bem e alcança o resultado desejado. No entanto, você mencionou que achou esse código um pouco "verborrágico", e eu concordo que pode ser simplificado. Além disso, ao percorrer a hierarquia DOM dessa maneira, você está tornando o código mais suscetível a quebras se a estrutura HTML for alterada.

Segunda Abordagem:

Na segunda abordagem, você utiliza uma abordagem mais direta. Quando o ícone de edição é clicado, você utiliza um seletor de atributo para encontrar o campo de entrada específico associado ao item que está sendo editado e, em seguida, remove o atributo "disabled" e define o foco neste campo. Aqui está o código relevante:

const inputEdit = document.querySelector(`[data-value="${itemAEditar}"] input[type="text"]`);

if (inputEdit) {
    inputEdit.removeAttribute('disabled');
    inputEdit.focus();
    inputEdit.addEventListener('keydown', event => {
        if (event.key === 'Enter') {
            salvarEdição();
        }
    })
}

Esta abordagem é mais concisa e direta, pois não requer a navegação pela hierarquia DOM. Além disso, ela é menos propensa a erros caso a estrutura HTML seja alterada, pois se baseia no atributo "data-value" associado ao item.

Escolha da Melhor Abordagem:

A segunda abordagem parece mais eficiente e limpa em comparação com a primeira. Ela direciona diretamente o elemento de entrada do item que está sendo editado, evitando a necessidade de navegar pela árvore DOM e acessar elementos pai e filho.

No geral, ambas as abordagens funcionam, mas a segunda é mais eficiente e direta. Espero que esta explicação tenha ajudado a esclarecer a diferença entre as duas abordagens e por que a segunda é preferível.

Um abraço e bons estudos.

Acho q n fui muito claro na minha dúvida:

// codigo omitido

function mostrarItem() {

    listaDeCompras.innerHTML = "";
    listaItensComprados.innerHTML = "";
    listaDeItens.forEach((elemento, index) => {

        if(elemento.checar) {

            listaItensComprados.innerHTML += `
                <li class="item-compra is-flex is-justify-content-space-between" data-value="${index}">
                    <div>
                        <input type="checkbox" checked class="is-clickable" />
                        <span class="itens-comprados is-size-5">${elemento.valor}</span>
                    </div>
                    <div>
                        <i class="fa-solid fa-trash is-clickable deletar" data-deletar></i>
                    </div>
                </li>
            `

        } else {

            listaDeCompras.innerHTML += `
                <li class="item-compra is-flex is-justify-content-space-between" data-value="${index}">
                    <div>
                        <input type="checkbox" class="is-clickable" />
                        <input type="text" class="is-size-5" value="${elemento.valor}" ${index !== Number(itemAEditar) ? "disabled" : ""} **data-item**></input>
                    </div>
                    <div>
                        ${index == Number(itemAEditar) ? '<button onclick="salvarEdicao()"><i class="fa-regular fa-floppy-disk is-clickable"></i></button>' : '<i class="fa-regular is-clickable fa-pen-to-square editar" data-editar></i>'} <i class="fa-solid fa-trash is-clickable deletar" data-deletar></i>
                    </div>
                </li>
            `;
        }

    });
    
// codigo omitido

const iconeEditar = document.querySelectorAll("[data-editar]");

    iconeEditar.forEach(i => {

        i.addEventListener("click", evento => {
        
            const itemInput = document.querySelector("[data-item]");
            console.log(itemInput);
            itemInput.focus();

            itemAEditar = evento.target.parentElement.parentElement.getAttribute("data-value");
            mostrarItem();
        })
    })
}

Essa era a lógica q eu estava em dúvida (ela tinha sido comentada por n estar funcionando como eu queria). Ela até cumpre o objetivo de editar o item clicado mas o problema é que n importa qual ícone de editar eu clique, ele sempre me devolve no log o primeiro item da lista. Além disso, o metodo focus() não funciona. Eu adicionei o data-item ao campo de input q deve ser editado quando o ícone de editar é clicado.

solução!

Oi Lucas!

Então sua dúvida é sobre a primeira abordagem que você tentou implementar para focar no input do item a ser editado ao clicar no botão de edição. Vou explicar a situação e as razões pelas quais ela não funcionou como o esperado.

Primeiro, vamos revisitar o trecho de código em questão:

const iconeEditar = document.querySelectorAll("[data-editar]");

iconeEditar.forEach(i => {
    i.addEventListener("click", evento => {
        const itemInput = document.querySelector("[data-item]");
        console.log(itemInput);
        itemInput.focus();

        itemAEditar = evento.target.parentElement.parentElement.getAttribute("data-value");
        mostrarItem();
    })
})

Aqui, você está tentando encontrar o elemento de input para edição usando document.querySelector("[data-item]"). No entanto, esse seletor vai pegar o primeiro elemento com o atributo data-item, independentemente do ícone de edição que você clicou. Isso explica por que sempre obtém o primeiro item da lista.

Além disso, o método focus() não está funcionando porque você está tentando focar em todos os elementos com o atributo data-item, e isso não é o comportamento desejado.

Para resolver essa situação, você precisa direcionar o elemento de input associado ao ícone de edição clicado. Aqui está uma sugestão de como você pode ajustar seu código:

const iconeEditar = document.querySelectorAll("[data-editar]");

iconeEditar.forEach(i => {
    i.addEventListener("click", evento => {
        const itemIndex = evento.target.parentElement.parentElement.getAttribute("data-value");
        const itemInput = document.querySelector(`[data-value="${itemIndex}"] input[data-item]`);
        if (itemInput) {
            itemInput.focus();
        }

        itemAEditar = itemIndex;
        mostrarItem();
    })
})

Neste código revisado, você usa o atributo data-value do ícone de edição clicado para encontrar o elemento de input correto usando um seletor mais específico. Isso garantirá que você esteja focalizando o input correto e também evita que você obtenha o primeiro item da lista o tempo todo.

Lembrando que essa é uma ideia de solução para você, o ideal é você ajustar para como quer deixar.

Um abraço e bons estudos.

De qualquer forma, me ajudou a entender o que estava acontecendo e acabei misturando a solução que estava usando antes, ficando assim:

function editarItem () {

    const iconeEditar = document.querySelectorAll("[data-editar]");

    iconeEditar.forEach(i => {

        i.addEventListener("click", evento => {

            itemAEditar = evento.target.parentElement.parentElement.getAttribute("data-value");
            mostrarItem();
        })
    })

        const itemInput = document.querySelector(`[data-value="${itemAEditar}"] input[type="text"]`);
        
        if (itemInput) {
            
            itemInput.focus();
            itemInput.addEventListener('keydown', event => {
                    
                if (event.key === 'Enter') {
                    salvarEdicao();
                }
            })
        }
    }

Se entendi direito, o itemIdex seria o itemAEditar da lógica sugerida, então eu apenas usei esse index para achar o input do campo desejado e referenciei em uma constante. Ai usei a mesma ideia do if clause para o focus() e adicionei o escutador de evento para poder usar o Enter, sem precisar ficar clicando no incone de salvar.