You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

XSLT 3.0按eop标签分页时奇偶页方向切换异常求解

可行实现方案

核心问题是之前的实现把页面规则的绑定范围局限在了<article>标签上,没有覆盖跨层级的页面边界,XSLT 3.0/XPath 3.1原生支持跨层级按文档顺序查找前置节点,配合分组逻辑可以彻底解决这个问题,不需要逐节点绑定复杂的判断逻辑。


1. 最优方案:按<eop>做全局跨层级分页分组

不要用仅在<article>节点读取累加器的方案,直接用xsl:for-each-group的group-starting-with规则,从文档根节点遍历所有后代节点——不管<eop>嵌套在<p>、<span>还是任意深度的层级下,都以它作为页面分界点,把两个相邻<eop>之间的所有内容归为同一页,从页面容器的根节点就绑定对应方向和布局样式,从根本上避免样式沿用问题。
基础实现代码如下:

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs">
  <xsl:output method="html" indent="yes"/>

  <xsl:template match="/">
    <html>
      <head>
        <style>
          .page {padding: 2rem; min-height: 100vh; box-sizing: border-box;}
          .two-col {display: grid; grid-template-columns: 1fr 1fr; gap: 2rem;}
        </style>
      </head>
      <body>
        <!-- 全局遍历所有后代节点,按eop做跨层级分页,完全忽略eop的父级层级 -->
        <xsl:for-each-group select="descendant::node()" group-starting-with="eop">
          <xsl:variable name="page-num" as="xs:integer" select="
            if (self::eop) then count(preceding::eop) + 2
            else 1
          "/>
          <xsl:variable name="dir-val" as="xs:string" select="
            if ($page-num mod 2 = 1) then 'ltr' else 'rtl'
          "/>
          <!-- 页面容器直接绑定方向和两栏布局,从页首就生效 -->
          <section class="page two-col" dir="{$dir-val}">
            <!-- 跳过eop标记本身,渲染当前页所有内容 -->
            <xsl:apply-templates select="current-group()[not(self::eop)]"/>
          </section>
        </xsl:for-each-group>
      </body>
    </html>
  </xsl:template>

  <!-- 其余标题、段落、列表等元素的渲染模板按正常规则编写即可 -->
</xsl:stylesheet>

2. 逐节点判断方案(不使用全量分组时使用)

如果业务场景不允许做全量分组,要让任意节点都能拿到文档顺序上最近的前置<eop>,直接用XPath的preceding轴即可——这个轴本身就是跨层级、按文档顺序返回前置节点的,对任意当前节点,下面的表达式就能拿到离它最近的前置<eop>,完全不受XML树层级限制:

(: 当前节点最近的前置eop,第一页没有前置eop时返回空序列 :)
let $latest-eop := preceding::eop[1]

配合全局生效的累加器可以直接拿到当前节点所属页码,不需要把逻辑绑定在<article>模板里:

<!-- 全局累加器,只要遍历到eop就触发页码+1,不管eop在哪个层级 -->
<xsl:accumulator name="current-page" as="xs:integer" initial-value="1">
  <xsl:accumulator-rule match="eop" select="$value + 1"/>
</xsl:accumulator>
<xsl:mode use-accumulators="current-page"/>

任意节点都可以直接调用accumulator-before('current-page')拿到所属页码,再对应设置方向和布局样式即可。


3. 注意事项

如果<eop>出现在行内元素(比如<p>、<span>)内部,分组会把原本的父元素拆成跨页的两部分,只需要在对应块级元素的模板里补充分页拆分逻辑,遇到页面边界时主动闭合当前标签、在新页重开标签即可,避免生成不合法的HTML结构。

内容的提问来源于stack exchange,提问作者OrYo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 02:57:25