页面加载完成后新建的元素事件绑定问题(下)

2008年7月24日 – 04:57

页面加载完成后新建的元素事件绑定问题(上)
=============================
2号解决方案——事件冒泡法
利用事件冒泡的原理,我们给这个按钮的祖先元素绑定事件处理函数。
然后通过event.target这个对象来判断,这个事件是不是我们要找的对象触发的。
通常可以利用一些DOM属性,比如event.target.className、event.target.tagName等之类的来判断。

  1. <td><button class="del">删除</button></td>
  1. jQuery(function($) {
  2.     //第四个表格的删除按钮事件绑定
  3.     $("#table4").click(function(e) {
  4.         if (e.target.className=="del") {
  5.             $(e.target).parents("tr").remove();
  6.         };
  7.     });
  8.     //第四个表格的添加按钮事件绑定
  9.     $("#add4").click(function(){
  10.         $("#table4>tbody").append('<tr><td>新增行</td><td><button class="del">删除</button></td></tr>')
  11.     });
  12. });

=============================
3号解决方案——复制事件法
上面几种方案可以说即便你没有用到jQuery库,你也能相对比较容易的实现。但这种方案相对依赖jQuery的程度更高。而且必须要求jQuery 1.2版以上。低版本jQuery需要插件。
上面两个方案都是对删除函数动了很多脑筋,换了多种触发、绑定的方式。这个方案不同,可以与平时纯静态的元素一样在domready的时候绑定。但在我们添加新行的时候我们改动一下,不再想上面那样拼接字符串来添加新行了。这回我们尝试使用复制DOM元素的方式。并且复制的时候连同绑定的事件一起复制,复制完之后再用find之类的修改内部的元素。
同时,就像这个例子,如果你会把所有元素都删除光,那template这个模板是必须的,如果不会删光,那就未必需要用template了。为了防止被误删,此处我把template设了隐藏。
我使用了jQuery中特有的clone(true)

  1. .template{display:none;}
  1. <tr class="template">
  2.             <td>这里是模板</td>
  3.             <td><button class="del">删除</button></td>
  4.         </tr>
  5.         <tr>
  6.             <td>这行原来就有</td>
  7.             <td><button class="del">删除</button></td>
  8.         </tr>
  9.         <tr>
  10.             <td>这行原来就有</td>
  11.             <td><button class="del">删除</button></td>
  12.         </tr>
  1. jQuery(function($) {
  2.     //第五个表格的删除按钮事件绑定
  3.     $("#table5 .del").click(function() {
  4.         $(this).parents("tr").remove();
  5.     });
  6.     //第五个表格的添加按钮事件绑定
  7.     $("#add5").click(function(){
  8.         $("#table5>tbody>tr:eq(0)")
  9.             //连同事件一起复制
  10.             .clone(true)
  11.             //去除模板标记
  12.             .removeClass("template")
  13.             //修改内部元素
  14.             .find("td:eq(0)")
  15.                 .text("新增行")
  16.                 .end()
  17.             //插入表格
  18.             .appendTo($("#table5>tbody"))
  19.     });
  20. });

=============================
总评:
上面4种方案,各有优劣。
0号方案,结构与行为完全没有分离,而且污染全局命名空间。最不推荐。所以我都不把它当作一个方案来看。但对于js初学者,可以用来项目救急。
1号方案,中规中矩,没啥好也没啥不好
2号方案,这种方法充分的发挥了js事件冒泡的优势。而且效率最高。但同时由于这种方案无视了jQuery强大的选择器,所以如果涉及的元素属性要求过多就会比较麻烦了。你会徘徊在众多if的条件的是非关系之中。后来我想起来,可以用jQuery中的$(event.target).is(selector)来作为条件。这样可以极大提升开发效率,但略微降低执行效率。
3号方案,这是我认为最能体现结构与行为分离的思想的一种方案。但缺点也很明显,对于jQuery依赖性过于高了,要不就自己写一个复制连同事件一起复制的函数,但这也显然对于初学者来说异常困难。但从未来的趋势的角度来看,还是很推荐使用这种方案的。

