Onload

DOMContentLoaded

イベントは オブジェクトで発生します。


キャッチするためには を使わなければなりません:

例:

例では、 ハンドラはドキュメントがロードされたときに実行され、ページ読み込みは待ちません。したがって、 で表示されるサイズはゼロです。

一見すると、 イベントはとてもシンプルです。DOM ツリーが準備できた – ここのイベントです。しかし、特徴はほとんどありません。

ブラウザが最初にHTMLをロードし、テキスト中の に出くわすと、DOM の構築を続けることができません。すぐにスクリプトを実行する必要があります。そのため、このようなスクリプトがすべて実行された後にのみ は発生する可能性があります。

外部スクリプト ( を持つもの) も、スクリプトがロードされ実行されている間はDOM の構築は一時停止します。したがって、 は外部スクリプトも待ちます。

唯一の例外は、 や 属性をもつ外部スクリプトです。これらは、ブラウザにそのスクリプトは待たずに処理を続けるよう言います。なので、ユーザはスクリプトのロードが終わる前にページを見ることができ、パフォーマンスに良いです。

と に関する言葉

属性 と は外部スクリプトに対してのみ動作します。 がない場合には無視されます。

どちらも、ブラウザにページでの処理をつづけるよう言い、“バックグラウンド” でスクリプトをロードします。その後、ロードできたらスクリプトを実行します。したがって、スクリプトは DOM の構築とページレンダリングをブロックしません。

そららの間に2つの違いがあります。

順番 を持つスクリプトは 読み込んだもの順 で実行します。ドキュメント順は関係ありません – 最初にロードされたものが最初に実行されます。 を持つスクリプトは常に ドキュメント順 で実行されます。
を持つスクリプトは、ドキュメントがまだ完全にダウンロードされていなくても実行されることがあります。これはスクリプトが小さい or キャッシュされており、ドキュメントが十分長い場合に発生します。 を持つスクリプトは、ドキュメントがロードされ、パースされた後(必要に応じて待機する)に実行されます。 の直前です。

そのため、 は完全に独立したスクリプトに対して使われます。

外部のスタイルシートは DOM には影響しないので、 はそれらを待ちません。

しかし、落とし穴があります: スタイルの後にスクリプトがある場合、そのスクリプトはスタイルシートが実行されるのを待たなければなりません。:

この理由は、上の例のように、スクリプトが座標や他のスタイルに依存した要素のプロパティを取得したい場合があるためです。当然、それはスタイルがロードされるのを待たなければいけません。

がスクリプトを待つので、その前のスタイルも同様に待つことになります。

Firefox, Chrome や Opera の自動入力は で起こります。

例えば、ページがログインとパスワードのフォームを持っていて、ブラウザがその値を覚えていた場合、 でそれらを自動入力しようとする場合があります(ユーザが許可している場合)。

なので、読み込みに時間のかかるスクリプトによって が延びると、自動入力もまた待ちます。恐らくあなたもサイトによっては見たことがあるでしょう(ブラウザの自動入力を利用している場合) – ログイン/パスワードフィールドの自動入力がすぐにはされず、ページが完全にロードされるまで遅延があります。実際、それは イベントまでの遅延です。

外部スクリプトに対して や を使う小さなメリットの1つは – をブロックせず、ブラウザの自動入力に遅延がないことです。

Код кроссбраузерной поддержки

«Родное» событие есть не во всех браузерах, поэтому мы рассмотрим код для кроссбраузерной поддержки этого события:

function bindReady(handler){

	var called = false

	function ready() { // (1)
		if (called) return
		called = true
		handler()
	}

	if ( document.addEventListener ) { // (2)
		document.addEventListener( "DOMContentLoaded", function(){
			ready()
		}, false )
	} else if ( document.attachEvent ) {  // (3)

		// (3.1)
		if ( document.documentElement.doScroll && window == window.top ) {
			function tryScroll(){
				if (called) return
				if (!document.body) return
				try {
					document.documentElement.doScroll("left")
					ready()
				} catch(e) {
					setTimeout(tryScroll, 0)
				}
			}
			tryScroll()
		}

		// (3.2)
		document.attachEvent("onreadystatechange", function(){

			if ( document.readyState === "complete" ) {
				ready()
			}
		})
	}

	// (4)
    if (window.addEventListener)
        window.addEventListener('load', ready, false)
    else if (window.attachEvent)
        window.attachEvent('onload', ready)
    /*  else  // (4.1)
        window.onload=ready
	*/
}

