Статьи

Почему ваш JavaScript, разработанный в Firefox, может не работать в IE


Так приятно найти краткие руководства по срыву проблем веб-разработки, которые являются обычным явлением, но плохо документированы или их легко найти. Сегодня я нашел ответ на SO о разработке JavaScript для Firefox и IE, который может быть очень полезным для многих людей, если они смогут его найти. Я помещаю его в зону Web Builder, так что, надеюсь, его найдут другие веб-разработчики. Ответом стала вики сообщества, отредактированная
Дэвидом Моррисси ,
Марселем Корпелем ,
Крозиным и
Матиасом Биненсом . Остальная часть этого поста — вся их хорошая работа:

Примечание:IE9 устраняет многие из следующих проблем, поэтому многое из этого применимо только к IE8 и ниже и в определенной степени к IE9 в режиме причуд. Например, IE9 изначально поддерживает SVG, <canvas>, <audio> и <video>, однако для их доступности необходимо включить режим соответствия стандартам.

Генеральная:

  • Проблемы с частично загруженными документами. Рекомендуется добавить свой JavaScript в window.onload или подобное событие, так как IE не поддерживает много операций в частично загруженных документах.

  • Отличающиеся атрибуты : в CSS это elm.style.styleFloat в IE против elm.style.cssFloat в Firefox. В тегах <label> доступ к атрибуту for осуществляется с помощью elm.htmlFor в IE против elm.for в Firefox. Обратите внимание, что for зарезервировано в IE, поэтому elm [‘for’], вероятно, является лучшей идеей, чтобы не дать IE вызвать исключение.


Базовый язык JavaScript:

  • Доступ к символам в строках : ‘string’ [0] не поддерживается в IE, поскольку это не входит в исходные спецификации JavaScript. Используйте ‘string’.charAt (0) или’ string’.split (») [0], отметив, что массивы значительно быстрее, чем строки в IE.

  • Запятые перед концом объектов: например, {‘foo’: ‘bar’,} не допускаются в IE.


