Webologist, Software Craftsperson, Mom to 2 Daughters, Musician, Watercolourist
12 stories
·
1 follower

Nigeria: Satellite images show horrific scale of recent Boko Haram massacre

1 Share
The Nigerian government disputes reports that some 2,000 were killed, and says "only 150" died. Read the rest
Read the whole story
tamouse
3384 days ago
reply
Mendota Heights, MN, USA
Share this story
Delete

The More You Fail, The Greater Your Success: A User-Centered Design Case Study

1 Share

Jeffrey Zeldman once said, “Usability is like love. You have to care, you have to listen, and you have to be willing to change. You’ll make mistakes along the way, but that’s where growth and forgiveness come in.” Steve Jobs once said, “Design is a funny word. Some people think design means how it looks. But of course, if you dig deeper, it’s really how it works.” If you think of design as a tool it becomes a much more powerful skill set and by putting the user first you can transform your product into something truly remarkable.

This article will be an introduction to the human-centered design process. I’ll tell a personal story in which I built a challenged family member a device to help them communicate more efficient and effortlessly and I’ll share lessons I learned from the failures and successes along the way. Before the story starts, I’d like you to keep one thing in mind. The definition of User-Centered Design.

“Usability is like love. You have to care, you have to listen, and you have to be willing to change. You’ll make mistakes along the way, but that’s where growth and forgiveness come in.”

Jeffrey Zeldman1

The Learning

Dustin is my 30-year-old brother. He was born with little to no oxygen in his body and underwent two open-heart surgeries before his 10th birthday. He is also autistic. As a result of those health issues combined with the autism, he is not able to speak. Many wrongly confuse his silence with a lack of knowledge and understanding. But Dustin knows what’s going on. He probably has more thoughts than you and me; he just can’t express them fully.

My brother and me when we were children.
My brother and me when we were children. (Image credit: Matt Reamer 3128252219161131 3229262320171242 )

Growing up, I have always seen how much Dustin has struggled to communicate a simple word to anyone in our family, much less a random stranger. He makes only a few sounds, and only one person on Earth mildly understands what he’s trying to say — a magnificent woman I call Mom. I can only imagine the way he feels when she is not at his side to speak for him. So, I’ve always said that if a genie were to grant me three wishes, I would need just one: to make my brother “normal.” But, as time has passed and Dustin and I have gotten older, I have come to realize that “normal” does not exist, and if it does, then maybe he is a bit more normal than me.

Fast forward 20 years, to my last year of graduate school. I was six months away from moving across the country. With this knowledge I had, I wanted to design a tool that would help my brother to communicate his needs and that would relieve a little stress on my family as a whole.

Using my skills as an experience designer and having worked in web development, I planned to build Dustin a web app that would live on an iPad. In its simplest form, he would be able to tap the picture associated with his immediate need, and that tap gesture would trigger an audio file to speak the need to my parents. Simple, right? Everything was set, so I began sketching away and looking for inspiration to get started.

A couple of pages of the early sketch concepts for this tool. 2 3
A couple of pages of the early sketch concepts for this tool. (Image credit: Matt Reamer 3128252219161131 3229262320171242 ) (View large version 4 5 )

Little did I know that I hadn’t been thinking about how Dustin would actually utilize the functionality of the app. One night at my parents’ house for dinner, the idea popped into my head. Dustin grabbed my phone and started tapping randomly. As I watched him continue to struggle with the phone, I realized that I had already failed. My idea was already dead. That was when I grasped the true importance of a discovery phase in any project.

Lesson 1: Know the End User Inside Out

Observe, research, test, repeat. A “day in the life” of your end user persona is a helpful exercise to do in the research or discovery phase. With persona mapping, you — the designer — choose one representative user and design to their daily needs. The user may be fictitious or real, but most of us use a fictitious persona who brings together attributes from a larger segment. Mapping out this persona’s daily routine during a 24-hour period will help us to pinpoint holes in their routine where your product would be most useful to them, and possibly also help us to find ways to improve other aspects of their daily life in the process. In web design, a day-in-the-life exercise can be beneficial when the budget or time for focus groups is limited. It provides opportunities for key findings on a product’s structure. Jeffrey Rubin identifies a few usability objectives 5 6 :

  • Usefulness
    product enables user to achieve their goals — the tasks that it was designed to carry out and/or wants needs of user.
  • Effectiveness (ease of use)
    quantitatively measured by speed of performance or error rate and is tied to a percentage of users.
  • Learnability
    user’s ability to operate the system to some defined level of competence after some predetermined period of training. Also, refers to ability for infrequent users to relearn the system.
  • Attitude (likeability)
    user’s perceptions, feelings and opinions of the product, usually captured through both written and oral communication.
You need to understand your ideal user base, what makes them tick, their motivations, fears, influencers, etc.
You need to understand your ideal user base, what makes them tick, their motivations, fears, influencers, etc. (Image credit: Ben Yoskovitz 6 7 )

Lesson 2: The Pivot

For a company to succeed, it should frequently test its product-related predictions or prototypes with users. This will help to validate or contradict any assumptions made during the previous design phases. In subsequent design phases, we use the insights and learning gathered during user testing to pivot the company’s business model or the product’s utility and features.

Pivoting is scary, but don’t be afraid. More often than not, when all of your research and testing is leaning towards it, pivoting the product is the best thing to do. All of the hard work you’ve put into the first round might seem like a waste at first, but think of it as an investment into a better and more user-centric product, one that will now better cater to its consumers and that will prolong the product’s life cycle.

