分类目录归档:JS

Javascript学习与交流

获取DOM父元素和子元素

利用javascript可以遍历DOM树,这篇文章介绍用获取一个DOM元素的所有父节点和获取一个DOM元素的所以子孙节点。

1.获取所有父节点。用递归的方法,用parentNode属性。

<!DOCTYPE html>
<html lang=”en” >
<head>

<title>getParents</title>

</head>
<body >
<div >
<div id=”test”> </div>
<div></div>
</div>

<script type=”text/javascript”>
var getParents=function(id){
var dom=id.parentNode;
while(dom.tagName!=null){
document.write(dom.tagName);
dom=dom.parentNode;
}
return dom;
}
getParents(test);
</script>
</body>
</html>

运行结果(chrome、firefox、IE9):DIVBODYHTML

2.遍历所以子孙节点。

<!DOCTYPE HTML>
<html>
<head>
<meta charset = “utf-8″/>
<title>getChildren</title>
</head>
<body>
<div>&nbsp;&nbsp;I am in second floor
<div>&nbsp;&nbsp;I am in second floor</div>
</div>
<div>1
<div>&nbsp;&nbsp;I am in second floor
<div>&nbsp;&nbsp;&nbsp;&nbsp;I am in third floor</div>
</div>
</div>
<div>1</div>
<div>1</div>
<div>1</div>
<div id=”test”>
<div>&nbsp;&nbsp;I am in second floor</div>
<div>&nbsp;&nbsp;I am in second floor</div>
</div>
<script type=”text/javascript”>
var node;
node = document.getElementsByTagName(‘body’);

//深度遍历
function getChildren(node,f){ //f表示第几层,根元素为第0层
if(node.nodeType!=3){
console.log(“nodename: “+node.nodeName);
console.log(“nodetype: ” + node.nodeType);
console.log(“the “+f+”th floor”);
var childlist = node.childNodes;
console.log(childlist);
var length;
length = childlist.length;
if(childlist.length>0){
var f = f+1;
for(var i=0;i<childlist.length;i++){
getChildren(childlist[i],f);
}
}
}else if(node.nodeValue.length > 1){ //因为每个nodeValue都带一个换行符”%0A”
console.log(“value: “+node.nodeValue);
}
}

getChildren(node[0],0);
//层次遍历DOM树
function getChildrenByLev(arr,f,Matri){ //f表示第几层,根元素为第0层,arr表示遍历起始层的节点,Matri为层次遍历输出的结果,结果以一个二维数组表示,第一个索引表示层次
if(arr.length<1)return Matri;
f = f+1;
Matri[f] = Matri[f] || new Array();
for(var i = 0; i < arr.length ; i++){
children = arr[i].childNodes;
for(j in children){
if(children[j].nodeType == 1){
Matri[f].push(children[j]);
}
}
}
getChildrenByLev(Matri[f],f,Matri);
}

var levelMatri = new Array();
levelMatri[0] = new Array();
levelMatri[0][0] = node[0];
getChildrenByLev(node,0,levelMatri);
console.log(levelMatri);
</script>
</body>
</html>

深度遍历的结果如图(注意:截图不全):

traverse1

层次遍历的结果如图:

traverse2

document.write 到底做了什么?

在使用document.write()时,经常会遇到奇怪的现象,比如说下面这段代码。注释里是一些怪异的现象。
<html>
<head>
<title></title>
    <script language="javascript" type="text/javascript">
        var div=document.createElement("div");
        div.innerHTML="被添加的div";
        //如果直接document.body.appendChild(div); 则失败.  
        //document.write("<span></span>");  //添加这行,document.body.appendChild(div) 显示“被添加的div”.
        //document.write("&nbsp;");         //document.body.appendChild(div); IE下显示“被添加的div”,Firefox,Chrome不显示.
        //document.write(" ");              //document.body.appendChild(div); 不显示.
        document.body.appendChild(div);
    </script>
