В связи с большим количеством писем на тему как можно менять содержимое ячеек у таблиц решил написать эту статью. В ней в основном, как проделать эту операцию найболее правильно (на мой взгляд) и так, чтобы все работало и в NN4. Далее - история создания еще одного скрипта.

Пример

Начнем с примера. Введите в строке ввода текст и нажмите кнопку... текст в зеленом(?) поле должен быть заменен.

Как все это устроено?

В MSIE4+ и NN6/Mozilla (а на самом деле в DOM level 1) есть замечательное свойство elem.innerHTML. С его помощью можно достать или изменить содержание практически любого элемента страницы, которое является HTML-кодом. Поэтому функция для этих браузеров получается довольно простая:

function replaceElemContents(elemId, contents) {
  if (dom) document.getElementById(elemId).innerHTML = contents;
    else if (ie4) document.all[elemId].innerHTML = contents;
}

Но не спешите радоваться - это был мой начальный вариант. Но начальные варианты потому и начальны, что зачастую очень сильно отличаются от финальных.

Глюк 1 - Opera 4+

В настоящий момент ни одна версия этого браузера не знает что такое innerHTML. К сожалению, однако в нем нет и никакого другого способа, который мог бы продублировать действие innerHTML, или по крайней мере я не нашел его. Если такой способ известен - напишите мне по адресу leechy@dhtml.ru, плз.

Несмотря на заявления разработчиков, оказывается, что Опера - очень ограниченный браузер (вспомните свойство display). Из-за того, что Опера положительно отзывается на запрос document.getElementById, она попадает в мое определение "DOM-совместимый браузер". Для совместимости мы переделаем нашу функцию так:

opera = (navigator.userAgent.indexOf('Opera') >= 0)? true : false;

function replaceElemContents(elemId, contents) {
  if (dom && !opera) document.getElementById(elemId).innerHTML = contents;
    else if (ie4 && !opera) document.all[elemId].innerHTML = contents;
}

Это все еще вариант 2. Но есть и другие...

Глюк 2 - ...другие DOM-совместимые браузеры

Как выяснилось, все больше из новых браузеров поддерживают document.getElementById, и все больше становиться вероятность, что какой-то скрипт, где-то не заработает. К сожалению у меня нет возможности отслеживать всех версий браузеров для всех платформ. Но не перестаю надеяться, что эти браузеры будут по-настоящему DOM-совместимые и все будет ok. И по-прежнему забочусь только об Опере. Надеюсь делаю правильно.

Насчет второго глюка - под какой-то Linux в браузере Konqueror этот скрипт подглючивает. Мне так и не стало ясно что именно глючить, как глючить, но факт, что время от времени скрипт все-таки работает, наводит меня на мысль, что глючит браузер, а не скрипт.

Глюк 3 или Большая Проблема - Netscape Navigator 4

В четвертом Netscape нет innerHTML. И не будет. И вообще нельзя менять ничего на странице после перезагрузки. Кроме слоев, конечно. Но перезагружать страницу, только чтобы поменять строчку текста - это по-моему извращение. Поэтому нужно что-то делать со слоями.

Еще один минус - слой, у которого можно менять содержимое - должен быть только абсолютно позиционированный. В принципе задача не тяжелая, но нужно помнить, что скрипт универсален, и не у всех есть возможность server-side определять браузер и выдавать/не выдавать слой. К тому-же абсолютно позиционированному слою нельзя сказать ширина = 100% от той-то ячейки той-то таблицы, поэтому нужно еще померить ширину ячейки и т.д.

Из-за всего этого, я счел что самое правильное решение - померить ширину и расположение ячейки, а потом создать динамически слой этой-же ширины и поставить его "сверху" нужного места.

Первый этап - как померить ширину?
Первая попытка была поставить относительно позиционированный слой в ячейке и разтянуть его на 100%, после чего померить его ширину и положение. Идея неплохая и была бы вообще идеальная, но... не сработало - ширина = undefined.

Поэтому пришлось остановиться на использование двух относительно позизционированных слоев, разнесенные по краям ячейки. И снова проблемы - так и не смог разнести их с помощью атрибутов align. Никак.

Так как третий вариант определение ширины у меня нет, я решил, что нужно использовать таблицу из двух ячеек. Одна с шириной 100%, а другая только со слойчиком, в котором есть один <br>. А чтобы не грузить другие браузеры этой-же таблицы, содержимое этой ячейки писать с помощью document.write.

Опять неудача. Оказывается, если пишеш код относительно позиционированного слоя с помощью document.write (например <span id=a style="position: relative">), то Netscape4 отказывается его считать за слой! Я на 98% уверен, что это у меня ошибка, а не у браузера, но был настолько разъярен, что не стал выяснять доконца. Слава богу никто не отменил тэг <ilayer> - его NN4 воспринимает всегда, а так как у меня уже есть определение браузера - я могу его использовать сколько нужно.

Итак, вот реальное содержание ячейки:

<script language=JavaScript><!--

if ((dom && !opera) || (ie4 && !opera))
    document.write('<div id="chble">Подождите загрузки страницы...<\/div>');
  else if (nn4)
      document.write('<table cellpadding=0 cellspacing=0 border=0 width=100%>'+
      '<td width=100%><ilayer id="chbleLeftAnc"><br><\/ilayer><\/td>'+
      '<td><ilayer id="chbleRightAnc"><br><\/ilayer><\/td><\/table>');
    else
      document.write('<p>Ваш браузер не поддерживаеть динамическую замену содержимого элементов!<\/p>');

// --></script>

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

