Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save subtleGradient/759332 to your computer and use it in GitHub Desktop.
Save subtleGradient/759332 to your computer and use it in GitHub Desktop.
What is the best syntax to use for declarative markup in HTML5?

Declarative Component Mapping Syntax Options

Language / Prior Art ‘Valid’ HTML HTML Mapping Example Example with Args
‘Valid’ CSS Selectable CSS Mapping CSS Select CSS Selector w Args
JavaScript MooTools Multiple instance Separate Args new Foo(element); new Bar(element) new Foo(element, {a:"a", bB:"b B", c:{c:1}}); new Bar(element, {d:"d"})
HTML attr Dojo <1.6 NO 1 Element per instance <div nsType=foo> <div nsType=foo a=a bB="b B" c.c=1>
CSS: A Single + Args [nsType] [nsType=Foo][a=a][bB~=b][bB~=B][c.c=1]
data-* Dojo 1.6+ YES 1 Element per instance <div data-ns="Foo"> <div data-ns="Foo" data-ns-a="a" data-ns-b-b="b B" data-ns-c.c="1">
CSS: A Single + Args [data-ns-type] [data-ns-type=Foo][data-ns-a=a][data-ns-bB~=b][data-ns-bB~=B][data-ns-c.c=1]
class & attr Apple iAd NO Multiple instances Combined Args <div class="ad-foo ad-bar"></div> <div class="ad-foo ad-bar" ad-title="MyBarButton"></div>
CSS: A+ Multi + Args .ad-foo, .ad-bar .ad-foo[ad-title], .ad-bar[ad-title]
data-* Hue Behavior YES Multiple instances Combined Args <div data-filters="ns.foo, ns.bar"> <div data-filters="ns.foo, ns.bar" data-a="a" data-b-b="b B" data-c='{"c":1}' data-d="d">
CSS: D All + Args [data-filters] [data-filters][data-a=a][data-bB~=b][data-bB~=B]
data-* & CSS YES Multiple instances Separate Args <div data-ns data-ns.foo data-ns.bar> <div data-ns data-ns.foo="a:a; b-b:b B; c{c:1}" data-ns.bar="d:d">
CSS: B Multi [data-ns] [data-ns.foo], [data-ns.bar]
attr & CSS NO Multiple instances Separate Args <div ns foo bar> <div ns foo="a:a; b-b:b B; c{c:1}" bar="d:d">
CSS: B Multi [ns] [ns][foo], [ns][bar]
data-* YES Multiple instances Separate Args <div data-ns="Foo Bar Baz"> <div data-ns="Foo Bar Baz" value=HTML data-foo.value=FOO data-bar.value=BAR>
CSS: A++ Multi + Multi Args [data-ns] [data-ns~=Foo][value=Frog]:not([data-foo.value]), [data-ns~=Foo][data-foo.value=Frog]
HTML attr NO Multiple instances Separate Args <div ns="Foo Bar Baz"> <div ns="Foo Bar Baz" value=HTML foo.value=FOO bar.value=BAR>
CSS: A++ Multi + Multi Args [ns] [ns~=Foo][value=Frog]:not([foo.value]), [ns~=Foo][foo.value=Frog]
data-* YES Multiple instances Separate Args <div data-ns="Foo, Bar, Baz"> <div data-ns="Foo:value(FOO), Bar:value(BAR), Baz:value(HTML)" value=HTML>
CSS: D- All [data-ns]
data-* YES Multiple instances Separate Args <div data-ns="Foo, Bar, Baz"> <div data-ns="Foo[value=FOO], Bar[value=BAR], Baz[value=HTML]" value=HTML>
CSS: D- All [data-ns]
<meta data-is=DataStore.JSON name=jsonStore content=dataItems.json>
<meta data-is=Model.Forest name=continentModel
content=jsonStore data-query=type:continent
data-root-id=continentRoot data-root-label=Continents data-children-attrs=children
>
<table data-is=DataGrid.Tree id=grid data-model=continentModel>
<thead>
<tr>
<th data-field=name style=width:auto>Name
<th data-field=population style=width:auto>Population
<th data-field=timezone style=width:auto>Timezone
</thead>
</table>
<span dojoType="dojo.data.ItemFileWriteStore"
jsId="jsonStore" data="dataItems"></span>
<div dojoType="dijit.tree.ForestStoreModel" jsId="continentModel"
store="jsonStore" query="{type:'continent'}"
rootId="continentRoot" rootLabel="Continents" childrenAttrs="children"></div>
<table jsid="grid" dojoType="dojox.grid.TreeGrid" class="grid" treeModel="continentModel">
<thead>
<tr>
<th field="name" width="auto">Name</th>
<th field="population" width="auto">Population</th>
<th field="timezone" width="auto">Timezone</th>
</tr>
</thead>
</table>
<a href="http://archive.dojotoolkit.org/nightly/dojotoolkit/dojox/grid/tests/test_treegrid_model.html" title="dojox.grid.TreeGrid Model-based test">Example from Dojo Toolkit dojox.grid.TreeGrid Model-based test</a>
@subtleGradient
Copy link
Author

