Использовать JavaScript, чтобы предотвратить оценку более позднего тега `<script>`? - PullRequest
28 голосов
/ 18 января 2011

Это немного странный случай, но у меня есть свои причины:

Я бы хотел написать

<script type="text/javascript" src="first.js"></script>
<script type="text/javascript" src="second.js"></script>

в моей разметке и, используя код в first.js, предотвращает или задерживает выполнение second.js. Это возможно в любом браузере? Что, если содержимое first.js встроено? (Если это поможет, предположим, что второй тег сценария имеет атрибут id.)

Поскольку я получил пару ответов, в которых не было того, к чему я клоню, я должен уточнить:

  1. Решение должно быть полностью в пределах first.js. Все, что требует изменения исходного HTML-кода страницы или second.js, недопустимо.
  2. Это приемлемо для загрузки second.js через Ajax и выполнения его с помощью eval. Это легкая часть. Трудная часть препятствует немедленному исполнению second.js.
  3. Предположим, что вы не знаете , что в second.js. Таким образом, вы не можете просто заменить каждую глобальную функцию, вызываемую second.js, функцией no-op. (Кроме того, это почти наверняка приведет к ошибкам.)

Если вам известно о решении, которое работает в некоторых браузерах, но не в других, я бы хотел услышать его.

Пример : Чтобы сделать это немного более конкретным, допустим, что код

<script type="text/javascript">
  function func() {
    window.meaningOfLife = 42;
    window.loadSecond();
  };
  setTimeout(func, 10);
</script>

предшествует двум script включениям, а second.js содержит строку

if (window.meaningOfLife !== 42) {throw new Error();}

first.js должен быть в состоянии предотвратить эту ошибку, задерживая выполнение second.js до запуска window.loadSecond. (Предположим, что реализация window.loadSecond также находится в first.js.) не разрешено касаться window.meaningOfLife.

Обновление : ответ Алохчи отвечает этим требованиям, но только при условии, что второй тег сценария будет сразу после первого, с пустым пространством между ними Если бы кто-то мог продлить свой хак, чтобы избежать этого требования, не представляя других нежелательных последствий, это было бы удивительно ...

Ответы [ 7 ]

30 голосов
/ 22 января 2011

Учитывая ваши конкретные требования, это на самом деле довольно просто и должно работать полностью кросс-браузерно.Однако требуется, чтобы first.js непосредственно предшествовал second.js без каких-либо различий между ними, кроме пробелов.

Сначала давайте предположим, что HTML-код выглядит следующим образом:

<!DOCTYPE html>
<html>
  <head>
    <title>Test Case</title>
    <meta charset="UTF-8" />
    <script type="text/javascript">
      function func() {
        window.meaningOfLife = 42;
        window.loadSecond();
      };
    </script>
    <script type="text/javascript" src="first.js"></script>
    <script type="text/javascript" src="second.js"></script>
  </head>
  <body>
    <p>Lorem ipsum dolor sit amet ...</p>
    <a href="javascript:func()">Run Func()</a>
  </body>
</html>

I 'мы удалили setTimeout, потому что это может привести к запуску func () до запуска start.js, что приведет к ошибке «loadSecond is notfined».Вместо этого я предоставил ссылку для запуска func ().

Во-вторых, давайте предположим, что second.js выглядит следующим образом:

document.body.appendChild(document.createTextNode("second.js has run. "));
if (window.meaningOfLife !== 42) {throw new Error();}

Здесь я только что добавил строку, чтобы добавить некоторый текст в тело документа, чтобы было прощечтобы увидеть, когда на самом деле выполняется second.js.

Тогда решение для first.js таково:

function loadSecond()
{
    var runSecond = document.createElement("script");
    runSecond.setAttribute("src", "second.js"); 
    document.body.appendChild(runSecond);
}

document.write("<script type='application/x-suppress'>");

Функция loadSecond предназначена для запуска second.js при запуске func ()..

Ключом к решению является строка document.write.Он вставит в HTML <script type='application/x-suppress'> между тегом close script в файле first.js и тегом open script в файле second.js.

Анализатор увидит это и запустит новый элемент script.Поскольку атрибут type имеет значение, которое не распознается браузером как JavaScript, он не будет пытаться запустить его содержимое.(Таким образом, существует неограниченное количество возможных значений атрибутов типа, которые вы можете использовать здесь, но вы должны включить атрибут типа, так как при его отсутствии браузер будет предполагать, что содержимое сценария является JavaScript.)

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

