Очерёдность событий и синхронизация в JavaScript
Проблемы с очерёдностью исполнения являются источником некоторых наиболее коварных багов в JavaScript-приложениях. Ошибки, которые не проявлялись во время разработки, внезапно начинают приводить к проблемам, когда приложение используется конечным пользователем на старом компьютере или с медленным доступом в интернет. Эти проблемы могут проявляться случайным образом. В этом случае их довольно сложно воспроизвести.
Простой пример: рассмотрим кнопку с обработчиком нажатия мышью, которая изменяет некоторый элемент, находящийся ниже её. Если пользователь нажимает кнопку до того, как исходный текст элемента был распарсен (декодирован и преобразован во внутреннее представление), скрипт вызовет ошибку. Разработчик никогда не столкнётся с этой проблемой, поскольку он тестирует страницу на быстром компьютере с быстрым подключением, на котором вся страница загружается в одно мгновение.
Данная статья является попыткой описать всевозможные проблемы с очерёдностью исполнения в современных браузерах.
Окно браузера использует единственный поток исполнения, который производит разпознавание HTML-кода, управление событиями и выполнение кода на JavaScript.
Код на JavaScript исполняется в одном из двух режимов:
- Код высшего уровня (основная программа) выполняется в процессе загрузки страницы
- Функции-обработчики событий выполняются в процессе управления событиями
Оба типа исполнения инициируются браузером, и они выполняются в одном и том же потоке таким образом, что только одна единица кода выполняется в один момент времени.
Как правило, исполнение кода в браузере управляется событиями (т.е. является реакцией на пользовательский ввод), но во время загрузки страницы оно управляется ещё и потоком парсинга страницы.
Событие является сигналом от браузера, говорящим о каком-то изменении в окне страницы, которое уже произошло или должно произойти в ближайшем времени, если вы не предпримете каких-либо мер.
Обработчик события - функция на JavaScript, которая назначается в соответствие некоторой паре из объекта и названия события. Когда соответствующее событие происходит для данного объекта, выполняются все обработчики событий, назначенные этому узлу.
Все функции-обработчики событий выполняются последовательно, и каждое событие обрабатывается полностью (включая всплывание сквозь DOM и выполнение действия по умолчанию) перед тем, как будет выполнено следующее событие.
Действие по умолчанию является особенностью модели событий браузера: это действие, которое происходит, если JavaScript не перехватывает событие. Например, действие по умолчанию для события click на ссылке является переход на URL. Событием по умолчанию для нажатия мышью на чекбоксе является включение галочки, и так далее.
Действие по умолчанию не является само по себе обработчиком события, и мы не можем его удалить или переопределить, в отличие от пользвательских обработчиков событий. Однако, мы можем отменить его выполнение во время обработки события, используя preventDefault() (или event.returnValue в IE). Если действие по умолчанию отменено, все прочие обработчики событий всё равно будут выполнены, но действие по умолчанию после этого выполнено не будет.
Такие события как load возникают только для соответствующего объекта (в данном случае, window или document ). Однако, для событий, происходящих для отдельных элементов документа, возможно также выполнение обработчиков событий для элементов-предков.
До того как событие возникнет для целевого объекта, имеет место фаза перехвата, во время которой предки целевого узла могут перехватить событие. Однако, перехватывание событий работает неодинаково в разных браузерах.
Некоторые события всплывают, что означает, что после того как они возникли для целевого элемента, они по очереди возникают для каждого предка в DOM-дереве, вплоть до объекта document включительно. Этот процесс идентичен в различных браузерах.
Весь процесс возникновения события для всех соответствующих элементов и вызова действия по умолчанию называется управлением событиями.
Для невсплывающего события порядок управления событиями является таким:
- Фаза перехвата: все "перехватывающие" обработчики событий выполняются для всех элементов-предков, сверху вниз по иерархии.
- Событие возникает для целевого элемента, что означает, что выполняются все обработчики события, назначенные данному элементу для данного события (в неопределённом порядке).
- Выполняется действие по умолчанию (если оно не было отменено одним из обработчиков).
Для всплывающего события порядок таков:
- Фаза перехвата: все "перехватывающие" обработчики событий выполняются для всех элементов-предков, сверху вниз по иерархии.
- Событие возникает для целевого элемента.
- Фаза всплывания: событие возникает для всех событий-предков, начиная с целевого и выше.
- Выполняется действие по умолчанию (если оно не было отменено одним из обработчиков).
Всплывание события возможно отменить используя stopPropagation() (cancelBubble() в IE), однако действие по умолчанию всё равно будет выполнено. Таким образом, отмена всплывания и отмена действия по умолчанию являются отдельными и независимыми операциями.
Отдельные этапы работы модели событий объясняются гораздо более подробно в спецификации DOM 3 Events.
Существуют некоторые интересные ситуации, в которых действие по умолчанию на самом деле происходит до управления событиями - но всё равно может быть отменено. Например, когда происходит нажатие на чекбоксе, отображение галочки и установка атрибута checked происходят ещё до активации обработчиков события. Однако, если действие по умолчанию отменяется, эти изменения откатываются во время фазы действия по умолчанию. Галочка снова снимается, а атрибут checked переключается обратно.
Некоторые события активируются группами: т.е. одно действие пользователя приводит к вызову нескольких событий.
Например, когда фокус переходит от одного поля к другому, событие blur возникает для одного поля, а focus - для другого. В принципе, это происходит одновременно (поскольку это является реакцией на одно действие пользователя), однако события всё равно возникают по очереди.
Если событие всплывает, вся последовательность перехвата-всплывания событий и вызов действия по умолчанию происходят перед обработкой следующего события.
Примером такого случая является отпускание кнопки мыши над кнопкой на странице. В этом случае возникают события mouseup и click . Выполняются следующие этапы:
- Фаза перехвата для
mouseup – выполняются все перехватывающие обработчики.
- Событие возникает для целевого элемента.
- Фаза всплывания для
mouseup - событие возникает для всех родительских элементов
- (Для
mouseup нет действия по умолчанию).
- Фаза перехвата – выполняются все перехватывающие обработчики.
- Событие
click возникает для целевого элемента.
- Фаза всплывания - событие
click возникает для всех родительских элементов
- Выполняется действие по умолчанию для
click .
В каждом случае обработки события возможна отмена только текущего события. Например, в обработчике события mouseup отмена действия по умолчанию не возымеет никакого эффекта, поскольку для mouseup нет действия по умолчанию. Это не остановит возникновение события click, поскольку они являются различными событиями.
Однако действие по умолчанию может привести к возникновению другого события. В случае события click для кнопки отсылки формы действием по умолчанию является отсылка текущей формы, которая, в свою очередь, породит событие submit . В этом случае отмена события по умолчанию предотвратит возникновение следующего события.
События возникают в ответ на пользовательский ввод (с мыши или клавиатуры) или в ответ на внутренние события, например окончание загрузки страницы. Однако, вызов событий асинхронен породившему его вводу.
Пользовательский ввод может случиться в тот момент, когда обработчик другого события всё ещё выполняется. В таком случае действия пользователя буферизуются, и когда модуль обработки событий вновь свободен, происходит обработка событий, соответствующих буферизованным действиям. События всегда возникают в правильном порядке, но между действием и вызовом события может произойти заметная задержка, если код некоторого обработчика события займёт продолжительное время.
Internet Explorer и Mozilla совершенно не реагируют на пользовательские действия в течение выполнения скриптов-обработчиков. Даже панели инструментов браузеров блокируются. Хотя пользователь по-прежнему может, например, нажимать на кнопки, и эти действия заносятся в буфер, никакой наблюдаемой реакции они не вызывают. Это может дезориентировать пользователя, который, не осознав, что его действие было учтено, вероятно, попробует нажать кнопку несколько раз, что может привести к нежелательным последствиям. Пользователь даже может решить, что браузер завис вследствие ошибки.
Opera же продолжает визуально реагировать на действия пользователя (например, нажатия кнопок) даже во время исполнения другого скрипта. Однако действия по-прежнему буферизуются и обрабатываются последовательно, как и в других браузерах. Таким образом, действия по умолчанию для события не производятся до тех пор, пока обработчик событий не доберётся до него. Это тоже может озадачить пользователя, хотя, наверное, не так сильно, как полная блокировка IE и Mozilla.
Отсюда следует сделать вывод, что скрипты-обработчики событий никогда не должны занимать много времени. С особой осторожностью следует отнестись к синхронным запросам XMLHttpRequest , так как они могут вызвать заметную задержку, которая заблокирует браузер или окно документа.
Существует особый случай, когда события обрабатываются не последовательно, а "вложенно". Если событие активируется непосредственно из скрипта с использованием метода dispatchEvent() (fireEvent() в Internet Explorer), то событие обрабатывается немедленно. Исходный скрипт продолжит выполняться только тогда, когда закончится обработка вложенного события (и будет выполнено действие по умолчанию).
Также события изменения DOM, которые не поддерживаются в Internet Explorer, будут обработаны синхронно, непосредственно в момент изменения DOM, например при вызове appendChild() .
Производимые скриптом изменения в DOM или наборе стилей могут быть отображены не сразу. Это зависит от браузера.
Например, если цвет фона элемента меняется при помощи DOM, изменения немедленно будут отражены в структуре DOM (и событие изменения DOM будет немедленно синхронно обработано), но мы не можем знать наверняка, когда движок рендеринга браузера сподобится реально отобразить эти изменения на экране. Похоже, что в Mozilla и Internet Explorer изменения откладываются до окончания текущей обработки событий, а в Opera они отображаются немедленно.
Метод setTimeout() позволяет "заказать" выполнение функции через определённый промежуток времени:
window.setTimeout(someFunction, 1000);
Отложенные скрипты работают в некотором роде аналогично скриптам обработки событий. Хотя они и выполняются по прошествии определённого количества времени, а не в ответ на пользовательский ввод, они так же последовательно обрабатываются потоком обработки событий, как и пользовательские действия.
По этой причине не следует ожидать, что отложенное действие будет выполнено точно в указанный момент. Если выполняются другое событие или пакет, отложенный скрипт будет просто поставлен в очередь. Фактически, мы можем быть уверены только в том, что функция будет выполнена через как минимум секунду. На самом деле времени может пройти гораздо больше.
Это удивительно полезная особенность. Если обработчик отложен на время 0, он не выполняется, но немедленно добавляется к очереди. Он не будет выполнен до тех пор, пока не отработает обработка текущего события (включая действие по умолчанию).
Если отложенное выполнение создаётся в обработчике события, являющегося частью пакета событий (например blur /focus , mouseup /click ), то отложенный код будет выполнен после завершения обработки всех событий в пакете.
К прочим событиям, не вызываемым действиями пользователя, относятся:
- События загрузки страницы
- Отложенные события
- Обработчики результатов асинхронных запросов
XMLHttpRequest
Эти события добавляются в очередь обработки событий аналогично событиям, вызываемым действиями пользователя. Это, например, означает, что обработчик ответа XMLHttpRequest не выполняется непосредственно в момент получения данных, но всего лишь становится в очередь обработки событий.
Диалоговые окна alert (а также confirm и prompt ) обладают некоторыми странными свойствами.
Они синхронны в том смысле, что выполнение скрипта, вызвавшего этот диалог, замораживается до закрытия диалога. Скрипт ожидает завершения функции alert() , прежде чем продолжить выполнение.
Однако некоторые браузеры позволяют производить обработку событий до тех пор, пока диалог виден на экране и ожидает пользовательских действий. Это означает, что пока один скрипт заморожен, ожидая конца выполнения функции alert , другая функция может быть выполнена в процессе обработки другого события.
События пользовательского интерфейса, такие как mouseup и click , не будут возникать в момент исполнения alert , поскольку диалог является модальным и перехватывает весь пользовательский ввод, однако не-пользовательские события, такие как загрузка страницы, отложенный код и обработчики результата асинхронных запросов XMLHttpRequest , могут возникнуть в это время.
Страницы HTML парсятся и отображаются прогрессивно, т.е. по мере скачивания документа браузером.
Большая часть внешних ресурсов, таких как изображения и встраиваемые медиа-объекты, загружается асинхронно. Когда парсер встречает элементы img , embed , iframe или object , создаётся новый поток. Он скачивает и отображает внешний ресурс независимо от парсинга основной страницы. Страницы во фреймах и встроенных фреймах (iframe ) также загружаются асинхронно.
Внешние таблицы стилей являются особым случаем. Некоторые браузеры загружают их асинхронно (как изображения), некоторые браузеры загружают их синхронно, предположительно чтобы избежать повторного рендеринга всей страницы, когда таблица стилей догрузится. (Это позволяет избежать проблемы мерцания не использующего стили контента, которая была свойственна ранним браузерам.) Другими словами, полагаться на какое-либо конкретное поведение здесь не следует.
Элементы script парсятся синхронно. Когда элементы script ссылаются на внешние скрипты, парсинг страницы останавливается до тех пор, пока внешние скрипты не будут подгружены, распарсены и выполнены.
Блоки JavaScript, содержащие код, парсятся и выполняются в тот момент, когда встречается закрывающий тег.
Блок JavaScript (содержащий встроенный код или ссылающийся на внешний файл) обрабатывается в два этапа. Сначала он парсится и выполняется. В процессе парсинга производится проверка базового синтаксиса кода. Если обнаруживается синтаксическая ошибка, выполнение скрипта не производится.
На этапе исполнения выполняется весь код высшего уровня (т.е. не принадлежащий функциям). Инструкции высшего уровня могут содержать внешние ссылки на функции, объявленные в том же блоке, так как объявления функций загружаются на этапе парсинга. Этот код будет работать:
var x = getMagicNumber();
function getMagicNumber() { return 117; }
Однако следующий код работать не будет, поскольку выражения function вычисляются только в ходе выполнения:
var x = getMagicNumber(); // ОШИБКА! getMagicNumber не определена!
var getMagicNumber = function() { return 117; }
Следующий код также не будет работать, поскольку каждый блок script исполняется непосредственно после того, как будет встречен закрывающий тег:
<script>
alert(getMessage());
</script>
<script>
function getMessage() { return "Hello!"; }
</script>
Скрипт может выводить HTML-код непосредственно в текущий документ, используя метод document.write() . Сгенерированный код будет буферизован до завершения выполнения текущего блока. После этого буферизованный код будет распарсен. Этот код, в свою очередь, может (как всё сложно!) содержать блоки script , которые выполняются в ходе парсинга.
Сгенерированный HTML-код вставляется в документ непосредственно после сгенерировавшего его блока script .
По ходу загрузки страницы парсер последовательно выстраивает дерево DOM. Пустой элемент вставляется в DOM когда парсится соответствующий тег. Непустой элемент вставляется, когда парсится открывающий тег. Например, элемент body появляется в DOM, как только парсер начинает парсить содержимое элемента.
Заметим, что DOM не обязательно точно соответствует входному HTML-коду. Такие элементы как html и head будут созданы в DOM даже если они не встречаются в HTML.
Если исходный код HTML некорректен (например, элемент title появляется внутри body ), браузер перестроит DOM так, чтобы он был корректным. В этом случае нельзя положиться на то, что дерево DOM будет выстроено по порядку.
У синхронной загрузки скриптовых блоков есть существенный недостаток: если в ходе парсинга заголовка страницы требуется загрузить и выполнить большое количество кода, перед началом отрисовки страницы может наблюдаться существенная задержка.
Чтобы устранить эту проблему, мы могли бы использовать в элементах script атрибут defer . Он означает, что браузеру разрешается загружать этот скрипт асинхронно. Однако мы не можем быть уверены, когда скрипт в действительности будет загружен. Это может произойти как до, так и после окончания рендеринга страницы. Opera полностью игнорирует атрибут defer .
<script defer>
alert("это сообщение появится в непредвиденный момент во время загрузки страницы");
</script>
Отложенные скрипты не могут использовать document.write() , поскольку они не синхронизированы с парсером.
Существует ещё одна тонкость: скриптовые блоки всегда выполняются в том порядке, в котором они появились в документе, вне зависимости от наличия атрибута defer . Так что если элемент script без атрибута defer следует за скриптом с атрибутом defer , парсер должен закончить загрузку и выполнение отложенного скрипта до исполнения не-отложенного скрипта. При этом теряется весь смысл использования атрибута defer . Это означает, что всегда необходимо располагать не-отложенные блоки скриптов перед отложенными.
По этим причинам атрибут defer нельзя использовать для задания очерёдности выполнения скриптовых блоков. Он всего лишь позволяет некоторым браузерам продолжить парсинг документа после блока script .
Ход рендеринга страницы на экране не всегда синхронизирован с построением DOM. Порядок рендеринга в ходе загрузки страницы является довольно непредсказуемым. В зависимости от скорости подключения и размера страницы, браузер может, прежде чем начать рендеринг, ждать загрузки всей страницы, либо, в случае медленного соединения, может рендерить страницу по частям.
Следует иметь в виду, что интерфейс реагирует на пользовательские события с того момента, как страница начинает рендериться. Это может привести к проблемам с "ссылками вперёд", если обработчик события ссылается на элемент, который встречается далее по ходу документа.
Пример опасного кода:
<button
onclick="document.getElementById('lamp').backgroundColor = 'yellow'">
Жми сюда, чтобы включить лампу!
</button>
<div id='lamp'>O</div>
Проблема здесь в том, что элемент 'lamp' может быть ещё не распарсен в тот момент, когда кнопка нажата. Обработчик события никогда не должен ссылаться на элементы, определённые далее в документе.
В более сложных пользовательских интерфейсах отказ от ссылок вперёд между элементами интерфейса может быть неприемлем. Вместо этого следует сделать все элементы управления отключенными по умолчанию и активировать их только в обработчике события onload , где мы можем быть уверены, что загрузка всей страницы завершена.
Заметим, что onload также ждёт окончания загрузки всех изображений (и фреймов, и т.п.). Если на странице есть большие изображения, это может занять длительное время. Обходным решением является активация страницы посредством встроенного скрипта внизу страницы. Он будет выполнен при окончании загрузки страницы, но не будет дожидаться загрузки внешних ресурсов.
В идеале, код на JavaScript никогда не должен выполняться слишком долго, поскольку это доставляет неудобства пользователю. Но в некоторых ситуациях это неизбежно. В этом случае следует показать сообщение вроде "Пожалуйста, подождите" или индикатор прогресса выполнения, чтобы сообщить пользователю, что браузер не завис. Задача состоит в том, что сообщение должно быть показано до начала выполнения длительного процесса.
Вот пример в псевдо-коде:
headlineElement.innerHTML = "Пожалуйста, подождите...";
performLongRunningCalculation();
headlineElement.innerHTML = "Закончено!";
В Internet Explorer и Mozilla текст "Пожалуйста, подождите..." никогда не будет показан пользователю, поскольку изменения будут отображены только после окончания работы скрипта. С другой стороны, в Opera текст "Пожалуйста, подождите..." будет отображён во время работы длительных вычислений.
Для того чтобы отобразить сообщение в Internet Explorer и Mozilla, необходимо на время передать управление интерфейсу браузера, чтобы сообщение было отрисовано до начала вычислений:
headlineElement.innerHTML = "Пожалуйста, подождите...";
function doTheWork() {
performLongRunningCalculation();
headlineElement.innerHTML = "Закончено!";
}
setTimeout(doTheWork, 0);
Фокус с setTimeout гарантирует, что сообщение будет отображено до того, как браузер будет заблокирован длительной работой. Конечно, во время вычислений браузер всё равно заблокирован, поэтому это является не очень элегантным решением. Если мы хотим избежать блокировки, следует разбить вычисления на несколько функций, соединённых вместе через setTimeout . Однако на этом пути возникает множество сложностей.
Каждое окно (и фрейм) обладает собственной очередью.
В Opera каждое окно обладает собственным потоком JavaScript. Это включает окна в iframe . Результатом этого является то, что обработчики событий, запущенные из различных фреймов, могут выполняться одновременно. Если эти одновременные скрипты обращаются к разделённым данным (например, свойствам главного окна), появляется возможность возникновения состояний гонок (race conditions).
Я не стану углубляться в возможные опасности состояний гонок, просто укажу на то, что они могут приводить к крайне загадочным багам.
В качестве решения можно всегда добавлять обработчики событий в очередь событий основного окна, даже если они были вызваны событиями в других фреймах.
Рассмотрим страницу с iframe . Элемент iframe обладает обработчиком onload , который будет выполнять функцию в содержащей его странице:
// плохая функция onload в frame:
window.top.notifyFrameLoaded()
Это опасно, поскольку onload может выполниться в тот момент, когда содержащая его страница выполняет другой скрипт. Однако функция может быть поставлена в очередь:
// хорошая функция onload в frame
window.parent.setTimeout(window.top.notifyFrameLoaded, 0)
Важно использовать метод setTimeout для родительского окна, чтобы занести функцию в очередь событий родительского окна.
- Избегать долго выполняющихся скриптов.
- Избегать синхронных запросов
XMLHttpRequest .
- Не позволять скриптам из разных фреймов управлять одним и тем же глобальным состоянием.
- Не использовать диалоги
alert для отладки, так как они могут совершенно изменить логику программы.
|
Хорошая серия статей. Спасибо.
Не понятно только почему не использовать alert для отладки. В некоторых ситуациях только алёртами и можно отследить что же не нравится этой прекрасной IEшке =)
Думаю потому что при алерте дается время на отработку не-пользовательских событий...
Верно, поэтому лучше использовать самодельную консольку с дивом и выводом туда отладочной инфы. (Либо консолями для браузеров)
Означает ли это , что элементы страницы парсятся в том порядке, в котором они стоят в тексте страницы (от начала к концу текста) ?
Да, так и есть.
HTML парсятся и отображаются прогрессивно, т.е. по мере скачивания документа браузером.
Огромное спасибо. За эту статью в частности, и за весь учебник в целом.
Кажется, в каком-то и пунктов опечатка. На мой взгляд во втором, по идее должно быть снизу вверх. Или я не прав?
На фазе перехвата - всегда сверху вниз. Просто некоторые события (например onfocus) не всплывают, для них последующая фаза всплытия (которая снизу вверх) отсутствует.
Не, автор, не верно. Сравни направление передачи события в ФФ и ИЕ, возможно это вас удивит.. а статья так себе
почему при onclick Button выполняются еще и onclick'и TD,TR
подскажите, как избежать такого, охота сохранить и onclick TR и onclick button
Происходит всплытие событий от child наверх
сначала выполняется событие на button, далее так как мышка находиться "внутри" td выполняется событие и на td, и т.д.
Сделать stopPropagation когда обрабатываете событие на кнопке. Тогда оно не будет распространятся дальше.
Такой вопрос, у меня долго выполняющийся скрипт, пробую в нём вместо (точнее после)
headlineElement.innerHTML = "Пожалуйста, подождите..." ;
установить, например, фоном кнопки, двигающийся кружок загрузки(анимированный gif)., но он в момент выполнения долгого скрипта, двигается только в опере, а в IE и FF статичен.
setTimeout(doTheWork, 0); устанавливаю....
В ИЕ пока управление передано браузеру, он не будет выполнять никаких анимации или перерисовки страницы, видимо все сделано для оптимизации скорости работы.
Под FF такого не замечено.
Похоже. что IE и FF просто зависают на время выполнения скрипта.(вкладки броузеров не работают) Видимо с этим ничего не сделать?
> Автор: GABRIEL_, дата: пт., 11/09/2009 - 13:06
> Кажется, в каком-то и пунктов опечатка. На мой взгляд во втором, по идее
> должно быть снизу вверх. Или я не прав?
"Перехват" и "всплытие" - это разные методы обработки вложенных событий (порядок противоположный).
Вот тут подробно описано:
http://javascript.ru/tutorial/events/intro#poryadok-srabatyvaniya-sobyti...
>Этот код будет работать:
>Однако следующий код работать не будет, поскольку выражения function >вычисляются только в ходе выполнения:
Видимо, опечатка.
Оба куска кода абсолютно одинаковы.
Наверное, в первом случае должно быть:
book с чего ты это взял, что они одинаковы?
Это правда, что между function g() и var g = function() есть разница.
Попробуй этот блок
и этот блок
А в FF 3.6.2 возникают...
К сожалению эта статья так и не разъяснила мне как синхронизировать потоки.
Я хотел что-то типа synchonized в джава SE
Ну или что-то что может заблокировать объект или функцию для выполнения только в одном объекте
Эх!
Придется кастыль писать!
Здравствуйте, Илья. Хорошая статья, многие не очевидные моменты насчет очередности событий стали понятнее, но есть один вопрос, почему не срабатывает событие onclick, при нажатии на кнопку. Я понимаю, что из-за того, что элемент скрывается, но какова суть процесса?
Вот поэтому и отказался от жабных скриптов, что глючат и ведут себя на разных броузерах по-разному. И другим советую, не захламлять себе этой дрянью голову. PHP рулит!
Сами поняли, что сказали:-))))?????????
"Отложенные скрипты не могут использовать document.write(), поскольку они не синхронизированы с парсером."
Что значит фраза "не могут"? В них нежелательно использовать document.write() или они этот метод просто "не заметят"?
Понимаю что тема давно минувших дней. Но есть вопрос, может удастся получить здесь ответ. Ситуация такова: выводится форма и на событие on-load правятся некие моменты в отображении, а именно метки располагаются на элементах формы а не рядом/над ними. И есть обработчики событий которые метки скрывают/показывают на событиях onfocus/onblur.
Всё бы хорошо, и, в принципе, при обращении к странице я не вижу первого варианта отображения, браузеры успевают стартовые скрипты прогнать до отображения и пользователь видит форму, как и задумывали дизайнеры.. НО! при обновлении страницы, происходит более шустрый процесс отображения, а следовательно сначала видно форму с метками над элементами, а через секунду-другую все становиться на свои места... это конечно не критично, но не очень приятно. Может подскажете можно ли как-то избежать этого?
P.S. 1. Про то что в HTML5 есть простое решение для этой задачи я в курсе, в моём случае не подходит, ибо требования к кросс-браузерности высокие, да и CMS формирует XHTML разметку, а валидность тоже в требованиях.
2. Сохранение стандартной разметки для меток/элементов при выключенном JS опять же важно.
Полезно
Нужен скрипт отображения времени загрузки страницы и функцией выполнения определенный действий при определенных значениях... Например: Скрипт показывает, что время загрузки страницы меньше секунды, а действие при этом будет следующее: выводится текст типа "отличная скорость" или если больше двух, трех секунд - сообщение "скорость хорошая"...
Есть что-то, что может перехватить выполнение кода dispatchEvent()?
Здравствуйте! Подскажите пожалуйста...
Я вот вызываю в цикле запросы через XMLHttpRequest, всего штук 8-12, и мне надо как-то дождаться момента когда они выполнятся ВСЕ, а дальше уже выполнить код оперирующий с результатами одновременно ВСЕХ запросов.
Как бы это корректно сделать?