Разберем его по шагам.

  1. Код будет пытаться поймать событие различными способами. Вполне может получиться так, что несколько способов сработают независимо.

    Поэтому завернем обработчик в функцию , единственный смысл которой — гарантировать, что будет вызван не более одного раза.

  2. Событие поддерживают достаточно новые Firefox, Opera, Safari/Chrome. Нет гарантии, что версия посетителя поддерживает это событие, но попробовать стоит.
  3. Браузер Internet Explorer не поддерживает , поэтому для него используются обходные пути.
    1. Функция пытается скроллить документ вызовом . Если получается — значит, документ загрузился, если нет — заказывает повторную попытку через setTimeout, и так пока документ наконец не будет готов. На практике это очень надежный способ, но есть проблемы с фреймами, поэтому используется только для окон верхнего уровня. Дополнительный фильтр — проверка
    2. Событие с проверкой , как и , срабатывает после загрузки документа. Но, к сожалению, оно происходит уже после загрузки картинок. Поэтому — вообще говоря, не то, что нам надо. Но это событие работает для фреймов, и при этом срабатывает до . Поэтому будем использовать и этот способ.
  4. Для тех браузеров, в которых не сработали предыдущие методы (например, очень старый Firefox), добавим вызов обработчика при событии .
    1. Для совсем древних браузеров, в которых нет , вы можете раскомментировать и строчку (4.1). При этом, разумеется, возможен конфликт с другими обработчиками .

Этот код взят, с небольшими упрощениями, из библиотеки jQuery, а методы придуманы различными авторами.

Событие onprogress в деталях

При обработке события есть ряд важных тонкостей.

Можно, конечно, их игнорировать, но лучше бы знать.

Заметим, что событие, возникающее при , имеет одинаковый вид на стадии отправки (в обработчике ) и при получении ответа (в обработчике ).

Оно представляет собой объект типа со свойствами:

Сколько байт уже переслано.

Имеется в виду только тело запроса, заголовки не учитываются.

Если , то известно полное количество байт для пересылки, и оно хранится в свойстве .

Общее количество байт для пересылки, если известно.

А может ли оно быть неизвестно?

  • При отправке на сервер браузер всегда знает полный размер пересылаемых данных, так что всегда содержит конкретное количество байт, а значение всегда будет .
  • При скачивании данных – обычно сервер в начале сообщает их общее количество в HTTP-заголовке . Но он может и не делать этого, например если сам не знает, сколько данных будет или если генерирует их динамически. Тогда будет равно . А чтобы отличить нулевой размер данных от неизвестного – как раз служит , которое в данном случае равно .

Ещё особенности, которые необходимо учитывать при использовании :

Событие происходит при каждом полученном/отправленном байте, но не чаще чем раз в 50 мс. Это обозначено в .

В процессе получения данных, ещё до их полной передачи, доступен , но он не обязательно содержит корректную строку. Можно до окончания запроса заглянуть в него и прочитать текущие полученные данные

Важно, что при пересылке строки в кодировке UTF-8 кириллические символы, как, впрочем, и многие другие, кодируются 2 байтами. Возможно, что в конце одного пакета данных окажется первая половинка символа, а в начале следующего – вторая

Поэтому полагаться на то, что до окончания запроса в находится корректная строка нельзя. Она может быть обрезана посередине символа. Исключение – заведомо однобайтные символы, например цифры или латиница.

Сработавшее событие не гарантирует, что данные дошли. Событие срабатывает, когда данные отправлены браузером. Но оно не гарантирует, что сервер получил, обработал и записал данные на диск. Он говорит лишь о самом факте отправки. Поэтому прогресс-индикатор, получаемый при его помощи, носит приблизительный и оптимистичный характер.

Loading a script

Let’s say we need to load a third-party script and call a function that resides there.

We can load it dynamically, like this:

…But how to run the function that is declared inside that script? We need to wait until the script loads, and only then we can call it.

Please note:

For our own scripts we could use JavaScript modules here, but they are not widely adopted by third-party libraries.

The main helper is the event. It triggers after the script was loaded and executed.


For instance:

So in we can use script variables, run functions etc.

…And what if the loading failed? For instance, there’s no such script (error 404) or the server is down (unavailable).

Errors that occur during the loading of the script can be tracked in an event.

For instance, let’s request a script that doesn’t exist:

Please note that we can’t get HTTP error details here. We don’t know if it was an error 404 or 500 or something else. Just that the loading failed.

Important:

Events / track only the loading itself.

Errors that may occur during script processing and execution are out of scope for these events. That is: if a script loaded successfully, then triggers, even if it has programming errors in it. To track script errors, one can use global handler.

readyState

ドキュメントがロードされた後に ハンドラを設定すると何が起こるでしょうか?

当然、実行されません。

ドキュメントが準備できたかどうかが定かでない場合があります。例えば 属性を持つ外部スクリプトを読み込み、それが非同期に実行するような場合です。ネットワークに依存して、ドキュメントが完成する前に実行されるかもしれないし、その後かもしれず、定かではありません。従って、我々はドキュメントの現在の状態を知ることができるべきです。

プロパティは我々にその情報を提供します。3つの値を取り得ます。:

  • – ドキュメントがロード中です。
  • – ドキュメントは完全に読み込まれました。
  • – ドキュメントは完全に読み込まれ、すべてのリソース(画像のような)も読み込まれました。

なので、 をチェックし、ハンドラを設定、もしくはすでに準備出来ている場合はすぐにコードを実行することができます。

このように:

状態が変わったときにトリガされる イベントがあります。なので、次のようにしてこれらの状態を出力することができます。:

イベントはドキュメントの読み込み状態を追跡する代替のメカニズムで、それは昔に登場しました。最近ではほとんど使われていませんが、完全性のために説明しておきます。

他のイベントの中で の場所はどこでしょう?

タイミングを見るために、ここではイベントを記録する , とハンドラを持つドキュメントがあります。:

動作例は サンドボックスの中 です。

典型的な出力:

  1. initial readyState:loading
  2. readyState:interactive
  3. DOMContentLoaded
  4. iframe onload
  5. readyState:complete
  6. img onload
  7. window onload

角括弧内の数値はそれが発生したおおよその時間を示します。実際の時間はもう少し大きいですが、同じ数字でラベル付けされたイベントは、ほぼ同時に発生します(± 数ミリ秒)。

  • は の直前に になります。これら2つのイベントは実際には同じことを意味します。
  • は、すべてのリソース( や )がロードされたときに になります。ここでは、 ( は最後のリソース) と がほぼ同じ時間に発生していることが分かります。 状態にスイッチすることは、 と同じことを意味します。違いは、 は常に他のすべての ハンドラの後で動作するということです。

Определение и использование

Атрибут onload срабатывает при загрузке объекта.

onload чаще всего используется в элементе <body> для выполнения сценария после того, как веб-страница полностью загрузила все содержимое (включая изображения, файлы скриптов, файлы CSS и т.д.). Однако его можно использовать и на других элементах (Смотрите «Поддерживаемые HTML-теги» below).

Для входных элементов атрибут onload поддерживается только при <input type=»image»>

Атрибут onload можно использовать для проверки типа браузера посетителя и версии браузера и загрузки соответствующей версии веб-страницы на основе информации.

DOMContentLoaded

이벤트는 객체에서 발생합니다.

따라서 이 이벤트를 다루려면 를 사용해야 합니다.

예시:

위 예시에서 핸들러는 문서가 로드되었을 때 실행됩니다. 따라서 핸들러 아래쪽에 위치한 뿐만 아니라 모든 요소에 접근할 수 있습니다.

그렇지만 이미지가 로드되는 것은 기다리지 않기 때문에 창엔 이미지 사이즈가 0이라고 뜹니다.

처음 이벤트를 접하면 그다지 복잡하지 않은 이벤트라는 생각이 듭니다. «DOM 트리가 완성되면 이벤트가 발생한다»라고 생각하기 때문이죠. 그런데 에는 몇 가지 특이사항이 있습니다.

브라우저는 HTML 문서를 처리하는 도중에 태그를 만나면, DOM 트리 구성을 멈추고 를 실행합니다. 스크립트실행이 끝난 후에야 나머지 HTML 문서를 처리하죠. 에 있는 스크립트가 DOM 조작 관련 로직을 담고 있을 수 있기 때문에 이런 방지책이 만들어 졌습니다. 따라서 이벤트 역시 안에있는 스크립트가 처리고 난 후에 발생합니다.

예시를 통해 이를 살펴봅시다.

