【问题标题】:Split HTML into virtual pages将 HTML 拆分为虚拟页面
【发布时间】:2019-07-25 14:19:16
【问题描述】:

我的内容很长(包含图片、列表、div、文本等的多个内容...)。我想将此内容作为虚拟 PDF 页面显示给用户。我不想生成 PDF,只需将此 HTML 内容显示为具有相同页眉/页脚的页面(由宽度/高度定义)。它应该如下图所示,正如您在第一页上看到的那样,我想拆分该文本并显示在下一页:

我正在 React 上开发这个应用程序。我不知道这个内容会是什么,每个渲染都会不同(根据用户活动使用不同的内容)。

您对如何做到这一点有任何建议吗? (CSS 解决方案,或者 JS,或者我不知道可能是一些 React lib ...)

【问题讨论】:

  • 内容的本质是什么?是纯 html 还是 React 元素数组或其他?
  • 是React组件生成的HTML
  • 我这么说是因为我不知道任何 css 能够不连续地进行布局,因此必须解析内容。在 html 生成之前采取行动会更简单。
  • 是的,我能想到的唯一方法是查看 HTML 内容并找出某种方法将其拆分为逻辑上适合每个页面的块,然后将每个块传递给页面组件.
  • @KristiánStroka 解决方案对您不起作用?

标签: html css reactjs


【解决方案1】:

这是一个幼稚但有效的实现。

我们的想法是将 html 安装到屏幕外 div 中,该 div 与我们尝试呈现的页面具有相同的尺寸。

然后遍历这个屏幕外 div 的元素(即 children 来自已解析的 html)并使用 getBoundingClientRect 查询 dom 以找到第一个溢出的元素。

然后我们从屏幕外的 div 中删除溢出元素之前的所有元素,并将它们缓存在一个数组中。

重新开始一个新块,直到屏幕外 div 中没有更多元素。

将其适应 React 只需将 dangerouslySetInnerHTML 与每个页面的 html 内容一起使用。

(display flex 只是用来强制元素流动,但任何布局都可以,只要它在 offscreenDiv 和 page 中相同)

function generateRandomContent() {
  var alph = "abcdefghijklmnopqrstuvwxyz";
  var content = "";
  // we will generate 100 random elements displaying their index to keep track of what's happening
  for (var i = 0; i < 100; i++) {
    var type = parseInt(Math.random() * 2, 10);
    switch (type) {
      case 0: // text, generates and random p block
        content = content + "<p>" + i + " ";
        var numWords = 10 + parseInt(Math.random() * 50, 10);
        for (var j = 0; j < numWords; j++) {
          var numLetters = 2 + parseInt(Math.random() * 15, 10);
          if (j > 0) {
            content = content + " ";
          }
          for (var k = 0; k < numLetters; k++) {
            content = content + alph[parseInt(Math.random() * 26, 10)];
          }
          
        }
        content = content + "</p>";
        break;
      case 1: // colored div, generates a div of random size and color
        var width = 30 + parseInt(Math.random() * 20, 10) * 10;
        var height = 30 + parseInt(Math.random() * 20, 10) * 10;
        var color = "rgb(" + parseInt(Math.random() * 255, 10) + ", " + parseInt(Math.random() * 255, 10) + ", " + parseInt(Math.random() * 255, 10) + ")";
        content = content + '<div style="width: ' + width + 'px; height: ' + height + 'px; background-color: ' + color + '">' + i + '</div>';
        break;
       
    }
  }
  return content;
}

function getNodeChunks(htmlDocument) {
  var offscreenDiv = document.createElement('div');
  offscreenDiv.className = 'page';
  offscreenDiv.style.position = 'absolute';
  offscreenDiv.style.top = '-3000px';
  offscreenDiv.innerHTML = htmlDocument;
  offscreenDiv.display = 'flex';
  offscreenDiv.flexWrap = 'wrap';
  document.body.appendChild(offscreenDiv);
  offscreenRect = offscreenDiv.getBoundingClientRect();
  // console.log('offscreenRect:', offscreenRect);
  var chunks = [];
  var currentChunk = []
  for (var i = 0; i < offscreenDiv.children.length; i++) {
    var current = offscreenDiv.children[i];
    var currentRect = current.getBoundingClientRect();
    currentChunk.push(current);
    if (currentRect.bottom > (offscreenRect.bottom)) {
      // current element is overflowing offscreenDiv, remove it from current chunk
      currentChunk.pop();
      // remove all elements in currentChunk from offscreenDiv
      currentChunk.forEach(elem => elem.remove());
      // since children were removed from offscreenDiv, adjust i to start back at current eleme on next iteration
      i -= currentChunk.length;
      // push current completed chunk to the resulting chunklist
      chunks.push(currentChunk);
      // initialise new current chunk
      currentChunk = [current];
      offscreenRect = offscreenDiv.getBoundingClientRect();
    }
  }
  // currentChunk may not be empty but we need the last elements
  if (currentChunk.length > 0) {
    currentChunk.forEach(elem => elem.remove());
    chunks.push(currentChunk);
  }
  // offscreenDiv is not needed anymore
  offscreenDiv.remove();
  return chunks;
}

function appendChunksToPages(chunks) {
    var container = document.getElementsByClassName('root_container')[0];
    chunks.forEach((chunk, index) => {
      // ex of a page header
      var header = document.createElement('div');
      header.innerHTML = '<h4 style="margin: 5px">Page ' + (index + 1) + '</h4>';
      container.appendChild(header);
      var page = document.createElement('div');
      page.className = 'page';
      chunk.forEach(elem => page.appendChild(elem));
      container.appendChild(page);
    });
}

// generateRandom content outputs raw html, getNodeChunks returns
// an array of array of elements, the first dimension is the set of
// pages, the second dimension is the set of elements in each page
// finally appendChunks to pages generates a page for each chunk 
// and adds this page to the root container
appendChunksToPages(getNodeChunks(generateRandomContent()));
 
 .page {
  border: 1px solid;
  display: flex;
  flex-wrap: wrap;
  height: 700px;
  width: 50%;
  margin-bottom: 20px;
 }
&lt;div class="root_container"&gt;&lt;/div&gt;

【讨论】:

  • 您好,代码运行良好,但是您对处理表格有什么建议吗?由于整个表被读取为一个块,因此不会将其拆分。因此,如果表格超过一页,它将溢出而不创建新页面。
  • 您是否有针对深度嵌套结构的解决方案,当该结构的多个级别充当用于其他目的的虚拟包装器并且不介意在页面之间分成两半时?
  • 这个解决方案不够健壮,无法处理深度嵌套的 html,因为它只能处理一层子级。处理深度嵌套的html通过反复试验递归处理节点:开始在屏幕外div中写入节点,如果它有子节点,则递归地决定它是否适合页面,如果适合就这样,否则复制它,拆分它溢出并在新页面上分配剩余的孩子。如果更多人感兴趣,我可能会在另一个答案中尝试一下。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-01-19
  • 2023-04-01
  • 1970-01-01
  • 2011-12-27
  • 1970-01-01
相关资源
最近更新 更多