I’m experiencing an issue with my JavaScript script that is supposed to manage video playback on my platform. Currently, the script works correctly for video elements already present on the page, but it does not account for new video elements that are dynamically loaded. None of the scripts work for these new elements.
Dynamic Loading: Videos are added to the page via AJAX when a user scrolls down.
Using IntersectionObserver: I’m attempting to start or stop video playback based on their visibility using the IntersectionObserver.
Problems Encountered
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.
What can I do to ensure that the script also works for newly loaded video elements?
Are there best practices I should follow to correctly observe these new elements?
My 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
}
My global 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>
My HTML which is implemented with 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>`
SKorfa is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
You should call initializeNewElements after the new content is loaded and appended to the DOM. You can do this in the loadMoreContent function, after the new content is received and appended to the pulseContainer.
Here’s how you can modify your loadMoreContent function to call initializeNewElements after new content is loaded:
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;
});
}
Additionally, you should also observe the new video elements in the initializeNewElements function. You’re already doing this, but you should also add event listeners for the new video elements. Here’s how you can modify your initializeNewElements function to add event listeners for the new video elements:
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
}
Hussnain Iftikhar is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
1