How can I target a specific group of siblings in a flat hierarchy?
Due to the way the general sibling combinator works, it is not possible to limit a selection of siblings to a specific range or group, even of consecutive siblings. Even the :not()
selector won't be of any help here.
You will have to use JavaScript to target the right elements. jQuery's .nextUntil()
method immediately springs to mind.
CSS General Sibling Selector Specificity
Both selectors have the same specificity, what causes h2 ~ p
to take precedence is that it is defined after, therefore cascading over h1 ~ p
. How close the sibling are is of no consequence.
For the behavior you want you can use the adjacent sibling selector +
.
If you change the h1 ~ p
after you will see it takes precidience
h2 ~ p { color: green;}
h1 ~ p { color: red;}
<h1>Section 1</h1><p>I came after a h1 and should be red.</p> <h2>Section 2</h2><p>I came after a h2 and should be green.</p>
<h1>Section 3</h1><p>I should be the same color as the section one text?</p>
Sibling css selector not changing
Given html
at Question you can use adjacent sibling selector +
, !important
[class="1"],[class="1"] ~ tr { background-color: blue;}
[class="2"],[class="2"] + tr { background-color: red !important;}
<table> <tbody> <tr class="1"> <td>foo</td> </tr> <tr> <td>foo</td> </tr> <tr> <td>foo</td> </tr> <tr class="2"> <td>foo</td> </tr> <tr> <td>foo</td> </tr> <tr class="1"> <td>foo</td> </tr> <tr> <td>foo</td> </tr> <tr> <td>foo</td> </tr> <tr> <td>foo</td> </tr> </tbody></table>
Building hierarchy objects from flat list of parent/child
Here's the function I ended up writing. I'm using MPTT to store objects, so the list is in order of the 'left' value, which basically means the parent always comes before any given item in the list. In other words, the item referenced by item.ParentID has always already been added (except in the case of top-level or root nodes).
public class TreeObject
{
public int Id { get; set; }
public int ParentId { get; set; }
public string Name { get; set; }
public IList<TreeObject> Children { get; set; } = new List<TreeObject>();
}
public IEnumerable<TreeObject> FlatToHierarchy(List<TreeObject> list)
{
// hashtable lookup that allows us to grab references to containers based on id
var lookup = new Dictionary<int, TreeObject>();
// actual nested collection to return
var nested = new List<TreeObject>();
foreach (TreeObject item in list)
{
if (lookup.ContainsKey(item.ParentId))
{
// add to the parent's child list
lookup[item.ParentId].Children.Add(item);
}
else
{
// no parent added yet (or this is the first time)
nested.Add(item);
}
lookup.Add(item.Id, item);
}
return nested;
}
and a simple test (that works in LinqPad):
void Main()
{
var list = new List<TreeObject>() {
new TreeObject() { Id = 1, ParentId = 0, Name = "A" },
new TreeObject() { Id = 2, ParentId = 1, Name = "A.1" },
new TreeObject() { Id = 3, ParentId = 1, Name = "A.2" },
new TreeObject() { Id = 4, ParentId = 3, Name = "A.2.i" },
new TreeObject() { Id = 5, ParentId = 3, Name = "A.2.ii" }
};
FlatToHierarchy(list).Dump();
}
Results:
Since I'm updating this 5 years later, here's a recursive LINQ version:
public IList<TreeObject> FlatToHierarchy(IEnumerable<TreeObject> list, int parentId = 0) {
return (from i in list
where i.ParentId == parentId
select new TreeObject {
Id = i.Id,
ParentId = i.ParentId,
Name = i.Name,
Children = FlatToHierarchy(list, i.Id)
}).ToList();
}
How to group a flat un-categorized list into sub lists using XSLT grouping
Couldn't you do simply:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/html">
<body>
<xsl:for-each-group select="body/*" group-starting-with="p[@class='step']">
<xsl:choose>
<xsl:when test="self::p[@class='step']">
<step>
<xsl:copy-of select="."/>
<xsl:for-each-group select="current-group() except ." group-starting-with="p[@class='substep']">
<xsl:choose>
<xsl:when test="self::p[@class='substep']">
<substep>
<xsl:copy-of select="current-group()"/>
</substep>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</step>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</body>
</xsl:template>
</xsl:stylesheet>
Join siblings next to each other when two attribute conditions are met
As some XSLT 2 processors like Saxon 9 or 10 or Altova Raptor or XmlPrime also support XQuery 3 it might be more convenient to use the XQuery 3 window
clause to formulate such conditions as it explicitly allows you to form end
conditions where you have access to variable binding comparing the "last" item in a "window/group" to the following item (next
):
<ROOT>
{
for tumbling window $w in ROOT/p
start when true()
end $e next $n when $e/@y != $n/@y or $n/@x - $e!(@width + @x) ge 4
return
<p>
{
head($w)/@*, $w/node()
}
</p>
}
</ROOT>
https://xqueryfiddle.liberty-development.net/3Nzd8bU
In XSLT 2/3 using for-each-group group-starting-with
and assuming the grouping population is a sibling sequence you could use
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="ROOT">
<xsl:copy>
<xsl:for-each-group select="p"
group-starting-with="p[1] | p[preceding-sibling::p[1][@y != current()/@y or (@x - current()!(@width + @x) ge 4)]]">
<xsl:copy>
<xsl:apply-templates select="@*, current-group()/node()"/>
</xsl:copy>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/naZYrpE/7
The used expression current()!(@with + @x)
is XSLT/XPath 3 but you can use current()/(@with + @x)
in XSLT 2. Instead of using the declarative xsl:mode
to set up the identity transformation you would need to spell out the template <xsl:template match="@* | node()"><xsl:copy><xsl:apply-templates select="@* | node()"/></xsl:copy></xsl:template>
.
Related Topics
Apply CSS Style to Child Elements
How to Keep CSS Floats in One Line
How to Center a Bootstrap Div with a 'Spanx' Class
How to Add a Margin to a Table Row <Tr>
How to Have a Position: Fixed; Behaviour for a Flexbox Sized Element
Shrinking Navigation Bar When Scrolling Down (Bootstrap3)
Where Do CSS and JavaScript Files Go in a Maven Web App Project
CSS Flexbox: Difference Between Align-Items and Align-Content
What CSS Selector Can Be Used to Select the First Div Within Another Div
Position Div to Bottom of Containing Div
How to Target Microsoft Edge with CSS
What Is the Em Font-Size Unit? How Much Is It in Pixels
How to Vertically Align 2 Different Sizes of Text
CSS Class and Id with the Same Name