新浪是一个没有节操的公司,微博是一个没有节操的产品。
创建和监听自定义事件
做前端开发的筒子们,有没有经常觉得原生的事件类型不够用?今天就来介绍一下如何创建自定义的事件类型吧。
举个例子,你有没有实现过像“长按”这样的需求?这是一个挺常见的需求,可是原生的事件类型里并没有它。以前通常的做法是用 mousedown 和 mouseup 来模拟。同时监听这两个事件,记录两次事件触发的时间戳,得出按下时长,再决定是否执行后续动作。这个方案还不错,思路清晰,实现容易。
好,现在新的需求来了(该死的 PM!),页面上有两个需要监听长按状态的元素,怎么办?这好办,写具名函数嘛,复用一下还是很容易的。别急,PM 又来了…… 两个元素触发长按所需的时长不一样,怎么办?时长还要能方便的修改,怎么办?在心里咒骂 PM 肯定少不了,但需求还是得给他实现了。好,修修改改勉强又能用了。可是这么一看,代码写得真丑啊…… 万一待会 PM 又改需求怎么办?
有没有办法把这个需求实现得简单好看又灵活呢?当然可以!创建一个自定义事件吧骚年!
在文档对象模型(DOM)中有 createEvent,initEvent,dispatchEvent 这么一组方法可以很方便的创建,初始化和触发一个自定义的事件。以前面提到的长按需求为例,我们首先需要创建一个事件对象。
var evt = document.createEvent('Event');
在这里我基于最基础的事件模块(Event)创建了一个对象,如果你需要对事件进行精细地控制,可以选择更具体的事件模块(UIEvents、MouseEvents、MutationEvents、HTMLEvents 等)。
接下来就是初始化这个事件,三个参数分别是事件类型(字符串)、是否冒泡(布尔)、是否可取消(布尔)。
evt.initEvent('longpress', true, true);
注意这里初始化事件的方法名跟上一步所选的事件模块是有关的,具体可以参照 MDN 这个表格。
然后我们要做的事,就是在特定条件(按下时间超过 1 秒)下触发这个事件。
var timer;
button.addEventListener('mousedown', function() {
timer = Date.now();
}, false);
button.addEventListener('mouseup', function() {
if(Date.now() - timer > 1000) {
evt.duration = Date.now() - timer;
button.dispatchEvent(evt);
}
}, false);
检测鼠标按下和松开之间的时间间隔,大于 1 秒(1000 毫秒)则触发自定义的 longpress 事件。在这个过程中我们还可以为自定义的事件对象增加属性,比如上面例子中的 duration 属性,即鼠标按下的时长。
最后只需要像监听原生的事件那样,监听这个自定义的事件,太简单了。
button.addEventListener('longpress', function(e) {
console.log('Pressed for ' + e.duration + ' milliseconds.');
}, false);
到这里就结束了吗?当然没有,别忘了 PM 的需求,触发事件的时长要可以方便的修改,还要能支持多个实例。
不过我想你应该也知道要怎么做了,就是把自定义事件的这部分代码包装一下。
function enableLongPress(target, threshold) {
var timer;
var evt = document.createEvent('Event');
evt.initEvent('longpress', true, true);
target.addEventListener('mousedown', function() {
timer = Date.now();
}, false);
target.addEventListener('mouseup', function() {
if(Date.now() - timer > threshold) {
evt.duration = Date.now() - timer;
target.dispatchEvent(evt);
}
}, false);
}
把需要监听的 DOM 元素和触发事件的条件作为参数传入,调用这个函数,就完成了事件的自定义。
enableLongPress(button, 1000);
事件的监听部分完全不用改动。
button.addEventListener('longpress', function(e) {
console.log('Pressed for ' + e.duration + ' milliseconds.');
}, false);
增加实例易如反掌。
enableLongPress(anotherButton, 2000);
anotherButton.addEventListener('longpress', function(e) {
console.log('Pressed for ' + e.duration + ' milliseconds.');
}, false);
最后给个演示,大家随便玩。
绝对定位的瀑布流
写了个瀑布流这种事,我会随便到处跟人讲吗?
绝对定位,页面宽度自适应,demo 在 这里。IE6/7 的用户就别看了,我敢保证,你啥都看不到的。IE8/9 没有动画效果。
似乎还发现一个 computedStyle 的奇妙 bug,有时间详细写一下……
几个经典排序算法的 JavaScript 实现
作为一个学化学转行做互联网的人,缺乏计算机科学的基础训练一直是我的硬伤。上星期花时间专门看了一些排序类算法,并且决定要写几个实现出来,免得一转眼又忘了。经过周末休息日的死磕,我终于在理解算法的基础上,用 JavaScript 写出了归并排序和快速排序的实现。在此记录一笔,以示备忘……
不过在开始之前,还是先来看看我长久以来唯一会写的冒泡排序吧……
为方便起见,我扩展了一个交换数组项的 swap 方法。
if(!Array.prototype.swap) {
Array.prototype.swap = function(a, b) {
var temp = this[a];
this[a] = this[b];
this[b] = temp;
return this;
};
}
当然我们也可以用更好的方式来扩展原型,这是题外话了。
if(!Array.prototype.swap) {
Object.defineProperty(Array.prototype, 'swap', {
value: function(a, b) {
var temp = this[a];
this[a] = this[b];
this[b] = temp;
return this;
}
});
}
接下来就是真正的冒泡排序了。
var bubbleSort = function(arr) {
var len = arr.length;
if(len > 1) {
for(var i = 1; i < len; i++) {
for(var j = i; j > 0; j--) {
if(arr[j] < arr[j-1]) {
arr.swap(j, j-1);
} else {
break;
}
}
}
}
return arr;
};
稍作调整也可以改成数组方法的形式,或者参考上面提到的 defineProperty 的形式。
Array.prototype.bubbleSort = function() {
var arr = this,
len = arr.length;
if(len > 1) {
for(var i = 1; i < len; i++) {
for(var j = i; j > 0; j--) {
if(arr[j] < arr[j-1]) {
arr.swap(j, j-1);
} else {
break;
}
}
}
}
return arr;
};
接下来看看比冒泡更快的归并排序吧。这个算法采用了“分而治之(divide and rule/conquer)”的思想。算法的实现由“分”和“治”两个部分构成,把一个大问题分割成若干个容易解决的小问题,然后递归地逐个击破,从而解决整个大问题。
var merge = function(left, right) {
var arr = [];
while(left.length && right.length) {
if(left[0] < right[0]) {
arr.push(left.shift());
} else {
arr.push(right.shift());
}
}
return arr.concat(left, right);
};
var mergeSort = function(arr) {
var len = arr.length;
if(len > 1) {
var index = Math.floor(len/2),
left = arr.slice(0, index),
right = arr.slice(index);
return merge(mergeSort(left), mergeSort(right));
} else {
return arr;
}
};
由于 JavaScript 本身并未对递归进行优化,而可以用递归实现的算法,也都可以用非递归的方式实现,因此自然有人考虑用迭代来实现归并排序。
var mergeSortIteration = function(arr) {
var len = arr.length;
if(len > 1) {
var work = [];
for(var i = 0; i < len; i++) {
work.push([arr[i]]);
}
work.push([]);
for(var j = len; j > 1; j = Math.ceil(j/2)) {
for(var k = 0, l = 0; l < j; k++, l += 2) {
work[k] = merge(work[l], work[l+1]);
}
work[k] = [];
}
return work[0];
} else {
return arr;
}
};
最后来说说快速排序。这应该是实际生产和各种算法类面试题中最常见的排序算法了吧。快排也是一个典型的“分而治之”算法,用递归来写实现非常简单,也很容易理解。
var quickSort = function(arr) {
var len = arr.length;
if(len > 1) {
var pivot = arr[0],
left = [],
right = [];
for(var i = 1; i < len; i++) {
if(arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return quickSort(left).concat(pivot, quickSort(right));
} else {
return arr;
}
};
这个实现有一个不足,就是会占用额外的内存空间,这跟归并算法是一样的。而归并算法很稳定,极端情况下的耗时要优于快排,因此这个快排的现实意义并不大。实际生产中大量使用的快排算法,大多是在比较过程中直接对数组元素进行交换操作,避免了占用额外的空间,这就是所谓的原地(in-place)快排。
var partition = function(arr, start, end) {
var index = start,
pivot = arr[start];
arr.swap(start, end);
for(var i = start; i < end; i++) {
if(arr[i] < pivot) {
arr.swap(i, index);
index++;
}
}
arr.swap(index, end);
return index;
};
var sorting = function(arr, start, end) {
if(end - start > 1) {
var index = partition(arr, start, end-1);
sorting(arr, start, index);
sorting(arr, index+1, end);
}
return arr;
};
var quickSortInPlace = function(arr) {
var len = arr.length;
if(len > 1) {
return sorting(arr, 0, len);
} else {
return arr;
}
};
好,本文到此也应该结束了。虽然 JavaScript 数组已经有了用快排实现的原生 sort 方法,我还是希望本文提到这些算法的思路和实现会对你有帮助。
为 WordPress 官方插件目录的页面添加图片横幅
WordPress 官方插件目录最近一次改版以后,有些插件页面(比如 Hello Dolly)增加了一个图片横幅,看起来效果很不错哦。

那怎么让自己光秃秃的插件页面也能用上好看的图片横幅呢?其实很简单,只要制作一张名为 banner-772×250 的图片,尺寸当然就是宽 772 像素,高 250 像素啦,图片格式可以是 png 或 jpg。在本地的 SVN 目录下新建一个名为 assets 的文件夹,把刚才制作好的图片放进这个文件夹,通过 SVN 提交到官方的插件仓库,稍等一会就可以看到效果啦。

我的 txt2img 山寨长微博插件在添加图片横幅以前是这样的:

对比一下添加横幅以后的样子。怎么样?有没有觉得下载按钮更诱人了?

WP MashSocial Widget 插件的作者就在 2 月 16 日添加了图片横幅。看看插件的下载数据,怎么样?效果很惊人吧!

一图胜千言!插件作者们,还等什么?赶紧给你的插件页面也填上图片横幅吧。