一个专注于技术的IT男
web开发
link的地址以斜线开头完全没有道理啊
八 25th
在内部搭一个类stackoverflow的系统,选了个基于django的开源的osqa,部署的时候陆陆续续发现源码里有点小问题,比如用户相关的很多link是这样写的:
<a href="/users/{{ user.id }}/{{ user.username|slugify }}/">{% gravatar user 32 %}</a>
这么搞的话link就指向了服务器的documentRoot,但是我不会把osqa这个应用直接挂在根域名下,这样的hard code我觉得挺愚蠢的。
其实同样的问题也存在我们的jsp里面,这个编程习惯不好,要改正其实也不难,手工做的话可以取得app的rootPath就行了,或者用url标签来帮我们自动搞定。(django&jsp都有的)
另一个Note:osqa第一次装的时候会把配置信息写到forum_keyvalue表里面,后面修改上传路径等的配置没有用,必须到数据库修改。
mysql> update forum_keyvalue set value="S'/upfiles/logo.png'
"> p0
"> ." where id=26;
mysql> update forum_keyvalue set value="S'/var/lib/osqa/forum/upfiles'
"> p0
"> ." where id=33;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from forum_keyvalue where id=33;
+----+----------------+-------------------------------------+
| id | key | value |
+----+----------------+-------------------------------------+
| 33 | UPFILES_FOLDER | S'/var/lib/osqa/forum/upfiles'
p0
. |
+----+----------------+-------------------------------------+
Something more:
Larry wrote 2 useful CSS related posts:
CSS – small tips (设置图像最大宽度,打印分页)
CSS – sprites, manage images (图像合并切割)
END
jQuery Tips : 给AJAX回调函数传递额外参数
六 5th
讨论这个问题基于如下场景:点击页面上某个按钮之后,触发click事件,事件处理函数内部发送一个AJAX请求,AJAX回调函数更新页面的某一个部分
具体到这个例子,我们希望button1和button2点击之后,用AJAX的方式取example.html的内容,然后动态更新页面的id=callbackdemo3的div
HTML如下:
<div id="callbackdemo1">
<button id="button1">ajax load1</button><br/>
</div>
<div id="callbackdemo2">
<button id="button2">ajax load2</button><br/>
</div>
<div id="callbackdemo3" class="log"></div>
<button onclick="$('.log').html('');">clear</button>
第一种做法:
适用于最简单的情况,也是比较直观的做法,就是在ajax回调函数中,使用jQuery的id选择器$(“#callbackdemo3″)得到id为callbackdemo3的div后更新其HTML内容
$("#callbackdemo1>#button1").click(
function load(){
$.get("example.html",{ 'param[]': ["var1", "var2"] },
function f1(data, textStatus, XMLHttpRequest)
{
$("#callbackdemo3").html(data);
}
);
}
);
第二种做法:
定义好一个接受额外参数的回调函数,然后在默认回调函数的内容调用这个预先定义好的回调函数
这样就能达到传递额外参数的目的了,较之第一种方法,此方法能比较方便的利用各个回调函数的上下文。
function callback_with_extraParam(data,param)
{
param.html(data);
}
$("#callbackdemo2>#button2").click(
function load(){
var extraParam = $("#callbackdemo3")
$.get("example.html",{ 'param[]': ["var1", "var2"] },
function f2(data)
{
callback_with_extraParam(data,extraParam);
}
);
}
);
对于额外参数的重要性,不妨看一个稍微复杂一点点的HTML情况,我们希望更新button3下面的那个div的HTML,这个div没有id,那么如何做呢?
<div id="callback_complexdemo">
<button id="button3">ajax load3</button><br/>
<button onclick="$('div',$(this).closest('div')).html('');">clear</button><br/>
<div></div>
</div>
当然使用#callback_complexdemo>div也是可以的,但是如果是更加复杂的HTML页面呢?如果没有id=callback_complexdemo呢?嵌套很深呢?
这种情况下,我们就需要有效利用上下文参数这个特性了。
在click的事件处理函数load()中,我们可以方便的获得当前元素的位置var whereAmI = $(this);
这个whereAmI是个jQuery对象,然后在AJAX回调函数f3中,我们利用whereAmI即被点中的这个button想办法去获得想要的div
这一行代码新手有的人看不懂:$(‘div’,$(whereAmI).closest(‘div’))
首先$closest_parent_div = $(whereAmI).closest(‘div’)即查询获得whereAmI最近的父div对象
然后$(‘div’,$closest_parent_div) 即在最近的父div对象这个上下文中,查找内部的div对象
$("#callback_complexdemo>#button3").click(
function load(){
var whereAmI = $(this);
$.get("example.html",{ 'param[]': ["var1", "var2"] },
function f3(data)
{
console.log(whereAmI);
$('div',$(whereAmI).closest('div')).html(data);
}
);
}
);
希望通过这个小例子,能让大家学会如何给AJAX回调函数传递额外参数,以及实用且重要的上下文参数。
Cool demo with jQuery plugin FancyBox
五 21st
代码如下:
<script type="text/javascript" src="http://code.jquery.com/jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="http://www.haojii.com/webdev/fancyboxdemo/fancybox/jquery.mousewheel-3.0.2.pack.js"></script>
<script type="text/javascript" src="http://www.haojii.com/webdev/fancyboxdemo/fancybox/jquery.fancybox-1.3.1.js"></script>
<link rel="stylesheet" type="text/css" href="http://www.haojii.com/webdev/fancyboxdemo/fancybox/jquery.fancybox-1.3.1.css" media="screen" />
<script type="text/javascript">
$(document).ready(function() {
$("#login").fancybox({
'padding' : 0,
'autoScale' : false,
'transitionIn' : 'none',
'transitionOut' : 'none'
});
$("#flash").fancybox({
'padding' : 0,
'autoScale' : false,
'transitionIn' : 'none',
'transitionOut' : 'none'
});
});
</script>
<div>
<h1><a id="login" href="#register-form" title="This is the title for register form">猛击我吧!</a></h1>
<div style="display:none">
<!-- Registration -->
<div id="register-form" style="width: 500px; height: 300px; margin:auto;">
<h1><a id="flash" href="http://www.adobe.com/jp/events/cs3_web_edition_tour/swfs/perform.swf">A Flash Demo</a></h1>
</div>
</div>
<!-- /Registration -->
</div>
你应该使用的20个有用的jQuery方法
四 15th
原文地址:20 Helpful jQuery Methods you Should be Using
原文作者:Andrew Burgess
这篇文章介绍了jQuery中有用的20个方法,1-20编号如下,本文没有对照原文逐句翻译,这个20个方法名称,我其实就是用下面第14个map()的方式取出来的,亮点如下,firebug真是个离不开的好工具。
说到工具,那就顺便再提一下一个简洁的jQuery代码调试工具:jQueryPad
支持代码高亮,不支持代码辅助,小问题是这个工具带的jQuery.js的版本旧了,需要自己更新一份到它的目录。
(当然,我还是最喜欢firebug,特别是网页复杂的时候)
1 after() / before()
这个无需多说了,上面的图就是个例子,就是在当前对象的前面或者后面插入内容,被插入的内容是当然对象的兄弟节点
2 change()
和click()或者hover()一样,它也是个事件处理器,这个change事件作用于 textarea, text input, select, 当目标元素的值改变的时候触发该事件。它不同于对象失去焦点时触发的事件foucusOut()和blur()。
change()事件对于客户端验证的任务最适合不过了,因为你不需要在输入字段值没有变化的时候重新验证字段。
$(document).ready(
function() {
$('input[type=text]').change(function () {
switch (this.id) {
/* some validation code here */
case "name":
alert(this.value+"....");
break;
default:
alert(this.id);
break;
}
});
});
// HTML Form
Name: <input id="name" type="text"/>
3 Context
在jQuery中,使用选择器,其实默认作用于document这个上下文上
了解了这一点话,Context就简单了,它是一个属性兼参数
作为属性:每个jQuery对象,都有个context属性
作为参数:当使用jQuery选择器时,可以使用第二个参数context,细化了查询的范围
用Firebug试验一下就明白了!
4 data() / removeData()
在元素上(确切的说应该是jQuery对象)上存储数据的一种办法
5 queue() / dequeue()
用在jQuery动画效果上,可以方便的增加或者移除一个特效
6 delay()
暂停一段时间的动画效果
7 bind(), unbind(),live(), and die()
以前的文章说过了,不废话了
8 eq()
用数组下标的方式取得一组元素中的某一个
9 get()
取得jQuery元素对应的Dom对象格式
10 grep()
这个我挺喜欢,第二个参数是个用于过滤的回调函数,很容易懂的
var nums = '1,2,3,4,5,6,7,8,9,10'.split(',');
nums = $.grep(nums, function(num, index) {
// num = the current value for the item in the array
// index = the index of the item in the array
return num > 5; // returns a boolean
});
console.log(nums) // 6,7,8,9,10
11 Pseudo-Selectors
$(':animated'); // returns all elements currently animating
$(':contains(me)'); // returns all elements with the text 'me'
$(':empty'); // returns all elements with no child nodes or text
$(':parent'); // returns all elements with child nodes or text
$('li:even'); // returns all even-index elements (in this case, <li>s)
$('li:odd'); // can you guess?
$(':header'); // returns all h1 - h6s.
$('li:gt(4)'); // returns all elements with an (zero-based) index greater than the given number
$('li:lt(4)'); // returns all element with an index less than the given number
$(':only-child'); // returns all . . . well, it should be obvious
12 isArray() / isEmptyObject() / isFunction() / isPlainObject()
$.isArray([1, 2, 3]); // returns true
$.isEmptyObject({}); // returns true
$.isFunction(function () { /****/ }); // returns true
function Person(name) {
this.name = name
return this;
}
$.isPlainObject({})); // returns true
$.isPlainObject(new Object()); // returns true
$.isPlainObject(new Person()); // returns false
13 makeArray()
使用jQuery选择器返回的都是jQuery对象,所以$.makeArray()变得挺实用的
var ps = $('p');
$.isArray(ps); //returns false;
ps = $.makeArray(ps);
$.isArray(ps); // returns true;
14 map()
上面firebug的贴图中延伸过了,自己试试看吧
15 parseJSON()
把JSON格式的字符串结果解析成JSON对象
16 proxy()
$.proxy() 解决了函数调用的上下文问题
var person = {
name : "Andrew",
meet : function () {
alert('Hi! My name is ' + this.name);
}
};
person.meet(); // 正常工作
$('#test').click(person.meet); // 返回 Hi! My name is undefined 或者 Hi! My name is 空
$('#test').click($.proxy(person.meet, person));
// we could also do $.proxy(person, "meet")
17 replaceAll() / replaceWith()
用来替换内容的,replaceWith更顺脑
<div class="error1">error section 1</div>
<br/>
<div class="error2">error section 2</div>
<br/>
$('<span class=fixed>The error has been corrected</span>').replaceAll('.error1');
"$('.error2').replaceWith('<span class=fixed>The error has been corrected</span>');
18 serialize() / serializeArray()
看例子吧,易懂
<form>
<input type="text" name="name" value="John Doe" />
<input type="text" name="url" value="http://www.example.com" />
</form>
console.log($('form').serialize()); // logs : name=John+Doe&url=http%3A%2F%2Fwww.example.com
console.log($('form').serializeArray());
// logs : [{ name : 'name', value : 'John Doe'} , { name : 'url', value : 'http://www.example.com' } ]
19 siblings()
返回所有兄弟节点
20 wrap() / wrapAll() / wrapInner()
在作用对象的外面,周围,或者里面,包一层东西
Conclusion
好好玩这些个方法吧!
…
为iPhone优化网页的技巧
二 7th
为了使得网页看上去更像native的程序,我们需要对网页进行一些优化
本文包含一些小的技巧:
1. 如何隐藏iPhone Safari的地址栏
2. 如何设置网页的桌面快捷方式图标
3. 如何为网页设置iPhone的样式
4. 如何在网页中检测屏幕的方向
5. 如何在网页中检测当前的位置
为iPhone优化网页,最需要了解的应该是下面这个属性
viewport: 可见窗口
可见窗口在iPhone Safari上和桌面Safari略有不同,iPhone上默认没有滚动条,默认的实际的可见大小在竖屏状态下,如下图是320*356,地址栏占据了60像素的高度,稍后我们有办法将其隐藏,得以使用这宝贵的60像素。
iPhone的Safari在显示传统的web网页时(未做优化的网页),viewport会被设置成显示网页宽度的980像素,即在320*356的大小内显示原网页980像素的内容,做了适当的缩放等调整。
所以我们做网页优化的时候,可以通过设置这个meta属性,更多细节可以参考: “Safari Web Content Guide.pdf”
<meta name="viewport" content="width=device-width,height=device-height, initial-scale=1.0, user-scalable=no"/>
width=device-width, //设备宽度
height=device-height, //设备高度
initial-scale=1.0, //初始的缩放比例
user-scalable=no //是否允许用户缩放
小技巧
1. 如何隐藏iPhone Safari的地址栏
设置这个属性(文档里面说有用,但是好像不太成功)
<meta name="apple-mobile-web-app-capable" content="yes" />
还有一种办法,页面加载完成之后滚动窗口,这个确实有效,唯一要注意的是页面高度必须够长,我一般设成device-height就没问题了:
<script type="text/javascript">
addEventListener("load", function() { setTimeout(hideURLbar, 0); }, false);
function hideURLbar(){
window.scrollTo(0,1);
}
</script>
2. 如何设置网页的桌面快捷方式图标
这个其实不算技巧,只是从Apple文档里找到这一项罢了
<link rel="apple-touch-icon" href="apple-touch-icon.png"/> <link rel="apple-touch-startup-image" href="startup.png"/>
第一行就是设置桌面快捷方式图标的,图标必须是57*57像素的文件,不需要自己做圆角和高亮效果,系统会自动帮你搞定这个
放在网页根目录会作用于下面的全部网页,当然也可以为每个页面设置单独的图标
第二行是设置启动画面(没看到成功效果)
3. 如何为网页设置iPhone的样式
这个是CSS media selector的一点小技巧
<!--[if !IE]>--> <link media="only screen and (max-device-width: 480px)" rel="stylesheet" type="text/css" href="iphone.css"/> <!--<![endif]-->
4. 如何在网页中检测屏幕的方向
<script type="text/javascript">
function updateOrientation()
{
var displayStr = "Orientation : ";
switch(window.orientation)
{
case 0:
displayStr += "Portrait";
break;
case -90:
displayStr += "Landscape (right, screen turned clockwise)";
break;
case 90:
displayStr += "Landscape (left, screen turned counterclockwise)";
break;
case 180:
displayStr += "Portrait (upside-down portrait)";
break;
}
document.getElementById("output").innerHTML = displayStr;
}
</script>
<body onorientationchange="updateOrientation();">
5. 如何在网页中检测当前的位置
<script type="text/javascript">
navigator.geolocation.getCurrentPosition(showMap);
function showMap(position) {
latitude = position.coords.latitude;
longitude = position.coords.longitude;
document.getElementById("latitude").innerHTML = latitude;
document.getElementById("longitude").innerHTML = longitude;
}
//Register for location changes
var watchId = navigator.geolocation.watchPosition(scrollMap,handleError);
function scrollMap(position) {
latitude = position.coords.latitude;
longitude = position.coords.longitude;
document.getElementById("latitude").innerHTML = "scrollMap->"+latitude;
document.getElementById("longitude").innerHTML = "scrollMap->"+longitude;
}
function handleError(error) {
document.getElementById("error").innerHTML = error;
}
function buttonClickHandler() {
// Unregister when the user clicks a button
navigator.geolocation.clearWatch(watchId);
}
</script>
</head>
<body>
<div id="location">
latitude:<p id="latitude">null</p>
longitude:<p id="longitude">null</p>
</div>
<div>
<input type="button" value="clearWatch" onclick="buttonClickHandler();"/>
</div>
<div id="error"></div>
</body>
炫酷CSS超链接按钮一
一 27th
上一篇文章提到用CSS分割图片的技巧,例子中也展示了一个很漂亮的CSS按钮,今天在tutorialzine看到这篇教程 Sweet AJAX Tabs With jQuery 1.4 & CSS3的按钮也很漂亮,把demo代码下载下来之后,看了一下也是用CSS定位分割图片的技巧做出来的,和上一篇文章中按钮实现略有不同,下面就来解析怎么做出如下的效果:
首先按钮的HTML代码,
<a class="green" href="#">Tab two <span class="left"></span><span class="right"></span></a>
这个按钮的图片由左中右三部分构成,两边的图片是做好了边框效果的,中间的图片可以无限扩展。即下图显示的效果(注:图像来源于tutorialzine原教程)
源图像是这样的,其实还分成了亮度有区别的上下两部分,使用上文中已经介绍过的CSS技巧达到鼠标移上去高亮的效果。
->
->
->
让我们来看看实现一个按钮核心的CSS代码:
a.blue{background:url(img/blue_mid.png) repeat-x top center; color:#03426e;}
a.blue span.left{ background:url(img/blue_left.png) no-repeat left top;}
a.blue span.right{ background:url(img/blue_right.png) no-repeat right top;}
然后是定义按钮size、边框、边距等的CSS代码:
a,a:visited{
/* Styling the hyperlinks of the tabs as colorful buttons */
float:left;
font-size:18px;
/* display:block allows for additinal CSS rules to take effect, such as paddings: */
display:block;
padding:7px 16px 1px;
margin:4px 5px;
height:29px;
/* Giving positioning */
position:relative;
/* CSS3 text-shadow */
text-shadow:1px 1px 1px #CCCCCC;
}
.left{
/* The left span in the hyperlink */
height:37px;
left:0;
position:absolute;
top:0;
width:10px;
}
.right{
/* The right span in the hyperlink */
height:37px;
right:0;
position:absolute;
top:0;
width:10px;
}
CSS分割背景图片的技巧
一 24th
因为我也不是CSS高手,所以本文的目的是写清楚如何弄明白CSS中分割图片的技巧,说到分割其实并不是真正意义上的剪裁图片,只是已经流行多年了的一种CSS best practise,用一个拼接起来的大的图片资源替代加载多个小图片的一种方法,优点是节约页面的加载速度,不会出现有的图片能显示有的还在加载的情况,这个效果或者性能用firebug可以测试出来。
当然用这个技巧也要选对地方用才行,要是经常需要改动的图像用这个技术还是有点折腾的。
核心的属性是这个:background-position 参数值从w3schools找出来的
Property Values
| Value | Description |
|---|---|
| top left top center top right center left center center center right bottom left bottom center bottom right |
If you only specify one keyword, the second value will be “center”. Default value is: 0% 0% |
| x% y% | The first value is the horizontal position and the second value is the vertical. The top left corner is 0% 0%. The right bottom corner is 100% 100%. If you only specify one value, the other value will be 50%. |
| xpos ypos | The first value is the horizontal position and the second value is the vertical. The top left corner is 0 0. Units can be pixels (0px 0px) or any other CSS units. If you only specify one value, the other value will be 50%. You can mix % and positions |
| inherit | Specifies that the setting of the background-position property should be inherited from the parent element |
这里有篇不错的文章描述的是下面这个例子,将原图竖着的1234显示成横着的4个盒子
CSS: Using Percentages in Background-Image
下面这个例子也是常用到的:
鼠标移到按钮上,用稍微亮一点点的图片显示的悬停效果。
用到的green.png原图:

按钮的代码:
<a class="green" href="#">Green<span> </span></a>
按钮的CSS:
a {
/* The section titles */
display:block;
font-size:21px;
height:34px;
overflow:hidden;
padding:10px 20px 0;
position:relative;
width:200px;
}
a {
text-decoration: none;
}
a:hover{
/* Removing the inherited underline from the titles */
text-decoration:none;
}
a span{
/* This span acts as the right part of the section's background */
height:44px;
position:absolute;
right:0;
top:0;
width:4px;
display:block;
}
a.green{background:url(img/green.png) repeat-x top left; color:#436800;}
a.green span{ background:url(img/green.png) repeat-x top right;}
/* The hover effects */
a:hover{ background-position:bottom left;}
最重要的一行,鼠标移上去的时候设置图像的位置为bottom left,而下半部分的图像比上半部分亮一点。
效果如下:
最后一个按钮样式和第一个其实是一样的,只不过最后是从sprite.png加载过来的,我使用了CSS 图片拼合生成器将四张图拼接起来了,然后再用CSS去定位。
拼接完成之后的图:![]()
jQuery1.4发布: 你必须知道的15个新特性
一 18th
原文地址:jQuery 1.4 Released: The 15 New Features you Must Know
原文作者:James Padolsey
译文地址:jQuery1.4发布: 你必须知道的15个新特性
译文作者:Ji Hao
1. 传递属性给jQuery(…)构造器
早在1.4版本之前,jQuery就支持通过有用的”attr“方法为元素集合添加属性,参数可以传递属性的名-值对或者是一个指定了多个属性的对象。jQuery1.4则支持元素创建的时候传递一个属性对象作为jQuery构造器的第二个参数。
举例来说,如果你要用jQuery1.4创建一个有多个属性的超链接对象,用如下简单的代码即可实现:
jQuery('<a/>', {
id: 'foo',
href: 'http://google.com',
title: 'Become a Googler',
rel: 'external',
text: 'Go to Google!'
});
你可能已经注意到了”text”属性——可能你正怀疑它在这里出现是干啥的,毕竟超链接这玩意没有”text”属性!好吧,jQuery1.4其实统一了它自身的一些方法接受某些属性的行为。上面指定的”text”属性将会引起jQuery调用”.text()”方法,传递”Go to Google!”作为它的参数。
一个现实中更好点的例子:
jQuery('<div/>', {
id: 'foo',
css: {
fontWeight: 700,
color: 'green'
},
click: function(){
alert('Foo has been clicked!');
}
});
“id”被作为一个普通的属性添加,而”css”和”click”属性则触发了相应方法的调用。上面的代码在1.4之前需要链式的这样写出来:
jQuery('<div/>')
.attr('id', 'foo')
.css({
fontWeight: 700,
color: 'green'
})
.click(function(){
alert('Foo has been clicked!');
});
2. 所有的”until”方法!
在1.4中DOM遍历兵工厂里加入了3个新的方法, ”nextUntil“, “prevUntil“和 “parentsUntil“。这3个方法都是朝着某个方向遍历DOM直到指定的选择器条件满足。假定你有如下的水果列表:
<ul>
<li>Apple</li>
<li>Banana</li>
<li>Grape</li>
<li>Strawberry</li>
<li>Pear</li>
<li>Peach</li>
</ul>
如果你要选择”Apple”后面的直到”Strawberry”为止的所有列表元素,可以用如下简单的代码即可实现:
jQuery('ul li:contains(Apple)').nextUntil(':contains(Pear)');
// Selects Banana, Grape, Strawberry
更多请参考文档:prevUntil, nextUntil, parentsUntil
3. 绑定多个事件处理器
除了将多个事件绑定代码链式的写在一起之外,你还可以在一个bind方法里写多个事件绑定代码如下:
jQuery('#foo).bind({
click: function() {
// do something
},
mouseover: function() {
// do something
},
mouseout: function() {
// do something
}
})
这种写法对于.one()方法同样有效
更多请参考文档:.bind(…)
4. 按照每个属性的擦除(Easing)效果
以前你只能为单个动画指定其擦除效果,现在你能为当前定义动画效果的每个属性来指定擦除效果。jQuery包含了两个擦除效果”linear” 和”swing”(默认).其他的效果你需要单独下载。
要为每个属性指定擦除效果,只要把目标属性定义为数组即可,数组的第一个值是你要的动画效果设置的属性值,第二个值是擦除效果函数使用的值:
jQuery('#foo').animate({
left: 500,
top: [500, 'easeOutBounce']
}, 2000);
你还可以在可选参数options对象属性”specialEasing”里按照名-值对的形式定义每个属性的擦除效果:
jQuery('#foo').animate({
left: 500,
top: 500
}, {
duration: 2000,
specialEasing: {
top: 'easeOutBounce'
}
});
原文编辑注:本文的作者James Padolsey谦虚了,这个新特性其实是他的主意。
5. 全新的Live事件代理!
jQuery1.4增加了对”submit“, “change“, “focus” 和”blur“事件的代理支持。在jQuery中我们使用”.live()”方法来代理事件。当你需要对很多元素进行事件处理器注册的时候很有用,或者是当新的元素在将来被加入到DOM文档中我们却需要对其进行事件处理器绑定的情况下(使用”.live()”方法比进行重新绑定更高效)(译者注:更多关于jQuery事件代理请参考:Working with Events, Part 3: More Event Delegation with jQuery)
但是,要小心哪!你必须使用事件的名称,如果你要代理”focus”和”blur”,则必须用”focusin“和”focusout”
jQuery('input').live('focusin', function(){
// do something with this
});
6. 控制函数的上下文
jQuery1.4在其命名空间下提供了一个新的”proxy”函数。这个函数接受两个参数,一个”scope”加一个方法名,或者是一个函数加目标作用域。JavaScript的”this”关键字是非常难掌握的。有时候你希望它是你之前创建出来的对象而不是一个元素。
举个例子:我们有如下的一个”app”对象,它有两个属性,一个”clickHandler”方法和一个config对象:
var app = {
config: {
clickMessage: 'Hi!'
},
clickHandler: function() {
alert(this.config.clickMessage);
}
};
“clickHanlder”方法在被这样调用时”app.clickHandler()”将有”app”对象作为其上下文,也就是说”this”关键字访问的就是”app”。如果我们只是做这样的简单调用,这个工作起来完全没有问题:
app.clickHandler(); // "Hi!" is alerted
让我们来试试将其作为事件处理器来绑定到某个事件上:
jQuery('a').bind('click', app.clickHandler);
当我们点击超链接的时候,它看上去并没有工作(因为没有警告消息弹出来)。这是因为jQuery(以及大多数健全的事件模型)都默认的将事件处理器的上下文设置成了事件目标元素——即:使用”this”访问的其实是被点击的元素。但是我们并不想要这样的结果,我们希望”this”能够访问”app”对象。在jQuery1.4中实现这个再容易不过了:
jQuery('a').bind(
'click',
jQuery.proxy(app, 'clickHandler')
);
现在不管什么时候点击超链接,总是会有”Hi!”这个警告消息。
这个proxy函数返回你的函数的一个包装过的版本,”this”被设置成你指定的值。proxy函数在其他上下文也是有用的,例如传递一个回调函数给其他的jQuery方法或者传递给插件。
更多请参考文档:jQuery.proxy
7. 延迟一个动画队列
你现在可以为你的动画队列设置一个延迟。事实上任何队列都能设置延迟,但是最通用的场景应该是”fx”队列。它允许你在动画之间暂停,而不用再使用回调函数和”setTimeout”写的乱七八糟的。”.delay()”的第一个参数是你要延迟的毫秒数。
jQuery('#foo')
.slideDown() // Slide down
.delay(200) // Do nothing for 200 ms
.fadeIn(); // Fade in
如果你要延迟默认的”fx”队列之外的队列,则需要传递队列的名称作为第二个参数。
更多请参考文档:.delay(…)
8. 检查是否一个元素包含某些元素
jQuery1.4是的检查一个元素或者一个集合是否”.has()”某些元素更加容易。程序上等同于jQuery选择过滤器”:has()”。这个方法将选择当前集合内的符合参数(选择器)的所有元素。
jQuery('div').has('ul');
上面的这行代码将选择所有包含UL元素的所有DIV元素。这个情况下你用选择过滤器 (“:has()”)也够用了,但是这个方法在你需要用编程的方式过滤一个集合的时候是很有用的。
jQuery1.4 在jQuery名称空间下还提供了一个 “contains”函数。这是一个底层的函数,接受两个DOM节点作为参数,返回boolean值以指示第二个元素是否在第一个元素内。看下面的例子:
jQuery.contains(document.documentElement, document.body); // Returns true - <body> is within <html>
更多请参考文档:.has(…), jQuery.contains(…)
9. 解除元素的包装
有”.wrap()”方法已经一段时间了,jQuery1.4新增了”.unwrap()”方法做的是和”wrap()”完全相反的事情。假定有如下的DOM结构:
<div> <p>Foo</p> </div>
我们可以解除段落元素的包装:
jQuery('p').unwrap();
结果DOM结构如下:
<p>Foo</p>
本质上,这个方法简单的去除了任何元素的父元素
更多请参考文档:.unwrap(…)
10. 删除元素而不删除其数据
新的 ”.detach()“方法允许你从DOM文档中删除元素,和”.remove()”方法很像。关键的区别在于这个方法不会删除jQuery保存在这个元素上的数据。这些数据也包含使用”.data()”加上去的数据,也包含使用jQuery事件机制加上去的事件处理器。
这个适用于需要从DOM删除元素然后还要再加入DOM中的情况。被.detach的元素的事件监听器以及其他数据都会被保留。
var foo = jQuery('#foo');
// Bind an important event handler
foo.click(function(){
alert('Foo!');
});
foo.detach(); // Remove it from the DOM
// … do stuff
foo.appendTo('body'); // Add it back to the DOM
foo.click(); // alerts "Foo!"
更多请参考文档:.detach(…)
11. index(…)的增强
jQuery1.4提供了两种新的方法使用”.index()”方法。之前,你只能传递一个元素作为它的参数,改方法返回一个数字只是当前集合中目标元素的位置。
如果不传参数,则返回当前元素在同辈元素中的位置。假定我们有下面的DOM结构:
<ul> <li>Apple</li> <li>Banana</li> <li>Grape</li> <li>Strawberry</li> <li>Pear</li> <li>Peach</li> </ul>
当一个列表选项被点击的时候你想要知道他在其他列表选项中的位置:
jQuery('li').click(function(){
alert( jQuery(this).index() );
});
jQuery1.4还允许你指定一个选择器作为”.index()”方法的第一个参数,这么做将返回当前元素在选择器工作产生的结果集里的位置。
要注意的是这个方法返回整数值,如果选择器没有匹配的结果则返回-1
更多请参考文档:.index(…)
12. DOM操作方法接受回调函数作为参数
现在大多数的DOM操作方法都支持接受一个函数作为单个参数(或者第二个参数,如果是”.css()”和”.attr()”方法的话)。这个传递的函数将会为每个集合中的元素都运行一遍,函数的返回结果将作为DOM操作方法的值。
下面的方法都有接受回调函数作为参数的能力:
- after
- before
- append
- prepend
- addClass
- toggleClass
- removeClass
- wrap
- wrapAll
- wrapInner
- val
- text
- replaceWith
- css
- attr
- html
在这个回调函数里,你可以通过”this”访问当前正在遍历的元素,回调函数的第一个参数的值是当然遍历元素的位置。
jQuery('li').html(function(i){
return 'Index of this list item: ' + i;
});
还有,上面某些方法,在回调函数中还可以接收第二个参数。如果你正在调用一个设值方法(如”.html()”或者”.attr(‘href’)”)你可以访问当前要设置成的值,例如:
jQuery('a').attr('href', function(i, currentHref){
return currentHref + '?foo=bar';
});
正如你所看到的使用”.css()”和”.attr()”方法的时候,你可以传递函数作为第二个参数,因为第一个参数是你想要改变的属性的名称。
jQuery('li').css('color', function(i, currentCssColor){
return i % 2 ? 'red' : 'blue';
});
13. 判断对象的类型
jQuery1.4增加了两个副主函数(在jQuery名称空间下)帮助你检测正在处理的对象类型。
首先,有个”isEmptyObject”,这个函数返回boolean值,指示传递的对象是否为空(没有属性-直接或者间接的)。其次,有个”isPlainObject”,这个函数返回boolean值,指示传递的对象是否是一个纯的JavaScript对象,即使用”{}”或者”new Object()”创建出来的对象。
jQuery.isEmptyObject({}); // true
jQuery.isEmptyObject({foo:1}); // false
jQuery.isPlainObject({}); // true
jQuery.isPlainObject(window); // false
jQuery.isPlainObject(jQuery()); // false
更多请参考文档: isPlainObject(…), isEmptyObject(…)
14. closest()的增强
jQuery的”.closest()”方法现在能接受一组选择器作为参数。这个特性使得你在遍历一个元素祖先节点的时候,或者查找(多个)含有某些特定字符的最近元素的时候更有用。
除此之外,本方法还接受一个context作为其第二个参数,意思是你能控制遍历的深度。这两项增加适用的场景不是很多,但是jQuery内部使用这个方法来获得更好的性能。
更多请参考文档:.closest(…)
15. 新事件!focusIn 和 focusOut
其实上文已经提到,要代理”focus”和”blur”事件,你必须使用新的事件名称”focusin”和”focusout”。这些事件允许你在元素或者某元素的子元素获得焦点的时候干一些事情。
jQuery('form')
.focusin(function(){
jQuery(this).addClass('focused');
});
.focusout(function(){
jQuery(this).removeClass('focused');
});
你还应该注意到的一点是,这些事件是不传播的(冒泡),他们是捕获型事件。意思就是,最外层的元素首先触发该事件,然后才是”目标”元素。
享受最受期望的,最具特色的,到目前为止性能最好的jQuery1.4吧!
好吧,差不多了。我已经把自己认为对你们有影响的新特性涵盖到了。
如果你还没有准备好,应该看看”14 days of jQuery“,一个很酷的为jQuery1.4发布以及jQuery4岁生日准备的在线活动。
还有不要忘记看看新的API 文档!
–
Working with Events, Part 3: More Event Delegation with jQuery
一 17th
原文地址:http://www.learningjquery.com/2009/09/working-with-events-part-3-more-event-delegation-with-jquery
原文作者: Louis-Rémi Babé
正如本系列的第一篇文章中描述的那样,事件代理是利用事件冒泡机制来避免多次绑定事件监听器的一种办法。jQuery1.3和即将到来的jQuery1.4有很多新的特性可以使你的网页中使用事件代理更容易。这篇指南的目标就是帮助你理解这些新特性是如何工作的。
从传统的事件监听到事件代理
既然一个DOM元素上发生的事件会被传递到它所有的祖先元素(或者说是DOM节点)上,那么一个事件监听器就能被绑定在众多DOM元素的单一的那个祖先节点上,而不用为所有的DOM元素每个都单独绑定事件监听器。
看下面的列表先,再考虑下面的问题:
<ul class="myList"> <li class="red">The first item.</li> <li class="green">The second item.</li> <li class="yellow">The third item.</li> <li class="blue">The fourth item.</li> </ul> <p>Class of the last clicked item: <span id="display"> </span> </p>
如果我们想知道被点击的列表元素的class,使用传统事件监听器的jQuery代码应该是这样写的:
$("li").click( function( event ) {
$("#display").text(event.target.className);
});
传递给事件处理器的参数event对象有一个target属性,对应于被点击的元素。
使用事件代理的等价的代码是这样写的:
$("ul").click( function( event ) {
$("#display").text(event.target.className);
});
试试看实际效果:
- The first item.
- The second item.
- The third item.
- The fourth item.
Class of the last clicked item:?
事件代理有两个主要的优势:
1. 使用一个事件监听器代替多个事件监听器明显更快
2. 任何页面加载完成之后动态新插入的元素也将会拥有相同的行为(在Working with Events, Part 1中已经演示过了)
这个转换成事件代理太简单了,因为我们原来的目标只是显示列表元素的class。使用前面的这个代码片断,无序列表本身的class(myList,点列表那个黑点的左边就能看出效果)也会被显示出来。因此,我们还应该判断一下点击的目标是个<li>元素。
$("ul").click( function( event ) {
if(event.target.nodeName == "LI") {
$("#display").text(event.target.className);
}
});
试试看现在的效果:
- The first item.
- The second item.
- The third item.
- The fourth item.
Class of the last clicked item: ?
扫描event.target的祖先节点: .closest() 方法
操作像上面的例子一样如此简单的DOM文档,使用事件代理非常容易搞定。但是如果列表项里还包含子元素的话事情就会变得复杂许多,如下面这个列表元素:
<ul class="myList"> <li class="red"><b>The <i>first <u>item</u></i></b>.</li> <li class="green"><b>The <i>second <u>item</u></i></b>.</li> <li class="yellow"><b>The <i>third <u>item</u></i></b>.</li> <li class="blue"><b>The <i>fourth <u>item</u></i></b>.</li> </ul> <p>Class of the last clicked item: <span id="display"> </span> </p>
这种情况下,如果用户点击了 item 这个单词,event.target就会使<u>.这样的话就必须遍历当前目标元素的所有的祖先节点,来找到我们关心的元素:即<li>.
$("ul").click( function( event ) {
var elem = event.target;
while( elem.nodeName != "LI" && elem.parentNode) {
elem = elem.parentNode;
}
if(elem.nodeName == "LI") {
$("#display").text(event.target.className);
}
});
注意如果用户点击到<li>的外面,我们需要在某个时候停止对祖先节点的循环,这个例子,我们会一直找到根节点(根节点没有父节点)。
jQuery1.3引入了一个.closest() 方法,可以用一行代码替换刚刚的循环。
$("ul").click( function( event ) {
var $elem = $(event.target).closest("li");
if($elem.length) {
$("#display").text($elem.attr("class"));
}
});
试试看用.closest()的效果是不是还正确:
- The first item.
- The second item.
- The third item.
- The fourth item.
Class of the last clicked item:?
传递给.closest()方法的参数是一个CSS选择器,这个选择器将会匹配第一个感兴趣的祖先节点。
上下文参数
可以这样说:当点击事件发生在<li>外面的时候,我们可以在查找的祖先节点在找到<ul>的时候就停止,而不用等到一直文档根节点才停止循环。jQuery1.4将会引入一个可选的context参数到.closest()方法来达到这个目的。
$("ul").click( function( event ) {
$("#display").text($(event.target).closest("li", this).attr("class"));
});
这里的this即事件监听器绑定的<ul>元素。这个可选参数因为避免了查找可能不存在的祖先节点的资源浪费,从而提高了事件代理的性能。
用一行代码实现事件代理: .live()方法
jQuery1.3引入了一个.live()方法,这个方法绑定事件监听器的时候隐式调用.closest()方法来决定事件监听器是不是该被执行。
$("li").live("click", function( event ) {
$("#display").text(
$(event.currentTarget).attr("class")
);
});
注意使用.live()方法的语法是不同的。看上去我们换回了传统的事件绑定的写法。但是最大的不同是,这样.live()方法写的事件监听器对于当前页面中已存在的元素和之后动态插入的元素都是有效的。当然没什么神秘的:.live()方法幕后只是把事件监听器绑定在文档根节点上,然后使用.closest()方法过滤任何event.target.
和传统的事件绑定语法相比一个值得注意的区别是,在事件处理器内部,我们不再使用event的target属性,而是使用currentTarget。事实上,event对象的target可能是一个<li>的子元素,而currentTarget属性则是被隐式的.closest()方法找到的元素。
正如在Event对象文档中将的,currentTarget属性应该是事件冒泡阶段的当前DOM元素,即:事件监听器检测到的元素总是事件监听器被绑定的元素。当使用.live()方法的时候,currentTarget总是文档根节点,你需要再一次用.closest()方法根据target找到感兴趣的祖先节点。
$("li").live("click", function( event ) {
$("#display").text(
$(event.target).closest("li").attr("class")
);
});
如果使用jQuery1.3则必须要写成这样,如果使用jQuery1.4,currentTarget已经在内部被默认修改掉了,可以避免在事件处理器内部使用额外的.closest()方法来查找目标。
上下文参数
在jQuery1.3中所有使用live方式的事件监听器实质上都是绑定在DOM文档根节点上的。因为一个事件监听器检测到的所有事件都将触发执行隐式的.closest()方法,即使事件的目标元素不是我们所感兴趣的,所以这将影响网页的性能。
在jQuery1.4中.live()方法利用jQuery对象的上下文参数,它能够将事件监听器绑定到文档的一个具体的我们关心的元素上:
$("li", $("ul")[0]).live("click", function( event ) {
$("#display").text(
$(event.currentTarget).attr("class")
);
});
上下文参数就是创建jQuery对象的第二个参数。为了对.live()方法有用,它必须被置成一个纯的DOM元素,这就是为什么$(“ul”)后面跟了个[0]的原因。Brandon Aaron 有一篇有用的文章详细解释了上下文参数 。
解除.live()绑定: .die()方法
就像.bind()和.unbind()互为补充允许事件的绑定和解除绑定,.live()也有与其互为补充的.die()方法。
和.bind()不一样的是.live()不支持使用 namespaced events。
处理不冒泡的事件
事件代理对于有些事件不能实现,因为这些事件不会冒泡。jQuery1.4允诺.live()方法依然能够支持这些不冒泡的事件。这个已经实现,而且将会在接下来的第四篇文章中被涵盖到。
本文包含的脚本:
—
Working with Events, part 2
一 12th
原文地址:http://www.learningjquery.com/2008/05/working-with-events-part-2
原文作者: Karl Swedberg
在我的上一篇文章中,描述了一个常见的问题,即事件对新加入HTML文档的元素表面上不工作的情况,不管这个元素是通过ajax方式还是修改DOM的方式加入进来的。我们也检查了一个解决此问题办法:事件代理。使用这种办法,我们将事件绑定到一直在DOM中的容器元素上然后再检查事件发生的目标元素。
Cloning Nodes
这一次,我们将一起看看解决此问题的另一个方法:重新绑定事件处理器。但是这么做之前,我应该要提一下,在jQuery1.2中事件处理器能够随着元素一起被克隆。看这个无序列表:
<ul id="list3" class="eventlist"> <li>plain</li> <li class="special">special <button>I am special</button></li> <li>plain</li> </ul>
我们可以拷贝一个<li class=”special”>然后插入到被拷贝的元素后面,同时取得任何绑定在原来元素上的事件处理器。jQuery提供的这个.clone()方法不会为我们把这任务都干了,它只会做元素的拷贝
$(document).ready(function() {
$('#list3 li.special button').click(function() {
var $parent = $(this).parent();
$parent.clone().insertAfter($parent);
});
});
试试看:
- plain
- special
- plain
正如你所看到的,原按钮能够继续创建新的列表元素,但是“动态生成”的列表元素内的按钮不能创建新的按钮。
为了使得事件处理器也能被拷贝到新的元素上,我们只需要传递true给clone方法:
$(document).ready(function() {
$('#list4 li.special button').click(function() {
var $parent = $(this).parent();
$parent.clone(true).append(' I\'m a clone!').insertAfter($parent);
});
});
注:我加了个.append(‘ I\’m a clone!’)只是为了让发生的事情看上去更明显。
试试看:
- plain
- special
- plain
当我们想拷贝已有元素及其事件处理器的时候使用.clone(true)尤为有效,但是还有很多其他和克隆不相关的情况我们也希望事件处理器能够被保留。
Re-binding基础
re-binding的概念相当直接:我们定义一个绑定事件处理器的函数,然后每当有新元素加入的时候调用这个函数。例如:上面的无序列表,我们首先创建一个addItem函数,这个函数注册一个事件处理器,这个事件处理器的意图是点击事件发生的时候,添加一个新的列表元素。
function addItem() {
$('#list5 li.special button').click(function() {
var $newLi = $('<li>special and new <button>I am new</button></li>');
$(this).parent().after($newLi);
});
下一步,当DOM加载完成的时候调用这个函数:
$(document).ready(function() {
addItem();
});
最后我们在click事件处理器内再次调用这个函数,这样做的话,新加入的元素也能绑定事件处理器。
我们在多加一个事件处理器绑定在按钮上,但是不为这个做re-bind,为了试验的时候能看出区别。
为#list5内button做处理的全部的代码:
function addItem() {
$('#list5 li.special button').click(function() {
var $newLi = $('<li>special and new <button>I am new</button></li>');
$(this).parent().after($newLi);
addItem();
});
}
$(document).ready(function() {
addItem();
// non-rebinding click handler ...
$('#list5 li.special button').click(function() {
$(this).after(' pressed');
});
});
试试看(IE下还是不要点击了,死循环啊!!!):
- plain
- special
- plain
可以看到每当第一个列表项的按钮被点击的时候”pressed”会被添加到这个列表项的后面,但是不会被添加到我们动态创建的列表元素的后面。另一方面,动态创建出来的按钮也能创建新的列表元素,因为函数已经被重新绑定过了。
然而,如果我们点击不只一次这个按钮,就会发现我们刚刚做的这一切产生了不受欢迎的结果。每点击一个按钮都会再绑定一次事件处理器,产生乘法的效果,即:第1次点击产生1个额外的列表项,第2次点击产生2个,第2次点击产生4个,循环下去。
Unbind和Bind
为了避免乘积型的绑定,我们可以先解除绑定然后再重新绑定。所以上面代码的第二行,我们将替换原来的
$(‘#list5 li.special button’).click(function() {
为
$(‘#list6 li.special button’).unbind(‘click’).bind(‘click’,(function() {
注意这里的.bind()方法,这是jQuery通用的事件绑定处理方法。所有其他的诸如.click(), .blur(), .resize()等等都是等价的.bind(‘event’)的简写形式。
全部的代码如下,作为对比再看一下非重新绑定的事件处理器代码
function addItemUnbind() {
$('#list6 li.special button')
.unbind('click')
.bind('click', function() {
var $newLi = $('<li>special and new <button>I am new</button></li>');
$(this).parent().after($newLi);
addItemUnbind();
});
}
$(document).ready(function() {
addItemUnbind();
// non-rebinding click handler
$('#list6 li.special button').click(function() {
$(this).after(' pressed');
});
});
看看这次它怎么工作?
- plain
- special
- plain
不幸的是,我们尝试解除函数addItemUnbind()绑定的方法过头了,把”non-rebinding”的点击处理器(甚至它连一次运行的机会都没得到)也解除了绑定,(证据可以从运行结果看出来,因为在”I am special”的按钮后面没有加上”pressed”的文本)。很明显,我们将不得不对我们需要解除绑定的东西更加小心。
使用事件名称空间
一种避免过度的解除绑定的方法是为绑定和解除绑定的相应点击事件加上“名称空间”。所以,不同于.bind('click') 和 .unbind('click)例如我们用这样的方法.bind('click.addit') 和 .unbind('click.addit').这是一个代码的示例除了使用有名称空间的事件之外和之前的代码基本一样。
function addItemNS() {
$('#list7 li.special button')
.unbind('click.addit')
.bind('click.addit', function() {
var $newLi = $('<li>special and new <button>I am new</button></li>');
$(this).parent().after($newLi);
addItemNS();
});
}
$(document).ready(function() {
addItemNS();
// non-rebinding click handler
$('#list7 li.special button').click(function() {
$(this).after(' pressed');
});
});
终于,我们有了期望得到的这一堆按钮和其被加上的正确的事件行为。(IE下还是不要点击了,这个也会产生死循环啊!!!):
- plain
- special
- plain
请阅读Brandon Aaron的文章以了解更多关于事件名称空间的知识: Namespace Your Events.
Bonus:通过函数引用的方式解除绑定
如果你一直看到这里,证明你是个非常有耐心的人,所以我将特别奖励一个重新绑定的终极方法。除了使用事件名称空间之外,我们还可以在调用.bind()和.unbind()的方法时,增加第二个参数以引用函数。我们需要稍微调整一下代码防止递归,最终下面的代码应该是OK的.
function addItemFinal() {
var $newLi = $('<li>special and new <button>I am new</button></li>');
$(this).parent().after($newLi);
$('#list8 li.special button')
.unbind('click', addItemFinal)
.bind('click', addItemFinal);
}
$(document).ready(function() {
$('#list8 li.special button').bind('click', addItemFinal);
// non-rebinding click handler
$('#list8 li.special button').click(function() {
$(this).after(' pressed');
});
});
注意这里在bind和unbind里的addItemFinal是没有括号的,因为我们只是在引用函数,而不是在做函数调用,让我们最后再测试一下吧:
- plain
- special
- plain
实现此功能可选的jQuery插件
有三个很棒的插件能为我们干上面的事:
- Live Query by Brandon Aaron
- Listen by Ariel Flesler
- Intercept by Ariel Flesler
如果本文让你困惑的或者你只想要一个快速的测试过的解决方案,你一定要试试这几个插件中的一个,每个插件工作起来略有不同,但是都用样精彩。
更新:使用事件名称空间的添加删除例子
回复Nick Johns 下面的评论,我做了一个允许增加和删除的例子,代码基于我们所讲的”使用事件名称空间”的方法如下:
function addRemoveItemNS() {
var $newLi = $('<li>special and new <button>I am new</button> <button>remove me</button></li>');
$('#list9 li.special')
.find('button.addone')
.unbind('click.addit')
.bind('click.addit', function() {
$(this).parent().after($newLi);
addRemoveItemNS();
})
.end()
.find('button.removeme')
.unbind('click.removeit')
.bind('click.removeit', function() {
$(this).parent().remove();
});
}
$(document).ready(function() {
addRemoveItemNS();
});
我为初始的按钮家了一个”addone”的class,否则列表就和其他一样了,试试下面的例子:
- plain
- special
- plain
<ul class="eventlist" id="list9"> <li>plain</li> <li class="special">special <button class="addone">I am special</button></li> <li>plain</li> </ul>



