当子项处于XSLT活动状态时,为父元素提供一个类

时间:2010-01-21 08:46:25

标签: xml xslt

直到最近,XSLT对我来说还是全新的,我在XSLT中使用名为Dynamicweb的丹麦CMS进行了一段时间的菜单/子菜单。

我不知道这是一个Dynamicweb特定问题还是与XSLT相关的问题,但无论如何我都会问。

我当前的XSLT文档如下所示:

<xsl:template match="//Page">
    <xsl:param name="depth"/>
    <li>
        <xsl:attribute name="id">
            <xsl:value-of select="concat('', translate(translate(@MenuText, translate(@MenuText, $validRange, ''), ''), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'))" />
        </xsl:attribute>

        <a>
            <xsl:attribute name="class">
                <!-- Add .inpath class -->
                <xsl:if test="@InPath='True'">
                    <xsl:text> inpath</xsl:text>
                </xsl:if>

                <!-- Add .firstitem class -->
                <xsl:if test="position() = 1">
                    <xsl:text> firstitem</xsl:text>
                </xsl:if>

                <!-- Add .miditem class -->
                <xsl:if test="position() &gt; 1 and position() &lt; count(//Page)">
                    <xsl:text> miditem</xsl:text>
                </xsl:if>

                <!-- Add .lastitem class -->
                <xsl:if test="position() = count(//Page)">
                    <xsl:text> lastitem</xsl:text>
                </xsl:if>

                <!-- Add .active class -->
                <xsl:if test="@Active = 'True'">
                    <xsl:text> active</xsl:text>
                </xsl:if>
            </xsl:attribute>

            <!-- Add link ID (URL friendly menu text) -->
            <xsl:attribute name="id">
                <xsl:value-of select="concat('anchor-', translate(translate(@MenuText, translate(@MenuText, $validRange, ''), ''), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'))" />
            </xsl:attribute>

            <!-- Add link URL -->
            <xsl:attribute name="href">
                <xsl:value-of select="@FriendlyHref" disable-output-escaping="yes" />
            </xsl:attribute>

            <!-- Add link text -->
            <span><xsl:value-of select="@MenuText" disable-output-escaping="yes" /></span>
        </a>
        <xsl:if test="count(Page) and @MenuText != 'Home'">
            <ul class="level{@AbsoluteLevel+1}">
                <xsl:apply-templates select="Page">
                    <xsl:with-param name="depth" select="$depth+1"/>
                </xsl:apply-templates>
            </ul>
        </xsl:if>
    </li>

</xsl:template>

您可以看到我如何根据activeinpath的Dynamicweb标记添加类。问题是文档(未粘贴在此处)的下方是用于打印子菜单(ulli)的代码。如果我单击一个子菜单项,该项将获得active类,但是有没有办法给父级active类?

更新:添加了原始XML(对于凌乱的XML抱歉)。

<?xml version="1.0" encoding="utf-8"?>
<NavigationTree>
  <Settings>
    <Pageview ID="1" AreaID="1" MenuText="Home" Title="Home" NavigationName="" />
    <Setting Level="1">
      <NavigationImage Value="" />
      <NavigationMouseoverImage Value="" />
      <NavigationActiveImage Value="" />
      <NavigationImgAfter Value="" />
      <NavigationDividerImage Value="" />
      <NavigationHideSpacer Value="True" />
      <NavigationSpace Value="0" />
    </Setting>
    <Setting Level="2">
      <NavigationImage Value="" />
      <NavigationMouseoverImage Value="" />
      <NavigationActiveImage Value="" />
      <NavigationImgAfter Value="" />
      <NavigationDividerImage Value="" />
      <NavigationHideSpacer Value="" />
      <NavigationSpace Value="0" />
    </Setting>
    <Setting Level="3">
      <NavigationImage Value="" />
      <NavigationMouseoverImage Value="" />
      <NavigationActiveImage Value="" />
      <NavigationImgAfter Value="" />
      <NavigationDividerImage Value="" />
      <NavigationHideSpacer Value="" />
      <NavigationSpace Value="0" />
    </Setting>
    <Setting Level="4">
      <NavigationImage Value="" />
      <NavigationMouseoverImage Value="" />
      <NavigationActiveImage Value="" />
      <NavigationImgAfter Value="" />
      <NavigationDividerImage Value="" />
      <NavigationHideSpacer Value="" />
      <NavigationSpace Value="3" />
    </Setting>
    <Setting Level="5">
      <NavigationImage Value="" />
      <NavigationMouseoverImage Value="" />
      <NavigationActiveImage Value="" />
      <NavigationImgAfter Value="" />
      <NavigationDividerImage Value="" />
      <NavigationHideSpacer Value="" />
      <NavigationSpace Value="3" />
    </Setting>
  </Settings>
  <Page ID="1" AreaID="1" MenuText="Home" MouseOver="" Href="Default.aspx?ID=1" FriendlyHref="/default/home.aspx" Image="" ImageActive="" ImageMouseOver="" Title="" Allowclick="True" ShowInSitemap="True" AbsoluteLevel="1" RelativeLevel="1" Sort="1" LastInLevel="False" InPath="True" ChildCount="3" class="L1_Active" Active="True" IsPagePasswordProtected="False" IsPageUserProtected="False" CanAccessPasswordProtectedPage="False" CanAccessUserProtectedPage="True">
    <Page ID="6" AreaID="1" MenuText="News" MouseOver="" Href="Default.aspx?ID=6" FriendlyHref="/default/home/news.aspx" Image="" ImageActive="" ImageMouseOver="" Title="" Allowclick="True" ShowInSitemap="True" AbsoluteLevel="2" RelativeLevel="2" Sort="1" LastInLevel="False" InPath="False" ChildCount="0" class="L2" Active="False" IsPagePasswordProtected="False" IsPageUserProtected="False" CanAccessPasswordProtectedPage="False" CanAccessUserProtectedPage="True" />
    <Page ID="7" AreaID="1" MenuText="About" MouseOver="" Href="Default.aspx?ID=7" FriendlyHref="/default/home/about.aspx" Image="" ImageActive="" ImageMouseOver="" Title="" Allowclick="True" ShowInSitemap="True" AbsoluteLevel="2" RelativeLevel="2" Sort="2" LastInLevel="False" InPath="False" ChildCount="0" class="L2" Active="False" IsPagePasswordProtected="False" IsPageUserProtected="False" CanAccessPasswordProtectedPage="False" CanAccessUserProtectedPage="True" />
    <Page ID="8" AreaID="1" MenuText="Presence" MouseOver="" Href="Default.aspx?ID=8" FriendlyHref="/default/home/presence.aspx" Image="" ImageActive="" ImageMouseOver="" Title="" Allowclick="True" ShowInSitemap="True" AbsoluteLevel="2" RelativeLevel="2" Sort="3" LastInLevel="True" InPath="False" ChildCount="0" class="L2" Active="False" IsPagePasswordProtected="False" IsPageUserProtected="False" CanAccessPasswordProtectedPage="False" CanAccessUserProtectedPage="True" />
  </Page>
  <Page ID="2" AreaID="1" MenuText="Hygiene" MouseOver="" Href="Default.aspx?ID=14" FriendlyHref="/default/hygiene.aspx" Image="" ImageActive="" ImageMouseOver="" Title="" Allowclick="False" ShowInSitemap="True" AbsoluteLevel="1" RelativeLevel="1" Sort="2" LastInLevel="False" InPath="False" ChildCount="2" class="L1" Active="False" IsPagePasswordProtected="False" IsPageUserProtected="False" CanAccessPasswordProtectedPage="False" CanAccessUserProtectedPage="True">
    <Page ID="14" AreaID="1" MenuText="Professional" MouseOver="" Href="Default.aspx?ID=14" FriendlyHref="/default/hygiene/professional.aspx" Image="/Files/Navigation/menu_antibac_01.gif" ImageActive="/Files/Navigation/menu_antibac_02.gif" ImageMouseOver="/Files/Navigation/menu_antibac_02.gif" Title="" Allowclick="True" ShowInSitemap="True" AbsoluteLevel="2" RelativeLevel="2" Sort="1" LastInLevel="False" InPath="False" ChildCount="0" class="L2" Active="False" IsPagePasswordProtected="False" IsPageUserProtected="False" CanAccessPasswordProtectedPage="False" CanAccessUserProtectedPage="True" />
    <Page ID="15" AreaID="1" MenuText="Private" MouseOver="" Href="Default.aspx?ID=15" FriendlyHref="/default/hygiene/private.aspx" Image="/Files/Navigation/menu_antibac_01.gif" ImageActive="/Files/Navigation/menu_antibac_02.gif" ImageMouseOver="/Files/Navigation/menu_antibac_02.gif" Title="" Allowclick="True" ShowInSitemap="True" AbsoluteLevel="2" RelativeLevel="2" Sort="2" LastInLevel="True" InPath="False" ChildCount="0" class="L2" Active="False" IsPagePasswordProtected="False" IsPageUserProtected="False" CanAccessPasswordProtectedPage="False" CanAccessUserProtectedPage="True" />
  </Page>
  <Page ID="3" AreaID="1" MenuText="Household &amp; Leisure" MouseOver="" Href="Default.aspx?ID=3" FriendlyHref="/default/household---leisure.aspx" Image="" ImageActive="" ImageMouseOver="" Title="" Allowclick="True" ShowInSitemap="True" AbsoluteLevel="1" RelativeLevel="1" Sort="3" LastInLevel="False" InPath="False" ChildCount="0" class="L1" Active="False" IsPagePasswordProtected="False" IsPageUserProtected="False" CanAccessPasswordProtectedPage="False" CanAccessUserProtectedPage="True" />
  <Page ID="4" AreaID="1" MenuText="Car Care" MouseOver="" Href="Default.aspx?ID=4" FriendlyHref="/default/car-care.aspx" Image="" ImageActive="" ImageMouseOver="" Title="" Allowclick="True" ShowInSitemap="True" AbsoluteLevel="1" RelativeLevel="1" Sort="4" LastInLevel="False" InPath="False" ChildCount="1" class="L1" Active="False" IsPagePasswordProtected="False" IsPageUserProtected="False" CanAccessPasswordProtectedPage="False" CanAccessUserProtectedPage="True">
    <Page ID="20" AreaID="1" MenuText="Carix" MouseOver="" Href="Default.aspx?ID=20" FriendlyHref="/default/car-care/carix.aspx" Image="/Files/Navigation/menu_carix_01.gif" ImageActive="/Files/Navigation/menu_carix_02.gif" ImageMouseOver="/Files/Navigation/menu_carix_02.gif" Title="" Allowclick="True" ShowInSitemap="True" AbsoluteLevel="2" RelativeLevel="2" Sort="1" LastInLevel="True" InPath="False" ChildCount="0" class="L2" Active="False" IsPagePasswordProtected="False" IsPageUserProtected="False" CanAccessPasswordProtectedPage="False" CanAccessUserProtectedPage="True" />
  </Page>
  <Page ID="5" AreaID="1" MenuText="Industrial Chemicals" MouseOver="" Href="Default.aspx?ID=5" FriendlyHref="/default/industrial-chemicals.aspx" Image="" ImageActive="" ImageMouseOver="" Title="" Allowclick="True" ShowInSitemap="True" AbsoluteLevel="1" RelativeLevel="1" Sort="5" LastInLevel="True" InPath="False" ChildCount="0" class="L1" Active="False" IsPagePasswordProtected="False" IsPageUserProtected="False" CanAccessPasswordProtectedPage="False" CanAccessUserProtectedPage="True" />
