如何在从网络流入DOM元素时附加它们?

时间:2016-07-16 16:48:27

标签: javascript html dom streaming service-worker

我有一个元素,我希望将其作为请求的HTML流填充,而不是等待完整的响应。事实证明这非常困难。

我尝试的事情:

1。 milestoneNode.insertAdjacentHTML('beforebegin', text)

如果这样可行,那就太可爱了。不幸的是,具有古怪解析的元素会破坏它 - 例如<p><table>。得到的DOM可以被称为Dada。

2。使用虚拟DOM / DOM更新管理库

Google incremental-dom似乎最有希望,但其patch()操作始终从容器节点的开头重新启动。不知道如何&#34;冻结&#34;它到位了。

这也包含了在JavaScript中至少进行HTML标记化的行李,并且某些实际的树构建必须发生,除非一个服务格式良好的XHTML5。 (没有人这样做。)重新实现 浏览器的 HTML解析器似乎是一个迹象,我已经犯了可怕的错误。

3。 document.write()

我很绝望。具有讽刺意味的是,这位古老的笨蛋们几乎我所需要的行为,没有&#34;扔掉现有的页面&#34;的事情。

4。附加到字符串,然后定期innerHTML

击败流媒体点,因为最终整个响应都被保存在内存中。还有重复的序列化开销。

从好的方面来说,它确实有效。但肯定有更好的方法吗?

2 个答案:

答案 0 :(得分:2)

Jake Archibald figured out a silly hack to get this behavior in browsers today。他的示例代码说它比我更好:

// Create an iframe:
const iframe = document.createElement('iframe');

// Put it in the document (but hidden):
iframe.style.display = 'none';
document.body.appendChild(iframe);

// Wait for the iframe to be ready:
iframe.onload = () => {
  // Ignore further load events:
  iframe.onload = null;

  // Write a dummy tag:
  iframe.contentDocument.write('<streaming-element>');

  // Get a reference to that element:
  const streamingElement = iframe.contentDocument.querySelector('streaming-element');

  // Pull it out of the iframe & into the parent document:
  document.body.appendChild(streamingElement);

  // Write some more content - this should be done async:
  iframe.contentDocument.write('<p>Hello!</p>');

  // Keep writing content like above, and then when we're done:
  iframe.contentDocument.write('</streaming-element>');
  iframe.contentDocument.close();
};

// Initialise the iframe
iframe.src = '';
  

虽然将<p>Hello!</p>写入iframe,但它会显示在父文档中!这是因为解析器维护一堆打开的元素,新创建的元素被插入其中。我们移动<streaming-element>并不重要,它只是有效。

答案 1 :(得分:1)

您可以使用fetch(),处理Response.body ReadableStream; TextDecoder()

let decoder = new TextDecoder();

function handleResponse(result) {
  element.innerHTML += decoder.decode(result.value);
  return result
}

fetch("/path/to/resource/")
.then(response => response.body.getReader())
.then(reader => {
  return reader.read().then(function process(result) {
    if (result.done) {
      console.log("stream done");
      return reader.closed;
    }
    return reader.read().then(handleResponse).then(process)
  })
  .then(function() {
    console.log("stream complete", element.innerHTML);
  })
  .catch(function(err) {
    console.log(err)
  })
});