Как загружать сторонний тег javascript асинхронно, у которого есть document.write

Мы выделяем кусок тегов javascript, таких как <script src="http://ours.com/some.js"></script>, которые владельцы сайтов помещают на свой сайт, например http://example.com, и в этом теге javascript мы хотим динамически включают сторонние js, например, которые могут иметь document.write в нем, но, конечно, если мы попытаемся включить его с помощью обычного метода,

var script_tag = document.createElement('script');
script_tag.type = 'text/javascript';
script_tag.src="http://third-party.com/some.js";
document.getElementById('target').appendChild(script_tag);

мы получаем предупреждение от браузера,

Предупреждение: вызов document.write() из асинхронно загруженного внешний script был проигнорирован.

Как нам обойти это? Имейте в виду, что мы действительно не контролируем сторонние скрипты, поэтому мы не можем изменить логику в нем. Мы ищем какое-то решение, которое может работать во всех браузерах.

Ответ 1

Проблема с загрузкой script в уже загруженном документе (вместо того, чтобы браузер игнорировал document.write()), заключается в том, что вы удалите весь существующий HTML. См. этот пример, чтобы вы могли точно понять, что происходит, или для более подробной информации смотрите для метода document.write().

Хотя я знаю, что это может быть не то, что вы ожидаете получить в качестве ответа, я считаю, что вам не повезло, поскольку переписывание script не является вариантом.

Этот вопрос представляет собой аналогичный вопрос с похожими ответами.

Ответ 2

Как насчет загрузки script путем добавления элемента script, вы загружаете содержимое URL-адреса script с помощью вызова AJAX, а затем используете eval() для запуска его в глобальной области? Вот пример, и я проверил его, чтобы убедиться, что он работает:

<!DOCTYPE html>
<html>
<head>
<script>

var xmlhttp;

if (window.XMLHttpRequest) {
  xmlhttp = new XMLHttpRequest();
}else{
  xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}

xmlhttp.onreadystatechange = function() {
  if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
    window.eval(xmlhttp.responseText); //Indirect call to eval to execute in global scope (http://perfectionkills.com/global-eval-what-are-the-options/)
  }
}

xmlhttp.open("GET", "https://third-party.com/test.js", false); //This is synchronous so that any document.write calls don't overwrite the entire page when they get called after the document is finished rendering. For all intents and purposes, this just loads the script like any other script, synchronously.

xmlhttp.send();

</script>
</head>
<body>

<div><h2>Hello World</h2></div>

</body>
</html>

И вот содержимое, которое у меня было в файле test.js:

document.write("This is a test...");
alert("...This is a test alert...");
console.log("...And a console message.");

Я сделал запрос AJAX для script синхронным, чтобы он загружался точно так же, как если бы он был встроенным тегом script. Если вы запускаете его асинхронно, а script использует document.write после того, как страница была полностью отображена, она очищает DOM, а затем записывает на нее... На самом деле это раздражает. Лемм знает, работает ли это для вас.:)

Ответ 3

Document.write не будет работать с async script, потому что документ уже загружен, когда начинает работать script.

Но вы можете сделать это:

document.body.innerHTML = document.body.innerHTML + '<h1>Some HTML</h1>';

Ответ 4

Другая процедура - изменить поведение функции document.write().
Предположим, у вас есть основной файл index.php:

<html>
<head>
<meta charset="utf-8" />
</head>
<body>
Hello<br>
<div id="target"></div>
<script>
document.write = function(input) {
    document.body.innerHTML += input;
}
var doit = function() {
    var script_tag = document.createElement('script');
    script_tag.type = 'text/javascript';
    script_tag.src="http://127.0.0.1:8080/testPlace/jsfile.js";
    document.getElementById('target').appendChild(script_tag);
}
</script>
</body>
</html>

и jsfile.js выглядит следующим образом:

document.write("OK MAN!");

теперь, если вы наберете doit() в консоли браузера js для выполнения этой функции (и script выполните то, что вы написали), тогда результат будет выглядеть следующим образом:

Hello
OK MAN!

В котором html выглядит следующим образом:

<html><head>
<meta charset="utf-8">
</head>
<body>
Hello<br>
<div id="target"><script src="http://127.0.0.1:8080/testPlace/jsfile.js" type="text/javascript"></script></div>
<script>
    //That Script which here I removed it to take less space in answer
</script>

OK MAN!</body>
</html>

Ответ 5

Вы можете поддерживать script инъекцию правильным способом, перехватив вызовы document.write следующим образом:

document.writeText = document.write;

document.write = function(parameter) {
    if (!parameter) return; 
    var scriptPattern = /<script.*?src=['|"](.*?)['|"]/;
    if (scriptPattern.test(parameter)) {
        var srcAttribute = scriptPattern.exec(parameter)[1];
        var script = document.createElement('script');
        script.src = srcAttribute;
        document.head.appendChild(script); 
    }
    else {
        document.writeText(parameter);
    }   
};

Очевидно, это можно немного свести к минимуму, но имена переменных включены для ясности.

Источник

Ответ 6

Что такое сторонний файл javascript?

Если это API Google Maps JavaScript API v3, убедитесь, что вы включили "& callback = your_init_funct" в URL script. Затем он будет вызывать "your_init_funct" после загрузки библиотеки карт, чтобы вы могли начать отображать карту.

Другим решением будет bezen.domwrite.js, который доступен здесь: http://bezen.org/javascript/index.html

Демо: http://bezen.org/javascript/test/test-domwrite.html

Ответ 7

Да, document.write не может быть вызван из асинхронно загруженного script, поскольку он отсоединен от документа, поэтому он не может его записать.

Вы можете увидеть подход, используемый здесь для google-карт api, чтобы обойти эту проблему. Таким образом, некоторые из ваших сторонних скриптов, которые вы не назвали, могли бы иметь аналогичный шаблон обратного вызова.

https://developers.google.com/maps/documentation/javascript/examples/map-simple?hl=EN

<!DOCTYPE html>
<html>
  <head>
    <title>Simple Map</title>
    <meta name="viewport" content="initial-scale=1.0">
    <meta charset="utf-8">
    <style>
      html, body {
        height: 100%;
        margin: 0;
        padding: 0;
      }
      #map {
        height: 100%;
      }
    </style>
  </head>
  <body>
    <div id="map"></div>
    <script>

var map;
function initMap() {
  map = new google.maps.Map(document.getElementById('map'), {
    center: {lat: -34.397, lng: 150.644},
    zoom: 8
  });
}

    </script>
    <script src="https://maps.googleapis.com/maps/api/js?callback=initMap"
        async defer></script>
  </body>
</html>