Luckily, I was able to reign in my excitement and focus on the user quickly. As I did more research on autism spectrum disorder (ASD), I recognized behaviors in Dustin that I had always seen but never understood. The majority of people with ASD have difficulty processing everyday sensory information such as sounds, touch, sights and smells. This is usually called sensory integration difficulties, or sensory sensitivity. As the National Autistic Society of the UK explains 7 8 , it can profoundly affect a person’s life.

With that in mind, I began to observe Dustin and track his behaviors. I interviewed his closest caregivers at home, showed him different styles of icons (thanks, NounProject 8 9 ) and tested colors, palettes and textures. I had to take many variables into consideration. The communication device had to be simple to use, be durable, be visually inviting, use some sort of icon language and have a texture similar to what he is used to in his everyday life. This was important because most people affected by autism prefer order and need a routine schedule.

Something else I had to keep in mind was that Dustin would not be the only user. Communication requires two parties. So, I started to view my mom not as a caregiver, but as a user. This revealed the struggles she has faced and enabled me to compare the needs of both end users and create something that bridges this communication barrier.

“Dustin and I communicate pretty well, but that’s after 30 years of being together all day, every day. Sometimes it takes a few minutes of Dustin struggling to express what he wants or needs. I get scared and wonder what he would do if something happened to me? The way we communicate is strange and no one, not even your dad, gets it.”

– Missy Reamer (Mom)

Another factor was thrown my way. My mom mentioned that she was worried for the day when they might not be together, and even worried about the simple pleasure of a vacation. The device had to not only be able to communicate to my mom in a timely manner, but also be interchangeable for other caregivers upon request. To achieve this, I decided to use SMS or, as the general public calls it, text messaging. I had worked on previous projects using SMS, so I was familiar with programming it onto the Arduino board.

You might be wondering what an Arduino board is? It is a small microcontroller whose hardware is an open-sourced board designed around an 8-bit Atmel AVR microcontroller or a 32-bit Atmel ARM. In other words, these various boards serve different purposes but all generally enable you to build interactive objects that serve various functions through APIs and Arduino libraries.

Although there are multiple ways to supply the 5 volts needed to power an Arduino, the best method for my purpose was to connect it to a computer via USB, so that anyone with minimal computer skills would be able to easily plug it in and simply change or add a phone number connected to the device for the day, week, etc. Once I had completed the programming, it would become a simple plug-and-play device.

Now that I knew how the device would work, it was time to move on to the look and feel of the exterior. The surface of the device was blue, and the shell for the electronics was already made. I took it to my parents’ home to show it off to everyone, and Dustin seemed really uninterested and kind of turned off. Frustration set in. Come on, work with me here, Dustin!

Then, another pivotal moment. I noticed Dustin standing in front of the mirror staring at himself. Remembering back to our childhood, I know that he had always been drawn to shiny metallic things: the toy guns he used to play with, the micro machine cars that were strewn around the house, his obsession with new cars, the bouncy new balls he got out of the 25-cent machine. I was onto something. I showed him the metal top of a project enclosure from RadioShack 9 10 , and he loved it.

A metallic surface was the perfect answer because it is smooth to the touch and the all-white external shell is soft on his eyes. This was another lesson. If your product is changing as a result of user observation or feedback, then it’s changing for the better.

Lesson 3: Keep Asking “Why”

The more you ask yourself “Why” while designing an experience, the more successful it will be. “Why” is a powerful question that I’ve gotten into the habit of asking myself when designing experiences. “Why is this here?” “Why should it animate like this?” “Why does the end user care?” Why, why, why. Always have a reason for the design decisions you make.

This is an early prototype I worked on, until I realized it was a failure. Failure is a good thing, though. 10 11
This is an early prototype I worked on, until I realized it was a failure. Failure is a good thing, though. (Image credit: Matt Reamer 3128252219161131 3229262320171242 ) (View large version 12 13 )

The Build

At this point, the product had gone through quite an evolution. It started as a web app concept that requires the physical presence of a caregiver, and it evolved into a device with push buttons that sends text messages to the recipient in real time. Using an Arduino Yun 13 14 and the Temboo library 14 15 allowed me to get free SMS service through a Wi-Fi connection. With the exterior shell for the electronics in place, it was time to roll out functionality for the SMS delivery. Sending an SMS requires some sort of trigger or button.

The choice of arcade buttons was on purpose because their large size would minimize errors and incorrect button hits. Having a tactility that is familiar to Dustin and comfortable for him was also important. Because he has always had a passion for and deep understanding of video games, the arcade-style buttons make perfect sense. On top of each button is an icon representing the six particular needs in his day-to-day life that stood out for my mom in my interview with her.

The build as it progressed and became powered by an Arduino Yun. 15 16
The build as it progressed and became powered by an Arduino Yun. (Image credit: Matt Reamer 3128252219161131 3229262320171242 ) (View large version 17 18 )

I had to keep in mind some other important factors, too. The product had to have all of the following:

  • a visual indication that a message was successfully sent,
  • a modular design that is easily mountable and moveable by my parents,
  • a push button that has an audible sound to signify its hit state,
  • 5-volt low-energy consumption.
An early demo of the communication device I built for my brother. 18 19
An early demo of the communication device I built for my brother. (Image credit: Matt Reamer 3128252219161131 3229262320171242 ) (View video 20 21 )

The requirements above are necessary, all for different reasons. Because it is a physical device with no screens, I had to create the simplest way to let Dustin know that an SMS has gone through successfully. This is where the red and green LED lights come in, as well as the recognizable sound that the arcade buttons trigger in their hit states. Still thinking of my parents as secondary users, I had to make sure that the device was low on energy consumption and easy to move because Dustin is not always in one place. In this process, known as future-proofing, the creator thinks of all possible scenarios in which the device might be used in future.

Lesson 4: The Future

If you always think five years ahead (or more), your iteration will be golden. Users of your product are forever evolving, as are their needs. If a product doesn’t iterate and evolve, then its relevance will fade away. My observation of the user (Dustin) operating this device over the next five years will be the basis for future iteration.

Learn from Blockbuster. A big brand let a little startup (heard of Netflix?) creep in their backyard, pitch a tent and camp out. Soon enough, that tent transformed into a shed, which then transformed into a guest house, and eventually the startup demolished the existing house and constructed its own — all this because Blockbuster did not evolve its product. Sorry, Blockbuster, I used to love you.

The inside and outside of the shell prior to soldering. 21 22
The inside and outside of the shell prior to soldering. (Image credit: Matt Reamer 3128252219161131 3229262320171242 ) (View large version
23)The build as it progressed and became powered by an Arduino Yun.24 24)The build as it progressed and became powered by an Arduino Yun.25
The build as it progressed and became powered by an Arduino Yun. (Image credit: Matt Reamer 3128252219161131 3229262320171242 ) (View large version
26)The choice of arcade buttons was due to their large size, which minimizes error and incorrect button hits.27 27)The choice of arcade buttons was due to their large size, which minimizes error and incorrect button hits.28
The choice of arcade buttons was due to their large size, which minimizes error and incorrect button hits. Having a tactility that Dustin is familiar and comfortable with was also important, and playing video games fills his spare time. (Image credit: Matt Reamer 3128252219161131 3229262320171242 ) (View large version 29 30 )

Back to Dustin. The way the product stands now is not how it will remain, for this is a forever evolving experience and I plan to continue to further it. Future plans are to build a back-end CMS where, after my brother and parents have learned a mode of communication for certain needs together, they can swap the meanings and icons of particular buttons. If we do not have an icon that represents a particular need, we will quickly create an icon for it. This way, the library of icons will grow in an efficient and user-centered way. For a more personal experience, the user could upload a photo into a graphical OLED button for quick communication. Refinement of the construction of the device itself is also planned.

This device was designed and built just for my brother, but variations could help thousands of people who are challenged by speech or motor-function disabilities. In a world where we can customize our Nike’s for only $100, why does it cost upwards of a thousand dollars for someone with a life-altering disability to have what many fortunate people are born with, speech?

The website that is being built will hopefully house the future of this helpful and cheap product. 30 31
The website that is being built will hopefully house the future of this helpful and cheap product. (Image credit: Matt Reamer 3128252219161131 3229262320171242 ) (View large version 32 33 )

I think variations of this device could help many others. I anticipate creating dozens of use cases and design icons for future users to load onto their devices when connected via Bluetooth, micro-USB or Wi-Fi.

Dustin’s SMS device 33 34

Conclusion

In designing this experience for Dustin, I’ve learned much more about him than I had previously known. Putting user-centered design truly into practice takes research, followed by iteration and testing. Iteration is the key to making any product, experience or service useful. But that experience means little without love: love for what you do and respect for who you are doing it for. I hope that this has inspired or at least has gotten you thinking about how you can push your skills to help the people around you, as well as shed some light on some basic principles of user experience design that are too often overlooked.

The source code and schematics for this project are available on Github 34 . Feel free to interpret this project in your own way to help someone you know.

To conclude, one of my favorite quotes, from author James Jesse Garrett:

“User-centered design means understanding what your users need, how they think, and how they behave — and incorporating that understanding into every aspect of your process.”

Further resources

(cc, al, ml)

Footnotes

  1. 1 http://iamtheream.io/dustinsWords.html
  2. 2 http://www.smashingmagazine.com/wp-content/uploads/2014/11/02-sketches-opt.jpg
  3. 3 http://iamtheream.io/dustinsWords.html
  4. 4 http://www.smashingmagazine.com/wp-content/uploads/2014/11/02-sketches-opt.jpg
  5. 5 http://www.w3.org/WAI/redesign/ucd
  6. 6 http://www.instigatorblog.com/day-in-the-life/2011/04/26/
  7. 7 http://www.autism.org.uk/sensory
  8. 8 http://thenounproject.com/
  9. 9 http://www.radioshack.com/product/index.jsp?productId=2062285
  10. 10 http://www.smashingmagazine.com/wp-content/uploads/2014/11/04-early-prototype-opt.jpg
  11. 11 http://iamtheream.io/dustinsWords.html
  12. 12 http://www.smashingmagazine.com/wp-content/uploads/2014/11/04-early-prototype-opt.jpg
  13. 13 http://arduino.cc/en/Main/ArduinoBoardYun?from=Products.ArduinoYUN
  14. 14 https://www.temboo.com/arduino/yun/getting-started
  15. 15 http://www.smashingmagazine.com/wp-content/uploads/2014/11/05-build-opt.jpg
  16. 16 http://iamtheream.io/dustinsWords.html
  17. 17 http://www.smashingmagazine.com/wp-content/uploads/2014/11/05-build-opt.jpg
  18. 18 http://instagram.com/p/lFkeu-DnfS/?modal=true
  19. 19 http://iamtheream.io/dustinsWords.html
  20. 20 http://instagram.com/p/lFkeu-DnfS/?modal=true
  21. 21 http://www.smashingmagazine.com/wp-content/uploads/2014/11/06-build-opt.jpg
  22. 22 http://iamtheream.io/dustinsWords.html
  23. 23 http://www.smashingmagazine.com/wp-content/uploads/2014/11/06-build-opt.jpg
  24. 24 http://www.smashingmagazine.com/wp-content/uploads/2014/11/07-build-opt.jpg
  25. 25 http://iamtheream.io/dustinsWords.html
  26. 26 http://www.smashingmagazine.com/wp-content/uploads/2014/11/07-build-opt.jpg
  27. 27 http://www.smashingmagazine.com/wp-content/uploads/2014/11/08-build-opt.jpg
  28. 28 http://iamtheream.io/dustinsWords.html
  29. 29 http://www.smashingmagazine.com/wp-content/uploads/2014/11/08-build-opt.jpg
  30. 30 http://www.smashingmagazine.com/wp-content/uploads/2014/11/09-in-progress-web-site-opt.png
  31. 31 http://iamtheream.io/dustinsWords.html
  32. 32 http://www.smashingmagazine.com/wp-content/uploads/2014/11/09-in-progress-web-site-opt.png
  33. 33 http://vimeo.com/96746468
  34. 34 https://github.com/iamtheream/brothersSmsDevice
  35. http://alistapart.com/article/tacklingusability
  36. 2 http://iamtheream.io/dustinsWords.html
  37. 3 http://www.smashingmagazine.com/wp-content/uploads/2014/11/02-sketches-opt.jpg
  38. 4 http://iamtheream.io/dustinsWords.html
  39. 5 http://www.smashingmagazine.com/wp-content/uploads/2014/11/02-sketches-opt.jpg
  40. 6 http://www.w3.org/WAI/redesign/ucd
  41. 7 http://www.instigatorblog.com/day-in-the-life/2011/04/26/
  42. 8 http://www.autism.org.uk/sensory
  43. 9 http://thenounproject.com/
  44. 10 http://www.radioshack.com/product/index.jsp?productId=2062285
  45. 11 http://www.smashingmagazine.com/wp-content/uploads/2014/11/04-early-prototype-opt.jpg
  46. 12 http://iamtheream.io/dustinsWords.html
  47. 13 http://www.smashingmagazine.com/wp-content/uploads/2014/11/04-early-prototype-opt.jpg
  48. 14 http://arduino.cc/en/Main/ArduinoBoardYun?from=Products.ArduinoYUN
  49. 15 https://www.temboo.com/arduino/yun/getting-started
  50. 16 http://www.smashingmagazine.com/wp-content/uploads/2014/11/05-build-opt.jpg
  51. 17 http://iamtheream.io/dustinsWords.html
  52. 18 http://www.smashingmagazine.com/wp-content/uploads/2014/11/05-build-opt.jpg
  53. 19 http://instagram.com/p/lFkeu-DnfS/?modal=true
  54. 20 http://iamtheream.io/dustinsWords.html
  55. 21 http://instagram.com/p/lFkeu-DnfS/?modal=true
  56. 22 http://www.smashingmagazine.com/wp-content/uploads/2014/11/06-build-opt.jpg
  57. 23 http://iamtheream.io/dustinsWords.html
  58. 24 http://www.smashingmagazine.com/wp-content/uploads/2014/11/06-build-opt.jpg
  59. 25 http://www.smashingmagazine.com/wp-content/uploads/2014/11/07-build-opt.jpg
  60. 26 http://iamtheream.io/dustinsWords.html
  61. 27 http://www.smashingmagazine.com/wp-content/uploads/2014/11/07-build-opt.jpg
  62. 28 http://www.smashingmagazine.com/wp-content/uploads/2014/11/08-build-opt.jpg
  63. 29 http://iamtheream.io/dustinsWords.html
  64. 30 http://www.smashingmagazine.com/wp-content/uploads/2014/11/08-build-opt.jpg
  65. 31 http://www.smashingmagazine.com/wp-content/uploads/2014/11/09-in-progress-web-site-opt.png
  66. 32 http://iamtheream.io/dustinsWords.html
  67. 33 http://www.smashingmagazine.com/wp-content/uploads/2014/11/09-in-progress-web-site-opt.png
  68. 34 http://vimeo.com/96746468
  69. 35 http://www.iso.org/iso/catalogue_detail.htm?csnumber=52075

The post The More You Fail, The Greater Your Success: A User-Centered Design Case Study appeared first on Smashing Magazine.

Read the whole story
tamouse
3419 days ago
reply
Mendota Heights, MN, USA
Share this story
Delete

Making A Complete Polyfill For The HTML5 Details Element

1 Share

HTML5 introduced a bunch of new tags, one of which is <details>. This element is a solution for a common UI component: a collapsible block. Almost every framework, including Bootstrap and jQuery UI, has its own plugin for a similar solution, but none conform to the HTML5 specification — probably because most were around long before <details> got specified and, therefore, represent different approaches. A standard element allows everyone to use the same markup for a particular type of content. That’s why creating a robust polyfill makes sense1.

Disclaimer: This is quite a technical article, and while I’ve tried to minimize the code snippets, the article still contains quite a few of them. So, be prepared!

Existing Solutions Are Incomplete

I’m not2 the first person3 to try to implement such a polyfill. Unfortunately, all other solutions exhibit one or another problem:

  1. No support for future content
    Support for future content is extremely valuable for single-page applications. Without it, you would have to invoke the initialization function every time you add content to the page. Basically, a developer wants to be able to drop <details> into the DOM and be done with it, and not have to fiddle with JavaScript to get it going.
  2. Not a true polyfill for the open attribute
    According to the specification4, <details> comes with the open attribute, which is used to toggle the visibility of the contents of <details>.
  3. The toggle event is missing
    This event is a notification that a details element has changed its open state. Ideally, it should be a vanilla DOM event.

In this article we’ll use better-dom5 to make things simpler. The main reason is the live extensions6 feature, which solves the problem of invoking the initialization function for dynamic content. (For more information, read my detailed article about live extensions7.) Additionally, better-dom outfits live extensions with a set of tools that do not (yet) exist in vanilla DOM but that come in handy when implementing a polyfill like this one.

1-details-element-in-Safari-8-opt8
The details element in Safari 8 (View large version18149)

Check out the live demo10.

Let’s take a closer look at all of the hurdles we have to overcome to make <details> available in browsers that don’t support it.

Future Content Support

To start, we need to declare a live extension for the "details" selector. What if the browser already supports the element natively? Then we’ll need to add some feature detection. This is easy with the optional second argument condition, which prevents the logic from executing if its value is equal to false:

// Invoke extension only if there is no native support
var open = DOM.create("details").get("open");

DOM.extend("details", typeof open !== "boolean", {
  constructor: function() {
    console.log("initialize <details>…");
  }
});

As you can see, we are trying to detect native support by checking for the open property, which obviously only exists in browsers that recognize <details>.

What sets DOM.extend11 apart from a simple call like document.querySelectorAll is that the constructor function executes for future content, too. And, yes, it works with any library for manipulating the DOM:

// You can use better-dom…
DOM.find("body").append(
  "<details><summary>TITLE</summary><p>TEXT</p></details>");
// => logs "initialize <details>…"

// or any other DOM library, like jQuery…
$("body").append(
  "<details><summary>TITLE</summary><p>TEXT</p></details>");
// => logs "initialize <details>…"

// or even vanilla DOM.
document.body.insertAdjacentElement("beforeend",
  "<details><summary>TITLE</summary><p>TEXT</p></details>");
// => logs "initialize <details>…"

In the following sections, we’ll replace the console.log call with a real implementation.

Implementation Of <summary> Behavior

The <details> element may take <summary> as a child element.

The first summary element child of details, if one is present, represents an overview of details. If no child summary element is present, then the user agent should provide its own legend (for example, “Details”).

Let’s add mouse support. A click on the <summary> element should toggle the open attribute on the parent <details> element. This is what it looks like using better-dom:

DOM.extend("details", typeof open !== "boolean", {
  constructor: function() {
    this
      .children("summary:first-child")
      .forEach(this.doInitSummary);
  },
  doInitSummary: function(summary) {
    summary.on("click", this.doToggleOpen);
  },
  doToggleOpen: function() {
    // We’ll cover the open property value later.
    this.set("open", !this.get("open"));
  }
});

The children method returns a JavaScript array of elements (not an array-like object as in vanilla DOM). Therefore, if no <summary> is found, then the doInitSummary function is not executed. Also, doInitSummary and doToggleOpen are private functions12, they always are invoked for the current element. So, we can pass this.doInitSummary to Array#forEach without extra closures, and everything will execute correctly there.

Having keyboard support in addition to mouse support is good as well. But first, let’s make <summary> a focusable element. A typical solution is to set the tabindex attribute to 0:

doInitSummary: function(summary) {
  // Makes summary focusable
  summary.set("tabindex", 0);
  …
}

Now, the user hitting the space bar or the “Enter” key should toggle the state of <details>. In better-dom, there is no direct access to the event object. Instead, we need to declare which properties to grab using an extra array argument:

doInitSummary: function(summary) {
  …
  summary.on("keydown", ["which"], this.onKeyDown);
}

Note that we can reuse the existing doToggleOpen function; for a keydown event, it just makes an extra check on the first argument. For the click event handler, its value is always equal to undefined, and the result will be this:

doInitSummary: function(summary) {
  summary
    .set("tabindex", 0)
    .on("click", this.doToggleOpen)
    .on("keydown", ["which"], this.doToggleOpen);
},
doToggleOpen: function(key) {
  if (!key || key === 13 || key === 32) {
    this.set("open", !this.get("open"));
    // Cancel form submission on the ENTER key.
    return false;
  }
}

Now we have a mouse- and keyboard-accessible <details> element.

<summary> Element Edge Cases

The <summary> element introduces several edge cases that we should take into consideration:

1. When <summary> Is a Child But Not the First Child

2-summary-element-is-not-the-first-child-opt13
What the Chrome browser outputs when the summary element is not the first child. (View large version18149)

Browser vendors have tried to fix such invalid markup by moving <summary> to the position of the first child visually, even when the element is not in that position in the flow of the DOM. I was confused by such behavior, so I asked the W3C for clarification15. The W3C confirmed that <summary> must be the first child of <details>. If you check the markup in the screenshot above on Nu Markup Checker16, it will fail with the following error message:

Error: Element summary not allowed as child of element details in this context. […] Contexts in which element summary may be used: As the first child of a details element.

My approach is to move the <summary> element to the position of the first child. In other words, the polyfill fixes the invalid markup for you:

doInitSummary: function(summary) {
  // Make sure that summary is the first child
  if (this.child(0) !== summary) {
    this.prepend(summary);
  }
  …
}

2. When the <summary> Element Is Not Present

3-summary-element-does-not-exist-opt17
What the Chrome browser outputs when the summary element is not present (View large version18149)

As you can see in the screenshot above, browser vendors insert “Details” as a legend into <summary> in this case. The markup stays untouched. Unfortunately, we can’t achieve the same without accessing the shadow DOM19, which unfortunately has weak support20 at present. Still, we can set up <summary> manually to comply with standards:

constructor: function() {
  …
  var summaries = this.children("summary");
  // If no child summary element is present, then the
  // user agent should provide its own legend (e.g. "Details").
  this.doInitSummary(
    summaries[0] || DOM.create("summary>`Details`"));
}

Support For open Property

If you try the code below in browsers that support <details> natively and in others that don’t, you’ll get different results:

details.open = true;
// <details> changes state in Chrome and Safari
details.open = false;
// <details> state changes back in Chrome and Safari

In Chrome and Safari, changing the value of open triggers the addition or removal of the attribute. Other browsers do not respond to this because they do not support the open property on the <details> element.

Properties are different from simple values. They have a pair of getter and setter functions that are invoked every time you read or assign a new value to the field. And JavaScript has had an API to declare properties since version 1.5.

The good news is that one old browser we are going to use with our polyfill, Internet Explorer (IE) 8, has partial support for the Object.defineProperty function. The limitation is that the function works only on DOM elements. But that is exactly what we need, right?

There is a problem, though. If you try to set an attribute with the same name in the setter function in IE 8, then the browser will stack with infinite recursion and crashes. In old versions of IE, changing an attribute will trigger the change of an appropriate property and vice versa:

Object.defineProperty(element, "foo", {
  …
  set: function(value) {
    // The line below triggers infinite recursion in IE 8.
    this.setAttribute("foo", value);
  }
});

So you can’t modify the property without changing an attribute there. This limitation has prevented developers from using the Object.defineProperty for quite a long time.

The good news is that I’ve found a solution.

Fix For Infinite Recursion In IE 8

Before describing the solution, I’d like to give some background on one feature of the HTML and CSS parser in browsers. In case you weren’t aware, these parsers are case-insensitive. For example, the rules below will produce the same result (i.e. a base red for the text on the page):

body { color: red; }
/* The rule below will produce the same result. */
BODY { color: red; }

The same goes for attributes:

el.setAttribute("foo", "1");
el.setAttribute("FOO", "2");
el.getAttribute("foo"); // => "2"
el.getAttribute("FOO"); // => "2"

Moreover, you can’t have uppercased and lowercased attributes with the same name. But you can have both on a JavaScript object, because JavaScript is case-sensitive:

var obj = {foo: "1", FOO: "2"};
obj.foo; // => "1"
obj.FOO; // => "2"

Some time ago, I found that IE 8 supports the deprecated legacy argument lFlags21 for attribute methods, which allows you to change attributes in a case-sensitive manner:

  • lFlags [in, optional]
    • Type: Integer
    • Integer that specifies whether to use a case-sensitive search to locate the attribute.

Remember that the infinite recursion happens in IE 8 because the browser is trying to update the attribute with the same name and therefore triggers the setter function over and over again. What if we use the lFlags argument to get and set the uppercased attribute value:

// Defining the "foo" property but using the "FOO" attribute
Object.defineProperty(element, "foo", {
  get: function() {
	return this.getAttribute("FOO", 1);
  },
  set: function(value) {
    // No infinite recursion!
    this.setAttribute("FOO", value, 1);
  }
});

As you might expect, IE 8 updates the uppercased field FOO on the JavaScript object, and the setter function does not trigger a recursion. Moreover, the uppercased attributes work with CSS too — as we stated in the beginning, that parser is case-insensitive.

Polyfill For The open Attribute

Now we can define an open property that works in every browser:

var attrName = document.addEventListener ? "open" : "OPEN";

Object.defineProperty(details, "open", {
  get: function() {
    var attrValue = this.getAttribute(attrName, 1);
    attrValue = String(attrValue).toLowerCase();
    // Handle boolean attribute value
    return attrValue === "" || attrValue === "open";
  }
  set: function(value) {
    if (this.open !== value) {
      console.log("firing toggle event");
    }

    if (value) {
      this.setAttribute(attrName, "", 1);
    } else {
      this.removeAttribute(attrName, 1);
    }
  }
});

Check how it works:

details.open = true;
// => logs "firing toggle event"
details.hasAttribute("open"); // => true
details.open = false;
// => logs "firing toggle event"
details.hasAttribute("open"); // => false

Excellent! Now let’s do similar calls, but this time using *Attribute methods:

details.setAttribute("open", "");
// => silence, but fires toggle event in Chrome and Safari
details.removeAttribute("open");
// => silence, but fires toggle event in Chrome and Safari

The reason for such behavior is that the relationship between the open property and the attribute should be bidirectional. Every time the attribute is modified, the open property should reflect the change, and vice versa.

The simplest cross-browser solution I’ve found for this issue is to override the attribute methods on the target element and invoke the setters manually. This avoids bugs and the performance penalty of legacy propertychange22 and DOMAttrModified23 events. Modern browsers support MutationObservers24, but that doesn’t cover our browser scope.

Final Implementation

Obviously, walking through all of the steps above when defining a new attribute for a DOM element wouldn’t make sense. We need a utility function for that which hides cross-browser quirks and complexity. I’ve added such a function, named defineAttribute25, in better-dom.

The first argument is the name of the property or attribute, and the second is the get and set object. The getter function takes the attribute’s value as the first argument. The setter function accepts the property’s value, and the returned statement is used to update the attribute. Such a syntax allows us to hide the trick for IE 8 where an uppercased attribute name is used behind the scenes:

constructor: function() {
  …
  this.defineAttribute("open", {
    get: this.doGetOpen,
    set: this.doSetOpen
  });
},
doGetOpen: function(attrValue) {
  attrValue = String(attrValue).toLowerCase();
  return attrValue === "" || attrValue === "open";
},
doSetOpen: function(propValue) {
  if (this.get("open") !== propValue) {
    this.fire("toggle");
  }
  // Adding or removing boolean attribute "open"
  return propValue ? "" : null;
}

Having a true polyfill for the open attribute simplifies our manipulation of the <details> element’s state. Again, this API is framework-agnostic:

// You can use better-dom…
DOM.find("details").set("open", false);

// or any other DOM library, like jQuery…
$("details").prop("open", true);

// or even vanilla DOM.
document.querySelector("details").open = false;

Notes On Styling

The CSS part of the polyfill is simpler. It has some basic style rules:

summary:first-child ~ * {
  display: none;
}

details[open] > * {
  display: block;
}

/*  Hide native indicator and use pseudo-element instead */
summary::-webkit-details-marker {
  display: none;
}

I didn’t want to introduce any extra elements in the markup, so obvious choice is to style the ::before pseudo-element. This pseudo-element is used to indicate the current state of <details> (according to whether it is open or not). But IE 8 has some quirks, as usual — namely, with updating the pseudo-element state. I got it to work properly only by changing the content property’s value itself:

details:before {
  content: '\25BA';
  …
}

details[open]:before {
  content: '\25BC';
}

For other browsers, the zero-border trick will draw a font-independent CSS triangle. With a double-colon syntax for the ::before pseudo-element, we can apply rules to IE 9 and above:

details::before {
  content: '';
  width: 0;
  height: 0;
  border: solid transparent;
  border-left-color: inherit;
  border-width: 0.25em 0.5em;
  …
  transform: rotate(0deg) scale(1.5);
}

details[open]::before {
  content: '';
  transform: rotate(90deg) scale(1.5);
}

The final enhancement is a small transition on the triangle. Unfortunately, Safari does not apply it for some reason (perhaps a bug), but it degrades well by ignoring the transition completely:

details::before {
  …
  transition: transform 0.15s ease-out;
}
4-details-element-animation26
A sample animation for the toggle triangle

You can find the full source code on Github27.

Putting It All Together

Some time ago, I started using transpilers in my projects, and they are great. Transpilers enhance source files. You can even code in a completely different language, like CoffeeScript instead of JavaScript or LESS instead of CSS etc. However, my intention in using them is to decrease unnecessary noise in the source code and to learn new features in the near future. That’s why transpilers do not go against any standards in my projects — I’m just using some extra ECMAScript 6 (ES6) stuff and CSS post-processors (Autoprefixer28 being the main one).

Also, to speak about bundling, I quickly found that distributing *.css files along with *.js is slightly annoying. In searching for a solution, I found HTML Imports29, which aims to solve this kind of problem in the future. At present, the feature has relatively weak browser support30. And, frankly, bundling all of that stuff into a single HTML file is not ideal.

So, I built my own approach for bundling: better-dom has a function, DOM.importStyles31, that allows you to import CSS rules on a web page. This function has been in the library since the beginning because DOM.extend uses it internally. Since I use better-dom and transpilers in my code anyway, I created a simple gulp task:

gulp.task("compile", ["lint"], function() {
  var jsFilter = filter("*.js");
  var cssFilter = filter("*.css");

  return gulp.src(["src/*.js", "src/*.css"])
    .pipe(cssFilter)
    .pipe(postcss([autoprefixer, csswring, …]))
     // need to escape some symbols
    .pipe(replace(/\\|"/g, "\\$&"))
     // and convert CSS rules into JavaScript function calls
    .pipe(replace(/([^{]+)\{([^}]+)\}/g,
      "DOM.importStyles(\"$1\", \"$2\");\n"))
    .pipe(cssFilter.restore())
    .pipe(jsFilter)
    .pipe(es6transpiler())
    .pipe(jsFilter.restore())
    .pipe(concat(pkg.name + ".js"))
    .pipe(gulp.dest("build/"));
});

To keep it simple, I didn’t put in any optional steps or dependency declarations (see the full source code32). In general, the compilation task contains the following steps:

  1. Apply Autoprefixer to the CSS.
  2. Optimize the CSS, and transform it into the sequence of DOM.importStyles calls.
  3. Apply ES6 transpilers to JavaScript.
  4. Concatenate both outputs to a *.js file.

And it works! I have transpilers that make my code clearer, and the only output is a single JavaScript file. Another advantage is that, when JavaScript is disabled, those style rules are completely ignored. For a polyfill like this, such behavior is desirable.

Closing Thoughts

As you can see, developing a polyfill is not the easiest challenge. On the other hand, the solution can be used for a relatively long time: standards do not change often and have been discussed at length behind the scenes. Also everyone is using the same language and is connecting with the same APIs which is a great thing.

With the common logic moved into utility functions, the source code is not very complex33. This means that, at present, we really lack advanced tools to make robust polyfills that work close to native implementations (or better!). And I don’t see good libraries for this yet, unfortunately.

Libraries such as jQuery, Prototype and MooTools are all about providing extra sugar for working with the DOM. While sugar is great, we also need more utility functions to build more robust and unobtrusive polyfills. Without them, we might end up with a ton of plugins that are hard to integrate in our projects. May be it’s time to move into this direction?

Another technique that has arisen recently is Web Components34. I’m really excited by tools like the shadow DOM, but I’m not sure if custom elements35 are the future of web development. Moreover, custom elements can introduce new problems if everyone starts creating their own custom tags for common uses. My point is that we need to learn (and try to improve) the standards first before introducing a new HTML element. Fortunately, I’m not alone in this; Jeremy Keith, for one, shares a similar view36.

Don’t get me wrong. Custom elements are a nice feature, and they definitely have use cases in some areas. I look forward to them being implemented in all browsers. I’m just not sure if they’re a silver bullet for all of our problems.

To reiterate, I’d encourage creating more robust and unobtrusive polyfills. And we need to build more advanced tools to make that happen more easily. The example with <details> shows that achieving such a goal today is possible. And I believe this direction is future-proof and the one we need to move in.

(al)

Footnotes

  1. 1 http://caniuse.com/#feat=details
  2. 2 https://github.com/mathiasbynens/jquery-details
  3. 3 https://github.com/manuelbieh/Details-Polyfill
  4. 4 http://www.w3.org/html/wg/drafts/html/master/interactive-elements.html#the-details-element
  5. 5 https://github.com/chemerisuk/better-dom
  6. 6 https://github.com/chemerisuk/better-dom/wiki/Live-extensions
  7. 7 http://www.smashingmagazine.com/2014/02/05/introducing-live-extensions-better-dom-javascript/
  8. 8 http://www.smashingmagazine.com/wp-content/uploads/2014/11/1-details-element-in-Safari-8-large-opt.jpg
  9. 9
  10. 10 http://chemerisuk.github.io/better-details-polyfill/
  11. 11 http://chemerisuk.github.io/better-dom/DOM.html#extend
  12. 12 https://github.com/chemerisuk/better-dom/wiki/Live-extensions#public-members-and-private-functions
  13. 13 http://www.smashingmagazine.com/wp-content/uploads/2014/11/2-summary-element-is-not-the-first-child-large-opt.jpg
  14. 14
  15. 15 http://lists.w3.org/Archives/Public/public-html/2014Nov/0043.html
  16. 16 http://validator.w3.org/nu/
  17. 17 http://www.smashingmagazine.com/wp-content/uploads/2014/11/3-summary-element-does-not-exist-large-opt.jpg
  18. 18
  19. 19 http://www.w3.org/TR/shadow-dom/
  20. 20 http://caniuse.com/#feat=shadowdom
  21. 21 http://msdn.microsoft.com/en-us/library/ie/ms536739(v=vs.85).aspx
  22. 22 http://msdn.microsoft.com/en-us/library/ie/ms536956(v=vs.85).aspx
  23. 23 https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Mutation_events
  24. 24 https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
  25. 25 http://chemerisuk.github.io/better-dom/$Element.html#defineAttribute
  26. 26 http://www.smashingmagazine.com/wp-content/uploads/2014/11/4-details-element-animation.gif
  27. 27 https://github.com/chemerisuk/better-details-polyfill/blob/master/src/better-details-polyfill.css
  28. 28 https://github.com/postcss/autoprefixer
  29. 29 http://www.html5rocks.com/en/tutorials/webcomponents/imports/
  30. 30 http://caniuse.com/#feat=imports
  31. 31 http://chemerisuk.github.io/better-dom/DOM.html#importStyles
  32. 32 https://github.com/chemerisuk/better-dom-boilerplate/blob/master/gulpfile.js#L34
  33. 33 https://github.com/chemerisuk/better-details-polyfill/blob/master/src/better-details-polyfill.js
  34. 34 http://webcomponents.org
  35. 35 http://w3c.github.io/webcomponents/spec/custom/
  36. 36 https://adactio.com/journal/7431

The post Making A Complete Polyfill For The HTML5 Details Element appeared first on Smashing Magazine.

Read the whole story
tamouse
3432 days ago
reply
Mendota Heights, MN, USA
Share this story
Delete

Supercell tornado in Nebraska: GIF

7 Comments and 22 Shares
hU9tbWQ

“Supercell tornado at West Point Nebraska.” via Reddit.

Read the whole story
tamouse
3447 days ago
reply
really neat anigif
Mendota Heights, MN, USA
popular
3447 days ago
reply
Share this story
Delete
5 public comments
awilchak
3445 days ago
reply
Oooooh
Brooklyn, New York
dreadhead
3445 days ago
reply
Neat!
Vancouver Island, Canada
hansolosays
3447 days ago
reply
can't stop watching
Norfolk, Virginia
jhamill
3447 days ago
reply
Mesmerizing
California
satadru
3447 days ago
reply
gorgeous
New York, NY

One-Pot 45 Minute Coq au Vin with Brown Butter Sage Mashed Potatoes.

1 Share
One-Pot 45 Minute Coq au Vin with Brown Butter Sage Mashed Potatoes-2

So all you classically trained chefs out there, don't hate me for this.

The post One-Pot 45 Minute Coq au Vin with Brown Butter Sage Mashed Potatoes. appeared first on Half Baked Harvest.

Read the whole story
tamouse
3449 days ago
reply
Mendota Heights, MN, USA
Share this story
Delete

$100K life-size T-Rex skeleton replica

1 Comment and 2 Shares


It's 40' long from nose to tail, is composed of 190 bones, is billed as "museum grade" and comes with an assembly crew that will stage it in any "anatomically possible" pose. His name is Stan.

Read the whole story
tamouse
3456 days ago
reply
Mendota Heights, MN, USA
Share this story
Delete
1 public comment
rewingau
3456 days ago
reply
Birthday list
Canberra, Australian Capital T
Next Page of Stories