I've always found CSS positioning with both float and position: absolute/relative hard to work with. I want to introduce to you an alternative way borrowed from the World of Warcraft Interface: Anchors.

Anchor

The concept is extremely simple. You can tell where you want the element to be, relative to another. For example, top left of this blog's main content is 10px right relative to the top right of the menu.

<frame name="Content">
  <anchor point="TOP LEFT" relativeTo="Menu" relativePoint="TOP RIGHT" x="10" />
</frame>
  • Allowed points:
       TOP LEFT ,  TOP   , TOP RIGHT
           LEFT , CENTER , RIGHT
    BOTTOM LEFT , BOTTOM , BOTTOM RIGHT
  • Default Values:
    • point, relativePoint: TOP LEFT.
    • relativeTo: Parent of the element.
    • x, y: 0.

Demo

I've made a quick implementation of this behavior in HTML using data-attribute. Check the Javascript tab to see the 35 lines of CoffeeScript that makes the following code work:

<!-- In order to put the sidebar centered at the right of the frame:
  Sidebar.left = Frame.right                                          -->
<div id="sidebar" data-anchor="left, #frame, right"></div>
 
<!-- In order to put the chat at the top right of the screen:
  Chat.topRight = HTML.topRight + {x: -10, y: 10}                       -->
<div id="chat" data-anchor="top right, html, top right, -10, 10"></div>

Limitations

Static

The current code works well for static elements. As there isn't any event for DOM move/resize, the position will not be updated if anything moves 🙁

It might be possible to achieve the same effect by setting properly the position: absolute/relative and top/bottom/right/left CSS properties. I'd be really interested to know if you try to tackle this challenge 🙂

CSS Attribute

It would be more semantically correct to add an anchor CSS attribute instead of an HTML data attribute. However, it's not possible to access the value of a custom CSS property.

#sidebar {
  anchor: bottom left #frame top right 10px 0;
}

Multiple Anchors

World of Warcraft supports multiple anchors. For example if you anchor both the left and right sides, then the width of the element is going to be updated accordingly.

I came across a CSS problem, text-align: justify does not work with only one line.

Justify behavior

The reason is because it has been designed with paragraphs in mind. It justifies all the lines but the last one.

Normal Justify

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat laboris.

Full Justify

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat laboris.

Solution

The solution (given by cam) is to put an extra tag at the end of the paragraph that will be big enough to create a new line. Fortunately, it is possible to do it without affecting the markup using :after. We are going to put an empty tag as big as the line.

.fulljustify {
  text-align: justify;
}
.fulljustify:after {
  content: "";
  display: inline-block;
  width: 100%;
}

Now you can add class="fulljustify" in order to justify your one-line divs 🙂

This is a one-liner. Please justify!

MMO-Champion is a World of Warcraft news website. When a new patch is released, we want to show what has changed in the game (Post Example). An english summary of each spell change is hand written, but we want to show the exact tooltip changes.

Generate Tooltips

First, we use the database application to generate the tooltips in HTML form.

Epic
Binds when equipped
Chest
1669 Armor
+341 Intellect
+512 Stamina
Requires Level 85
Item Level 359
Equip: Improves critical strike rating by 205.
Equip: Increases your mastery rating by 241.
Epic
Binds when equipped
Chest
1669 Armor
+321 Intellect
+512 Stamina
Requires Level 85
Item Level 359
Equip: Improves critical strike rating by 205.
Equip: Increases your mastery rating by 221.

Tokenize

We are not going to do a full HTML diff which is really difficult. Instead, the trick is to parse the HTML and extract only text nodes. We split them by space to generate a token stream.

ChestguardofNature'sFuryBindswhenequippedChest1669Armor+341Intellect+512StaminaRequiresLevel85Equip:Improvescriticalstrikeratingby205.Equip:Increasesyourmasteryratingby241. ChestguardofNature'sFuryBindswhenequippedChest1669Armor+321Intellect+512StaminaRedSocketSocketBonus:+10HasteRatingRequiresLevel85Equip:Improvescriticalstrikeratingby205.Equip:Increasesyourmasteryratingby221.

Diff

Paul Butler SimpleDiff algorithm is used on the two token streams to label each token with old, new or both.

ChestguardofNature'sFuryBindswhenequippedChest1669Armor+341Intellect+512StaminaRequiresLevel85Equip:Improvescriticalstrikeratingby205.Equip:Increasesyourmasteryratingby241. ChestguardofNature'sFuryBindswhenequippedChest1669Armor+321Intellect+512StaminaRedSocketSocketBonus:+10HasteRatingRequiresLevel85Equip:Improvescriticalstrikeratingby205.Equip:Increasesyourmasteryratingby221.

Combine

We parse the HTML again. This time we pretty-print the HTML while traversing it. As we parse text nodes, we add <ins> tags where we need to. We just make sure to group contiguous insertions.

Epic
Binds when equipped
Chest
1669 Armor
+341 Intellect
+512 Stamina
Requires Level 85
Item Level 359
Equip: Improves critical strike rating by 205.
Equip: Increases your mastery rating by 241.
Epic
Binds when equipped
Chest
1669 Armor
+321 Intellect
+512 Stamina
Requires Level 85
Item Level 359
Equip: Improves critical strike rating by 205.
Equip: Increases your mastery rating by 221.

Text Diff

We would also like to get a text-based diff we can insert in a Patch Note. We combine both token streams into one.

Chestguard of Nature's Fury
Binds when equipped
Chest
1669 Armor
+341+321 Intellect
+512 Stamina
Red Socket
Socket Bonus: +10 Haste Rating
Requires Level 85
Equip: Improves critical strike rating by 205.
Equip: Increases your mastery rating by 241.221.

Chestguard of Nature's Fury Binds when equipped Chest 1669 Armor +341+321 Intellect +512 Stamina Red Socket Socket Bonus: +10 Haste Rating Requires Level 85 Equip: Improves critical strike rating by 205. Equip: Increases your mastery rating by 241.221.

However, the result isn't that readable. There is too much noise. We want to show only the information that has changed! In order to do that, we are going to keep only the lines that contain changes.

+341+321 Intellect
Red Socket
Socket Bonus: +10 Haste Rating
Equip: Increases your mastery rating by 241.221.

+341+321 Intellect Red Socket Socket Bonus: +10 Haste Rating Equip: Increases your mastery rating by 241.221.

And it works a lot better 🙂

Conclusion

This approach is working really well with the World of Warcraft tooltips. After many patches and hundreds of changes, I haven't seen any weird behavior. They were common before using this technique. The only downside: non textual changes such as icons or colors will be completly ignored.

At school we've been studying Lie Algebra and we were asked to make a 3D representation of a Lie Group. We chose to represent Julia Set in the Quaternion domain.

We were really impressed to see that it was possible to generate many different forms given such a simple equation. Feel free to share links with some cool settings in the comments 🙂

The demo has been written in WebGL using ThreeJS.

A bit of Mathematics

Note: The following text was written by Felix Abecassis. Sorry, it's in French 🙂

Les ensembles de Julia sont des fractales définies par une suite de récurrence \(z_{n+1} = z^{2}_{n} + r\). Avec \(r\) une constante. Si, pour un \(z_{0}\) fixé, \((z_{n})\) converge alors \(z_{0}\) est dans l'ensemble de Julia.

Les ensembles de Julia peuvent être représentés en 2 dimensions en prenant \(r \in \mathbb{C}\) ou alors en 4 dimensions en prenant \(r \in \mathbb{H}\). \(\mathbb{H}\) étant l'ensemble des quaternions. Les quaternions sont des nombres hypercomplexes de la forme: \[a + i \times b + j \times c + k \times d \ \ \ \ \ \ (a, b, c, d) \in \mathbb{R}\] Avec \(i\), \(j\), \(k\) des racines complexes de l'unité, c'est-à-dire: \(i^{2} = j^{2} = k^{2} = ijk = 1\).

Afin d'obtenir une représentation dans un espace à quatre dimensions on représente chaque point de cet espace par un quaternion. À \(r\) fixé on obtient l'ensemble de Julia correspondant en testant la convergence de
\((z_{n})\) pour tous les points de l'espace.

Dans notre démonstration les coefficients de la constante sont modifiables via les curseurs \(a\), \(b\), \(c\) et \(d\). Pour obtenir une représentation en 3D il est nécessaire de réaliser une coupe de l'ensemble de Julia par un hyperplan. Dans la pratique cela signifie que nous assignons une valeur constante à un même coefficient pour tous les quaternions. On assimile alors les 3 coefficients restants aux axes \(x\), \(y\) et \(z\) d'un repère 3D.

Dans notre démonstration on peut choisir quel coefficient fixer et modifier sa valeur avec le curseur Hyperplane. Une fois un coefficient fixé on fait ensuite varier \(x\), \(y\), et \(z\). Par exemple si on a fixé le coefficient de \(k\) à \(W\), la valeur initiale de la suite est donc: \(z_{0} = x + i \times y + j \times z + k \times W\).

Le point \((x, y, z)\) est alors dans l'ensemble de Julia si après un nombre fixé d'itérations la valeur de la suite n'a pas dépassé un certain seuil. Dans notre démonstration, nous avons choisi d'effectuer 10 itérations.

In my Binary Decision Diagram Library, the performance bottleneck was the uniqueness cache. By reducing the number of cache lookup, it is possible to greatly improve the performances.

Common pattern

In order to test if the key is already in the cache, the usual pattern is to use key in cache. This leads to the following code:

function cachedValue(key) {
  if (key in cache) {
    return cache[key];
  } else {
    var result;
    /* generate result */
    return cache[key] = result;
  }
}

Ideal cache lookup

The expensive operation with a cache is to traverse the data structure in order to find the position where the element should be. In the previous code, there are three independent lookups: key in cache and 2x cache[key].

In order to improve performance, the ideal would be to make that expensive lookup once, and do the test/get/set directly at that position.

function cachedValue(key) {
  var position = cache.lookup(key);
  if (cache.has(position)) {
    return cache.get(position);
  } else {
    var result;
    /* generate result */
    cache.set(position, result);
  }
}

Javascript Workaround

However, there is no API to directly manipulate the lookup and position. We have to find workarounds.

We remark that cache[key] will either return the value or undefined if not present in the cache. The belonging test no longer requires a full lookup, you only have to compare the value with undefined.

function cachedValue(key) {
  var result = cache[key];
  if (result === undefined) {
    /* generate result */
    cache[key] = result;
  }
  return result;
}

There is only one drawback: it is no longer possible to use undefined as a value in your cache.

Without temporary

If the result can be generated inline and your cache will never contains falsy values, you can use a shorter version. It should not impact performance however.

function cachedValue(key) {
  return cache[key] || (cache[key] = /* generate result */);
}

Conclusion

There are still two lookups done in case of a cache miss. If you find a way to factor the code in a way that removes it, I'm really interested in knowing 🙂

At the time of writing, the described technique is faster on all the tested browsers. As performance in browser rapidly evolve, check out the jsPerf entry before you decide to use it in your application.