JavaScript/Best practices
This chapter will describe current JavaScript community standards and best practices that every programmer should know.
document.write
editThis is deprecated. Use innerHTML
or DOM manipulation methods instead.
In XHTML, document.write
does not work, but you can achieve the same effects with DOM manipulation methods[1].
JavaScript protocol
editTry to avoid links that exist solely for executing JavaScript code.
<a href="javascript:resumeFancyVersion()">See my résumé!</a>
Instead consider:
<a href="resumePlainVersion.html" onclick="return !resumeFancyVersion()">See my résumé!</a>
Users with JavaScript enabled will be provided with the JavaScript-embellished version of content (perhaps using DHTML), whereas those without will be redirected to a XHTML page that provides it statically. This is more accessible than having an <a> element lacking a normal href attribute value. First, it uses proper language semantics. Second, it provides access to your content for users without JavaScript. Third, it detects whether the function execution succeeded and redirects JS-enabled readers too in case of a failure.
The onclick expression evaluates to a Boolean value. If the function succeeds to perform the desired effect and returns true, the onclick will return a failure and hyperlink not execute. When the function fails for whatever reason, the false, null or undefined value will evaluate to true and not prevent the link from being executed. Alternatively, if you do not wish to provide a static equivalent, you may embed the onclick attribute within an element with less demanding semantics:
<strong onclick="resumeFancyVersion()">See my résumé!</strong>
Thus no user agent will be confused upon reading a reduced <a> element.
Email validation
editMany people use JavaScript functions to immediately catch the most common sorts of errors in form entry. For example, some web forms ask people to enter the same thing twice. Other web forms ask people to enter an email address. Then they do a quick check to see if what was entered looks at least vaguely like an email address:
function isValidEmail(string) {
// These comments use the following terms from RFC2822:
// local-part, domain, domain-literal and dot-atom.
// Does the address contain a local-part followed an @ followed by a domain?
// Note the use of lastIndexOf to find the last @ in the address
// since a valid email address may have a quoted @ in the local-part.
// Does the domain name have at least two parts, i.e. at least one dot,
// after the @? If not, is it a domain-literal?
// This will accept some invalid email addresses
// BUT it doesn't reject valid ones.
var atSym = string.lastIndexOf("@");
if (atSym < 1) { return false; } // no local-part
if (atSym == string.length - 1) { return false; } // no domain
if (atSym > 64) { return false; } // there may only be 64 octets in the local-part
if (string.length - atSym > 255) { return false; } // there may only be 255 octets in the domain
// Is the domain plausible?
var lastDot = string.lastIndexOf(".");
// Check if it is a dot-atom such as example.com
if (lastDot > atSym + 1 && lastDot < string.length - 1) { return true; }
// Check if could be a domain-literal.
if (string.charAt(atSym + 1) == '[' && string.charAt(string.length - 1) == ']') { return true; }
return false;
}
Unfortunately, some other "email validation" JavaScript functions reject perfectly valid email addresses. For example, some incorrectly reject valid addresses that include "+" signs.
The original email address syntax (RFC 821) did allow "+" signs. RFC 822, published in the same month (August 1982), also allowed them. The current version of the syntax is given in RFC2821/RFC2822. Section 3 of RFC3696 gives useful examples of unusual valid email addresses.
After validation, many JavaScript programmers encode the email address
using encodeURIComponent()
to work-around certain client-side languages that can't seem to handle plus signs properly.[1][2]
The complexity of the quoting rules used for email addresses makes it impractical to test the local-part of an address or a domain literal completely. Given that there isn't necessarily a real mailbox corresponding to a valid local-part how much extra download time is worth spending on a complex validation script?
Examples valid according to RFC2822
editme@example.com
a.nonymous@example.com
name+tag@example.com
a.name+tag@example.com
me.example@com
"spaces must be quoted"@example.com
!#$%&'*+-/=.?^_`{|}~@[1.0.0.127]
!#$%&'*+-/=.?^_`{|}~@[IPv6:0123:4567:89AB:CDEF:0123:4567:89AB:CDEF]
me(this is a comment)@example.com
– comments are discouraged but not prohibited by RFC2822.
Examples invalid according to RFC2822s
editme@
@example.com
me.@example.com
.me@example.com
me@example..com
me\@example.com
spaces\ must\ be\ within\ quotes\ even\ when\ escaped@example.com
a\@mustbeinquotes@example.com
Test page
editCOMMENT: This code has been incorrectly designed to reject certain emails that are actually valid. If changes to valid and invalid emails are accepted, the following code should also be reviewed.
The following test page can be used to test an email validation function. Save the three files in the same directory and then open validEmail.htm in a web browser.
validEmail.js
function isValidEmail(string) {
// These comments use the following terms from RFC2822:
// local-part, domain, domain-literal and dot-atom.
// Does the address contain a local-part followed an @ followed by a domain?
// Note the use of lastIndexOf to find the last @ in the address
// since a valid email address may have a quoted @ in the local-part.
// Does the domain name have at least two parts, i.e. at least one dot,
// after the @? If not, is it a domain-literal?
// This will accept some invalid email addresses
// BUT it doesn't reject valid ones.
var atSym = string.lastIndexOf("@");
if (atSym < 1) { return false; } // no local-part
if (atSym == string.length - 1) { return false; } // no domain
if (atSym > 64) { return false; } // there may only be 64 octets in the local-part
if (string.length - atSym > 255) { return false; } // there may only be 255 octets in the domain
// Is the domain plausible?
var lastDot = string.lastIndexOf(".");
// Check if it is a dot-atom such as example.com
if (lastDot > atSym + 1 && lastDot < string.length - 1) { return true; }
// Check if could be a domain-literal.
if (string.charAt(atSym + 1) == '[' && string.charAt(string.length - 1) == ']') { return true; }
return false;
}
function testIsValidEmail(string) {
alert("'" + string + "' is " + (isValidEmail(string) ? "" : "NOT ") + " a valid email address.");
}
function checkSamples() {
var validAddress = [
'me@example.com',
'a.nonymous@example.com',
'name+tag@example.com',
'name\\@tag@example.com',
'spaces\\ are\\ allowed@example.com',
'"spaces may be quoted"@example.com',
'!#$%&\'*+-/=.?^_`{|}~@[1.0.0.127]',
'!#$%&\'*+-/=.?^_`{|}~@[IPv6:0123:4567:89AB:CDEF:0123:4567:89AB:CDEF]',
'me(this is a comment)@example.com'
];
var invalidAddress = [
'me@',
'@example.com',
'me.@example.com',
'.me@example.com',
'me@example..com',
'me.example@com',
'me\\@example.com'
];
var results = new StringBuffer();
var handlesValidAddressesCorrectly = true;
results.append('<table border="1">');
results.append('<caption>Does the function accept all the valid sample addresses?</caption>');
results.append('<tr><th>Valid address</th><th>Function returns</th></tr>');
for (var i = 0; i < validAddress.length; i++) {
results.append('<tr><td>');
results.append(validAddress[i]);
results.append('</td><td>');
if (isValidEmail(validAddress[i])) {
results.append('<span class="good">true</span>');
} else {
handlesValidAddressesCorrectly = false;
results.append('<strong class="fail">false</strong>');
}
results.append('</td></tr>');
}
results.append('</table>');
var handlesInvalidAddressesCorrectly = true;
results.append('<table border="1">');
results.append('<caption>Does the function reject all the invalid sample addresses?</caption>');
results.append('<tr><th>Valid address</th><th>Function returns</th></tr>');
for (var i = 0; i < invalidAddress.length; i++) {
results.append('<tr><td>');
results.append(invalidAddress[i]);
results.append('</td><td>');
if (!isValidEmail(invalidAddress[i])) {
results.append('<span class="good">false</span>');
} else {
handlesInvalidAddressesCorrectly = false;
results.append('<em class="warning">true</em>');
}
results.append('</td></tr>');
}
results.append('</table>');
var conclusion;
if (handlesValidAddressesCorrectly) {
if (handlesInvalidAddressesCorrectly) {
conclusion = '<p><strong class="good">The function works correctly with all the sample addresses.</strong></p>';
} else {
conclusion = '<p><em class="warning">The function incorrectly accepts some invalid addresses.</em></p>';
}
} else {
conclusion = '<p><strong class="fail">The function incorrectly rejects some valid addresses.</strong></p>';
}
document.getElementById('testResults').innerHTML = conclusion + results.toString();
}
function StringBuffer() {
this.buffer = "";
}
StringBuffer.prototype.append = function(string) {
this.buffer += string;
return this;
};
StringBuffer.prototype.toString = function() {
return this.buffer;
};
validEmail.css
body {
background-color: #fff;
color: #000
}
table {
margin-bottom: 2em
}
caption {
margin-top: 2em
}
.good {
background-color: #0f0;
color: #000
}
.warning {
background-color: #fc9;
color: #000
}
.fail {
background-color: #f00;
color: #fff
}
validEmail.htm
<!DOCTYPE html>
<html lang="en">
<head>
<title>Valid email test</title>
<link rel="stylesheet" href="validEmail.css">
<script src="validEmail.js"></script>
</head>
<body onload="checkSamples()">
<h1>Unit test for email address validation functions</h1>
<h2>Interactive test</h2>
<form action="#">
<fieldset>
<legend>Email address</legend>
<label for="emailAddress">Email</label>
<input type="text" size="40" value="" name="email" id="emailAddress">
<input type="button" name="validate" value="Check address"
onclick="testIsValidEmail(this.form.email.value)">
</fieldset>
</form>
<h2>Selected samples</h2>
<p>This section shows the results of testing the function with sample strings.
The sample includes both valid strings and invalid strings
according to <a href="http://www.faqs.org/rfcs/rfc2822.html">RFC2822</a>.
</p>
<div id="testResults">You need to enable JavaScript for this unit test to work.</div>
</body>
</html>
use strict
editMany JavaScript programmers recommend enabling ECMAScript 5's strict mode by putting the exact statement "use strict";
before any other statements:[3][4][5]
"use strict";
function …
For further reading
editJavaScript best practices:
- Coding Cookbook/Validate Email Address
- "Six JavaScript features we do not need any longer" by Christian Heilmann.
- source code for Apple's recommended JavaScript validation functions: checkEmail(), etc.
- JavaScript form validation - doing it right
- "FORM submission and the ENTER key?" discusses forms that submit when you press Enter; forms that don't submit when you press Enter; and how to make a form work the other way.
- "JavaScript Best Practices" by Matt Kruse
- "Comparing E-mail Address Validating Regular Expressions" has a list of valid and invalid email addresses and a variety of regular expressions and how well each one works on that list.
Best practices in other languages
editReferences
edit- ↑ Jan Wolter. "JavaScript Madness: Query String Parsing" 2011.
- ↑ PHP bug #39078: Plus sign in URL arg received as space.
- ↑ Nicholas C. Zakas. "It’s time to start using JavaScript strict mode". 2012.
- ↑ "What does “use strict” do in JavaScript, and what is the reasoning behind it?"
- ↑ Mozilla Developer Network: "Strict mode".