Taking a step back, there are multiple different concepts going on here.

Content-based Webpage. Markup your content with tags. Add meta data using attributes. Add style using CSS. Add behaviors using a blob of domready code or a behavior sheet. UPDATED: See https://gist.github.com/765730 for the thread about Behavior Sheets.

<button class="isFancy" data-tooltip="This will make winning happen for you!">Click for great win</button>
/* CSS */ .isFancy:hover {background:url(prancing-ponies.gif);}
/* BE */ .isFancy:hover {tooltip:attr(data-tooltip)}

Data-backed Webapp. Expose content via REST / JSON. Define your app layout and widgets using declarative syntax (HTML + special attributes) or function syntax (JS). e.g.

<span id="addressBookModel" myType="DataModel" type="text/json" source="addressbook.json"></span>
<input type=search store="addressBookModel" myType="Filter" placeholder="Filter this list!">
<table id="addressBookView" dataStore="addressBookModel">
    <thead>
        <tr field="firstName">First Name</tr>
        <tr field="lastName">Last Name</tr>
        <tr field="iq">Awesomeness Quotient</tr>
    </thead>
</table>
<button something-goes-here plural="Delete selected Emails">Delete this Email</button>
<button something-goes-here>New Email</button>

I think a lot of the differences in syntax options comes from the very different use cases that each of us are planning to use this stuff for.

It's important to recognize up front if you're building a website or a webapp and then choose the programming patterns that make the most sense for that specific project. Trying to mash a webapp into the programming model of a website will be very bad, and vice-versa. Also consider the implications of using data-backed widgets inside of page-based websites.

Don't try to make every project conform to the programming patterns that you're already comfortable with.
Just imagine trying to build a realtime system monitoring application using website-style programming patterns.

I'm not sure it even makes sense to program webapps and websites using the same exact programming patterns.

@svicalifornia
Copy link

It's not BSS, it's CBS (Cascading Behavior Sheets).

I really like the Data-backed Webapp concept and proposed syntax. That's what I intended to do in Maui. (And nice use of element IDs, by the way. /inside joke ;)

@cpojer
Copy link

cpojer commented Jan 2, 2011

Just one note, if we use another file format that externally defines the behavior it kind of defeats the purpose of having this kind of functionality. The options must be tied to the HTML it comes with. Otherwise for highly dynamic content it might create yet another request (if you have several files with behaviors defined) and it isn't much better than just going with the "large domready block".

I just wanted to highlight that any kind of implementation must also be able to work well with updated content, not just on domready. So if you have a selector to retrieve all elements that need a behavior, you need to filter out the elements that have a behavior applied already. Something like [data-behavior]:not([data-behavior-attached]). Just sayin' for the record.

@digitarald
Copy link

I'd allow 2 styles (<ul data-slideshow-theme="golden" data-slideshow='{"src":"/data/images.json","speed":"JSON"}'>]), for the sake of controllable CSS ([data-slideshow-theme="golden"]) but also to allow servers-side generation of interchangeable data. Both sources would be merged into one option object.

This caters to both concepts and can be applied on content and app-based structures.

I see some edge cases that don't work after the element-2-instance pattern, requiring event delegation or other pattern. That's why I like the onAttach, onDetach approach I saw. We can also make the callback very customizable, allowing simple concepts like events ('ajax': { delegate: true, click: function(event, options) { stuff(); } }) but also classes (slideshow: App.Slideshow or provided via dependency injection slideshow: 'App/Slideshow')

@rolfnl
Copy link

rolfnl commented Jan 3, 2011

Previously I was using json as the attribute value for settings of some behavior like an fx because I thought it was the most logical approach. Later I dropped it and started using lots of data- attributes describing each param separately. Reason I dropped the json was because I read somewhere in the mootools docs/blog that such an approach (dunno in what class, maybe some Fx or Validator class) was going to be dropped as it wasn't valid html..?!

UPDATE: ah yes, here it's in Aaron's Form.Validator: http://mootools.net/docs/more/Forms/Form.Validator
"[...] You can use a property called "validatorProps" and pass in Json values if you like, but this is not valid XHTML. This is deprecated but will continue to be supported."

However, I sometimes prefix the data-value attributes for a filter like:
data-filter="video" data-video-width="9" data-video-source="foo.mp4" ...
else you don't know which attribute applies to the correct filter (e.g. when you apply two). With json you can put one object into 1 data- attribute... so... too many thoughts

http://jsfiddle.net/SubtleGradient/DWcgY/embedded/result,html,css/
Been using things like that as well.. would you consider this hackish/ugly? I kind of like it, but it can't work without js for IE7 and such

@subtleGradient
Copy link
Author

@digitarald & @Rolf-nl

I believe that using both separate attributes as well as separate options attributes are the way to go, but strictly separated by what they're for.
Use attributes when you are adding meta data about the tag and its contents. Use an options attribute when you are passing specific options to some specific behavior.

e.g.

