Quiero crear un sistema de clasificación por estrellas que tenga 5 estrellas. No puede seleccionar media estrella, solo una entera. Quiero lograr lo siguiente: si el usuario hace clic en la estrella, debe activarse la una y la otra anterior, y si el usuario hace clic en una estrella inferior, desactive todas las estrellas después de la seleccionada.
Esto es lo que obtuve hasta ahora: el usuario puede seleccionar 4 estrellas de cinco (en el quinto clic tengo un error que debe resolverse).
PD: Estoy trabajando con imágenes SVG, pero sería demasiado feo insertarlas, por lo que [ ] son las estrellas vacías (el valor predeterminado) y [X] son las estrellas seleccionadas (activas).
Aquí está mi código:
for (let i = 1; i <= 5; i++) { document.getElementById("w__stars").innerHTML += `<span class="r__icon">[ ]</span>`; } var icon = document.getElementsByClassName("r__icon"); for (let i = 0; i < icon.length; i++) { icon[i].addEventListener("click", function (e) { console.log("--"); for (let j = 0; j < i+1; j++) { console.log("i: " +i); console.log("j: "+(j+1)); console.log("Rest: "+ (j+(5-(i+1)))); icon[j].innerHTML = `[X]`; icon[i+(5-(i+1))].innerHTML = `[ ]`; } }); } <div class="flex flex-row product-star-con" id="w__stars"></div>Su método solo necesita un enfoque diferente. Por ejemplo, ese bucle interno es innecesario si va a colocar esto allí icon[j].innerHTML = '[X]' .. que se puede colocar justo dentro del bucle externo.
Además, los cálculos innecesarios hacen que la tarea parezca más difícil de lo que realmente es. Y dado que se trata de un ciclo, la variable i siempre tendrá el valor más alto dentro del ciclo, ya que no hay una declaración de break allí.
El siguiente método apunta a los siguientes elementos y elementos anteriores en relación con el que se está haciendo clic en este momento y les aplica el ' innerHTML ' apropiado.
// Function to get previous and next siblings of the target element function getSiblings(element, type){ var arraySib = []; if ( type == 'prev' ){ while ( element = element.previousSibling ){ arraySib.push(element); } } else if ( type == 'next' ) { while ( element = element.nextSibling ){ arraySib.push(element); } } return arraySib; } for (var i = 1; i <= 5; i++) { document.getElementById("w__stars").innerHTML += `<span class="r__icon">[ ]</span>`; } var icon = document.getElementsByClassName("r__icon"); for (var i = 0; i < icon.length; i++) { icon[i].addEventListener("click", function (e){ this.innerHTML = `[X]`; var prev = getSiblings(this, 'prev') var next = getSiblings(this, 'next') // populate previous siblings for(p = 0; p < prev.length; p++){ prev[p].innerHTML = `[X]` } // clear next siblings for(n = 0; n < next.length; n++){ next[n].innerHTML = `[]` } }); } <div class="flex flex-row product-star-con" id="w__stars"></div>Otro enfoque:
// Setting stars const stars = []; for (let i = 0; i <= 4; i++) { stars.push({ active: false, index: i }); } const renderStars = (parentElement, stars, activeContent, notActiveContent) => { parentElement.innerHTML = ''; stars.forEach(({ active, index }) => { parentElement.innerHTML += ` <span class="r__icon">${active ? activeContent : notActiveContent}</span>`; }); Array.from(parentElement.querySelectorAll('.r__icon')).forEach((item, itemIndex) => { const star = stars.find(({ index }) => index === itemIndex); stars[star.index].element = item; item.addEventListener('click', (e) => { const itemElement = e.target; const starIndex = stars.findIndex(({ element }) => element === itemElement); if (starIndex === -1) { return; } const toActive = stars[starIndex].active !== true; stars = stars.map(star => { if (toActive) { // Set items before index to true, and after to false if (star.index <= starIndex) { return { ...star, active: true }; } return { ...star, active: false }; } else { // Set items after index to false, and before to true if (star.index >= starIndex) { return { ...star, active: false }; } return { ...star, active: true }; } }); renderStars(parentElement, stars, activeContent, notActiveContent); }); }); }; const setupStars = (stars, activeContent, notActiveContent) => { const parentElement = document.getElementById("w__stars"); if (!parentElement) { return; } renderStars(parentElement, stars, activeContent, notActiveContent); }; setupStars(stars, '[X]', '[ ]'); <div class="flex flex-row product-star-con" id="w__stars"></div>