Manipulating the DOM with CSS animations

What would it look like to try to manipulate the DOM with CSS? I don’t mean complex manipulations like you’d do in JavaScript. Let’s think about what sort of simple DOM manipulations could be performed by just using CSS3 animation support. I have a few ideas.

Suppose I have a survey website (“pick your preferences” – maybe this is a dating site or a grocery store isle survey for learning where people want to find their groceries) and I want users to be able to click and drag items from a predetermined list of elements into one of two other lists (call them the “nay” and “yeah” lists). I might have something like this:


div id="options-list"
  ul
    li Option 1
    li Option 2
div id="nay-list"
  ul
div id="yeah-list"
  ul

I’ve put the lists into divs to show the flexibility of the feature I want to demonstrate, not because we couldn’t just attach the ID directly to the lists.

To perform the operation in JavaScript would require getting the selected element followed by an ugly mess of finding the location the mouse is hovering over and then attaching it via ugly DOM code (and remembering to remove it from the old location). You might use jQuery or Scriptaculous to do the job, but suppose we have the added criterion of creating our site without 3rd party libraries? “No dependencies!” your boss says. So you either create your own selector or you dream of a CSS feature like the following.

Let’s suppose CSS4 allows us to perform an “attachment” action on CSS elements at the end of an animation. In addition, let’s also suppose that we can have the user’s mouse act as an attachment point. Let’s look at how we might set this up in our CSS file.


#options-list > ul > li:active {
 /* ... */
 animation-duration: 0s;
 animation-name: li-to-mouse;
}

@keyframes li-to-mouse {
 attach this to mouse;
}

#nay-list:active {
 /* ... */
 animation-name: mouse-to-li;
}

@keyframes mouse-to-li {
 attach mouse to "this > ul";
}

#yeah-list:active {
 /* ... */
 animation-name: mouse-to-li;
}

The “this” here refers to the element that the animation is attached onto. Rather than “this”, we might specify some other DOM element by id, prefixed by “#”. Or perhaps “parent” or “parent parent” so that we can use this on elements higher up the DOM tree.

A thought: Rather than “this” we might also use a keyword such as “active”, which refers to the active element. Using such a keyword would allow for animations on completely different objects to control the attachment and detachment of elements elsewhere. For instance, you might perform a changing of your website layout after the user has made a selection but the elements being animated are different than the one the user should attach to. This would be messy, however, since you don’t know where the user’s mouse is.

Note that the mouse would only be able to contain a single element, unless we had an array syntax such as mouse[0] and mouse[1], which would allow for multiple elements attached to the mouse. The elements on the mouse would retain their CSS attribute values with the exception of the size determinants, such as “position”, “left” “width”, and such, which would need to be temporarily replaced with a static “position:absolute; width:original-calculated-width; height:original-calculated-height;” so as not to appear odd to the user. The display property could be very useful here by making elements disappear before they are added to the mouse.

The “attach” command applies at the end of the animation, not before, so it’s not in the “to” or “from” sections of the keyframes. It doesn’t really make sense to put it in the “from” section since you can’t actually determine the starting DOM location of the element anyways. If you do your animations by percentage, then it might make sense to put it in on of those sections.

Set State

It might be nice to force certain elements into the states “active”, “hover”, etc. to trigger their animations. Suppose we are running an online store and we want the user to see all of the related items on a page. We could group them accordingly, but perhaps they don’t take too well to grouping because our products have a bunch of interchangeable parts. It might be nice to have the elements of each of these products to perform a soft highlighting when we hover over one of them. Again, we turn to CSS animations and our new animation feature: setstate.

Let’s assume we have a camping website that has the following DOM equivalent:


div class="climbing" id="hooks"
  img
div class="outdoors" id="shoes"
  img
div class="hiking" id="compass"
  img
div class="hiking" id="backpack"
  img
div class="climbing" id="rope"
  img
div class="outdoors" id="cargopants"
  img
div id="food"
  img

