Сам я использую заплатки уже достаточно давно. Разумеется, основные заплатки ставятся на IE6-8, с которыми, по-видимому, ещё предстоит много возни, пока они, наконец, отомрут. Однако так же не помешает установить их и для старых версий иных браузеров.
Итак, часто JavaScript-разработчики вставляют код, направленный на совместимость, непосредственно внутрь логики основного сценария. Например, если нам нужно установить слушателя события загрузки страницы, мы могли бы написать:
function onLoadListener() {
//some code...
}
if ('addEventListener' in window)
addEventListener("load", onLoadListener, false);
else if ('attachEvent' in window)
attachEvent("onload", onLoadListener)
В принципе, ничего нет плохого в том, что бы так делать, однако проблема заключается в том, что при написании сложных сценариев ваша голова итак забита сложностью решаемой задачи и Вам не захочется отвлекаться ещё и на все эти глупые браузеросовместимости - вам бы хоть как-то её решить. И потом, при отладке и чтении кода вам всё время будет попадаться на глаза этот фрагмент и мозолить глаза. С точки зрения алгоритма он не несёт никакого смысла, а на экране занимает место, которое могли бы занять более полезные смысловые фрагменты кода - видя их одновременно, без прокрутки, Вам могла бы придти в голову ценная мысль, которая не пришла бы, если бы вам пришлось вращать колесо мыши, что бы всё увидеть.
Тем не менее, сократить эту конструкцию до стандартного кода
addEventListener("load", function() {
//some code...
}, false);
мы не можем, поскольку стандарты поддерживают не все браузеры, в которых нам хотелось бы, что бы наш сценарий успешно выполнялся. Для остальных же браузеров у нас есть многочисленные хаки. Так как же быть?
Как только я в своих проектах сталкиваюсь с несовместимостью работы того или иного браузера со стандартными методами, первая мысль у меня возникает о том, что бы вынести обеспечение совместимости в отдельный от основного код, что бы он его не захламлял. У меня уже накопилось достаточно много таких заплаток, латающих различные дыры в поддержке стандартов различным браузерами - главным образом IE. Обычно я помещаю их в отдельный файл проекта, называя его "commons.js" (есть и исключения - например в случае, если это нужно только для тестирования, я считаю не зазорным вставлять такой код просто в начало тестового файла).
В данном случае полезной была бы следующая заплатка:
(function setAddEventListener()
{
if ('attachEvent' in this && !('addEventListener' in this))
addEventListener = function(eventName, handlerб isCapturing) {
if (isCapturing)
throw new Error("We are in IE, so we haven`t way to set event listener on capturing phase of any event to any of HTML-elements");
attachEvent("on" + eventName, handler);
}
})(); //IE patch for window.addEventListener
Здесь элегантно используется то обстоятельство, что ссылка this
при вызове в глобальном контексте в функции указывает на объект window
. По-этому сразу же после объявления функция просто вызывается.
Здесь не могу не отметить, что заплатка получилась лишь частичная - третий параметр метода addEventListener
невозможно эмулировать при помощи метода attachEvent
, доступного в IE. Так что в этом случае приходится вызывать исключение, что бы при отладке или тестировании это обязательно всплыло в виде корректного сообщения.
Если необходима реализация парного метода removeEventListener
, её легко написать по аналогии, реализовав через метод detachEvent
.
Теперь другие элементы, которым так же нужно "привить" правильное поведение, могли бы обрабатываться следующим образом:
setAddEventListener.call(document.getElementById('id1'));
В статье "JavaScript: Реализация pattern`а Singleton" я уже приводил ещё одну заплатку - для безпроблемного объявления объекта XMLHttpRequest
(который я нашёл в англоязычной Wikipedia):// Provide the XMLHttpRequest class for IE 5.x-6.x:
if (typeof XMLHttpRequest == "undefined")
/** @constructor */
XMLHttpRequest = function() {
try { return new ActiveXObject("Msxml2.XMLHTTP.6.0") } catch(e) {}
try { return new ActiveXObject("Msxml2.XMLHTTP.3.0") } catch(e) {}
try { return new ActiveXObject("Msxml2.XMLHTTP") } catch(e) {}
try { return new ActiveXObject("Microsoft.XMLHTTP") } catch(e) {}
throw new TypeError( "This browser does not support XMLHttpRequest." )
};
Напоследок приведу ещё одну заплатку. Она служит для безпроблемной реализации стандартного метода window.getComputedStyle
, отсутствующего в IE вплоть до 8 версии включительно. Кто не знает, это очень удобный метод, позволяющий получать ссылки на объект, содержащий информацию о "вычислимых стилях" для HTML-элемента. Вычислимым называется стиль, специально не установленный разработчиком, однако получившийся в результате вывода браузером этого элемента на странице. IE вместо него имеет свойство computedStyle
, доступное у каждого элемента. К сожалению, оно не может представить вычислимый стиль для псевдоклассов, по-этому заплатку можно вставить так же, как и для addActionListener
`а - лишь частичную.
Таким образом, вот как можно решить данную проблему несовместимости IE со стандартом:
//IE patch for window.getComputedStyle
if (!('getComputedStyle' in window))
getComputedStyle = function(element, pseudoclass) {
if (pseudoclass === null || typeof pseudoclass === 'undefined')
return element.currentStyle;
else
throw new Error("We are in IE, so we haven`t way to get pseudoclass styles of element");
};