$(документ). уже эквивалентно без jQuery
У меня есть скрипт, который использует $(document).ready
, но он не использует ничего другого из jQuery. Я хотел бы облегчить это, удалив зависимость JQuery.
Как я могу реализовать свой собственный $(document).ready
функциональность без использования JQuery? Я знаю что используя window.onload
не будет таким же, как window.onload
срабатывает после загрузки всех изображений, кадров и т. д.
42 ответа
Существует замена на основе стандартов,DOMContentLoaded
это поддерживается более чем 98% браузеров, хотя не IE8:
document.addEventListener("DOMContentLoaded", function(event) {
//do work
});
Нативная функция jQuery намного сложнее, чем просто window.onload, как показано ниже.
function bindReady(){
if ( readyBound ) return;
readyBound = true;
// Mozilla, Opera and webkit nightlies currently support this event
if ( document.addEventListener ) {
// Use the handy event callback
document.addEventListener( "DOMContentLoaded", function(){
document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
jQuery.ready();
}, false );
// If IE event model is used
} else if ( document.attachEvent ) {
// ensure firing before onload,
// maybe late but safe also for iframes
document.attachEvent("onreadystatechange", function(){
if ( document.readyState === "complete" ) {
document.detachEvent( "onreadystatechange", arguments.callee );
jQuery.ready();
}
});
// If IE and not an iframe
// continually check to see if the document is ready
if ( document.documentElement.doScroll && window == window.top ) (function(){
if ( jQuery.isReady ) return;
try {
// If IE is used, use the trick by Diego Perini
// http://javascript.nwbox.com/IEContentLoaded/
document.documentElement.doScroll("left");
} catch( error ) {
setTimeout( arguments.callee, 0 );
return;
}
// and execute any waiting functions
jQuery.ready();
})();
}
// A fallback to window.onload, that will always work
jQuery.event.add( window, "load", jQuery.ready );
}
Редактировать:
Вот жизнеспособная замена для JQuery готов
function ready(callback){
// in case the document is already rendered
if (document.readyState!='loading') callback();
// modern browsers
else if (document.addEventListener) document.addEventListener('DOMContentLoaded', callback);
// IE <= 8
else document.attachEvent('onreadystatechange', function(){
if (document.readyState=='complete') callback();
});
}
ready(function(){
// do something
});
Взято с https://plainjs.com/javascript/events/running-code-when-the-document-is-ready-15/
Еще одна хорошая функция domReady, взята с /questions/10638306/chistyij-javascript-ekvivalent-jquerys-ready-kak-vyizvat-funktsiyu-kogda-stranitsa-dom-gotova-k-etomu/10638313#10638313
Поскольку принятый ответ был очень далек от завершения, я соединил "готовую" функцию, такую как jQuery.ready()
основанный на источнике jQuery 1.6.2:
var ready = (function(){
var readyList,
DOMContentLoaded,
class2type = {};
class2type["[object Boolean]"] = "boolean";
class2type["[object Number]"] = "number";
class2type["[object String]"] = "string";
class2type["[object Function]"] = "function";
class2type["[object Array]"] = "array";
class2type["[object Date]"] = "date";
class2type["[object RegExp]"] = "regexp";
class2type["[object Object]"] = "object";
var ReadyObj = {
// Is the DOM ready to be used? Set to true once it occurs.
isReady: false,
// A counter to track how many items to wait for before
// the ready event fires. See #6781
readyWait: 1,
// Hold (or release) the ready event
holdReady: function( hold ) {
if ( hold ) {
ReadyObj.readyWait++;
} else {
ReadyObj.ready( true );
}
},
// Handle when the DOM is ready
ready: function( wait ) {
// Either a released hold or an DOMready/load event and not yet ready
if ( (wait === true && !--ReadyObj.readyWait) || (wait !== true && !ReadyObj.isReady) ) {
// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
if ( !document.body ) {
return setTimeout( ReadyObj.ready, 1 );
}
// Remember that the DOM is ready
ReadyObj.isReady = true;
// If a normal DOM Ready event fired, decrement, and wait if need be
if ( wait !== true && --ReadyObj.readyWait > 0 ) {
return;
}
// If there are functions bound, to execute
readyList.resolveWith( document, [ ReadyObj ] );
// Trigger any bound ready events
//if ( ReadyObj.fn.trigger ) {
// ReadyObj( document ).trigger( "ready" ).unbind( "ready" );
//}
}
},
bindReady: function() {
if ( readyList ) {
return;
}
readyList = ReadyObj._Deferred();
// Catch cases where $(document).ready() is called after the
// browser event has already occurred.
if ( document.readyState === "complete" ) {
// Handle it asynchronously to allow scripts the opportunity to delay ready
return setTimeout( ReadyObj.ready, 1 );
}
// Mozilla, Opera and webkit nightlies currently support this event
if ( document.addEventListener ) {
// Use the handy event callback
document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
// A fallback to window.onload, that will always work
window.addEventListener( "load", ReadyObj.ready, false );
// If IE event model is used
} else if ( document.attachEvent ) {
// ensure firing before onload,
// maybe late but safe also for iframes
document.attachEvent( "onreadystatechange", DOMContentLoaded );
// A fallback to window.onload, that will always work
window.attachEvent( "onload", ReadyObj.ready );
// If IE and not a frame
// continually check to see if the document is ready
var toplevel = false;
try {
toplevel = window.frameElement == null;
} catch(e) {}
if ( document.documentElement.doScroll && toplevel ) {
doScrollCheck();
}
}
},
_Deferred: function() {
var // callbacks list
callbacks = [],
// stored [ context , args ]
fired,
// to avoid firing when already doing so
firing,
// flag to know if the deferred has been cancelled
cancelled,
// the deferred itself
deferred = {
// done( f1, f2, ...)
done: function() {
if ( !cancelled ) {
var args = arguments,
i,
length,
elem,
type,
_fired;
if ( fired ) {
_fired = fired;
fired = 0;
}
for ( i = 0, length = args.length; i < length; i++ ) {
elem = args[ i ];
type = ReadyObj.type( elem );
if ( type === "array" ) {
deferred.done.apply( deferred, elem );
} else if ( type === "function" ) {
callbacks.push( elem );
}
}
if ( _fired ) {
deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] );
}
}
return this;
},
// resolve with given context and args
resolveWith: function( context, args ) {
if ( !cancelled && !fired && !firing ) {
// make sure args are available (#8421)
args = args || [];
firing = 1;
try {
while( callbacks[ 0 ] ) {
callbacks.shift().apply( context, args );//shifts a callback, and applies it to document
}
}
finally {
fired = [ context, args ];
firing = 0;
}
}
return this;
},
// resolve with this as context and given arguments
resolve: function() {
deferred.resolveWith( this, arguments );
return this;
},
// Has this deferred been resolved?
isResolved: function() {
return !!( firing || fired );
},
// Cancel
cancel: function() {
cancelled = 1;
callbacks = [];
return this;
}
};
return deferred;
},
type: function( obj ) {
return obj == null ?
String( obj ) :
class2type[ Object.prototype.toString.call(obj) ] || "object";
}
}
// The DOM ready check for Internet Explorer
function doScrollCheck() {
if ( ReadyObj.isReady ) {
return;
}
try {
// If IE is used, use the trick by Diego Perini
// http://javascript.nwbox.com/IEContentLoaded/
document.documentElement.doScroll("left");
} catch(e) {
setTimeout( doScrollCheck, 1 );
return;
}
// and execute any waiting functions
ReadyObj.ready();
}
// Cleanup functions for the document ready method
if ( document.addEventListener ) {
DOMContentLoaded = function() {
document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
ReadyObj.ready();
};
} else if ( document.attachEvent ) {
DOMContentLoaded = function() {
// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
if ( document.readyState === "complete" ) {
document.detachEvent( "onreadystatechange", DOMContentLoaded );
ReadyObj.ready();
}
};
}
function ready( fn ) {
// Attach the listeners
ReadyObj.bindReady();
var type = ReadyObj.type( fn );
// Add the callback
readyList.done( fn );//readyList is result of _Deferred()
}
return ready;
})();
Как пользоваться:
<script>
ready(function(){
alert('It works!');
});
ready(function(){
alert('Also works!');
});
</script>
Я не уверен, насколько функционален этот код, но он отлично работал с моими поверхностными тестами. Это заняло довольно много времени, поэтому я надеюсь, что вы и другие можете извлечь из этого пользу.
PS: предлагаю компилировать.
Или вы можете использовать http://dustindiaz.com/smallest-domready-ever:
function r(f){/in/.test(document.readyState)?setTimeout(r,9,f):f()}
r(function(){/*code to run*/});
или нативная функция, если вам нужно только поддерживать новые браузеры (в отличие от готового jQuery, он не запустится, если вы добавите его после загрузки страницы)
document.addEventListener('DOMContentLoaded',function(){/*fun code to run*/})
Три варианта:
- Если
script
является последним тегом тела, DOM будет готов перед выполнением тега скрипта - Когда DOM готов, "readyState" изменится на "complete"
- Поместите все в обработчик событий 'DOMContentLoaded'
onreadystatechange
document.onreadystatechange = function () {
if (document.readyState == "complete") {
// document is ready. Do your stuff here
}
}
Источник: MDN
DOMContentLoaded
document.addEventListener('DOMContentLoaded', function() {
console.log('document is ready. I can sleep now');
});
Обеспокоены браузерами каменного века: перейдите к исходному коду jQuery и используйте ready
функция. В этом случае вы не анализируете + не выполняете всю библиотеку, вы делаете только очень маленькую ее часть.
Поместите свой <script>/*JavaScript code*/</script>
прямо перед закрытием </body>
тег.
Следует признать, что это может не подходить для всех, поскольку требует изменения файла HTML, а не просто выполнения чего-либо в файле JavaScript. document.ready
, но до сих пор...
Бедное мужское решение:
var checkLoad = function() {
document.readyState !== "complete" ? setTimeout(checkLoad, 11) : alert("loaded!");
};
checkLoad();
Добавил этот, немного лучше, я думаю, собственный объем и не рекурсивный
(function(){
var tId = setInterval(function() {
if (document.readyState == "complete") onComplete()
}, 11);
function onComplete(){
clearInterval(tId);
alert("loaded!");
};
})()
Я использую это:
document.addEventListener("DOMContentLoaded", function(event) {
//Do work
});
Примечание. Вероятно, это работает только в новых браузерах, особенно в следующих: http://caniuse.com/
Это 2020 год и <script>
тег имеет defer
атрибут.
например:
<script src="demo_defer.js" defer></script>
он указывает, что сценарий выполняется, когда страница завершает синтаксический анализ.
версия 2022 года
В 2022 году все, что вам нужно сделать, это добавить атрибут defer к вашему сценарию и загрузить его в голове!
Ссылка: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-defer
<!doctype html>
<html>
<head>
<script src="/script.js" defer></script>
</head>
<body>
</p>In 2022, all you need to do is put the defer attribute on your script, and load it in the head!</p>
</body>
</html>
Действительно, если вы заботитесь только о Internet Explorer 9+, этого кода будет достаточно для замены jQuery.ready
:
document.addEventListener("DOMContentLoaded", callback);
Если вы беспокоитесь об Internet Explorer 6 и некоторых действительно странных и редких браузерах, это будет работать:
domReady: function (callback) {
// Mozilla, Opera and WebKit
if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", callback, false);
// If Internet Explorer, the event model is used
} else if (document.attachEvent) {
document.attachEvent("onreadystatechange", function() {
if (document.readyState === "complete" ) {
callback();
}
});
// A fallback to window.onload, that will always work
} else {
var oldOnload = window.onload;
window.onload = function () {
oldOnload && oldOnload();
callback();
}
}
},
Этот вопрос был задан довольно давно. Для тех, кто только видит этот вопрос, теперь есть сайт под названием "вам может не понадобиться jquery", который разбит - по уровню требуемой поддержки IE - все функции jquery и предоставляют некоторые альтернативные, более мелкие библиотеки.
Скрипт, готовый для IE8, в соответствии с вашими требованиями, может не потребовать jquery
function ready(fn) {
if (document.readyState != 'loading')
fn();
else if (document.addEventListener)
document.addEventListener('DOMContentLoaded', fn);
else
document.attachEvent('onreadystatechange', function() {
if (document.readyState != 'loading')
fn();
});
}
Кросс-браузер (тоже старые браузеры) и простое решение:
var docLoaded = setInterval(function () {
if(document.readyState !== "complete") return;
clearInterval(docLoaded);
/*
Your code goes here i.e. init()
*/
}, 30);
Я недавно использовал это для мобильного сайта. Это упрощенная версия Джона Ресига из "Техники Pro JavaScript". Это зависит от addEvent.
var ready = ( function () {
function ready( f ) {
if( ready.done ) return f();
if( ready.timer ) {
ready.ready.push(f);
} else {
addEvent( window, "load", isDOMReady );
ready.ready = [ f ];
ready.timer = setInterval(isDOMReady, 13);
}
};
function isDOMReady() {
if( ready.done ) return false;
if( document && document.getElementsByTagName && document.getElementById && document.body ) {
clearInterval( ready.timer );
ready.timer = null;
for( var i = 0; i < ready.ready.length; i++ ) {
ready.ready[i]();
}
ready.ready = null;
ready.done = true;
}
}
return ready;
})();
Ответ jQuery был довольно полезен для меня. С небольшим заводом это соответствовало моим потребностям хорошо. Надеюсь, это кому-нибудь еще поможет.
function onReady ( callback ){
var addListener = document.addEventListener || document.attachEvent,
removeListener = document.removeEventListener || document.detachEvent
eventName = document.addEventListener ? "DOMContentLoaded" : "onreadystatechange"
addListener.call(document, eventName, function(){
removeListener( eventName, arguments.callee, false )
callback()
}, false )
}
Вот самый маленький фрагмент кода для проверки готовности DOM, который работает во всех браузерах (даже в IE 8):
r(function(){
alert('DOM Ready!');
});
function r(f){/in/.test(document.readyState)?setTimeout('r('+f+')',9):f()}
Смотрите этот ответ.
Просто добавьте это внизу вашей HTML-страницы...
<script>
Your_Function();
</script>
Потому что документы HTML анализируются сверху вниз.
Самый минимальный и 100% рабочий
Я выбрал ответ от PlainJS, и у меня он отлично работает. Он расширяетсяDOMContentLoaded
так что его можно будет принять во всех браузерах.
Эта функция эквивалентна jQuery $(document).ready()
метод:
document.addEventListener('DOMContentLoaded', function(){
// do something
});
Однако, в отличие от jQuery, этот код будет правильно работать только в современных браузерах (IE > 8) и не будет работать в том случае, если документ уже отображается на момент вставки этого скрипта (например, через Ajax). Поэтому нам нужно немного расширить это:
function run() {
// do something
}
// in case the document is already rendered
if (document.readyState!='loading') run();
// modern browsers
else if (document.addEventListener)
document.addEventListener('DOMContentLoaded', run);
// IE <= 8
else document.attachEvent('onreadystatechange', function(){
if (document.readyState=='complete') run();
});
Это покрывает практически все возможности и является жизнеспособной заменой помощника jQuery.
Самый простой способ с использованием чистого JavaScript. Без jQuery:
document.addEventListener("DOMContentLoaded", function(event) {
// Your code to run since DOM is loaded and ready
});
Стоит поискать в Rock Solid addEvent() и http://www.braksator.com/how-to-make-your-own-jquery.
Вот код на случай, если сайт не работает
function addEvent(obj, type, fn) {
if (obj.addEventListener) {
obj.addEventListener(type, fn, false);
EventCache.add(obj, type, fn);
}
else if (obj.attachEvent) {
obj["e"+type+fn] = fn;
obj[type+fn] = function() { obj["e"+type+fn]( window.event ); }
obj.attachEvent( "on"+type, obj[type+fn] );
EventCache.add(obj, type, fn);
}
else {
obj["on"+type] = obj["e"+type+fn];
}
}
var EventCache = function(){
var listEvents = [];
return {
listEvents : listEvents,
add : function(node, sEventName, fHandler){
listEvents.push(arguments);
},
flush : function(){
var i, item;
for(i = listEvents.length - 1; i >= 0; i = i - 1){
item = listEvents[i];
if(item[0].removeEventListener){
item[0].removeEventListener(item[1], item[2], item[3]);
};
if(item[1].substring(0, 2) != "on"){
item[1] = "on" + item[1];
};
if(item[0].detachEvent){
item[0].detachEvent(item[1], item[2]);
};
item[0][item[1]] = null;
};
}
};
}();
// Usage
addEvent(window, 'unload', EventCache.flush);
addEvent(window, 'load', function(){alert("I'm ready");});
Всегда полезно использовать эквиваленты JavaScript по сравнению с jQuery. Одной из причин является то, что от библиотек зависит меньше библиотек, и они работают намного быстрее, чем эквиваленты jQuery.
Одна из фантастических ссылок на jQuery-эквиваленты - http://youmightnotneedjquery.com/.
Что касается вашего вопроса, я взял приведенный ниже код по ссылке выше:) Единственное предостережение: он работает только с Internet Explorer 9 и более поздними версиями.
function ready(fn) {
if (document.readyState != 'loading') {
fn();
}
else {
document.addEventListener('DOMContentLoaded', fn);
}
}
Этот кросс-браузерный код вызовет функцию, как только DOM будет готов:
var domReady=function(func){
var scriptText='('+func+')();';
var scriptElement=document.createElement('script');
scriptElement.innerText=scriptText;
document.body.appendChild(scriptElement);
};
Вот как это работает:
- Первая строка
domReady
вызываетtoString
метод функции, чтобы получить строковое представление передаваемой функции и обернуть его в выражение, которое немедленно вызывает функцию. - Остальная часть
domReady
создает элемент сценария с выражением и добавляет его кbody
документа. - Браузер запускает теги скрипта, добавленные к
body
после того, как DOM готов.
Например, если вы делаете это: domReady(function(){alert();});
следующее будет добавлено к body
элемент:
<script>(function (){alert();})();</script>
Обратите внимание, что это работает только для пользовательских функций. Следующее не будет работать: domReady(alert);
В настоящее время вы должны использовать модули. Поместите свой код в функцию модуля по умолчанию и импортируйте функцию в элемент скрипта.
client.js
:
export default function ()
{
alert ("test");
}
index.html
:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<script type="module">
import main from './client.js';
main ();
</script>
</body>
</html>
Я просто использую:
setTimeout(function(){
//reference/manipulate DOM here
});
И в отличие от document.addEventListener("DOMContentLoaded" //etc
как и в самом верхнем ответе, он работает еще в IE9 - http://caniuse.com/ указывает только на IE11.
Например, перейдите на https://netrenderer.com/index.php, выберите Internet Explorer 9 из раскрывающегося списка, введите https://dexygen.github.io/blog/oct-2017/jekyll/jekyll-categories/liquid-templates/2017/10/22/how-jekyll-builds-site-categories.html и нажмите "Render", и вы увидите что-то похожее на скриншот внизу этого поста.
Посмотрите следующий код Javascript, который я использую в заголовке, чтобы манипулировать стилем темы "хакер" Jekyll по моему вкусу - в частности, вы можете сослаться на if (location.pathname !== rootPath)
блок, чтобы увидеть, как я вставляю Home
а также Blog Home
ссылки, которые отображаются IE9 на сайте NetRenderer.
Интересно я наткнулся на это setTimeout
Решение 2009 года: проверяется ли готовность DOM к перерасходу? что, вероятно, можно было бы сформулировать немного лучше, как я имел в виду, используя более сложные подходы различных структур.
setTimeout(function() {//delay execution until after dom is parsed
var containerEls = document.getElementsByClassName('container');
var headingEl = containerEls[0].getElementsByTagName('h1')[0];
var headerEl = document.getElementsByTagName('header')[0];
var downloadsSectionEl = document.getElementById('downloads');
var rootPath = "/";
var blogRootPath = "/blog/";
containerEls[0].style.maxWidth = '800px';
containerEls[1].style.maxWidth = '800px';
headingEl.style.margin = '0';
headerEl.style.marginBottom = '7px';
downloadsSectionEl.style.margin = '0';
if (location.pathname !== rootPath) {
downloadsSectionEl.appendChild(generateNavLink('Home', rootPath));
if (location.pathname !== blogRootPath) {
downloadsSectionEl.appendChild(document.createTextNode(' | '));
downloadsSectionEl.appendChild(generateNavLink('Blog Home', blogRootPath));
}
}
function generateNavLink(linkText, hrefPath) {
var navLink = document.createElement('a');
var linkTextNode = document.createTextNode(linkText);
navLink.setAttribute('href', hrefPath);
navLink.appendChild(linkTextNode);
return navLink;
}
});
Мы нашли простую кросс-браузерную реализацию, которая может помочь в большинстве простых случаев с минимальной реализацией:
window.onReady = function onReady(fn){
document.body ? fn() : setTimeout(function(){ onReady(fn);},50);
};
Как насчет этого решения?
// other onload attached earlier
window.onload=function() {
alert('test');
};
tmpPreviousFunction=window.onload ? window.onload : null;
// our onload function
window.onload=function() {
alert('another message');
// execute previous one
if (tmpPreviousFunction) tmpPreviousFunction();
};
Сравнение
Здесь (во фрагменте ниже) сравниваются выбранные доступные "встроенные" методы браузера и последовательность их выполнения. Замечания
- то
document.onload
(X) не поддерживается ни одним современным браузером (событие никогда не запускается) - если вы используете
<body onload="bodyOnLoad()">
(F) и одновременноwindow.onload
(E) тогда будет выполнен только первый (потому что он перекрывает второй) - обработчик событий, указанный в
<body onload="...">
(F) обернут дополнительнымonload
функция document.onreadystatechange
(D) не отменятьdocument .addEventListener('readystatechange'...)
(C) вероятно cecasueonXYZevent-like
методы независимы, чемaddEventListener
очереди (что позволяет добавлять несколько слушателей). Вероятно, между выполнением этих двух обработчиков ничего не происходит.- все сценарии записывают свою метку времени в консоли, но сценарии, которые также имеют доступ к
div
пишите их временные метки также в теле (щелкните ссылку "Полная страница" после выполнения скрипта, чтобы увидеть ее). - решения
readystatechange
(C,D) выполняются браузером несколько раз, но для разных состояний документа: - loading - документ загружается (в сниппете не запускается)
- интерактивный - документ анализируется, запускается перед
DOMContentLoaded
- Complete - документ и ресурсы загружаются, запускаются перед
body/window onload
<html>
<head>
<script>
// solution A
console.log(`[timestamp: ${Date.now()}] A: Head script`) ;
// solution B
document.addEventListener("DOMContentLoaded", () => {
print(`[timestamp: ${Date.now()}] B: DOMContentLoaded`);
});
// solution C
document.addEventListener('readystatechange', () => {
print(`[timestamp: ${Date.now()}] C: ReadyState: ${document.readyState}`);
});
// solution D
document.onreadystatechange = s=> {print(`[timestamp: ${Date.now()}] D: document.onreadystatechange ReadyState: ${document.readyState}`)};
// solution E (never executed)
window.onload = () => {
print(`E: <body onload="..."> override this handler`);
};
// solution F
function bodyOnLoad() {
print(`[timestamp: ${Date.now()}] F: <body onload='...'>`);
infoAboutOnLoad(); // additional info
}
// solution X
document.onload = () => {print(`document.onload is never fired`)};
// HELPERS
function print(txt) {
console.log(txt);
if(mydiv) mydiv.innerHTML += txt.replace('<','<').replace('>','>') + '<br>';
}
function infoAboutOnLoad() {
console.log("window.onload (after override):", (''+document.body.onload).replace(/\s+/g,' '));
console.log(`body.onload==window.onload --> ${document.body.onload==window.onload}`);
}
console.log("window.onload (before override):", (''+document.body.onload).replace(/\s+/g,' '));
</script>
</head>
<body onload="bodyOnLoad()">
<div id="mydiv"></div>
<!-- this script must te at the bottom of <body> -->
<script>
// solution G
print(`[timestamp: ${Date.now()}] G: <body> bottom script`);
</script>
</body>
</html>
Вот то, что я использую, это быстро и охватывает все основы, я думаю; работает для всего, кроме IE<9.
(() => { function fn() {
// "On document ready" commands:
console.log(document.readyState);
};
if (document.readyState != 'loading') {fn()}
else {document.addEventListener('DOMContentLoaded', fn)}
})();
Это похоже на все случаи:
- срабатывает немедленно, если DOM уже готов (если DOM не "загружается", а "интерактивен" или "завершен")
- если DOM все еще загружается, он устанавливает прослушиватель событий, когда DOM доступен (интерактивно).
Событие DOMContentLoaded доступно в IE9 и во всем остальном, поэтому я лично считаю, что это нормально, использовать это. Перепишите объявление функции стрелки в обычную анонимную функцию, если вы не переносите свой код из ES2015 в ES5.
Если вы хотите дождаться загрузки всех ресурсов, отображения всех изображений и т. Д., Используйте вместо этого window.onload.
Если вам не нужно поддерживать очень старые браузеры, вот способ сделать это, даже если ваш внешний скрипт загружен с атрибутом async:
HTMLDocument.prototype.ready = new Promise(function(resolve) {
if(document.readyState != "loading")
resolve();
else
document.addEventListener("DOMContentLoaded", function() {
resolve();
});
});
document.ready.then(function() {
console.log("document.ready");
});
Представленные здесь решения setTimeout/setInterval будут работать только при определенных обстоятельствах.
Проблема проявляется особенно в старых версиях Internet Explorer до 8.
Переменные, влияющие на успех этих решений setTimeout/setInterval:
1) dynamic or static HTML
2) cached or non cached requests
3) size of the complete HTML document
4) chunked or non chunked transfer encoding
оригинальный (нативный Javascript) код для решения этой конкретной проблемы находится здесь:
https://github.com/dperini/ContentLoaded
http://javascript.nwbox.com/ContentLoaded (test)
это код, из которого команда jQuery создала свою реализацию.
Для IE9+:
function ready(fn) {
if (document.readyState != 'loading'){
fn();
} else {
document.addEventListener('DOMContentLoaded', fn);
}
}
Работает во всех известных браузерах (проверено через BrowserStack). IE6+, Safari 1+, Chrome 1+, Opera и т. Д. ИспользуетDOMContentLoaded
, с откатами на document.documentElement.doScroll()
а также window.onload
.
/*! https://github.com/Kithraya/DOMContentLoaded v1.2.6 | MIT License */
DOMContentLoaded.version = "1.2.6";
function DOMContentLoaded() { "use strict";
var ael = 'addEventListener', rel = 'removeEventListener', aev = 'attachEvent', dev = 'detachEvent';
var alreadyRun = false, // for use in the idempotent function ready()
funcs = arguments;
// old versions of JS return '[object Object]' for null.
function type(obj) { return (obj === null) ? 'null' : Object.prototype.toString.call(obj).slice(8,-1).toLowerCase() }
function microtime() { return + new Date() }
/* document.readyState === 'complete' reports correctly in every browser I have tested, including IE.
But IE6 to 10 don't return the correct readyState values as per the spec:
readyState is sometimes 'interactive', even when the DOM isn't accessible in IE6/7 so checking for the onreadystatechange event like jQuery does is not optimal
readyState is complete at basically the same time as 'window.onload' (they're functionally equivalent, within a few tenths of a second)
Accessing undefined properties of a defined object (document) will not throw an error (in case readyState is undefined).
*/
// Check for IE < 11 via conditional compilation
/// values: 5?: IE5, 5.5?: IE5.5, 5.6/5.7: IE6/7, 5.8: IE8, 9: IE9, 10: IE10, 11*: (IE11 older doc mode), undefined: IE11 / NOT IE
var jscript_version = Number( new Function("/*@cc_on return @_jscript_version; @*\/")() ) || NaN;
// check if the DOM has already loaded
if (document.readyState === 'complete') { ready(null); return; } // here we send null as the readyTime, since we don't know when the DOM became ready.
if (jscript_version < 9) { doIEScrollCheck(); return; } // For IE<9 poll document.documentElement.doScroll(), no further actions are needed.
/*
Chrome, Edge, Firefox, IE9+, Opera 9+, Safari 3.1+, Android Webview, Chrome for Android, Edge Mobile,
Firefox for Android 4+, Opera for Android, iOS Safari, Samsung Internet, etc, support addEventListener
And IE9+ supports 'DOMContentLoaded'
*/
if (document[ael]) {
document[ael]("DOMContentLoaded", ready, false);
window[ael]("load", ready, false); // fallback to the load event in case addEventListener is supported, but not DOMContentLoaded
} else
if (aev in window) { window[aev]('onload', ready);
/* Old Opera has a default of window.attachEvent being falsy, so we use the in operator instead
https://dev.opera.com/blog/window-event-attachevent-detachevent-script-onreadystatechange/
Honestly if somebody is using a browser so outdated AND obscure (like Opera 7 where neither addEventListener
nor "DOMContLoaded" is supported, they deserve to wait for the full page).
I CBA testing whether readyState === 'interactive' is truly interactive in browsers designed in 2003. I just assume it isn't (like in IE6-8).
*/
} else { // fallback to queue window.onload that will always work
addOnload(ready);
}
// This function allows us to preserve any original window.onload handlers (in super old browsers where this is even necessary),
// while keeping the option to chain onloads, and dequeue them.
function addOnload(fn) { var prev = window.onload; // old window.onload, which could be set by this function, or elsewhere
// we add a function queue list to allow for dequeueing
// addOnload.queue is the queue of functions that we will run when when the DOM is ready
if ( type( addOnload.queue ) !== 'array') { addOnload.queue = [];
if ( type(prev) === 'function') { addOnload.queue.push( prev ); } // add the previously defined event handler
}
if (typeof fn === 'function') { addOnload.queue.push(fn) }
window.onload = function() { // iterate through the queued functions
for (var i = 0; i < addOnload.queue.length; i++) { addOnload.queue[i]() }
};
}
// remove a queued window.onload function from the chain (simplified);
function dequeueOnload(fn) { var q = addOnload.queue, i = 0;
// sort through the queued functions in addOnload.queue until we find `fn`
if (type( q ) === 'array') { // if found, remove from the queue
for (; i < q.length; i++) { ;;(fn === q[i]) ? q.splice(i, 1) : 0; } // void( (fn === q[i]) ? q.splice(i, 1) : 0 )
}
}
function ready(ev) { // idempotent event handler function
if (alreadyRun) {return} alreadyRun = true;
// this time is when the DOM has loaded (or if all else fails, when it was actually possible to inference the DOM has loaded via a 'load' event)
// perhaps this should be `null` if we have to inference readyTime via a 'load' event, but this functionality is better.
var readyTime = microtime();
detach(); // detach any event handlers
// run the functions
for (var i=0; i < funcs.length; i++) { var func = funcs[i];
if (type(func) === 'function') {
func.call(document, { 'readyTime': (ev === null ? null : readyTime), 'funcExecuteTime': microtime() }, func);
// jquery calls 'ready' with `this` being set to document, so we'll do the same.
}
}
}
function detach() {
if (document[rel]) {
document[rel]("DOMContentLoaded", ready); window[rel]("load", ready);
} else
if (dev in window) { window[dev]("onload", ready); }
else {
dequeueOnload(ready);
}
}
function doIEScrollCheck() { // for use in IE < 9 only.
if ( window.frameElement ) {
// we're in an <iframe> or similar
// the document.documentElemeent.doScroll technique does not work if we're not at the top-level (parent document)
try { window.attachEvent("onload", ready); } catch (e) { } // attach to onload if were in an <iframe> in IE as there's no way to tell otherwise
return;
}
try {
document.documentElement.doScroll('left'); // when this statement no longer throws, the DOM is accessible in old IE
} catch(error) {
setTimeout(function() {
(document.readyState === 'complete') ? ready() : doIEScrollCheck();
}, 50);
return;
}
ready();
}
}
Применение:
<script>
DOMContentLoaded(function(e) { console.log(e) });
</script>