We would like the following groups of things to be highlighted together when the first thing is highlighted:
“.climbing” => {“.outdoors”, “#food”}, “.hiking” => {“.outdoors”, “#food”}, “.outdoors => {#food”}, “#food”.

That means when I highlight an element of the class “climbing”, the elements of the class “outdoors” and the elements of the id “food” should also be highlighted.

Each of these elements could have a different highlighting effect. Perhaps we want food to be highlighted in blue and outdoors gear in green. The thing they all share in common is their state. We don’t really want to write a new animator for each one of them, so it’d be nice to have all of these work together. We also need to be able to apply this to elements that are identified by both class and/or ID.

There are ways of doing this is JavaScript, but if you try that, you’ll have to do it entirely in JavaScript or figure out how to synchronize it. I’ve read it can be done in ReactJS, but again, let’s avoid the dependencies.

Here’s what we would like to have if we were to do this in CSS:


@keyframes food-color {
 from { background-color: white; }
 to { background-color: blue; }
}

@keyframes outdoors-color {
 setstate #food:hover;
 from { background-color: white; }
 to { background-color: green; }
}

@keyframes climbing-color {
 setstate .outdoors:hover;
 from { background-color: white; }
 to { background-color: red; }
}

@keyframes hiking-color {
 setstate .outdoors:hover;
 from { background-color: white; }
 to { background-color: purple; }
}

#food:hover {
 /* ... */
 animation-duration: 3s;
 animation-name: food-color;
}

.outdoors:hover {
 /* ... */
 animation-duration: 3s;
 animation-name: outdoors-color;
}

.climbing:hover {
 /* ... */
 animation-duration: 3s;
 animation-name: climbing-color;
}

.hiking:hover {
 /* ... */
 animation-duration: 3s;
 animation-name: hiking-color;
}

In a very simple way, we’ve tied together all of our elements’ animations. I’m sure you could imagine lots of more complicated combinations, so I leave that up to you to dream.

Admittedly, “setstate” may not be the ideal keyword. Something like “trigger-state” may be better. I haven’t thought about it long.

Perhaps having “setstate” within the “to” block of the animation would delay the effects of starting the second animation until the first animation had been completed.

Combining “setstate” and “attach”

Naturally, we can combine the effects of the setstate feature with our previously-discussed feature, “attach”. Let’s change website theme here and discuss a gaming site. Suppose you have a website displaying characters from two factions (or it could be an automobile site displaying cars). When the user clicks the image of a one of the members of a faction, a faction-specific form appears, requesting registration information.

Our HTML might contain two forms: A form for one faction and a form for another. This form would send a POST request with the login data, so our entire page could be entirely handled in CSS just using “setstate” and “attach”. The “attach” is required because “setstate” will only get us to transient states and we would like something permanent.

For this, we need merely make our forms invisible and give them CSS attribute values that would position them correctly on the page when they finally appear. We’ll attach them onto an element in the upper-left corner of the screen on top of everything else, having no size, but allowing overflow to appear so as to allow our forms to be attached and act as modal screens. The forms themselves will include their own modal screens and be hidden behind everything else on site so they don’t interfere with user activity while they are inactive.

Our DOM might look something like this:


div
  img class="faction1" id="1"
  img class="faction1" id="2"
div
  img class="faction2" id="1"
  img class="faction2" id="2"
div id="hidden-box" style="display:none;"
  div id="faction1_form"
    div id="cancel"
  div id="faction2_form"
    div id="cancel"
div id="corner-box" style="position:absolute; left:0; top:0; width:0; height:0;"

The element whose ID is “corner-box” is meant to be the attachment location for the forms. In the case that the user cancels, we need to hide our divs again, so we label the hidden div.

Other elements of the forms have been excluded for the sake of clarity of the example.

Let’s now look at the CSS.


@keyframes cancel-anim {
  attach parent to #hidden-box;
  /* Could even make this a fade */
}

#cancel:active {
  animation-duration:0s;
  animation-name: cancel-anim;
}

@keyframes faction1-form-show {
  attach #faction1_form to #corner-box;
}

@keyframes faction2-form-show {
  attach #faction1_form to #corner-box;
}

.faction1:active {
  animation-duration:0s;
  animation-name: faction1-form-show;
}

.faction2:active {
  animation-duration:0s;
  animation-name: faction2-form-show
}

We could do something similar very easily with jQuery, but this CSS looks alot cleaner in my opinion, and we can control the appearance of the transition without the bulk of JavaScript needed to change the style attributes.

Conclusion

We’ve looked at the “attach” and “setstate” keyword possibilities for CSS4 which would give greater control to designers, simplify the coding for animating user interaction with the page, and reduce the need for JavaScript in animations.

The syntax isn’t fleshed out, so it may need tweaking. Element selectors (e.g. “this > ul”) may need to be wrapped in quotes for clarity even if syntactically, it is still possible to parse them correctly.

I’m sure other people have had similar ideas, maybe even the same ideas, and maybe I’m rehashing what I’ve read elsewhere and only recently realized the possible need for it. Having said that, I’m not aware of what browsers are currently doing to implement this sort of this. It seems most news revolves around JS tech.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s