Skip to content

Conversation

@ConnorAtherton
Copy link
Contributor

@ConnorAtherton ConnorAtherton commented Jun 25, 2016

Summary

Allows users to get and set the value of CSS custom properties on a DOM element through the .css method.

It works exactly like the example in the issue, which I am copying here for reference.

<div style="--color: red; color: var(--color)">text</div>
<script>
$('div').css('--color') ; // Returns 'red'
$('div').css('--color' ,'blue') ; // Sets to 'blue'

Fixes #3144

Checklist

src/css.js Outdated
}

function isCustomProperty( value ) {
return value.match( /--(.*)/ );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few remarks:

  1. This will match things like a--b; you should check that the string starts with --, not just contains it.
  2. Why did you introduce the group (.*)? You don't use it anywhere.
  3. It's better to change that to /^--.*/.test( value )` as you only care about it matching or not and what you wrote will return the array with matches; the browser has better chances of optimising the former.

@mgol
Copy link
Member

mgol commented Jun 26, 2016

This PR adds 47 bytes (minified+gzipped). That's not a negligible amount. We should explore whether it wouldn't be better to just use the setProperty method everywhere. It might affect performance so we should do some performance testing. https://jsperf.com/ is offline; we can use benchmark.js, though.

@mgol
Copy link
Member

mgol commented Jun 26, 2016

@ConnorAtherton Would you like to try to run some performance tests? You can see my example template in one of my PRs.

@ConnorAtherton
Copy link
Contributor Author

@mgol Thanks for looking at this. All your comments are valid and I will rework my additions based on your feedback. Thanks for providing the link to your benchmarks in your other PR, I will update this thread with similar metrics in the coming days. I like the idea of using setProperty everywhere but will hold off on that decision until we have some metrics to consider.

@ConnorAtherton
Copy link
Contributor Author

Following on from my previous comment, I have benchmarked both methods for adding styles to a DOM node. Here is my benchmark (I think this would be a useful template to add to the repo for future PRs) followed by the results:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>A page for perf tests</title>
    <style>
        /**
         * Any styles for perf start state
         */
    </style>
</head>
<body>
    <div id="test-div"></div>
    <script src="http://code.jquery.com/jquery-git.js"></script>
    <script>
        var $j = jQuery.noConflict();
    </script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.13.1/lodash.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/benchmark/2.1.0/benchmark.js"></script>
    <script src="../dist/jquery.js"></script>
    <script>
        var suite = new Benchmark.Suite;

        var currentVersion = $j('#test-div');
        var newVersion = $('#test-div');

        // add tests
        suite
            .add('Adding styles using object assignment', function() {
                currentVersion.css('--color', 'blue');
                currentVersion.css('--color');
            })
            .add('Adding styles using setProperty', function() {
                newVersion.css('--color', 'blue');
                newVersion.css('--color');
            })
            // add listeners
            .on('cycle', function(event) {
                console.log(String(event.target));
            })
            .on('complete', function() {
                // So we can inspect the results ourselves in the console
                window.s = this;
                console.log('Fastest is ' + this.filter('fastest').map('name'));
            })
            // run async
            .run({'async': true});
    </script>
</body>
</html>

Chrome 51:

Adding styles using object assignment x 52,588 ops/sec ±3.13% (52 runs sampled)
Adding styles using setProperty x 38,907 ops/sec ±2.29% (54 runs sampled)
Fastest is Adding styles using object assignment

Firefox 49:

Adding styles using object assignment x 94,883 ops/sec ±5.27% (16 runs sampled)
Adding styles using setProperty x 15,532 ops/sec ±10.38% (15 runs sampled)
Fastest is Adding styles using object assignment

Safari 9:

Adding styles using object assignment x 84,248 ops/sec ±4.56% (41 runs sampled) 
Adding styles using setProperty x 69,650 ops/sec ±11.84% (42 runs sampled) 
Fastest is Adding styles using object assignment 

IE WIP

@dmethvin
Copy link
Member

dmethvin commented Jul 5, 2016

It's definitely slower, especially in Firefox for some reason, but I don't think it is too slow. Focusing on Firefox, 15,000 ops per second is one op in 66 microseconds. How many CSS properties would typical code set in a 16ms frame? Also, setting properties often causes reflow which would be a lot more expensive than the act of setting the property.

@markelog
Copy link
Member

markelog commented Jul 8, 2016

Also, setting properties often causes reflow which would be a lot more expensive than the act of setting the property.

Totally, should be fine

src/css.js Outdated
}