</head>
<body>
</body>
</html>
可以看出,这些现象主要是两类:一是结果与预想不一致,二是浏览器的兼容性问题。我们就来看看document.write()到底干了什么?
document.write()的作用是将html标签和javascript代码写到页面中。但是它的具体过程并不是这句话说的那么简单,它的背后其实隐藏着一个复杂的过程。在载入页面后,浏览器输出流自动关闭;在此之后,任何一个对当前页面进行操作的document.write()方法将打开—个新的输出流。它将清除当前页面内容(包括源文档的任何变量或值)、因此.假如希望用脚本生成的HTML替换当前页面,就必须把HTML内容连接起来赋给一个变量、使用一个document.write()方法完成写操作,不必清除文档并打开一个新数据流,一个document.write()调用就可完成所有的操作。

<html>
<body>

<p>Note that write() does NOT add a new line after each statement:</p>

<pre>
<script>
document.write(“Hello World!”);
document.write(“Have a nice day!”);
</script>
</pre>

</body>

</html>

上面这一段代码用浏览器打开,显示一行”Hello World!Have a nice day!”。如果要显示多行,可以用document.writeln()。

关于document.write()方法还有一点要说明的是它的相关方法document.close()。脚本向窗口(不管是本窗口或其他窗口)写完内容后.必须关闭输出流。在延时脚本的最后一个document.write()方法后面.必须确包含有document.close()方法,不这样做就不能显示图片和表单。并且,任何后面调用的document.write()方法只会把内容追加到页面后,而不会清除现有内容来写入新值。

document是引擎开始解析代码前就初始化,放到作用域,推到作用域链的末端,不过这时作用域链是空的,所以document是处于顶端,也就是我们所说的全局变量
初始化工作类似 var document = new Document();
而Document是拥有body,getElementById,getElementsByName,write等方法的类
所以document就拥有了Document的原型方法与其成员变量,但getElementById这些是基于body已经加载完成才能使用,这就是为什么当页面没加载完成时,我们使用不了getElementById的原因,而write并不需要,所以我们可以在页面还没加载完之前调用document.write方法
并且调用document.write方法的同时,会调用open与close的方法,具体会这么实现
document.open();
document.write();
document.close();
当open的时候将会打开一个文档流来输入内容,当文档已存在的时候,则先清空
write就是写入内容,close就是关闭文档流了
而文章开头的例子,当第一次使用document.write的时候,文档还没存在,所以它不会清空,而会将内容是直接附加到文档流中,当有内容附加在文档流后,body自然就会创建了,大家可以用firebug断点就清晰看到第一次document.write之前body为null,当document.write之后,body就为“body”了,所以为什么document.write之后能使用document.body.appendChild了。

angularjs学习笔记(3)–i18n使用

项目中用到angular-ui-bootstrap插件中的datepicker,可是datepicker显示的星期和月份是英文的,我需要他们都显示成中文的,最简单的方法就是用angularjs的i18n(Internationalization, 简称i18n)。
官方文档告诉我们,我们只需引入相应的local文件就好了,例如我要引入中文的i18n文件,只需引入angular-locale_zh-ch.js。然后将ngLocal模块注入我的当前工作模块即可。

其中angular-locale_zh-ch.js如下所示:

ngLocal就是一个模块,支持日期,数字和货币的国际化和本地化。另外,AngularJS还通过ngPluralize指令支持本地多元化。看下面这个例子:

https://github.com/angular/angular.js/blob/master/i18n/e2e/localeTest_zh.html

localTest_cn.html:(很抱歉目前html代码只能贴图,一时没找在wordpress里贴html代码而html特殊符号显示正常的方法,因为尖括号就是典型的html特殊符号。)

 

还有一种方法就是自定义i18n,毕竟官方给的i18n支持的本地化仅作用于日期,数字和货币。看下面的例子。

http://jsfiddle.net/7zdpa/
index.html

app.js

效果如图

angularjs学习笔记(2)–directive 理解$apply $watch

directive是angularjs扩展html标签的方式,它通过自定义属性,元素名称,类或声明来触发一个html行为或者说是DOM的变化。
directive的运行机制是这样的:

该机制的详细描述见官方文档conceptual Overview http://docs.angularjs.org/guide/concepts
需要注意的是大多数情况下(如在控制器,服务中),$apply都已经被用来处理当前事件的相应指令执行过了。只有当你使用自定义的事件回调或者是使用第三方类库的回调时,才需要自己执行$apply。
看下面的例子。
index.html:

app.html

运行结果如下:

这个时候,我们的directive只有link函数。在link函数里,默认的,我们能获得scope,element和attrutes。但是我们还不能把active类应用于按钮。
如果用$watch来观测表达式的变换和$apply来将变换传播给view。
app.js改为如下:

结果如下:

我们看看这个例子里按钮的点击事件是怎样绑定到视图的:
1.在编译阶段:button上的ng-modelg给绑定一个keyup事件;{{options.xxx}}变量通过$watch来接受鼠标所选择按钮,并将options对应的值改变,并给按钮添加或移除active样式。
2.在执行阶段:点击一个按钮后释放,就会触发button的keyup事件;然后调用$apply(“options.xxx = ‘X’;”)来更新处于AngularJS执行上下文中的模型;AngularJS将 options.xxx=’X’应用到模型上;$digest 循环开始;$watch 列表检测到了options.xxx值的变化,然后通知 ngModel.$modelValue变量来获得改变的值,更新{{options.xxx}}和相应的选择状态。用function(modelValue)这个回调函数来改变相应button的样式;AngularJS退出执行上下文,然后退出Javascript上下文中的keyup事件;
浏览器以更新的文本重渲染视图。

参考文章:watch how the $apply runs a digest http://www.cnblogs.com/rubylouvre/p/3268238.html
angularjs 移除不必要的$watch http://www.cnblogs.com/whitewolf/p/angularjs-remove-unused-watch.html
Getting started with AngularJS Directive http://suhairhassan.com/2013/05/01/getting-started-with-angularjs-directive.html#.UjXcRdKmger
AngularJS开发指南04:核心概览: http://www.angularjs.cn/A00q
AngularJS文档 http://docs.angularjs.org/guide/scope http://docs.angularjs.org/guide/concepts
directive tutorial http://www.befundoo.com/university/tutorials/angularjs-directives-tutorial/

AngularJs 学习笔记(1)–controller 通信

用angularJs一个月了,始终想写点类似于学习笔记的东西。酝酿了一个星期,就从今天开始吧。刚开始写有些东西可能是修改的别人的东西,希望多写几次后可以贴上自己的东西。
今天就从controller的通信机制写起。
每一个controller都有自己的作用域,即scope。scope就是指向应用模型的对象,是表达式的执行环境(scope is an object that refers to the application model. It is an execution context for expressions. )。父controller与子controller之间,以及兄弟controller之间的数据引用关系是怎样的呢?我们通过下面的例程来看一下:
index.html:

app.js:

运行结果如图:

由此我们可以看出,父controller的作用范围可以深入到子contrller里面去,而字controller的作用范围是不包括父controller的。那么父contrller的作用范围可以渗透到几层子孙controller呢?我们可以继续测试,答案是父controller的作用与可以向子孙controller渗透。
那么兄弟controller之间,子controller怎么向父controller通信呢?我们会很快想到javascript的异步回调响应式通信—事件机制(或消息机制)。对,这就是angularjs解决controller之间通信的机制,所推荐的唯一方式,简而言之这就是angular way。
Angularjs为在scope中为我们提供了冒泡和隧道机制,$broadcast会把事件广播给所有子controller,而$emit则会将事件冒泡传递给父controller,$on则是angularjs的事件注册函数,有了这一些我们就能很快的以angularjs的方式去解决angularjs controller之间的通信,看下面的例程:
index.html

app.js:

这里childCtr1的name改变会以冒泡传递给父controller,而父controller会对事件包装在广播给所有子controller,而childCtr2则注册了change事件,并改变自己。注意父controller在广播时候一定要改变事件name。

参考文章:
1.angular官方文档 http://docs.angularjs.org/guide/scope
2.破狼博客 http://www.cnblogs.com/whitewolf/archive/2013/04/16/3024843.html