Фиксим png в IE6 с помощью expression, одним классом и без использования однопиксельного gif'а

Это давно известная проблема. У этой проблемы существуют 1000 решений, правда все они используют в конечном счёте один и тот же, проверенный AlphaImageLoader. Я, скорее всего, не открою Америку, а лишь хочу рассказать вам об expression'е, которым с недавних пор пользуюсь и которого достаточно в 99% случаев, а также объясню как, почему и зачем я его написал.

Началось всё с того, что когда я пришёл на новую работу, то узнал, что моим старым добрым прописыванием png-шек в свойстве filter фиксить png-ки нельзя: это раздувает код, от этого нужно избавляться, а самое главное что появляется неразбериха с путями в CMS'ке. К тому времени я уже привык, что background-repeat и background-position одним магическим взмахом руки не добиться в IE6, но как-то слишком много гемора была даже с простым фиксом картинок (к слову, моё решение проблем с указанными свойствами не решает!). Тогда мне пришлось воспользоваться тем решением, что использовали другие: jquery.iFixPng + отдельный js, когда нужно сделать sizingMethod=«scaled» и два класса для элементов «png» и «png-scaled». По началу я тоже использовал этот метод, но меня очень напрягало, что это всё-таки было чисто на JS. Я стал думать.

Написать решение на expression'ах не составило труда. Главное что нужно было сделать — это передавать внутрь экспрешена переменную z_gif — путь к однопиксельному прозрачному gif'у для фикса img-элементов. Но тогда я ещё не мог объяснить другим верстальщикам, почему способ на эскпрешенах лучше, чем на JS. Через какое-то время я стал работать над проектом, где на одной странице могло быть сразу где-то 20 png-шек. Именно тогда фикс на JS показал себя во всей красе, т.к некоторые картинки были размерами с полстраницы, то после загрузки сайта пользователь (в том числе и заказчики) секунды три видели непофикшенные картинки, и только потом всё становилось хорошо и красиво. Заказчики очень жаловались на это, а я пожимал плечами «ничего не могу сделать». А потом всё-таки решил попробовать заменить js-фикс на expression и добился желаемого: после загрузки пользователь не видел непофикшенных картинок, на их месте была пустота!

Универсальный класс

 

Я пользовался этим фиксом некоторое время, но меня не устраивало то, что для scaled картинок приходилось использовать отдельный класс. Я захотел исправить это и избавиться от двух классов (png и png-scaled) в пользу одного. Подумав я заметил, что в общем-то scale'ить нелогично картинки, у которых обе стороны больше 1 пикселя (понятно, что на 99% случаев найдётся 1, но меня устраивали 99), и я в expression'е написал условие, которое это проверяло и соответственно меняло sizingMethod на «scale»:



* html .g-png24 {
 behaviour:expression(
  !this.fixedPNG?
   (function(el){
    if (el.tagName.toLowerCase() == "img") {
     el.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src=" + el.src + ")";
     el.style.backgroundImage = "none";
     el.src = z_gif; // А вот этот вот z_gif как раз и передаётся из html'ки, как-то так: <script>var z_gif = {{path-to-z-gif}}</script>
     }
     else {
      var sizingMethod = "crop";
      var tmpImg = new Image();
      tmpImg.src = el.currentStyle.backgroundImage.split('\"')[1];
      if (parseInt(tmpImg.width) == 1 || parseInt(tmpImg.height) == 1 || el.className.indexOf('g-png-24__scaled') > -1) {
       sizingMethod = "scale";
      }
     el.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src=" + el.currentStyle.backgroundImage.split('\"')[1] + ", sizingMethod='"+sizingMethod+"')";
     el.style.backgroundImage = "none";
     el.src = z_gif;
    }
   el.fixedPNG = true;
   })(this):''
  );
 }

Вредный !important

 

Это решение работало у меня до случая, когда я применил фикс к элементу, у которого background-image был задан с флагом !important и фикс не сработал! Что ещё интереснее, другие фиксы также не работали! Я стал думать и перепробовав кучу вариантов: достучаться до !important флага с помощью JS, пересоздать новый элемент, клонируя все свойства, кроме background-image, достучаться до свойств currentStyle и иже с ними… А в итоге всё оказалось как-то слишком просто: в том же файле стилей нужно было создать дополнительное правило:



* html .png-fixed {
 background-image: none !important;
 }

и просто добавить элементу класс png-fixed и всё работало! :)

 

Ошибаться нужно?

 

Но и это решение не давало покоя. Меня не устраивала необходимость каждый раз передавать в expression путь к однопиксельному прозрачному gif'у и где-то я подозревал, что от него можно избавиться. Я вертел expression и так и эдак, параллельно оптимизируя код. Для начала я попробовал не задавать src, но вредный IE показывал над пофикшенной картинкой иконку незагруженной картинки. Облазил тонны сайтов со все возможными фиксами. Пытался вынести код в отдельную htc-шку. А потом я зачем-то задал картинке ширину и высоту в 1 пиксель и в итоге непофикшенная картинка стала 1х1, а пофикшенная отобразилась нормально рядом с ней. Я не поверил и стал дальше думать, как убрать эту 1х1 и задал src="" и её не стало. Я ликовал, моей радости не было предела, у меня что-то получилось… и когда я стал применять фикс, то… картинки перестали scale'ться. В попыхах я скопировал забыл вписать в код смену sizingMethod'а, а когда дописал его, фикс перестал работать.

 

Я думал это провал, потому что тогда я уже рассказал всем о своём красивом фиксе, для которого требовался только один файлик, но подумав почему у меня всё-таки что-то работало я вспомнил, что у sizingMethod возможных значений не 2, а 3, есть ещё и такое свойство «image»! В

документации по этому значению написано что оно раздвигает или сжимает границы контейнера, чтобы вместить картинку. Но это оказалось не совсем так! Если элемент-контейнер меньше чем png, то png-шка покажется полностью, а элемент-контейнер останется в своих границах и именно это у меня и происходило, когда я задавал ширину и высоту = 1, а src="".

Итак, вот код моего фикса:



* html .g-png24 {
 behaviour:expression(
  (!this.fixedPNG?
   (function(el){
    var fixSrc = "", sizingMethod = "crop";
    if (el.tagName.toLowerCase() == "img") {
     fixSrc = el.src;
     sizingMethod = "image";

     el.style.width = 1;
     el.style.height = 1;
     el.src = "";
    }
    else {
     var tmpImg = new Image();
     tmpImg.src = el.currentStyle.backgroundImage.split('\"')[1];
     if (parseInt(tmpImg.width) == 1 || parseInt(tmpImg.height) == 1 || el.className.indexOf('g-png-24__scaled') > -1) {
      sizingMethod = "scale";
     }

     fixSrc = el.currentStyle.backgroundImage.split('\"')[1];
     el.className += " g-png-fixed";
    }
    el.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src=" + fixSrc + ", sizingMethod='" + sizingMethod + "')";
    el.fixedPNG = true;
   })(this):'')
  );
 }

* html .g-png-fixed {
 background-image: none !important;
}

Конечно, хозяин-барин, и это решение спасает жизнь только в 99% случаев (и то, это мои 99% случаев, а у вас всё может быть совсем иначе) и значительно облегчает жизнь в принципе, потому что нужно всего-то подчключить файл с фиксом и задать нужным элементам класс «g-png24», вне зависимости img это или div! Кому-то возможно интереснее покажется решение Виталия Харисова (правда в нём он использует один доп.элемент-обёртку для img), или известный всем iePngFix.


Автор: Olegbl4

 



Опубликовал admin
9 Ноя, Понедельник 2009г.



Программирование для чайников.