function isCustomProperty( value ) {
return /^--.*/.test( value );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually put definitions of regexes in the beginning of the module with r prefix, like -

rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,

Could you maybe do the same and also get rid of the isCustomProperty function and just use /^--.*/.test(...) directly?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, but the regex might as well be /^--/.

@mgol
Copy link
Member

mgol commented Jul 8, 2016

Thanks for doing the benchmarks, @ConnorAtherton! The numbers look good so I'd go with just always using setProperty/getProperty.

As an added bonus, we might get https://bugs.jquery.com/ticket/14394 fixed as a side-effect. :-) You could use a test from my PR (that had to be reverted later):
https://github.com/jquery/jquery/pull/1385/files, I think it should be passing in IE 9+ although I'm not 100% sure. That's not a blocker here, though, you might as well not do this part now, we could later check if it works and add the test if it passes.

src/css.js Outdated
style[ name ] = value;
}
if ( customProperty ) {
style.setProperty( name, value );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wondering, why we can't always use setProperty?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, i see, i missed discussion :)

Copy link
Contributor Author

@ConnorAtherton ConnorAtherton Jul 22, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we can use setProperty here because properties like maxWidth and marginRight don't work. I've delayed working on this because I keep running into this issue but according to the spec, and please correct me if I'm reading this wrong, camel-cased properties aren't transformed into their CSS equivalents, only converted to ASCII lowercase. Every test that fails when I switch to setProperty for every property is precisely because of this.

Sources:

@markelog
Copy link
Member

markelog commented Jul 8, 2016

As an added bonus, we might get https://bugs.jquery.com/ticket/14394 fixed as a side-effect. :-) You could use a test from my PR (that had to be reverted later):

That would be cool! But can we do this in separate PR?

@ConnorAtherton
Copy link
Contributor Author

Hey all, thanks for the feedback. Apologies for the delay with this, I'm currently on vacation. Going to work on this more today.

@ConnorAtherton
Copy link
Contributor Author

Seeing this behaviour on chrome when trying to fetch properties from the style object

screen shot 2016-07-13 at 11 54 58

@timmywil timmywil added this to the 3.2.0 milestone Jul 13, 2016
@mgol
Copy link
Member

mgol commented Jul 19, 2016

@ConnorAtherton That's expected. Regular CSS properties can be read or set using regular property access but CSS variables can't, you have to use setProperty/getPropertyValue.

Do you know what needs to be done to finish this PR?

test/unit/css.js Outdated
assert.equal( jQuery( "#customPropertyTest" ).css( "--color" ), "red", "Modified CSS custom property using object: Assert value is right" );

jQuery( "#customPropertyTest" ).css( { "--theme-dark": "purple" } );
assert.equal( jQuery( "#customPropertyTest" ).css( "--theme-dark" ), "purple", "Modified CSS custom property with dashed name: Assert value is right" );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A mixed-case custom property test would also be welcome.

@ConnorAtherton
Copy link
Contributor Author

@mgol Status update: I haven't been able to remove setProperty (see inline comment) but I have applied the test feedback and removed some stuff. All tests are passing locally for me, although I would like to check more browser coverage.

Thanks for the feedback everyone.

@markelog
Copy link
Member

What about #3199 (comment) and #3199 (comment)?

@mgol
Copy link
Member

mgol commented Oct 31, 2016

@ConnorAtherton Did you look at @markelog's comments?

@mgol
Copy link
Member

mgol commented Dec 5, 2016

@ConnorAtherton A friendly ping. :)

@mgol
Copy link
Member

mgol commented Dec 14, 2016

@ConnorAtherton We'd like to get it into 3.2.0. If you don't have time to finish please let us know so we'll tweak it & land it ourselves.

@mgol mgol self-assigned this Feb 13, 2017
mgol pushed a commit to mgol/jquery that referenced this pull request Mar 1, 2017
@mgol mgol mentioned this pull request Mar 1, 2017
4 tasks
@mgol
Copy link
Member

mgol commented Mar 1, 2017

I finished the PR as #3557.

@mgol
Copy link
Member

mgol commented Mar 1, 2017

@ConnorAtherton I've finished it up for you in #3557. You'll still be credited as the commit author. Thanks for all the work that went into this PR!

mgol pushed a commit to mgol/jquery that referenced this pull request Mar 6, 2017
mgol pushed a commit to mgol/jquery that referenced this pull request Mar 7, 2017
mgol pushed a commit to mgol/jquery that referenced this pull request Mar 7, 2017
@mgol mgol closed this in #3557 Mar 7, 2017
@lock lock bot locked as resolved and limited conversation to collaborators Jan 18, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Request: support for CSS custom properties

9 participants