Вопрос или проблема
У меня возникла проблема с моим скриптом JavaScript, который должен управлять воспроизведением видео на моей платформе. В настоящее время скрипт работает корректно для видеоэлементов, которые уже присутствуют на странице, но не учитывает новые видеоэлементы, которые загружаются динамически. Ни один из скриптов не работает для этих новых элементов.
Динамическая загрузка: видео добавляются на страницу через AJAX, когда пользователь прокручивает вниз. Используя IntersectionObserver: я пытаюсь запустить или остановить воспроизведение видео в зависимости от их видимости, используя IntersectionObserver.
Возникшие проблемы
Отсутствие воспроизведения для новых элементов: Когда новые видеоэлементы загружаются, скрипт не работает для них. Полная неработоспособность скрипта: Ни один из скриптов не работает для этих новых элементов.
Что я могу сделать, чтобы убедиться, что скрипт также работает для вновь загруженных видеоэлементов? Есть ли лучшие практики, которым я должен следовать, чтобы правильно отслеживать эти новые элементы?
Мой 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();
}
});
// Проверяем, достигло ли количество видимых видео 5
if (visibleVideoCount >= 5) {
console.log('пожалуйста, загрузите еще');
loadMoreContent(); // Загружает больше контента
visibleVideoCount = 0; // Сбрасывает счетчик
}
}
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('Ошибка загрузки видео:', 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'); // Получает видео при каждом вызове
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'); // Для нескольких артистов
allArtistElements.forEach(function(artistContent) {
const toggleButton = artistContent.querySelector('.toggle-description');
const descriptionContent = artistContent.querySelector('.description-content');
// Функция для настройки высоты .all_artist.open
function adjustHeight() {
const descriptionHeight = descriptionContent.scrollHeight; // Реальная высота контента
const additionalHeight = 0; // Дополнительная высота для повышения
artistContent.style.height = `${descriptionHeight + additionalHeight}px`; // Настраивает высоту в зависимости от контента
artistContent.style.transform = `translateY(-${descriptionHeight + additionalHeight}px)`;
}
// Функция для открытия и закрытия контента
function toggleArtistContent() {
artistContent.classList.toggle('open');
if (artistContent.classList.contains('open')) {
descriptionContent.style.transform = 'translateY(0)'; // Отменяет translateY
descriptionContent.style.opacity = '1'; // Показывает контент
descriptionContent.style.visibility = 'visible'; // Делает контент видимым
adjustHeight(); // Настраивает высоту .all_artist.open
} else {
descriptionContent.style.transform = 'translateY(-50px)'; // Скрывает элемент
descriptionContent.style.opacity = '0'; // Делает контент невидимым
descriptionContent.style.visibility = 'hidden'; // Скрывает контент
artistContent.style.height="auto"; // Сбрасывает высоту
artistContent.style.transform = `translateY(0)`; // Сбрасывает позицию
}
}
// Обработчик событий на кнопке для переключения отображения
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'); // Получает видео при каждом вызове
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); // Следит за каждым видео
});
}
const loadMoreUrl="/pulses/load-more-pulses/";
function loadMoreContent() {
console.log('Пытаемся загрузить больше контента...');
if (loading) return;
loading = true;
const url = `/pulses/load-more-pulses/?page=${currentPage}`;
fetch(url)
.then(response => {
console.log('Статус ответа:', response.status);
if (!response.ok) {
throw new Error('Сетевой ответ не соответствует ожидаемому');
}
return response.json();
})
.then(data => {
console.log('Полученные данные:', data);
if (data.pulse_data) {
data.pulse_data.forEach(pulse => {
pulseContainer.insertAdjacentHTML('beforeend', pulse.html);
});
currentPage++;
initializeNewElements()
}
})
.catch(error => {
console.error('Ошибка при загрузке более контента:', error);
})
.finally(() => {
loading = false;
});
}
// Вызывайте функции при начальной загрузке
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));
});
});
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('Ссылка скопирована!');
})
.catch(err => {
console.error('Ошибка при копировании ссылки:', err);
});
}
})
.catch(error => console.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; // Проверка наличия видео
videos.forEach(video => {
observer.observe(video);
});
setupInteractionButtons(); // Добавить взаимодействия к новым кнопкам
}
Мой глобальный HTML:
Pulse
{% for pulse in pulses %}
{% include 'pages/partial-pulse.html' with pulse=pulse %}
{% endfor %}
Мой HTML, который реализован с помощью ajax:
{% load static %}
{% if pulse.user.profile_picture %}
{% else %}
{% endif %}
{{ pulse.user.username }}
{% if request.user != pulse.user %}
{{ pulse.follower_count }}
{% endif %}
{{ pulse.description }}{{ pulse.description|slice:"100:" }}
{% for hashtag in pulse.hashtags.all %}
#{{hashtag}}
{% endfor %}
``
</div><div class="s-prose js-post-body" itemprop="text">
Вы должны вызывать initializeNewElements после загрузки нового контента и добавления его в DOM. Вы можете сделать это в функции loadMoreContent, после того как новый контент будет получен и добавлен в pulseContainer.
Вот как вы можете изменить вашу функцию loadMoreContent, чтобы вызвать initializeNewElements после загрузки нового контента:
function loadMoreContent() {
console.log('Пытаемся загрузить больше контента...');
if (loading) return;
loading = true;
const url = `/pulses/load-more-pulses/?page=${currentPage}`;
fetch(url)
.then(response => {
console.log('Статус ответа:', response.status);
if (!response.ok) {
throw new Error('Сетевой ответ не соответствует ожидаемому');
}
return response.json();
})
.then(data => {
console.log('Полученные данные:', data);
if (data.pulse_data) {
data.pulse_data.forEach(pulse => {
pulseContainer.insertAdjacentHTML('beforeend',
pulse.html);
});
currentPage++;
initializeNewElements(); // Вызывайте initializeNewElements
после загрузки нового контента
}
})
.catch(error => {
console.error('Ошибка при загрузке более контента:', error);
})
.finally(() => {
loading = false;
});
}
Кроме того, вам также следует отслеживать новые видеоэлементы в функции initializeNewElements. Вы уже делаете это, но вам также следует добавить обработчики событий для новых видеоэлементов. Вот как вы можете изменить вашу функцию initializeNewElements, чтобы добавить обработчики событий для новых видеоэлементов:
function initializeNewElements() {
const videos = document.querySelectorAll('.pulse-video');
if (!videos.length) return; // Проверка наличия видео
videos.forEach(video => {
observer.observe(video); // Смотрим за новыми видеоэлементами
video.addEventListener('error', (e) => {
console.error('Ошибка загрузки видео:', e);
});
video.src = video.getAttribute('data-src');
video.load();
});
setupInteractionButtons(); // Добавить взаимодействия к новым
кнопкам
setupVideoClickHandler(); // Добавить обработчики событий для новых видео
элементов
}
</div>
Ответ или решение
Ваша проблема заключается в том, что при динамической загрузке новых видеоэлементов они не обрабатываются вашим исходным JavaScript-кодом. Чтобы исправить это, необходимо убедиться, что новые элементы видео, добавляемые на страницу через AJAX, становятся наблюдаемыми для IntersectionObserver
, а также обрабатываются в других аспектах вашего кода.
Вот шаги, которые нужно предпринять для решения вашей проблемы:
-
Обновление функции
loadMoreContent
: После добавления новых видеоэлементов вpulseContainer
, вам нужно вызывать функциюinitializeNewElements()
для того, чтобы они начали обрабатываться.Модифицированная версия вашей функции
loadMoreContent
будет выглядеть следующим образом: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(); // Вызовите initializeNewElements после загрузки нового контента } }) .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; // Проверка наличия видео videos.forEach(video => { observer.observe(video); // Наблюдение за новыми видео video.addEventListener('error', (e) => { console.error('Ошибка загрузки видео:', e); }); video.src = video.getAttribute('data-src'); video.load(); // Загружаем видео }); setupInteractionButtons(); // Добавить взаимодействия для новых кнопок setupVideoClickHandler(); // Добавить обработчики событий для новых видеоэлементов }
-
Убедитесь, что
setupVideoClickHandler
также вызывается: ФункцияsetupVideoClickHandler
должна убедиться, что новый обработчик событий устанавливается на новые видеоэлементы.
Теперь, когда вы полностью реализовали указанные изменения, новый динамически загруженный контент будет корректно обрабатываться вашим скриптом. Каждое новое видео будет добавляться в наблюдение, и к ним будут применяться все логики управления воспроизведением, определенные в вашем коде.
Кроме того, рекомендуется использовать консольные логи для отслеживания изменения статуса загрузки и обработки видео во время тестирования, чтобы убедиться, что каждый элемент правильно инициализируется и обрабатывается.
Следуя этим рекомендациям, вы сможете решить проблему с динамической загрузкой видео и их воспроизведением на вашей платформе.
Комментарии
{% if pulse.comments.all %} {% for comment in pulse.comments.all %}{{ comment.text }}
{{ comment.created_at|date:"d M Y, H:i" }}Нет комментариев
{% endif %}