通过innerHTML注入内容时,如何在<script>标记中运行JavaScript?

时间:2019-07-05 00:11:36

标签: javascript

我正在尝试为画廊编写基于JavaScript的分页,该画廊通过来自服务器的onClick事件加载HTML。只需通过innerHtml附加HTML。唯一的困难是我需要用缩略图URL填充数组以进行预览。数据通过与HTML相同的HTTP请求提供。

我试图通过<script>标签将其添加到HTML,但是当通过innerHtml插入时,这些标签不会执行(请参见here)。

约束:

  • HTML必须在服务器上呈现,不能通过JavaScript手动创建HTML元素(由于模板的可交换性)
  • 由于复杂性,脚本和HTML必须通过单个HTTP请求提供服务

可能的解决方案

由于我对JavaScript的了解有限,所以我只看到2个解决方案,但似乎没有一个对的。

  1. 将HTML和数组数据发送为JSON。因此,当HTTP请求返回时,可以通过innerHTML附加HTML,并且可以将数组数据传递给函数。
  2. “内部网络抓取”:理论上,我可以将数组数据作为JSON字符串保留在<script>元素中,并通过一些晦涩的JS技巧来提取其内容。但是我敢肯定,我会为此受到殴打。

任何建议都值得赞赏。

可能与谁有关的一些代码:

function loadNextPage(uri) {
  var xhr = new XMLHttpRequest();
  xhr.open('GET', uri);
  xhr.onload = function() {
    if (xhr.status === 200) {
      var loadNextButton = document.getElementById("load-next-button");
      loadNextButton.parentNode.removeChild(loadNextButton);
      var vbw = document.getElementById("vbw");
      vbw.innerHTML = vbw.innerHTML + xhr.response;
    } else {
      conosole.log('Failed: ' + xhr.status);
    }
  };
  xhr.send();
}
<div id="vbw">
  <script type="application/javascript">
    let thumbNailStore = {
      concat: function() {
        console.log('Concat has been ran');
      },
    };

    function addThumbnailListeners() {
      console.log('addThumbnailListeners  has been ran');
    }
    thumbNailStore.concat(); // This produces a JSON which is appended to an array in the <head> of the page
    addThumbnailListeners();
  </script>
  <button id="load-next-button" onclick="loadNextPage('10')">Load more</button>
</div>

和模板:

@standardtemplate.commons.gallery(videoList, LandingPage.empty()) // That produces the actual gallery HTML

@if(pagination.getNextPath.isPresent) {
    <script type="application/javascript">
        thumbNailStore.concat(@Html(TemplateHelper.thumbListToJson(videoList))); // This produces a JSON which is appended to an array in the <head> of the page
        addThumbnailListeners();
    </script>
    <button id="load-next-button" onclick="loadNextPage('@pagination.getNextPath.get()')">Load more</button>
}

谢谢!

2 个答案:

答案 0 :(得分:1)

如果使用jQuery,load()将执行加载的内容中包含的所有脚本元素,并修改内部HTML,这听起来像您所需要的。

否则,您可以使用eval。您只需要某种方法来检索脚本元素(也许是vbw的最后一个脚本元素子级,可以是某个预定的id,甚至可以是从响应数据中创建一个元素并从那里访问脚本subElement)。无论哪种方式,一旦有了脚本元素对象:

eval(scriptElement.innterHtml);

答案 1 :(得分:1)

您的解决方案几乎是正确的,我将vbw.innerHTML = vbw.innerHTML + xhr.response;替换为.appendChild()方法。但我也注意到您要添加倍数<script type="application/javascript">...</script>。因此,我认为如果您两次按下按钮 Load More (加载更多),最终将在代码中添加3个<script>标签。

如何解决此问题?

好吧,首先提取内联脚本inlineScript并将其附加到当前脚本中。然后,使用DOMParser().parseFromString()方法创建一个新文档。这将解析您的xhr.response,然后获取<button>元素,最后将其附加到您的<div>

这里是一个例子:

let matches, clickCount = 0;
const regexScript = /<script[\s\S]*?>([\s\S]*?)<\/script>/gi;
const regexButton = /<button[\s\S]*?>[\s\S]*?<\/button>/gi;
let nodeCounter = document.getElementById('counter');

function getResponse() {
  // To simulate response from your server
  return `&lt;script type=&quot;application/javascript&quot;&gt;let thumbNailStore={concat:function(){console.log(&#39;Concat for videolist ${clickCount} &#39;)}};function addThumbnailListeners(){console.log(&#39;addThumbnailListeners called ${clickCount}&#39;)}thumbNailStore.concat(),addThumbnailListeners();&lt;/script&gt;&lt;button id=&quot;load-next-button&quot; onclick=&quot;loadNextPage(&#39;https://jsonplaceholder.typicode.com/users/1&#39;)&quot;&gt;Load more&lt;/button&gt;`;
}

function htmlDecode(input) {
  var e = document.createElement('div');
  e.innerHTML = input;
  return e.childNodes[0].nodeValue;;
}

function loadNextPage(uri) {
  let xhr = new XMLHttpRequest();
  xhr.open('GET', uri);
  xhr.onload = function() {
    if (xhr.status === 200) {
      // Im' using responseTest you should use xhr.response
      let rawdata = htmlDecode(getResponse());
      clickCount++

      // Script matches
      matches = Array.from(rawdata.matchAll(regexScript));
      let rawScript = matches[0][1];
      let inlineScript = document.createTextNode(rawScript);

      // Button matches
      matches = Array.from(rawdata.matchAll(regexButton));
      let rawButton = matches[0];

      let script = document.getElementById('loaded-script');
      script.innerHTML = '';
      script.appendChild(inlineScript);

      let loadNextButton = document.getElementById('load-next-button');
      loadNextButton.parentNode.removeChild(loadNextButton);
      let vbw = document.getElementById('vbw');

      let doc = new DOMParser().parseFromString(rawButton, 'text/html');
      //let script = doc.body.childNodes;
      let button = doc.getElementById('load-next-button');
      // vbw.innerHTML = vbw.innerHTML + xhr.response;
      vbw.appendChild(button);
      nodeCounter.innerHTML = 'Request processed: ' + clickCount;
      eval(script.innerHTML);
    } else {
      conosole.log('Failed: ' + xhr.status);
    }
  };
  xhr.send();
}
<div id="vbw">
  <script id="loaded-script" type="application/javascript">
    let thumbNailStore = {
      concat: function() {
        console.log('Concat original');
      },
    };

    function addThumbnailListeners() {
      console.log('addThumbnailListeners');
    }
    thumbNailStore.concat(); // This produces a JSON which is appended to an array in the <head> of the page
    addThumbnailListeners();
  </script>
  <button id="load-next-button" onclick="loadNextPage('https://jsonplaceholder.typicode.com/users/1')">Load more</button>
</div>
<div id="counter"></div>