예시를 실행하면 «라이브러리 로딩이 끝나고…»가 먼저 보인 후 «DOM이 준비되었습니다!»가 출력되는 것을 확인할 수 있습니다. 스크립트가 모두 실행되고 난 후에야 이벤트가 발생한 것이죠.

DOMContentLoaded를 막지 않는 스크립트

위와 같은 규칙엔 두 가지 예외사항이 있습니다.

  1. 속성이 있는 스크립트는 를 막지 않습니다. 속성에 대해선 곧 학습할 예정입니다.
  2. 로 동적으로 생성되고 웹페이지에 추가된 스크립트는 를 막지 않습니다.

외부 스타일시트는 DOM에 영향을 주지 않기 때문에 는 외부 스타일시트가 로드되기를 기다리지 않습니다.

그런데 한 가지 예외가 있습니다. 스타일시트를 불러오는 태그 바로 다음에 스크립트가 위치하면 이 스크립트는 스타일시트가 로드되기 전까지 실행되지 않습니다.

이런 예외는 스크립트에서 스타일에 영향을 받는 요소의 프로퍼티를 사용할 가능성이 있기 때문에 만들어졌습니다. 위 예시에선 스크립트에서 요소의 좌표 정보를 사용하고 있네요. 스타일이 로드되고, 적용되고 난 다음에야 좌표 정보가 확정되기 때문에 자연스레 이런 제약이 생겼습니다.


는 스크립트가 로드되길 기다립니다. 위의 경우라면 당연히 스타일시트 역시 기다리게 됩니다.

Firefox와 Chrome, Opera의 폼 자동완성(form autofill)은 에서 일어납니다.

페이지에 아이디와 비밀번호를 적는 폼이 있고, 브라우저에 아이디, 비밀번호 정보가 저장되어 있다면 이벤트가 발생할 때 인증 정보가 자동으로 채워집니다. 물론 사용자가 자동 완성을 허용했을 때 그렇겠죠.

따라서 실행해야 할 스크립트가 길어서 이벤트가 지연된다면 자동완성 역시 뒤늦게 처리됩니다. 브라우저 자동 완성 기능을 켜 놓은 사용자라면 특정 사이트에서 자동 완성이 늦게 처리되는 걸 경험 해 보셨을 겁니다. 이런 사이트에선 페이지 로딩이 다 끝난 후에야 아이디나 패스워드 같은 브라우저에 저장한 정보가 폼에 뜨죠. 이런 지연이 발생하는 이유는 이벤트가 실행되는 시점 때문입니다.

Crossorigin policy

There’s a rule: scripts from one site can’t access contents of the other site. So, e.g. a script at can’t read the user’s mailbox at .

Or, to be more precise, one origin (domain/port/protocol triplet) can’t access the content from another one. So even if we have a subdomain, or just another port, these are different origins with no access to each other.

This rule also affects resources from other domains.

If we’re using a script from another domain, and there’s an error in it, we can’t get error details.

For example, let’s take a script that consists of a single (bad) function call:

Now load it from the same site where it’s located:

We can see a good error report, like this:

Now let’s load the same script from another domain:

The report is different, like this:

Details may vary depending on the browser, but the idea is the same: any information about the internals of a script, including error stack traces, is hidden. Exactly because it’s from another domain.

Why do we need error details?

There are many services (and we can build our own) that listen for global errors using , save errors and provide an interface to access and analyze them. That’s great, as we can see real errors, triggered by our users. But if a script comes from another origin, then there’s not much information about errors in it, as we’ve just seen.

Similar cross-origin policy (CORS) is enforced for other types of resources as well.

To allow cross-origin access, the tag needs to have the attribute, plus the remote server must provide special headers.

There are three levels of cross-origin access:

  1. No attribute – access prohibited.
  2. – access allowed if the server responds with the header with or our origin. Browser does not send authorization information and cookies to remote server.
  3. – access allowed if the server sends back the header with our origin and . Browser sends authorization information and cookies to remote server.

Please note:

You can read more about cross-origin access in the chapter Fetch: Cross-Origin Requests. It describes the method for network requests, but the policy is exactly the same.

Such thing as “cookies” is out of our current scope, but you can read about them in the chapter Cookies, document.cookie.

In our case, we didn’t have any crossorigin attribute. So the cross-origin access was prohibited. Let’s add it.

We can choose between (no cookies sent, one server-side header needed) and (sends cookies too, two server-side headers needed).

If we don’t care about cookies, then is the way to go:

Now, assuming that the server provides an header, everything’s fine. We have the full error report.

