-
Notifications
You must be signed in to change notification settings - Fork 664
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Добавляет рецепт рейтинга 5 ⭐️ #5592
base: main
Are you sure you want to change the base?
Conversation
Чек-лист для новой статьиМетаданные
Статья
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Привет, красота 🤩
Оставил несколько комментов, и будет космическая ракета!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Микро-правочка
This comment was marked as resolved.
This comment was marked as resolved.
const stars = document.querySelectorAll('.star') | ||
|
||
stars.forEach((star, index) => { | ||
star.addEventListener('click', (event) => { | ||
const activeStar = event.currentTarget | ||
|
||
if (activeStar.classList.contains('active')) { | ||
activeStar.classList.remove('active') | ||
rating.dataset.ratingValue = '' | ||
} else { | ||
stars.forEach(star => star.classList.remove('active')) | ||
activeStar.classList.add('active') | ||
rating.dataset.ratingValue = parseInt(activeStar.textContent.trim()) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Привет,
Классные звёздочки ! Ставлю: ⭐⭐⭐⭐⭐
В скрипте можно немножко сэкономить:
const rating = document.querySelector('.star-rating')
// дочерние элименты ищем локально
const starsContainer = rating.querySelector('.stars')
const stars = rating.querySelectorAll('.star')
// добавляем один EventListener к элементу ul
starsContainer.addEventListener('click', (event) => {
// получаем звезду на которой произошёл клик
const activeStar = event.target.closest('.star')
...
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
Co-authored-by: Alexey Nikitchenko <[email protected]>
Превью контента из 1b51203 опубликовано. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
\((( ̄( ̄( ̄▽ ̄) ̄) ̄)))/
Надо будет переписать рецепт на чекбоксы/радио-кнопки. Пока переведу в драфт |
Я в комментах, пожалуй, предложу правочки по коду. Вот так NVDA читает текущие звёздочки. Порядок фокуса с клавиатуры начинается с последней звезды (потому что в коде они расположены в таком порядке, а визуально нет). Выбрана кнопка или нет не понятно, поэтому сложно сказать, какой рейтинг ты поставил. Про nvda-before.mp4 |
Что я предлагаю. HTML: <form class="star-rating" role="radiogroup" data-rating-value="">
<fieldset class="star-group">
<legend>Оцените товар:</legend>
<ul class="stars">
<li class="star">
<button class="star-button" role="radio" aria-checked="false" aria-label="1 звезда">
<span aria-hidden="true">⭐️</span>
</button>
</li>
<li class="star">
<button class="star-button" role="radio" aria-checked="false" aria-label="2 звезды">
<span aria-hidden="true">⭐️</span>
</button>
</li>
<li class="star">
<button class="star-button" role="radio" aria-checked="false" aria-label="3 звезды">
<span aria-hidden="true">⭐️</span>
</button>
</li>
<li class="star">
<button class="star-button" role="radio" aria-checked="false" aria-label="4 звезды">
<span aria-hidden="true">⭐️</span>
</button>
</li>
<li class="star">
<button class="star-button" role="radio" aria-checked="false" aria-label="5 звёзд">
<span aria-hidden="true">⭐️</span>
</button>
</li>
</ul>
</fieldset>
</form> CSS: *,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
color-scheme: dark;
}
body {
display: grid;
place-items: center;
min-height: 100dvh;
margin: 0;
padding: 50px;
background-color: #18191c;
color: #ffffff;
font-family: "Roboto", sans-serif;
}
legend {
font-size: 24px;
font-weight: 500;
}
.star-button {
background-color: transparent;
border: none;
color: inherit;
cursor: pointer;
font: inherit;
padding: 0;
}
.star-group {
text-align: center;
border: none;
}
.stars {
list-style: none;
display: flex;
margin-top: 20px;
gap: 1rem;
font-size: 5rem;
}
.star {
line-height: 1;
opacity: 0.5;
cursor: pointer;
transition: opacity 0.2s;
}
.active,
.star:has(~ .active) {
opacity: 1;
}
.stars:hover .star {
opacity: 0.5;
}
.stars .star:hover,
.star:has(~ .star:hover) {
opacity: 1;
}
.stars .star:has(.star-button:focus-visible),
.stars .star:has(~ .star .star-button:focus-visible) {
opacity: 1;
}
.stars .star:has(.star-button:focus-visible) ~ .star {
opacity: 0.5;
} JS: const rating = document.querySelector('.star-rating')
const stars = rating.querySelectorAll('.star')
const activeStarsClass = ('active')
const starsButtonClass = ('.star-button')
function checkStars(activeStar, activeButton, value) {
stars.forEach(star => {
star.classList.remove(activeStarsClass)
star.querySelector(starsButtonClass).setAttribute('aria-checked', 'false')
})
if (activeStar) {
activeStar.classList.add(activeStarsClass)
activeButton.setAttribute('aria-checked', 'true')
}
rating.dataset.ratingValue = value
}
rating.addEventListener('submit', (event) => {
event.preventDefault()
})
stars.forEach((star, index) => {
const starButton = star.querySelector(starsButtonClass)
starButton.addEventListener('focus', () => {
checkStars(star, starButton, index + 1)
})
starButton.addEventListener('keydown', (event) => {
let nextIndex
switch (event.key) {
case 'ArrowRight':
case 'ArrowUp':
event.preventDefault()
nextIndex = index < stars.length - 1 ? index + 1 : 0
break
case 'ArrowLeft':
case 'ArrowDown':
event.preventDefault()
nextIndex = index > 0 ? index - 1 : stars.length - 1
break
default:
return
}
const nextButton = stars[nextIndex].querySelector(starsButtonClass)
nextButton.focus()
checkStars(stars[nextIndex], nextButton, nextIndex + 1)
})
star.addEventListener('click', (event) => {
const activeStar = event.currentTarget
const activeStarButton = activeStar.querySelector(starsButtonClass)
if (activeStar.classList.contains(activeStarsClass)) {
checkStars(null, null, '')
} else {
checkStars(activeStar, activeStarButton, index + 1)
}
})
}) Так NVDA зачитывает этот код: nvda-after.mp4 |
Объяснения, почему предлагаю так. Если мы оставляем кнопки и избегаем радиокнопок, то надо руками прописать нативное поведение группы радиобаттонов. По умолчанию по ним можно туда-сюда перемещаться стрелочками, не только табом. У кнопок нет по умолчанию состояния выбранности, поэтому надо добавить Так как кнопки в формочке, надо поработать над сбросом данных при отправке. В HTML-коде лучше поменять порядок кнопок, а их визуальное состояние выделенности решить без Названия кнопок засунула в |
Попалась мне тут задачка на собеседовании и я решила написать по её следам рецепт. Вариантов решения этой задачи в интернете много. Но этот показался мне самым оптимальным.
@TatianaFokina очень интересно будет узнать твои рекомендации по доступности такого элемента 🙏
@skorobaeus приходи завалидировать демки =)