Tag Archives: CSS

Thinking Inside The Box With Vanilla JavaScript

During the past four or five years of blogging regularly and doing research for other writing projects, I’ve come across probably thousands of articles on JavaScript.

To me, it seems that a big chunk of these articles can be divided into two very general categories:

  1. jQuery;
  2. Theory and concept articles focused on things like IIFEsclosures and design patterns.

Yes, I’ve likely stumbled upon a ton of other articles that don’t fall into either of these categories or that are more specific. But somehow it feels that most of the ones that really get pushed in the community fall under one of the two categories above.

I think those articles are great, and I hope we see more of them. But sometimes the simplest JavaScript features are sitting right under our noses and we just haven’t had a lot of exposure to them. I’m talking about native, more-or-less cross-browser features that have been in the language for some time.

So, in this article, I won’t be talking about jQuery, and I won’t be looking at structural code concepts or patterns. Instead, I’m going to introduce you to some pure JavaScript features that you can use today and that you might not have ever considered before.

insertAdjacentHTML()

Years ago, Microsoft introduced a method called insertAdjacentHTML() as a way to insert a specified string of text as HTML or XML into a specific place in the DOM. This feature has been available in Internet Explorer (IE) since version 4. Let’s see how it works.

Suppose you have the following HTML:

<div id="box1">
    <p>Some example text</p>
</div>
<div id="box2">
    <p>Some example text</p>
</div>

And suppose you want to insert another snippet of HTML between #box1 and #box2. You can do this quite easily using insertAdjacentHTML():

var box2 = document.getElementById("box2");
box2.insertAdjacentHTML('beforebegin', '<div><p>This gets inserted.</p></div>');

With that, the generated DOM ends up like this:

<div id="box1">
    <p>Some example text</p>
</div>
<div><p>This gets inserted.</p></div>
<div id="box2">
    <p>Some example text</p>
</div>

The insertAdjacentHTML() method takes two parameters. The first defines where you want to place the HTML, relative to the targeted element (in this case, the #box2element). This may be one of the following four string values:

  • beforebegin
    The HTML would be placed immediately before the element, as a sibling.
  • afterbegin
    The HTML would be placed inside the element, before its first child.
  • beforeend
    The HTML would be placed inside the element, after its last child.
  • afterend
    The HTML would be placed immediately after the element, as a sibling.

Again, these are string values, not keywords, so they must be placed inside of single or double quotes.

The second parameter is the string you want to insert, also placed in quotes (or else it would be a variable holding a string that was previously defined). Note that it should be a string, not a DOM element or element collection; so, it could just be text, with no actual markup.

insertAdjacentHTML() has, as outlined in a post on Mozilla Hacks, a couple of advantages over something more conventional, like innerHTML(): It does not corrupt the existing DOM elements, and it performs better.

And if you’re wondering why this one hasn’t received a lot of attention so far, despite being well supported in all in-use versions of IE, the reason is probably that, as mentioned in the Mozilla Hacks article, it was not added to Firefox until version 8. Because all other major browsers support this, and Firefox users have been auto-updating since version 5, it’s quite safe to use.

For more on this method:

getBoundingClientRect()

You can obtain the coordinates and, by extension, the dimensions of any element on the page using another lesser-known method, the getBoundingClientRect() method.

Here’s an example of how it might be used:

var box = document.getElementById('box'),
    x, y, w;

x = box.getBoundingClientRect().left;
y = box.getBoundingClientRect().top;

if (box.getBoundingClientRect().width) {
  w = box.getBoundingClientRect().width; // for modern browsers
} else {
  w = box.offsetWidth; // for oldIE
}

console.log(x, y, w);

Here, we’ve targeted an element with an ID of box, and we’re accessing three properties of the getBoundingClientRect() method for the #box element. Here’s a summary of six fairly self-explanatory properties that this method exposes:

  • top
    How many pixels the top edge of the element is from the topmost edge of the viewport
  • left
    How many pixels the left edge of the element is from the leftmost edge of the viewport
  • right
    How many pixels the right edge of the element is from the leftmost edge of the viewport
  • bottom
    How many pixels the bottom edge of the element is from the topmost edge of the viewport
  • width
    The width of the element
  • height
    The height of the element

All of these properties are read-only. And notice that the coordinate properties (top,leftright and bottom) are all relative to the top-left of the viewport.

What about the if/else in the example from above? IE 6 to 8 don’t support the widthand height properties; so, if you want full cross-browser support for those, you’ll have to use offsetWidth and/or offsetHeight.

As with insertAdjacentHTML(), despite the lack of support for width and height, this method has been supported in IE since ancient times, and it has support everywhere else that’s relevant, so it’s pretty safe to use.

I will concede something here: Getting the coordinates of an element using offset-based values (such as offsetWidth) is actually faster than using getBoundingClientRect(). Note, however, that offset-based values will always round to the nearest integer, whereas getBoundingClientRect()’s properties will return fractional values.

For more info:

The <table> API

If you’ve ever manipulated elements on the fly with JavaScript, then you’ve likely used methods such as createElementremoveChildparentNode and related features. And you can manipulate HTML tables in this way, too.

But you may not realize that there is a very specific API for creating and manipulating HTML tables with JavaScript, and it has very good browser support. Let’s take a quick look at some of the methods and properties available with this API.

All of the following methods are available to be used on any HTML table element:

  • insertRow()
  • deleteRow()
  • insertCell()
  • deleteCell()
  • createCaption()
  • deleteCaption()
  • createTHead()
  • deleteTHead()

And then there are the following properties:

  • caption
  • tHead
  • tFoot
  • rows
  • rows.cells

With these features, we can create an entire table, including rows, cells, a caption and cell content. Here’s an example:

var table = document.createElement('table'),
    tbody = document.createElement('tbody'),
    i, rowcount;

table.appendChild(tbody);

for (i = 0; i <= 9; i++) {
  rowcount = i + 1;
  tbody.insertRow(i);
  tbody.rows[i].insertCell(0);
  tbody.rows[i].insertCell(1);
  tbody.rows[i].insertCell(2);
  tbody.rows[i].cells[0].appendChild(document.createTextNode('Row ' + rowcount + ', Cell 1'));
  tbody.rows[i].cells[1].appendChild(document.createTextNode('Row 1, Cell 2'));
  tbody.rows[i].cells[2].appendChild(document.createTextNode('Row 1, Cell 3'));
}

table.createCaption();
table.caption.appendChild(document.createTextNode('A DOM-Generated Table'));

document.body.appendChild(table);

The script above combines some customary core DOM methods with methods and properties of the HTMLTableElement API. The same code written without the table API might be considerably more complex and, thus, harder to read and maintain.

Once again, these table-related features have support all the way back to IE 7 (and probably earlier) and everywhere else that’s relevant, so feel free to use these methods and properties where you see fit.

For more info:

Wrapping Up

This discussion of specific native JavaScript features has been a reminder of sorts. We can easily become comfortable with the features of a language that we know well, without looking deeper into the language’s syntax for simpler and more maintainable ways to solve our problems.

So, from time to time, look inside the box, so to speak. That is, investigate all that vanilla JavaScript has to offer, and try not to rely too much on plugins and libraries, which can unnecessarily bloat your code.

(Credits of image on front page: nyuhuhuu)

(al ea)

Challenging CSS Best Practices

When it comes to CSS, I believe that the sacred principle of “separation of concerns” (SoC) has lead us to accept bloat, obsolescence, redundancy, poor caching and more. Now, I’m convinced that the only way to improve how we author style sheets is by moving away from this principle.

For those of you who have never heard of the SoC principle in the context of Web design, it relates to something commonly known as the “separation of the three layers”:

  • structure,
  • presentation,
  • behavior.

It is about dividing these concerns into separate resources: an HTML document, one or more cascading style sheets and one or more JavaScript files.

But when it comes to the presentational layer, “best practice” goes way beyond the separation of resources. CSS authors thrive on styling documents entirely through style sheets, an approach that has been sanctified by Dave Shea’s excellent project CSS Zen Garden. CSS Zen Garden is what most — if not all — developers consider to be thestandard for how to author style sheets.

The Standard

To help me illustrate issues related to today’s best practices, I’ll use a very common pattern: the media object. Its combination of markup and CSS will be our starting point.

MARKUP

In our markup, a wrapper (div.media) contains an image wrapped in a link (a.img), followed by a div (div.bd):

<div>
  <a href="http://twitter.com/thierrykoblentz">
        <img src="thierry.jpg" alt="me" width="40" />
  </a>
  <div>
    @thierrykoblentz 14 minutes ago
  </div>
</div>

CSS

Let’s give a 10-pixel margin to the wrapper and style both the wrapper and div.bd asblock-formatting contexts (BFC). In other words, the wrapper will contain the floated link, and the content of div.bd will not wrap around said link. A gutter between the image and text is created with a 10-pixel margin (on the float):

.media {
    margin: 10px;
}
.media,
.bd {
    overflow: hidden;
    _overflow: visible;
    zoom: 1;
}
.media .img {
    float: left;
    margin-right: 10px;
}
.media .img img {
    display: block;
}

RESULT

Here is the presentation of the wrapper, with the image in the link and the blob of text:


me
 

@thierrykoblentz 14 minutes ago

 

A NEW REQUIREMENT COMES IN

Suppose we now need to be able to display the image on the other side of the text as well.

MARKUP

Thanks to the magic of BFC, all we need to do is change the styles of the link. For this, we use a new class, imgExt.

<div>
    <a href="http://twitter.com/thierrykoblentz">
        <img src="thierry.jpg" alt="me" width="40" />
  </a>
  <div>
    @thierrykoblentz 14 minutes ago
  </div>
</div>

CSS

We’ll add an extra rule to float the link to the right and change its margin:

.media {
    margin: 10px;
}
.media,
.bd {
    overflow: hidden;
    _overflow: visible;
    zoom: 1;
}
.media .img {
    float: left;
    margin-right: 10px;
}
.media .img img {
    display: block;
}
.media .imgExt {
    float: right;
    margin-left: 10px;
}

RESULT

The image is now displayed on the opposite side:


me
 

@thierrykoblentz 14 minutes ago

 

ONE MORE REQUIREMENT COMES IN

Suppose we now need to make the text smaller when this module is inside the right rail of the page. To do that, we create a new rule, using #rightRail as a contextual selector:

MARKUP

Our module is now inside a div#rightRail container:

<div id="rightRail">
    <div>
        <a href="http://twitter.com/thierrykoblentz">
            <img src="thierry.jpg" alt="me" width="40" />
        </a>
        <div>
            @thierrykoblentz 14 minutes ago
        </div>
    </div>
</div>

CSS

Again, we create an extra rule, this time using a descendant selector, #rightRail .bd.

.media {
    margin: 10px;
}
.media,
.bd {
    overflow: hidden;
    _overflow: visible;
    zoom: 1;
}
.media .img {
    float: left;
    margin-right: 10px;
}
.media .img img {
    display: block;
}
.media .imgExt {
    float: right;
    margin-left: 10px;
}
#rightRail .bd {
    font-size: smaller;
}

RESULT

Here is our original module, showing inside div#rightRail:


me
 

@thierrykoblentz 14 minutes ago

 

 

What’s Wrong With This Model?

  • Simple changes to the style of our module have resulted in new rules in the style sheet.
    There must be a way to style things without always having to write more CSS rules.
  • We are grouping selectors for common styles (.media,.bd {}).
    Grouping selectors, rather than using a class associated with these styles, will lead to more CSS.
  • Of our six rules, four are context-based.
    Rules that are context-specific are hard to maintain. Styles related to such rules are not very reusable.
  • RTL and LTR interfaces become complicated.
    To change direction, we’d need to overwrite some of our styles (i.e. write morerules). For example:
.rtl .media .img {
    margin-right: auto; /* reset */
    float: right;
    margin-left: 10px;
}
.rtl .media .imgExt {
    margin-left: auto; /* reset */
    float: left;
    margin-right: 10px;
}

 

Meet Atomic Cascading Style Sheet

a·tom·ic
/ə’tämik/
of or forming a single irreducible unit or component in a larger system.

As we all know, the smaller the unit, the more reusable it is.

“Treat code like Lego. Break code into the smallest little blocks possible.” —@csswizardry (via @stubbornella#btconf

— Smashing Magazine (@smashingmag) May 27, 2013

To break down styles into irreducible units, we can map classes to a single style, rather than many. This will result in a more granular palette of rules, which in turn improves reusability.

Let’s revisit the media object using this new approach.

MARKUP

We are using five classes, none of which are related to content:

<div>
    <a href="http://twitter.com/thierrykoblentz">
        <img src="thierry.jpg" alt="me" width="40" />
    </a>
    <div>
        @thierrykoblentz 14 minutes ago
    </div>
</div>

CSS

Each class is associated with one particular style. For the most part, this means we have one declaration per rule.

.Bfc {
    overflow: hidden;
    zoom: 1;
}
.M-10 {
    margin: 10px;
}
.Fl-start {
    float: left;
}
.Mend-10 {
    margin-right: 10px;
}
.Fz-s {
    font-size: smaller;
}

RESULT


me
 

@thierrykoblentz 14 minutes ago

 

WHAT IS THIS ABOUT?

Let’s ignore the class names for now and focus on what this does (or does not):

  • No contextual styling
    We do not use contextual or descendant selectors, which means that our style sheet has no dead weight.
  • Directions (left and right) are “abstracted.”
    Rather than overwriting styles, we serve a RTL style sheet that contains rules such as these:
.Fl-start {
    float: right;
}
.Mend-10 {
    margin-left: 10px;
}

Same classes, same properties, different values.

But the most important thing to notice here is that we are styling via markup. We have changed the context in which we style our modules. We are now editing HTML templates instead of style sheets.

I believe that this approach is a game-changer because it narrows the scope dramatically. We are styling not in the global scope (the style sheet), but at the module and block level. We can change the style of a module without worrying about breaking something else on the page. And we can do this without adding any rule to the style sheet, let alone creating a new class and rule:

.someBasicStyleForThisElementHere {...}

We get no redundancy. Selectors are not duplicated, and styles belong to a single rule instead of being part of many. For example, the style sheets that this page links to contain 72 float declarations.

Also, abandoning a style — for example, deciding to always keep the image on the left side of the module — does not make any of our rules obsolete.

SOUND GOOD?

Not sold yet? I hear you saying, “This goes against every single rule in the book. This is no better than inline styling. And your class names are not only cryptic, but unsemantic, too!”

Fair enough. Let’s address these concerns.

REGARDING UNSEMANTIC CLASS NAMES

If you check the W3C’s “Tips for Webmasters,” where it says “Good names don’t change,” you’ll see that the argument is about maintenance, not semantics per se. All it says is that changing styles is easier in a CSS file than in multiple HTML files. .border4pxwould be a bad name only if changing the style of an element required us to change the declaration that that class name is associated with. In other words:

.border4px {border-width:2px;}

REGARDING CRYPTIC CLASS NAMES

For the most part, these class names follow the syntax of Zen Coding — see the “Zen Coding Cheat Sheet” (PDF) — now renamed Emmet. In other words, they are simple abbreviations.

There are exceptions for styles associated with direction (left and right) and styles that involve a combination of declarations. For example, Bfc stands for “block-formatting context.”

REGARDING MIMICKING INLINE STYLES

Hopefully, the diagram below clears things up:

Venn diagram that shows all possible logical relations between inline styles and styling via classes and markup.
Inline styles versus Atomic CSS.

  • Specificity
    The technique is not as specific as @style. It lowers style weight because rules rely on a single class, as opposed to rules like .parent .bd {}, which clocks in at 0.0.2.0 (see “CSS Specificity: Things You Should Know”).
  • Verbosity
    Most classes are abbreviations of declarations (for example, M-10 versus margin: 10px). Some classes, such as Bfc, refer to more than one style (see “Mapping” in the diagram above). Other classes use “start” and “end” keywords, rather than left and right values (see “Abstraction” in the diagram above).

Here are the advantages of @style:

  • Scope
    Styles are “sandboxed” to the nodes they are attached to.
  • Portability
    Because the styles are “encapsulated,” you can move modules around without losing their styles. Of course, we still need the style sheet; however, because we are making context irrelevant, modules can live anywhere on a page, website or even network.

The Path To Bloat

Because the styles of our module are tied only to presentational class names, they can be anything we want them to be. For example, if we need to create a simple two-column layout, all we need to do is replace the link with a div in our template. That would look like this:

<div>
    <div>
        column 1
    </div>
    <div>
        column 2
    </div>
</div>

And we would need only one extra rule in the style sheet:

.Bfc {
    overflow: hidden;
    zoom: 1;
}
.M-10 {
    margin: 10px;
}
.Fl-start {
    float: left;
}
.Mend-10 {
    margin-right: 10px;
}
.Fz-s {
    font-size: smaller;
}
.W-50 {
    width: 50%;
}

Compare this to the traditional way:

<div>
    <div>
        column 1
    </div>
    <div>
        sidebar
    </div>
</div>

This would require us to create three new classes, to add an extra rule and to group selectors.

.wrapper,
.content,
.media,
.bd {
    overflow: hidden;
    _overflow: visible;
    zoom: 1;
}
.sidebar {
    width: 50%;
}
.sidebar,
.media .img {
    float: left;
    margin-right: 10px;
}
.media .img img {
    display: block;
}

I think the code above pretty well demonstrates the price we pay for following the SoC principle. In my experience, all it does is grow style sheets.

Moreover, the larger the files, the more complex the rules and selectors become. And then no one would dare edit the existing rules:

  • We leave alone rules that we suspect to be obsolete for fear of breaking something.
  • We create new rules, rather than modify existing ones, because we are not sure the latter is 100% safe.

In other words, we make things worse because we can get away with bloat.

Nowadays, people are accustomed to very large style sheets, and many authors think they come with the territory. Rather than fighting bloat, they use tools (i.e. preprocessors) to help them deal with it. Chris Eppstein tells us:

“LinkedIn has over 1,100 Sass files (230k lines of SCSS) and over 90 web developers writing Sass every day.”

CSS BLOAT VS. HTML BLOAT

Let’s face it: the data has to live somewhere. Consider these two blocks:

<div>
<div>

In many cases, the “semantic” class name makes up more bytes than the presentational class name (.wrapper versus .Bfc). But I do not think this is a real concern compared to what most apps onboard these days via data- attributes.

This is where gzip comes into play, because the high redundancy in class names across a document would achieve better compression. And the same is true of style sheets, in which we have many redundant sequences:

.M-1 {margin: 1px;}
.M-2 {margin: 2px;}
.M-4 {margin: 4px;}
.M-6 {margin: 6px;}
.M-8 {margin: 8px;}
etc.

Caching

Presentational rules do not change. Style sheets made from such rules mature into tool sets in which authors can find everything they need. By their nature, they stop growing and become immutable, and immutable is cache-friendly.

No More .button Class?

The technique I’m discussing here is not about banning “semantic” class names or rules that group many declarations. The idea is to reevaluate the benefits of the common approach, rather than adopting it as the de facto technique for styling Web pages. In other words, we are restricting the “component” approach to the few cases in which it makes the most sense.

For example, you may find the following rules in our style sheets, rules that set styles for which we do not create simple classes or rules that ensure cross-browser support.

.button {
    display: inline-block;
    *display: inline;
    zoom: 1;
    font-size: bold 16px/2em Arial;
   	height: 2em;
   	box-shadow: inset 1px 1px 2px 0px #fff;
   	background: -webkit-gradient(linear, left top, left bottom, color-stop(0.05, #ededed), color-stop(1, #dfdfdf));
   	background: linear-gradient(center top, #ededed 5%, #dfdfdf 100%);
   	filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ededed', endColorstr='#dfdfdf');
   	background-color: #ededed;
    color: #777;
    text-decoration: none;
   	text-align: center;
   	text-shadow: 1px 1px 2px #ffffff;
   	border-radius: 4px;
   	border: 2px solid #dcdcdc;
}
.modal {
    position: fixed;
    top: 50%;
    left: 50%;
    -webkit-transform: translate(-50%,-50%);
    -ms-transform: translate(-50%,-50%);
    transform: translate(-50%,-50%);
    *width: 600px;
    *margin-left: -300px;
    *top: 50px;
}
@media screen {
    .modal {
        width: 600px;
        margin-left: -300px;
        top: 50px;
    }
}

On the other hand, you would not see rules like the ones below (i.e. styles bound to particular modules), because we prefer to apply these same styles using multiple classes: one for font size, one for color, one for floats, etc.

.news-module {
    font-size: 14px;
    color: #555;
    float: left;
    width: 50%;
    padding: 10px;
    margin-right: 10px;
}
.testimonial {
    font-size: 16px;
    font-style: italic;
    color: #222;
    padding: 10px;
}

Do We Include Every Possible Style In Our Style Sheet?

The idea is to have a pool of rules that authors can choose from to style anything they want. Styles that are common enough across a website would become part of the style sheet. If a style is too specific, then we’d rely on @style (the style attribute). In other words, we’d prefer to pollute the markup rather than the style sheet. The primary goal is to create a sheet made of rules that address various design patterns, from a basic rule that floats an element to “helper” classes.

/**
 * one liner with ellipsis
 * 1. we inherit hyphens:auto from body, which would break "Ell" in table cells
 */
.Ell {
    max-width: 100%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    -webkit-hyphens: none; /* 1 */
    -ms-hyphens: none;
    -o-hyphens: none;
    hyphens: none;
}
/**
 * kinda line-clamp
 * two lines according to default font-size and line-height
 */
.LineClamp {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    font-size: 13px;
    line-height: 1.25;
    max-height: 32px;
    _height: 32px;
    overflow: hidden;
}
/**
 * reveals an hidden element on :hover or :focus
 * visibility can be forced by applying the class "RevealNested-on"
 * IE8+
 */
:root .NestedHidden {
    opacity: 0;
}
:root .NestedHidden:focus,
:root .RevealNested:hover .NestedHidden,
:root .RevealNested-on .NestedHidden {
    opacity: 1;
}

How Does This Scale?

We have just released a brand new My Yahoo, which relies heavily on this technique. This is how it compares to a few other Yahoo products (after gzip’ing):

CSS Assets
answers.yahoo.com 30.1 KB
sports.yahoo.com 67.4 KB
omg.yahoo.com 46.2 KB
yahoo.com 45.9 KB
my.yahoo.com 21.3 KB

Our style sheet weighs 17.9 KB (about 3 KB of which are property-specific), and it is shareable (unlike the style sheets of other properties). The reason for this is that none of the rules it contains relate to content.

Wrapping Up

Because presentational class names have always been deemed “out of bounds,” we — the community — have not really investigated what their use entails. In fact, in the name of best practice, we’ve dismissed every opportunity to explore their potential benefits.

Here at Yahoo, @renatoiwa@StevenRCarlson and I are developing projects with this new CSS architecture. The code appears to be predictable, reusable, maintainable and scalable. These are the results we’ve experienced so far:

  • Less bloat
    We can build entire modules without adding a single line to the style sheets.
  • Faster development
    Styles are driven by classes that are not related to content, so we can copy and paste existing modules to get started.
  • RTL interface for free
    Using start and end keywords makes a lot of sense. It saves us from having to write extra rules for RTL context.
  • Better caching
    A huge chunk of CSS can be shared across products and properties.
  • Very little maintenance (on the CSS side)
    Only a small set of rules are meant to change over time.
  • Less abstraction
    There is no need to look for rules in a style sheet to figure out the styling of a template. It’s all in the markup.
  • Third-party development
    A third party can hand us a template without having to attach a style sheet (or astyle block) to it. No custom rules from third parties means no risk of breakage due to rules that have not been properly namespaced.

(Note that if maintenance is easier on the CSS side than on the HTML side, then the reason is simply that we can cheat on the CSS side by not cleaning up rules. But if we were required to keep things lean and clean, then the pain would be the same.)

FINAL NOTE

I was at a meetup a couple of weeks ago, where I heard Colt McAnlis say, “Tools, not rules.” A quick search for this idiom returned this:

“We all need to be open to new learnings, new approaches, new best practices and we need to be able to share them.”

(al, ea)

/**
* custom styles for this article
* Namespace: “Atomiccss”
*/
.Atomiccss .media {
margin: 10px;
}
.Atomiccss .media,
.Atomiccss .bd {
overflow: hidden;
_overflow: visible;
zoom: 1;
}
.Atomiccss .media .img {
float: left;
margin-right: 10px;
}
.Atomiccss .media .img img {
display: block;
}
.Atomiccss .media .imgExt {
float: right;
margin-left: 10px;
}
.Atomiccss #rightRail .bd {
font-size: smaller;
}
/* ACSS */
.Atomiccss .Bfc {
overflow: hidden;
zoom: 1;
}
.Atomiccss .M-10 {
margin: 10px;
}
.Atomiccss .Fl-start {
float: left;
}
.Atomiccss .Mend-10 {
margin-right: 10px;
}
.Atomiccss .Fz-s {
font-size: smaller;
}
/* for some reason, @width is stripped from the markup */
.Atomiccss img {
width: 40px;
}

The Future Of Video In Web Design

Federico was the only other kid on the block with a dedicated ISDN line, so I gave him a call. It had taken six hours of interminable waiting (peppered with frantic bouts of cursing), but I had just watched 60 choppy seconds of the original Macintosh TV commercial in Firefox, and I had to tell someone. It blew my mind.

Video on the Web has improved quite a bit since that first jittery low-res commercial I watched on my Quadra 605 back in 7th grade. But for the most part, videos are still separate from the Web, cordoned off by iframes and Flash and bottled up in little windows in the center of the page. They’re a missed opportunity for Web designers everywhere.

But how do you integrate video into an app or a marketing page? What would it look like, and how do you implement it? In this article, you will find inspiration, how-tos and a few technical goodies to get you started with modern video on the Web.

When Video Leaves Its Cage

Video combined with animation is a powerful tool for innovative and compelling user experiences. Imagine interactive screencasts and tutorials in which DOM elements flow and move around the page in sync with the instructor. Why not combine video with animation to walk new users through your app? Or what about including videos of your product on your marketing page, instead of static JPEGs? Getting carried away is easy — video can become little more than sophisticated blink tags if you’re not careful. But there are plenty of beautiful, inspiring examples of video tightly integrated in a design.

Apple’s new marketing page for the Mac Pro is a stunning example of video reaching out from its cage into the surrounding content. The new Mac Pro is featured in the center of the page, and as you scroll, it swoops and spins and disassembles itself. Supporting copy fades in to describe what you are seeing.


A static screenshot of the new landing page doesn’t do the new Mac Pro justice. (larger view)

Another great example of interactive video is Adrian Holovaty’s Soundslice. Soundslice is filled with YouTube videos of music sliced and diced into tablature (or tabs), which is notation that guitar players use to learn music.


The musical bars at the bottom stay in sync with the video. (larger view)

When you watch a music video, the tabs are animated at the bottom in time with the music, so that you can play along with your guitar. You can even slow down the video or loop selections to practice difficult sections, and the tab animation will stay in sync.

How Do You Add Video To A Design?

If you venture into video and animation in your next project, you won’t have many resources to lean on for implementation. No canonical, easy-to-use, open-source library for syncing video with animation exists, so every implementation is a bit different. Should you use a JavaScript animation library or pure CSS keyframes and transitions? Should you host the videos yourself and take advantage of HTML5’s video tag events or use YouTube or Vimeo? And then how exactly do you tie animations to a video?

Together, we will explore answers to the above-mentioned questions and more as we build our own micro JavaScript framework. Charlie.js provides an easy-to-use API for building pages with synchronized video and CSS3 animation.


Charlie.js, named in honor of Charlie Chaplin. (Image source)

The best way to learn is by doing, so let’s dive in.

What Does Charlie.js Do?

We need a way to create animations and then trigger them at certain moments in a video. We also need to pause the animations if the video stops, and we’ll need a way to handle the user jumping around to different times in the video.

To limit the scope of this article, we’ll have Charlie.js use only CSS animations. JavaScript animation libraries are more flexible and powerful than CSS animations, but wrapping one’s head around the straightforward, declarative syntax of keyframes is pretty easy, and the effects are hardware-accelerated. Sticking with only CSS animations is a pretty good choice for small projects.

To keep it simple, Charlie.js will support only one video per page.

As we go through the exercise of building this library, remember that we’re using the framework just to learn about CSS animation and video on the Web. The goal is to learn, not to create production-quality code.

Define The API

For our little framework, defining the API first makes sense. In other words, we need to figure out how someone would use the library and then write the JavaScript to implement the API.

A video and animation library could work in many ways, but the main interface puzzle is to figure out how to couple the animation to the video. How should a developer specify which animations should appear on which elements and at which times they should start in the video?

One option is to suck down the data in JSON or XML. The opposite solution is to have no separate data files and to put all of the configuration into pure JavaScript function calls. Both are fine, but there is a middle road.

Normally, CSS animation is defined in a style sheet. Ideally, that’s where it should be defined for Charlie.js, not in a JSON file. It just makes sense, and doing it this way has advantages. When the animation is in a style sheet, rather than a JavaScript or JSON file, you can test it without loading the entire video and animation library.

The animations are coupled to an element with data attributes. The data attributes define the animation names and also specify the start times.

Let’s say you have a CSS animation named fade for dialing down the opacity, and another named fling for moving elements off the page. And you want a div on the page to use both animations three seconds into the video. Your markup would look like this:

<div data-animations="fade, fling" data-times="3, 3">
...
</div>

Charlie.js will see this and know to run the fade and fling animations once the video hits three seconds.

The fade and fling animations are defined in a style sheet that is linked to the document.

Here is what the fade animation might look like (browser prefixes are excluded here but are required for Chrome and Safari):

.fade {
	animation-name: fade;
	animation-duration: 3s;
	animation-timing-function: linear;
	animation-iteration-count: 1;
	animation-direction: normal;
	animation-fill-mode: forwards;
}

@keyframes fade {
	0% {
		opacity: 1;
	}

	100% {
		opacity: 0;
	}
}

The .fade class is what Charlie.js applies to the animated element, which will trigger the fade animation.

Host The Videos: HTML5 Vs. Flash And Silverlight

With the API out of the way, the next decision is how to host the video. The host will determine what kind of container the video is stuffed into, and the container will determine what is possible with the video.

Video embedded with Flash or Silverlight will limit your design options, so the video-hosting service should ideally support HTML5’s video tag. The video tag is easier to style and move around on the page. You can apply CSS filters and transforms and even use CSS animation on the video itself. Plus, the standard media events are robust and provide plenty of places and ways to hook your code into the video. The big downside of the video tag is compatibility. It doesn’t work in Internet Explorer 8.

What kinds of video-hosting should Charlie.js support? Building a library that supports multiple hosting options is feasible. For example, Popcorn.js (an awesome library for syncing content with video) supports several hosting options and APIs. But to keep it simple, our little library will support only a vanilla video tag. Anything in an iframe or Flash container won’t be supported.

That’s nice for Charlie.js, but what if you are stuck supporting old browsers and have to deal with a video stuffed in an iframe? Most video-hosting companies have decent APIs. At the very least, you should be able to use those APIs to sync up your animation — you’ll just be stuck working with an embedded Flash object. YouTube and Vimeo are the most popular services, and both offer extensive APIs. Wistia is another great option but less well known.

If you want to use a pure video tag but don’t want to host the video yourself, take a look at Vid.ly. Once you upload your video, Vid.ly will encode it in every format you need and give you a universal URL that you can use in your video tag, which will automatically choose the correct video type according to the user agent.

<video id="video" src="http://vid.ly/4m4e2n?content=video" controls="" preload="none">
Your browser does not support the HTML5 video element.
</video>

Heads Up

The JavaScript in most of these snippets uses Underscore; stuff like _.forEach and_.toArray are utility functions from that library. Underscore encourages a functional style of programming that might look strange if you’ve never seen it before, but a little time invested in learning Underscore can save you a lot of time and lines of code. It’s worth checking out. For this article, you’ll find comments in the code to tell you what’s going on, and it should be pretty easy to understand.

One other caveat: The code here will run in most modern browsers, but no attempt has been made to make this completely cross-browser compatible. If your business really needs CSS animation to be synced with video and to run in almost every browser, then this library will not help you out. But for my business, and perhaps for yours, supporting only modern browsers is fine. And even with this restriction, plenty of material here is still worth learning.

Control CSS Animations With JavaScript

JavaScript is the glue between video and CSS animation. There is no way to couple an animation to a video purely with CSS. Animation doesn’t start until a style is applied, and CSS gives you only so many ways to trigger extra styles (such as :hover). In order to sync animation to video, we will need to pause, stop, resume, skip to the middle, and even reverse running animations.

All of this is possible with JavaScript. So, the first step is to get the CSS animation out of the style sheet and into JavaScript. Every CSS animation has two parts. The first part is the keyframe and the properties used to configure how the animation behaves, such as duration, iteration and direction. The second part is what triggers the animation. Charlie.js will need to find both parts in the style sheets.

The first thing we need is a utility function to search through style sheets that are loaded on the page.

findRules = function(matches){

		//document.stylesheets is not an array by default.
		// It's a StyleSheetList. toArray converts it to an actual array.
		var styleSheets = _.toArray(document.styleSheets),
		rules = [];

		// forEach iterates through a list, in this case passing
		//every sheet in styleSheets to the next forEach
		_.forEach(styleSheets, function(sheet){

		//This foreach iterates through each rule in the style sheet
		//and checks if it passes the matches function.
		_.forEach(_.toArray(sheet.cssRules), function(rule){
			if (matches(rule)){
				rules.push(rule);
			}
		});
	});
return rules;
}

The findRules function iterates through every rule of every style sheet and returns a list of rules that match the passed-in matches function. To get all of the keyframe rules, we pass in a function to findRules that checks whether the rule is a keyframe:

// A little code to handle prefixed properties
	var KEYFRAMES_RULE = window.CSSRule.KEYFRAMES_RULE
		|| window.CSSRule.WEBKIT_KEYFRAMES_RULE
		|| window.CSSRule.MOZ_KEYFRAMES_RULE
		|| window.CSSRule.O_KEYFRAMES_RULE
		|| window.CSSRule.MS_KEYFRAMES_RULE,

		...

		var keyframeRules = findRules(function(rule){
			return KEYFRAMES_RULE === rule.type;
		}),

		...

At this point, we have the keyframes in JavaScript, but we still need the rest of the animation styles that define duration, iterations, direction and so on.

To find all of these classes, we again use the findRules function to go through every rule in every style sheet. This time, though, the matches function that we’ll pass in will check to see whether the rule has an animationName property.

	...

	var animationStyleRules = findRules(function(rule){
		return rule.style && rule.style[animationName(rule.style)];
	});

	...

The animationsName function is there to handle the prefixes, because the animationNameproperty still requires prefixes in some browsers. That function looks like this:

...

if (style.animationName) {
	name = "animationName"; }
else if (style.webkitAnimationName) {
	name = "webkitAnimationName"; }
else if (style.mozAnimationName) {
	name = "mozAnimationName"; }
else if (style.oAnimationName) {
	name="oAnimationName"; }
else if (style.msAnimationName) {
	name = "msAnimationName"; }
else {
	name = "";
}
return name;

...

Once the correct prefix has been determined, the name is cached and used for future look-ups.

Once the keyframes and animation styles have been collected, they get stuffed into an instance of a helper class and stored for Charlie.js to use later.

var CSSAnimations = function(keyframes, cssRules){
	this.keyframes = keyframes;
	this.cssRules = cssRules;
};

Get The Timing Information From The Data Attributes

Timing information is attached to the element that will be animated using a data attribute (remember that we decided this when we were defining the API). So, we need to crawl the document and pull out the information. Any element that will be animated is marked with the class of charlie, which makes it pretty easy to find the data attributes we are looking for.

var scrapeAnimationData = function() {

	/* Grab the data from the DOM. */
	var data = {};
	_.forEach(
		//loop through every element that should be animated
		document.getElementsByClassName("charlie"),

		//for each element, pull off the info from the dataset
		function(element) {

			/*
			* Creates an object of animation name: time, e.g.
			*
			* { swoopy: [
			*    { element: domElement,
			*  time: 6522 },
			*    { element: anotherElement,
			*  time: 7834 }]
			* }
			*/

			//     var names = element.dataset.animations.split(/s*,s*/),
			times = element.dataset.times.split(/s*,s*/),

			// creates an array of arrays, each one called a "tuple"
			// basically ties the time to the
			// animation name, so it looks like this:
			//[["zippy", 1], ["fade", 2] ... ]
			tuples = _.zip(names, times);

			/*
			* turn the tuples into an object,
			* which is a little easier to work with.
			* We end up with an object that looks like this:
			* {
			*  fade: [ {element: domElement, time: "1.2s"}, ... ],
			*  fling: [ {element: domelement, time: "2.4s"}, ... ]
			* }
			* So, we can reuse an animation on different elements
			* at different times.
			*/

			_.forEach(tuples, function(tuple){
				var name = tuple[0],
				time = tuple[1];
				data[name] = data[name] || [];
				data[name].push({
					element: element,
					time: time
				})
			});
		});
	return data;
},

This stores all of the timing information in an object with the animation’s name as the key, followed by a list of times and elements. This object is used to create severalAnimation objects, which are then stuffed into various data structures to make it easy and fast to look up which animations should be running in the big loop.

The requestAnimationFrame Loop

The heart of Charlie.js is a loop that runs whenever the video runs. The loop is created with requestAnimationFrame.

tick: function(time){
	if (this.running){
		this.frameID = requestAnimationFrame(this.tick.bind(this));
		this.controller.startAnimations(time, video.currentTime);
	}
}

The requestAnimationFrame function is specifically designed to optimize any kind of animation, such as DOM manipulations, painting to the canvas, and WebGL. It’s a tighter loop than anything you can get with setTimeout, and it’s calibrated to bundle animation steps into a single reflow, thus performing better. It’s also better for battery usage and will completely stop running when the user switches tabs.

The loop starts when the video starts and stops when the video stops. Charlie.js also needs to know whether the video ends or jumps to the middle somewhere. Each of those events requires a slightly different response.

video.addEventListener("play", this.start.bind(this), false);
video.addEventListener("ended", this.ended.bind(this), false);
video.addEventListener("pause", this.stop.bind(this), false);
video.addEventListener("seeked", this.seeked.bind(this), false);

As the video plays, the loop keeps ticking. Each tick runs this code:

// allow precision to one tenth of a second
var seconds = roundTime(videoTime),
me = this;

//resume any paused animations
me.resumeAnimations();

/* start up any animations that should be running at this second.
* Don't start any that are already running
*/

if (me.bySeconds[seconds]){
	var animations = me.bySeconds[seconds],
	notRunning = _.filter(animations, function(animation){
		return !_.contains(me.running, animation);
	});

	/* requestAnimationFrame happens more than
	*  every tenth of a second, so this code will run
	*  multiple times for each animation starting time
	*/

	_.forEach(notRunning, function(animation){
		animation.start();
		me.running.push(animation);
	});
}

Everything we have done up to this point has been to support these few lines of code. The seconds variable is just the video.currentTime value rounded to the nearest tenth of a second. The bySeconds property is created from the time data that is scraped from the HTML — it’s just a quick way to grab a list of animations to start at a given time. Therunning array is a list of animations that are currently running. TherequestAnimationFrame loop is really fast and runs many, many times a second, and Charlie.js only supports a resolution of one tenth of a second.

So, if one animation starts at the 2-second mark, then requestAnimationFrame will try to start it several times until the video has progressed to the next tenth of a second. Toprevent animations from starting over and over again during that tenth of a second, they get put into the running array so that we know what is running and don’t start it again unnecessarily.

To start a CSS animation, just add the animation properties to an element’s style. The easiest way to do this is to just add the animation class to the element’s classList, and that is exactly what the animation’s start method does.

start: function(){
	var me = this;
	//The name of the animation is the same as the class name by convention.
	me.element.classList.add(me.name);
	onAnimationEnd(me.element, function(){
		me.reset();
	});
}

The name of the animation is the same as the class name by convention.

Pause And Resume Animations

When the video stops, the animations should stop with it. There is a pretty straightforward way to do this using CSS animations: We just set the animationPlayStateproperty of the element to paused.

...

//method on the animation object
pause: function(){
	this.element.style.webkitAnimationPlayState = "paused";
	this.element.style.mozAnimationPlayState = "paused";
	this.element.style.oAnimationPlayState = "paused";
	this.element.style.animationPlayState = "paused";
},

resume: function(){
	this.element.style.webkitAnimationPlayState = "running";
	this.element.style.mozAnimationPlayState = "running";
	this.element.style.oAnimationPlayState = "running";
	this.element.style.animationPlayState = "running";
}

...

//called on the video "pause" event
while(animation = me.running.pop()){
	animation.pause();
	//keep track of paused animations so we can resume them later ...
	me.paused.push(animation);
}

The only trick here is to keep track of which animations have been paused, so that they can resume once the video starts again, like so:

while (animation = me.paused.pop()){
	animation.resume();
	me.running.push(animation);
}

How To Start An Animation In The Middle

What if someone skips ahead in the video and jumps right into the middle of an animation? How do you start a CSS animation in the middle? The animationDelayproperty is exactly what we need. Normally, animationDelay is set to a positive number. If you want an animation to start three seconds after the animation style has been applied, then you’d set animationDelay to 3s. But if you set animationDelay to a negative number, then it will jump to the middle of the animation. For example, if an animation lasts three seconds, and you want the animation to start two seconds in, then set the animationDelay property to -2s.

Whenever a user scrubs to the middle of the video, Charlie.js will need to stop all of the animations that are currently running, figure out what should be running, and then set the appropriate animationDelay values. Here is the high-level code:

// 1. go through each to start
// 2. set the animation delay so that it starts at the right spot
// 3. start 'em up.

var me = this,
seconds = roundTime(videoTime),
toStart = animationsToStart(me, seconds);

// go through each animation to start
_.forEach(toStart, function(animation){

	//set the delay to start the animation at the right place
	setDelay(animation, seconds);

	//start it up
	animation.start();

	/* If the move is playing right now, then let the animation
	* keep playing. Otherwise, pause the animation until
	* the video resumes.
	*/

	if (playNow) {
	me.running.push(animation);

	} else {
		me.paused.push(animation);
		animation.pause();
	}
});

The animationsToStart function loops through a sorted list of animations and looks for anything that should be running. If the end time is greater than the current time and the start time is less than the current time, then the animation should be started.

var animationsToStart = function(me, seconds) {

	var toStart = [];

	for(var i = 0; i < me.timeModel.length; i++) {

		var animation = me.timeModel[i];

		//stop looking, nothing else is running
		if (animation.startsAt > seconds) {
			break;
		}

		if (animation.endsAt > seconds) {
			toStart.push(animation);
		}
	}
	return toStart;
};

The timeModel is a list of animations sorted by the times when the animations should end. This code loops through that list and looks for animations that start before the current time and end after the current time. The toStart array represents all of the animations that should be running right now.

Those values get passed up to the higher-level code, which then computes and sets the delay in the setDelay function.

setDelay = function(animation, seconds) {
	var delay = -(seconds - animation.startsAt);
	delay = delay < 0 ? delay : 0,
	milliseconds = Math.floor(delay * 1000) + "ms";
	animation.element.style.webkitAnimationDelay = milliseconds;
	animation.element.style.mozAnimationDelay = milliseconds;
	animation.element.style.oAnimationDelay = milliseconds;
	animation.element.style.msAnimationDelay = milliseconds;
	animation.element.style.animationDelay = milliseconds;
};

The seconds parameter is the current time in the video. Let’s say that the video is at 30 seconds, that the animation starts at 24 seconds and that it lasts for 10 seconds. If we set the delay to -6s, then it will start the animation 6 seconds in and will last another 4 seconds.

Look At The Code For Yourself

We’ve covered here how to use requestAnimationFrame to create a tight, optimized loop for animations, how to scrape keyframes and animation styles from the style sheet, how to start and stop animations with the video, and even how to start CSS animations in the middle. But to get to the point, we’ve skipped over quite a bit of glue code. Charlie.js is only a couple of hundred lines of code, and it is open source and commented thoroughly. You are welcome to grab the code and read it.

You can even use it if you want, with a few caveats:

  • Charlie.js was made for educational purposes. It was made to be read and for you to learn about CSS animations, videos, requestAnimationFrame, etc. Don’t just plug it into your production code unless you really know what you are doing.
  • Cross-browser support for animation is pretty good, and Charlie.js tries to be friendly to all the browsers when it can be. However, it hasn’t been heavily tested.
  • It eats up CPU, even if the video is paused. (This has something to do with CSS animations still rendering.)
  • The user can’t drag the seek bar while the video is unpaused. If they do, then the animations will start and overlap each other.
  • Charlie.js does not respond to changes in frame rate. So, if the video stutters or you want to slow down the rate of the video, then the animations will fall out of sync. Also, you can’t run video backwards.
  • Animations won’t start if the current tab isn’t set to the video, due torequestAnimationFrame not running unless the video tab is active. This could confuse users who switch back and forth between tabs.

Some of these limitations can be fixed pretty easily, but Charlie.js was made for a very limited use case. I’ve put together a demo of Charlie.js in action so that you can see what it can do.

The future of video in Web design is filled with possibilities, and I for one can’t wait to see what happens.

Reinventing The Tech Conference Experience

If you had to name one thing that could have been better at the last conference or meetup you attended, what would it be? I bet you’d say that the content or the interaction could have been better in some way. I created Onslyde to solve this problem. It’s a free service andopen-source project that (hopefully) will make public speaking easier and conferences better.

The motivation for the project came from my own speaking engagements in the tech industry. I wanted to see how many people in the audience actually agreed or disagreed with what I was saying. I also wanted to leverage their experience and knowledge to create a better learning environment.

Presentations today are mostly unidirectional, with a single presenter giving information to the audience. But it doesn’t have to be that way. Now, with the ubiquity of mobile devices, everyone in the room can contribute to the conversation and make it better. Many books have been written on the topic of collective wisdom. In The Wisdom of Crowds, James Surowiecki states:

“… a diverse collection of independently deciding individuals is likely to make certain types of decisions and predictions better than individuals or even experts.”

For the past year, I have been putting this thesis to the test, enabling people to interact with and change the content that I deliver. It’s been a lot of fun and work, and now you get to see the result.

Ratings And Feedback

When we look at current systems of rating and feedback at conferences, most of them are reactive, meaning that participants rate the session after it’s over. This is why mostpeople don’t even rate sessions, unless they are asked or forced to by the doorkeeper. Those who do rate sessions might not care to be accurate (giving all 5s or 4s and then hurrying to the coffee line). Other attendees might have enjoyed the majority of the talk, but then got upset by the last slide or by the way the speaker ended the talk.

If these people decided to rate the presentation, how many stars do you think they would give? Perhaps 3 or 4 stars because of their anger at the end, but who really knows? Without context, a low rating doesn’t tell the speaker which part of the talk an attendee didn’t like.

Real-time feedback gives context to a rating, making traditional feedback unnecessary. Conference organizers and speakers no longer have to rely solely on Twitter hash tags and reactive ratings to see how well things went. We can now visualize exactly how the audience felt at any millisecond of a presentation.

REAL-TIME RATINGS WITH ONSLYDE

Giving a presentation that allows for real-time feedback is like riding a bicycle down a really steep hill for the first time. You have no idea whether you will crash and burn or come out with an adrenaline-filled scream at the other end. And it’s just as much fun for the audience.

With Onslyde, you get an accurate measure from the audience while you’re speaking. If you see that a lot of people are disagreeing with what you’re saying, you can adapt: Ask audience members for their thoughts, or maybe move on to another topic. When you’re presenting, actually seeing how the audience collectively feels in real time and then responding accordingly is a very cool experience.

The worst thing a presenter can do is tell the audience something it already knows or say something totally wrong. The audience wants to be intrigued and entertained by you. But too many speakers seem to think this requires GIFs of cats. Well, I don’t want to spoil anyone’s fun but I’m an adult now, and I don’t go to thousand-dollar conferences to look at funny pictures of cats. I do want to be able to challenge you, engage with the content and tell you when you’re wrong.

So, let’s talk about how the audience indicates whether you’re right or wrong. Onslyde’s remote control gives audience members between two and four ways to interact, depending on the type of session. It works for both normal presentations and panel discussions.

For presentations:

  • Any slide can be agreed or disagreed with.
  • Presenters can poll the audience by asking a question and giving two answers to choose from.
  • Remote devices are updated with content for each slide.

pres-remote-250-opt

For panel discussions:

  • Anyone (either panelist or audience member) can be agreed or disagreed with.
  • Votes are on a rolling average, meaning that they expire after 15 seconds (or a time specified by the moderator).
  • Any audience member can request to speak. This option will add them to a queue on the main screen.

panel-remote-250-opt

All remote devices receive real-time updates based on the slide or current speaker. In individual presentations, the speaker can specify content or images to send to all remotes per slide. For example, if a bulleted list is on the current slide, you could send it to the remotes by adding class="send" to the markup:

<section>
	<ul>
	<li>Onslyde makes you a better presenter.</li>
	</ul>
</section>

We’ll go over the technical details in the next section. For now, note how easy it is to broadcast markup to everyone’s remotes.

Edge Conf, hosted at Google’s New York office in October 2013, was the first time ever when the audience participated in real time (via Onslyde) during an entire conference.

edge1-panel-detail-500-opt

WHAT IF THERE’S NO WI-FI?

At this point, you might be thinking that this is a cool idea, but what about the 99% of conferences that have shaky or no Wi-Fi connectivity?

A presentation will work with or without an Internet connection; however, with no connection, it will just be like a normal presentation, and the audience would not be able to interact.

If you have a smartphone with a tetherable data plan, you can tether your laptop and enable Onslyde to connect from a mobile phone. It might sound crazy, but the lightweight messages that are sent across the wire still allow the presentation to run smoothly.

Even if Wi-Fi at a conference is unstable, the audience can always connect to the presentation with their mobile device’s data plan. I’ve personally given six or seven presentations with Onslyde and haven’t run into any issues so far.

Below you can see the network statistics from a talk I gave earlier this year. Even though the conference had stable Wi-Fi, attendees were connecting through their mobile data plan:

visits-by-provider-opt

Some have suggested bringing SMS into the picture, as a fallback. This would enable attendees to vote by sending text messages to the server. If you have other ideas, please leave them in the comments!

Building An Interactive Presentation

The Onslyde framework plugs into any HTML-based presentation system. For now, it supports presentations written with the following:

Currently, there is no nice WYSIWYG tool for building a slide deck. A tiny bit of HTML knowledge is required to build a presentation, but that’s it — no JavaScript or other programming. This is good because you don’t have to log into the system to edit your deck minutes before going on stage. You can change the markup at any time, and you will be ready to present after refreshing the browser.

The following use cases show how to add interactive elements to a presentation.

AUDIENCE MEMBERS VOTE ON (I.E. AGREE OR DISAGREE WITH) ANY SLIDE

This comes out of box, no set-up required. Onslyde is built around “slide groups.” “Agree” and “Disagree” votes can only be given once in a slide group. This lets you focus on a particular topic with multiple slides and not be bombarded with votes on each screen.

<section>
	<section>
		<p>Onslyde makes you a better presenter.</p>
	</section>
</section>

buttons-opt
The buttons above will become active once the presenter moves to a new slide group.

ASK UNLIMITED QUESTIONS WITH TWO ANSWERS

To create an onscreen poll, use the following markup:

<section>

	<section data-option="master">
		<h3>
		What is your favorite JavaScript framework?
		</h3>
	</section>

	<section data-option="jquery">
		<div>
		Time to talk about jQuery!
		</div>
	</section>

	<section data-option="querySelector">
		<div>
		Let’s go into more detail about core JavaScript!
		</div>
	</section>

</section>

By adding the markup above to your presentation, the bar chart in the following image will be automatically created and populated as votes are submitted:

favorite-javascript-framework-500-opt

The buttons at the top will illuminate on the remote controls, and attendees may start voting:

start-voting-opt

SEND CONTENT TO ALL DEVICES AT ANY TIME

Notice how the remote control’s image shows the title of the poll? As mentioned, this is because we added class="send" to the parent element of the heading.

<section>
	<h3>
	What is your favorite JavaScript framework?
	</h3>
…

start-voting-2-opt
The sent markup will appear below the button options.

You can also send something completely different than what is shown by the projector. Instead of sending the title of the poll, you could send the names of other JavaScript libraries for attendees to research:

<section>
	<h3>
	What is your favorite JavaScript framework?
	</h3>

	<div>
		<ul>
		<li>Zepto</li>
		<li>xui</li>
		<li>…</li>
		</ul>
	</div>

RAFFLE OR GIVEAWAY: RANDOM SELECTION OF AUDIENCE MEMBER

To me, one of the funnest parts of this tool is giving away swag at the end of a talk. You can randomly select anyone who is connected by adding the following link to your slide deck:

<a href="javascript:onslyde.slides.roulette()">Pick a winner</a>

winner-opt

Just place the link on the final slide and click it at the end of your talk. Everyone’s mobile device will flash red; then, the winner’s will turn yellow. Continue clicking to pick more people at random.

Best Practices

The easiest way to get started with each feature is to focus on how you want the audience to interact with you and your content. Here are some good rules of thumb that I have learned using this tool:

  • Start each presentation with a poll on the first or second slide. This allows the audience to get their device ready, get connected and start giving their opinion. It’s fun to pose a controversial question about your presentation topic and watch the votes pour in.
  • Polls can solicit a “Yes” or “No” feeling from the audience or go deeper. If you ask a question like, “Which JavaScript framework do you use?” and then give options for “AngularJS” or “Backbone”, you will be able to fork the slides. This is why preparing the slides beforehand is more rewarding. Onslyde will detect the majority vote, and when you go to the next slide, the losing slides will be removed from the presentation (temporarily) and only the slides from the winning option will be shown.

Getting Started

Presentations can be created at onslyde.com. And the open-source back end that manages a presentation’s feedback is hosted on GitHub. Anyone can run the server and host their own instance of the entire Onslyde stack if they wish.

All of the features mentioned are integrated in each demo to show use cases. To begin your first presentation, go to the “Getting Started” section, where you will have the option to run a demo presentation or sign in and create a private one.

onslyde.getting-started-500-opt
(Large view)

After you’ve signed in, the “Getting Started” page will give you options for setting up a poll. After hitting the “Preview” button, you must save the HTML to your computer in order to edit and present the slides.

setting-up-500-opt
(Large view)

An Onslyde presentation can be run anywhere. Essentially, it’s the same as saving a Keynote or PowerPoint presentation on your computer. Just click on the HTML file and run it from a Web browser — no need to install anything or run it from a server.

REGARDING KEYNOTE AND POWERPOINT

For those of you who use non-HTML slide decks, such as Keynote and PowerPoint, there are plans to create a transparent overlay on top of Windows and Mac OS X. This will allow presenters to use any presentation tool and to collect feedback in real time.

Understanding Audience Feedback

After your talk is over, you can view the analytics for your session. The votes for each slide are recorded so that you can track your performance at a fine-grained level.

chart1-500-opt

For questions that the audience voted on, each option is recorded, along with the “Agree” and “Disagree” votes for each poll.

chart2-500-opt

The other important pieces of data come from Google Analytics, including engagement times and what kinds of devices and mobile networks were used.

analytics-500-opt
Google Analytics showed that real-time feedback was provided not just from the people in the room. (Large view)

From the above image, we can see that 281 iOS devices and approximately 113 Android devices were used at Edge Conf. Because the event was streamed live, over twice as many unique devices appeared as there were people in the room — people were participating and providing real-time feedback as they watched the live stream from their home or office around the world!

One other cool piece to this analytics puzzle is mapping voting times to the video. Edge Conf was streamed live, and the videos were subsequently added to YouTube. After finding a reference point in the video and calculating the starting time, we were able to map the charts to the videos. If you clicked on any data point from a session, the video would skip to that voting period. Try it out for yourself.

voting-period-500-opt
We were able to map the voting times to the conference videos. (Large view)

Conclusion

As we’ve seen, so much more valuable data can be pulled from the conferences we attend. Not only can we get an accurate snapshot of how speakers performed and how audience members felt, but we can change the learning experience for everyone.

This project is a use case for the mobile Web and a playground for the new technologies that are available to modern Web browsers, such as WebSockets and WebRTC. Many other companies and conferences provide similar services using native apps and proprietary hardware, but the convenience of opening a Web browser and being exposed to a new level of education and interaction is immeasurable.

Many thanks to Andrew Betts for helping me with this article and providing great feedback. He is also responsible for most of the features in the Onslyde panel and remote interfaces.

(al, ea)

PHP Mail Function

Send Mail in PHP

PHP provides inbuilt function known as mail() function which allow you to send mail directly from your program. This function returns TRUE on success and FALSE when failed. Mail function accept 5 parameters like to, subject, message, headers and  extra parameters.

Syntax
mail(to,subject,message,headers,parameters)
Name Required Description
to Yes Mail Address of the receiver or receivers
subject Yes Subject of the mail content.
message Yes Mail message. This can either be plain text or HTML formatted mail.
headers No Additional headers can be From, Cc, and Bcc etc.
parameters No Additional parameter to the send mail program.

PHP Simple Mail Function

The following example will send simple text mail.

<?php
$to = "receiver@domain.com";
$subject = "This is a test mail";
$message = "Hi! This is a simple text mail to test";
$from = "sender@a2zwebhelp.com";
$headers = "From:" . $from;
$result = mail($to,$subject,$message,$headers);
if($result==true){
   echo "Mail Sent successfully.";
}
?>

PHP Send Mail With CC and BCC

<?php
$to = "receiver@domain.com";
$cc = "CCto@domain.com";
$bcc = "BCCto@domain.com";
$subject = "This is a test mail with CC and BCC";
$message = "Hi! This is a simple text mail to test with CC and BCC";
$from = "sender@a2zwebhelp.com";
$headers = "From:" . $from . "\r\n";
$headers .= "CC:" . $cc . "\r\n";
$headers .= "BCC:" . $bcc. "\r\n";
$result = mail($to,$subject,$message,$headers);
if($result==true){
   echo "Mail Sent successfully.";
}
?>

PHP Send HTML Mail

<?php
$to = "receiver@domain.com";
$cc = "CCto@domain.com";
$bcc = "BCCto@domain.com";
$subject = "This is a test mail with CC and BCC";
$from = "sender@a2zwebhelp.com";
$message = "<table border="0" width="100%" cellspacing="0" cellpadding="0" 
style="font-family: Verdana; font-size: 8pt">
<tr>
<td colspan="2" align="center" height="40"><b>Simple HTML Mail</b></td>
</tr>
<tr>
<td height="25" width="10%">&nbsp;</td>
<td height="25">Mail content goes here. </td>
</tr>
<tr>
<td height="25" width="10%">&nbsp;</td>
<td height="25">Mail second line goes here.</td>
</tr>
</table>";
// Set content-type when sending HTML email
$headers = "MIME-Version: 1.0" . "\r\n";
$headers .= "Content-type:text/html;charset=UTF-8" . "\r\n";

$headers .= "From:" . $from . "\r\n";
$headers .= "CC:" . $cc . "\r\n";
$headers .= "BCC:" . $bcc. "\r\n";
$result = mail($to,$subject,$message,$headers);
if($result==true){
   echo "Mail Sent successfully.";
}
?>

Send mail with attachment

Click here to know how to send mail with attachment.