Original English text: The seven rules of Unobtrusive JavaScript
Original address: http://icant.co.uk/articles/seven-rules-of-unobtrusive-javascript/
Original author: Chris Heilmann
Translation address: http://www.zhuoqun.net/html/y2008/1103.html
Written in front: When kejun was training us JavaScript some time ago, he recommended many classic articles on the slides, including this one One article. I feel very good after reading it, but I often don’t understand the articles deeply. It just so happened that I didn’t find a Chinese version of this article, so I came up with the idea of translating it, so that I can share it and deepen my understanding. The author of this article, Chris Heilmann, is a British engineer at Yahoo! (a "godfather" figure according to Kejun), and the translation of this article was also approved by him.
One more thing here, I have translated a lot of things before, but at that time I was translating more for the sake of translation, and I didn’t understand many technical articles, so I am still a translator until now. I will continue to translate some articles in the future, but I should only translate classic articles that need to be understood carefully. If you have time, you should still write more code. Practice is the way to go.
Translation of terminology: Regarding the term "Unobtrusive JavaScript", I can't think of a particularly appropriate translation. After searching on the Internet, I found that some were translated as "low-key JavaScript", some were translated as "non-intrusive JavaScript", and some in Taiwan were translated as "no intrusive JavaScript"... After many researches, I decided to use "no intrusive JavaScript". This translation is "obtrusive JavaScript" (although this still doesn't quite suit me), please see this article for details. In fact, "Unobtrusive JavaScript" contains many meanings, and it is difficult to summarize it in one word. If you are interested, you can take a look at the explanation of "Unobtrusive JavaScript" on Wikipedia. In addition, I think translation is to express the author's meaning, and it does not necessarily have to be translated word for word. Therefore, in order to facilitate readers' understanding, I have deleted some and added some in the article, but these are without damaging the meaning of the original text. carried out on a basis.
There is another point to note, that is, my translation skills are very amateur, so there are bound to be mistakes in the translation, so please correct me.
After years of developing, teaching, and writing unobtrusive JavaScript, I've discovered the following guidelines. I hope they help you understand a little bit about why it's better to design and execute JavaScript this way. These rules have helped me deliver products faster, with higher quality and easier to maintain.
1. Don’t make any assumptions (JavaScript is an unreliable assistant)
Perhaps the most important characteristic of unobtrusive JavaScript is that you have to stop making any assumptions:
don’t assume that JavaScript is available, you better think that it is possible is unavailable rather than relying on it directly.
Don't assume that the browser supports methods and properties until you've tested them and confirmed that they work.
Don't assume that the HTML code is as correct as you think, check it every time, and do nothing if it's not available.
Make JavaScript function independent of input devices Remember that other scripts may affect the functionality of your JavaScript, so make sure your scripts are scoped as safely as possible.
Before starting to design your script, the first thing to consider is to check the HTML code for which you are going to script and see if there is anything that can help you achieve your goal.
2. Find hooks and node relationships (HTML is the cornerstone of scripting)
Before you start writing scripts, take a look at the HTML you want to write JavaScript for. If the HTML is unorganized or unknown, it's almost impossible to have a good scripting solution - it's likely that you'll either create too much markup in JavaScript, or the application will be too dependent on JavaScript.
There are some things to consider in HTML, and that's hooks and node relationships.
<1>.HTML hook
The original and most important hook of HTML is ID, and ID can be accessed through the fastest DOM method-getElementById. If all IDs in a valid HTML document are unique (there is a bug in IE regarding name and ID, but some good libraries solve this problem), using IDs is safe, reliable, and easy to test.
Some other hooks are HTML elements and CSS classes. HTML elements can be accessed through the getElementsByTagName method, but CSS classes cannot be accessed through native DOM methods in most browsers. However, there are many external class libraries that provide methods that can access CSS class names (similar to getElementsByClassName).
<2>.HTML Node Relationship
Another interesting point about HTML is the relationship between tags. Think about the following question:
How can we reach the target node most easily and with the least amount of DOM traversal?
By modifying what mark, we can access as many child nodes as possible that need to be modified?
What attributes or information does a given element have that can be used to reach another element?
Traversing the DOM is resource-intensive and slow, which is why you should try to use techniques already used in browsers to do it.
3. Leave the traversal to the experts (CSS, traverse the DOM faster).
Scripting the DOM and using methods or properties (getElementsByTagName, nextSibling, previousSibling, parentNode and others) to traverse the DOM seems to confuse many people. This is very confusing. interesting. The interesting thing is that we have already done these things through another technology-CSS.
CSS is a technique that uses CSS selectors to access target elements and change their visual properties by traversing the DOM. A complex piece of JavaScript using the DOM can be replaced with a CSS selector:
var n = document.getElementById('nav');
if(n){
var as = n.getElementsByTagName('a');
if(as.length > 0){
for(var i=0;as[i];i++){
as[i].style.color = '#369′;
as[i].style.textDecoration = 'none';
}
}
}
/* The following code has the same function as the above*/
#nav a{
color:#369;
text-decoration:none;
}
This is a very powerful technique that can be put to good use. You can achieve this by dynamically adding classes to high-level elements in the DOM or changing the element ID. If you use the DOM to add a CSS class to the document's body, designers can easily define static and dynamic versions of the document.
JavaScript:
var dynamicClass = 'js';
var b = document.body;
b.className = b.className ? b.className + 'js' : 'js';
CSS:
/* Static version*/
#nav {
....
}
/* Dynamic version*/
body.js #nav {
....
}
4. Understand browsers and users (and build what you need based on existing usage patterns)
A big part of unobtrusive JavaScript is understanding how browsers work (especially how browsers crash) and What users expect. Regardless of the browser you can easily create a completely different interface using JavaScript. Drag-and-drop interfaces, folding areas, scroll bars and sliders can all be created using JavaScript, but this problem is not a simple technical issue. You need to think about the following questions:
Can this new interface be independent of input devices? If not, what can you rely on?
Does the new interface I create follow the guidelines of a browser or other rich interface (can you switch between multi-level menus directly with the mouse? Or do you need to use the tab key?)
What functionality do I need to provide that relies on JavaScript?
The last question isn't really a problem because you can use the DOM to create HTML out of thin air if needed. An example of this is a "print" link. Since browsers do not provide a non-JavaScript functionality to print a document, you need to use the DOM to create such links. The same is true for a clickable title bar that implements expand and collapse content modules. The title bar cannot be activated by the keyboard, but the links can. So in order to create a clickable title bar you need to add the link using JavaScript, and then all users with a keyboard can collapse and expand the content module.
An excellent resource for solving this type of problem is the Design Pattern Library. As for knowing which things in the browser are independent of input devices, it depends on the accumulation of experience. The first thing you need to understand is the event handling mechanism.
5. Understand events (event handling causes change)
Event handling is the second step towards unobtrusive JavaScript. The point is not to make everything draggable, clickable, or add inline handling to them, but to understand that event handling is something that can be completely separated. We've separated HTML, CSS, and JavaScript, but we haven't gone very far in separating event handling.
The event handler will monitor changes that occur on the elements in the document. If an event occurs, the handler will find a wonderful object (usually a parameter named e). This object will tell the element what happened and What can be done with it.
What's really interesting about most event handling is that it doesn't just happen on the element you want to access, but on all elements higher up in the DOM (but not all events are like this, focus and The exception is the blur event). For example, you can use this feature to add only one event handler to a navigation list, and use the event handler method to get the element that actually triggered the event. This technique is called event delegation, and it has several advantages:
you only need to check whether an element exists, instead of checking each element. You can dynamically add or remove child nodes without removing the corresponding event handler. You can Another thing to remember when responding to the same event on different elements is that you can stop the event from propagating to the parent element and you can override the default behavior of HTML elements (such as links) . However, sometimes this is not a good idea because browsers give HTML elements the behaviors they do for a reason. For example, links might point to a target within the page, and leaving them unmodified ensures that the user can also bookmark the page's current script state.
6. Think of others (namespaces, scopes and schemas)
Your code will almost never be the only script code in a document. So it is particularly important to ensure that there are no global functions or global variables in your code that other scripts can override. There are several patterns available to avoid this problem, the most basic of which is to use the var keyword to initialize all variables. Suppose we write the following script:
var nav = document.getElementById('nav');
function init(){
// do stuff
}
function show(){
// do stuff
}
function reset(){
// do stuff
}
The above code contains a global variable called nav and three functions named init, show and reset. These functions can access the nav variable and can access each other through the function name:
var nav = document.getElementById('nav');
function init(){
show();
if(nav.className === 'show'){
reset();
}
// do stuff
}
function show(){
var c = nav.className;
// do stuff
}
function reset(){
// do stuff
}
You can avoid the above global coding by encapsulating the code into an object, so that functions can be turned into methods in the object, and global variables can be turned into properties in the object. You need to use the "name + colon" method to define methods and properties, and you need to add a comma as a separator after each property or method.
var myScript = {
nav:document.getElementById('nav'),
init:function(){
// do stuff
},
show:function(){
// do stuff
},
reset:function(){
// do stuff
}
}
All methods and properties can be accessed externally and internally using the "class name + dot operator".
var myScript = {
nav:document.getElementById('nav'),
init:function(){
myScript.show();
if(myScript.nav.className === 'show'){
myScript.reset();
}
// do stuff
},
show:function(){
var c = myScript.nav.className;
// do stuff
},
reset:function(){
// do stuff
}
}
The disadvantage of this model is that every time you access other methods or properties from a method, you must add the name of the object in front, and everything in the object can be accessed from the outside. If you just want some code to be accessible to other scripts in the document, consider the following module pattern:
var myScript = function(){
//These are private methods and properties
var nav = document.getElementById('nav');
function init(){
// do stuff
}
function show(){
// do stuff
}
function reset(){
// do stuff
}
//Public methods and properties are wrapped in the return statement using object syntax
return {
public:function(){
},
foo:'bar'
}
}();
You can access the returned public properties and methods in the same way as the previous code, in this case: myScript.public() and myScript.foo. But there is another uncomfortable point here: when you want to access a public method from the outside or from a private method inside, you still have to write a lengthy name (the name of the object can be very long). To avoid this, you need to define them as private and only return an alias in the return statement:
var myScript = function(){
// These are private methods and properties
var nav = document.getElementById('nav');
function init(){
// do stuff
}
function show(){
// do stuff
// do stuff
}
function reset(){
// do stuff
}
var foo = 'bar';
function public(){
}
//Return only pointers to those private methods and properties you want to access
return {
public:public,
foo:foo
}
}();
This ensures consistent code style and allows you to use shorter aliases to access methods or properties.
If you don't want to expose any methods or properties to the outside world, you can encapsulate all the code into an anonymous method and execute it immediately after its definition:
(function(){
// these are all private methods and properties
var nav = document.getElementById('nav');
function init(){
// do stuff
show(); // No class name prefix is required here
}
function show(){
// do stuff
}
function reset(){
// do stuff
}
})();
This pattern is great for code modules that are executed only once and have no dependencies on other functions.
By following the rules above, your code will work better for users, and your code will run better on machines and get along better with other developers' code. However, there is one group that needs to be taken into consideration.
7. Consider the developers who will take over your code (makes maintenance easier)
The final step in making your script truly unobtrusive is to double-check it after you write it, and take care of the developers who will take over your code once the script goes live. . Consider the following questions:
Are all variable and function names sensible and easy to understand?
Is the code properly organized? Does it flow smoothly from start to finish?
Are all dependencies obvious?
Have comments been added where possible that might cause confusion?
The most important thing to note is: realize that the HTML and CSS code in the document are more likely to be changed than the JavaScript (because they are responsible for the visual effect). So don't include any classes and IDs that can be seen by end users in the script code, but separate them into an object that holds configuration information.
myscript = function(){
var config = {
navigationID:'nav',
visibleClass:'show'
};
var nav = document.getElementById(config.navigationID);
function init(){
show();
if(nav.className === config.visibleClass){
reset();
};
// do stuff
};
function show(){
var c = nav.className;
// do stuff
};
function reset(){
// do stuff
};
}();
This way the maintainer knows where to modify these properties without having to change other code.
More information
So those are the seven guidelines I discovered. If you want to learn more about the topics discussed above, check out the following links: