Подтвердить что ты не робот

Использовать JavaScript для предотвращения оценки более позднего тега `<script>`?

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

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

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

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

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

  • Решение должно быть полностью в пределах first.js. Все, что требует изменения исходного HTML-страницы или second.js, неприемлемо.
  • Допустимо загрузить second.js через Ajax и выполнить его с помощью eval. Это легкая часть. Жесткая часть предотвращает немедленное выполнение second.js.
  • Предположим, что вы не знаете, что в 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.

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

4b9b3361

Ответ 1

Учитывая ваши конкретные требования, это на самом деле довольно просто и должно работать полностью кросс-браузер. Однако это требует, чтобы 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>

Я удалил setTimeout, потому что это может привести к тому, что func() будет запущен до запуска start.js, в результате чего ошибка "loadSecond не определена". Вместо этого я предоставил якорь, на который нужно щелкнуть, чтобы запустить 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 и открытый тег script second.js.

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

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

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

Ответ 2

В 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)

Ответ 3

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

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

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

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

Ответ 4

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

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

Рабочий пример взлома jQuery, затем загрузка позже (с помощью dependecies).
Протестировано: IE 6+, 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 не является обязательным.

Ответ 5

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

Я не знаю, что это значит, но ответ на ваш вопрос, вероятно, "Нет, это невозможно".

Ответ 6

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