DOM操作技术

目录

  • 动态脚本
  • 动态样式
  • 操作表格
  • 使用NodeList

 

动态脚本

使用<script>元素可以向页面中插入JavaScript代码的两种方式:通过src特性包含外部文件;使用这个元素本身来包含代码
动态脚本指的是页面加载时不存在,在将来某一刻需要通过修改DOM动态添加脚本。跟操作HTML元素一样,创建动态脚本也有两种方式:插入外部文件和直接插入JS代码。
(1)插入外部文件

//动态创建<script type="text/javascript" src="test.js"></script>
var script = document.createElement("script");
script.type = "text/javascript";
script.src= "test.js";
document.body.appendChild(script);//这句放哪都可以

在执行最后一行代码把<script>元素添加到页面中之前,是不会下载外部文件的。整个过程可以用函数封装:

function loadScript(url){
   var script = document.createElement('script');
   script.type= "text/javascript";
   script.src = url;
   document.body.appendChild(script);
}

loadScript("test.js");

加载完成后就可以在页面中其他地方使用这个脚本了。怎么知道脚本什么时候加载(从服务器请求下载过来了)完成了呢,有一些事件可以探知,但要取决于浏览器,支持的浏览器少的可怜。
<script>(在<=IE10和Opera)和<link>(仅<=IE10)元素会触发readystatechange事件(继承自HTMLElement.prototype.onreadystatechange),可以用来确定外部JavaScript和Css文件是否已经加载完成。当把动态创建的元素添加到页面中浏览器开始下载外部资源,当元素的readyState属性无论等于“loaded”还是“complete”都表示资源已经可用。下面给出一段加载外部JavaScript文件代码(限在<=IE10且支持addEventListener中运行)。

window.onload = function(e){
  var script = document.createElement('script');
  script.addEventListener('readystatechange', function(e){
     if(e.target.readyState == 'loaded' || e.target.readyState == 'complete'){
         e.target.removeEventListener('readystatechange', arguments.callee, false);
         alert('Script loaded');
     }
  });
  script.src = 'example.js';
  document.body.appendChild(script);
}

此时获取<script src="example.js">的readyState的值为“loaded”,与此同时就可以执行已经加载完的外部文件‘example.js’中的函数了。
(2)行内直接插入JS代码(在除了<=IE8之外浏览器可运行)

var script = document.createElement('script');
script.type = 'text/javascript';
script.appendChild(document.createTextNode("function sayHi(){alert('hi')}"));
document.body.appendChild(script);

因为<=IE8将<script>视为一个特殊的元素,不允许DOM访问其子节点,不过可以使用<script>元素text属性(继承自HTMLScriptElement.prototype)来指定JavaScript代码。

//针对<=IE8和Safari3及之后版本
var script  = document.createElement('script');
script.type = 'text/javascript';
script.text = "function sayHi(){alert('hi')}";
document.body.appendChild(script);

兼容所有浏览器的代码

function loadScriptString(code){
  var script = document.createElement('script');
  script.type = "text/javascript";
  try{
   //除过<=IE8和Safari3及之后版本
   script.appendChild(document.createTextNode(code));
  }catch(e){
    script.text = code;
  }
  document.body.appendChild(script);
}

实际上这样执行代码与在全局作用域中把相同的字符串传递给eval()是一样的。

 

动态样式

能够把CSS样式包含到HTML页面中的元素有<link>和<style>
(1).动态样式是指在页面刚加载时不存在的样式,动态样式是在页面加载完后动态添加到页面中的。

function loadStyles(url){
  var link = document.createElement("link");
  link.rel = "stylesheet";
  link.type = "text/css";
  link.href = url;
  var head = document.getElementsByTagName('head')[0];
  head.appendChild(link);
}

加载外部样式文件的过程是异步的,即加载样式与执行JavaScript代码的过程没有固定次序。在<=IE10且支持addEventListener的IE中可以利用几种事件来检测样式是否加载完成

window.onload = function(e){
   var link = document.createElement('link');
   link.type = "text/css";
   link.rel = "stylesheet";
   link.addEventListener('readystatechange', function(e){
     if(e.target.readyState == 'loaded' || e.target.readyState == 'complete'){
        e.target.removeEventListener('readystatechange', arguments.callee, false);
        alert('css loaded');
     }
  });
  link.href ="example.css";
  document.getElementsByTagName('head')[0].appendChild(link);
}

此时获取<link href="example.css">的readyState的值为“complete”。
(2).使用<style>元素来包含嵌入式CSS

var style = document.createElement('style');
style.type = "text/css";
style.appendChild(document.createTextNode("body{background-color: red}"));
var head = document.getElementsByTagName('head')[0];
head.appendChild(style);

同样<=IE8将<style>视为一个特殊的,与<script>类似的节点不允许访问其子节点。解决这个问题就是访问元素的styleSheet属性(继承自HTMLStyleElement.prototype),注意这个属性只在<=IE10有,其他浏览器(chrome和FF)经测试还没有,这个属性指向一个CSSStyleSheet类型的一个实例。这个实例属性又继承了CSSStyleSheet.prototype的cssText属性,该属性值可以接受CSS代码。下图测试结果为IE10仿真。

兼容所有浏览器代码

function loadStyleString(css){
   var style = document.createElement('style');
   style.type = "text/css";
   try{
      style.appendChild(document.createTextNode(css));
   }catch(e){
      style.styleSheet.cssText = css;
   } 
   var head = document.getElementsByTagName("head")[0];   
   head.appendChild(style);
}

注意经cssText设置之后,再通过style.styleSheet.cssText访问该值时候返回的是大写字符串,如图


操作表格

为方便构建表格,HTML DOM还为<table>,<tbody>,<tr>元素添加了些属性和方法。先来看看它们都有啥方法和属性。
(1).table.__proto__->HTMLTableElement.prototype->HTMLElement.prototype->Element.prototype->Node.prototype->EventTarget.prototype->Object.prototype

  • caption:保存着对<caption>元素(如果有)的指针
  • tBodies:是一个<tbody>元素的HTMLCollection
  • tFoot:保存着对<tfoot>元素(如果有)的指针
  • tHead:保存着对<tHead>元素(如果有)的指针
  • rows:是一个表格中所有行的HTMLCollection
  • createTHead():创建<thead>元素,并自动将其放到表格中,返回该<thead>引用。注意若表格中已经存在thead元素,此时调用createTHead()再创建无效,Chrome不会报错,返回原表格中的那个thead元素,其他浏览器还没测试。但一个表格中是可以存在多个thead元素的(通过HTML代码,Chrome渲染没什么错),当在已存在thead元素的表格中再调用该方法无效,返回原表格中第一个thead元素。
  • createTFoot():创建<tfoot>元素,并自动将其放到表格中,返回该<tfoot>引用。注意问题同上。
  • createCaption():创建<caption>元素,并自动将其放到表格中,返回该<caption>引用。注意问题同上。
  • createTBody():创建<tbody>元素,并自动将其放到表格中,返回该<tbody>引用,可多次创建tbody。
  • deleteTHead():从前往后删除<thead>元素。
  • deleteTFoot():从前往后删除<tfoot>元素
  • deleteCaption():从前往后删除<caotion>元素
  • deleteRow(pos):删除指定位置的行。
  • insertRow(pos):向rows集合中指定位置插入一行,返回新插的那行<tr>元素

(2).tbody.__proto__->HTMLTableSectionElement.prototype->HTMLElement.prototype->Element.prototype->Node.prototype->EventTarget.prototype->Object.prototype

  • rows:保存着<tbody>元素中行的HTMLCollection
  • deleteRow(pos):删除指定位置的行
  • insertRow(pos):向rows集合中的指定位置插入一行,返回被插入新行的引用。

(3).tr.__proto__->HTMLTableRowElement.prototype->HTMLElement.prototype->Element.prototype->Node.prototype->EventTarget.prototype->Object.prototype

  • cells:保存着tr元素中单元格的HTMLCollection
  • deleteCell(pos):删除指定位置单元格
  • insertCell(pos):向cells集合中的指定位置插入一个单元格,返回对新插入单元格的引用。
//创建table
var table = document.createElement('table');
table.border = 1;
table.width = "100%";

//创建tbody
var tbody = document.createElement("tbody");
table.appendChild(tbody);

//创建第一行
tbody.insertRow(0);
tbody.rows[0].insertCell(0);
tbody.rows[0].cells[0].appendChild(document.createTextNode("cell 1,1"));
tbody.rows[0].insertCell(1);
tbody.rows[0].cells[1].appendChild(document.createTextNode("cell 1,2"));

//创建第二行
tbody.insertRow(1);
tbody.rows[1].insertCell(0);
tbody.rows[1].cells[0].appendChild(document.createTextNode("cell 2,1"));
tbody.rows[1].insertCell(1);
tbody.rows[1].cells[1].appendChild(document.createTextNode("cell  2,2"));

//表格添加到文档中
document.body.appendChild(table);

 

使用NodeList

NodeList NamedNodeMap HTMLCollection 这三个集合都是动态的,除了有个例。
NodeList :getElementsByName,childNodes,querySelectorAll(静态集合)等返回的都是NodeList实例
HTMLCollection:getElementsByTagName,getElementsByClassName,getElementsByTagNameNS,document.forms,document.children等返回的都是HTMLCollection实例
NamedNodeMap:表示属性节点对象的集合,ele.attributes返回NamedNodeMap实例

访问DOM文档时实时运行的查询,所以下面代码会导致无限循环。

var divs = document.getElementsByTagName('div'),i,div;
for(i = 0;i< divs.length; i++){
   div = document.createElement('div');
   document.body.appendChild(div);
}

浏览器不会将创建的所有集合都保存在一个列表中,而是在下次访问集合时再更新集合,i和divs.length每次都会同时递增,结果它们的值永远不会相等。正确写法如下:

var divs = document.getElementsByTagName('div'),i,len,div;
for(i = 0,len = divs.length; i<len;i++){
   div = document.createElement('div');
   document.body.appendChild(div);
}

 

参考

《JavaScript高级程序设计》

posted @ 2016-05-04 16:38  venoral  阅读(1281)  评论(0编辑  收藏  举报