Круглов С.А.
Кросс-браузер DHTML

www.kruglov.ru / Статьи / Нетривиальный JavaScript

Нетривиальный JavaScript

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

Оглавление


Не получается впечатать вызов скрипта (document.write("<script>"))

Метод document.write(...) позволяет печатать текст в документ. Можно впечатать любой текст с любыми тегами. Даже скрипт.

Начинающие веб-программисты пишут, к примеру, так:

<script>
  document.write("<script src='myscript.js'></script>");
</script>

и получают в ответ непонятную ошибку вроде "Незавершенная строка".

Все дело в том, что наш текст содержит 2 тега </script> - один в конце, а другой в аргументе document.write. Специфика разбора HTML браузером такова, что он сначала определяет границы скрипта, а потом уже интерпретирует его. И браузер считает, что код скрипта завершился на первой же встретившейся ему последовательности символов "</script>", т. е. не на той, на которой было нужно нам. Решение довольно просто - нужно избегать таких строчек в коде скрипта. А наш скрипт можно переписать хотя бы так:

<script>
  document.write("<script src='myscript.js'></scr"+"ipt>");
</script>

Ссылки, запускающие js-код, останавливают загрузку и анимацию

Часто для запуска скрипта при клике посетителем на ссылку программисты пишут что-то такое:

<a href="#" onclick="javascript_code">...</a>
<a href="javascript:void(0)" onclick="javascript_code">...</a>
<a href="javascript:javascript_code">...</a>

При этом код выполняется и, на первый взгляд, все нормально. Но на второй взгляд становится видно, что после клика на ссылку могут прекратить грузиться недогруженные элементы страницы, останавливаются анимированние GIF'ы и, может, происходит что-то еще из этой серии. Все дело тут в том, что браузер считает клик пользователя по ссылке переходом на другую страницу, поэтому полагает, что заботиться о текущей странице больше не надо, ведь она с секунды на секунду заменится новой. Решение же такое: во-первых, не используйте вызов кода через протокол javascript: в атрибуте href; во-вторых, пропишите выход из обработчика ссылки <a> после отработки кода в onclick:

<a href="#" onclick="javascript_code; return false">

В этом случае после вызова javascript_code выполнение клика на ссылке прекратится, поэтому href проигнорируется и браузер не будет считать, что произошел переход на другую страницу.


Как работать с именами элементов форм, имеющих в названии скобки "[]"?

Иногда надо назвать элемент формы как массив - элемент[]. Хотя бы при обработке формы php-скриптом. При этом уже не получается обратиться в этому элементу обычным образом: document.форма.элемент или document.форма.элемент[]

К счастью, в языке JavaScript объект с компонентами аналогичен массиву с элементами. То есть, в частности, для каждой формы существует массив document.форма[], который и содержит в себе все подобъекты, среди них и элементы формы. И мы можем обратиться к нашему "неудобному" элементу вот так:

document.форма["элемент[]"]

Можно воспользоваться еще и тем, что документ содержит массив forms, содержащий только формы, а форма содержит массив elements, содержащий только ее элементы:

document.forms["форма"].elements["элемент"]

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


Не получается открыть маленькое окно

Функцией window.open() нельзя открыть окно размером, скажем, 30x20 пикселей. Минимальные размеры - 100 пикселей по каждому измерению.


Можно ли открыть окно не с файлом, а с результатом работы скрипта?

Да, можно, иначе зачем бы я этот абзац писал? :)

Дело в том, что результатом открытия ссылки с протоколом "javascript:" является текст, если выражение после javascript: имеет значение. Поясню эти невнятные объяснения на примере: строчка

window.open("javascript:\"hello\"")

открывает окно, в котором будет написано "hello". Можно туда вписать и что-то более существенное, к примеру:

window.open("javascript:\"<title>Я и семья</title><img src='we.jpg'>\"")

что выдаст нам окошко с картинкой we.jpg и заголовком "Я и семья".

Правда, на длину передаваемых данных есть ограничения. В MSIE это 4 кб, поэтому, если нужно передать побольше текста, придется открывать окно и печатать туда данные через document.write, т. е.

