Метод 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 после загрузки страницы очищает ее содержимое. Последующие вызовы дописывают текст после первого.
Поделиться с друзьями
-->

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


  1. schroeder
    10.07.2016 19:02

    позвольте ламерский вопрос:
    а что значит «когда страница загружена»? Как определяется, что страница загружена?


    1. Vindicar
      10.07.2016 19:17

      Я полагаю, после отработки события onLoad?


    1. impwx
      10.07.2016 19:38

      Если не ошибаюсь, страница считается загруженной, когда браузер получил ее код от сервера целиком и построил по нему DOM. Пользователь может отследить этот момент, как правильно сказал Vindicar, по событию onLoad, или с помощью более удобного синтаксиса у jQuery.


      1. heilage
        10.07.2016 19:53
        +2

        То, что вы описали — это DOMContentLoaded, событие Load срабатывает при полной обработке всех ресурсов страницы. Однако, в таких ненормальных условиях, как наличие document.write непосредственно в script-теге, возможно существует и какое-то иное поведение.


    1. vermilion1
      10.07.2016 20:21
      +27

      События, происходящие до полной загрузки страницы.
      image


  1. artyfarty
    10.07.2016 19:59
    +3

    полезно еще знать, что есть document.close(), который по-хорошему стоит вызывать после document.write, иначе некоторые браузеры будут показывать крутилку «загрузки».


    1. yarkov
      10.07.2016 22:46
      +1

      Если я правильно понял, то document.close(); актуально только при создании дочерних окон и выполнении document.write в контексте нового окна. Так?


      1. artyfarty
        10.07.2016 23:03

        В моём случае это наблюдалось с src-less iframe (тобишь да, дочерним окном), но я без понятия, ограничивается ли явление только такими сценариями.


  1. Zenitchik
    10.07.2016 22:50
    +1

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


    1. mike114
      11.07.2016 04:00

      Половина энтерпрайза все еще использует document.write, некоторые даже в относительно новых продуктах. Все зависит от того, какой ответ нагуглился и заработал первым у «талантливого» offshore разработчика.


    1. Pakos
      12.07.2016 13:04

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


  1. olen
    10.07.2016 23:22
    +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>


    1. gearbox
      12.07.2016 21:53
      +4

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


      1. helarqjsc
        13.07.2016 10:01
        +1

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

        You can't do it synchronously.


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


        1. gearbox
          13.07.2016 16:13

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


          1. sumanai
            13.07.2016 17:09

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


            1. gearbox
              13.07.2016 20:26

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


              1. sumanai
                13.07.2016 20:41

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


                1. gearbox
                  13.07.2016 21:05
                  +1

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


                  1. sumanai
                    13.07.2016 22:15

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


                    1. gearbox
                      14.07.2016 09:58

                      Извиняюсь за немного резкую фразу.

                      Принято )


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

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


                      1. sumanai
                        14.07.2016 16:05

                        > Так вот поводов использовать document.write — нет
                        Так же выше повод- синхронная вставка зависимостей, такой стиль имеет право на жизнь.


  1. fireSparrow
    11.07.2016 09:57
    +7

    >> * Под «до бесконечности» подразумевается 20 раз.

    Автор статьи, видимо, раньше в рекламе работал?


  1. adasoft
    11.07.2016 10:45

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


  1. izyk
    11.07.2016 15:52
    +2

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

    wnd=window.open('...');
    wnd.document.open('....');
    wnd.document.write('....');


  1. LynXzp
    12.07.2016 00:51
    +4

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

    *Под «моментально» имеется в виду время загрузки было меньше времени реакции человека.


  1. gro
    12.07.2016 11:10
    +1

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

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

    Если страница будет долго грузится и таймер сработает раньше, он грохнет текущий документ ещё до его загрузки.


  1. GEMOzloBIN
    12.07.2016 11:27

    Много раз сталкивался с примерами document.write и до сих пор не понимаю почему всегда когда вставляется скрипт он разбит на части

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

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

    будет работать точно также.
    Это эстетство или есть определенные подводные камни которые обходят таким образом?


    1. ookami_kb
      12.07.2016 11:40
      +2

      Перестраховка, по большей части: http://stackoverflow.com/a/236106/1037345


    1. Zenitchik
      12.07.2016 13:25
      +1

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


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

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


    1. Zenitchik
      12.07.2016 13:33

      del


  1. saaadel
    12.07.2016 13:12
    +1

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

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

    На экране будет только — 3


  1. n1sh
    12.07.2016 18:09
    +1

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

    Mobify.jsтыц


  1. unchqua
    14.07.2016 11:51

    Не поэтому ли все закладки Geektimes у меня крутятся бесконечно? И даже Escape не останавливает, потому что нажатия перехватываются яндексовой метрикой. Только крестиком остановить можно.


  1. grompe
    17.07.2016 03:17
    +1

    У меня есть пример использования document.write() в юзерскрипте, в котором нужно полностью заменить страницу своей, с более мощной функциональностью.