Вопросы, связанные с элементами:

  • Получение документа IFrame :

    • Firefox: IFrame.contentWindow.document
    • IE: IFrame.contentDocument
    • (IFrame.contentWindow ссылается на окно в обоих браузерах.)

       

  • Canvas: версии IE до IE9 не поддерживают элемент <canvas>. Однако IE поддерживает VML, который является аналогичной технологией, и explorercanvas может предоставить встроенную оболочку для элементов <canvas> для многих операций. Имейте в виду, что IE8 в режиме соответствия стандартам в 10 раз медленнее и имеет гораздо больше сбоев, чем в режиме совместимости при использовании VML.

  • SVG: IE9 изначально поддерживает SVG. IE6-8 может поддерживать SVG, но только с внешними плагинами, причем только некоторые из этих плагинов поддерживают манипулирование JavaScript.

  • <audio> и <video>: поддерживаются только в IE9.

  • Динамическое создание переключателей: в IE <8 есть ошибка, из-за которой переключатели, созданные с помощью document.createElement, не проверяются. См. Также Как динамически создать переключатель в Javascript, который работает во всех браузерах? для способа обойти это.

  • Встроенный JavaScript в тегах <a href> и конфликтах onbeforeunload в IE: если в теге href есть встроенный JavaScript (например, <a href=»javascript: doStuff()»>, то IE всегда будет отображать сообщение, возвращенное из onbeforeunload если обработчик onbeforeunload не будет удален заранее, см. также функцию Javascript на закрытии веб-страницы .

  • Отличия событий тега <script>: onsuccess и onerror не поддерживаются в IE и заменяются специфичным для IE onreadystatechange, который запускается независимо от того, удалась загрузка или не удалась. Смотрите также JavaScript Madness для получения дополнительной информации.


Размер элемента / положение / прокрутка и положение мыши:

  • Получение размера / позиции элемента : ширина / высота элементов иногда равна elm.style.pixelHeight / Width в IE, а не elm.offsetHeight / Width, но ни один из них не является надежным в IE, особенно в режиме причуд, и иногда один дает лучший результат, чем другой.

    elm.offsetTop и elm.offsetLeft часто сообщаются неверно, что приводит к нахождению неправильных позиций элементов, поэтому всплывающие элементы и т. д. во многих случаях отключаются на несколько пикселей.

    Также обратите внимание, что если элемент (или родитель элемента) не имеет ни одного отображения, IE будет вызывать исключение при доступе к атрибутам размера / позиции, а не возвращать 0, как это делает Firefox.

  • Получить размер экрана (получение видимой области экрана):

    • Firefox: window.innerWidth / innerHeight
    • Режим стандартов IE: document.documentElement.clientWidth / clientHeight
    • IE причудливый режим: document.body.clientWidth / clientHeight
  • Положение прокрутки документа / положение мыши : это на самом деле не определено w3c, поэтому является нестандартным даже в Firefox. Чтобы найти scrollLeft / scrollTop документа:

    • Firefox и IE в режиме причуд: document.body.scrollLeft / scrollTop
    • IE в стандартном режиме: document.documentElement.scrollLeft / scrollTop
    • ПРИМЕЧАНИЕ. Некоторые другие браузеры также используют pageXOffset / pageYOffset.

      function getDocScrollPos() {
       var x = document.body.scrollLeft ||
               document.documentElement.scrollLeft ||
               window.pageXOffset || 0,
           y = document.body.scrollTop ||
               document.documentElement.scrollTop ||
               window.pageYOffset || 0;
       return [x, y];
      };<span class="kwd"></span><span class="pun"></span><span class="pln">
      </span>

    Чтобы получить позицию курсора мыши, evt.clientX и evt.clientY в событиях mousemove будут указывать позицию относительно документа без добавления позиции прокрутки, поэтому необходимо будет включить предыдущую функцию:

    var mousepos = [0, 0];
    document.onmousemove = function(evt) {
     evt = evt || window.event;
     if (typeof evt.pageX != 'undefined') {
      // Firefox support
      mousepos = [evt.pageX, evt.pageY];
     } else {
      // IE support
      var scrollpos = getDocScrollPos();
      mousepos = [evt.clientX+scrollpos[0], evt.clientY+scrollpos[1]];
     };
    };<span class="kwd"></span><span class="pun"></span><span class="pln">
    </span>

Выбор / диапазонов:

  • <textarea> и <input> selections : selectionStart и selectionEnd не реализованы в IE, и на их месте есть проприетарная система «диапазонов», см. также Как получить позицию курсора в textarea? ,

  • Получение текущего выделенного текста в документе:

    • Firefox: window.getSelection (). ToString ()
    • IE: document.selection.createRange (). Text

Получение элементов по ID:

  • document.getElementById также может ссылаться на атрибут name в формах (в зависимости от того, что определено первым в документе), поэтому не используйте разные элементы с одинаковыми именами и идентификаторами! Это восходит к тем дням, когда id не был стандартом w3c. document.all значительно быстрее, чем document.getElementById, но это еще более неприятно, так как он всегда расставляет приоритеты имени перед id. Я лично использую этот быстрый «гибридный» код, прибегая к дополнительным проверкам, чтобы быть уверенным:

    function getById(id) {
     var e;
     if (document.all) {
      e = document.all[id];
      if (e && e.tagName && e.id === id) {
       return e;
      };
     };
     e = document.getElementById(id);
     if (e && e.id === id) {
      return e;
     } else if (!e) {
      return null;
     } else {
      throw 'Element found by `name` instead of `id`: ' + id;
     };
    };<span class="kwd"></span><span class="pun"></span><span class="pln">
    </span>

Проблемы с доступом только для чтения innerHTML:

  • IE не поддерживает установку innerHTML элементов table, tbody, tfoot, thead и tr. Вот функция, которая работает вокруг этого:

    function setHTML(elm, html) {
     // Try innerHTML first
     try {
      elm.innerHTML = html;
     } catch (exc) {
      // IE 6-8 don't support setting innerHTML for
      // TABLE, TBODY, TFOOT, THEAD, and TR directly
      var tn = elm.tagName.toLowerCase();
      if (tn === 'table') {
       replace(getElm('<table>' + html + '</table>'));
      } else if (tn in {tbody:0, tfoot:0, thead:0}) {
       replace(getElm('<table><tbody>' + html + '</tbody></table>').firstChild);
      } else if (tn === 'tr') {
       replace(getElm('<table><tbody><tr>' + html + '</tr></tbody></table>').firstChild.firstChild);
      } else {
       throw exc;
      };
      function getElm(html) {
       // Create a new element and return the first child
       var e = document.createElement('div');
       e.innerHTML = html;
       return e.firstChild;
      };
      function replace(elms) {
       // Remove the old elements from `elm`
       var x = elm.children.length, y = 0, c;
       while (c = elm.children[--x]) {
        elm.removeChild(c);
       };
       // Add the new elements from `elms` to `elm`
       while (c = elms.children[y++]) {
        elm.appendChild(c);
       };
      };
     };
    };

     

    Также обратите внимание, что IE требует добавления <tbody> в <table> перед добавлением <tr> к этому элементу <tbody> при создании с использованием document.createElement, например:

    var table = document.createElement('table');
    var tbody = document.createElement('tbody');
    var tr = document.createElement('tr');
    var td = document.createElement('td');
    table.appendChild(tbody);
    tbody.appendChild(tr);
    tr.appendChild(td);
    // and so on<span class="kwd"></span><span class="com"></span><span class="pln">
    </span>

Различия в событиях:

  • Получение переменной события: события DOM не передаются функциям в IE и доступны как window.event. Один из распространенных способов получения события — использовать, например,
    elm.onmouseover = function (evt) {evt = evt || window.event},
    который по умолчанию равен window.event, если evt не определено.

  • Основные различия в кодах событий: Коды ключевых событий сильно различаются, хотя, если вы посмотрите на Quirksmode или JavaScript Madness , вряд ли они специфичны для IE, Safari и Opera снова различаются.

  • Различия в событиях мыши: атрибут кнопки в IE является бит-флагом, который позволяет использовать несколько кнопок мыши одновременно:

    • Слева: 1 (var isLeft = evt.button & 1)
    • Справа: 2 (var isRight = evt.button & 2)
    • Центр: 4 (var isCenter = evt.button & 4)

      Модель W3C (поддерживаемая Firefox) является менее гибкой, чем модель IE: допускается одновременное использование только одной кнопки: слева 0, справа 2 и центр 1. Обратите внимание, что, как упоминает Питер-Пол Кох , это очень нелогично, так как 0 обычно означает «нет кнопки».

      offsetX и offsetY проблематичны, и, вероятно, лучше избегать их в IE. Более надежный способ получить offsetX и offsetY в IE — получить позицию относительно позиционированного элемента и вычесть его из clientX и clientY.

      Также обратите внимание, что в IE, чтобы получить двойной щелчок в событии click, вам нужно зарегистрировать и функцию click, и событие dblclick для функции. Firefox запускает как щелчок, так и dblclick при двойном щелчке, поэтому для такого же поведения необходимо специфическое для IE обнаружение.

  • Различия в модели обработки событий: как собственная модель IE, так и модель Firefox поддерживают обработку событий снизу вверх, например, если есть события в обоих элементах <div> <span> </ span> </ div> then events будет вызывать в промежутке, а не div, а не порядок, в котором они связаны, если используется традиционный, например, elm.onclick = function (evt) {}.

    События «захвата», как правило, поддерживаются только в Firefox и т. Д., Что вызывает события div и span в порядке сверху вниз. В IE есть elm.setCapture () и elm.releaseCapture () для перенаправления событий мыши из документа в элемент (в данном случае, elm) перед обработкой других событий, но они медленные и обычно их следует избегать.

    • Fire Fox:

      Приложить : elm.addEventListener (тип, слушатель, useCapture [истина / ложь])
      Отделить : elm.removeEventListener (тип, слушатель, useCapture)
      (тип , например , ‘Mouseover’ без на)

    • IE: в IE можно добавить только одно событие данного типа для элемента — исключение возникает, если добавлено более одного события одного типа. Также обратите внимание, что this относится к окну, а не к связанному элементу в функциях событий (так что это довольно бесполезно):

      Приложить : elm.attachEvent (Sevent, fpNotify)
      Отделить : elm.detachEvent (Sevent, fpNotify)
      (Sevent является , например , ‘OnMouseOver’)

  • Отличия атрибутов события:

    • Остановить события от обработки любыми другими функциями прослушивания :

      Firefox: evt.stopPropagation ()
      IE: evt.cancelBubble = true

    • Остановить, например, ключевые события от вставки символов или остановки проверки флажков:

      Firefox: evt.preventDefault ()
      IE: evt.returnValue = false
      Примечание. Просто возвращая значение false при нажатии клавиш, нажатие клавиши, mousedown, mouseup, щелчок и сброс также будет предотвращать использование по умолчанию.

    • Получите элемент, который вызвал событие:

      Firefox: evt.target
      IE: evt.srcElement

    • Получение элемента, от которого курсор мыши отошел от: evt.fromElement в IE, — это evt.target в Firefox, если в событии onmouseout, в противном случае evt.relatedTarget

    • При получении элемента курсор мыши перемещается в: evt.toElement в IE — это evt.relatedTarget в Firefox, если в событии onmouseout, в противном случае evt.target

    • Примечание: evt.currentTarget (элемент, с которым было связано событие) не имеет эквивалента в IE.

Большая часть этой страницы воспроизводится из
этого ответа, созданного и переданного в
Stack Overflow и используемого в соответствии с условиями, описанными в
Лицензии на атрибуцию Creative Commons 3.0 .