Методы объекта XMLHttpRequest

open()

Варианты вызова:

  • open( method, URL )
  • open( method, URL, async )
  • open( method, URL, async, userName )
  • open( method, URL, async, userName, password )

Первый параметр method — HTTP-метод. Как правило, используется GET либо POST, хотя доступны и более экзотические, вроде TRACE/DELETE/PUT и т.п.

URL — адрес запроса. Можно использовать не только HTTP/HTTPS, но и другие протоколы, например FTP и FILE://. При этом есть ограничения безопасности, так называемая «same origin policy»: запрос со страницы можно отправлять только на тот домен и порт, с которого она пришла.

Ниже это ограничение и способы обхода будут рассмотрены подробнее.

async = true задает асинхронные запросы, эта тема была поднята выше.

userName, password — данные для HTTP-авторизации.

send()

Отсылает запрос. Аргумент — тело запроса. Например, GET-запроса тела нет, поэтому используется , а для POST-запросов тело содержит параметры запроса.

abort()

Вызов этого метода xmlhttp.abort() обрывает текущий запрос.

Здесь есть одно НО для браузера Internet Explorer. Успешный вызов abort() на самом деле может не обрывать соединение, а оставлять его в подвешенном состоянии на некоторый таймаут (20-30 секунд). Отловить такие повисшие соединения можно через прокси для отладки, например, Fiddler.

У браузера есть лимит: не более 2 одновременных соединений с одним доменом-портом. Т.е, если два соединения уже висят (и отвиснут по таймауту), то третье открыто не будет, пока одно из них не умрет. Надеюсь, Вы с такой проблемой не столкнетесь. Ее можно обойти использованием кросс-доменных XmlHttpRequest.

setRequestHeader(name, value)

Устанавливает заголовок name запроса со значением value. Если заголовок с таким name уже есть — он заменяется.

Например,

xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

Несколько обработчиков

Код из примера выше позволяет навешивать только один обработчик. Для поддержки нескольких — добавим дополнительную обертку:

readyList = []

function onReady(handler) {

	if (!readyList.length) {
		bindReady(function() {
			for(var i=0; i<readyList.length; i++) {
				readyList()
			}
		})
	}

	readyList.push(handler)
}

Функция при первом вызове вешает обработчик , который запускает все функции из списка , а в дальнейшем просто добавляет новый обработчик в этот список.

Следующий пример демонстрирует использование :

<html>
<head>
<script src="bindReady.js"></script>
<script src="onReady.js"></script>

<script>
  onReady(function() {
    var divs = document.body.getElementsByTagName('div')
    alert(divs.innerHTML)
  })
</script>

<link rel="stylesheet" href="my.css" type="text/css">
</head>
<body>

<img src="img5.php"/>

<div>done</div>
</body>
</html>

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

Картинка загружается скриптом, который ждет 5 секунд. Это сделано для демонстрации, что вызывается до полной загрузки.

readyState

문서가 완전히 로드된 후에 핸들러를 설정하면 어떤 일이 발생할까요?

아마도 절대 실행되지 않을 겁니다.

그런데 가끔은 문서가 로드되었는지 아닌지를 판단할 수 없는 경우가 있습니다. DOM이 완전히 구성된 후에 특정 함수를 실행해야 할 때는 DOM 트리 완성 여부를 알 수 없어 조금 난감하죠.

이럴 때 현재 로딩 상태를 알려주는 프로퍼티를 사용할 수 있습니다.

프로퍼티의 값은 세 종류가 있습니다.

  • – 문서를 불러오는 중일 때
  • – 문서가 완전히 불러와졌을 때
  • – 문서를 비롯한 이미지 등의 리소스들도 모두 불러와졌을 때

우리는 의 값을 확인하고 상황에 맞게 핸들러를 설정하거나 코드를 실행하면 됩니다.

예시:

이 외에도 상태가 변경되었을 때 실행되는 이벤트 를 사용하면 상태에 맞게 원하는 작업을 할 수 있습니다. 개발자 도구의 콘솔창을 열고 아래 예시를 실행해 상태 변화를 직접 출력해봅시다.

이렇게 아주 오래전부터 있었던 이벤트라는 대안을 사용해도 문서 로딩 상태를 파악할 수 있습니다. 그런데 이 이벤트는 요즘엔 잘 사용하지 않습니다.

이제 마무리로 지금까지 배운 이벤트들의 순서에 대해 정리해봅시다.

