Lazy load баннеров
Сегодня из-за очередных проблем с sol.adbureau.net было решено реализовать ленивую загрузку баннеров.
Начальное решение было использовать iframe, но идея была не очень удачной, т. к. могут быть проблемы с подсчётом кликов, да и модные картинки, увеличивающиеся при наведении, будут вести себя странно. А как этот iframe смотрится в разных браузерах — это вообще сказка)
В итоге родилась идея: при генерации страницы размещать в нужных местах, к примеру, пустые дивы с id с нужным префиксом (<div id=»js-advert-place-{num}»></div>), а сами баннеры загружать в футер вот в такую, к примеру, разметку:
<div id="js-all-advert-block" class="all-advert-block">
<div class="js-advert-block-content" data-number="1">
{баннер}
</div>
<div class="js-advert-block-content" data-number="5">
{баннер}
</div>
<div class="js-advert-block-content" data-number="15">
{баннер}
</div>
</div>
{здесь грузится счётчик показов}
После загрузки страницы размещаем все баннеры на нужных местах:
(function( $ ) {
function moveAdvert() {
$( '#js-all-advert-block' ).find( '.js-advert-block-content' ).each( function() {
var $oldContent = $( this );
// в ие перемещаем
if ( $.browser.msie ) {
$( "#js-advert-place-" + $oldContent.data( "number" ) ).append( $oldContent );
}
// в других браузерах убираем лишнее и вставляем заново
else
{
var cleanedContent = $oldContent.html().replace( /document\.write/gi, 'function a__(){}' );
$( "#js-advert-place-" + $oldContent.data( "number" ) ).html( cleanedContent );
$oldContent.remove();
}
// этот вариант менее хороший - глюки в ие и проблемы с яндекс.директ
// var $newContent = $.browser.msie ? $( $oldContent.html() ) : $oldContent.clone( true );
//// не надо заново создавать скрипты - они заново выполнятся, а там document.write
// $newContent.find( "script, link" ).each( function() {
// $( this ).remove();
// } );
//// вставляем
// $( "#js-advert-place-" + $oldContent.data( "number" ) ).html( $newContent );
//// на старом месте нужно убить всё, кроме скриптов
//// (в скриптах могут быть необходимые для правильного подсчёта кликов переменные)
// $oldContent.find( '*' ).each( function() {
// var $el = $( this );
// if ( ! $( 'script, link', $el ).length && this.tagName.toLowerCase() != 'script' && this.tagName.toLowerCase() != 'link' ) {
// $el.remove();
// }
// } );
} );
}
$( document ).ready( function() {
moveAdvert();
} );
}( jQuery ));
В итоге отвалившаяся внешняя баннерная площадка позволяет корректно отображать сайт без лишних задержек.
За рамками осталась работа сервер-сайда, где нужно собрать данные по всем отображаемым на странице баннерам, а потом вывести их все сразу внизу.
UP:
Всё-таки проблем оказалась куча. К примеру, opera 10.10 (хотя у меня 11.52) некорректно понимает тег script при .html( cleanedContent ), поэтому приходится чистить все теги script, но тогда умирает yandex.direct. К сожалению, на проекте нет нормальной возможности отделить директ от остальной рекламы (нет возможности объяснить тем людям, как что нужно настраивать), поэтому этот способ подходит с большими оговорками.
UP2: Наткнулся на гениальное решение — переопределить document.write, потом немного доработал и вот что получилось:
(function ( $ )
{
// для собирания тега script
var previousValue = '',
// для проверки, собрали ли уже полный тег script
reTestIfScript = new RegExp( '<\/scr' + 'ipt>$', 'i' ),
// чтобы найти id дива, куда вставлять код соли
reGetSolId = new RegExp( "sol\\.adbureau\\.net[^>]+aamsz=(\\d+x\\d+)", 'i' ),
// для проверки, что это относится к яндекс-директу
reYandexDirect = new RegExp( "an\\.yandex\\.ru", 'i' );
// заменяем стандартные методы
document.writeln = document.write = function(value)
{
// если последовательно пишут в документ для получения тега script (document.write( '<SCR' ); document.write( '<IPT src="...' );)
try {
$( value );
}
catch ( e )
{
var joinedValue = previousValue + value;
// сли собрали полный тег скрипт, то вставляем его, куда надо
if ( reTestIfScript.test( joinedValue ) )
{
value = joinedValue;
previousValue = '';
}
// если начали писать тег скрипт, продолжаем до сбора всего тега
else if ( joinedValue.toLowerCase().indexOf( '<sc' ) == 0 )
{
previousValue = joinedValue;
return;
}
// непонятно что это - ничего не делаем
else {
return;
}
}
var parentNodeId,
matches;
// если в строке есть упоминание яндекса - вставляем в яндекс
if ( reYandexDirect.test( value ) ) {
parentNodeId = 'adv_media';
}
// если в строке есть упоминание соли - ищем id и вставляем в нужный div
else if ( matches = value.match( reGetSolId ) ) {
parentNodeId = 'sol' + matches[1];
}
// скрипт вставляем только в нужный элемент
if ( $( value )[0].tagName == "SCRIPT" && parentNodeId )
{
var js = document.createElement( 'SCRIPT' );
var obj = document.getElementById( parentNodeId ).appendChild( js );
// подгрузка внешнего скрипта
if ( $( value ).attr( 'src' ) !== "undefined" ) {
$( obj ).attr( {type: 'text/javascript', 'src': $( value ).attr( "src" )} );
}
// выполнение скрипта
else
{
var reReplaceWrite = new RegExp( '(document\\.write[a-zA-Z]{0,2}\\([^)]+)(\\))', 'ig' );
eval( $( value ).text().replace( reReplaceWrite, "$`$1, '" + parentNodeId + "'$2$'" ) );
}
}
// стили ставим в head
else if ( $( value )[0].tagName == "LINK" )
{
var js = document.createElement( 'LINK' );
var obj = document.getElementsByTagName( 'head' )[0].appendChild( js );
$( obj ).attr( {rel: 'stylesheet', 'href': $( value ).attr( "href" ) , type: 'text/css'} );
}
else if ( $( value )[0].tagName == "STYLE" )
{
if ( $.browser.msie ) {
var css = document.createElement( 'STYLE' );
document.documentElement.firstChild.appendChild( css );
$( obj ).attr( {type: "text/css"} );
css.styleSheet.cssText = $( value ).html();
}
else {
$( value ).appendTo( $( "head" ) );
}
}
// любой другой элемент вставляем в DOM, только если поняли, в какой элемент вставлять
else if ( parentNodeId ) {
$( value ).appendTo( $( "#" + parentNodeId ) );
}
};
}( jQuery ));
Всё бы ничего с этим решением, и даже знает, как собирать тег script из кусочков, но всё-таки мы не можем знать, в какое место DOM должна была производится запись, так что там есть костыли с определением места по контенту, и это решение не универсально.
UP3:
В итоге сделал более универсальное решение для «ленивой» загрузки любого контента. Выкладывать не буду — идеи все те же самые, отличается только серверная часть.
LEAVE A COMMENT
Для отправки комментария вам необходимо авторизоваться.