Lazy load баннеров
Сегодня из-за очередных проблем с sol.adbureau.net было решено реализовать ленивую загрузку баннеров.
Начальное решение было использовать iframe, но идея была не очень удачной, т. к. могут быть проблемы с подсчётом кликов, да и модные картинки, увеличивающиеся при наведении, будут вести себя странно. А как этот iframe смотрится в разных браузерах — это вообще сказка)
В итоге родилась идея: при генерации страницы размещать в нужных местах, к примеру, пустые дивы с id с нужным префиксом (<div id=»js-advert-place-{num}»></div>), а сами баннеры загружать в футер вот в такую, к примеру, разметку:
1 2 3 4 5 6 7 8 9 10 11 12 |
<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> {здесь грузится счётчик показов} |
После загрузки страницы размещаем все баннеры на нужных местах:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
(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, потом немного доработал и вот что получилось:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
(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:
В итоге сделал более универсальное решение для «ленивой» загрузки любого контента. Выкладывать не буду — идеи все те же самые, отличается только серверная часть.
Similar Posts
- None Found
LEAVE A COMMENT
Для отправки комментария вам необходимо авторизоваться.