2014-01-10 3 views
7

У меня есть JavaScript в теге HEAD, который динамически вставляет асинхронный загрузочный тег сценария перед последним скриптом на странице (который в настоящее время разбирался). Этот динамически включенный тег скрипта содержит JavaScript, который должен анализировать DOM после того, как DOM доступен, но до того, как были загружены все изображения и теги скриптов. Важно, чтобы JavaScript запускался до того, как все JS было загружено, потому что если есть висячий скрипт, это приведет к плохому пользователю. Это означает, что я не могу дождаться события DOMContentLoaded. У меня нет никакой гибкости относительно того, где я помещаю первый бит JavaScript, который динамически включает тег скрипта.Порядок выполнения выполнения DOM

Мой вопрос: безопасно ли мне начать синтаксический анализ через DOM сразу, не дожидаясь события DOMContentLoaded? Если нет, есть ли способ сделать это, не дожидаясь события DOMContentLoaded?

+0

Что вы имеете в виду «прежде всего JS был загружен в»? Вы не можете контролировать это, когда динамически генерируют элементы сценария, которые будут загружаться асинхронно. И поскольку висячие скрипты _allways_ приводят к плохому UX, вы должны избегать их, а не включать их в свои требования. – Bergi

+5

[Какая проблема вы пытаетесь решить] (http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem)? Может быть, это пример того, где вам нужно это поведение. – Bergi

+0

@Bergi Висячие скрипты из моего контроля, потому что это не веб-страницы, которые я контролирую –

ответ

1

... JavaScript в ГОЛОВЕ ... динамически вставляет асинхронно ЗАГРУЗКА тег сценария перед тем последний сценарий на странице ...

Я принимаю the loader script is inline, а это означает, что выделенный бит фактически относится к «текущему» элементу сценария, т.е. загрузчику. Это происходит потому, что только html, предшествующий тегу сценария загрузчика, был проанализирован и интерпретирован, поэтому вставленный тег скрипта на самом деле все еще находится в head, а не в нижней части страницы. Таким образом, целевой скрипт ограничивается выполнением операций DOM только для предыдущих элементов, если только вы не переверните код в готовый обратный вызов DOM ... это то, чего вы пытаетесь избежать в первую очередь!

В основном вы хотите загрузить весь html, чтобы страница была видимой/сканируемой, начать загрузку изображений/таблиц стилей (что происходит в неблокирующих потоках) и , затем загрузить любой javascript. Один из подходов заключается в put your target script at the bottom of the page, просто выберите их порядок правильно (интерактивность сначала, усовершенствования второй, сторонняя аналитика/интеграция в социальные сети/что-то еще супер-тяжелое последнее) и приспосабливаются к вашим потребностям. Технически it still blocks the page load, но есть все сценарии, оставленные в нижней части страницы в любом случае (и поскольку они находятся внизу, вы сможете напрямую манипулировать DOM, как только они будут загружены, за вычетом некоторых особенностей IE7).

Существует соответствующий rant/overview Я хотел сделать ссылку на который предоставляет достойные примеры и некоторую газораспределительную мелочь на использовании и злоупотреблении DOM готовых обратных вызовов, а также "other side of the story" о том, почему звездной производительности может иметь меньшую ценность, чем нормальный системы управления зависимостями. Тема последнего слишком широка, чтобы быть исчерпанной в одном ответе, но что-то вроде requirejs documentation должно дать вам представление о том, как работает шаблон.

Возможно, еще одним шаблоном для рассмотрения является создание приложения для одной страницы SPA, которое использует асинхронный доступ к фрагментам контента, а не «традиционную» навигацию между полными страницами. Шаблон имеет недооцененную, но довольно значительную выгоду от производительности от not having to parse and re-execute shared javascript on every page, которая также будет учитывать вашу (действительную) озабоченность по поводу производительности сторонних js. В конце концов, только a good caching policy будет делать чудеса для времени загрузки, но плохой код javascript или избыточные накладные расходы на каркасах остаются.

Обновление: Выяснено. С учетом вашего конкретного сценария (т.нет контроля над Разметкой самого по себе, и хочет быть последним сценарием для выполнения), вы должны обернуть вставку элемента асинхронного сценария в DOM в 0ms setTimeout обратного вызов:

