16

I want to set a data-index value of a parent element on one of its (nested) children. Desired result: string "index" should appear around the h2.heading.

Markup:

<div class="foo" data-index="index">
    <div>
        <h2 class="heading"><span>title</span></h2>
    </div>
</div>

CSS (the first data-index rule works - but not in the right place):

div.foo[data-index] .heading span {
    display: none;
}

div.foo[data-index]::after { 
    content: attr(data-index);
    color: green;
}

div.foo[data-index] .heading::after { 
    content: attr(data-index);
    color: red;
    border: 1px solid red;
}

http://codepen.io/anon/pen/jyxdoz

montrealist
  • 5,593
  • 12
  • 46
  • 68
  • The problem is that I don't think you can pull the `attr(data-index)` from a parent of an element in CSS3. You'd have to have the `data-index` property in the `div.foo > div` element that is wrapping the header in order to put an `::after` pseudo on it. – forrestmid Feb 03 '17 at 23:57
  • @forrestmid I'm suspecting that; is there any reading I could do on this? – montrealist Feb 04 '17 at 00:29
  • 1
    Yeah, [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/attr) has a definition on it. Specifically, "The attr() CSS function is used to retrieve the value of an attribute of the **selected element** and use it in the style sheet." I did a bit of research and wasn't able to find anything saying that it could be done with a child element. – forrestmid Feb 04 '17 at 00:31
  • just added a workaround version that sets a CSS variable directly on the html element. – Gabriele Petrioli Feb 04 '17 at 01:03

3 Answers3

20

update

A workaround could be to set a CSS variable directly on the html element and use that.

div.foo[data-index] .heading span {
    display: none;
}

div.foo[data-index] .heading::after { 
    content: var(--index);
    color: red;
    border: 1px solid red;
}
<div class="foo" style="--index:'test';" data-index>
  <div>
    <h2 class="heading"><span>title</span></h2>
  </div>
</div>

original

It can't be done currently (as mentioned the attr only works on the current element).

In the future, when attr() can be used to properties besides the content, combined with css variables you could do it like this

div.foo[data-index] .heading span {
  display: none;
}
div.foo[data-index] {
  --data: attr(data-index);
  --index: var(--data);
}
div.foo[data-index] .heading::after {
  content: var(--index);
  color: red;
  border: 1px solid red;
}
<div class="foo" data-index="index">
  <div>
    <h2 class="heading"><span>title</span></h2>
  </div>
</div>
Klesun
  • 12,280
  • 5
  • 59
  • 52
Gabriele Petrioli
  • 191,379
  • 34
  • 261
  • 317
  • Would the variables approach work in latest Chrome (only browser I need to support internally)? It's listed as supported at [caniuse.com](http://caniuse.com/#feat=css-variables). – montrealist Feb 04 '17 at 02:12
  • @montrealist yes it will work with latest Chrome. Here is the updated Pen: http://codepen.io/gpetrioli/pen/KaedNx – Gabriele Petrioli Feb 04 '17 at 16:28
  • 3
    Very elegant hack, if I can say so. – Georgy Ivanov Sep 29 '18 at 20:15
  • 2
    Coming from the future where you can use attr() in css variables. The second snippet still doesn't work because the css-variable actually holds the attr() function which will thus get computed on the current target (.heading). – Kaiido Feb 19 '20 at 08:43
  • @Kaiido good catch. Updated to use `var` to evaluate it on the spot. – Gabriele Petrioli Apr 08 '20 at 12:07
0

One more alternative:

If the value is inheritable, and it is not set on the child directly. Then you could also assign it to parent with attr() and make do the inheritance do its thing.

tikej
  • 179
  • 1
  • 16
-2

You could use this css.

div.foo[data-index]::after, div.foo[data-index]::before { 
    content: attr(data-index);
    color: green;
}

div.foo[data-index] .heading::after { 
    content: attr(data-index);
    border: 1px solid red;
}

Note the use of the :before pseudo element.

I think this is what you were looking for.

Web Dev Guy
  • 1,693
  • 3
  • 18
  • 42