понедельник, 17 августа 2009 г.

jQuery toggle() IE8 bug

Недавно в процессе разработки наткнулся на интересный баг в jQuery. В библиотеке jQuery ver.1.3.2 реализована некорректная работа функции toggle() под IE8. В остальных браузерах работает нормально. Заключается ошибка в следующем: последовательно применяя toggle() к элементу таблицы в браузере Internet Explorer 8 элемент будет успешно скрыт, но обратно не будет показан при повторном вызове.

Поколупавшись в логике работы функции toogle(), выяснил, что она использует два селектора для определения состояния элемента страницы (видимый/невидимый). Приведу код ниже:

Sizzle.selectors.filters.hidden = function(elem){
    return elem.offsetWidth === 0 || elem.offsetHeight === 0;
};

Sizzle.selectors.filters.visible = function(elem){
    return elem.offsetWidth > 0 || elem.offsetHeight > 0;
};


Оба из них проверяют видимость элемента на странице путём проверки его высоты и ширины. Итак, в чём же кроется проблема данного определения, и почему не работает метод toggle() под IE8? Элемент
, хоть и имеет аттрибут display:none, всё равно имеет ширину > 0. Высота скрытого tr равна 0. Логика :visible/:hidden селекторов считает элемент видимым, если элемент имеет какую-то высоту или ширину, что есть неправильно для «любимого» IE.

Эту проблему можно исправить, проверяя атрибут display у искомого элемента. Тогда селекторы должны принять такой вид:

Sizzle.selectors.filters.hidden = function(elem){
    return (elem.style.display.toLowerCase() !== 'none');
};

Sizzle.selectors.filters.visible = function(elem){
    return (elem.style.display.toLowerCase() === 'none');
};

Могу предложить и более сложное решение, которое проверяет, как и раньше, высоту и ширину элемента, но при этом берёт во внимание особенности работы с tr:

Sizzle.selectors.filters.hidden = function(elem){
    var width = elem.offsetWidth, height = elem.offsetHeight,
         force = /^tr$/i.test( elem.tagName );
    return ( width === 0 && height === 0 && !force ) ?
        true :
            ( width !== 0 && height !== 0 && !force ) ?
                false :
                    !!( jQuery.curCSS(elem, "display") === "none" );
};

Sizzle.selectors.filters.visible = function(elem){
    return !Sizzle.selectors.filters.hidden(elem);
};

4 комментария:

  1. Приятно слышать, что кому-то помог c jQuery toggle() bug-ом в IE8... потому, что сами разработчики jQuery не торопятся его чинить

    ОтветитьУдалить
  2. как решение проблемы использую toggleClass

    ОтветитьУдалить
  3. да, это тоже выход, можно конечно и ручками переключать и навешивать атрибуты при помощи простого обработчика, но хотелось бы использовать нативный инструмент

    ОтветитьУдалить

Рекоммендую

Попробуйте надёжный хостинг от Scala Hosting