В работу поступила одна задача которая показалась мне интересной. Хотел бы поделиться решением с Хабросообществом.
Задача
Существует скрипт который автоматически подключается к каждой посещаемой пользователем странице, задача скрипта опустить весь контент страницы на N пикселей для отрисовки баннера в верхней части страницы. Основные требования были что бы скрипт был един для всех браузеров, а так же что бы верстка не ломалась. Ниже приведу сам скрипт, и некоторые умозаключения. Если интересно — добро пожаловать под кат.
Решение
На каждую страницу, сразу после открывающегося тега подключается скрипт который и должен проводить смещение контента.
Первой мыслью было немного оптимизировать сервер, и кроме вставки js оборачивать весь контент в слой с относительным позиционированием, который и сдвигать. Но, увы, с этим вариантом возникло много проблем. Вот основные из них:
- CSS, на многих сайтах стили привязываются к тегу body, и приходилось перелопачивать и переназначать огромное количество css, что негативно сказывалось на производительности
- JavaScript, тот же jQuery при инициализации манипулирует конструкцией document.body, и получает элементы относительно его. Лечилось болезнь переназначением document.body = document.getElementById("_NEW_DIV"); Но тут возникала следующая проблема — эта строка должна была отработать первой. Перед остальными скриптами сайта.
- Снятие обработчиков с body. onLoad etc. Это приходилось делать на стороне сервера. Что было не гуд, так как сервер часть мы старались разгрузить по полной.
Кроме этого было еще множество проблем связаных с этим методом. Если будут желающие — распишу подробнее.
Поскольку прошлый метод был отброшен было принято решение зайти с другой стороны.
Скрипт проходился по всему DOM дереву, и смещал нужные элементы.
Логика алгоритма следующая:
- _M$.start() запускается и ждет пока будет загружен весь документ.
- _M$.shift(document.body, 0) начинает рекурсивно проходить по всему документу. первый раз родителем передается document.body, для него выбираются все child. Затем идет проверка на position. Нас интересует position 3-х типов. absolute, fixed, и relative
- Второй аргумент является флагом который указывает нужно ли смещать элементы с absolute позицией.
- В случае если position = relative; мы меняем флаг смещения на 1 и идем глубже в этот элемент. В дальнейшем в нем нас будут интеревовать только child с position fixed
- Если position = absolute и флаг 0 — то идет смещение элемента на нужную высоту. Флаг меняется на 1. Если флаг уже 1 то просто идем глубже
- Если position = fixed то мы всегда смещаем элемент
- На том же этапе когда мы проверяли позицию — мы так же проверяем display. В случае если он является grid (то есть moz-grid, ie-grid и т.д.) и флаг = 0 элементу присваивалось relative позиционирование и шло его смещение
- В конце идет отрисовка слоя в самом верху страницы. С position = static он смещал автоматически все обычные слои
А вот и сам скрипт:
(function(){ _D$ = { e: {}, g:function(id){ if(typeof _D$.e[id] == "undefined") _D$.e[id] = document.getElementById(id); return _D$.e[id]; }, c:function(e){ if(typeof e != "undefined" && e.hasChildNodes()) return e.childNodes; }, s:function(e, s){ if(document.defaultView&&document.defaultView.getComputedStyle){ return document.defaultView.getComputedStyle(e,"").getPropertyValue(s) } else if(e.currentStyle){ return e.currentStyle[ s.replace( /-(w)/g,function(strMatch,p1){ return p1.toLowerCase() } ) ] } return""; } } _B$ = { w: 600, h: 100, insert:function(){ el = document.createElement("div"); el.setAttribute("id", "_HSS_TOP"); el.setAttribute("style", "width: 100% !important; height: "+_B$.h+"px !important; background-color: transparent !important;"); document.body.insertBefore(el, document.body.firstChild); } } _M$ = { start:function(){ if( document.readyState != "complete") return setTimeout(arguments.callee, 100); _M$.shift(document.body, 0); _B$.insert(); }, shift:function(e, lable){ var e = _D$.c(arguments[0]); if(typeof e == "undefined") return; for(var key in e){ if(e[key].nodeType != 1 || e[key].nodeName == "SCRIPT" || e[key].nodeName == "STYLE"){ delete e[key]; continue; } var p = _D$.s(e[key], "position"), l = lable; if(l == 1 && p != "fixed") p = ""; else{ var d = _D$.s(e[key], "display"); if(d.indexOf("grid") != -1){ e[key].style.position = "relative"; p = "absolute"; } } if(p == "absolute" || p == "fixed"){ _M$.shift_e(e[key]); l = 1; }else if(p == "relative") l = 1; _M$.shift(e[key], l); } }, shift_e:function(e){ var b = _D$.s(e, "bottom"); if(b != "auto") return; var t = _D$.s(e, "top"); t += _B$.h; if(t == "auto") t = 0; if(t.replace) t = t.replace(/[^0-9-\.]+/ig, "")*1; e.style.top = t+"px"; e.setAttribute("edit", "true"); } } _M$.start(); } )();
P.S. Заранее прошу прощения если что то не так. Уже довольно поздно, топик дописываю из последних сил)
P.P.S. Замечания по грамматическим ошибкам — прошу писать в личку
P.P.P.S. Если у кого то есть идеи по оптимизации алгоритма — с огромнейшим удовольствием выслушаю
ссылка на оригинал статьи http://habrahabr.ru/post/159021/
Добавить комментарий