53

I was wondering whether I can combine the calc() function with the attr() function to achieve something like the following:

<div class="content" data-x="1">
    This box should have a width of 100px
</div>

<div class="content" data-x="2">
    This box should have a width of 200px
</div>

<div class="content" data-x="3">
    This box should have a width of 300px
</div>

CSS

.content{
    //Fallback. If no calc is supported, just leave it at 100px
    width: 100px;
}


.content[data-x]{
    // Multiply the width of the element by the factor of data-x
    width: calc(100px * attr(data-x));
}

The draft says it should work, but in my case (Chrome 31.0.1650.63 m and Firefox 25.0.1 ) it doesn't. There are two cases then:

  1. I did it wrong
  2. It is not supported yet

Whats the deal?

Example Fiddle

dippas
  • 58,591
  • 15
  • 114
  • 126
Loupax
  • 4,728
  • 6
  • 41
  • 68

3 Answers3

41

There appears to be a way around it using vars

.content{
    --x: 1;
    width: calc(100px * var(--x));
    background: #f00;
}

[data-x="1"] { --x: 1; }
[data-x="2"] { --x: 2; }
[data-x="3"] { --x: 3; }

/*doesn't look like this works unfortunately
[data-x] { --x: attr(data-x); }
seems to set all the widths to some really large number*/

The commented out section would have been perfect, and this may be the very same reason your idea didn't work, but it seems css doesn't perform the nice automatic casting that you might be used to in javascript ('2' * 3 //=6).
attr() returns a string, not a number, and this can be seen by adding .content:after { content:var(--x) }; nothing gets printed, --x is a number, content accepts strings.

If there is some css function to cast I feel like that would be the key to this problem.


Looks like casting (well, interpreting) will be a thing in CSS4, and it'll be as simple as

.content{
    width: calc(100px * attr(data-x number, 1));
    background: #f00;
}

To date, no browsers support even this experimental spec, but I'll update when it does.

Hashbrown
  • 12,091
  • 8
  • 72
  • 95
  • 1
    This is pretty awesome, even without the attr() support! It can allow definition of some really nice layouts! – Loupax Nov 27 '16 at 17:26
  • FYI, IE does not support CSS variables, and Edge is slowly getting there. Most other browsers support them though! http://caniuse.com/#feat=css-variables – eppsilon Aug 25 '17 at 16:09
  • Looking back at this and my comment that `[it] seems to set all the widths to some really large number`, it got me thinking that if CSS doesn't cast, it might be trying to use the string *as* a numbers. So I tried with [`data-x=""`](http://jsfiddle.net/521x3had/) to be `1`, `` for `2` et cetera, but the widths arent getting set at all. The browsers must have patched to not attempt to use string vars at all. I wonder if this would have worked in the past.. – Hashbrown Feb 28 '19 at 01:31
  • I hope is not too late. For me this is also a valid answer, since attr tents to be dynamic, that still needs JS in order to set the value of that attribute. Then instead, we can set the style inline such as `element.setAttribute('style',\`"--x: ${x}"\`);` Or in other hand modify a Style element with `textContent` in the same way. That would works perfectly in browser which support css vars. – Angel Fraga Parodi Nov 20 '20 at 11:29
  • In addition we can cast the `calc` unit in that way: `calc(1UNIT * (YOUR OPERATIONS) )` ex `calc(1px * 16/9 )` – Angel Fraga Parodi Nov 20 '20 at 11:35
  • can the element itself specify the var? `
    `
    – theonlygusti Jan 13 '23 at 15:35
  • No I don't think any sort of reflection or meta variables/templating will be available through css – Hashbrown Jan 14 '23 at 02:42
36

Right now attr() is not supported by default in any major browser for any attributes other then "content". Read more about it here: https://developer.mozilla.org/en-US/docs/Web/CSS/attr

jede
  • 2,385
  • 2
  • 20
  • 17
  • 1
    Here's an example with "content": http://jsfiddle.net/cQ8F4/2/ – randak Dec 10 '13 at 09:44
  • Yes not supported. You can do it not dynamic with css like this: http://jsfiddle.net/cQ8F4/3/ For the dynamic solution i would use javascript or jQuery: http://jsfiddle.net/cQ8F4/4/ – nkmol Dec 10 '13 at 09:51
  • 1
    As of 19 May 2018 this answer is still mostly correct, but the linked documentation only states that support for `attr()` for properties other than `content` is experimental. I suggest referring to [the Can I use... page](https://caniuse.com/#feat=css3-attr) as it will likely be updated if / when browsers start to support this usage. – Vince May 19 '18 at 06:32
29

At the moment the attr() function isn't working in Chrome.

An almost as nice solution is to use CSS variables:

<!DOCTYPE html>
<html>
  <head>
    <style>
      :root {
         --module-size: 100px;
         --data-x:1; /* Default value */
       }

      .content{
          width: calc(var(--module-size) * var(--data-x));
          border: 1px solid;
      }
    </style>
  </head>
  <body>
    <div class="content" style="--data-x:1">
        This box should have a width of 100px
    </div>

    <div class="content" style="--data-x:2">
        This box should have a width of 200px
    </div>

    <div class="content"  style="--data-x:3">
        This box should have a width of 300px
    </div>
  </body>
</html>
Simon Rigét
  • 2,757
  • 5
  • 30
  • 33