Black_Star, у меня есть полностью рабочее решение:
Где-то в инете нашёл скрипт jQl и немного его поменял (уже не помню где и что именно менял):
var jQl={q:[],dq:[],gs:[],ready:function(a){"function"==typeof a&&jQl.q.push(a);return jQl},getScript:function(a,c){jQl.gs.push([a,c])},unq:function(){for(var a=0;a<jQl.q.length;a++) $(document).ready(jQl.q[a]);jQl.q=[]},ungs:function(){for(var a=0;a<jQl.gs.length;a++)jQuery.getScript(jQl.gs[a][0],jQl.gs[a][1]);jQl.gs=[]},bId:null,
boot:function(a){"undefined"==typeof window.jQuery.fn?jQl.bId||(jQl.bId=setInterval(function(){
jQl.boot(a)},25)):(jQl.bId&&clearInterval(jQl.bId),jQl.bId=0,jQl.unqjQdep(),jQl.ungs(),jQuery(jQl.unq()), "function"==typeof a&&a())
},booted:function(){return 0===jQl.bId},loadjQ:function(a,c){setTimeout(function(){var b=document.createElement("script");b.src=a;document.getElementsByTagName("head")[0].appendChild(b)},1);jQl.boot(c)},loadjQdep:function(a){jQl.loadxhr(a,jQl.qdep)},qdep:function(a){a&&("undefined"!==typeof window.jQuery.fn&&!jQl.dq.length?jQl.rs(a):jQl.dq.push(a))},unqjQdep:function(){if("undefined"==typeof window.jQuery.fn)setTimeout(jQl.unqjQdep,50);else{for(var a=0;a<jQl.dq.length;a++)jQl.rs(jQl.dq[a]); jQl.dq=[]}},rs:function(a){var c=document.createElement("script");document.getElementsByTagName("head")[0].appendChild(c);c.text=a},loadxhr:function(a,c){var b;b=jQl.getxo();b.onreadystatechange=function(){4!=b.readyState||200!=b.status||c(b.responseText,a)};try{b.open("GET",a,!0),b.send("")}catch(d){}},getxo:function(){var a=!1;try{a=new XMLHttpRequest}catch(c){for(var b=["MSXML2.XMLHTTP.5.0","MSXML2.XMLHTTP.4.0","MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP","Microsoft.XMLHTTP"],d=0;d<b.length;++d){try{a= new ActiveXObject(b[d])}catch(e){continue}break}}finally{return a}}};if("undefined"==typeof window.jQuery){var $=jQl.ready,jQuery=$;$.getScript=jQl.getScript};
Данный код создаёт 3 массива (для $(document).ready(), для $.getScript и для собственной ф-ии jQl.loadjQdep() - она асинхронно загружает внешний скрипт сразу, не дожидаясь когда загрузится jQuery (но выпонит его лишь после загрузки jQuery)).
Вставляем этот код в head на каждой странице. Затем закидываем код jQuery и всё что от него зависит в один js файл, в самом конце кода вставляем строку:
jQl.boot();
которая инициирует выполнение кода из массивов (фактически, просто вызывает эти же ф-ии в том же порядке, а поскольку они на этот момент уже переопределены кодом jQuery, то будут выполнены им, за исключением 3-го массива).
Получившийся единый js-файл подгружаем асинхронно (обязательно _после_ кода jQl):
<script async type="text/javascript" src="/js/combined.min.js?v1"></script>
Соответственно, весь js-код на странице, который зависит от скриптов из combined.min.js надо обернуть в $(document).ready(function(){ ... }); либо в короткий вариант того же самого: $(function(){ ... });
Если же внешний файл, зависимый от jQuery нельзя поместить в общий js-файл (например он большой, но нужен не на всех страницах), то загружаем его через jQl.loadjQdep('/path/to/file.js');
Но для каждого такого скрипта надо соблюсти очерёдность исполнения inline-кода - для этого используем deffered:
1) В конце общего js-файла (но перед строкой jQl.boot();) определяем переменную-индикатор, например для скрипта lazyload:
$lazyload = $.Deferred();
2) В конце файла отдельного скрипта ставим резолв этой переменной:
$lazyload.resolve();
3) Весь inline-код на странице, зависимый от этого скрипта, помещаем в блок, который гарантирует что он выполнится только после резолва:
$(function(){ $lazyload.done(function(){ ... }); });
P.S.: в процессе эксплуатации выяснилось, что в сафари событие DOMContentLoaded не запускает ф-ии, которые начали слушать это событие после того как оно уже сработало, поэтому иногда весь jQuery-зависимый inline-код не срабатывает (если общий файл с кодом jQuery загрузился после основной страницы). Для обхода этого добавили небольшой хак, и общее решение теперь выглядит так:
В <head> каждой страницы такой код:
<input id="workaround-DOMContentLoaded-event" type="hidden" value="0" />
<script>
var jQl={q:[],dq:[],gs:[],ready:function(a){"function"==typeof a&&jQl.q.push(a);return jQl},getScript:function(a,c){jQl.gs.push([a,c])},unq:function(){for(var a=0;a<jQl.q.length;a++) $(document).ready(jQl.q[a]);jQl.q=[]},ungs:function(){for(var a=0;a<jQl.gs.length;a++)jQuery.getScript(jQl.gs[a][0],jQl.gs[a][1]);jQl.gs=[]},bId:null,
boot:function(a){"undefined"==typeof window.jQuery.fn?jQl.bId||(jQl.bId=setInterval(function(){
jQl.boot(a)},25)):(jQl.bId&&clearInterval(jQl.bId),jQl.bId=0,jQl.unqjQdep(),jQl.ungs(),jQuery(jQl.unq()), "function"==typeof a&&a())
},booted:function(){return 0===jQl.bId},loadjQ:function(a,c){setTimeout(function(){var b=document.createElement("script");b.src=a;document.getElementsByTagName("head")[0].appendChild(b)},1);jQl.boot(c)},loadjQdep:function(a){jQl.loadxhr(a,jQl.qdep)},qdep:function(a){a&&("undefined"!==typeof window.jQuery.fn&&!jQl.dq.length?jQl.rs(a):jQl.dq.push(a))},unqjQdep:function(){if("undefined"==typeof window.jQuery.fn)setTimeout(jQl.unqjQdep,50);else{for(var a=0;a<jQl.dq.length;a++)jQl.rs(jQl.dq[a]); jQl.dq=[]}},rs:function(a){var c=document.createElement("script");document.getElementsByTagName("head")[0].appendChild(c);c.text=a},loadxhr:function(a,c){var b;b=jQl.getxo();b.onreadystatechange=function(){4!=b.readyState||200!=b.status||c(b.responseText,a)};try{b.open("GET",a,!0),b.send("")}catch(d){}},getxo:function(){var a=!1;try{a=new XMLHttpRequest}catch(c){for(var b=["MSXML2.XMLHTTP.5.0","MSXML2.XMLHTTP.4.0","MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP","Microsoft.XMLHTTP"],d=0;d<b.length;++d){try{a= new ActiveXObject(b[d])}catch(e){continue}break}}finally{return a}}};if("undefined"==typeof window.jQuery){var $=jQl.ready,jQuery=$;$.getScript=jQl.getScript};
document.addEventListener('DOMContentLoaded', function(){
var i = document.getElementById('workaround-DOMContentLoaded-event');
i.value = 1;
i.click();
});
</script>
<script async type="text/javascript" src="/js/combined.min.js?v1"></script>
А в конце файла combined.min.js такой код:
jQl.boot();
$('#workaround-DOMContentLoaded-event').on('click', function(){
if( this.value != 1 ) return;
jQuery.ready();
$(this).remove();
}).trigger('click');