[Proposal] Element#setAttributes(object)


#1

There is no way in vanilla JS to set multiple attributes on an element without writing each attribute to its own line. Writing them out as individual lines makes scanning the code more difficult to parse, as it appears less clearly grouped. There aren’t sneaky way around this either, like we can do with styles using Object.assign(element.style, {}).

Therefore, I propose setAttribute be extended to support an object, or that a new method like setAttributes be created that allows developers to do this. I believe this would be a welcome and conservative improvement to the creation of DOM within JavaScript.

I wrote a hopefill for such functionality earlier this year, which you can test @ https://github.com/jonathantneal/element-set-attribute-object

FWIW, I would be happy to untie that script from the DOM, should its hopefill behavior inhibit any committees from considering it for inclusion.

Example usage

element.setAttribute({
	id: 'nav-item-1',
	class: 'nav-listitem',
	ariaControls: 'nav-item-1-submenu',
	ariaHaspopup: true,
	ariaExpanded: false
})

#2

I love the idea of this, but have a few thoughts.

  1. Can we not auto squat on a overloaded name? If this took off and worked differently in some sense (maybe an undefined ultimately deletes the attr for example or there is some intelligence in boolean-izing or something) then this will be incompatible and a widely used thing will actually hold it back from getting the name you want. W3C TAG is early drafting a doc about this now that I hope sees the light of day https://docs.google.com/document/d/1u9VgjkPFBgaZE_4xeNCqgF-YReedkTfgXn1WRwmdGFU/edit#.

  2. minor bikeshed but doesn’t s/setAttribute/setAttributes or even mapAttributes() make more sense conceptually?

  3. This is the wrong venue for such a comment but adding more *fill terms that ultimately mean the same thing isn’t going to help add clarity IMO. This means exactly prollyfill which has numerous uses in print, presentation, podcasts, even video. TAG doc currently refers to it as a speculative polyfill for reasons debated there. Any debate/judgements there aside, I think it would be helpful to not add a third option if we can help it.


#3

It’d probably be better not to deal with camelCase at all, and provide attribute names as is by wrapping those with quotes instead.

There was a similar proposal made by me on 2013-11-22 at the www-dom@w3.org mailing list.

My proposal included two parts:

  1. extend the createElement() method with the second parameter that a map object of attributes could be provided with:
var input = document.createElement('input', {
    'type'     : 'email',
    'name'     : 'foo',
    'required' : ''
});
  1. extend the Element.setAttribute() method for setting multiple attributes at once:
element.setAttribute({
    'id'     : 'foo',
    'class'  : 'lorem ipsum dolor',
    'data-i' : 42
});

There is also a proposal (2015-04-17) by Edwin Reynoso here on WICG similar to the first part of my proposal.


#4

I think the key part of this proposal, as with the styles one, is to explain why this has to be built-in and not part of a library. IMO it doesn’t add any new capabilities, just syntax sugar, so could and should be a library.


#5

The pain of any library is that it’s a dependency while built-in features are available out of the box.

There is nothing wrong with syntactic sugar, we have a bunch in JS already, it simplifies development and maintenance and makes code more readable.


#6

Thanks for looking at this, @AshleyScirra. I attempted to do as much, but please let me know how I might improve.


#7

Thanks for looking at this, @briankardell. Please let me know if I can resolve each of your concerns.

Yes, I will update the polyfill to not squat any name. I’m deciding whether it’s better to make it an ES6 export default, or a CommonJS module.exports, or a window.setAttributes global. And then should I move or remove the feature test? Should the rest of the code remain IE8 compatible (like the for loop)?

Yes, I can defer to setAttributes, but it’s worth noting that you could theoretically set 0+ attributes with this method.

I will update the project use the term “speculative polyfill”.


#8

Obviously I’m in support of this, as it mimics my proposal. The only thing I have to say (as @MT did) is to quote the key instead of camelCase.

I think a major thing to point out is that much of this can be done in jQuery by chaining .attr('foo', 'bar') or similar, but chaining can’t be done in vanilla JS.


#9

@jhpratt, thanks for your support.

jQuery’s attr supports objects, and I considered it a cowpath toward my proposal. The only difference now is that I defer to @briankardell’s suggestion that, in vanilla JS, it might be better not to overload setAttribute to handle both the (attributeName, value) and (attributes) usage.


#10

You could use this argument to specify absolutely anything, including things which definitely ought to be in a library. I’m not convinced.


#11

Sure, but I could also take the extreme argument of saying you should have everything in a library, and nothing built in. I think the purpose of this community is to decide where the dividing line is.


#12

Actually I think that would be a good idea if it were possible. However it’s not always possible, because some things are not possible even by building a library on top of existing features. For example nobody can build a library to access the user’s contacts list on Android, because it is simply impossible. For that it is necessary to specify a built-in feature. In other words, in my opinion specifications should focus on making things possible that are currently impossible. Everything else is basically nice-to-haves which a library can provide without obliging browser vendors to support it forever.

This is basically the extensible web manifesto - “the standards process should focus on adding new low-level capabilities to the web platform”.


#13

We can live with that. :wink:


#14

You can ignore my feedback, but I’m sure browser developers will bring up a similar theme as you try to specify this, so I’m trying to help you address this now rather than later after even more work has been done on this.


#15

Nuance matters, please see also The Tao of the Extensible Web. “Focus” there is about prioritization of time and resources. Many high level features of the Web were, historically, blazing entirely new paths only at a very high level and, in the process, it takes years of investment, often based on somewhat questionable speculation about what developers will like, understand or use that we often still get wrong. When we get it wrong, it is all lost. The EWM argues for a different path, including using libraries and speculative polyfills and - things like this, wicg and discourse and libraries to experiment and answer a lot of those questions in a manner more consistent with how most languages work. So, you’re right, browsers shouldn’t jump on a lot of proposals but you could make a strong argument that things like this have been done by libraries and if we can get real data and a plan put together for precisely how it should work, with real uptake, then that situation could turn around very quickly.

In any case, floating these kinds of ideas, providing answers and seeing what works in an effort to improve the platform is pretty much what WICG and discourse in particular is all about.


#16

Everything that can be polyfilled is technically syntactic sugar, yet we add it to the platform anyway, because the alternative is dependency hell.


#17

I really like this proposal, and yes, the cowpaths have been paved, since almost every DOM library includes something like this, there is a very clear need. I would also vote for overloading setAttribute. No need for a separate name, since it’s very easy to disambiguate between cases, and setAttribute({}) today fails with an error, so it wouldn’t break any existing cases.