JavaScript 自定义星级评分系统
JavaScript Custom Star Rating System
我想创建一个有 5 颗星的星级评定系统。你不能 select 半颗星,只能整颗。我想实现以下目标:如果用户点击星星,则应激活之前被选中的星星,如果用户点击较低的星星,则停用 selected 之后的所有星星。
这是我目前得到的结果:用户可以 select 4 星(满分 5 星)(在第五次点击时我有一个应该解决的错误)。
PS:我正在处理 SVG 图像,但插入它会太难看,所以 [ ]
是空星星(默认),[X]
是 selected(活跃)明星。
这是我的代码:
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>
您的方法只是需要一种不同的方法。例如,如果你要把它放在里面,那么内循环是不必要的 icon[j].innerHTML = '[X]'
.. 可以放在外循环中。
此外,不必要的计算使任务看起来比实际更难。由于这是一个循环,i
变量在循环中将始终具有最高值,因为那里没有 break
语句
下面的方法针对与当前被点击的元素相关的下一个元素和上一个元素,并对它们应用适当的“innerHTML”
// 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>
另一种方法:
// 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>
我想创建一个有 5 颗星的星级评定系统。你不能 select 半颗星,只能整颗。我想实现以下目标:如果用户点击星星,则应激活之前被选中的星星,如果用户点击较低的星星,则停用 selected 之后的所有星星。
这是我目前得到的结果:用户可以 select 4 星(满分 5 星)(在第五次点击时我有一个应该解决的错误)。
PS:我正在处理 SVG 图像,但插入它会太难看,所以 [ ]
是空星星(默认),[X]
是 selected(活跃)明星。
这是我的代码:
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>
您的方法只是需要一种不同的方法。例如,如果你要把它放在里面,那么内循环是不必要的 icon[j].innerHTML = '[X]'
.. 可以放在外循环中。
此外,不必要的计算使任务看起来比实际更难。由于这是一个循环,i
变量在循环中将始终具有最高值,因为那里没有 break
语句
下面的方法针对与当前被点击的元素相关的下一个元素和上一个元素,并对它们应用适当的“innerHTML”
// 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>
另一种方法:
// 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>