Удивительная история document.write

Автор оригинала: Zack Bloom
  • Перевод
Метод document.write — один из самых странных методов. Он вставляет HTML-код на страницу сразу после себя. Точнее говоря, сразу после тега <script>, внутри которого он расположен. И только в том случае, если документ еще не был загружен полностью. А если был? Тогда страница очищается и заменяется на, что было указано.

Можно вставить строку, которая явно сломает остальную страницу:

document.write('<plaintext>')

Или можно поиграть в русскую рулетку:

if (Math.random() > 0.9)
  document.write('<!--')

Оказывается, тот факт, что текст вставляется сразу после тега <script> — это важно. Например:

<script>
  document.write('<script src="jquery.js"></' + 'script>');
  alert(jQuery.guid)
</script>

Данный код выведет ошибку о том, что переменная jQuery не объявлена.
С другой стороны:

<script>
  document.write('<script src="jquery.js"></' + 'script>');
</script>
<script>
  alert(jQuery.guid)
</script>

А этот код выполнится без ошибок, потому что второй тег <script> не начнет исполняться до тех пор, пока не будет полностью обработан скрипт, подгруженный из первого тега.

Дальше становится все любопытнее. Представим, что есть такой код:

<script>
  console.log('a');
  document.write('<script src="printC.js"></' + 'script>');
  console.log('b');
</script>
<script>
  console.log('d');
</script>

А в файле printC.js содержится следующее:

console.log('c')

Данный код выведет в консоль следующее:

a
b
c
d

Из этого примера видно, что весь код из первого тега <script> после document.write был выполнен сразу же, а вот второй тег начал исполняться только после того, как завершил свою работу printC.js.

Следует помнить. что этот способ вставки контента работает только непосредственно во время загрузки документа. Что будет, если вызвать document.write после того, как он уже загружен? Было бы логично выдать ошибку, или не делать ничего. Вместо этого вся существующая страница стирается, а на ее место выводится переданная строка:

setTimeout(function(){
  document.write("Oops");
})

Существует ли нормальный повод применять document.write?


Я о таком не слышал. По всей видимости, он используется в баннерокрутилках, потому что позволяет показать отслеживающую картинку вместо AJAX-запроса, если пользователь заходит на сайт через Netscape 2. В современном же мире он особо ни для чего не нужен.

Правда, иногда встречаются замечательные инструменты, которые незаметно оборачивают передаваемый вами HTML в вызовы document.write.

Можно ли вызвать document.write внутри document.write?


Ага!

Типа, бесконечно много раз?


Нет, 20 раз.

Серьезно? Двадцать?


Ага.

Google Chrome, несомненно, более совершенный браузер — там можно аж 21 раз.

Они что, независимо друг от друга до этого додумались?


Разработчики Firefox первыми исправили баг, потенциально позволявший уронить браузер, а разработчики Webkit скопировали их решение. Ходят слухи, что в IE тоже есть некий предел, но он еще ниже.

Можно ли сломать браузер еще быстрее?


<div id="uniqid">
  <script language="JavaScript" type="text/JavaScript">
    document.write(">"+document.getElementById('uniqid').innerHTML+"<");
  </script>
</div>

(Код взят из тикета)

Какие еще глупости можно натворить с document.write?


Да какие угодно!

Например, можно сделать синхронный AJAX-запрос и вставить его результат непосредственно в тело страницы:

x = new XMLHttpRequest()
x.open('GET', "", false)
x.send()
document.write(x.responseText);

Отправка запроса заблокирует загрузку основной страницы, и содержимое ответа будет вставлено так, как если бы оно изначально располагалось на загружаемой странице. В данном случае вместо URL передана пустая строка, поэтому загружаться будет та же самая страница, а она, в свою очередь, снова начнет загружать сама себя, и так до бесконечности*.

* Под «до бесконечности» подразумевается 20 раз. Или 21. Или еще меньше, если вы в IE.

В общем, можно развлекаться по-разному.

Что-нибудь еще?


