So one of the things I really wanted with Stimulus was I wanted to solve a couple of specific problems or bad patterns that I was seeing in our sprinkles code at Basecamp. One of the first things I wanted to address was the notion of how do you find the elements that you want to mutate or work with? We had a bunch of different styles.
[00:24:07.11] Sometimes we were using a hierarchical approach where you’d say “Oh, give me the parent of the parent of the parent here. I know that the structure of my DOM tree is like this, and I know I want the third parent up.” That’s a pretty brittle way of targeting elements, right? Someone reorganizes things or puts them in a different way and all of a sudden you’re getting the wrong element. So that wasn’t a good pattern.
Another pattern that we’ve used was targeting elements by finding them through CSS classes. So we’d say “Give me the elements that match this CSS class”, which on the one hand seems okay, because a lot of times the CSS classes are explanatory. They say “Oh, this element is about the title of a person, or something”, so when you query for that in the code, it kind of explains what you’re trying to get, but it’s also pretty brittle.
So I didn’t like the ugliness of the code, and I also didn’t like the brittleness of it. But BEM often times – like, you add another –down or a –pad, or whatever it is that you add to a BEM class to ever so slightly tweak it; well, the designers were doing that. Well, again, it broke the code. So that sense of brittleness I wanted to sort of get away from, and that’s where the concept of targets came up.
Basically, Stimulus implores you to only find DOM elements you want to work with through the concept of targets, which is basically just an explicit name that says “This element is going to go by this logical name that belongs to this controller.” And then we can move that name around and it’s not tied to this specific type of element; this could be an input element, it could be a button element, it could be spam element, and as long as it has a data-target that’s of a certain thing, you can always find it… Which not only gives you this sense of clarity around what elements when you read the HTML code are actually used by the dynamic behavior, and in the code itself, it’s very clear when you’re referencing a specific target what that target is and what the purpose is.
[00:27:58.17] And I wanted to get a bit more generic around that, such that we could build up a library of generic behavior that we could apply to any sort of structure, regardless of how the designer wanted the page to look. So I wanted that distance between those two things.
That’s one of the other reasons why I’m not really a big fan of tying the specific DOM layout to the code, as React does and what a lot of component-style development does, because it doesn’t afford you this sense of reuse that you can develop generic concerns and generic aspects of your dynamic behavior that you can tie to any DOM tree.
That was one of the considerations, so that’s the target concept, that’s a prime motivator, and then the other thing – there’s really only two other things. There’s controllers and there’s actions. And actions are quite similar to targets. They are the triggers. A lot of code we had in Basecamp was using explicit event handling, where we would tie an event handler to usually somewhere up the tree, to some parent, and then we would sort of interrogate that event as it bottled up, to see if it was relevant for the behavior we were trying to match, and then do some code.
Usually, what we were doing - we were using the attribute called data behavior, and then we would scan these data behavior attributes as the events bottled up, and then if the attribute was a match, the data behavior was a match, we would trigger the behavior. Well, that does not provide very readable code always, I’d say. Sometimes we would provide a data behavior on a parent element, and then there would be specific DOM elements inside of that parent that would trigger behavior and we would catch these events and we would do something.
[00:31:52.13] The second part of that was that just like data targets, the actions were generic and they could be moved around. So if we currently have an action on, let’s say, a button, and we move that action to a link, the controller could remain unchanged, because the controller didn’t actually care or know what type of element invoked its functions, which again is quite different from when you marry the HTML structure with your component and with your dynamic behavior. Then you’re kind of locked in step and you can’t reuse these things and you can’t move them around. So that seemed like a big advantage.
There’s three basic concepts, and it doesn’t take a long time to learn it. Even without learning it, you can read the code, you can read the HTML structure and you can understand what’s going on, and that’s really all we needed. In fact, I was kind of shocked when I first started extracting this stuff; I was kind of thinking “Oh man, there’s gonna be so much stuff here” and it was the real epiphany of seeing that those three basic concepts - the controllers, the targets and the actions - were enough to extract such a wide body of behavior from Basecamp.