Я работаю над веб-страницей, на которой мне нужно создать элемент со следующими свойствами:
Вот моя текущая структура HTML:
<div class = "horizontal-scroll-videos">
{% for dance in dances %}
<a href = "/sample/{{ dance.id }}" class = "dance-card">
<div class = "dance-card-image-description">
<img src = "{{ dance.image_url }}" class = "dance-card-image" alt = "{{ dance.name }}">
<video class = "dance-card-video" dance-video-path = "{{ dance.video }}" muted></video>
<p class = "dance-card-description body-text">{{ dance.description }}</p>
</div>
<div class = "dance-card-body">
<h5 class = "dance-card-title">{{ dance.name }}</h5>
<div class = "dance-card-tags">
<span class = "badge bg-primary">{{ dance.difficulty }}</span>
<span class = "badge bg-secondary">{{ dance.genre }}</span>
{% if dance.has_instructions %}
<span class = "badge bg-warning">Tutorial</span> <!-
{% endif %}
</div>
</div>
</a>
{% endfor %}
</div>
Мне удалось реализовать каждую из этих функций индивидуально; но проблема в том, что я не могу найти способ, чтобы оба поведения функционировали одновременно.
Я обнаружил, что проблема связана с определением элемента горизонтальной прокрутки видео, особенно с переполнением-x.
.horizontal-scroll-videos {
display: flex;
align-items: flex-start;
gap: var(--inter-video-gap);
align-self: stretch;
overflow-x: auto;
overflow-y: visible;
scroll-behavior: smooth;
white-space: nowrap;
scrollbar-width: none;
}
Когда включено overflow-x: auto: горизонтальная прокрутка работает отлично. Однако эффект наведения отключен, а танцевальные карточки не могут накладываться на другие элементы по желанию.
Прокрутка работает, но наведение не работает
Если включен параметр overflow-x:visible: эффект наведения работает правильно, и танцевальные карточки накладываются на другие элементы по желанию. Однако функциональность горизонтальной прокрутки теряется.
Наведение работает, но прокрутка не работает
Я пытался динамически переключать поведение overflow-x в зависимости от взаимодействия с пользователем, но проблема в том, что прокрутка теряет свое положение. При обратном переключении на overflow-x:visible позиция прокрутки сбрасывается, и новый контент, отображаемый во время прокрутки, теряется.
Код для эффекта наведения и эффекта прокрутки:
function addHoverEffectDanceCards(){
const danceCards = document.querySelectorAll(".dance-card");
danceCards.forEach(danceCard => {
// Get the video and image elements within the dance card
const video = danceCard.querySelector(".dance-card-video");
const image = danceCard.querySelector(".dance-card-image");
// Get the children (the elements within) the dance card
const children = danceCard.children;
const childrenArray = Array.from(children);
childrenArray.forEach(child => {
// If any element in a dance card gets moused over, add the hover class to every element in the dance card
child.addEventListener("mouseover", function() {
const container = danceCard.closest(".horizontal-scroll-videos");
const containerRect = container.getBoundingClientRect();
const danceCardRect = danceCard.getBoundingClientRect();
// Check if the dance card is fully visible within the container; don't show preview if it is not
if (danceCardRect.left >= containerRect.left &&
danceCardRect.right <= containerRect.right) {
childrenArray.forEach(child => {
classes = child.classList;
classes.add("hover");
// Add the hover to the children within the dance-card-image-description div
if (classes.contains("dance-card-image-description")) {
const imgDesChildren = child.children;
const imgDesChildrenArray = Array.from(imgDesChildren);
imgDesChildrenArray.forEach(imgDesChild => {
imgDesChild.classList.add("hover");
});
};
});
// Add the hover class to the dance card itself
danceCard.classList.add("hover");
// Check if the video src for preview is loaded
if (!video.src) {
// Get the video if it is not defined
fetch('/generate_video_url', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({video_path: video.getAttribute('dance-video-path')})
})
.then(response => response.json())
.then(data => {
video.src = data.video_url; // Asign the video
})
.catch(error => console.error('Error fetching video presigned URL:', error));
}
// Start playing the preview
video.currentTime = 0;
video.play();
video.style.display = "block";
image.style.display = "none";
}
});
// Remove the hover when no longer mousing over
child.addEventListener("mouseout", function() {
childrenArray.forEach(child => {
classes = child.classList;
classes.remove("hover");
// Remove the hover effect from the children inside the dance-card-image-description div
if (classes.contains("dance-card-image-description")) {
const imgDesChildren = child.children;
const imgDesChildrenArray = Array.from(imgDesChildren);
imgDesChildrenArray.forEach(imgDesChild => {
imgDesChild.classList.remove("hover");
});
};
});
// Remove the hover class from the dance card itself
danceCard.classList.remove("hover");
// Pause the video and show the image
video.pause();
video.style.display = "none";
image.style.display = "block";
});
});
});
const horizontalScrollContainers = document.querySelectorAll(".horizontal-scroll-videos");
horizontalScrollContainers.forEach(container => {
let scrollInterval;
container.addEventListener('mouseover', (e) => {
const screenWidth = window.innerWidth;
const scrollThreshold = 200;
// Check if mouse is within the scrollThreshold from the right edge
const checkLeftScroll = (e) => {
const mouseX = e.clientX;
return mouseX > screenWidth - scrollThreshold;
};
const checkRightScroll = (e) => {
const mouseX = e.clientX;
return mouseX < scrollThreshold;
}
if (checkLeftScroll(e)) {
scrollInterval = setInterval(() => {container.scrollLeft += 180;}, 30);
} else if (checkRightScroll(e)) {
scrollInterval = setInterval(() => {container.scrollLeft -= 180;}, 30);
}
});
container.addEventListener('mouseout', () => {
clearInterval(scrollInterval);
scrollInterval = null;
});
});
Я новичок в этой области, и буду очень признателен за любую помощь или предложения о том, как этого добиться!
Кажется, я совершенно неверно истолковал ваш вопрос. К сожалению, overflow-x и overflow-y по умолчанию имеют неприятное поведение: они не позволяют сделать один из них видимым, а другой — прокручивать/скрывать. Вот вопрос Stack Overflow, который подробно объясняет это.
Я бы предложил использовать одно из представленных ими решений.
Причина, по которой эффект наведения перестает работать, заключается в том, что эффект наведения проверяется только тогда, когда вы наводите указатель мыши на элемент, а НЕ если этот элемент перемещается под мышью, пока мышь не движется.
Поскольку прокрутка происходит автоматически, когда танцевальные карточки перемещаются под мышью, :hover не срабатывает.
Чтобы сохранить функциональность обоих, вам, вероятно, придется проверить, находится ли указатель мыши над танцевальной карточкой с помощью Javascript вместо CSS. Я считаю, что события Javascript onmouseover и onmouseenter будут иметь те же проблемы, что и CSS :hover.
Это немного усложняет задачу. Один из способов добиться этого — постоянно проверять с помощью document.elementFromPoint элемент, над которым вы наводите курсор, и, если это танцевальная карта, запускать эффекты, которые вы бы сделали с помощью :hover. Затем, если элемент не является танцевальной картой, сбросьте значение этой карты. https://developer.mozilla.org/en-US/docs/Web/API/Document/elementFromPoint
Возможно, есть более простые решения проблемы, но я думаю, что это один из способов решения этой проблемы.
Вот еще один вопрос о переполнении стека, иллюстрирующий ту же проблему. Вы также можете попробовать их решение: Запуск: наведение курсора на движущийся элемент без перемещения мыши
Я бы сказал, что самым простым подходом (если он существует) было бы найти способ закодировать это так, чтобы контент мог переполняться при прокрутке, а также выходить за пределы контейнера горизонтальной прокрутки для наложения поверх другого контента. Если у вас есть какие-либо предложения о том, как этого добиться, или есть другой подход, который может работать лучше, я буду очень признателен за ваши советы! Еще раз спасибо за вашу помощь!
Я вижу, это имеет смысл. Можете ли вы показать текущие правила, которые вы установили для элемента при :hover?
Конечно, у меня есть: .dance-card.hover { align-self: flex-end; box-shadow: var(--raised---level-3); border-radius: var(--dc-hover-corner-radius); gap: 0; transition-duration: var(--dc-hover-duration); z-index: 1; } .dance-card-video.hover { width: var(--dc-hover-width); border-radius: var(--dc-hover-corner-radius) var(--dc-hover-corner-radius) 0 0; box-shadow: none; position: absolute; bottom: 0; z-index: 1; transition-duration: var(--dc-hover-duration); }
Большое спасибо за ваш ответ! Теперь я понимаю, что мои слова, возможно, были неточными при описании проблемы. Частично работает эффект наведения: показывает описание танца и увеличивает карточку танца. На скриншотах видно, что поведение при наведении срабатывает, но не так, как ожидалось. Под «ожидаемым» я подразумеваю, что танцевальная карточка должна накладываться поверх других элементов, отображая при этом ее описание и увеличивая ее размер. Проблема в том, что эффекта наложения не происходит — карточка обрезается контейнером горизонтальной прокрутки, а не выходит за его пределы.