`我的 JavaScript 脚本遇到了问题,该脚本本应管理我平台上的视频播放。目前,该脚本对于页面上已存在的视频元素可以正常工作,但它不考虑动态加载的新视频元素。所有脚本都不适用于这些新元素。
动态加载:当用户向下滚动时,视频通过 AJAX 添加到页面。 使用 IntersectionObserver:我尝试使用 IntersectionObserver 根据视频播放的可见性来启动或停止视频播放。
遇到的问题
No Playback for New Elements: When new video elements are loaded, the script does not work for these.
Complete Script Non-Functional: None of the script seems to function for these new elements.
如何确保脚本也适用于新加载的视频元素? 我是否应该遵循最佳实践来正确观察这些新元素?
我的js:
document.addEventListener('DOMContentLoaded', () => {
let currentVideo = null;
const pulseContainer = document.getElementById('pulse-container');
let currentPage = 1;
let loading = false;
let visibleVideoCount = 0;
function handleVideoPlayback(entries) {
entries.forEach(entry => {
const video = entry.target;
if (entry.isIntersecting) {
if (currentVideo !== video) {
video.play();
video.loop = true;
if (currentVideo) {
currentVideo.pause();
}
currentVideo = video;
}
visibleVideoCount++;
console.log(visibleVideoCount);
} else {
if (currentVideo === video) {
currentVideo = null;
}
video.pause();
}
});
// Vérifiez si le compteur de vidéos visibles atteint 5
if (visibleVideoCount >= 5) {
console.log('loadmore please');
loadMoreContent(); // Charge plus de contenu
visibleVideoCount = 0; // Réinitialise le compteur
}
}
const observer = new IntersectionObserver(handleVideoPlayback, {
root: null,
rootMargin: '0px',
threshold: 0.5
});
const videos = document.querySelectorAll('.pulse-video');
videos.forEach(video => {
observer.observe(video);
video.addEventListener('error', (e) => {
console.error('Erreur de chargement de la vidéo:', e);
});
video.src = video.getAttribute('data-src');
video.load();
});
function toggleGlobalSound() {
const newMutedState = !Array.from(videos).some(video => video.muted);
videos.forEach(video => {
video.muted = newMutedState;
});
const globalSoundButtons = document.querySelectorAll('#global-sound-toggle i');
globalSoundButtons.forEach(icon => {
icon.classList.toggle('fa-volume-xmark', newMutedState);
icon.classList.toggle('fa-volume-high', !newMutedState);
});
}
const globalSoundButtons = document.querySelectorAll('#global-sound-toggle');
globalSoundButtons.forEach(button => {
button.addEventListener('click', toggleGlobalSound);
});
function setupVideoClickHandler() {
const videos = document.querySelectorAll('.pulse-video'); // Récupère les vidéos à chaque appel
videos.forEach(video => {
video.addEventListener('click', () => {
video.paused ? video.play() : video.pause();
if (currentVideo && currentVideo !== video) {
currentVideo.pause();
}
currentVideo = video;
});
});
}
function handleVisibilityChange() {
if (document.hidden && currentVideo) {
currentVideo.pause();
} else if (!document.hidden && currentVideo) {
currentVideo.play();
}
}
const artistContents = document.querySelectorAll('.artist-content');
artistContents.forEach(content => {
const toggleButton = content.querySelector('.toggle-description');
if (toggleButton) {
toggleButton.addEventListener('click', () => {
content.classList.toggle('open');
});
}
});
const allArtistElements = document.querySelectorAll('.all_artist'); // Pour plusieurs artistes
allArtistElements.forEach(function(artistContent) {
const toggleButton = artistContent.querySelector('.toggle-description');
const descriptionContent = artistContent.querySelector('.description-content');
// Fonction pour ajuster la hauteur de .all_artist.open
function adjustHeight() {
const descriptionHeight = descriptionContent.scrollHeight; // Hauteur réelle du contenu
const additionalHeight = 0; // Hauteur supplémentaire pour que ça monte plus
artistContent.style.height = `${descriptionHeight + additionalHeight}px`; // Ajuste la hauteur en fonction du contenu
artistContent.style.transform = `translateY(-${descriptionHeight + additionalHeight}px)`;
}
// Fonction pour ouvrir et fermer le contenu
function toggleArtistContent() {
artistContent.classList.toggle('open');
if (artistContent.classList.contains('open')) {
descriptionContent.style.transform = 'translateY(0)'; // Annule le translateY
descriptionContent.style.opacity = '1'; // Affiche le contenu
descriptionContent.style.visibility = 'visible'; // Rend le contenu visible
adjustHeight(); // Ajuste la hauteur de .all_artist.open
} else {
descriptionContent.style.transform = 'translateY(-50px)'; // Cache l'élément
descriptionContent.style.opacity = '0'; // Rend le contenu invisible
descriptionContent.style.visibility = 'hidden'; // Cache le contenu
artistContent.style.height = 'auto'; // Réinitialise la hauteur
artistContent.style.transform = `translateY(0)`; // Réinitialise la position
}
}
// Écouteur d'événement sur le bouton pour basculer l'affichage
toggleButton.addEventListener('click', toggleArtistContent);
});
document.addEventListener('visibilitychange', handleVisibilityChange);
function adjustVideoSize() {
videos.forEach(video => {
video.style.width = '100%';
video.style.height = '100%';
video.style.objectFit = 'cover';
});
}
function preloadVideos() {
const videos = document.querySelectorAll('.pulse-video'); // Récupère les vidéos à chaque appel
videos.forEach(video => {
const rect = video.getBoundingClientRect();
if (rect.top < window.innerHeight && rect.bottom > 0) {
if (video.src === '') {
video.src = video.getAttribute('data-src');
video.load();
}
}
observer.observe(video); // Assure-toi que chaque vidéo est observée
});
}
const loadMoreUrl = '/pulses/load-more-pulses/';
function loadMoreContent() {
console.log('Trying to load more content...');
if (loading) return;
loading = true;
const url = `/pulses/load-more-pulses/?page=${currentPage}`;
fetch(url)
.then(response => {
console.log('Response status:', response.status);
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
console.log('Data received:', data);
if (data.pulse_data) {
data.pulse_data.forEach(pulse => {
pulseContainer.insertAdjacentHTML('beforeend', pulse.html);
});
currentPage++;
initializeNewElements()
}
})
.catch(error => {
console.error('Error loading more content:', error);
})
.finally(() => {
loading = false;
});
}
// Appelle les fonctions au chargement initial
preloadVideos();
function setupInteractionButtons() {
const likeButtons = document.querySelectorAll('.like-button');
const shareButtons = document.querySelectorAll('.share-button');
likeButtons.forEach(button => {
button.addEventListener('click', () => {
const pulseId = button.dataset.pulseId;
fetch('{% url "pulses:toggle_like" %}', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'X-CSRFToken': getCookie('csrftoken')
},
body: new URLSearchParams({ 'pulse_id': pulseId })
})
.then(response => response.json())
.then(data => {
if (data.error) {
alert(data.error);
} else {
button.querySelector('i').classList.toggle('fa-solid', data.liked);
button.querySelector('i').classList.toggle('fa-regular', !data.liked);
button.querySelector('i').style.color = data.liked ? '#d20000' : 'floralwhite';
button.querySelector('.like-count').textContent = data.like_count;
}
})
.catch(error => console.error('Error:', error));
});
});
shareButtons.forEach(button => {
button.addEventListener('click', () => {
const pulseId = button.dataset.pulseId;
fetch('{% url "pulses:share_pulse" %}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCookie('csrftoken')
},
body: JSON.stringify({ pulse_id: pulseId })
})
.then(response => response.json())
.then(data => {
if (data.success) {
document.querySelector(`.share-count[data-pulse-id="${pulseId}"]`).textContent = data.share_count;
navigator.clipboard.writeText(data.share_url)
.then(() => {
alert('Lien copié!');
})
.catch(err => {
console.error('Erreur lors de la copie du lien:', err);
});
}
})
.catch(error => console.error('Error:', error));
});
});
}
document.querySelectorAll('.toggle-comments').forEach(button => {
button.addEventListener('click', () => {
const commentsSection = button.closest('.pulse_item').querySelector('.comments-section');
commentsSection.style.display = commentsSection.style.display === 'none' || commentsSection.style.display === '' ? 'flex' : 'none';
});
});
setupVideoClickHandler();
setupInteractionButtons();
adjustVideoSize();
preloadVideos();
});
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let cookie of cookies) {
cookie = cookie.trim();
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
function loadScript(scriptUrl) {
const script = document.createElement('script');
script.src = scriptUrl;
script.type = 'text/javascript';
document.body.appendChild(script);
}
function initializeNewElements() {
const videos = document.querySelectorAll('.pulse-video');
if (!videos.length) return; // Vérification de la présence de vidéos
videos.forEach(video => {
observer.observe(video);
});
setupInteractionButtons(); // Ajouter les interactions aux nouveaux boutons
}
我的全局 html :
<div class="all_pulse">
<div class="pulse_title">Pulse</div>
<div class="pulse_contains" id="pulse-container">
{% for pulse in pulses %}
{% include 'pages/partial-pulse.html' with pulse=pulse %}
{% endfor %}
</div>
<div class="bar">
<div class="bar-item">
<a href="{% url 'content:index' %}" style="color:white;"><i class="fa-solid fa-house"></i></a>
<a href="{% url 'beats:new_explore' %}" style="color:white;"><i class="fa-solid fa-magnifying-glass"></i></a>
<a href="{% url 'pulses:pulse' %}" style="color:white;"><i class="fa-solid fa-compact-disc fa-lg" style="margin-top:5.5px;"></i></a>
<a href="{% url 'accounts:conversations' %}" style="color:white;"><i class="fa-solid fa-message"></i></a>
<a href="{% url 'accounts:profile' %}" style="color:white;"><i class="fa-solid fa-user"></i></a>
</div>
</div>
</div>
我的HTML是用ajax实现的:
{% load static %}
<script src="{% static 'js/new_pulse.js' %}"></script>
<div class="pulse_item">
<div class="video-wrapper">
<video data-src="{{ pulse.video.url }}" class="pulse-video" src="{{ pulse.video.url }}" muted playsinline></video>
</div>
<div class="comments-section" style="display: none;">
<div class="comment-list">
<h5 class="title_comments">Comments</h5>
{% if pulse.comments.all %}
{% for comment in pulse.comments.all %}
<div class="comment">
<strong>{{ comment.user.username }}:</strong>
<p>{{ comment.text }}</p>
<small style="color:grey;">{{ comment.created_at|date:"d M Y, H:i" }}</small>
</div>
{% endfor %}
{% else %}
<p style="color:white;">Aucun commentaire</p>
{% endif %}
</div>
<textarea class="comment-input" placeholder="Écrivez un commentaire..."></textarea>
<button class="button-up" data-pulse-id="{{ pulse.id }}">Envoyer</button>
</div>
<div class="interacts">
<div class="item-orga">
<button class="like-button" data-pulse-id="{{ pulse.id }}" style="background-color:transparent;color:white;border:none;">
<i class="fa-heart {% if pulse.liked %}fa-solid{% else %}fa-regular{% endif %} fa-xl" style="color: {% if pulse.liked %}#d20000{% else %}floralwhite{% endif %}; margin-right: 4px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);"></i>
<span class="like-count">{% if pulse.like_count %}{{ pulse.like_count }}{% else %}0{% endif %}</span>
</button>
<button class="share-button" data-pulse-id="{{ pulse.id }}" style="background-color:transparent;color:white;border:none;">
<i class="fa-solid fa-share fa-xl" style="color:floralwhite;box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);"></i>
<span class="share-count" data-pulse-id="{{ pulse.id }}">{% if pulse.share_count %}{{ pulse.share_count }}{% else %}0{% endif %} </span>
</button>
<button class="comments-button" data-pulse-id="{{ pulse.id }}" style="background-color:transparent;color:white;border:none;">
<i class="fa-solid fa-comments fa-xl toggle-comments" style="color:floralwhite;box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);"></i>
<span class="comments_count" data-pulse-id="{{ pulse.id }}">{% if pulse.comments %}{{ pulse.comments_count }}{% else %}0{% endif %} </span>
</button>
<button id="global-sound-toggle"><i class="fa-solid fa-volume-xmark" style="color:floralwhite;box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);"></i></button>
</div>
</div>
<div class="data_artist">
<div class="all_artist">
<div class="artist-content">
<div class="profile_picture_pulse">
{% if pulse.user.profile_picture %}
<img src="{{ pulse.user.profile_picture.url }}" class="img-fluid rounded-circle" style="min-width: 50px;max-width: 50px; height: 50px;" alt="Profile Picture">
{% else %}
<img src='/static/images/default_profile.png' class="img-fluid rounded-circle" style="width: 50px; height: 50px;" alt="Profile Picture">
{% endif %}
</div>
<div class="username_pulse">
<strong> <p id="superstrong" style="margin-bottom:0;">{{ pulse.user.username }} </p></strong>
{% if request.user != pulse.user %}
<button id="sub_pulse"
class="follow-toggle-btn btn-style-2 {% if is_followed %}btn-yes{% else %}btn-no{% endif %}"
data-user-id="{{ pulse.user.id }}"
>
{% if is_followed %}Unfollow{% else %}Follow{% endif %}
</button>
<span style="margin-left:5px;" class="follower-count">{{ pulse.follower_count }}</span>
<input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}">
{% endif %}
<button class="toggle-description" aria-label="Show description">
<i class="fa-solid fa-chevron-down"></i>
</button>
</div>
</div>
<div class="description-content">
<p class="description-text">
{{ pulse.description }}<span class="more-text">{{ pulse.description|slice:"100:" }}</span>
{% for hashtag in pulse.hashtags.all %}
<a href="" alt="{{hashtag.name}}"style="text-decoration:none;">#{{hashtag}} </a>
{% endfor %}
</p>
</div>
</div>
<div class="song-content">
<div class="song_pulse">
<img src="/static/images/explore.webp" alt="Cover" class="square-image-x-fill beat-icon-beat_detail">
</div>
</div>
</div>
</div>`
您应该在新内容加载并附加到 DOM 后调用initializeNewElements。您可以在收到新内容并将其附加到脉冲容器后,在 loadMoreContent 函数中执行此操作。
以下是如何修改 loadMoreContent 函数以在加载新内容后调用initializeNewElements:
function loadMoreContent() {
console.log('Trying to load more content...');
if (loading) return;
loading = true;
const url = `/pulses/load-more-pulses/?page=${currentPage}`;
fetch(url)
.then(response => {
console.log('Response status:', response.status);
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
console.log('Data received:', data);
if (data.pulse_data) {
data.pulse_data.forEach(pulse => {
pulseContainer.insertAdjacentHTML('beforeend',
pulse.html);
});
currentPage++;
initializeNewElements(); // Call initializeNewElements
after new content is loaded
}
})
.catch(error => {
console.error('Error loading more content:', error);
})
.finally(() => {
loading = false;
});
}
此外,您还应该在initializeNewElements函数中观察新的视频元素。您已经这样做了,但您还应该为新视频元素添加事件侦听器。以下是您可以修改initializeNewElements函数以添加新视频元素的事件侦听器的方法:
function initializeNewElements() {
const videos = document.querySelectorAll('.pulse-video');
if (!videos.length) return; // Vérification de la présence de vidéos
videos.forEach(video => {
observer.observe(video); // Observe the new video elements
video.addEventListener('error', (e) => {
console.error('Erreur de chargement de la vidéo:', e);
});
video.src = video.getAttribute('data-src');
video.load();
});
setupInteractionButtons(); // Ajouter les interactions aux nouveaux
boutons
setupVideoClickHandler(); // Add event listeners for the new video
elements
}