具体选用哪一个方案,没有定数。具体看你的项目以及你js还有结构与行为分离的思想的掌握程度。最适合的才是最好的。

=============================
附加:
把3号方案改造成完美的结构行为分离的样式。
首先,带有template的是模板元素。他是一切复制的源泉,为了防止被误删,所以设为不可见。如果不会删除光,那么这个模板元素也是可选的。因为你可以复制任何一个已经存在的用于循环元素。
其次,给每个重复的元素加上一个repeat,方便用于删除按钮找到这一级元素。这个是可选的,有时候并不需要。
最后是给每一个要修改的元素加上一个类,便于用find找到。比如我这里就家了content类,新增加的可以修改里边的值。
这样一个完美的结构与行为分离的案例就完成了。

  1. <table id="table6">
  2.     <tbody id="tbody6">
  3.         <tr class="template repeat">
  4.             <td class="content">这里是模板</td>
  5.             <td><button class="del">删除</button></td>
  6.         </tr>
  7.         <tr class="repeat">
  8.             <td class="content">这行原来就有</td>
  9.             <td><button class="del">删除</button></td>
  10.         </tr>
  11.         <tr class="repeat">
  12.             <td class="content">这行原来就有</td>
  13.             <td><button class="del">删除</button></td>
  14.         </tr>
  15.     </tbody>
  16.     <tfoot>
  17.         <tr>
  18.             <td>&nbsp;</td>
  19.             <td><button id="add6">添加</button></td>
  20.         </tr>
  21.     </tfoot>
  22. </table>
  1. jQuery(function($) {
  2.     //第六个表格的删除按钮事件绑定
  3.     $("#tbody6 .del").click(function() {
  4.         $(this).parents(".repeat").remove();
  5.     });
  6.     //第六个表格的添加按钮事件绑定
  7.     $("#add6").click(function(){
  8.         $("#tbody6>.template")
  9.             //连同事件一起复制
  10.             .clone(true)
  11.             //去除模板标记
  12.             .removeClass("template")
  13.             //修改内部元素
  14.             .find(".content")
  15.                 .text("新增行")
  16.                 .end()
  17.             //插入表格
  18.             .appendTo($("#tbody6"))
  19.     });
  20. });

同样,这段js也适用于如下的html结构

  1. <ul id="tbody6">
  2.     <li class="template repeat">
  3.         <span class="content">这里是模板</span>
  4.         <span><button class="del">删除</button></span>
  5.     </li>
  6.     <li class="repeat">
  7.         <span class="content">这行原来就有</span>
  8.         <span><button class="del">删除</button></span>
  9.     </li>
  10.     <li class="repeat">
  11.         <span class="content">这行原来就有</span>
  12.         <span><button class="del">删除</button></span>
  13.     </li>
  14. </ul>

页面加载完成后新建的元素事件绑定问题(上)

2008年7月24日 – 04:15

本文demo:http://shawphy.com/demo/event/event.html
很多人会像我一样遇到这样一个问题:
在页面加载完成后给元素绑定了事件,但又新增加的元素上却没有绑定任何事件。

js的事件监听跟css不一样,css只要设定好了样式,不论是原来就有的还是新添加的,都有一样的表现。而事件监听不是,你必须给每一个元素单独绑定事件。

常见的例子是处理表格的时候。每行行末有个删除按钮,点了这个能够删除这一行。

<table>
    
<tbody>
        
<tr>
            
<td>这行原来就有</td>
            
<td><button class="del">删除</button></td>
        
</tr>
        
<tr>
            
<td>这行原来就有</td>
            
<td><button class="del">删除</button></td>
        
</tr>
    
</tbody>
</table>

通常,我会这么绑定

  1. jQuery(function($) {
  2.     //已有删除按钮初始化绑定删除事件
  3.     $(".del").click(function() {
  4.         $(this).parents("tr").remove();
  5.     });
  6. });

对于在domready之前就存在的删除按钮,一切都很完美。但如果在domready之后用js动态添加几行,那新增的几行中的这些按钮都将失去任何作用。

如何解决这个问题?以下提供4种解决方案:
=============================
0号解决方案——onclick法
如果不顾结构与行为分离的准则的话,通常,我会这么做。
注意,此时的deltr这个function必须是全局函数,得放jQuery(function($) {})外面,放里边就成局部函数了,html里的onclick就调用不到了!

  1. <td><button onclick="deltr(this)">删除</button></td>
  1. jQuery(function($) {
  2.     //添加行
  3.     $("#add2").click(function(){
  4.         $("#table2>tbody").append('<tr><td>新增行</td><td><button onclick="deltr(this)">删除</button></td></tr>')
  5.     });
  6. });
  7. //删除行的函数,必须要放domready函数外面
  8. function deltr(delbtn) {
  9.     $(delbtn).parents("tr").remove();
  10. };

=============================
1号解决方案——重复绑定法
即,在domready的时候就给已有的元素绑定事件处理函数,
而后当新增加的元素的时候再次绑定。

  1. <td><button class="del">删除</button></td>
  1. jQuery(function($) {
  2.     //定义删除按钮事件绑定
  3.     //写里边,防止污染全局命名空间
  4.     function deltr() {
  5.         $(this).parents("tr").remove();
  6.     };
  7.     //已有删除按钮初始化绑定删除事件
  8.     $("#table3 .del").click(deltr);
  9.     //添加行
  10.     $("#add3").click(function(){
  11.         $('<tr><td>新增行</td><td><button class="del">删除</button></td></tr>')
  12.             //在这里给删除按钮再次绑定事件。
  13.             .find(".del").click(deltr).end()
  14.             .appendTo($("#table3>tbody"));
  15.     });
  16. });

=============================
2号解决方案——事件冒泡法
利用事件冒泡的原理,我们给这个按钮的祖先元素绑定事件处理函数。
然后通过event.target这个对象来判断,这个事件是不是我们要找的对象触发的。
通常可以利用一些DOM属性,比如event.target.className、event.target.tagName等之类的来判断。

  1. <td><button class="del">删除</button></td>
  1. jQuery(function($) {
  2.     //第四个表格的删除按钮事件绑定
  3.     $("#table4").click(function(e) {
  4.         if (e.target.className=="del") {
  5.             $(e.target).parents("tr").remove();
  6.         };
  7.     });
  8.     //第四个表格的添加按钮事件绑定
  9.     $("#add4").click(function(){
  10.         $("#table4>tbody").append('<tr><td>新增行</td><td><button class="del">删除</button></td></tr>')
  11.     });
  12. });

页面加载完成后新建的元素事件绑定问题(下)

jQuery中不为人知的秘密之一

2008年7月16日 – 15:29

jQuery的向来以其完善的文档著称,而不像早期的Prototype那样库写的很牛,而文档很糟糕,其他使用者不得不看他的源码以了解一些功能。
但是,不得不说,文档的更新速度是远没有其程序变化的快的。
这个专题就是用于介绍一些隐藏在jQuery源码内部的事情。

今天先揭秘2条

1,序列化一个对象
类似于Prototype中的$H(obj).toQueryString()
jQuery中也有一个内部函数,用于在ajax时候序列化对象用:
jQuery.param

  1. var obj={A:1,B:2,C:3};
  2. jQuery.param(obj)//A=1&B=2&C=3

2,获取原始的event对象
用过jQuery都知道,jQuery提供了一个事件对象,用于在事件处理函数中使用,并且这个对象已经将ie中的事件修复成了标准的W3C事件。具体可以参考我这篇日志

但他并没有完全统一所有事件,比如获取mousemove的event事件中鼠标的相对坐标的位置,ie和ff分别用的x和layerX来实现,而jQuery没有给统一。我们只能使用原始的event对象再自己判断,此时就可以使用
e.originalEvent
这样就得到了原始的event对象了
在ie中,这个指向的是window.event,而在其他浏览器中,就是传递给事件处理函数的第一个参数。

像上面那个例子,我就用了如下的代码,用于获取鼠标的相对坐标。

  1. $("#menuWrap").mousemove(function(e) {
  2.     var xx=e.originalEvent.x||e.originalEvent.layerX||0;
  3. });

