我正在开发一个网页,需要创建具有以下属性的元素:
这是我当前的 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>
我已经成功地单独实现了这些功能;但问题是我找不到一种方法让两种行为同时发挥作用。
我发现问题出在horizontal-scroll-videos元素的定义上,特别是overflow-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;
});
});
我是这个领域的初学者,任何有关如何实现这一目标的帮助或建议将不胜感激!
悬停效果停止工作的原因是因为悬停效果仅检查当您将鼠标移动到某个元素上时,而不检查该元素是否在鼠标不移动时在鼠标下方移动。
因为你的滚动是自动的,所以当舞蹈卡在鼠标下移动时:悬停不会触发。
为了保留两者的功能,您可能需要使用 Javascript 而不是 CSS 检查鼠标是否位于舞蹈卡上。我相信 Javascript 事件 onmouseover 和 onmouseenter 会遇到与 CSS :hover 相同的问题。
这让事情变得更加困难。解决此问题的一种方法是不断检查 document.elementFromPoint 以获取您悬停的元素,如果它是舞蹈卡,则触发您使用 :hover 所要的效果。然后,只要该元素不是舞蹈卡,就重置该卡的值。 https://developer.mozilla.org/en-US/docs/Web/API/Document/elementFromPoint
问题可能有更简单的解决方案,但我认为这是解决问题的一种方法。