아래 예시엔 이벤트를 로깅하는 과 를 비롯한 여러 이벤트 핸들러가 있습니다.

샌드박스를 열어 직접 예시를 실행해보세요.

실행 결과는 다음과 같습니다.

  1. initial readyState:loading
  2. readyState:interactive
  3. DOMContentLoaded
  4. iframe onload
  5. img onload
  6. readyState:complete
  7. window onload

대괄호 안에 있는 숫자는 실제 해당 로그가 출력되기까지 걸린 시간을 나타냅니다. 같은 숫자는 1 미리 초 오차 범위 내에서 동시에 실행된 이벤트라는 것을 의미합니다.

  • 는 가 실행되기 바로 직전에 가 됩니다. 따라서 와 는 같은 상태를 나타낸다고 볼 수 있습니다.
  • 는 , 를 비롯한 리소스 전부가 로드되었을 때 가 됩니다. 위 예시에서 우리는 의 값이 와 가 실행된 시점과 거의 동일한 시점에 로 바뀌었다는 것을 확인할 수 있습니다. 의 값이 로 바뀐다는 것은 가 실행된다는 것과 동일한 의미입니다. 이 둘의 차이점은 는 다른 핸들러가 전부 실행된 후에야 동작한다는 것에 있습니다.

DOMContentLoaded

由 对象触发。

我们使用 来监听它:

document.addEventListener("DOMContentLoaded", ready);

举个例子

<script>
  function ready() {
    alert('DOM is ready');

    // image is not yet loaded (unless was cached), so the size is 0x0
    alert(`Image size: ${img.offsetWidth}x${img.offsetHeight}`);
  }

  document.addEventListener("DOMContentLoaded", ready);
</script>

<img id="img" src="https://en.js.cx/clipart/train.gif?speed=1&cache=0">

在这个例子中 在 document 加载完成后就被触发,无需等待其他资源的载入,所以 输出的图像的大小为 0。

这么看来 似乎很简单,DOM 树构建完毕之后就运行该事件,不过其实存在一些陷阱。

DOMContentLoaded 和脚本

当浏览器在解析 HTML 页面时遇到了 标签,将无法继续构建DOM树(译注:UI 渲染线程与 JS 引擎是互斥的,当 JS 引擎执行时 UI 线程会被挂起),必须立即执行脚本。所以 有可能在所有脚本执行完毕后触发。

外部脚本(带 的)的加载和解析也会暂停DOM树构建,所以 也会等待外部脚本。

不过有两个例外是带 和 的外部脚本,他们告诉浏览器继续解析而不需要等待脚本的执行,所以用户可以在脚本加载完成前可以看到页面,有较好的用户体验。

和 属性仅仅对外部脚本起作用,并且他们在 不存在时会被自动忽略。

它们都告诉浏览器继续处理页面上的内容,而在后台加载脚本,然后在脚本加载完毕后再执行。所以脚本不会阻塞DOM树的构建和页面的渲染。

(译注:带有 和 的脚本的下载是和 HTML 的下载与解析是并行的,但是 JS 的执行一定是和 UI 线程是互斥的,像下面这张图所示, 在下载完毕后的执行会阻塞HTML的解析)

他们有两处不同:

顺序 带有 的脚本是优先执行先加载完的脚本,他们在页面中的顺序并不影响他们执行的顺序。 带有 的脚本按照他们在页面中出现的顺序依次执行。
带有 的脚本也许会在页面没有完全下载完之前就加载,这种情况会在脚本很小或本缓存,并且页面很大的情况下发生。 带有 的脚本会在页面加载和解析完毕后执行,刚好在 之前执行。

所以 用在那些完全不依赖其他脚本的脚本上。

DOMContentLoaded 与样式表

外部样式表并不会阻塞 DOM 的解析,所以 并不会被它们影响。 不过仍然有一个陷阱:如果在样式后面有一个内联脚本,那么脚本必须等待样式先加载完。

(译注:简单来说,JS 因为有可能会去获取 DOM 的样式,所以 JS 会等待样式表加载完毕,而 JS 是阻塞 DOM 的解析的,所以在有外部样式表的时候,JS 会一直阻塞到外部样式表下载完毕)

<link type="text/css" rel="stylesheet" href="style.css">
<script>
  // 脚本直到样式表加载完毕后才会执行。
  alert(getComputedStyle(document.body).marginTop);
