本文主要收集平常使用到的一些JavaScript Codes和Snippets。
classNameid属性classNamefunction foo() {
  var id = 0;
  return function () {
    return id++;
  }
}
var makeid = foo();
var i = makeid();
var j = makeid();在函数foo中,id实际上是私有成员,我们通过且仅能通过闭包函数makeid()访问它。
私有成员:私有Private成员要由构造器生成。构造器中的普通的var变量和参数都成为私有成员。
function Container(param) {
  this.member = param;
  var secret = 3;
  var self = this;
}这个构造器有三个私有实例变量:param, secret, 和self,它们被附加到了对象上,但它们无法从外部访问,同时它们也无法被这个对象的公共方法所访问。他们只对私有方法和特权方法可见。私有方法则是构造器内部的函数,而特权方法是用this在构造器中分配的。
如果想了解更多关于私有成员的实现请参考Private properties in JavaScript.
function foo() {
  var req = new XMLHttpRequest();
  req.onreadystatechange = function () { /*此处创建闭包*/
    if (req.readyState == 4) {
      ...
    }
  }
}尽管onreadystatechange是req的属性或是方法,但是它并不是由req触发的。因为它需要访问它自己,所以解决方案可以是使用一个
全局变量(如window.req)或是使用闭包让这些变量的值始终保存在内存中,让它不至于在foo()函数执行完毕就被GC回收。
getClickHandler: function () {
  var me = this;
  return function(e) {
    me.showTip();
    ...
  }
}以上编程模式,是经常会使用到的其中一种闭包写法。
闭包是指在建立函数时,绑定了当时作用域下的有效的自由变量。基于此,首先我们给闭包下个定义:
闭包就是能够读取其他函数内部变量的函数。
闭包最大用处有两个:
那么什么是自由变量呢?
自由变量是相对于函数而言,既不是本地变量也不是参数的变量,它的作用范围基本上是在函数定义的范围中。举个例子:
function Robot() {
  var createTime = new Date();
  this.getAge = function () { // 这里建立了Closure
    var now = new Date();
    var age = now - createTime;
    return age;
  }
}
var robot = new Robot();
alert(robot.getAge());单单看getAge函数的话,createTime并没有多大的意义,对getAge而言,createTime是一个自由变量,同时它也是Robot函数的本地私有成员变量,但是在getAge所引用的函数中被关闭了,从而createTime的生命周期得以延续,即使它是使用var声明的,只要getAge中所引用的Function对象还存在,createTime自由变量也就能继续存活。
关于闭包,Crockford曾经说过:
一个内部的函数总是可以访问这个函数外部的变量和参数,甚至在外部的函数返回之后。
var myAlerts = [];
for (var i = 0; i < 2; i++) {
  myAlerts.push(
    function inner() {
      alert(i);
    });
}
myAlerts[0](); // 2
myAlerts[1](); // 2第一眼看过去,JavaScript新手会认为alert(i)会弹出inner函数定义时逐步增长的i值,也就是分别显示0,1。这是我们最常犯的一种错误。inner函数是在全局上下文中创建的,从而它的scope chain是静态地绑定到全局上下的。当调用inner函数时,它会去找寻标识符i,而标识符的搜寻是沿着scope chain来的。所以它会去inner的scope chain上搜救变量i,但是,在调用inner函数的时候,变量i的值已经变为2,所以每次调用inner函数的结果都会是一样的。
JavaScript的一个很重要的特性就是它的解析器使用的是Lexical Scoping,意味着所以内部函数都是静态地绑定到创建它们的父上下文中的。闭包只能取得包含函数中任何变量的最后一个值。也就是说,闭包所保存的是整个变量对象,而不是某个特殊的变量。
上面的例子中,所有的myAlerts中保存的函数中能会引用到2,也就是被关闭的变量i的最后一个值(循环的计数器i在循环中的内部函数中被关闭了,它的生命周期得以延续,即使它是使用var声明的,只要myAlerts中所引用的Function对象还存在,i也就能继续存活)。
但是我们通过使用闭包保存变量所在的作用域解决。
var myAlerts = [];
for (var i = 0; i < 2; i++) {
  myAlerts.push(
    (function inner(i) {
      return function () {
        alert(i);
      }
    })(i));
}
myAlerts[0](); // 0
myAlerts[1](); // 1var obj = new function() {
  /* stuff */
};
  var obj = {
  /* stuff */
};Object.create是ECMAScript 5引入的新方法,它是new的一个变体,它可以让你基于一个原型对象来创建一个新的对象。
var prototypeDef = {
  protoBar: "protoBar",
  protoLog: function () {
    console.log(this.protoBar);
  }
};
var propertiesDef = {
  instanceBar: {
    value: "instanceBar"
  },
  instanceLog: {
    value: function () {
      console.log(this.instanceBar);
    }
  }
}
var foo = Object.create(prototypeDef, propertiesDef);
foo.protoLog(); // logs "protoBar" 
foo.instanceLog(); //logs "instanceBar"这里有几点需要注意:
Object.creaet中第二个参数properties中的值会覆盖第一个参数prototype中的同名属性Object.creaet中第二个参数properties的类型是数组或是对象,那么通过Object.create创建的对象都会共享它isPrototypeOf来检查对象的prototype对象而不是instanceOf我们可以解决第二个问题,首先初始化propertyArrayValue_为null,然后当你添加元素时才初始化该数组。
var prototypeDef = {
  protoArray: [],
};
var propertiesDef = {
  propertyArrayValue_: {
    value: null,
    writable: true
  },
  propertyArray: {
    get: function () {
      if (!this.propertyArrayValue_) {
        this.propertyArrayValue_ = [];
      }
      return this.propertyArrayValue_;
    }
  }
};
var foo = Object.create(prototypeDef, propertiesDef);
var bar = Object.create(prototypeDef, propertiesDef);
foo.protoArray.push("foobar");
console.log(bar.protoArray); //logs ["foobar"] 
foo.propertyArray.push("foobar"); 
console.log(bar.propertyArray); //logs []function MyObj() {}
MyObj.prototype = {
  foo: function () {
      alert(this);
  },
    ...etc..
};
var my1 = new MyObj();
my1.foo();T.dom.toggle = function (element) {
  element = T.dom.g(element);
  element.style.display = element.style.display == "none" ? "" : "none";
  return element;
};使用display:'',与之相对的就是display:block。
设置elem.style.display = ''仅仅设置或是移除了内联样式。如果用户自定义的样式存在并且已经应用到了该元素上,那么一旦内联样式被移除了,该元素仍然会使用自定义样式来渲染。(这是因为elem.style.display已经移除了高优先级的内联样式,但是自定义的样式并没有受到影响,仍然会起作用。)因此,
style.display=...改变目标元素的显示或隐藏状态时,不要同时使用非内联样式和内联样式(只使用内联样式)className样式或是同时使用内联/非内联样式,可以通过className以及display属性来完成elem.style.display = ''这种形式,而使用elem.style.display = 'none'|'block'的这种形式//inline style
elem.style.xxx = ...
//classname
elem.className  = ...修改内联样式或是修改className即可。
classNamevar elem = $('#id');
elem.className = 'foo';使用className,而不是class,那是因为在ECMAScript 5中class是一个Future Reserved Word,而在ECMAScript 6中class已经是一个关键词了。
事实上,并不存在DOM Level 0标准,它只存在于DOM的历史长河中。DOM Level 0通常被认为是Internet Explorer 4.0 and Netscape Navigator 4.0中所支持的
DHTML。
DOM Level 0中的事件处理模型由Netscape Navigator引入,有二种主要的类型:
在内联事件模型中,事件处理器是作为HTML元素的属性添加的。如下所示:
<p>Hey <a href="http://www.example.com" onclick="triggerAlert('Joe'); return false;">Joe!</p>如果想了解更多,请参考这里。
在传统事件模型中,事件处理器是通过JavaScript脚本添加或删除的。与内联模型相同的是,每一个事件一次只能绑定一个事件处理器。
element.onclick = doSomething; //添加事件处理器
element.onclick = null; // 移除事件处理器如果想了解更多,请参考这里。
id属性在使用id属性时,文档中所有的元素必须都有唯一的id。对于多个元素使用相同的id,在IE中会导致各种种样的问题。
classNamevar table = document.getElementById('myTable');
var rows = table.getElementsByTagName('tr');
for (var i = 0; i < rows.length; i++) {
  if (i % 2) {
    rows[i].className += " even";
  }
}在上面的代码中,展示了给一个table添加class以实现斑马线的效果。对于onmouseover/onmouseout的JS事件处理函数也可以通过这种方式添加。
$('foo').related = $('bar');给DOM对象添加指向其它相关联的DOM对象的属性,这种方式不会导致IE中的内存溢出,那是因为所有的操作都是在DOM内完成的。
更多请参考IE的Memory Leak。
首先有个问题:
浏览器是如何渲染一个Web页面的?
从整体上来看,下面是浏览器渲染引擎在取得HTML内容之后的基本流程:
解析HTML以构建DOM树 -> 构建Render树 -> 布局Render树 -> 绘制Render树
Renderer或Render object,而Gecko叫frame),但是Render树并不会包含一些不可见的DOM元素(包含<head>元素或是拥有display:none样式的元素)。每个Renderer都包含与之对应的DOM对象和计算完成的样式信息(如颜色,大小等等),它们将按照正确的顺序显示到屏幕上Renderer,还需要计算它在屏幕上的确切坐标,这一步叫做LayoutPainting,即遍历Render树,并使用UI后端层在浏览器窗口中绘制每个节点修改元素样式(如background-color, border-color, visibility)时,如果并不影响元素在页面中的位置,那么浏览器只是会使用新的样式信息重绘该元素。
除了重绘外,另外一些改变会影响文档的内容,结构或是元素位置,那么就会发生重绘(Webkit叫Layout,Gecko叫Reflow)。通常如下的一些修改会触发重新定位或回流:
display:none),移动元素或是修改元素出现顺序:hover, :checked, :first-child等等看一个简单来自Google Speedtracer的例子:
// This line invalidates layout.
elementA.className = 'foo';
// This line requires layout to be up to date.
var aWidth = elementA.offsetWidth;
// Invalidates layout again.
elementB.className = 'bar';
// Requires layout to be up to date
var bWidth = elementB.offsetWidth;通常来说,浏览器会尽可能地把重绘限制在更新的元素的那块区域。比如某个元素的重绘只会影响它后面的元素。 所以,我们可以通过批量读取和批量设置来解决。
// This line invalidates layout.
elementA.className = 'foo';
elementB.className = 'bar';
// This line requires layout to be up to date.
var aWidth = elementA.offsetWidth
// Second Layout pass not needed.
var bWidth = elementB.offsetWidth;参考:
我们可以通过x.style来访问内联样式,如下所示。另外,内联样式会覆盖掉其它的样式,除非使用!important来提高指定样式规则的权重。
element.style.colorcolor 只在下列两种情况下是有效的:
element的内联样式定义上设置的element.style.color = 'red';否则,color 都是无效的。
<a href='javascript:showImg()'>
  <img title='my image' name='sunset'>比如,在一个简单的slide展示中,鼠标滑过一个链接时,需要根据一些信息在图片显示区域展示相应的一张图片。那么img标签的title和name属性就可以帮你很容易的达到目的。 当然,其它一些自定义的属性也可以使用的。