В текущей спецификации этого нет (хотя было в прошлых версиях), но можно передавать в document.write несколько параметров, например:

document.write("П", "Р", "Е", "В", "Е", "Д");

Если метод бесполезен, зачем я про него вообще читаю?


А вот это действительно хороший вопрос!

Примечание переводчика:

Любопытно, что только первый вызов document.write после загрузки страницы очищает ее содержимое. Последующие вызовы дописывают текст после первого.
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 35

    0
    позвольте ламерский вопрос:
    а что значит «когда страница загружена»? Как определяется, что страница загружена?
      0
      Я полагаю, после отработки события onLoad?
        0
        Если не ошибаюсь, страница считается загруженной, когда браузер получил ее код от сервера целиком и построил по нему DOM. Пользователь может отследить этот момент, как правильно сказал Vindicar, по событию onLoad, или с помощью более удобного синтаксиса у jQuery.
          +2
          То, что вы описали — это DOMContentLoaded, событие Load срабатывает при полной обработке всех ресурсов страницы. Однако, в таких ненормальных условиях, как наличие document.write непосредственно в script-теге, возможно существует и какое-то иное поведение.
          +27
          События, происходящие до полной загрузки страницы.
          image

          +3
          полезно еще знать, что есть document.close(), который по-хорошему стоит вызывать после document.write, иначе некоторые браузеры будут показывать крутилку «загрузки».
            +1
            Если я правильно понял, то document.close(); актуально только при создании дочерних окон и выполнении document.write в контексте нового окна. Так?
              0
              В моём случае это наблюдалось с src-less iframe (тобишь да, дочерним окном), но я без понятия, ограничивается ли явление только такими сценариями.
            +1

            Оригинальная статья какого года? У меня первая ассоциация: "школьник 21 века и кассетный плеер". Я так стар? Уже выросло поколение, не заставшее эпоху, когда половина сайтов работала на это хреновине?

              0
              Половина энтерпрайза все еще использует document.write, некоторые даже в относительно новых продуктах. Все зависит от того, какой ответ нагуглился и заработал первым у «талантливого» offshore разработчика.
                0
                Я застал сайт, делавший document.write("<br>"); по числу нужных br'ов. Прошли годы. но я до сих пор не понимаю смысла так делать (в попавшем в руки этом коде просто заменил их на <br> ибо короче, а модемы бывали и на 2400).
                +3
                > Существует ли нормальный повод применять document.write?
                > Я о таком не слышал.

                А как же что-то типа этого?
                <script src="//ajax.aspnetcdn.com/ajax/jquery/jquery-2.0.0.min.js"></script>
                <script>window.jQuery || document.write('<script src=«js/jquery-2.0.0.min.js»>\x3C/script>')</script>
                  +4

                  Поговаривают что некоторые программисты пользуют document.createElement('script') — врут наверное.

                    +1
                    По вашей же ссылке лучший ответ:

                    You can't do it synchronously.


                    И следующий ответ советует использовать document.write.
                      0

                      А зачем вам это делать synchronously? Какая объективная причина может быть для этого?

                        0
                        Чуть ниже могут быть скрипты, зависящие от jQuery, и они сломаются.
                          0

                          если сторонний скрипт или легаси — то да, может быть, но в своем коде — то проверяться надо и предохраняться. Зависимостей может не быть по многим причинам.

                            0
                            А вы не используете сторонние скрипты и легаси код? Я счастлив за вас.
                            А в реальном мире и первого и второго навалом, поэтому лучше гарантировать загрузку нужных зависимостей вовремя, нежели чем переписывать тонны скриптов в расчёте на внезапную их пропажу.
                              +1

                              Использую, зачем вы из меня плюшевого мишку делаете? Дискуссия развернулась с вот этой фразы — "Существует ли нормальный повод применять document.write?". ПОВОД! Не нужда.

                                0
                                > Использую, зачем вы из меня плюшевого мишку делаете?
                                Извиняюсь за немного резкую фразу.
                                > ПОВОД! Не нужда.
                                Как- то не отделяю данные понятия. Нужда даёт повод.
                                  0
                                  Извиняюсь за немного резкую фразу.

                                  Принято )


                                  Как- то не отделяю данные понятия. Нужда даёт повод.

                                  Ну как же? "А как бы нам тут приспособится к этой гребаной либе, которая использует :"№%%:? Да, придется делать document.write." Это нужда.
                                  "А как бы нам тут document.write поиспользовать. Очень хочется, есть где?" Это поиск повода.
                                  Так вот поводов использовать document.write — нет, язык развился, DOM развился, есть нормальные инструменты, закопайте стюардессу.

                                    0
                                    > Так вот поводов использовать document.write — нет
                                    Так же выше повод- синхронная вставка зависимостей, такой стиль имеет право на жизнь.
                  +7
                  >> * Под «до бесконечности» подразумевается 20 раз.

                  Автор статьи, видимо, раньше в рекламе работал?
                    0
                    Применял document.write() именно на этапе загрузки страницы, в зависимости от UserAgent. А далее уже подгружаемый фрейворк разобрал «вписанную» структуру по своим правилам. И всё в одном html файле, без внешних зависимостей и т.п. Помоему довольно удобно.
                      +2
                      Существует ли нормальный повод применять document.write?

                      wnd=window.open('...');
                      wnd.document.open('....');
                      wnd.document.write('....');
                        +4
                        «Существует ли нормальный повод применять document.write?» Как-то делал фан-сайт игры основной функциональностью которого было показывать логи. Т.е. информация однотипна, уникальной информации мало, и большую часть составляет статистика вычисленная на основе базовой информации, подсвеченные интересные моменты и т.п. Сначала были static html таблички по мегабайту и больше, но т.к. предполагалось что их должно быть много и храниться должны на сервере чуть ли не вечность переделал html в: и все. А подгружаемый скрипт уже с помощью document.write рисовал всю страницу полностью. В итоге вместо >1Мб html, я получил 100b html + 100kb js (который кэшируется) — неимоверная экономия места на сервере, экономия трафика, и страницы стали загружаться моментально*.

                        *Под «моментально» имеется в виду время загрузки было меньше времени реакции человека.
                          +1
                          Вообще, не совсем «пока страница не загружена».
                          Когда вызовы идут в основном потоке загрузки, то пишется в этот поток.
                          Но, например:

                          setTimeout(function () {
                          document.write(4);
                          }, 0);

                          Если страница будет долго грузится и таймер сработает раньше, он грохнет текущий документ ещё до его загрузки.
                            0
                            Много раз сталкивался с примерами document.write и до сих пор не понимаю почему всегда когда вставляется скрипт он разбит на части
                            document.write('<script src="printC.js"></' + 'script>');
                            

                            Но ведь и
                            document.write('<script src="printC.js"></script>');
                            

                            будет работать точно также.
                            Это эстетство или есть определенные подводные камни которые обходят таким образом?
                              +2
                              Перестраховка, по большей части: http://stackoverflow.com/a/236106/1037345
                                +1

                                Закрывающий тег script может быть распознан браузером как конец скрипта. А вот вариант


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

                                Валиден повсюду.

                                  0

                                  del

                                  +1
                                  на сколько я помню, document.write не закрывает документ, и потому дописывает после первого стирания. Сам браузер закрывает документ когда построит DOM, и вызов document.write просто открывает новый документ

                                  попробуйте следующий пример:
                                  document.write(1); document.write(2);
                                  document.close(); document.write(3);

                                  На экране будет только — 3
                                    +1
                                    Какие еще глупости можно натворить с document.write?

                                    Mobify.jsтыц
                                      0
                                      Не поэтому ли все закладки Geektimes у меня крутятся бесконечно? И даже Escape не останавливает, потому что нажатия перехватываются яндексовой метрикой. Только крестиком остановить можно.
                                        +1
                                        У меня есть пример использования document.write() в юзерскрипте, в котором нужно полностью заменить страницу своей, с более мощной функциональностью.

                                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                        Самое читаемое