Pure CSS Star Rating System

I often asked interview candidate to build something, and at the meantime I wish they should present something different – something using pure CSS3. JavaScript can do everything related to user interaction, but a lot of developers have not noticed how powerful CSS3 can do.

I believe there are many other ways to implement this star rating system. Here is a piece I wrote today using radio buttons.

HTML:

<div id=”container”>
<input id=”star-1″ type=”radio” name=”star” />
<input id=”star-2″ type=”radio” name=”star” />
<input id=”star-3″ type=”radio” name=”star” />
<input id=”star-4″ type=”radio” name=”star” />
<input id=”star-5″ type=”radio” name=”star” />
<div id=”stars”></div>
</div>

CSS:

#stars {
    height: 18px;
    width: 95px;
    background-image: url(stars-sprite.png);
    background-repeat: no-repeat;
    background-position: -98px -259px;
    position: absolute;
    top: 0;
    z-index: -1;
}
input {
    width: 15px;
    height: 15px;
    margin: 0;
    opacity: 0;
    cursor: pointer;
}
input#star-1:checked ~ div {
    background-position: -79px -259px;
}
input#star-2:checked ~ div {
    background-position: -60px -259px;
}
input#star-3:checked ~ div {
    background-position: -41px -259px;
}
input#star-4:checked ~ div {
    background-position: -22px -259px;
}
input#star-5:checked ~ div {
    background-position: -3px -259px;
}
#container {
    position: relative;
}

Demo on jsFiddle

HackDay Retrospective

Last week, I pitched my ideas for the Search and Discovery HackDay at my company, and luckily both of my ideas got picked. I decided to take one and compete for this event. I recruited two  of my colleagues to join me for this hack.

HackDay is intense. It requires good ideas, great skills and physical endurance, and of course leadership and team collaboration. At the same time, it is so fun and rewarding. It is like building your own business and implementing your own ideas in a quick way with low cost. That’s why I love this concept of HackDay. This was the second time I was competing for a 24-hour hackathon, and the first time I led a team for hacking my idea.

Here are the good and the bad (need to improve).

Good:

1. Planning early

I got the estimating skills from sprint meetings.

  • “Working backwards” (Write user stories)
  • Split the project into small actionable tasks;
  • Assign ownership;
  • Finish the learning/ramp-up tasks before the HackDay;
  • Have a check-in schedule;
  • Settle down the mock-up.

2. Checking in on time

Long span projects are easy to slip by due dates because of few check-in times. Check-in times make sure team members on the same page, and focus on reducing dependencies. Even we cannot finish the action items on time, it can be a good chance to communicate and gives us sense of urgency to finish the tasks promptly.

3. Teammates’ commitment

I was lucky that two of my team members made commitment to this challenge. Because it was out of work responsibility, I was really happy they stayed late with me to finish the work.

4. Refocusing quickly

My original idea was about an mobile app related to camera. Developing an Android app in 24 hours does not seem realistic. Luckily, I found an alternative solution using HTML5.

5. Great demo

The demo we had was really really good in my opinion. It was fun and engaging judges and audience.

6. Recruiting the right number

The maximum number of the team was 6, but I thought 3 was enough to complete this project. Bringing too many people on board might make the team hard to operate.

Bad:

1. Trust

I wish I could trust my teammates more. When we were very tired, and realized we failed to finish items according to the schedule, I was nervous, and prepared to finish my team member’s task if he could not finish. However, it turned out he was slow because he was trying to find an automatic way to get the results. Of course, he provided a good result, but I wish I could trust him more and I could focus on my action items.

2. Making it work first

I was so sensitive to the way I wrote code, that sometimes it took me time to fix namespacing, styles, variables, etc. What it matters at the end is a working demo with a good user experience, which is only judges and audience can see. Focus on those first, and let go the minor things. When you get a working demo, now it is your time to refine everything else.

A Tip on Learning a New Tech

I just found out a good way to learn new technologies. Besides doing some Hello World stuff, you can go find out the most popular questions being asked about this technology on the web. They are popular and with the most votes on Stack Overflow, which means many many people encounter these problems, and you would probably have the same. The popular answers are in general very good, since they have been tested by many experienced developers.

One example about Mobile Web Development on Stack Overflow:

http://stackoverflow.com/questions/tagged/mobile-web?sort=votes&pagesize=50

You can find out plenty of good questions and good answers! :) They are good teachers!

Prototyping. Being Lazy.

Recently I got a chance of working across teams and making a project prototype. During the discussion I had with the development manager of the other team, he introduced to me a very interesting concept – Being Lazy. 

For prototyping, the goal is to write less code and quickly find the problems that need to solve. Especially for the projects with external dependencies, identify where the dependencies are, extract the part you should own and you have the full control of it, and notify the dependent teams what you need and how you consume their services, and you can move on. When you design your software, mock the dependency input, which unblocks you from any external dependencies. The prototype needs to be quick, and implemented with less code. There is nothing wrong about being lazy when making a prototype.

Prototyping is a trailblazing job. Hope this advice could help people focus.

Beloved JavaScript Pattern – Singleton

There are many ways to write a Singleton in JavaScript to meet your needs. I love anonymous functions, which empowers to do almost everything, if you need an object! Recently, as I wrote, I found a very precise way to write Singleton. It looks like the way Prototype is created, but it is a Singleton. It makes me happy!

Example:

var Counter = {
    current: 0,
    increment: function () {
        return (++this.current);
    },
    decrement: function () {
        if(--this.current) {
            this.reset();
        }
        return this.current;
    },
    reset: function () {
        return (this.current = 0);
    }
};

Isn’t it that simple? It is really tidy, and can get things done.

Live Nodes!

Thank to several passionate people I talked to recently, I learned about the liveness of some DOM objects. These objects remain live and updated even after the layout is changed. The two live objects are NodeList and NamedNodeMap.

NodeList

Definition

From http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-536297177

The NodeList interface provides the abstraction of an ordered collection of nodes, without defining or constraining how this collection is implemented. NodeListobjects in the DOM are live.

Example

>>var nodeList = document.getElementsByTagName('div');
>> nodeList
NodeList[400]
>> var newNode = document.createElement('div');
>> document.body.appendChild(newNode);
>> nodeList
NodeList[401]

The same variable nodeList has got a updated list of node.

Key points

  • This collection is auto updated, so you need to query again if you still want to refer this set of nodes, even after the markup has been changed.
  • Faster collecting elements. getElementsByTagName() is significantly faster than querySelectorAll(). See explanation in Nicholas Zakas’s post Why is getElementsByTagName() faster that querySelectorAll()?
  • It is supported in most of the browsers, even though some of them are buggy. See SitePoint.
  • Do not use the length of the updated nodeList as a criteria for breaking the loop. Otherwise, it will end up with never ending running script.

NamedNodeMap

Definition

From http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-1780488922

Objects implementing the NamedNodeMap interface are used to represent collections of nodes that can be accessed by name. Note that NamedNodeMap does not inherit from NodeList; NamedNodeMaps are not maintained in any particular order. Objects contained in an object implementing NamedNodeMap may also be accessed by an ordinal index, but this is simply to allow convenient enumeration of the contents of a NamedNodeMap, and does not imply that the DOM specifies an order to these Nodes. NamedNodeMap objects in the DOM are live.

Example

From SitePoint

>> var attrs = document.getElementsByTagName('link')[0].attributes;
>> attrs
NamedNodeMap
0: Attr
1: Attr
length: 2
__proto__: NamedNodeMap
>> var media = document.createAttribute('media');
>> media.nodeValue = 'all';
>> attrs.setNamedItem(media);
NamedNodeMap
0: Attr
1: Attr
2: Attr
length: 3
__proto__: NamedNodeMap

Conclusion

getElementsByTagName() and attributes are live HTML DOM objects. It keeps updating while markups are being changed. We will benefit if we make good use of them.

Not be lazy with prototype

Recently I happened to have a chance to write some prototype utilities (extending prototypes, and inheritance). These helped me get more understanding towards some of the prototype concepts.

constructor

I saw someone wrote a similar pattern before, and I followed it this time, and did not do what I used to do. Then I found out a big problem.

function A () {
    this.counter = 0;
}

A.prototype = {
    increment: function () {
        this.counter += 1;
    },
    decrement: function () {
        this.counter -= 1;
    }
};

The code above looks alright at the first glance. Theoretically, A.prototype.constructor should be the same as A:

A.prototype.constructor === A //true?

However, they are not equal! Then I looked back at the way I listed the methods of A, and noticed that once I assigned an object to A.prototype, it already lost its constructor – it overwrites the constructor. Interestingly, A.prototype.constructor is still available, but it is the native constructor Object.

So I would prefer writing in this way:

function A () {
    this.counter = 0;
}

A.prototype.increment = function () {
    this.counter += 1;
};

A.prototype.decrement = function () {
    this.counter -= 1;
};

Prototype Members

function A(){
    this.text = 'Hello World';
}

A.prototype.text = 'Hello World';

Are they the same?

Sprite vs Pure Image Tag

Yesterday when I could not decide whether I should use Sprite for some scenarios, I found a good quote from StackOverflow. This may be a good rule of thumb.

When an image has semantical meaning, so it is considered to be content, use an IMG tag, without sprites, and a correctly set up ALT.

When an image is just decoration, like the background of a box, background of a button, background of a menu option, etc., it has no semantical meaning, so you can just use it as a background of a SPAN, DIV, etc. from CSS. You can use sprites in this case.

prevenDefault and stopPropagation

These were the two event methods I hadn’t paid much attention to. I could use them correctly for most of scenarios. When it came to more complicated cases, I was baffled. So let’s look into them more closely.

stopPropagation

From W3C – Document Object Model Events:

The stopPropagation method is used prevent further propagation of an event during event flow. If this method is called by any EventListener the event will cease propagating through the tree. The event will complete dispatch to all listeners on the current EventTarget before event flow stops. This method may be used during any stage of event flow.

I have two catches from this definition:

  1. When this method is called, the event will not stop until it dispatch on the current event target;
  2. It seems to have not connection with DOM implementation, which is different from preventDefault.

preventDefault

From W3C – Document Object Model Events:

If an event is cancelable, the preventDefault method is used to signify that the event is to be canceled, meaning any default action normally taken by the implementation as a result of the event will not occur. If, during any stage of event flow, the preventDefault method is called the event is canceled. Any default action associated with the event will not occur. Calling this method for a non-cancelable event has no effect. Once preventDefault has been called it will remain in effect throughout the remainder of the event’s propagation. This method may be used during any stage of event flow.

So what about cancelable?

Some events are specified as cancelable. For these events, the DOM implementation generally has a default action associated with the event. An example of this is a hyperlink in a web browser. When the user clicks on the hyperlink the default action is generally to active that hyperlink. Before processing these events, the implementation must check for event listeners registered to receive the event and dispatch the event to those listeners. These listeners then have the option of canceling the implementation’s default action or allowing the default action to proceed. In the case of the hyperlink in the browser, canceling the action would have the result of not activating the hyperlink.

From the definitions, there are the following 3 key points:

  1. This cancelation is for the actions in DOM implementation, not event listeners.
  2. Event listener takes precedence over the default DOM action, and it has the ability to cancel the DOM action.
  3. This event may be canceled during any stage of event flow, so it may not happen only on the very element node that the event has started.

Here are the example:

Example 1:

HTML:

<a id="test-link" href="http://www.google.com/">Link</a>

JavaScript:

var link = document.getElementById('test-link');
link.addEventListener('click', function (e) {
    e.preventDefault();
}, false);

This event listener cancels the default action of this HREF link, which is very clear.

Example 2:

HTML:

<a id="test-link" href="http://www.google.com/"><img src="/image.png" alt="" id="test-image" />

JavaScript:

var img = document.getElementById('test-image');
img.addEventListener('click', function (e) {
    e.preventDefault();
}, false);

However, this event listener actually cancels the default action of the parent of the element being clicked. This was something I was really happy to find out.