另外一个例子是:
<input type=text pattern='^\w+$' required=true />页面加载时,JS代码可以遍历所有的表单字段,并且如果pattern和required属性存在的话,那么就添加一个validate函数,该函数主要负责在表单提交前,根据正则表达式来验证文本框中的内容。
---| 
-----| 
--------| 
-----------|一个进度条的简单动画实现:我们可以通过逐渐增加div或img的宽度来实现。
function progress() {
  var img = $('img');
  if(img.width < 200) {
    img.width += 5;
    // Don't autosize height along with width
    img.height = 5;
  } else {
    img.width = origwidth;
  }
}
setInterval('progress()', 500);淡入/淡出效果一般用于从页面中添加/删除某个元素时。
function fade(el) {
  var b = 155;
  var el = $('div');
  function f() {
    el.style.background = 'rgb(255,255,' + (b += 4) + ')';
    if (b < 255) {
      setTimeout(f, 40);
    }
  };
  f();
}
fade();更多的可以参考这里。
document.write("< img src='foo.jpg?" + Math.random() + "' />"); 我们可以修改URL,添加了一个伪随机数,这是一种常用的手段,常常用于禁止浏览器缓存图片。
更多的信息请参考quirksmode。
DIV元素节点document.createDocumentFragment():创建一个空的DOM文档碎片。我们可以利用这个API,先创建一个DOM文档碎片,然后在它上面添加新的元素节点,之后将它作为一个整体插入DOM中,以减少Layout/Reflow.
getElementsByTagName() 方法,它将返回包含文档中所有子元素的集合,元素排列的顺序就是它们在文档中的顺序element中带有指定标签名的元素对象的集合我们常用的一些JavaScript库,如jQuery, Prototype等等,已经实现了通过CSS2/CSS3选择器返回元素集合,所以除了这些原生的方法外,我们还可以有更多的选择。
`nodeType’包含一个表示元素类型的数字。我们经常用到的如下所示:
对文本节点来说,nodeValue表示真正的文本。value of text for a Text node (useful for #text). Null for most other nodes (including element nodes) devedge link
<p>I am a JavaScript hacker.</p> var x = [the paragraph];
var text = x.firstChild.nodeValue;而对于属性节点来说,nodeValue则表示的是属性值。
除此之外,对于document和其它元素节点来说,它会返回null。
nodeName是最有用的一个属性。与它的名字一样,它包含了节点的名字。元素节点的名字始终与标签名相同,属性节点的名字始终与属性名字相同,文本节点的名字始终是#text,而文档节点的名字始终是#document。
只是有一点我们需要注意:对于HTML元素节点来说,不管你在HTML中写的是大写或是小写,nodeName都会返回大写的标签名。
返回节点上你需要查询的属性的值。
#### node.setAttribute(name, value)
设置节点上某个指定的属性的新的值。
<img src='sample.png' id='test' />var imgEl = document.getElementById('test'); 
alert(imgEl.getAttribute('src'));
imgEl.setAttribute('src', 'sample.png');var node = ..some_element..
node.parentNode.removeChild(node);var newItem = document.getElementsByTagName('p')[0]; 
var existingItem = document.getElementsByTagName('h1')[0]; 
newItem.parentNode.insertBefore(newItem, existingItem);insertBefore()返回的是被插入节点的一个引用。
var x = y.insertBefore(newItem, existingItem);那么现在x就包含对newItem的一个引用。
var node = ...
var newNode = node.cloneNode(true|false); // true=deep copy, else shallow需要注意的是,克隆节点时,并不会同时克隆事件处理函数。
replaceChild()方法可以允许你将一个节点替换为另外一个节点。如果被插入的节点已经在DOM中了,那么它首先会从当前的DOM中位置移除掉。并且插入的节点和被替换的节点都会保持它们的所有子节点不变。
var newNode = document.getElementsByTagName('h1')[0];
var oldNode = document.getElementsByTagName('p')[1];
newNode.parentNode.replaceChild(newNode, oldNode);replaceChild()返回的是被替换的节点的一个引用。
var x = y.replaceChild(newNode, oldNode);那么现在x就包含对oldNode的一个引用。