Вы можете увидеть рабочую версию на http://www.alohci.net/static/jsprevent/jsprevent.htm

6 голосов
/ 18 января 2011

В first.js, установите var shouldILoad = true

Затем загрузите second.js таким образом:

<script>
    if (shouldILoad) {
        (function() {
            var myscript = document.createElement('script');
            myscript.type = 'text/javascript';
            myscript.src = ('second.js');
            var s = document.getElementById('myscript');
            s.parentNode.insertBefore(myscript, s);
        })();
    }
</script>

(где «myscript» - это идентификатор некоторого элемента, перед которым вы хотите вставить новый элемент Script)

4 голосов
/ 20 января 2011

Насколько я знаю, ты не можешь.Если разметка выглядит как

<script type="text/javascript" src="first.js"></script>
<script type="text/javascript" src="second.js"></script>

, вы не можете получить доступ ко второму элементу сценария из first.js, так как он не был добавлен в DOM в момент запуска первого сценария (даже еслиВы назначаете id для второго элемента).Неважно, встроен ли код second.js в строку или во внешний файл.

Единственное, чего я не понимаю, это ваш второй момент.Сначала вы говорите, что не можете контролировать разметку документа, но затем утверждаете, что можно динамически загружать second.js (используя AJAX).

2 голосов
/ 21 января 2011

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

Предотвратить выполнение second.js довольно просто: break это!Предполагая, что second.js пытается вызвать document.getElementById, например:

Рабочий пример взлома jQuery с последующей загрузкой (с зависимостями).
Проверено на: IE6+, FF 3.6+, Chrome

конец first.js

var execute;

// saving our position
var scripts = document.getElementsByTagName("script");
var i = scripts.length;

// breaking getElementById
var byId = document.getElementById;
document.getElementById = null;

var interval = setInterval(function () {
    if (i != scripts.length) {
      var second = scripts[i];
      // stop polling
      clearInterval(interval);
      // fix getElementById
      document.getElementById = byId;
      // set the delayed callback
      execute = function (onload) {
        var script = document.createElement("script");
        script.src = second.src;
        script.onload = script.onreadystatechange = onload;
        document.getElementsByTagName("head")[0].appendChild(script);
      };
    }
}, 100);

в любое время, когда вы захотите выполнить second.js

execute(function(){
   // second.js dependant code goes here...
});

Примечание: параметр onload для execute является необязательным.

0 голосов
/ 21 августа 2018

После статья описывает способ блокирования загрузки / выполнения скриптов (сторонних) из вашего скрипта (включая теги в заголовке страницы и динамически добавленные теги).

Для обработки существующих тегов на странице:

  1. Используйте MutationObserver для наблюдения за вставкой элементов скрипта и внутри обратного вызова MutationObserver сделайте резервную копию скрипта (чтобы включить / вставить его позже) и измените тип скрипта на "javascript/ заблокирован "(не работает в IE, Edge, Firefox).Также вы можете обработать устаревшее (но работающее) событие beforescriptexecute в Firefox, чтобы предотвратить загрузку скрипта.
  2. Установить вручную тип "javascript / заблокирован" (работает везде, включая IE и Edge), например <script type="text/javascript" type="javascript/blocked" src="second.js"></script>затем сделайте резервную копию в обратном вызове MutationObserver и добавьте его позже.

Для обработки динамически добавленных тегов

  1. Обезьяна-патч document.createElement .
  2. Переопределить дескрипторы src и type в прототипе HTMLScriptElement.

Также эти ребята предоставляют библиотеку Yett с подходом, описанным в статье..

0 голосов
/ 21 января 2011

Чтобы посмотреть, возможно ли это, я first.js отправил синхронный XHR в файл PHP и удалил файл PHP second.js.Когда readyState достиг 4, у меня было что-то, предупреждающее JS, чтобы остановить поток.Затем я пошел и проверил сервер ... Да, second.js был удален.И все же, это не сработает.Я бы закрыл окно с предупреждением, и код, который был в second.js, все равно был бы выполнен, несмотря на тот факт, что файл исчез.это значит, но ответ на ваш вопрос, вероятно, «Нет, это невозможно».

0 голосов
/ 18 января 2011

вы можете использовать setTimeout (), чтобы задержать выполнение некоторого кода

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...