win=window.open()
win.document.write(".....")

У окна, открытого через showModalDialog (showModelessDialog), нет свойства opener

Если открыть новое окно через window.open, то из него через opener можно достучаться до того, откуда его открыли. Но при открытии модального окна opener не определен.

В этом случае обычно поступают так: передают ссылку на родительское окно при вызове диалога как параметр. Т. е. при вызове мы пишем:

vReturnValue = showModalDialog(sURL, window, sFeatures)

а в диалоге обращаемся к родительскому окну так:

window.dialogArguments

Примечание: Этот метод не работает в MSIE 6.0, если контент диалога создается динамически через javascript: протокол - т. е.:

showModalDialog("javascript:...")

object.outerHTML=object.innerHTML: теряем крайние пробелы

Иногда при фильтрации разметки HTML-объекта программист пытается убрать окружающие теги таким образом:

object.outerHTML=object.innerHTML

Но эта конструкция (кстати, она работает только в MSIE 5+ и Opera 7+) имеет побочный эффект: если мы уберем тег <tag> из такой строки

word1<tag> word2</tag>
то пропадет ведущий пробел сразу после тега <tag>, т. е. мы получим word1word2, а не word1 word2, как ожидали.

Решение: для удаления тега можно использовать DOM-метод removeNode, который имеет аргумент true/false, который показывает, удалять объект совсем или удалять только окружающий тег. (По научному - удалять коллекцию потомков или нет).

В Mozilla придется удалять несколько сложнее (но только на глаз :)

object.parentNode.replaceChild(object.firstChild, object)

Поскольку это решение, созданное с привлечением спецификаций W3C, работает не только в Mozilla, можно советовать его к применению.


<iframe> - не получается обратиться к его свойствам

Этот вопрос довольно сложный (в смысле - может иметь 2 слабо связанные между собой причины, а не в смысле, что он не для простых людей вроде нас ;)

Первое: Iframe как таковой имеет две ипостаси - HTML-объект (подобно <div>) и окно (типа window). К первой ипостаси мы обращаемся через document.getElementById(...), а ко второй - через window.frames[...]. Поэтому бессмысленно пытаться обращаться к стилям у window.frames['myiframe'] или менять location у document.getElementById('myiframe'). Хотя у "ифрейма" как HTML-объекта есть свойства contentDocument (Mozilla практически вся, Opera 7+) и contentWindow (Mozilla 0.9.4+, MSIE 5.5+. Все Оперы включая 7.50 - не поддерживают), обратившись к которому, мы получаем содержащийся в ифрейме документ или окно. У contentDocument есть "обратное" свойство - defaultView (Mozilla, MSIE 5.5+, Opera 7+). Т. е.

iframe.contentDocument.defaultView == iframe.contentWindow
примечание: очевидно, что
iframe.contentWindow.document == iframe.contentDocument

Второе: Иногда возникает проблема обращения к свойству документа, загруженного в iframe (или frame, или открытого через window.open). Это происходит в основном потому, что этот документ мы берем с другого сайта. Тут уж ничего не поделаешь - такова политика безопасности браузеров. И в этом есть смысл:

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


Не работает присвоение через скрипт некоторых свойств. Например, colspan или onClick

Язык HTML долгие годы приучал нас к тому, что регистр символов не имеет значения, что можно писать <td colspan="2">, <TD COLSPAN="2"> или вообще <Td CoLsPaN="2">. Но этот стереотип разбивается о суровую действительность Javascript и DOM (Document Object Model).

В DOM при именовании свойств применяется такой принцип: название пишется со строчной буквы, остальные буквы тоже строчные, за исключением тех, которые начинают новое слово, если название сложносоставленное. Это colSpan, rowSpan, vAlign, noWrap, bgColor и др. Свойство background сложносоставленным не является.

Но и в этом правиле есть исключение — событийные атрибуты: onload, onclick, onpropertychange и пр. Они пишутся строчными буквами полностью.



[26 декабря 2005]

(c) Круглов С.А. <info@kruglov.ru>