setTimeout(function(){ 

    //the rest is how GA operates 
    var targetScript = document.createElement('script'); 
    targetScript.type = 'text/javascript'; 
    targetScript.async = true; 
    targetScript.src = 'target.js'; 

    var s = document.getElementsByTagName('script')[0]; 
    s.parentNode.insertBefore(targetScript, s); 

}, 0); 

Благодаря одно- поточный характер среды, js setTimeout обратный вызов в основном добавляется в очередь для выполнения 0мс-задержки , как только поток больше не занят (more thorough explanation here). Поэтому браузер даже не осознает необходимость загрузки, не говоря уже о выполнении целевого скрипта, до тех пор, пока не будет завершен код «более высокий приоритет»! И поскольку DOM работает, когда добавляется тег скрипта, вам не нужно будет проверять его явно в самом целевом скрипте (что удобно, когда оно загружается «мгновенно» из кеша).

+0

Спасибо за подробный ответ. К сожалению, в некоторых случаях JavaScript не может быть вставлен в нижней части страницы. У вас есть ответ с этим требованием? –

+0

@JustinMeltzer: вам нужно иметь достаточный контроль над разметкой, чтобы переместить теги (если нет, у вас больше проблем, чем неаккуратная производительность). Да, если вы находитесь за F5, возможно, вы можете применить профиль пользовательского потока для этого на лету, и, вероятно, существует существующий модуль Apache, который делает то же самое - но разметка не должна требовать таких обходных решений. Что вы, RoR? Если да, [у этого парня есть правильная идея] (http://coderberry.me/blog/2012/04/24/asset-pipeline-for-dummies/). Если нет, вы должны обновить вопрос со сведениями о вашем стеке/ограничениями. –

+0

Моя компания предоставляет услугу, которая требует от наших клиентов вставки фрагмента кода на свои страницы, поэтому мы не имеем непосредственного контроля над ним. –

1

Поведение следующих способов сделать его безопасным для анализа DOM ...

  1. Использование нагрузки окна или DOMContentLoaded событие
  2. Declare или вводить свой сценарий в нижней части страницы
  3. Place " асинхронной»атрибут на вашем тег сценария
  4. или делать это:
<script> 
    setTimeout(function(){ 
    // script declared inside here will execute after the DOM is parsed 
    },0); 
</script> 

Также они будут NOT BLOCK Загрузка страницы в DOM.

Не нужно вызывать событие DomContentLoaded при объявлении скрипта под любым DOM, в зависимости от того, что вы хотите выполнять вычисления или позиционирование по размеру, поскольку изображения/видео изменят размер элементов, если ширина/высота не указаны ,

Вот несколько сценариев, где это работает.

DEPENDENT DOM IS ABOVE 
<script src="jquery.js"></script> 
<script> 
    $('mydom').slideDown('fast'); 
</script> 

или попробовать это:

<script> 
    // won't fart 
    setTimeout(function(){ console.log(document.getElementById('mydom').innerHTML); },0); 
</script> 
DEPENDENT DOM IS BELOW or ABOVE (dont' matter) 

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

http://jsfiddle.net/FFLL2/

+1

Почему вы думаете, что 'setTimeout' отложит вашу функцию до загрузки DOM? – Bergi

+0

Спасибо, что заставили меня дважды проверить эти примеры, @Bergi. Загрузка внешних синхронных скриптов не будет работать с зависимым кодом setTimeout, однако встроенные немедленные работы, как я показал здесь. –

+0

@JasonSebring: Хмм, я помню, что использовал 0ms 'setTimeout' для отсрочки выполнения сценария в подобной ситуации, но я не могу вспомнить, откуда взялось решение. Любые ссылки? –

0

Да, вы должны ждать агента пользователя, чтобы сказать вам, что загружен DOM. Однако есть несколько способов сделать это.

Существует разница между onreadystatechange интерактивному и DOMContentLoaded.

По http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html первое, что агент пользователя делает, после остановки разбора документа, является установка документа о готовности к «интерактивным».

Пользовательский агент не ожидает загрузки скриптов.

При изменении готовности документа пользовательский агент будет запускать readystatechange в объекте Document.

Так что если скрипты, о которых вы беспокоитесь, не являются встроенными, вы можете подключиться к readystatechange.

Говоря о кросс-браузере: это спецификация.

0

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

http://www.html5rocks.com/en/tutorials/speed/script-loading/

Смежные вопросы