Отложенная загрузка контента через div Ajax'ом
Приветствую!
Есть страница .HTML: <html> <head> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script> <style> header { background: cyan; min-height: 100px; } .content_container { background: rgba(255, 0, 0, 0.3); padding: 20px; display: none; } .inner-content { height: 400px; margin: 5px; background: red; } footer { background: cyan; min-height: 500px; } </style> </head> <body> <header> <div>other content</div> <p>text two</p> </header> <div id="haupt"> <div id="acidhaupt" class="haupt-wrap background-color"> <div class="content_container"> <div id="content_a" class="inner-content"></div> <div id="content_b" class="inner-content"></div> <div id="content_c" class="inner-content"></div> <div id="content_d" class="inner-content"></div> </div> </div> </div> <footer> <p>footer content</p> </footer> <script> $(function () { $("#content_a").load("/first.html", function(){ if($(this).html() != '') { $(this).show(); } }); $("#content_b").load("/second.html", function(){ if($(this).html() != '') { $(this).show(); } }); $("#content_c").load("/third.html", function(){ if($(this).html() != '') { $(this).show(); } }); $("#content_d").load("/fourth.html", function(){ if($(this).html() != '') { $(this).show(); } }); }); </script> </body> </html> На странице выводится 4 блока, в которые подгружается Ajax'ом разный контент. После первой загрузки страницы, часть блоков, которые видимы, грузятся Ajax'ом сразу, а остальные блоки, которые находятся за пределами экрана, не должны загружаться. Невидимые блоки должны подгружаться по мере видимости при прокрутке скролла вниз или вверх, в зависимости на каком уровне просмотра перезагрузили страницу или открыли новую страницу, заранее за 50px.. Почитал про IntersectionObserver, но я могу его сделать только под каждый div индивидуально, это слишком много кода для многих блоков, а мне нужно сделать оптимизированный код: один общий скрипт, с применением IntersectionObserver, который можно разместить внизу футера, и в div добавлять активный class, для активации функции отложенной загрузки для этого дива, а для вызов Ajax обернуть в функцию этого скрипта. У каждого div свой Ajax, который вызывает свой файл .html с контентом. За решение этой задачи предлагаю 500 руб., после выполнения задания, на карту банка в РФ, или на yoomoney.ru. Заранее, спасибо, откликнувшимся! |
Vaska,
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <style> header { background: cyan; min-height: 100px; } .content_container { background: rgba(255, 0, 0, 0.3); padding: 20px; } .inner-content { height: 400px; margin: 5px; background: red; overflow: hidden; } footer { background: cyan; min-height: 500px; } </style> <title></title> </head> <body> <header> <div> other content </div> <p>text two</p> </header> <div id="haupt"> <div id="acidhaupt" class="haupt-wrap background-color"> <div class="content_container"> <div class="inner-content" data-url="/first.html"></div> <div class="inner-content" data-url="/second.html"></div> <div class="inner-content" data-url="/third.html"></div> <div class="inner-content" data-url="/fourth.html"></div> </div> </div> </div> <footer> <p>footer content</p> </footer> <script> document.addEventListener('DOMContentLoaded', function() { const lazyShow = divs => { divs.forEach(e => { if (e.intersectionRatio < .3) return; let div = e.target, url = div.dataset.url; (async () => { let response = await fetch(url); let html = await response.text(); div.innerHTML = html; })() observer.unobserve(div); }); }; let observer = new IntersectionObserver(lazyShow, { rootMargin: "50px", threshold: [.3] }); document.querySelectorAll('.inner-content').forEach(div => observer.observe(div)) }); </script> </body> </html> |
рони,
Хитро придумано. Проверил, всё отлично работает! Большое спасибо! Написал в личку. |
рони,
Вылезла одна проблема: в вызываемых страницах контента, например в third.html, перестал инициализироваться ID в DIV'е (например id="mc_point_info"), к которому привязан скрипт, размещённый в этой странице third.html. Теперь, чтобы инициализировать ID, мне пришлось вынести скрипт из файла third.html - на страницу, из которой вызывается third.html и обернуть его в DOMNodeInserted. Так работает: $.getScript("{$Think.PLUGINS_SITE_ROOT}/jquery.SuperSlide.2.1.3.js", function() { $(document).on('DOMNodeInserted', '#ds-bundling', function() { setTimeout( function(){ jQuery('.mc_point_info').slide({mainCell:"ul",effect:"left",trigger:"click",switchLoad:"_src",delayTime:1000,prevCell:".btn-prev",nextCell:".btn-next",vis:5}); }, 0); }); }); Раньше было так: $.getScript("{$Think.PLUGINS_SITE_ROOT}/jquery.SuperSlide.2.1.3.js", function() { jQuery('.mc_point_info').slide({mainCell:"ul",effect:"left",trigger:"click",switchLoad:"_src",delayTime:1000,prevCell:".btn-prev",nextCell:".btn-next",vis:5}); }); По причине того, что из файла third.html приходится переносить вызов скрипта на страницу, откуда я хотел убрать всё лишнее, немного потерялся смысл. Почему при простом вызове Ajax'ом файла third.html id="mc_point_info" инициализировался скриптом внутри id="mc_point_info", а сейчас нет? Может возможно учесть это в вашем коде? С другими файлами, которые вызываются вашим скриптом, где есть скрипты - такая же беда. Или можно обернуть скрипт внутри файла third.html, чтобы его не выносить из файла, и чтобы он инициализировал id="mc_point_info"? У меня не получилось. Получилось только когда вынес в файл из которого вызывается файл third.html. |
Vaska,
так поставьте вместо fetch ваш прежний код, и id вставьте какое вам нужно. |
Vaska,
как-то так, зачем нужно id не понимаю, jquery учитывает скрипты на странице, парсит и запускает отдельно, и то что вы сделали вручную, там заложено. <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <style> header { background: cyan; min-height: 100px; } .content_container { background: rgba(255, 0, 0, 0.3); padding: 20px; } .inner-content { height: 400px; margin: 5px; background: red; overflow: scroll; } footer { background: cyan; min-height: 500px; } </style> <title></title> </head> <body> <header> <div> other content </div> <p>text two</p> </header> <div id="haupt"> <div id="acidhaupt" class="haupt-wrap background-color"> <div class="content_container"> <div id="content_a" class="inner-content" data-url="/first.html"></div> <div id="content_b" class="inner-content" data-url="/second.html"></div> <div id="content_c" class="inner-content" data-url="/third.html"></div> <div id="content_d" class="inner-content" data-url="/fourth.html"></div> </div> </div> </div> <footer> <p>footer content</p> </footer> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script> <script> document.addEventListener('DOMContentLoaded', function() { const lazyShow = divs => { divs.forEach(e => { if (e.intersectionRatio < .3) return; let div = e.target, url = div.dataset.url; $(div).load(url); observer.unobserve(div); }); }; let observer = new IntersectionObserver(lazyShow, { rootMargin: "10px", threshold: [.3] }); document.querySelectorAll('.inner-content').forEach(div => observer.observe(div)) }); </script> </body> </html> |
рони,
сейчас всё отлично работает. Большое спасибо! |
рони,
можно ли определить область применения вашего скрипта, через назначение по ID. Как здесь $(document).on('DOMNodeInserted', '#haupt', function() { Но ругается, если я добавляю второй аргумент document.addEventListener('DOMContentLoaded', '#haupt', function() { . |
Цитата:
document.querySelectorAll('#haupt .inner-content').forEach |
рони,
Спасибо! |
Vaska,
вариант если ещё блоки с url добавляются к исходным. <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <style> header { background: cyan; min-height: 100px; } .content_container { background: rgba(255, 0, 0, 0.3); padding: 20px; } .inner-content { height: 400px; margin: 5px; background: red; overflow: scroll; } footer { background: cyan; min-height: 500px; } </style> <title></title> </head> <body> <header> <div> other content </div> <p>text two</p> </header> <div id="haupt"> <div id="acidhaupt" class="haupt-wrap background-color"> <div class="content_container"> <div id="content_a" class="inner-content" data-url="/first.html"></div> <div id="content_b" class="inner-content" data-url="/second.html"></div> <div id="content_c" class="inner-content" data-url="/third.html"></div> <div id="content_d" class="inner-content" data-url="/fourth.html"></div> </div> </div> </div> <footer> <p>footer content</p> </footer> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script> <script> document.addEventListener('DOMContentLoaded', function() { const lazyShow = divs => { divs.forEach(e => { if (e.intersectionRatio < .3) return; let div = e.target, url = div.dataset.url; $(div).load(url); observer.unobserve(div); }); }; let observer = new IntersectionObserver(lazyShow, { rootMargin: "10px", threshold: [.3] }); let parent = document.querySelector('#haupt'); const add = () => parent.querySelectorAll('.inner-content:not(.load)').forEach(div => { observer.observe(div); div.classList.add('load'); }); add(); let listObserver = new MutationObserver(add); //отслеживание появления новых блоков .inner-content в #haupt listObserver.observe(parent, { childList: true, subtree: true}); }); </script> </body> </html> |
Цитата:
Можете подробней объяснить логику последнего варианта? |
Цитата:
document.querySelector('#haupt .content_container').insertAdjacentHTML('beforeend', '<div id="content_e" class="inner-content" data-url="/six.html"></div>'); |
Vaska,
в нормальном случае, что добавляет блок, то и должно ставить блок на обработку, но можно использовать "сторожа" который добавленный блок, самостоятельно поставит в очередь(слежение,видно на экране, грузить контент или рано) на обработку. |
рони,
я так и не понял преимущества отслеживать блок отдельно. Я добавил на страницу , в футер, в теги <script> document.querySelector('#haupt .content_container').insertAdjacentHTML('beforeend', '<div id="content_e" class="inner-content" data-url="/six.html"></div>'); На странице, этот блок разместился вторым блоком, по счёту, в пределах ('#haupt .content_container'). У первого блока нет в классе inner-content. Всем классам, у кого есть inner-content, добавился класс load. И новому блоку тоже, при первой загрузке страницы. Что это даёт, ведь уже есть класс inner-content. При достижении нижней части ('#haupt .content_container'), этот второй блок подгрузился. Это же не контролируемый вывод блока в области ('#haupt .content_container'). Я точно не понимаю задумки и где можно применить эту фичу. Было бы хорошо на примерах объяснить где применять. |
Vaska,
забей, я пытался угадать, что-бы это значило Цитата:
|
Vaska,
пример использования, нажмакать любое количество блоков, во все блоки и созданные далее, будет грузиться контент, как только блок появится на экране. <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <style> header { background: cyan; min-height: 100px; } .content_container { background: rgba(255, 0, 0, 0.3); padding: 20px; } .inner-content { height: 400px; margin: 5px; background: red; overflow: scroll; background-repeat: no-repeat; background-size: cover; } footer { background: cyan; min-height: 500px; } </style> <title></title> </head> <body> <header> <div> other content </div> <p>text two</p> <script> let r = 1; function addDiv() { document.querySelector('#haupt .content_container').insertAdjacentHTML('beforeend', `<div class="inner-content" data-url="https://picsum.photos/300/100?${r++}"></div>`); } </script> <input name="" type="button" value="addDiv" onclick="addDiv()" > </header> <div id="haupt"> <div id="acidhaupt" class="haupt-wrap background-color"> <div class="content_container"> </div> </div> </div> <footer> <p>footer content</p> </footer> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script> <script> document.addEventListener('DOMContentLoaded', function() { const lazyShow = divs => { divs.forEach(e => { if (e.intersectionRatio < .3) return; let div = e.target, url = div.dataset.url; setTimeout(_=> div.style.backgroundImage = `url(${url})`, 600)//имитация грузим контент с сервера observer.unobserve(div); }); }; let observer = new IntersectionObserver(lazyShow, { rootMargin: "10px", threshold: [.3] }); let parent = document.querySelector('#haupt'); const add = () => parent.querySelectorAll('.inner-content:not(.load)').forEach(div => { observer.observe(div); div.classList.add('load'); }); add(); let listObserver = new MutationObserver(add); //отслеживание появления новых блоков .inner-content в #haupt listObserver.observe(parent, { childList: true, subtree: true}); }); </script> </body> </html> |
Цитата:
Это значит, что переинициализация будет проведена только внутри блока с id="haupt". |
Цитата:
|
рони,
У меня браузер ругается на кавычки в последнем скрипте. Uncaught SyntaxError: `` literal not terminated before end of script А здесь на форме работает. У меня нет. Я думаю, что последние две модификации скрипта уже вышли за рамки моей задачи. Моя задача решена уже здесь https://javascript.ru/forum/545079-post6.html |
рони,
В моём коде встретился вызов Ajax вида: $("#consult").load("{:url('consult.html',['goods_id'=>$goods.goods_id])}", function(){ // Membership card $(this).find('[dstype="mcard"]').membershipCard({type:'shop'}); }); В вызываемом файле consult.html, есть <a href="javascript:void(0)" target="_blank" data-param="{'id':{$buyer_id}}" dstype="mcard">{$buyer_name}</a> Как с помощью вашего скрипта передать параметры в файл consult.html, при его вызове? |
Vaska,
вместо $(div).load(url); будет if(url.includes('consult.html')) $(div).load("{:url('consult.html',['goods_id'=>$goods.goods_id])}", function(){ // Membership card $(this).find('[dstype="mcard"]').membershipCard({type:'shop'}); }); else $(div).load(url); |
У меня браузеры ругаются на кавычки в этой строке:
setTimeout(_=> div.style.backgroundImage = `url(${url})`, 600)//имитация грузим контент с сервера Uncaught SyntaxError: `` literal not terminated before end of script |
Vaska,
заменил кавычки на обычные <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <style> header { background: cyan; min-height: 100px; } .content_container { background: rgba(255, 0, 0, 0.3); padding: 20px; } .inner-content { height: 400px; margin: 5px; background: red; overflow: scroll; background-repeat: no-repeat; background-size: cover; } footer { background: cyan; min-height: 500px; } </style> <title></title> </head> <body> <header> <div> other content </div> <p>text two</p> <script> let r = 1; function addDiv() { document.querySelector('#haupt .content_container').insertAdjacentHTML('beforeend', '<div class="inner-content" data-url="https://picsum.photos/300/100?' + (r++) +'"></div>'); } </script> <input name="" type="button" value="addDiv" onclick="addDiv()" > </header> <div id="haupt"> <div id="acidhaupt" class="haupt-wrap background-color"> <div class="content_container"> </div> </div> </div> <footer> <p>footer content</p> </footer> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script> <script> document.addEventListener('DOMContentLoaded', function() { const lazyShow = divs => { divs.forEach(e => { if (e.intersectionRatio < .3) return; let div = e.target, url = div.dataset.url; setTimeout(_=> div.style.backgroundImage = 'url('+url+')', 600)//имитация грузим контент с сервера observer.unobserve(div); }); }; let observer = new IntersectionObserver(lazyShow, { rootMargin: "10px", threshold: [.3] }); let parent = document.querySelector('#haupt'); const add = () => parent.querySelectorAll('.inner-content:not(.load)').forEach(div => { observer.observe(div); div.classList.add('load'); }); add(); let listObserver = new MutationObserver(add); //отслеживание появления новых блоков .inner-content в #haupt listObserver.observe(parent, { childList: true, subtree: true}); }); </script> </body> </html> |
Vaska,
тоже самое, обычный вариант без "сторожа-наблюдателя", добавили элемент, внесли элемент в очередь. <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <style> header { background: cyan; min-height: 100px; } .content_container { background: rgba(255, 0, 0, 0.3); padding: 20px; } .inner-content { height: 400px; margin: 5px; background: red; overflow: scroll; background-repeat: no-repeat; background-size: cover; } footer { background: cyan; min-height: 500px; } </style> <title></title> </head> <body> <header> <div> other content </div> <p>text two</p> <input name="" type="button" value="addDiv" onclick="addDiv()"> </header> <div id="haupt"> <div id="acidhaupt" class="haupt-wrap background-color"> <div class="content_container"> </div> </div> </div> <footer> <p>footer content</p> </footer> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script> <script> document.addEventListener('DOMContentLoaded', function() { const lazyShow = divs => { divs.forEach(e => { if (e.intersectionRatio < .3) return; let div = e.target, url = div.dataset.url; setTimeout(_ => div.style.backgroundImage = 'url(' + url + ')', 600) //имитация грузим контент с сервера observer.unobserve(div); }); }; let observer = new IntersectionObserver(lazyShow, { rootMargin: "10px", threshold: [.3] }); let parent = document.querySelector('#haupt'); const add = () => parent.querySelectorAll('.inner-content:not(.load)').forEach(div => { observer.observe(div); div.classList.add('load'); }); add(); /*let listObserver = new MutationObserver(add); //отслеживание появления новых блоков .inner-content в #haupt listObserver.observe(parent, { childList: true, subtree: true});*/ let r = 1; document.querySelector('[value="addDiv"]').addEventListener('click', function() { document.querySelector('#haupt .content_container').insertAdjacentHTML('beforeend', '<div class="inner-content" data-url="https://picsum.photos/300/100?' + (r++) + '"></div>'); add(); }) }); </script> </body> </html> |
рони,
сейчас получаю такую ошибку (это предпоследний вариант вашего кода) ![]() Я этот кусок скрипта разместил в теле страницы, закомментировал <div id="consulting_demo" class="dss-loading"></div> Див вообще ненужно размещать? |
Скорее всего я что-то не так делаю, но мне скрипт нужен, чтобы по размеченным на странице дивам, тянуть Ajax'ом контент.
А последние версии вашего кода куда-то не туда пошли. И я запутался. У меня нет кнопок, чтобы на них жмакать и так подгружать контент, это не нужно. |
Цитата:
load("{:url('consult.html',['goods_id'=>$goods.goods_id])}", |
Цитата:
|
Цитата:
Заполнил адрес и получилось так: if(url.includes('/home/Goods/consulting.html')) $(div).load("/home/Goods/consulting.html?goods_id=2&store_id=1", function(){ // Membership card $(this).find('[dstype="mcard"]').membershipCard({type:'shop'}); }); else $(div).load(url); В итоге, получаю ошибку на скриншоте. |
Vaska,
так попробуйте <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <style> header { background: cyan; min-height: 100px; } .content_container { background: rgba(255, 0, 0, 0.3); padding: 20px; } .inner-content { height: 400px; margin: 5px; background: red; overflow: scroll; } footer { background: cyan; min-height: 500px; } </style> <title></title> </head> <body> <header> <div> other content </div> <p>text two</p> </header> <div id="haupt"> <div id="acidhaupt" class="haupt-wrap background-color"> <div class="content_container"> <div id="content_a" class="inner-content" data-url="/first.html"></div> <div id="content_b" class="inner-content" data-url="/second.html"></div> <div id="content_c" class="inner-content" data-url="/third.html"></div> <div id="content_d" class="inner-content" data-url="/fourth.html"></div> </div> </div> </div> <footer> <p>footer content</p> </footer> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script> <script> document.addEventListener('DOMContentLoaded', function() { const lazyShow = divs => { divs.forEach(e => { if (e.intersectionRatio < .3) return; let div = e.target, url = div.dataset.url; if(url.indexOf('/home/Goods/consulting.html') !== -1) $(div).load("/home/Goods/consulting.html?goods_id=2&store_id=1", function(){ // Membership card $(div).find('[dstype="mcard"]').membershipCard({type:'shop'}); }); else $(div).load(url); observer.unobserve(div); }); }; let observer = new IntersectionObserver(lazyShow, { rootMargin: "10px", threshold: [.3] }); document.querySelectorAll('.inner-content').forEach(div => observer.observe(div)) }); </script> </body> </html> |
Цитата:
Хотелось, чтобы и с параметрами работал. Но и на этом спасибо, первоначальная задача решена. |
Цитата:
Все их запихивать в один скрипт не годится. Скрипт будет огромных размеров или на каждой странице плодить экземпляр скрипта, что просто ухудшит то, что сейчас есть без скрипта. |
Часовой пояс GMT +3, время: 00:37. |