<img src="Cheddar.png" alt="Pungent Orange Cheddar Cheese"
    data-type="food-dairy-cheese-cheddar" data-smell="pungent" data-color="orange"
    data-behavior="Dance Sing"
    data-behavior-dance='{type:"waltz", speed:"slow"}'
    data-behavior-sing='{type:"show tune", speed:"fast"}'
/>

or with minimal HTML and CSS-style syntax…

<img src=Cheddar.png alt="Pungent Orange Cheddar Cheese"
    data-type=food-dairy-cheese-cheddar data-smell=pungent data-color=orange
    data-behavior="Dance Sing"
    data-behavior-dance="type:waltz; speed:slow"
    data-behavior-sing="type:show tune; speed:fast;"
>

Has many attributes.
Has many behaviors.
Each Behavior has up to one options attribute.

And for extra credit… CSS!

[data-type|=food-dairy] { background-image: url(cow.png) }
[data-type|=food-dairy][data-behavior~=Dance]{ background-image: url(dancing-cow.png) }

@subtleGradient
Copy link
Author

Maybe we need separate terminology for behaviors that effect how you interact with it vs behaviors that effect what it is.

e.g. Attributes on a DataGrid is really meta data about what it is, so even though that effects how you interact with it, it is more suitable as separate attributes since it is describing what that element is.

@subtleGradient
Copy link
Author

And then with a separate behavior sheet.

UPDATED: See https://gist.github.com/765730 for the thread about Behavior Sheets.

@ryanflorence
Copy link

Not sure what the point of the behavior sheet is, while interesting, seems no different than just cranking out some slick selectors and calling element methods on the results (like we sort of did on goconnect).

@digitarald
Copy link

Maybe I am not thinking abstract enough, but I saw the mapping for data-[class]="[options-table]" and data-[class]-[options-key]="[options-value]" … which than maps to our classy new Class(element, options).

class could be either mapped in a sheet, auto-detected from the name or, just a thought, referenced in the HTML (<div data-require="table=App.Core.Table; zebra=App.More.Zebra" data-table="{…}" data-zebra-selectable="true">). The latter provides a nice way to avoid all kind of sheets, since all references are kept in one place.

I don't feel comfortable with the sheets, they need to be generic enough to be external and than would need to define resources shared within the whole application. Having them inline as attributes

@rolfnl
Copy link

rolfnl commented Jan 4, 2011

Yes, my thoughts too. Or is w3c coming with something like a behavior sheet recommendation (I don't keep up with the specs/updates)? It's a nice idea, but I wouldn't use it. JSON for options is probably the easiest way to go as it already works and it's relatively easy to implement. I like Ryan's idea in his Element.Filters that the choice of parser is optional.
I'd like to hear cpojer's thoughts as well.

@ryan, goconnect is goconnect.org?

@digitarald
Copy link

Thomas, I'd like to see more practical examples than smelling and singing cheese ;)

@rolfnl
Copy link

rolfnl commented Jan 4, 2011

@digitarald++ for the mapping, which feels like it should be. Though I also like it when you can describe your custom behavior/filter that might instantiate more than one classes or a singleton that does a bunch of stuff. Kind of like Ryan's Element.Filters

@rolfnl
Copy link

rolfnl commented Jan 4, 2011

Thomas, how can you sing a fast show tune and dance a slow waltz at the same time? or did you forgot the chain option? Plus, I wouldn't mix low culture hamburger cheddar with high culture waltz stuff.. (naah, on 2nd thought, waltz is simple == low culture.. mix is good)

@subtleGradient
Copy link
Author

Dancing and Singing have nothing to do with what that element IS but is more about how that thing behaves.

e.g.

Bob is a Person and acts like a Clown

<div name=bob is=Person age=37 height="5feet 3in" acts-like="Clown" clown='{"nose-color":"Red", "nose-sound":"honk"}'>

May map to something like…

new Person(element, { name:"bob", age:37, height:"5feet 3in" });
new Clown(element, { name:"bob", age:37, height:"5feet 3in", "nose-color":"Red", "nose-sound":"honk" });

Edgar is a Cow and acts like a Person

<div name=edgar is=Cow age=37 acts-like="Person" clown='{"height":"5feet 3in"}'>

May map to something like…

new Cow(element, { name:"edgar", age:37 });
new Person(element, { name:"edgar", age:37, height:"5feet 3in" });

Ralph is a Person and acts like a Ninja

etc…

So all the attributes of an element could merge in all the specific attributes of that 'acts-like' thing and give you a single object. But all that is kindof OT for this thread. I think I have solved the original question that this thread posed, that is, what is the best syntax to use for declarative markup.

@subtleGradient
Copy link
Author

@digitarald Mapping of alias to specific JS namespace should happen in JS, not HTML. e.g. SubtleBehavior.defineAlias({zebra: My.NameSpace.Of.Doom.Zembra, foo:Foo, bar:MySuperBar})

@subtleGradient
Copy link
Author

See https://gist.github.com/765730 for the thread about Behavior Sheets.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment