Смещение контента сайта средствами JavaScript

от автора

Приветствую.

В работу поступила одна задача которая показалась мне интересной. Хотел бы поделиться решением с Хабросообществом.

Задача

Существует скрипт который автоматически подключается к каждой посещаемой пользователем странице, задача скрипта опустить весь контент страницы на 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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *