diff --git a/README.md b/README.md new file mode 100644 index 0000000..ede7ba5 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# Front-End-Development-Guidelines +Front end development guidelines related to best practices in: Accessibility, CSS & CSS3, HTML & HTML5, JavaScript & jQuery. + +Please visit http://coderlmn.github.io/Front-End-Development-Guidelines/ for Chinese version, which is translated by coderLMN. diff --git a/index.html b/index.html index fbdc26b..946a84f 100644 --- a/index.html +++ b/index.html @@ -1,1797 +1,1741 @@ - - - - - - Front End Development Guidelines - - - - - - - - - - - -
-
- -
-
- -
- -

Accessibility

- - - -
-

What's Up, DOCTYPE?

- -

- The absence of a DOCTYPE is a crime punishable by death. You may have relied on the following DOCTYPE in the past, but it's important to know that this is now being superseded by a leaner and meaner snippet. -

- -
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-    "http://w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
- -

- Ideally, the HTML5 DOCTYPE should be used. It's supported in all modern browsers, and throws IE6 and IE7 into standards mode. Source. -

- -
-<!DOCTYPE html>
- -
- - - - - -
-

Write Valid Semantic Markup

- -

- Writing websites with clean, semantic HTML is something we wish we could always do. Sometimes we find ourselves limited by the way pages were setup by our predecessors, or sometimes we're coding an HTML email. The validity of the HTML should never be compromised, even if to solve a browser specific bug. -

- -

- Headings should be heirarchically created from <h2> onwards, paragraphs should always be in <p> tags and so on and so forth. If you write semantic HTML, the resultant page will be cleaner, lighter and easily parsed by search engine spiders. This is one of the simplest SEO fixes you can undertake. -

- -

Which do you think looks cleaner, this?:

- -
-<span class="sectionHeading">A Heading</span>
-<br /> <br />
-Lorem ipsum dolor sit amet. ...
-<br /> <br />
- -

Or this?

- -
-<h2>A Heading</h2>
-<p>
-    Lorem ipsum dolor sit amet. ...
-</p>
-
- - - - - - -
-

Fallbacks for Middle Mouse Clicks

- -

- One of the most frustrating accessibility and usability flaws of the modern web stems from the remapping of hyperlink click functions. Elements that appear to be hyperlinks may have their single click functionality remapped via JavaScript, breaking middle mouse click (open in new tab) functionality. If they can be opened in a new tab, their href of a single hash sends you back to the same page. -

- -

- A modern example of a popular website that is contributing to this problem is the Twitter web app. Middle mouse clicking of names or user avatars yields completely different results throughout the web app. -

- -
-<!-- The old way, breaking the web -->
-<a href="#"></a>
-
-<!-- If you can't deliver a page on mouse click, it's not a hyperlink -->
-<span class="link" role="link"></span>
-
- -

- Another alternative is the use of "hashbangs", that remap normal URLs to hash links and fetch pages via AJAX. Libraries that provide hashbang functionality should be able to display the page normally when middle mouse clicked, or load the content from that page into a designated area when clicked normally. But tread carefully, there are plenty of people who believe hashbangs are breaking the web. -

- -
- - - - - - - -
-

Use Microformats

- -

- Microformats are a way of making contact information machine readable. hCard classes (not vCard) are used to define the type of content contained within elements. These are then extracted or highlighted by the browser. -

- -
-<span class="tel">
-    <span class="type">home</span>:
-    <span class="value">+1.415.555.1212</span>
-</span>
- -

- If you were to navigate to a page that uses this, you would notice that a program like Skype will easily detect what numbers on the page are phone numbers. Mobile Safari does something similar on iOS devices. -

- -

- For more information: http://microformats.org/wiki/hcard -

-
- - - - - -
-

Images Need ‘Alt’ Text

- -

- The <img> tag requires alt text to both validate and meet accessibility guidelines. The text in the alt attribute should be descriptive of what the image shows, or is trying to achieve, unless of course the image is not critical. -

-

- If the image is of a list bullet or other trivial icons, it is recommended to simply leave the alt attribute empty, but still present. A screenreader will then ignore it, as opposed to having to read out "bullet" 20 times. -

- -
-<img src="dog.gif" alt="Fido and I at the park!" /> 
-<!-- good, descriptive -->
-
-<img src="bullet.gif" alt="bullet" />
-<!-- bad, as silly as it seems -->
-
-<img src="bullet.gif" alt="" />
-<!-- good -->
-
- - - - - -
-

Use Tables for Tabular Data Only

- -

- Tables should only ever be used for the presentation of tabular data. The only exception is when composing HTML email, in which a table is almost the only thing supported by soul crushing email clients. -

-

- For accessibility, table headers should always be presented using <th> elements. Remember to also set cellpadding, cellspacing and border values to 0 as these are more consistently controlled by CSS. -

- -
-<table cellpadding="0" cellspacing="0" border="0">
-    <thead>
-        <tr>
-            <th>
-                Cell Header
-            </th>
-        </tr>
-    </thead>
-    <tbody>
-        <tr>
-            <td>
-                Cell Item
-            </td>
-        </tr>
-    </tbody>
-</table>
-            
- -
- - - - - -
-

Use jQuery & jQuery UI Widgets

- -

- jQuery and jQuery UI are constructed to look and behave as close to identical as possible on different browsers. - jQuery UI is designed to be WAI WCAG 2.0 and WAI ARIA compliant, so using the framework removes any uncertainty about plugins or scripts running on your site. -

-
- - - - - - - - - -

JavaScript

- - - -
-

Whitespacing & Formatting

- -

- Any discussion about formatting, whitespacing and the placement of braces is going to be hotly debated. I guess the simplest rule is that, unless you're willing to completely format a whole document, respect and maintain the formatting of an existing document. - That means: see same-line braces throughout a JS file, continue to write code with same-line braces. Your code should fail the code review process if it doesn't maintain consistency with the rest of the document. -

- -

- Consistent formatting makes code more readable, and also means the code can be easily modified with find and replace commands. The coding habits we have picked up are thankfully very similar to what jQuery officially encourages. There are a few minor discrepencies, but again, these are personal issues or things that we think cannot be maintained. - Further Reading -

- -

Character Spacing

- -
-// Bad
-if(blah==="foo"){
-    foo("bar");
-}
-
-// Good :)
-if (blah === "foo") {
-    foo("bar");
-}
- -

Same Line Braces

- -
-// Bad
-if (foo)
-{
-    bar();
-}
-
-// Good :)
-if (foo) {
-    bar();
-}
- -

Always Using Braces

- -
-// Bad
-if (foo)
-    bar();
-
-// Good :)
-if (foo) {
-    bar();
-}
- -

String Handling

-

- Strings should always use double quotes. Some people are very fond of their C style strings (single quotes), but this leads to conflicting styles within a script. - C style string handling dictates that empty and single character strings should be wrapped in single quotations, while phrases and words should be wrapped in double quotations. -

- -
- - - -
-

Commenting

- -

- The requirement to comment code obsessively was pioneered by managers, team leaders and other people that interact with code infrequently. - It is sought merely as a check box for an employee's KPIs, and provides little return for the time spent doing so. -

- -

- If a best-practice oriented developer follows the guidelines established in this document, their code should become so readable and obvious that the need to comment what it is doing is embarassingly redundant. - Consider the following example. In this: booleans are posed as questions, and functions are named intuitively. -

- -
-if (user.hasPermission) {
-    editPage();
-}
- -

- Commenting, in this scenario at least, is completely unnecessary. -

- -

SITUATIONS WHERE COMMENTING IS IMPORTANT

- -

- Some parts of a project will never be easy to scan and understand. Consider a complicated regular expression, or a math function calculating angles or switching between degrees and radians. - Without the comment above, beginner and intermediate readers will be fairly clueless to the scripts' meaning. -

- -
-// RegEx for validating US phone numbers, can be (XXX) XXX-XXXX (with or without dashes, spaces or brackets)
-var phoneRegEx = /^\(?(\d{3})\)?[- ]?(\d{3})[- ]?(\d{4})$/;
- - -
- - -
-

Always Use === Comparison

- -

- The use of the == equality operator allows for frustrating bugs to slip through almost undetected. It allows for weak typing that is best explained by JavaScript Garden. - The use of the strict equality operator === does not run type coercion and therefore strictly evaluates the difference between two objects. Again, consult JavaScript Garden for more information -

-
-var zeroAsAString = "0";
-
-if (zeroAsAString == 0) {
-    // gets in here lolwut
-}
-
-if (zeroAsAString === 0) {
-    // never gets in here
-}
- -

The Exception

-

- Double equals comparison is allowed when comparing to null, because it will detect both null or undefined properties. If you don't fully understand this, I still suggest you use triple equals. -

-
-var foo = null;
-
-// foo is null, but bar is undefined as it has not been declared
-if (foo == null && bar == null) {
-    // still got in here
-}
- -
- - - - - - -
-

Always Specify the Second ‘radix’ Parameter When Using .parseInt()

- -

- When parsing a string to an integer, it is considered good practice to specify the second 'radix' parameter - which determines to what base the string should be converted to. - The default setting will trigger a radix of 16 whenever the string is lead by a 0. Most beginner and intermediate users are only ever going to be using a radix of 10. Thanks to João Moreno for logging the correction. -

- -
               
-alert( parseInt("08") ); // alerts: 2
-
-alert( parseInt("08", 10) ); // alerts: 8
- -
- - - - - - - -
-

Avoid Comparing to true and false

- -

- Direct comparison to the values of true and false is unnecessary. Sometimes it might be good for clarity, but it's just extra code. -

- -
-if (foo === true) {
-    // good that they're using triple equals, bad as it's redundant
-}
-
-if (foo) {
-    // yay!
-}
-
-if (!bar) {
-    // the opposite
-}
- - -
- - - - - - -
-

Avoid Polluting the Global Namespace

- -

- An over-reliance on global variables is something all of us, myself especially, are guilty of. Arguments as to why globals are bad are fairly straight forward: the chance of script and variable conflicts is increased, - and both the source file and the namespace itself become littered with countless ambiguously named variables. -

- -

- Douglas Crockford believes that the quality of a JavaScript application can be assessed by the number of global variables it uses; the less the better. Given that not everything can be a local - (but let's be honest, that one you're thinking about right now, it can, don't be lazy) you need to find a way of structuring your variables to prevent clashes and minimise the bloat. - The easiest way is to employ a single variable or a minimal amount of modules on which the variables are set. Crockford mentions that YUI uses a single global, YAHOO. - He discusses this in more detail in his blog post "Global Domination". -

- -

- Considering that, in the case of small web apps, globals are generally used to store application-wide settings: it's generally better to namespace your project or settings as objects. -

- -
-// polluted global name space
-var settingA = true;
-var settingB = false;
-var settingC = "test";
-
-// a settings namespace
-var settings = {
-    settingA: true,
-    settingB: false,
-    settingC: "test"
-}
- -

- But if we're avoiding globals to reduce the chance of conflicts, isn't standardising the namespaces to be the same going to increase chance of one app's settings overwriting anothers? Well, it would make sense. - It is instead suggested that you namespace your globals to your own specific app name, or reassign your namespace much in the same way that jQuery uses $.noConflict() mode. -

- -
-var myAppName = {
-    settings: {
-        settingA: true
-    }
-}
-
-//accessed as
-myAppName.settings.settingA; // true
- -
- - - - - - - -
-

Camel Case Variables

- -

- The camel casing (or camelCasing) of JavaScript variables is accepted as the standard in most coding environments. - The only exception that was raised in the comment section is the use of uppercase and underscores to denote contants. -

- -
-var X_Position = obj.scrollLeft;
-
-var xPosition = obj.scrollLeft; // tidier
-
-SCENE_GRAVITY = 1; // constant
- -
- - - - - - - - -
-

Loop Performance - Cache Array Length

- -

- Looping is arguably the most important part of JavaScript performance to get right. Shave a millisecond or two off inside of a loop, potentially gain seconds overall. One such way is to cache the length of an array so it doesnt have to be calculated every time the loop is iterated through. -

- -
-var toLoop = new Array(1000);
-
-for (var i = 0; i < toLoop.length; i++) {
-    // BAD - the length has to be evaluated 1000 times
-}
-
-for (var i = 0, len = toLoop.length; i < len; i++) {
-    // GOOD - the length is only looked up once and then cached
-}
- -

The Exception

-

- If you're looping through an array to find and remove a particular item, this will alter the array length. Any time you change the array length by either adding or removing items from inside the loop, you will get yourself into trouble. Consider either re-setting the length or avoid caching it for this particular situation. -

- -
- - - - - - - - -
-

Loop Performance - Use ‘break;’ & ‘continue;’

- -

- The ability to step over and out of loops is really useful in avoiding costly loop cycles. -

- -

- If you're looking for something inside of a loop, what do you do once you find it? Say the condition you're looking for is matched halfway through a 1000 item loop. - Do you execute whatever you intend to do, and allow the loop to continue to iterate over the remaining 500 items, knowing that there's no chance it will hit an if statement? Nope! You break out of your loop, literally! -

- -
-var bigArray = new Array(1000);
-
-for (var i = 0, len = bigArray.length; i < len; i++) {
-    if (i === 500) {
-        break;
-    }
-    console.log(i); // will only log out 0 - 499
-}
- -

- Another problem is skipping over a particular iteration and then continuing on with the loop. While things like odds and evens are better managed by replacing i++ with i + 2, - some conditions need to be specifically listened for, to then trigger the skip. Anything that prevent's running through an entire iteration is pretty handy. -

- -
-var bigArray = new Array(1000);
-
-for (var i = 0, len = bigArray.length; i < len; i++) {
-    if (condition) {
-        continue;
-    }
-    doCostlyStuff();
-}
- -
- - - - - -
-

Don't Send Too Many Function Parameters

- -

- This is a pretty bad idea, more for readability than anything: -

- -
-function greet(name, language, age, gender, hairColour, eyeColour) {
-    alert(name);
-}
- -

- It's a much better idea to construct an object before-hand or to pass the object inline -

- -
-function greet(user) {
-    alert(user.name);
-}
-
-greet({
-    name: "Bob",
-    gender: "male"
-});
- -
- - - - - - - -
-

Remap ‘this’ to ‘self’

- -

- When writing object-oriented (OO) JavaScript, the scope of this must be understood. Regardless of what design pattern you choose to structure your pseudo-classes, a reference to this is generally the - easiest way to refer back to an instance. The moment you begin integrating jQuery helper methods with your pseudo-classes is the moment you notice the changing scope of this. -

- -
-Bob.findFriend("Barry");
-
-Person.prototype.findFriend = function(toFind) {
-    // this = Bob
-    $(this.friends).each(function() {
-        // this = Bob.friends[i]
-        if (this.name === toFind) {
-            // this = Barry
-            return this;
-        }
-    });
-}
- -

- In the above example, this has changed from a reference to Bob, to his friend Barry. It's important to understand what happened to the value of this over time. - Inside of the prototyped function, this refers to the instance of the pseudo-class (in this case Bob). - Once we step inside the $.each() loop, this is then re-mapped to be item i in the parsed array. -

- -

- The solution is to remap the value of this to either self or _self. - While self (sans underscore) is not exactly a reserved keyword, it is a property of the window object. - Although my use of self was picked up from the jQuery source code, they have realised their mistake and are attempting to rectify the situation and instead use _self. - Personally, I prefer the use of self for the sheer cleanliness - but it can throw some pretty confusing bugs for people. Tread carefully. -

- -

- In the following example I will better utilise the parameters made available with the $.each() helper, as well as re-mapping the value of this. -

- -
-Bob.findFriend("Barry");
-
-Person.prototype.findFriend = function(toFind) {
-
-    // the only time "this" is used
-    var _self = this; 
-    
-    $(_self.friends).each(function(i,item) {
-        if (item.name === toFind) {
-            return item;
-        }
-    });
-    
-}
- -
- - - - - - -
-

CanIHaz Boolean?

- -

- Booleans should be easily identifiable by the way they are named. Use prefixes like is, can or has to propose a question. -

- -
-isEditing = true;
-
-obj.canEdit = true;
-
-user.hasPermission = true;  
- -
- - - - - - -
-

Minimising Repaints & Reflows

- -

- Repaints and reflows relate to the process of re-rendering the DOM when particular properties or elements are altered. Repaints are triggered when an element's look is changed without altering its layout. Nicole Sullivan describes these changes in a thorough blog post as style changes such as visibility or background-color. Reflows are the more costly alternative, caused by changes that alter the layout of the page. Examples include the addition or removal of elements, changes to an element's width or height, and even resizing the browser window. Worst yet is the domino effect of reflows that cause ancestor, sibling and child elements to reflow. -

- -

- There is no doubt that both reflows and repaints should be avoided if possible, but how? -

- -

A Reflow Example

- -

- It's not that the following snippet is "bad code" exactly. But let's assume that the array arr has 10 items. -

- -
-var myList = document.getElementById("myList");
-
-for (var i = 0, len = arr.length; i < len; i++) {
-    
-    myList.innerHTML += "<li>" + arr[i].title + "</li>"; //reflow - appending to element
-    
-}
- -

- In the above for loop, a reflow will be triggered for every iteration of the loop. 10 iterations cause 10 reflows. -

- -

- Now consider the following: -

- -
-var constructedHTML = "";
-
-for (var i = 0, len = arr.length; i < len; i++) {
-    constructedHTML += "<li>" + arr[i].title + "</li>"; //no reflow - appending to string
-}
-
-document.getElementById("myList").innerHTML = constructedHTML; //reflow
- -

- In this scenario, the elements are being constructed within a string. Not a single reflow is created by the loop, as the DOM is not being altered. Only once the array has been completely looped through is the string then applied as the innerHTML of an object, causing the only reflow of the function. -

- -

- There are endless types of reflows and repaints that can be avoided, and lucky you gets to go on an read about them. Reading material on the subject matter is plentiful, but most of it is linked to from the excellent starting point that is Nicole Sullivan's blog post. There are important lessons to be taken away from this when it comes to a multitude of technologies synonymous with "web 3.0" and HTML5. The lesson above can be directly applied to writing jQuery. It's also important to consider when fiddling with canvas, and trying to keep a frame rate in the 30-60 range. -

- -
- - - -
-

Don't Use Milliseconds to Generate Unique IDs

- -

- There is a method for generating unique IDs that has hung around since the early days of web dev. It involved appending the elapsed milliseconds since January 1, 1970 to your static ID by way of: -

- -
-var myID = "static" + new Date().getTime();
- -

- This was a fairly foolproof method originally, because even if two ofthe above lines were performed one after the other, a few millisecondsnormally separated their execution. New browsers brought with them newJavaScript engines, coupled with ever increasing clock speed. Thesedays it's more likely that your milliseconds match than are slightlyincremented. -

- - -

- This leads to bugs that are near impossible to debug by conventionalmeans. Because your DOM is created on the fly, traditional validationof the page source won't identify multiple IDs as an error. JavaScriptand iQuery error handling dictates that the first match for the IDwill be utilised and other matches ignored. So it doesn't even throw aJS error! -

- - -

- No, the only real method to debug it is line by line breakpoint ingand logging - but "pause" at the wrong line and your milliseconds willno longer clash! -

- - -

- The good thing is that there are plenty of alternatives. To be pedantic, it's worth noting that a computer's random function is not truly random as it is seeded by system time- but the probability of clashes is rather minuscule. -

- -
-var myID = "static" + Math.round(Math.random() * 10000);
- - - - -

- Personally, I'm partial to a bit of faux GUID generation. Technicallya GUID is generated according to your hardware, but this JavaScriptfunction does the next best thing. The following is a handy function I've pinched from a stack overflow post. -

- -
-function S4() {
-   return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
-}
-function guid() {
-   return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
-}
- -
-var myID = "static" + guid();
- -
- - - - - - - -
-

Feature Sniff, Don't Browser Sniff

- -

- Does the client's browser support geolocation? Does the client's browser support web workers? HTML5 video? HTML5 audio? The answer used to be: -

- -
-if ($.browser.msie) {
-    // no it doesn't
-}
- -

- But things are rapidly changing. The latest version of IE is almost a modern browser, but as usual it's making front end development a pain. Earlier versions of IE were generally as equally sucky as their predecessors, - so it enabled lazy JavaScript developers to simply detect if (ie) and execute some proprietary Microsoft slops syntax. Now IE9 has done away with these functions, but that old if (ie) chestnut is throwing a spanner in the works. -

- -

- So what if you could detect support for individual features without sniffing the (unreliable and cloakable) user-agent? -

- -

- If you answered "that would be ghetto", then you are correct. -

- -

- In steps Modernizr, a JavaScript library developed in part by industry dream-boat Paul Irish. - With wide adoption, tiny file-size and plenty of documentation: implementing it is a no-brainer. - It creates a Modernizr object that contains the results of its detection tests, so checking feature support is as simple as the following: -

- -
-// old way of detecting canvas support
-if (!!document.createElement('canvas').getContext) { ... }
-
-// with Modernizr
-if (Modernizr.canvas) { ... }
- -
- - - - - - - - -
-

Readable Milliseconds

- -

- A handy way of writing milliseconds in a readable format. Great for beginners, but mostly a gimmick. -

- -
-// is this 3, 30 or 300 seconds?
-var timeout = 30000; 
-
-// an extra calculation, but easier to read and modify
-var timeout = 30 * 1000;
- -
- - - - - - - - - - - - - -

jQuery Specific

- - - - - - - -
-

Chain Like a Mad Dog

- -

- One of the best parts of jQuery is its function chaining. You've probably used it a bit, maybe a few simple calls one after another... but have you ever traversed the DOM like a mad dog? Take some time to familiarise yourself with the .end() function. It is critical for when you begin stepping up and down the DOM tree from your original selector. -

- -
-$(".quote")
-    .hide()
-    .find("a").text("Click here").bind("click",doStuff).end()
-    .parent().removeClass().addClass("testimonial").draggable().end()
-    .fadeIn("slow");
- -

- In the example above, the .end() function is used once we have finished doing things with a particular DOM object and want to traverse - back up the DOM to the original object we called. We then load back up and dive back into the DOM. -

- -
- - - - - - - -
-

Using data-* Attributes

- -

- Those of you who have been writing JavaScript (and not jQuery) for a good length of time are most likely familiar with attributes. Setting them. Getting them. Abusing rel and title instead... -

- -

- So when isn't HTML5 or jQuery coming the rescue? New specs allow the use of data- prefixes on HTML elements to indicate attributes which can hold data, and jQuery does an awesome job of converting the designated string into the correct JavaScript type. It's a beautiful partnership. Let's create a DIV with some data attributes. -

- -
-<div id="test" data-is-bool="true" data-some-number="123"></div>
- -

- Now even though our values are wrapped in quotation marks, they won't be handled as strings: -

- -
-typeof $("#test").data("isBool"); // boolean
-    
-typeof $("#test").data("someNumber"); // number
- -

Special Casing

- -

- It's also important to notice the lower casing required to get these snippets to work. But if you're a great front end developer, you will still want to camel case your data variables. Like many places in JavaScript, a preceding hyphen signifies camel casing of the next letter. The following camel casing of the HTML attribute does not work and the same JavaScript used above will return undefined. -

- -

- Does not work :( -

-
-<div id="test" data-isBool="true" data-someNumber="123"></div>
- -

- Does work :) -

-
-<div id="test" data-is-bool="true" data-some-number="123"></div>
- -
- - - - - - - -
-

‘.stop()’ Collaborate & Listen

- -

- Binding jQuery animations to mouse events is a key part of modern web-based user interaction. It's also something that you see done poorly on even the most famous of web sites. - This article provides a straight forward example of built up animations and demonstrates how visually jarring they can be. - Thankfully it's easily fixed with a single function prefix or a parameter added to $.animate calls. -

- -

- When using $.animate, queue: false can be added to the parameters to prevent chaining. Animation shortcuts such as $.fadeIn or $.slideDown do not take queue settings. - Instead you have to pre-empt these animations with the $.stop method of pausing currently executing animations. Certain scenarios require the animation to stop dead in its tracks, or to jump to the end of the transition. - It is recommended you familiarise yourself with the documentation of the parameters clearQueue and jumpToEnd, because god knows I can't help you there. -

- -
-$("selector").stop(true,true).fadeOut();
-
-$("selector").animate({
-    property: value
-}, {
-    duration: 1000,
-    queue: false
-}
- -
- - - - - - - -
-

Optimise Your Selectors

- -

- jQuery is pretty chill. It can do pretty much everything but make you coffee, and I hear that's in the roadmap for 2.0. One thing you have to be careful about is abusing the power that is the sizzleJS selector engine. - There are two strategies to overcome this: caching the selector results and using efficient selectors. -

- -

Caching Selector Results

- -

- Do a costly DOM query every time you want to change something, or store a reference to the element? Pretty clear choice. -

- -
-// before
-$(".quote a").bind("click", doStuff); // DOM query eww
-
-// now
-$(".quote a").addClass("quoteLink"); // DOM query eww
-
-// later
-$(".quote a").fadeIn("slow"); // DOM query eww
- -

- Ignoring chaining, this is better: -

- -
-// before
-var $quoteLinks = $(".quote a");  // the only DOM query
-$quoteLinks.bind("click", doStuff);
-
-// now
-$quoteLinks.addClass("quoteLink");
-
-// later
-$quoteLinks.fadeIn("slow");
- -
- -

Using Efficient Selectors

- -

- So jQuery/sizzleJS can use CSS3 selectors like a boss, but what's the real cost? Behind the scenes the browser is hopefully using document.querySelector(), but there's also a fair chance it will be breaking down your selector string and querying the DOM manually. -

- -
-// an ID search is the quickest possible query, then it just takes a list of the childNodes and matches the class
-$("#quoteList").children(".quotes"); 
-
-// looks for the "foo" class only in the pre-defined bar element
-$(".foo",bar); 
- -
- - - - - - -
-

A ‘for’ Loop is Always Quicker Than a ‘each()’ Loop

- -

- No matter what happens in the next few years of browser development, a native for loop will always be quicker than a jQuery $.each() loop. - When you think of what jQuery really is (a library wrapped around native JS functions) you begin to realise that the native underlying JavaScript is always going to be quicker. It's a tradeoff of run speed versus authoring speed. -

- -

- It is vital that a native for loop is always used for performance critical functions that could fire potentially hundreds of times per second. Examples include: -

- - - -
- - - - - - - - - - - - - - - - - - - -

CSS

- - -
- -

Understanding the Box Model is Key

- -

- The "box model" is a key determining factor in how a browser renders your page. A healthy understanding of it's intricacies will make your job so indescribably easier. The box model denotes the way in which the physical dimensions of a HTML element are calculated. If a block element has a fixed width of say, 100px, then how should the padding, border and margin be placed? -

- -

- Plenty of websites offer in depth descriptions, but put simply: the standards compliant implementation places the border and padding outside of the specified width. It's best explained with a graphic. Consider this code: -

- -
-/* the old way (178 + 20 + 2 = 200) */
-.foo {
-    width: 150px;
-    height: 150px;
-    padding: 25px;
-    border: 25px solid;
-    margin: 20px;
-}
- - -

What You Would Expect (Quirks Mode)

- -

- The padding and border are calucated inward, preserving the height and width specifically set to be 150px. -

- -

- Box Model - Quirks Mode -

- -

What You Get (Standards Compliant Mode)

- -

- Instead, you get 250px. 150px + (2 * 25) + (2 * 25). -

- -

- Box Model - Standards Mode -

- - -

- If you think it seems odd, you're not alone. There is a fix at hand, and it involves a CSS property called box-sizing, and it works in IE8 and above. It allows you to choose the exact way in which an elements dimensions are calculated, and its a lifesaver. Parameter support varies and vendor prefixes apply, so consult caniuse for specifics. -

- -
-/* the old way (178 + 20 + 2 = 200) */
-.foo {
-    width: 178px;
-    padding: 10px;
-    border: 1px;
-}
-
-/* a better way */
-.foo {
-    width: 200px;
-    padding: 10px;
-    border: 1px;
-        -webkit-box-sizing: border-box;
-        -moz-box-sizing: border-box;
-        box-sizing: border-box;
-}
- -

- While it was always possible to mentally calculate widths by removing pixel units from each other (as per the first method), it was never entirely clear how to do so with variable width units like percentages and EMs. There was no other solution at this point besides wrapping elements in parent elements to ensure widths and padding/margin/borders could all be separate. -

- -
- - - -
- -

Know when to Float, and when to Position

- -

- Gone are the days of table based layouts. The moment we admit that we can concentrate our efforts into better understanding the way floats and positions work. There's a particular mental model that needs to be grasped, and I believe this is something best done with practise. -

- -

- Floats are great for sucking elements out of the DOM and forcing them hard up against a left or a right edge. They became the bread and butter of the post table layout stage in front end dev, possibly because of the poor browser support of display: inline and inline-block, as well as z-index bugs stemming from position support. These days there really is no excuse. Inline-block is fairly well supported, and a quick hack will get it working in IE7. -

- -

- The arguments that previously held back absolutely positioning elements with CSS have thankfully died down. In theory, positioning allows you to place elements on a page (or within any container for that matter) with Xs and Ys in a straightforward manner that should be familiar to people like Flash developers. -

- -

Understanding Positions

- -

- It's important to understand one fact when positioning elements with CSS: the position is always relative to the nearest positioned parent element. When people first start dabbling with CSS, there's a common misconception that position: absolute; positions right up to the page root. I think this stems from the fact that, yes, without any parent elements with position styles - this is true. It traverses up the DOM tree, not finding any positioned elements, and settles on the root of the page. -

- -

- So if position: absolute; pulls elements out of their normal flow, how do you position an element relative to it's parent? That's straight forward. The parent element needs to by styled position: relative;, and then all child elements will draw from the top, right, bottom and left of this parent container. Using this knowledge, how would you go about achieving the following straightforward layout? -

- -

- How would go about coding up this image? -

- -

- Using float, you would need to wrap the items in a clearfix, float .one left, and fiddle with floats and margins on both .two and .three. You would end up with something similar to the following: -

- -
-.parent {
-    /* ghetto clearfix */
-    width: 310px;
-    overflow: auto;
-}
-.one {
-    width: 200px;
-    height: 210px;
-    float: left;
-}
-.two {
-    width: 100px;
-    height: 100px;
-    float: right;
-    margin-bottom: 10px;
-}
-.three {
-    width: 100px;
-    height: 100px;
-    float: right;
-}
- -

- Using position allows us to, as described earlier, paint the elements on the screen with x and y co-ordinates in a very explicit way. While the above method with floats will break long lines down the page, the below method will keep everything in place, regardless of content. -

- -
-.parent {
-    position: relative;
-    width: 310px;
-    height: 210px;
-}
-.one, .two, .three {
-    position: absolute;
-}
-.one {
-    top: 0;
-    left: 0;
-    width: 200px;
-    height: 210px;
-}
-.two {
-    top: 0;
-    right: 0;
-    width: 100px;
-    height: 100px;
-}
-.three {
-    bottom: 0;
-    right: 0;
-    width: 100px;
-    height: 100px;
-}
- -

- As mentioned earlier, there are z-index issues to be considered. While the above example might seem a bit excessive, once you start thinking with positions, it will opens a world of possibilities. -

- -
- - - -
-

Whitespacing

- -

- Whitespacing of CSS can be difficult as we chop and change between single and multi line CSS arguments. I'm not going to get into that. -

- -

Proper Spacing

- -
-/* BAD */
-.selector {display:none;background:#ff0000;color:#000000;} 
-
-/* GOOD - SINGLE LINE */
-.selector { display: none; background: #ff0000; color: #000000; } 
-
-/* GOOD - MULTI-LINE */
-.selector {
-    display: none;
-    background: #ff0000;
-    color: #000000;
-}
- -

Same Line Braces

- -
-.selector {
-    display: none;
-    background: #ff0000;
-    color: #000000;
-}
- -

Indenting Child Elements

- -

- Purely optional, and personally only employed when in a document with single line declarations. -

- -
-.selector { display: none; background: #ff0000; color: #000000; }
-    .selector a { text-decoration: none; }
-    .selector span { font-weight: bold; }
- -

Grouping & Indenting Vendor Prefixes

- -
-.selector { 
-    background: #FFF; border: 1px solid #000; color: #EAEAEA;
-        -webkit-border-radius: 3px;
-        -moz-border-radius: 3px;
-        border-radius: 3px;
-}
- -
- - - - - - - -
-

CSS Shorthand

- -

Grouping Properties

- -

- Grouping properties together is one of the single most effective methods to greatly reduce the size of a CSS file. It's important to understand how properties are ordered (clockwise - top, right, bottom, left) - and how they can be further shortened (top and bottom, left and right). -

- -
-/* LONG CODE IS LONG */
-padding-top: 1px;
-padding-right: 2px;
-padding-bottom: 1px;
-padding-left: 2px;
-
-/* BETTER */
-padding: 1px 2px 1px 2px;
-
-/* BEST */
-padding: 1px 2px;
- -

From 0px to Hero

- -

- Assigning a unit type to a property value of zero is redundant. It is not important to know whether an element should be 0px from the left or 0 elephants from the left, just that it's bang on the left. -

- -
-/* BAD */
-padding: 0px 10px;
- -
-/* GOOD */
-padding: 0 10px;
- -
- - - - - - - -
-

Commenting Blocks

- -

- Commenting large blocks of CSS is a great way of keeping track of multiple style areas within the one stylesheet. - Obviously it works better with single line CSS styles, but the effect is not entirely lost on multi-line either. - The use of dashes versus equals versus underscores are all up the individual, but this is how I like to manage my stylesheets. -

- -
-/* === HORIZONTAL NAV === */
-#horizNav { width: 100%; display: block; }
-#horizNav li { display: block; float: left; position: relative; }
-#horizNav li a { display: block; height: 30px; text-decoration: none; }
-#horizNav li ul { display: none; position: absolute; top: 30; left: 0; }
-
-/* === HOME PAGE - CAROUSEL === */
-#carousel { width: 960px; height: 150px; position: relative; }
-#carousel img { display: none; }
-#carousel .buttons { position: absolute; right: 10px; bottom: 10px; }
- -
- - - - - - - - - - -
-

Clearing Floats

- -

- Clearing a <div> used to mean extra DOM, because it involved adding an extra clearer element. The better way is to set a specific width on the parent element ("auto" doesn't work in all browsers and scenarios) and an overflow value of either "auto" or "hidden". - "Hidden" obviously degrades better, but there are some IE compatibility versions where "auto" works better. -

- -

The HTML:

- -
-<div class="parentElement">
-    <div class="childElement">
-        I'm floated left!
-    </div>
-    I'm normal text that wraps around the float
-</div>
- -

The CSS:

- -
-.parentElement {
-    width: 100%;
-    overflow: hidden;
-}
-.childElement {
-    float: left;
-}
- -

- Contributors have also brought the latest clearfix to my attention. The micro clear-fix - is considered stable and cross browser compliant enough to make it to the latest HTML5 boiler plate release. I highly recommend you check it out. - Although I am not a massive fan of browser-specific CSS and pseudo elements such as :after, the micro clearfix is definitely more robust. - It also prevents top margins from collapsing which is an absolute life saver. -

- -
- - - - - - - - - -
-

Vertical & Horizontal Centering

- -

- Centering elements horizontally is not exactly rocket science, and I'm sure most of you are familiar with the following snippet: -

- -
-.class {
-    width: 960px;
-    margin: 0 auto;
-}
- -

- Front end devs have been using this snippet for a long time, without fully understanding why it didn't work vertically. From my understanding, it's important to remember that the parent element will generally have a height: auto; on it, and that there is no 100% height in which to vertically center the element. Applying the position: absolute; effectively moves the element out into position mode and responds to the pushing and pulling of auto margins and no specific location. -

- -
-.exactMiddle {
-    width: 100px;
-    height: 100px;
-    position: absolute;
-    top: 0;
-    right: 0;
-    bottom: 0;
-    left: 0;
-    margin: auto;
-}
- -

- The downsides of this method include its lack of support in IE6 and IE7, and the absence of a scroll bar if the browser is resized to be smaller than the centered object. There are more methods on this page (this is Method 4), but this is by far the best. -

- -

- The vertical centering of text in an element is also straightforward. If the text is on a single line, like a horizontal navigation item, you can set the line-height to be that of the element's physical height. -

- -
-#horizNav li {
-    height: 32px;
-    line-height: 32px;
-}
- -
- - - - - - - -
-

Feature Sniff, Don't Browser Sniff

- -

- In the earlier discusison of JavaScript feature detection, applying properties if a browser is any version of IE is increasingly problematic. - Man-of-steel Paul Irish pioneered the use of IE version sniffing to address this problem, but Modernizr has since come to the rescue. - Modernizr places classes on the root <html> element specifying whether features are supported. Bleeding edge styles can then easily cascade from (or be removed from) these classes. -

- -
-.my_elem {
-   -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.25);
-   -moz-box-shadow: 0 1px 2px rgba(0,0,0,0.25);
-   box-shadow: 0 1px 2px rgba(0,0,0,0.25);
-}
-
-/* when box shadow isn't supported, use borders instead */
-.no-boxshadow .my_elem {
-   border: 1px solid #666;
-   border-bottom-width: 2px;
-}
- -
- - - - - - - -
-

You're Not !important

- -

- A reliance upon the !important tag is a dangerous thing. The cases that warrant its use are rare and specific. They revolve around the necessity to override another stylesheet which you do not have access or permission to edit. - Another scenario is hard coding an element's styles to prevent inline JavaScript styles from taking precedence. Instead !important is used as a lazy shortcut to set the priority of your style over another, causing headaches further down the line. -

- -

- The use of the !important tag can be mostly avoided via the better understanding of CSS selector precedence, and how to better target elements. The more specific the selector, the more likely it will be accepted as the applicable style. - The following example from vanseodesign demonstrates the specificity at work. -

- -
-p { font-size: 12px; }
-p.bio { font-size: 14px; }
- -

- Their article on style precedence does a better job explaining inheritence than I ever could, so please give it a go. -

- -
- - - - - - - -
-

Aggressive Degradation

- -

- It's worth noting that this is a personal opinion, and best suited to very specific situations. The stance of aggressive degradation will not be well received in large commercial projects or enterprise solutions relying upon older browsers. -

- -

- Aggressive degradation dictates that if a particular (older) browser cannot render a certain effect, it should simply be omitted. A CSS3 button is a good example. - Effects such as border-radius, box-shadow, text-shadow and gradients will be displayed in cutting edge browsers. - A graceful fallback of a .PNG would be provided for slightly older browsers, and the most graceful of all solutions would include a PNG-Fix for IE6 or the use of filter arguments to replicate gradients and shadows. - However, aggressive degradation in this situation instructs you to neglect the older browsers and present them with a flat, satisfactory object. -

- -

- Put simply, aggressive degradation boils down to: if your browser can't render a gradient or a box shadow, tough luck. -

- -

- While not ideal for every situation, it ensures the timely delivery of projects and that the root product is still usable and not reliant on (validation breaking) hacks. -

- -
- - - - - - - - - - - -

CSS3 & HTML5

- - - - - - - -
-

Feature Sniff with Modernizr

- -

- I think I've gone on enough about this already. Use Modernizr to detect the availability of specific HTML5 and CSS3 features. -

- -
- - - - - - - -
-

@font-face Use and Abuse

- -

- Before you consider embedding a custom font, is important that you inspect the EULA and check if web embedding is allowed. Foundries are understandably reluctant to allow designers and developers - the ability to place font files directly on a server which can then be copied by a savvy end user. Particular foundries also prohibit the embedding of particular file types, such as .TTF and .OTF. -

- -

- If, after careful consideration, you believe the desired font is web embeddable: head on over to the Font Squirrel @font-face Generator. - It utilises Fontspring's bulletproof @font-face structure and automatically generates all the required file formats. -

- -
- - - - - - - -
-

Degradation

- -

- Thankfully browser handling of unsupported HTML5 and CSS3 is already that of a graceful nature. New additions to the list of <input /> types such as "email", "search" etc. will generally - degrade to normal <input type="text" /> when not natively supported. Similarly, CSS3 properties that aren't supported will simply not appear. Responsive layouts controlled by height and width - media queries are simply not applied. -

- -

- - Subtle CSS3 effects should be employed as a reward for users who run a modern browser. - -

- -

- The resources section below includes a few libraries to help normalise HTML5 and CSS3 functionality across a range of older browsers. -

- -
- - - - - - - -

Resources

- - - - - - - -
-

Useful Resources

- -

- The following resources are vital for the standardisation of code and interaction in a modern web page. They ensure that CSS3 and HTML5 features are made accessible across a range of browsers that previously lacked support. -

- - - -
- - - - - - - - - - - -
-

Support and Suggestions

- -

- This document was prepared by Tait Brown (@taitems). -

- -

- Questions, corrections and suggestions can be lodged on the GitHub repository for this page. You can also fork your own branch and add your own company/product specific guidelines. -

- -
- - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - \ No newline at end of file + + + + + + 前端开发指南 + + + + + + + + +
+
+ +
+
+ +
+ +

可用性

+ + + +
+

咋地了, DOCTYPE?

+ +

+ 不定义DOCTYPE是一种可以被判死刑的罪行。 以前你可能用的是下面的DOCTYPE,不过你要知道现在已经有更简洁清晰的代码取而代之了。 +

+ +
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+ +

+ 理想的状况是用HTML5 DOCTYPE,所有现代的浏览器都支持它,即使是不支持HTML5的浏览器,例如IE6,IE7,也会由此转入标准模式。 参见来源。 +

+ +
+<!DOCTYPE html>
+ +
+ + + + + +
+

编写合法且语义清晰的标记

+ +

+ 用整洁、语义清晰的HTML编写网站代码是我们一直孜孜以求的。有时我们会发现前人配置页面的方式限制了我们,或者有时我们编写的是HTML格式的email模板。但永远不要偏离HTML的规范,即使是为了解决特定浏览器兼容性的bug。 +

+ +

+ 所有的标题应该从<h2>开始分层级创建,文字段落应该总是放在<p>标签里,诸如此类。如果你编写的HTML的语义清晰,产生的页面会更整洁、简练,而且易于被搜索引擎爬虫解析。这是你能做到的最简单的SEO修补方式。 +

+ +

来看看下面的段落,你觉得哪个更整洁?是这个?

+ +
+<span class="sectionHeading">A Heading</span>
+<br /> <br />
+Lorem ipsum dolor sit amet. ...
+<br /> <br />
+ +

还是这个?

+ +
+<h2>A Heading</h2>
+<p>
+    Lorem ipsum dolor sit amet. ...
+</p>
+
+ + + + + + +
+

鼠标中键点击的应变方式

+ +

+ 现代web应用最令人郁闷的可用性缺陷之一是超链接功能的变种。 + 一些看起来像是超链接的元素可能是通过Javascript映射的单击功能,这就破坏了鼠标中键点击(在新的tab中打开链接页面)的功能。即使它们能在新的标签页打开,它们只带有一个 # 的href又会把你带回到同样的页面。 +

+ +

+ 深刻诠释了该问题的一个现代热门网站的例子就是Twitter。在它的整个应用里,鼠标中键点击用户名或头像会得到完全不同的结果。 +

+ +
+<!-- 旧的方式,破坏网页语义 -->
+<a href="#"></a>
+
+<!-- 如果鼠标点击不能产生一个页面,那就不是超链接 -->
+<span class="link" role="link"></span>
+
+ +

+ 另一个替代方案是使用 # 引导的路径,它会把普通的url映射为 # 引导的链接,然后通过AJAX来获取页面片段。提供此功能的库应该能够在鼠标中键点击的时候正常显示页面,或者在左键点击时把该页面内容加载到指定的区域。不过这样也要慎重行事,有很多人都认为 #链接正在破坏web应用。 +

+ +
+ + + + + + + +
+

用Microformats格式表示联系人信息

+ +

+ Microformat是一种便于机器读取联系人信息的方式。hCard类(不是vCard)用来定义元素里包含的内容类型。这些内容会被浏览器提取并突出显示。 +

+ +
+<span class="tel">
+    <span class="type">home</span>:
+    <span class="value">+1.415.555.1212</span>
+</span>
+ +

+ 如果你曾经浏览采用此格式的网页,你会注意到类似skype的程序可以轻松检测到网页上的哪些数字是电话号码。在iOS设备上的Safari浏览器也可以做到类似的事情。 +

+ +

+ 有关Microformat的更多信息请参阅http://microformats.org/wiki/hcard +

+
+ + + + + +
+

图片需要设 ‘Alt’ 文本

+ +

+ <img> 标签需要 alt 文本,以便检查并满足可读性的要求。 在 alt 属性中的文本必须能够说明图片显示的内容或要达到的效果,除非该图片不重要。 +

+

+ 如果图片只是一个列表中的着重号或者其他无关紧要的图标,最好是给 alt 属性一个空字符串,但还是留着它。这样屏幕阅读器会忽略它,而不是把“着重号”连读20次。 +

+ +
+<img src="dog.gif" alt="Fido and I at the park!" />
+<!-- 很好,描述清晰 -->
+
+<img src="bullet.gif" alt="bullet" />
+<!-- 不好,显得多余 -->
+
+<img src="bullet.gif" alt="" />
+<!-- 好 -->
+
+ + + + + +
+

只对表格数据用table标签

+ +

+ table标签永远只应该用在表格数据的展示上。唯一的例外是当编写HTML格式的邮件时,这种情况下可能table是某些坑爹的邮件客户端唯一支持的样式了。 +

+

+ 为了可读性,表格头永远要使用 <th> 元素。同时切记要设置cellpadding, cellspacingborder 的值为 0 , 因为这些样式由CSS来控制更容易确保一致性。 +

+ +
+<table cellpadding="0" cellspacing="0" border="0">
+    <thead>
+        <tr>
+            <th>
+                Cell Header
+            </th>
+        </tr>
+    </thead>
+    <tbody>
+        <tr>
+            <td>
+                Cell Item
+            </td>
+        </tr>
+    </tbody>
+</table>
+            
+ +
+ + + + + +
+

使用 jQuery 和 jQuery UI Widgets

+ +

+ jQuery 和 jQuery UI 被做成尽可能在不同浏览器上表现出几乎相同的外观和功能。 + jQuery UI 被设计为符合 WAI WCAG 2.0 及 WAI ARIA, 因此采用该框架可以避免在你的站点上运行的插件或脚本的所有不确定性。 +

+
+ + + + + + + + + +

JavaScript

+ + + +
+

代码留空和格式

+ +

+ 任何关于代码格式、留空和大括号位置的讨论都会引起激烈辩论。对此,我想最简单的规则就是,除非你愿意把整个代码文件重新格式化,不然还是尊重并保持已有代码文件的格式。这意味着如果你看到一个JS文件里的大括号没有换行写,那你的代码也要继续保持大括号不换行。如果你的代码没有和代码文件里的其他部分保持一致,那么你的代码就不应该通过代码审查流程。 +

+ +

+ 一致的代码格式让代码更加易读,同时也意味着代码容易用查找/替换命令进行修改。谢天谢地,我们自己形成的编程习惯和jQuery正式推荐的方式非常相似。细微的差异也是有的,不过,那些是个人问题或者我们觉得没法维护的一些东西。 + 参阅jQuery核心样式指南 +

+ +

字符间距

+ +
+// 不好
+if(blah==="foo"){
+    foo("bar");
+}
+
+// 好 :)
+if (blah === "foo") {
+    foo("bar");
+}
+ +

大括号不换行

+ +
+// 不好
+if (foo)
+{
+    bar();
+}
+
+// 好 :)
+if (foo) {
+    bar();
+}
+ +

总是用大括号

+ +
+// 不好
+if (foo)
+    bar();
+
+// 好 :)
+if (foo) {
+    bar();
+}
+ +

字符串处理

+

+ 引用字符串永远要用双引号。 有些人非常喜欢用C语言风格的字符串(单引号),但这种习惯会导致脚本内部的风格冲突。C语言风格的字符串处理要求空字符串和单字符包在单引号里,而短语和单词必须包在双引号内。 +

+ +
+ + + +
+

注释

+ +

+ 往代码里玩命加注释的需求是由各种经理、主管及其他很少接触代码的人们引领的。这种需求无非是雇员们考核指标中的一个勾选栏,花在这上面的时间基本没有带来什么回报。 + 如果那些从善如流的开发者能遵循本文档中提出的建议,他们的代码会变得相当易于阅读,一目了然,以至于再用注释描述这些代码会多余得令人尴尬。来看下面的例子。在这里,布尔变量被作为问题提出,而函数也有直观的命名。 +

+ +
+if (user.hasPermission) {
+    editPage();
+}
+ +

+ 至少在这个场景中,注释是完全没有必要的。 +

+ +

注释重要的情况

+ +

+ 一个项目里,永远会有某些部分难以查阅和理解。比如一个复杂的正则表达式,或者一个对角度进行计算或在度和弧度单位之间切换的数学函数。没有上面的注释,初级或中级的读者将对脚本的含义毫无头绪。 +

+ +
+// 校验美国电话号码的正则表达式,号码格式是 (XXX) XXX-XXXX (减号、空格和括号都是可选的,可以有也可以没有)
+var phoneRegEx = /^\(?(\d{3})\)?[- ]?(\d{3})[- ]?(\d{4})$/;
+ + +
+ + +
+

总是使用 === 比较符

+ +

+ 使用 == 比较符可以让令人郁闷的bug消失于无形。它允许在 JavaScript花园 中有清楚解释的弱类型。使用严格的 === 比较符不会执行类型强制转换,从而能够严格地评估两个对象之间的差别。再说一遍,更多详细信息请参见 JavaScript花园。 +

+
+var zeroAsAString = "0";
+
+if (zeroAsAString == 0) {
+    // 这样也能判断为true,呵呵...
+}
+
+if (zeroAsAString === 0) {
+    // 判断为false
+}
+ +

例外

+

+ 在和null进行比较的时候,允许使用 == 比较符,因为它会检测null和undefined两个属性。如果你不完全理解这个原理,那我还是建议你用 === 比较符为好。 +

+
+var foo = null;
+
+// foo 是 null, 但 bar 是 undefined ,因为它尚未被声明
+if (foo == null && bar == null) {
+    // 上面的判断还是成立的
+}
+ +
+ + + + + + +
+

使用 .parseInt() 的时候,总是指定第二个 ‘radix’ 参数

+ +

+ 把字符串解析为整数的时候,有个好习惯是指定第二个基数参数 -- 它会确定该字符串被转换成几进制。当字符串以 0 开头的时候,缺省情况下会触发 16 进制作为基数。大部分初级和中级用户只会用到 10 这个基数。 感谢 João Moreno 记录的这个 勘误。 +

+ +
+alert( parseInt("08") ); // alerts: 2
+
+alert( parseInt("08", 10) ); // alerts: 8
+ +
+ + + + + + + +
+

避免比较 true 和 false

+ +

+ 直接比较 true 和 false 的值是没有必要的。有时候也许明确一下有好处,但它还是额外的代码。 +

+ +
+if (foo === true) {
+    // 用了 === 倒是不错,可这是多余的
+}
+
+if (foo) {
+    // 赞!
+}
+
+if (!bar) {
+    // 反过来也赞
+}
+ + +
+ + + + + + +
+

避免污染全局命名空间

+ +

+ 过分依赖全局变量是我们组所有人 -- 特别是我自己 -- 特别有负罪感的一件事。关于为啥全局变量不好的讨论是相当直接的:这增加了脚本和变量冲突的概率,而且源文件和命名空间本身都会充斥着数不清的命名模糊的变量。 +

+ +

+ Douglas Crockford 坚信一个Javascript应用的代码质量可以用其中使用的全局变量数来评价,越少越好。由于并不是什么都可以定义成local的(不过要诚实,其实你现在考虑的那个是可以的,别偷懒),你需要想办法整理你的变量以避免冲突,并把命名空间的膨胀减到最小。最简单的方法就是采用单变量或者把用到这些全局变量的模块尽可能减少。 Crockford提到YUI只用了一个全局变量,YAHOO。他在他的博文 "全局统治" 中讨论了更多的细节问题。 +

+ +

+ 考虑这种情况:对于小型web应用,全局变量通常用于保存应用级的设置,可以用你的项目名或者settings作为命名去定义一个对象,这样总的来说会更好。 +

+ +
+// 被污染的全局命名空间
+var settingA = true;
+var settingB = false;
+var settingC = "test";
+
+// 用 settings 作为对象命名
+var settings = {
+    settingA: true,
+    settingB: false,
+    settingC: "test"
+}
+ +

+ 不过,如果我们可以通过避免使用全局变量来减少冲突概率,但是把命名空间标准化成一样的,岂不是会增加各个应用之间产生冲突的概率么?呃,这个担忧确实有道理。所以,建议你用自己特定的应用名作为全局变量的命名空间,或者用和jQuery采取的 $.noConflict() 模式相同的方法重新分配你的命名空间. +

+ +
+var myAppName = {
+    settings: {
+        settingA: true
+    }
+}
+
+//访问全局变量
+myAppName.settings.settingA; // true
+ +
+ + + + + + + +
+

驼峰法变量命名

+ +

+ JavaScript变量的驼峰法命名在大部分编程环境中都是作为标准的。有读者在评论中提出了唯一的例外,就是要用大写字母加下划线来指代常量。 +

+ +
+var X_Position = obj.scrollLeft;
+
+var xPosition = obj.scrollLeft; // 更好,更简洁
+
+SCENE_GRAVITY = 1; // 常量
+ +
+ + + + + + + + +
+

循环的性能 - 缓存数组长度

+ +

+ 循环估计是Javascript性能调优最重要的部分了。在一个循环内部节省个一两毫秒的,说不定总体就省出好几秒来了。这里有一招就是把数组的长度缓存,这样在循环里就无需每次迭代的时候都计算一遍了。 +

+ +
+var toLoop = new Array(1000);
+
+for (var i = 0; i < toLoop.length; i++) {
+    // 败家玩意 - 长度会反复算 1000 次你知道不?
+}
+
+for (var i = 0, len = toLoop.length; i < len; i++) {
+    // 会过日子 - 长度只计算一次,然后缓存了
+}
+ +

例外

+

+ 如果你对一个数组做循环来查找并删除某个元素,这就会改变数组长度。任何时候你只要会在循环内部增加或删除元素来改变数组的长度,你就给自己带来了麻烦。这种情况下,你要么每次改变后重新设置数组长度,要么就别缓存它了。 +

+ +
+ + + + + + + + +
+

循环的性能 - 使用 ‘break;’ 和 ‘continue;’

+ +

+ 跳过和跳出循环的能力对于避免开销很大的循环周期是非常有用的。 +

+ +

+ 如果你是在循环内部查找,查找成功以后你会做什么?比如1000个元素的循环执行到一半,你就找到了你要的东西。你不管三七二十一,即使知道后面的if语句不会再有符合的机会,还是让循环继续把剩下的500个元素迭代完么?不!你应该跳出循环,必须的! +

+ +
+var bigArray = new Array(1000);
+
+for (var i = 0, len = bigArray.length; i < len; i++) {
+    if (i === 500) {
+        break;
+    }
+    console.log(i); // 这样只会输出 0 - 499
+}
+ +

+ 另一个问题是跳过某个特定的迭代,然后继续循环。虽然说类似于奇偶数这样的条件可以通过把 i++ 替换成 i + 2 的办法来管理,有些条件还是需要具体检测,然后触发跳过操作。任何能够避免执行完整的迭代过程的东西都是很有用的。 +

+ +
+var bigArray = new Array(1000);
+
+for (var i = 0, len = bigArray.length; i < len; i++) {
+    if (condition) {
+        continue;
+    }
+    doCostlyStuff();
+}
+ +
+ + + + + +
+

函数调用不要传输太多的参数

+ +

+ 对于可读性而不是其他因素来说,下面这种方式真是糟透了: +

+ +
+function greet(name, language, age, gender, hairColour, eyeColour) {
+    alert(name);
+}
+ +

+ 下面的例子预先构建了一个对象作为参数,或者把内联对象传递过去,这样就好多了。 +

+ +
+function greet(user) {
+    alert(user.name);
+}
+
+greet({
+    name: "Bob",
+    gender: "male"
+});
+ +
+ + + + + + + +
+

把 ‘this’ 映射为 ‘self’

+ +

+ 在编写面向对象(OO)Javascript代码的时候, 必须了解 this 的作用范围. 不管你用来构建伪类的设计模式是什么,对 this 的引用是指向一个实例的最简单办法。当你开始把jQuery的helper方法和你的伪类集成的时候,你就会注意到 this 的作用范围变化。 +

+ +
+Bob.findFriend("Barry");
+
+Person.prototype.findFriend = function(toFind) {
+    // this = Bob
+    $(this.friends).each(function() {
+        // this = Bob.friends[i]
+        if (this.name === toFind) {
+            // this = Barry
+            return this;
+        }
+    });
+}
+ +

+ 在上面的例子里, this 经历了从对 Bob 的引用,变成对他的朋友 Barry 的引用的过程。 了解 this 的取值在一段时间发生的变化是很重要的。在原型函数内部, this 指向所在伪类的当前实例(这里是 Bob )。而一旦我们进入 $.each() 循环, this 就会重新映射为被解析数组的第 i 个元素。 +

+ +

+ 解决办法是把 this 的值重新映射为 self 或者 _self。虽然 self (不带下划线)并不是 保留字, 但它 确实是 window 对象的一个属性。虽然我上面用到 self 的例子是从jQuery源代码中挑的,但他们也认识到了这个错误,正打算 修正目前的状况 ,也就是改用 _self。我个人还是喜欢用 self ,不为别的,就因为它的简洁 -- 不过它可能会冒出一些非常令人困惑的bug。总之,用 self 有风险,使用需谨慎。 +

+ +

+ 在下面的例子中,我会更好地利用在 $.each() helper 中提供的参数,同时重新映射 this 的值。 +

+ +
+Bob.findFriend("Barry");
+
+Person.prototype.findFriend = function(toFind) {
+
+    // 就这一次用到了 "this"
+    var _self = this;
+
+    $(_self.friends).each(function(i,item) {
+        if (item.name === toFind) {
+            return item;
+        }
+    });
+
+}
+ +
+ + + + + + +
+

我能用 Boolean 吗?

+ +

+ 布尔变量必须能够很容易通过命名来识别。可以用类似于 is, can 或者 has 的前缀来形成一个问句。 +

+ +
+isEditing = true;
+
+obj.canEdit = true;
+
+user.hasPermission = true;  
+ +
+ + + + + + +
+

尽量减少重新绘制和重新布局

+ +

+ 重新绘制和重新布局与重新渲染DOM的过程关联,这个过程会在特定属性或元素被改变时发生。重新绘制是在某个元素的外观被改变但没有对布局进行调整的情况下触发的。 + Nicole Sullivan 在一篇全面的 博文 + 中把这些改变描述为诸如是否可见或背景色变化之类的样式改变。重新布局则是开销更大的操作,由调整页面布局的一些改变引发。例如增加或删除元素,改变某个元素的宽度或高度,甚至是改变浏览器窗口的大小。最糟糕的情况是重新布局导致先辈、兄弟和孩子节点元素也需要重新布局的多米诺骨牌效应。 +

+ +

+ 毫无疑问,重新绘制和重新布局应该尽量避免,但是如何做到呢? +

+ +

重新布局的例子

+ +

+ 其实也不是说下面的代码就很糟糕啦。不过我们先假定数组 arr 有10个元素 +

+ +
+var myList = document.getElementById("myList");
+
+for (var i = 0, len = arr.length; i < len; i++) {
+
+    myList.innerHTML += "<li>" + arr[i].title + "</li>"; //重新布局 -- 增加到元素
+
+}
+ +

+ 在上面的 for 循环里,每次迭代会触发一次重新布局。10次迭代就是10次重新布局。 +

+ +

+ 现在考虑下面的代码: +

+ +
+var constructedHTML = "";
+
+for (var i = 0, len = arr.length; i < len; i++) {
+    constructedHTML += "<li>" + arr[i].title + "</li>"; //没有重新布局 - 增加到字符串
+}
+
+document.getElementById("myList").innerHTML = constructedHTML; //在这里重新布局
+ +

+ 在这个场景里,需要增加的元素是在一个字符串里构建的。循环里边没有产生任何重新布局,因为DOM并没有变化。只有当数组被完全循环完毕,构建的字符串被应用到某个对象的 innerHTML ,这才产生函数里唯一的一次重新布局。 +

+ +

+ 有无数种重新布局和重新绘制是可以避免的,希望你幸运地了解了那些诀窍。这方面的阅读材料汗牛充栋,不过大部分的材料都会引用到 Nicole Sullivan的这篇 博文 ,这是一个完美的起点。除了这里的经验,在涉及到"web 3.0"和HTML5时代的多种技术术语的时候,还有其他重要的经验教训值得汲取。上面的分析直接适用于写jQuery代码。在捣腾 canvas 的时候这些原则也很重要,另外尽量保持帧频在30-60的范围内。 +

+ +
+ + + +
+

不要用微秒来产生唯一的ID

+ +

+ 自打web开发早期开始,就流行一种产生唯一ID的方法。具体做法是把从1970年1月1日开始计算的微秒数加到你的静态ID后面,如下所示: +

+ +
+var myID = "static" + new Date().getTime();
+ +

+ 这本来是相当万无一失的方法,因为即便两段这样的代码连续执行,在它们执行的间隙也会有几毫秒。可是现在新的浏览器带着新的Javascript引擎,伴随着一直在提升的主频。到现在,上面的代码产生相同的毫秒数的可能性会比产生间隙的可能性更大。 +

+ + +

+ 这会导致传统方法难以debug的bug。因为你的DOM是在运行中创建的,对页面源代码进行传统的测试无法确定多个重复ID的错误。Javascript和jQuery的错误处理机制会把第一个匹配的作为ID并忽略其他的重复ID。所以它甚至都不会抛出JS错误! +

+ + +

+ 这样不行,唯一真正的方法是逐行设断点和log,但是如果断点的位置不对,你的毫秒数又不会冲突了! +

+ + +

+ 好消息是有很多产生唯一ID的替代方法。学究气一点的说法是,计算机的随机数函数其实并不是真正随机的,因为它还是来源于系统时间,虽然这一点值得注意,但是随机数冲突的可能性是微乎其微的。 +

+ +
+var myID = "static" + Math.round(Math.random() * 10000);
+ + + + +

+ 我个人更偏爱人工产生GUID方法。从技术角度说,GUID是根据你的硬件创建的,不过下面的Javascript函数做得相当棒。这是我从 stack overflow 的一个帖子 里偷来的,相当顺手的一个函数。 +

+ +
+function S4() {
+   return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
+}
+function guid() {
+   return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
+}
+ +
+var myID = "static" + guid();
+ +
+ + + + + + + +
+

检测特性,而不是检测浏览器类型

+ +

+ 用户的浏览器是否支持地理信息?用户的浏览器是否支持web works?HTML5 视频?HTML5 音频?答案曾经是这样的: +

+ +
+if ($.browser.msie) {
+    // 哦,是IE啊,那肯定不支持
+}
+ +

+ 但是世界在快速变化。最新版的IE几乎能算是现代浏览器了,但它依旧给前端开发带来痛苦。更早版本的IE基本上和它之前的版本一样烂,这就让偷懒的Javascript程序员习惯于检测 if (ie) 然后执行一些微软专用的破语法。现在IE9已经废弃了这些专用函数,那些原来的 if (ie) 老古董就反而会坏事了。 +

+ +

+ 那么,如果能检测每个特性而不用检测(既不可靠又能伪装的)user-agent,你觉得咋样? +

+ +

+ 如果你的回答是 "那相当靠谱", 那你就说对了。 +

+ +

+ 用 Modernizr 吧,这是行业梦幻级大师Paul Irish参与开发的一个Javascript库。该库集广泛应用、轻量级和海量文档三大优势于一身,实施无需动脑,实为居家旅行、杀人灭口必备。它会产生一个 Modernizr 对象,其中包含了它所有检测测试的结果,这样检测某个特性的支持与否就和下面的例子一样简单: +

+ +
+// 检测浏览器是否支持canvas的老办法
+if (!!document.createElement('canvas').getContext) { ... }
+
+// 用 Modernizr 检测
+if (Modernizr.canvas) { ... }
+ +
+ + + + + + + + +
+

使用可读的毫秒数

+ +

+ 毫秒数的一种方便的写法是写成可读的。对于初学者这很棒,但是大部分情况下其实只是一个噱头。 +

+ +
+// 这是3秒,30秒还是300秒啊?
+var timeout = 30000;
+
+// 增加了额外的计算开销,但是读和修改会更容易
+var timeout = 30 * 1000;
+ +
+ + + + + + + + + + + + + +

关于jQuery代码

+ + + + + + + +
+

像疯狗一样串接

+ +

+ jQuery最好的特性之一就是它的函数串接。你可能已经用过一点,也许把一些简单的调用一个接一个串起来...但是你是否曾经像头疯狗一样在DOM里上蹿下跳地遍历呢?还是花点时间来熟悉一下 .end() 函数。等你从起始选择器开始在DOM里上蹿下跳的时候,这个函数会很关键。 +

+ +
+$(".quote")
+    .hide()
+    .find("a").text("Click here").bind("click",doStuff).end()
+    .parent().removeClass().addClass("testimonial").draggable().end()
+    .fadeIn("slow");
+ +

+ 上例中,每次我们完成对某个DOM对象的操作,要反向遍历DOM返回我们引用的原始对象的时候,就需要使用 .end() 函数。然后我们就顺藤摸瓜扎回原来DOM里的位置了。 +

+ +
+ + + + + + + +
+

使用 data-* 属性

+ +

+ 你们当中那些已经写了很长时间Javascript(原生的,不是jQuery)代码的同学,很可能都熟悉各种属性吧。你们想办法设置它们,获取它们,或者滥用 reltitle ... +

+ +

+ 别说HTML5 或者 jQuery 没帮上忙哦。新的描述中允许在HTML元素中使用 data- 前缀来指明包含数据的属性,jQuery会把指定的字符串转换成正确的Javascript数据类型,这活干的非常漂亮。我们来创建一个带有某些数据属性的 DIV 。 +

+ +
+<div id="test" data-is-bool="true" data-some-number="123"></div>
+ +

+ 现在,即使我们的值被包装在引号里面,它们也不会被当做字符串处理: +

+ +
+typeof $("#test").data("isBool"); // boolean
+
+typeof $("#test").data("someNumber"); // number
+ +

特殊的大小写

+ +

+ 要注意,要让这些代码片段正常工作,(HTML里的属性定义)必须使用小写字母,这很重要。不过如果你是一位很强的前端开发者,你还是会想用驼峰法来命名你的数据变量。正如在Javascript里很多地方出现的,前置的连接符意味着下一个字母会适用于驼峰法大写。不过,下面的例子里在HTML属性定义中使用驼峰法是 不行的 ,会让上面的Javascript代码返回 undefined。 +

+ +

+ 不好使 :( +

+
+<div id="test" data-isBool="true" data-someNumber="123"></div>
+ +

+ 好使 :) +

+
+<div id="test" data-is-bool="true" data-some-number="123"></div>
+ +
+ + + + + + + +
+

‘.stop()’ 停止协作和监听

+ +

+ 把jQuery动画效果和鼠标事件绑定是基于web的现代用户交互方式中的关键部分,可是这方面即便某些最有名的网站也做得很蹩脚。这篇文章 提供了一个实现动画的直接例子并且演示了这些动画放在一起在视觉上会产生多么不和谐的效果。 + 好在这个问题可以利用一个函数前缀或在 $.animate 调用中加入一个参数来轻松解决。 +

+ +

+ 在使用 $.animate 的时候, 可以在参数中加入 queue: false 来避免串接。诸如 $.fadeIn$.slideDown 这样的动画快捷方式不接受 queue 设置,你必须用 $.stop 这个方法预先停止这些动画.。 + 在特定的场景下,需要某个动画直接停下,或跳转到变换的最终状态。推荐你先熟悉有关 clearQueuejumpToEnd 这两个参数的相关 文档 ,因为老天在上,我没有其他办法帮你。 +

+ +
+$("selector").stop(true,true).fadeOut();
+
+$("selector").animate({
+    property: value
+}, {
+    duration: 1000,
+    queue: false
+}
+ +
+ + + + + + + +
+

优化你的选择器

+ +

+ jQuery 很高冷。它几乎无所不能,不过它目前还没法给你冲咖啡,我听说在2.0版的路线图里有才这个特性。你需要当心的一件事是别滥用它的 sizzleJS 选择器引擎的能力。想避免这种问题可以有两个策略:缓存选择器结果 以及 使用高效率的选择器。 +

+ +

缓存选择器结果

+ +

+ 是每次你要修改一点东西的时候都先进行开销巨大的DOM查询,还是保存一份元素的索引?选择一目了然。 +

+ +
+// before
+$(".quote a").bind("click", doStuff); // DOM查询
+
+// now
+$(".quote a").addClass("quoteLink"); // DOM查询!!
+
+// later
+$(".quote a").fadeIn("slow"); // 又一次DOM查询!!!
+ +

+ 忽略串接,这样做更好: +

+ +
+// before
+var $quoteLinks = $(".quote a");  // 只需一次DOM查询
+$quoteLinks.bind("click", doStuff);
+
+// now
+$quoteLinks.addClass("quoteLink");
+
+// later
+$quoteLinks.fadeIn("slow");
+ +
+ +

使用高效率的选择器

+ +

+ + 好了,jQuery/sizzleJS 可以轻松使用CSS3选择器,但是真正的开销是什么? 在这种场景下浏览器有可能会使用 document.querySelector(), 但是它也有可能分拆你的选择器字符串,然后手工去查询DOM。 +

+ +
+// ID搜索是最快的查询方式,然后它获取孩子节点的列表,匹配其中class为'quotes'的元素
+$("#quoteList").children(".quotes");
+
+// 只在预先确定的bar元素下查找'foo'class
+$(".foo",bar); 
+ +
+ + + + + + +
+

‘for’ 循环总是比 ‘each()’ 循环快

+ +

+ 不管未来几年在浏览器开发领域会发生什么,本地的 for 循环永远会比jQuery的 $.each() 循环快。 + 当你思考jQuery到底是什么(把本地JS函数包装起来的一个库)这种高大上问题的时候,你就会开始认识到本地原生Javascript代码永远会更快。用库还是用原生,这是一个运行速度和编程速度之间的权衡。 +

+ +

+ 很重要的一点是,对那些可能每秒调用数百次的性能关键的函数,总是要使用 for 循环。例如: +

+ + + +
+ + + + + + + + + + + + + + + + + + + +

CSS

+ + +
+ +

理解盒子模型是关键

+ +

+ "盒子模型"对于理解浏览器如何渲染页面是关键性决定性的因素。对其复杂性的全面理解有助于奇迹般地简化你的工作。盒子模型描述了对HTML元素的物理维度进行计算的方式。如果一个块元素具有固定宽度,比如说100px,那么应该如何确定它的 padding, border 和 margin 呢? +

+ +

+ 很多网站都有深入的描述,但咱们简单点说:在遵循标准的浏览器中,border和padding是被放在指定宽度之外的。最好是用图形来解释。比如下列代码: +

+ +
+.foo {
+    width: 150px;
+    height: 150px;
+    padding: 25px;
+    border: 25px solid;
+    margin: 20px;
+}
+ + +

你可能估计的情况(Quirks 模式)

+ +

+ padding 和 border 都是往里算的,结果保持高度和宽度都是150px。 +

+ +

+ Box Model - Quirks Mode +

+ +

你看到的情况(遵循标准模式)

+ +

+ 可是,实际上出来的宽度和高度都是250px。 也就是150px + (2 * 25) + (2 * 25)。 +

+ +

+ Box Model - Standards Mode +

+ + +

+ 如果你觉得这个结果很奇怪,那你不是一个人(呃,你是人,只是说还有其他人也会这么想)。 现在手头有个修复办法,需要引入一个CSS属性叫 box-sizing,这个属性对于 IE8 及以上版本 都适用。它允许你选择计算元素维度的确切方式,这样就能救你于危难之中。具体支持的参数因浏览器而异,另外需要用到浏览器厂商的前缀,具体细节请参阅 caniuse.com 。 +

+ +
+/* 旧方法 (178 + 20 + 2 = 200) */
+.foo {
+    width: 178px;
+    padding: 10px;
+    border: 1px;
+}
+
+/* 更好的方法 */
+.foo {
+    width: 200px;
+    padding: 10px;
+    border: 1px;
+        -webkit-box-sizing: border-box;
+        -moz-box-sizing: border-box;
+        box-sizing: border-box;
+}
+ +

+ 虽然说你也总是可以对宽度进行心算,在各个像素数中减来减去(就像第一个方法做的那样),但在涉及到不同的宽度单位时(比如百分比或者EM),就没人能搞清楚到底该怎么做了。目前,除了把元素包在父元素中,以确保宽度和 padding/margin/borders 可以全部分开之外,也没有别的解决办法。 +

+ +
+ + + +
+ +

知道什么时候用float,什么时候用position

+ +

+ 用table进行布局的时代过去了。现在要承认我们可以集中精力去理解float和position的工作原理。这里需要掌握一套特别的思维模型,我相信这件事最好是通过动手练习来进行。 +

+ +

+ 用float从DOM中提取元素并强制它们靠到左边或右边,那是相当靠谱。它们已成为前端开发的后table布局时代的万金油,这可能是因为以前浏览器对于 display: inlineinline-block 的支持不力,还有对position的支持中冒出的 z-index bug。可现在就真的没有借口了。 inline-block 已经支持得很好了,简单的一点修正就能让它在 IE7 里应用。 +

+ +

+ 谢天谢地,以前那些阻挠用CSS对元素进行绝对定位的争论都消亡了。理论上,定位属性可以让你在页面上以X和Y坐标放置元素,这种方式简单直接,Flash开发者们都应该很熟悉。 +

+ +

理解 Position

+ +

+ 用CSS定位元素的时候,理解一个事实非常重要:定位的位置总是相对于离它最近的有定位属性的父元素而言的。人们刚开始用CSS的时候会有一个常见的误解,认为 position: absolute; 是相对页面的root元素定位的。 我觉得这种误解来源于某些情况下,元素没有任何父元素具备position样式 -- 在这种情况下,他们的结论是对的。这样向上遍历DOM树没有找到任何有定位样式的元素,就会定位到页面的根元素上。 +

+ +

+ 那么,如果 position: absolute; 是把元素从他们所在的流中抽取出来,那你如何相对一个元素的父元素对它进行定位呢? 方法很直接。父元素需要定义 position: relative; 样式,然后所有的孩子元素就会按上、右、下、左的顺序依次摆放。利用这个知识,你会如何实现下面很直观的布局呢? +

+ +

+ How would go about coding up this image? +

+ +

+ 使用 float,你会需要把这些元素包在一个父元素中, 然后把.one float靠左,然后改动 .two.threefloatmargin 。最后你应该写出类似下面的东西: +

+ +
+.parent {
+    /* ghetto clearfix */
+    width: 310px;
+    overflow: auto;
+}
+.one {
+    width: 200px;
+    height: 210px;
+    float: left;
+}
+.two {
+    width: 100px;
+    height: 100px;
+    float: right;
+    margin-bottom: 10px;
+}
+.three {
+    width: 100px;
+    height: 100px;
+    float: right;
+}
+ +

+ 正如我们前面所说,使用 position 让我们可以用很明确的方式,按照 X 和 Y 坐标把元素显示在屏幕上。 上面用float的方式会把页面上的长文字隔开,下面的方法则可以确保所有元素处于正常位置,无论页面上有什么内容。 +

+ +
+.parent {
+    position: relative;
+    width: 310px;
+    height: 210px;
+}
+.one, .two, .three {
+    position: absolute;
+}
+.one {
+    top: 0;
+    left: 0;
+    width: 200px;
+    height: 210px;
+}
+.two {
+    top: 0;
+    right: 0;
+    width: 100px;
+    height: 100px;
+}
+.three {
+    bottom: 0;
+    right: 0;
+    width: 100px;
+    height: 100px;
+}
+ +

+ 如前文所述,有些 z-index 的问题需要考虑。虽然上面的例子可能显得有点过分,不过一旦你开始思考定位,它会打开一个各种可能性的新世界. +

+ +
+ + + +
+

留空

+ +

+ 如果我们在单行和多行CSS参数的格式之间变来变去,CSS里的留空也会不一样。我不打算对这个说太细。 +

+ +

合适的空白

+ +
+/* 不好 */
+.selector {display:none;background:#ff0000;color:#000000;}
+
+/* 好 -- 单行 */
+.selector { display: none; background: #ff0000; color: #000000; }
+
+/* 好 -- 多行 */
+.selector {
+    display: none;
+    background: #ff0000;
+    color: #000000;
+}
+ +

大括号不换行

+ +
+.selector {
+    display: none;
+    background: #ff0000;
+    color: #000000;
+}
+ +

子元素缩进

+ +

+ 这个用不用就见仁见智了,我个人只会在单行定义的CSS文档中用这种格式。 +

+ +
+.selector { display: none; background: #ff0000; color: #000000; }
+    .selector a { text-decoration: none; }
+    .selector span { font-weight: bold; }
+ +

组合并缩进浏览器厂商前缀属性

+ +
+.selector {
+    background: #FFF; border: 1px solid #000; color: #EAEAEA;
+        -webkit-border-radius: 3px;
+        -moz-border-radius: 3px;
+        border-radius: 3px;
+}
+ +
+ + + + + + + +
+

CSS 速记格式

+ +

属性分组

+ +

+ 把属性分组到一起是大大减少CSS文件大小的最有效方法。这里很重要的一点是要理解属性是如何排序的(顺时针 -- 上,右,下,左),以及如何进一步缩短它们(上和下,左和右)。 +

+ +
+/* 逐个定义,太长了 */
+padding-top: 1px;
+padding-right: 2px;
+padding-bottom: 1px;
+padding-left: 2px;
+
+/* 上,右,下,左,好很多 */
+padding: 1px 2px 1px 2px;
+
+/* 上和下,左和右,最优 */
+padding: 1px 2px;
+ +

从 0px 到英雄

+ +

+ 给值为 0 的属性分配一个单位类型是多余的。一个元素是距离左边 0px 还是 0 elephants 根本不重要,只要知道它是贴着左边就行了。 +

+ +
+/* 不好 */
+padding: 0px 10px;
+ +
+/* 好 */
+padding: 0 10px;
+ +
+ + + + + + + +
+

注释块

+ +

+ 对于在一个样式表里维护多个样式区域的任务,给大段CSS加上注释是很好的办法。显然这和单行CSS风格配合使用效果更佳,不过这个效果在多行CSS风格里也不是完全没用。注释里用破折号、等号还是下划线起强调作用就见仁见智了,不过下面是我喜欢的方式: +

+ +
+/* === HORIZONTAL NAV === */
+#horizNav { width: 100%; display: block; }
+#horizNav li { display: block; float: left; position: relative; }
+#horizNav li a { display: block; height: 30px; text-decoration: none; }
+#horizNav li ul { display: none; position: absolute; top: 30; left: 0; }
+
+/* === HOME PAGE - CAROUSEL === */
+#carousel { width: 960px; height: 150px; position: relative; }
+#carousel img { display: none; }
+#carousel .buttons { position: absolute; right: 10px; bottom: 10px; }
+ +
+ + + + + + + + + + +
+

清除浮动

+ +

+ 清除一个 <div> 过去意味着额外的DOM,因为这会涉及到增加一个额外的清除元素。更好的办法是给父元素设置明确的宽度('auto'并不是在所有浏览器和场景中有效)以及把overflow属性设为'auto'或者'hidden'。'hidden'显然兼容性更好,但在某些兼容IE的版本里'auto‘的效果好一些。 +

+ +

HTML:

+ +
+<div class="parentElement">
+    <div class="childElement">
+        I'm floated left!
+    </div>
+    I'm normal text that wraps around the float
+</div>
+ +

CSS:

+ +
+.parentElement {
+    width: 100%;
+    overflow: hidden;
+}
+.childElement {
+    float: left;
+}
+ +

+ 有本项目的贡献者提醒我注意最新的clearfix。 micro clear-fix + 被认为相当稳定且跨浏览器兼容,足以列入最新的HTML5 boilerplate发布了。 我 强烈 建议你去看看。虽然我不是浏览器特定CSS和 :after 这种伪元素的狂热粉丝,不过这个micro clearfix的确更健壮。它还能避免顶层margin缩回的问题。 +

+ +
+ + + + + + + + + +
+

垂直和水平居中

+ +

+ 水平居中元素实际上不是什么高精尖的科技,我敢肯定你们大部分人都熟悉下面的代码片段: +

+ +
+.class {
+    width: 960px;
+    margin: 0 auto;
+}
+ +

+ 前端开发者们使用这种代码很长时间了,也没搞明白为什么这种方式对垂直居中不起作用。从我的理解说,很重要的一点是记住父元素一般会有个 height: auto; 样式, 也没有垂直居中元素所需的100%高度。而应用 position: absolute; 能有效地把元素转移到定位模式,然后被设为auto的margin会自动帮助它调整位置,达到居中效果。 +

+ +
+.exactMiddle {
+    width: 100px;
+    height: 100px;
+    position: absolute;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    margin: auto;
+}
+ +

+ 这种方法的不足之处包括在 IE6 和 IE7 中缺乏支持,以及当浏览器被缩小到比居中对象还小时不出现滚动条。 在 这个网页 里列出了更多的方法(现在这个是第4个),不过现在这个目前是最优方法。 +

+ +

+ 在一个元素里垂直居中文字也是很直接的。如果文字是单行的,例如一个水平导航元素,你可以设置 line-height 为该元素的物理高度。 +

+ +
+#horizNav li {
+    height: 32px;
+    line-height: 32px;
+}
+ +
+ + + + + + + +
+

检测特性,而不是检测浏览器类型

+ +

+ 在前面关于Javascript特性检测的讨论中,检测到浏览器是 任何版本 的 IE 然后就运用某些属性的做法越来越成问题了。铁人 Paul Irish 引领了使用 IE 版本检测 方法来解决这些问题的大潮,但是 Modernizr 从那时起拯救了我们。 + Modernizr 在 root <html> 元素里放入一些class,描述某些特性是否得到支持. 然后前沿的样式就可以很容易从这些class级联出来或者删除掉。 +

+ +
+.my_elem {
+   -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.25);
+   -moz-box-shadow: 0 1px 2px rgba(0,0,0,0.25);
+   box-shadow: 0 1px 2px rgba(0,0,0,0.25);
+}
+
+/* 如果 box shadow 不支持, 就应用 borders 属性 */
+.no-boxshadow .my_elem {
+   border: 1px solid #666;
+   border-bottom-width: 2px;
+}
+ +
+ + + + + + + +
+

!important 不要乱用

+ +

+ 依赖于 !important 标签是个危险的现象。非用它不可的情况屈指可数,而且是特殊情况。这些情况大抵是需要覆盖另外一套样式表,而你没法或者没权限编辑它。另一个场景是对元素的样式硬编码以防止Javascript在线产生的样式有更大优先级。而实际情况是 !important 往往被用做偷懒的快捷方式,让某个样式优先于其他的样式,这样做将来会产生很多问题。 +

+ +

+ 对 !important 标签的大部分使用是可以避免的,只要更好地理解CSS选择器优先级以及如何更准确地定位元素。选择器越具体,被接受为适用样式的可能性就越大。下面来自 vanseodesign 的例子展示了具体化起作用的情况。 +

+ +
+p { font-size: 12px; }
+p.bio { font-size: 14px; }
+ +

+ 关于样式优先级, 他们的文章 在解释继承性方面比我能写出来的文章都好,所以请给它点个赞吧。 +

+ +
+ + + + + + + +
+

进取性向下兼容

+ +

+ 值得注意的是,这段是我的个人观点,只适用于特定情况。在依赖老版本浏览器的大型商业项目或企业级解决方案中,进取性向下兼容的立场将不容易被接受。 +

+ +

+ 进取性向下兼容的意思是如果某个特定的(老版本)浏览器无法渲染某个特定效果,则应直接忽略它。CSS3 按钮就是一个好例子。诸如 border-radiusbox-shadowtext-shadowgradients 这些效果会在先进的浏览器里显示出来。对于版本稍微老一点的浏览器,可以用一个 .PNG 图片作为无伤大雅的补救办法,而所有解决办法中最优雅的应该是针对IE6提供一个PNG-Fix,或者用filter 参数来代替 gradients 和 shadows等属性。 + 不过,在这种情况下,进取性向下兼容方式会让你忽略老版本浏览器,而在其中展示一个平面的还过得去的对象。 +

+ +

+ 简单地说,进取性向下兼容说白了就是:如果你的浏览器渲染不了渐变色或盒子阴影,那是你运气不好。 +

+ +

+ 虽然这不是对所有情况都理想,这种方法能确保项目按时交付,且核心产品是可用的,而不需依赖于对浏览器的破解办法。 +

+ +
+ + + + + + + + + + + +

CSS3及HTML5

+ + + + + + + +
+

用Modernizr进行特性检测

+ +

+ 这个话题我想我已经说的够多了。用 Modernizr 来检测特定的 HTML5 和 CSS3 特性是否可用。 +

+ +
+ + + + + + + +
+

@font-face的使用和滥用

+ +

+ 在你考虑嵌入一套定制的字体之前,很重要的一点是你要查看 EULA 并检查是否允许web嵌入。 字体库厂商自然是不愿意让设计师和开发者有能力把字体库文件直接存放在服务器上,然后被熟练的终端用户拷贝走。某些厂商也禁止嵌入特定的文件类型,例如 .TTF.OTF。 +

+ +

+ 如果,经过慎重考虑,你相信想要的字体是可嵌入的,那就去看一下Font Squirrel的 @font-face 生成器。 + 它利用了 Fontspring的 防弹 @font-face 结构 并能自动生成所有需要的文件格式。 +

+ +
+ + + + + + + +
+

向下兼容

+ +

+ 谢天谢地,浏览器对于它不支持的HTML5 和 CSS3 特性的处理已经达到了优雅的本色。加到 <input /> 标签的新类型例如 "email", "search" 等等在浏览器本地不支持的情况下一般会向下兼容为正常的 <input type="text" />。 类似的,不支持的CSS3 特性就不会出现,由高度和宽度媒体查询控制的响应式布局也不会被应用。 +

+ +

+ + 精巧的CSS3效果应该被应用为对使用现代浏览器的用户的一种奖励。 + +

+ +

+ 在下面的“资源”小节里包括了一些有助于让HTML5和CSS3功能在一批老版本浏览器中保持正常的库。 +

+ +
+ + + + + + + +

资源

+ + + + + + + +
+

有用的资源

+ +

+ 下列的资源对于代码标准化和现代web页面的互动是很重要的。它们确保了CSS3 和 HTML5 特性在以前缺乏支持能力的一批浏览器中能够使用。 +

+ + + +
+ + + + + + + + + + + +
+

支持和建议

+ +

+ 本文由 Tait Brown 编写 (@taitems),此中文版由 coderLMN 翻译。 +

+ +

+ 有关的问题、勘误和建议可以提交到本页面所在的 GitHub repository。你也可以fork你自己的分支,再加上你自己公司或产品特定的准则。 +

+ +
+ + + + + + + + + + + + + + + + +
+ + + + + + + + + + + +