Closed trusktr closed 6 years ago
(Updated the description in the previous comment)
@trusktr: Thanks for the invite! This is interesting.
I think it's really starting to try and move us to looking at individual HTML elements as actual class definitions from the OOP world. As such, this response might turn into something a bit longer than expected or requested. :)
@joshbruce Seems like you like is="" more than has="". I'm curious to know more on that. Mind commenting on that over at #727, where I've published an implementation of element-behaviors and has=""?
For me it's not a question of "more than" - it's a question of language (communication not programming) and intent.
is
in my mind is analogous to class inheritance from class-based languages like PHP, Swift, and so on; therefore, the following would be equivalent representations of the same concept (please forget JS for the purposes of this reply):
<div is="my-custom-block-element"></div>
class Div {
// block element definitions and properties
}
class MyCustomBlockElement extends Div {
// my overrides and extensions
}
The has
proposal seems more like traits (from PHP) or multiple inheritance, which has its own issues and has been, and I believe continues to be, debated. There's a difference between something being an extension of something else and something having the qualities defined somewhere else. (@colinalford - might find this interesting.)
So, the following would also be analogous - using PHP traits - not JS.
<div has="bar baz"></div>
trait Bar {}
trait Baz {}
class Div {
has Bar, Baz;
}
I like to write code that is explicit in its intent; I can figure out what's going on by reading it...that's why I still mark things as public
despite not needing to in languages that have those keywords and concepts.
It seems like we are trying to do for script
what class
did for style
. However, CSS doesn't have the crazy inheritance that JS can provide.
So, why not both?
<div is="my-cool-contact-card" has="customHover customClick" class="rounded blue"></div>
This is much easier to read and doesn't assume the existence of custom elements support on the client.
<my-cool-contact-card has="customHover customClick" class="rounded blue"></div>
And, it removes the clutter of literal JS and CSS in the element.
In all honesty, I like is
from the perspective that it's a valid selector and allows me to have custom named HTML elements today - while the debate and conversation of how that looks continues on. Therefore, I can style and make my intent explicit without using class
as a junk-drawer for selection...since we don't seem to like taking advantage of the data-*
thing.
I like has
from the perspective that it could, in JS, grab the prototypes and implementations without having to run the selectors and finds...if I'm understanding correctly. (I'm finding myself using traits more and more in PHP as well; however, it's usually only when I discover moments when I just want the method - aka banana - and not the gorilla that comes with it...move method out of superclass and into a trait that is then used by the superclass.)
Let's pretend id
, class
, and style
only serve to inform the browser of our aesthetic intent - id
there is only one in this document, class
it has n number of stylistic traits, and style
this one specifically has these styles as well (most likely generated using a template engine or JS).
It's possible that is
, has
, and script
could do the same thing for interactivity (behavior) - is
I have all the interactive characteristics of this other thing (button), has
I have these additional interactive characteristics (keyboard shortcuts and so on), and script
this instance in particular also does these things.
Could be crazy talk, but that's how my mind is mapping these things - taking on the voice of the element itself for a second:
I've been toying with the notion of HTML as a language being less about the web and more about giving authors the ability to describe their intent - otherwise, we would only need div
and span
, because everything else could be accomplished in CSS + JS (or other scripting languages)...p
is just a div
with expressed intent kinda thing.
There's no interest in this, but custom attributes (with callbacks) might solve this. JavaScript mixins might make this easier going forward too.
@trusktr: Sorry. I think you and I might be on the same page though at a higher level...could be wrong. Shameless plug: https://m.8fold.pub/on-constraints-internet-bandwidth-eab05c20e218 - see highlight midway down the page.
@annevk What is the current alternative to the ideas proposed here. These would assist us with some of the issues we're encountering. But yet, is
keeps getting shot down — along with similar ideas.
@stefsullrew custom attributes are discussed in https://github.com/whatwg/html/issues/2271. Not sure if there's a concrete API proposal yet, don't think so. Not sure what you mean about is
. Apart from Apple everyone is on board there...
the "has" attribute idea is clever but not necessary when ES7 lands. In ES7 we would be able to easily define traits on a class using a custom @traits class-level decorator. Problem solved at the class level.
C'mon, keeps the html clean and lean. I think you've seen the abuse that can occur in Angular just because of an ng-if....Oh gosh!
@annevk @Nashorn Hey guys, you both mentioned mixins
and @traits
at the JS level, but besides the fact that class-factory mixins have existed for a long time, there's issues with them being defined on a class in JS:
<lume-node
has="obj-model phong-material transform"
color="pink"
shininess="30"
obj="path/to/model.obj"
position="10 20 30"
scale="10 10 10"
></lume-node>
Where the obj-model
feature/trait/behavior is an instance that reacts to the obj
attribute value, phong-material
is a feature/trait/behavior that observes the color
and shininess
attributes, and transform
is a feature/trait/behavior that observes position
and rotation
, etc. Maybe the user doesn't want to load a 3D obj model, but rather use a geometry primitive:
<lume-node
has="box-geometry phong-material transform"
color="pink"
shininess="30"
size="20 20 20"
position="10 20 30"
scale="10 10 10"
></lume-node>
has="foo bar"
to has="foo"
in order to remove the trait/behavior/feature. F.e., add a special-looking material when the object is selected:
<lume-node
has="box-geometry phong-material transform selection-material"
color="pink"
selection-style="zebra"
selection-color="teal"
shininess="30"
size="20 20 20"
position="10 20 30"
scale="10 10 10"
></lume-node>
In other words, the declarative power of being able to add/remove JS features to/from elements, declaratively, is really nice.
@annevk
There's no interest in this
How do you gauge that?
(@joshbruce I liked your ideas in your above reply 👍)
Please use inclusive language: https://notapattern.net/2014/10/14/ways-men-in-tech-are-unintentionally-sexist/.
As for determining interest, I wrote that comment during a face-to-face meeting, minutes available at https://github.com/w3c/webcomponents/issues/713#issuecomment-371703290.
Please use inclusive language: https://notapattern.net/2014/10/14/ways-men-in-tech-are-unintentionally-sexist/.
Sorry. Based on @Nashorn's GitHub profile, I can not tell whether Nashorn is a guy or gal. I should've used "guys" only if I'd known you both were under the "he/him" pronouns.
As for determining interest, I wrote that comment during a face-to-face meeting, minutes available at #713 (comment).
Can you quickly summarize that part?
Now that you have examples of how I'm using element-behaviors
in the above non-imaginary examples, what do you think about it?
Maybe there is not any interest because it simply doesn't exist and people aren't aware of it?
As far as I know, A-Frame is the only big library out there that has anything similar to this, they call it "entity-components" instead of "element-behaviors", but they are the same things. Theirs is only slightly different from behaviors: theirs is more like custom-attributes, and their custom attribute values accept style
-like key-value pairs as input into the components (it maps to a POJO inside the component).
As far as I can tell, people who use A-Frame love it.
In my case, element-behaviors is not tied to a specific library (f.e. A-Frame's entity-components are specific to A-Frame, not usable on any elements), and behaviors can be used on any elements.
SIDENOTE I was debating renaming "element-behaviors" to "element-traits" or "element-features" or "element-characteristics", because "behaviors" implies that each feature is associated with an action or verb: a behavior is a manner in which something acts by means of verbs. In contrast a feature or trait or characteristic can be merely the existence adjectives or nouns (f.e. a data object from which to read from and that doesn't execute any logic). Thoughts?
I am already forseeing that I will mostly use my element-behaviors
instead of Custom Elements in most cases, as it is simply just more flexible and easier to work with, and doesn't come with a load of compiler/transpiler/backwards-compatibility nightmares.
This is what it looks like to define a new Custom Element with a set of default behaviors in Lume:
import Node from './Node.js'
export default
class ObjModel extends Node {
static elementName = 'lume-obj-model'
static defaultBehaviors = [ 'obj-model' ]
}
That defines a <lume-obj-model>
Custom Element that has a default obj-model
behavior, which is short for <lume-node has="obj-model">
.
Here's how we'd use the obj-model
behavior without the Custom Element:
<!-- a lume-node element with an obj-model behavior. The obj-model
behavior observes the obj and mtl attributes. -->
<lume-node id="model"
rotation="0 40 0"
align="0.5 0.5 0"
size="0 0 0"
scale="200 200 200"
has="obj-model"
obj="./models/spaceship/ship.obj"
mtl="./models/spaceship/ship.mtl"
>
</lume-node>
And with the simple Custom Element definition above, here's how we'd use the lume-obj-model
which includes the behavior:
<!-- alternatively, the lume-obj-model is a node element that
implicitly has an obj-model behavior. We've omitted the mtl, so the
model will have a solid color material: -->
<lume-obj-model id="model2"
rotation="0 20 0"
align="0.5 0.5 0"
size="0 0 0"
scale="200 200 200"
obj="./models/spaceship/ship.obj"
>
</lume-obj-model>
This opens up a door for composability of functionalities on an element; ability to compose JS features dynamically (add or remove them any time).
Imagine this,
<red-monster ...>
then a user gets close enough to the monster, so we make it have rage:
<red-monster has="rage-mode" ...>
Then the user gets far enough away, so we remove the rage:
<red-monster ...>
That monster example is more behavioral, but you can see in my previous examples how materials and obj-models can be more traits/characteristics and not implying verbs.
(SIDENOTE I want to rename "element-behaviors" to "element-traits" or "element-features" or "element-characteristic", because "behaviors" implies that each feature
Agreed.
Also, thanks for the compliment on the article @trusktr. Gonna leave an updated link as I begin to retool my digital world, which is including changing the 8fold domains and figure out where the writing should live for real.
Of course, you coulda just been talking about the comment not the long-winded article. In either case, I'm tired and needed to update the link anyway. :)
Your last couple of comments reminds me of what we used to do in Flash-based games. What is the defined line between action and state?
@trusktr - the females are cows and young ones are calfs
What is the defined line between action and state?
Exactly. My thought is that something like "traits/characteristics/features" must include at least state, while "behavior" seems to want to include more.
This is a really great idea and a very constructive feedback.
@annevk Please reopen it and do not ignore it.
Based on #509, #662, and #663, I've released a standalone implementation of "Element Behaviors", published to NPM as element-behaviors.
I currently use Element Behaviors in Lume to implement various rendering behaviors for custom 3D elements, and I plan to use element behaviors to define mixable functionalities useful in interactive apps like 3D games or decorated web sites.
Additionally, I've implemented a "default behavior" system in Lume, on top of element-behaviors (not published as standalone yet) that makes it easy to define Custom Element classes that ship with default sets of behaviors, as well as makes it easy to add/remove behaviors in the same way that it is easy to add/remove classes with
el.classList.add/remove
.What is it?
For all intents and purposes, this is an "entity-component" system, but in this case called "element behaviors" which avoids conflict with widely-used term "component" in the web these days.
Element behaviors allow us to add any number of functionalities ("behaviors") to any number of elements, using interfaces similar to Custom Elements, and a new
has=""
attribute. See the README for more details, examples, and API.This is an alternative to the
is=""
attribute, where instead of using anis-a
design pattern we're using ahas-a
design pattern which is less complicated and more flexible.Basic Example
The first example from the README is on codepen: https://codepen.io/trusktr/pen/3abb3ab634d1171954fb00fbc188b9e7.
Not shown in that demo, also apply any number of behaviors to any number of elements:
mixin-like "element behaviors", without extending builtins
The idea uses a
has=""
attribute to apply more than one behavior/functionality to an element (is=""
can only apply a single behavior/functionality to an element), using lifecycle methods similar to Custom Elements.For example:
In a variety of cases, this has advantages over Custom Elements (with and without
is=""
and):table/tr
problem)is=""
!)augment existing HTML applications
Suppose we have an HTML app:
With Custom Elements, we could enhance it like the following, but it requires changing the names of the elements, which can cause conflicts with CSS and JS code:
With Element Behaviors, we can more easily add the functionality without modifying existing markup, CSS, or JS:
Of course, the implementation of a behavior that doesn't extend from the target element might require a different sort of implementation than a Custom Element that extends from the target element. Nothings perfect, and one way may be a little more work than the other way depending on scenario. But overall, the utility is improved (f.e. mix behaviors together, and some behaviors might even team up to do certain things when combined together on a single element), avoid strange parser problems, stay out of the way of existing CSS/JS, and more.