views:

185

answers:

8

Say I have:

<ul>
    <li id="x">
        <a href="x">x</a>
    </li>

    <li id="y">
        <a href="y">y</a>

        <ul>
            <li id="z">
                <a href="z">z</a>
            </li>
        </ul>
    </li>
</ul>

I want to add a class value to all the list items that are the parents of z. So, I want to modify y but not x.

Obviously, I can parse this into some kind of associative array and then recurse backwards. Any ideas how I can do it with just text processing (string replacing, regular expression, etc)?

Thanks!

A: 

I suggest you parse it into a DOM and recurse backwards like you were thinking. Regular expressions don't work very well for nested structures with arbitrary nesting levels.

hasseg
+2  A: 

I would use XSLT. You can specify to search for nodes that are ancestors of z .

Si Keep
+2  A: 

xpath has an ancestor-axis which includes all ancestors of the current context node.

//*[@id="z"]/ancestor::li

* any element
[@id="z"] that has an attribute id with the value z (since the attribute is id there can(/should be) only be one such element)
/ancestor::li all li elements that are ancestors of that

see also: http://www.w3schools.com/xpath/xpath_axes.asp

VolkerK
A: 

Will add the attribute only to elements with a descendent element of 'z'.

DaveP
A: 

Thanks for the input. I figured it was impossible without parsing (or using xsl, etc)... Oh well.

+1  A: 

Example of the whole XSLT:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

  <xsl:variable name="ancestors" select="descendant::li[@id = 'z']/ancestor::li"/>

  <xsl:template match="li">
    <xsl:copy>
      <!-- test if current li is in the $ancestors node-list -->
      <xsl:if test="count($ancestors | .) = count($ancestors)">
        <xsl:attribute name="class">ancestor</xsl:attribute>
      </xsl:if>
      <xsl:apply-templates select="node() | @*"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="node() | @*">
    <xsl:copy>
      <xsl:apply-templates select="node() | @*"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>
jelovirt
A: 

This is a good fit for jQuery if that's available to you.

$("#z").parent().parent().addClass("foo");
John Sheehan
A: 

In addition to John Sheehan's anwser:
With JQuery I'd rather use
$('#z').parents('li').addClass('myClass');
than relying on the actual structure.

VolkerK