</NavigationTree>

2 个答案:

答案 0 :(得分:5)

嗯,有几点意见:

  1. 您不需要match='//Page'中的前导斜杠。 match属性中的XPath用于选择模板,而不是导航到节点。

  2. 您不需要depth参数;由于Page的深度是它所拥有的Page祖先的数量,因此您只需使用count(ancestor::Page)来计算深度。

  3. 如果您希望通过向上走Active元素树获取Page属性,请使用ancestor-or-self::Page[last()]/@Active。祖先或自身轴是以上下文节点开始并包括其父节点,父节点的父节点等的节点列表,直到达到TLE为止。 Page部分仅在该轴上找到Page元素,[last()]谓词找到最后一个Page元素,这些元素始终是最高级{{1}元素。

  4. 我强烈怀疑你使用Page从长远来看不会对你有好处。这会计算源树中的每个count(//Page)元素,而不管它在何处被发现。这真的是你想要的吗?或者您想知道当前Page元素相对于其兄弟Page元素的位置?如果是这样,您可以Page。这是有效的,因为position() != 1 and position() != last()返回上下文节点相对于其表达式上下文的位置。也就是说,当调用position()时,它会创建一组元素并为每个元素找到匹配的模板。这就是表达背景;该列表中的第三个元素的xsl:apply-templates select='*'为3。

  5. 没有必要position()test='count(Page)做同样的事情;如果有任何子test='Page'元素,则计算结果为true。它更具可读性,而且速度更快。

  6. 修改

    收集Tomalak的观察(见评论)。

    修改

    你知道,我应该读这些问题。在你的上下文中,当一个类或它的一个 children 处于活动状态时,给一个父元素一个类,与我所做的相反,你会这样做:

    Page

    但Tomalak使用<xsl:if test="@Active='True' or Page[@Active = 'True']"> <xsl:text> active</xsl:text> </xsl:if> 轴的示例更可能是您想要使用的,如果想要激活菜单,如果其中任何位置处于活动状态。你也可以这样做:

    descendant-or-self

    as“.//”实际上只是<xsl:if test="@Active='True' or .//Page[@Active = 'True']"> <xsl:text> active</xsl:text> </xsl:if> 的快捷方式。

答案 1 :(得分:1)

我已经为你的问题创建了一个解决方案(我在这个过程中大量修改了你的方法):

<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />

  <xsl:variable name="upper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
  <xsl:variable name="lower" select="'abcdefghijklmnopqrstuvwxyz'" />
  <xsl:variable name="validRange" select="concat($upper, $lower)" />

  <xsl:template match="NavigationTree">
    <ul class="level1">
      <xsl:apply-templates select="Page">
        <xsl:sort select="@Sort" data-type="number" />
      </xsl:apply-templates>
    </ul>
  </xsl:template>

  <xsl:template match="Page">
    <xsl:variable name="idText" select="
      translate(
        translate(@MenuText, translate(@MenuText, $validRange, ''), ''), $upper, $lower
      )
    " />
    <li id="{$idText}">
      <a href="{@FriendlyHref}" id="anchor-{$idText}">
        <xsl:attribute name="class">
          <xsl:if test="position() = 1">firstitem </xsl:if>
          <xsl:if test="position() &gt; 1 and position() &lt; last()">miditem </xsl:if>
          <xsl:if test="position() = last()">lastitem </xsl:if>
          <xsl:if test="@InPath='True'">inpath </xsl:if>
          <!-- the descendant-or-self XPath axis solves your question! -->
          <xsl:if test="descendant-or-self::Page[@Active='True']">active </xsl:if>
        </xsl:attribute>
        <span>
          <xsl:value-of select="@MenuText" />
        </span>
      </a>

      <xsl:if test="Page">
        <ul class="level{Page/@AbsoluteLevel}">
          <xsl:apply-templates select="Page">
            <xsl:sort select="@Sort" data-type="number" />
          </xsl:apply-templates>
        </ul>
      </xsl:if>
    </li>
  </xsl:template>

</xsl:stylesheet>

导致(当第20页设置为“活动”时):

<ul class="level1">
  <li id="home">
    <a href="/default/home.aspx" id="anchor-home" class="firstitem inpath">
      <span>Home</span>
    </a>
    <ul class="level2">
      <li id="news">
        <a href="/default/home/news.aspx" id="anchor-news" class="firstitem">
          <span>News</span>
        </a>
      </li>
      <li id="about">
        <a href="/default/home/about.aspx" id="anchor-about" class="miditem">
          <span>About</span>
        </a>
      </li>
      <li id="presence">
        <a href="/default/home/presence.aspx" id="anchor-presence" class="lastitem">
          <span>Presence</span>
        </a>
      </li>
    </ul>
  </li>
  <li id="hygiene">
    <a href="/default/hygiene.aspx" id="anchor-hygiene" class="miditem">
      <span>Hygiene</span>
    </a>
    <ul class="level2">
      <li id="professional">
        <a href="/default/hygiene/professional.aspx" id="anchor-professional" class="firstitem">
          <span>Professional</span>
        </a>
      </li>
      <li id="private">
        <a href="/default/hygiene/private.aspx" id="anchor-private" class="lastitem">
          <span>Private</span>
        </a>
      </li>
    </ul>
  </li>
  <li id="householdleisure">
    <a href="/default/household---leisure.aspx" id="anchor-householdleisure" class="miditem">
      <span>Household &amp; Leisure</span>
    </a>
  </li>
  <li id="carcare">
    <a href="/default/car-care.aspx" id="anchor-carcare" class="miditem active">
      <span>Car Care</span>
    </a>
    <ul class="level2">
      <li id="carix">
        <a href="/default/car-care/carix.aspx" id="anchor-carix" class="firstitem lastitem active">
          <span>Carix</span>
        </a>
      </li>
    </ul>
  </li>
  <li id="industrialchemicals">
    <a href="/default/industrial-chemicals.aspx" id="anchor-industrialchemicals" class="lastitem">
      <span>Industrial Chemicals</span>
    </a>
  </li>
</ul>

请注意,您不希望disable-output-escaping="yes",我已将其删除,因为它会产生格式错误的XML。