Второй этап - как создать слой?
Так как у меня все еще нет универсального скрипта, динамически создающий слой в NN4 (и любые другие элементы в DOM-совместимых браузерах), придется быстро сделать неуниверсальную функцию, которая будет работать в этом пока что исключительном случае.

Итак, новый слой в NN4 создается с через объект Layer, которому задается ширина. Для определении ширины, мы вычитаем из положение правого относительно позициониранного слоя - положение левого. Как это сделать мы знаем (см. ст. Где находятся элементы?).

После нам нужно поставить его в нужном месте. Для этого свойства left и top новосозданного слоя приравниваем к аналогичным значениям одного из двух вспомагательных элементов. И наконец, так как динамически созданные слои по умолчанию скрыты - мы его показываем. Вот что получилось:

function createNN4Leer(leerId, firstLeer, secondLeer) { if (nn4) {
  document.layers[leerId] = new Layer(document.layers[secondLeer].pageX -
    document.layers[firstLeer].pageX);
  document.layers[leerId].left = document.layers[firstLeer].pageX;
  document.layers[leerId].top = document.layers[secondLeer].pageY;
  document.layers[leerId].visibility = "show";
}}

При этом этой передаем имя самого элемента, и имена двух вспомагательных - все просто!

Теперь нужно будет в уже знакомую функцию replaceElemContents добавить новые строки:

else if (nn4) {
  if (!document.layers[elemId])
    createNN4Leer(elemId, elemId+'LeftAnc', elemId+'RightAnc');
  document.layers[elemId].document.open();
  document.layers[elemId].document.write(contents);
  document.layers[elemId].document.close();
}

Что они делают... Во-первых, чтобы не создавать слой сразу после загрузки страницы (может получится так, что он вообще не понадобиться), мы его создаем только когда он реально нужен - перед первой замены и то только если его нет.

Раз у нас точно есть уже слой, то можно над ним уже издеваться. Вариант свойства innerHTML для NN4 - просто записать в нем с помощью document.write то, что нужно, своевременно открыв и закрыв его. Потому, что любой слой в NN4 - это самостоятельный документ, правда с некоторыми ограничениями (например нельзя сабмитить пост-форму в нем не перегружая всю страницу).

Согласен, что получилось настоящее извращение, но я заменю этот кусок с более-приличным, когда будет готов скрипт создающий слоев. Обещаю.

Все это работает. Не работает только то, что это отдельный слой и если там слишком много информации, то он может "налезть" на другой информации на странице - той, которая расположена ниже отведенного места. Поэтому нужно заранее позаботиться и поставить "распорку" нужной величины, зная какое количество информации может попасть туда.

Итоги

Так как все работает, можно считать цель достигнутой. Для копипейстеров - полный скрипт:

<script language=JavaScript><!--

//
// Copyright (c) Art. Lebedev Studio | http://www.design.ru/
// Author - Leechy | leechy@design.ru | http://www.dhtml.ru/
//

opera = (navigator.userAgent.indexOf('Opera') >= 0)? true : false;
dom = (document.getElementById)? true : false;
ie4 = (document.all && !dom)? true : false;
nn4 = (document.layers)? true : false;

function createNN4Leer(leerId, firstLeer, secondLeer) { if (nn4) {
  document.layers[leerId] = new Layer(document.layers[secondLeer].pageX -
    document.layers[firstLeer].pageX);
  document.layers[leerId].left = document.layers[firstLeer].pageX;
  document.layers[leerId].top = document.layers[secondLeer].pageY;
  document.layers[leerId].visibility = "show";
}}

function replaceElemContents(elemId, contents) {
  if (dom && !opera) document.getElementById(elemId).innerHTML = contents;
    else if (ie4 && !opera) document.all[elemId].innerHTML = contents;
      else if (nn4) {
        if (!document.layers[elemId])
          createNN4Leer(elemId, elemId+'LeftAnc', elemId+'RightAnc');
        document.layers[elemId].document.open();
        document.layers[elemId].document.write(contents);
        document.layers[elemId].document.close();
      }
}

function createReplacementElem(elemId, alternativeText) {
  if ((dom && !opera) || (ie4 && !opera))
      document.write('<div id="'+elemId+'">Подождите загрузки страницы...<\/div>');
    else if (nn4)
        document.write('<table cellpadding=0 cellspacing=0 border=0 width=100%>'+
        '<td width=100%><ilayer id="'+elemId+'LeftAnc"><br><\/ilayer><\/td>'+
        '<td align=right><ilayer id="'+elemId+'RightAnc"><br><\/ilayer><\/td><\/table>');
      else if (alternativeText) document.write(alternativeText);
        else document.write('Ваш браузер не поддерживаеть динамическую замену содержимого элементов!');
}

//--></script>

Я делал этот скрипт для Студии Артемия Лебедева, поэтому в коде стоит соответствующий копирайт. Если будете его использовать - не удаляйте его!

Функция createReplacementElem() - это кусок кода, который раньше вставлял в самом HTML. Он создает необходимые элементы, а если браузер не поддерживает замену - пишет об этом. Примерный код в HTML должен выглядеть так:

<script language=JavaScript>>!--
createReplacementElem('chble');
// --></script>
<noscript>
Выполнение скриптов в вашем браузере выключено, поэтому вы не сможете увидеть динамическую замену содержимого элементов!
</noscript>

После этого нужно только вызвать replaceElemContents('chble', 'желаемый текст'), где 'желаемый текст' - новое содержимое елемента и текст поменяется.

В итоге сам скрипт весит 1667 байт - я даже немного горжусь собой.

Если есть какие-то вопросы или предложения - к вашим услугам наш форум «dHTML Разговоры»

Москва, 31 мая 2001 Leechy




© 2000 - 2026 Leechy (leechy@dhtml.ru)