</script>

发生这种事的原因是脚本也许会像上面的例子中所示,去得到一些元素的坐标或者基于样式的属性。所以他们自然要等到样式加载完毕才可以执行。

需要等待脚本的执行,脚本又需要等待样式的加载。

浏览器的自动补全

Firefox, Chrome 和 Opera 会在 执行时自动补全表单。

例如,如果页面有登录的界面,浏览器记住了该页面的用户名和密码,那么在 运行的时候浏览器会试图自动补全表单(如果用户设置允许)。

所以如果 被一个需要长时间执行的脚本阻塞,那么自动补全也会等待。你也许见过某些网站(如果你的浏览器开启了自动补全)—— 浏览器并不会立刻补全登录项,而是等到整个页面加载完毕后才填充。这就是因为在等待 事件。

使用带 和 的脚本的一个好处就是,他们不会阻塞 和浏览器自动补全。(译注:其实执行还是会阻塞的)

2018.02.05: 是会阻塞 的,被 的脚本要在 触发前执行,所以如果HTML很快就加载完了(先不考虑 CSS 阻塞 的情况),而 的脚本还没有加载完,浏览器就会等,等到脚本加载完,执行完,再触发 ,放上一张图(取自在 devTool 下分析自己写的一个页面)

可以看到,HTML很快就加载和解析完毕(CSS 在这里是动态加载的,不阻塞 ),jQuery 和main.js 的脚本是 的, (蓝线)一直在等,等到这两个脚本下载完并执行完,才触发了 。 从这个角度看来, 和把脚本放在 前真是没啥区别,只不过 脚本位 于中,更早被读到,加载更早,而且不担心会被其他的脚本推迟下载开始的时间。

요약

페이지 로드 관련 이벤트는 다음과 같습니다.

  • – DOM 구성이 완료되었을 때 객체에서 실행됩니다. 자바스크립트를 사용해 요소를 조작하는 것은 이 이벤트가 실행된 후입니다.
    • 나 를 사용해 삽입한 스크립트는 DOMContentLoaded가 실행되는 것을 막습니다. 브라우저는 이 스크립트가 실행되길 기다립니다.
    • 는 실행되어도 이미지를 비롯한 기타 리소스들은 여전히 로드 중일 수 있습니다.
  • – 페이지를 비롯한 이미지 등의 자원 전부가 모두 불러와졌을 때 객체에서 실행됩니다. 모든 자원이 로드되는 걸 기다리기에는 시간이 오래 걸릴 수 있으므로 이 이벤트는 잘 사용되지 않습니다.
  • – 사용자가 페이지를 떠나려 할 때 객체에서 발생합니다. 이 이벤트를 취소하려 하면 브라우저는 사용자에게 «we have unsaved changes…»등의 메시지를 띄워 정말 페이지를 떠날 건지 물어봅니다.
  • – 사용자가 최종적으로 사이트를 떠날 때 객체에서 발생합니다. 이벤트 핸들러에선 지연을 유발하는 복잡한 작업이나 사용자와의 상호작용은 할 수 없습니다. 이 제약사항 때문에 이벤트는 아주 드물게 사용됩니다. 하지만 예외적으로 을 사용해 네트워크 요청을 보낼 수 있습니다.
  • – 문서의 현재 상태를 나타내줍니다. 이벤트를 사용하면 변화를 추적할 수 있습니다.
    • – 문서를 불러오는 중일 때
    • – 문서가 완전히 불러와졌을 때. 가 실행되기 바로 직전에 해당 값으로 변경됩니다.
    • – 문서를 비롯한 이미지 등의 리소스들도 모두 불러와졌을 때. 가 실행되기 바로 직전에 해당 값으로 변경됩니다.

浏览器兼容性

The compatibility table on this page is generated from structured data. If you’d like to contribute to the data, please check out https://github.com/mdn/browser-compat-data and send us a pull request.

Update compatibility data on GitHub

Chrome Edge Firefox Internet Explorer Opera Safari Android webview Chrome for Android Firefox for Android Opera for Android Safari on iOS Samsung Internet
event Chrome Full support 1 Edge Full support 12 Firefox Full support 1 IE Full support 9 Opera Full support 9 Safari Full support 3.1 WebView Android Full support 1 Chrome Android Full support 18 Firefox Android Full support 4 Opera Android Full support 10.1 Safari iOS Full support 2 Samsung Internet Android Full support 1.0

С этим читают