可以参考我写的一个简单的菜单
这个页面看上去动作还有点僵硬,是由于动画函数的问题。
关于动画函数,将在以后详细讲解。

jQuery的链式动画写法中加入setTimeout

2008年7月12日 – 16:59

今日闲逛,看到一个简单的封装,用于在两次animate之间,加入一定的时间间隔的等待。
原文地址:http://docs.jquery.com/Cookbook/wait
你还可以在里边看到demo

  1. $.fn.wait = function(time, type) {
  2.     time = time || 1000;
  3.     type = type || "fx";
  4.     return this.queue(type, function() {
  5.         var self = this;
  6.         setTimeout(function() {
  7.             $(self).dequeue();
  8.         }, time);
  9.     });
  10. };

具体用法是,.wait( [time], [type] )
第一个参数是时间,默认是1000毫秒。
第二个参数是类型,默认是fx即动画效果

  1. function runIt() {
  2.     $("div").wait()
  3.         .animate({left:'+=200'},2000)
  4.         .wait()
  5.         .animate({left:'-=200'},1500,runIt);
  6.     }
  7. runIt();

由于我对对queue这个函数用法不是很熟,所以我也不太清楚第二个参数还能设置除了fx之外的其他什么。
有熟悉的朋友可以帮忙解释一下。

JavaScript switch case 语句设置范围

2008年7月8日 – 04:07

不知是否出于什么缘故,我google搜索
javascript switch case 范围
这几个关键字,出来的前几条都是csdn上关于无解的表示。
可能是其他编译型语言中,case后便必须是常量的缘故吧。

后来在一个边角的地方看到某人发帖是VB还是不知道哪个语言中的中实现了switch case语句的范围选择,大家看了下面代码就明白了。

  1. var x=1
  2. switch(true){
  3.     case x>0&&x<10:
  4.         alert(1);break;
  5.     case x>=10&&x<20:
  6.         alert(2);break;
  7. }

看明白啦?就是利用传递给switch的参数设为true,把原来的变量变成常量,常量变变量,逆向思维。巧妙吧?

本来我想出来的办法是这样的,没有上面的好,没有跳出思维定势,也贴出来给大家看看,权当娱乐娱乐了

  1. var x=1
  2. switch(x){
  3.     case (x>0&&x<10)*x:
  4.         alert(1);break;
  5.     case (x>=10&&x<20)*x:
  6.         alert(2);break;
  7. }

一个有趣的布局

2008年7月6日 – 17:51


 
尝试一下,不用定位,只用浮动,就把这个排版排出来看看。

不要被她迷惑了,其实没你想的那么难

实在想不出来,可以看这里

  1. <div id="wrap">
  2.     <div id="a"></div>
  3.     <div id="b"></div>
  4.     <div id="c"></div>
  5.     <div id="d"></div>
  6.     <div id="e"></div>
  7. </div>

找出页面内重复的Id

2008年7月6日 – 00:33

今天群里提起这事,
判断页面内是否有重复的id

一种是已知id,看是否有重复。基本上Ctrl+F就能搞定~
但是Robin给出了方法:

  1. function chkid(idname){
  2.     s=0;
  3.     divs = document.getElementsByTagName("*");
  4.     for (i=0; i<divs.length; i++){
  5.         if(divs[i].getAttribute("id")==idname)
  6.             s++;
  7.     }
  8.     alert(s);
  9. }

而后我又在其基础上修改了能够找出页面内所有重复的Id

  1. function chkid(){
  2.     var did={},tmpid;
  3.     divs = document.getElementsByTagName("*");
  4.     for (var i=divs.length-1; i+1; i--){
  5.         (tmpid=divs[i].id)
  6.             &&did[tmpid]===undefined
  7.                 ?did[tmpid]=0
  8.                 :did[tmpid]++;
  9.     }
  10.     for (var i in did) {
  11.         did[i]&&alert("id="+i+"重复了"+(did[i]+1)+"");
  12.     }
  13. }

比较可靠,兼容ie和ff。
不过一般的html检查器都有这个功能,这个代码又有多大用处呢?呵呵,感觉没有。

博客从pjblog换成wordpress咯

2008年7月5日 – 15:43

博客换了,换的有点曲折,之前是因为.htaccess文件的问题操导致网站一直无法访问,可能是编码问题吧。

这回换成wordpress了,用了asp2wp这个转换工具,蛮强悍,文章和评论都在。不光是pj到wp,还能从lbs到wp。

之前试用了aw的修改版,总是失败,只能导入评论而没有文章。
那感谢大家一如既往的支持啦。

读jQuery基础教程时的需要注意的1.1和1.2版的差别

2008年6月24日 – 00:50

jQuery基础教程这本书已经出版咯~
这本书可以用于快速入门,大大缩短jQuery入门所需要的时间(实际上就算没有这本书,看现有的资料,1个星期也足够入门了)。
但还是强烈建议购买此书,因为这能让你能达到看现在有资料好几个月也未必能达到的水平。
至少我现在依然能从上面学到不少东西!

最近书到手了,翻阅了一下
也知道由于译者比较忠于原著,所以未能对其中少部分过时的内容予以修订。
看到他写了不少的“译者注”,但对于1.2的更新是完全没有涉及。

已知的重大变更是:
(1),1.2以后中属性选择器均不需要@,但依然兼容1.1的选择器。
(2),第一章里提到了XPath选择符在1.2中因为不常用且影响效率而取消了,如需使用则需要兼容1.1中的XPath插件(http://plugins.jquery.com/project/xpath),同样第六章和第九章涉及XML解析等也无法使用XPath语法了。关于更多xml解析的问题,可以看http://shawphy.com/2008/01/load-xml-by-ajax-with-jquery-and-parse-it.html
(3),第五章,关于clone(false)的描述,在1.2中,已经整合了复制绑定事件的插件了,即增加了clone(true)功能用于连同元素绑定的事件一起复制。而取消了clone(false)这个只复制元素而不复制内容的方法。读者需要注意。可以用.clone().empty()代替。
(4),第七章中部分地方使用了已被废弃的gt()和lt()方法。可以使用slice代替,或者用:gt和:lt选择符代替。具体请参考文档。
(5),第十章,流行插件中,由于Dimensions插件过于流行,已经被整合进jQuery,所以你无需再另行导入。而Interface插件也已经更新换代,又官方重新编写后改名为jQuery UI。(http://ui.jquery.com

位于p最开始的span设置text-indent影响p的整个第一行,反继承

2008年6月11日 – 11:08

今天遇到这个问题
描述起来也蛮麻烦的
搜也搜不到,所以问了群里的朋友,
感谢Robin还有$一揪就出来,
他俩对此问题进行了深入的讨论,
感谢他们细心解答,真相才得以水落石出。
最后本文采用了$一揪就出来的方法:

如下代码

  1. <style>
  2. span.imgblock{
  3.     height:16px;
  4.     width:16px;
  5.     display:inline-block;
  6.     background:#0099FF;
  7.     text-indent:5em;
  8.     overflow:hidden;
  9. }
  10. </style>
  11. <p><span class="imgblock">这里看不见,确可以复制到</span>选中前边的框子,然后复制吧。<br />你可以复制所有本文<p>

给最开始的span设置了text-indent属性之后,导致了整个父元素p的text-indent属性
IE6/7都确认有这个bug,可以说是一种反继承。

解决的方法是多一个标签,避开问题多多的text-indent,
设置内层标签的行高,以及padding-left。就能够大致解决问题了。
即保证了效果,也保证了文本的不丢失,为残障人士,搜索引擎,以及想要复制文章的人给予方便。
至少可以不用空格了,使用空格虽然可以轻松达到效果,无需使用text-indent了,但这显然不好。

  1. <style>
  2. span.imgblock{
  3.     height:16px;
  4.     width:16px;
  5.     display:inline-block;
  6.     background:#0099FF;
  7.     overflow:hidden;
  8. }
  9. strong.aaa{
  10.     display:inline-block;
  11.     padding-left:2000px;
  12. }
  13. </style>
  14. <p><span class="imgblock"><strong class="aaa">这里看不见,确可以复制到。</strong></span>选中前边的框子,然后复制吧<br />你可以复制所有本文<p>