Magic canvas & PNG

用 canvas 配合 PNG 可以做出什么好玩的东西呢?

昨天阿里中文站的方凳会,我为大家分享了这个主题。这是我第一次在方凳会上做大分享,也算迈出了这一步,以后会努力多在这个舞台上表现。

主要讲了运用 canvas 来做动态 favicon 和用 PNG 来打包字符串(js) 然后前端通过 js 对这个图片解码。

PPT 在 http://share.allenm.me/fd/ ,有兴趣的同学可以来看,我就不在这里重复一遍内容了。

javascript自定义事件(event)

曾经有次在一个jQuery交流群里,有人问,能不能自定义事件,然后监听事件,比如监听一个变量的变化,当时我屁颠屁颠的给别人说,这个不大可能。现在正是为我当时的无知和逞能感到羞耻,以后再也不轻易的否定一个事情,除非我有完全的理由否定,自己不知道,不是否定的理由.

下面我就来实现那次别人说到的自定义事件,监听某个变量的变化:

标准浏览器(firefox,chrome,safari,opera等)的实现非常简单,自然,有一个document.createEvent()的函数来专门创建自定义事件,使用起来也很简单,等会儿看代码一看就明白了。

var event = document.createEvent(type);

  • event is the created Event object.
  • type is a string that represents the type of event to be created. Possible event types include “UIEvents”, “MouseEvents”, “MutationEvents”, and “HTMLEvents”. See Notes section for details.

简单的说来,自定义事件到激发这个事件,需要document.createEvent(),event.initEvent(),element.dispatchEvent()这三部,分别是创建事件对象,初始化事件对象,触发事件。先给个简单的例子。

	function foo1(){
		console.log("foo1 is execute");
	}
	function foo2(){
		console.log("foo2 is execute");
	}
	var ev=document.createEvent('HTMLEvents');
	ev.initEvent('fakeEvent',false,false);
	document.addEventListener("fakeEvent",foo1,false);
	document.addEventListener("fakeEvent",foo2,false);

在标准浏览器里的console里执行 document.dispatchEvent(ev); 就可以看到console里显示出来了 foo1 is execute和 foo2 is execute

自定义事件已经实现了,那么怎么实现监听某个变量的变化呢?我们可以使用一个特定的函数来给变量赋值,在这个函数中,除了执行给变量赋值的操作外,还激发事件。为了约束程序员直接对变量复制,使用下面的代码来强制程序员不能直接对该变量赋值

var idChange=function(){
		var id=1;
		return {getId:function(){return id;},
				setId:function(a){id=a;
							document.dispatchEvent(ev);
		         }}
	}();

这样就只能通过idChange.setId()的方法对id赋值,通过 idChange.getId()来获得id的值,并且在赋值的过程中激发事件。

在标准浏览器里,现在已经实现了目标,遗憾的是IE并不支持document.createEvent()的方法,在JavaScript权威指南第五版中,作者给出IE支持的 document.createEventObject()和event.fireEvent()方法,但是经过测试,fireEvent并不能用于自定义事件,传给它的参数只能是在IE已经定义了的事件(MSDN文档写的不明不白的,看的很恼火)。在Dean Edwards的博客中看到了一个方法这里就直接拿来用了,性能方面值得考虑,请阅读这篇博客下面的评论部分,就会知道怎么改进这个方法的性能,为了方便,我这里直接用他博客中给的方法了,为了更直观,也添加了一个addLog()函数,来直接在页面中记录事件的发生, 为了配合addLog函数,页面中要有一个id为 log 的div ,具体代码如下:

function foo1(){
		addLog("foo1 is excute");
	}
	function foo2(){
		addLog("the id is "+idChange.getId()+" now!");
	}
	if(document.createEvent){ //This is for the stand browser.
		var ev=document.createEvent('HTMLEvents');
		ev.initEvent('fakeEvent',false,false);
		document.addEventListener("fakeEvent",foo1,false);
		document.addEventListener("fakeEvent",foo2,false);
	}else if(document.attachEvent){ //This is for the damn IE
		document.documentElement.fakeEvents = 0; // an expando property
		document.documentElement.attachEvent("onpropertychange", function(event) {
			if (event.propertyName == "fakeEvents") {
				foo1();
			}
		});
		document.documentElement.attachEvent("onpropertychange",function(event){
			if(event.propertyName == "fakeEvents"){
				foo2();
			}
		});
	}
	function addLog(log){
		var logDiv=document.getElementById('log');
		var p=document.createElement("p");
		p.appendChild(document.createTextNode(log));
		logDiv.appendChild(p);
	}
	var idChange=function(){
		var id=1;
		return {getId:function(){return id;},
				setId:function(a){
               id=a;
               if(document.dispatchEvent) document.dispatchEvent(ev);
	       else if(document.attachEvent)     document.documentElement.fakeEvents++; //This for IE
					}}
	}();

现在我们就已经实现了文章开头想要达到的需求,在浏览器地址栏里执行javascript:idChange.setId(8)就可以看见效果了。监听某个变量的变化了,你可能会想到,干嘛不直接在idChange.setId()中直接执行 foo1(),foo2(),是的,这样也可以实现,但是这样不是真正的事件,如果你在代码中多处需要注册监听器到这个自定义事件,并且随时需要取消注册,显然这种事件的方法更容易管理一些,另外事件还有另外的好处,代码更稳定,请参看上面给出的Dean Edwards的那篇博客Callbacks vs Events

IE文本域中回车自动提交表单

当表单中只有一个文本域的时候,在文本域输入完后按回车,IE会自动帮你提交,(谢谢Andre提醒,其实这种情况在其他浏览器里也会提交的,我测试了firefox,opera, chrome。safari没测试,不过表现应该和chrome是一样的,所以主流浏览器都会自动提交,而不仅仅是IE)哪怕没定义 submit按钮,也会提交。

这个是今天在解决一个其他BUG的时候,了解到的这个问题,虽然要解决的BUG和这个没关系,这个一会儿再说。

先说这个怎么解决吧,在form里建个隐藏的input标签就可以了,可以这样  <input style:”display:none”/> 。

另外我要说说,大家要慎用IE tester这个东西啊,太不靠谱了,在这个里面执行JS和原生浏览器执行,有很大差别。今天想用这个来解决IE6的一个BUG,一直没进行到要真正解决的部分去,因为我一直以为是上面的那个问题带来的BUG,只到我用机器上的IE8运行了一段测试代码,又用IE tester新建IE8来运行,结果不一样,当时就囧了,我意识到做了一个晚上的无用功了。

只好马上用Virtual BOX安装 XP了,用原生的IE6来解决问题,果然,问题不在那里,很快解决了BUG。

所以建议大家一定要慎用IE tester,尽量用原生的浏览器来测试。

然后今天遇到的实际的BUG,是因为在js中对字符串处理的时候使用的非标准的方法。我想返回字符串中指定位置的字符,我使用了类似PHP的方法:

var a=’hello world’;

var b=a[0];

这样在firefox中是有用的,b是 ‘h’ ,但是在IE下是不起作用的,翻了手册才知道,标准的方法是 charAt().

var b=a.charAt(0);

要这样才是标准的方式,换了这个后,马上就解决了要解决的BUG。

《JavaScript: The Good Parts》讲解闭包的章节中一处代码小错误

39页,给出了一个讲解闭包的代码:

var add_the_handlers=function(nodes){
var i;
for(i=0;i

这个函数的目的是: Make a function that assigns event handler functions to an array of nodes the right way. When you click on a node, an alert box will display the ordinal of the node.

但是代码中alert的却是'e',这个是event,如果是这样的话,点击每个节点,都是alert的 '[object MouseEvent]'
我们只要把alert(e)改为alert(i)就正确了,就可以alert出这个节点在所有选中的节点中的序数了。

javascript两种创建函数的方式的不同

javascript常见的两种创建function的方法是:

function testFunc(a){
    alert(a);
}

另外一种

var testFunc=function(a){
    alert(a);
}

对于第一种方式,当为创建全局执行环境而进行变量实例化时,会根据上面的函数声明创建相应的函数对象。

第二种方式,在全局执行环境的变量实例化过程中,会先为全局对象创建一个命名属性。而在计算赋值语句之前,暂时不会创建函数对象,也不会将该函数对象的引用指定给全局对象的命名属性。但是,最终还是会在全局执行环境中创建这个函数对象。

我们来看个例子来理解他们的不同

先看第一个例子:

testFunc('test');
function testFunc(a){
    alert(a);
}

在这种情况下,alert会执行,虽然函数定义在使用这个函数的代码之后,但是由于在创建全局执行环境的时候就会创建这个function,所以执行的时候是有效的,我们接着看这种情况

testFunc('test');
function testFunc(a){
    alert(a);
}

这种情况下,不会alert出’test’如果你有使用firebug等调试工具的话,会报错,提示testFunc不是个function。接着看:说明在执行 testFunc(‘test’)的时候,testFunc这个函数还被有被创建。

var testFunc=function(a){
    alert(a);
}
testFunc('test');

这种情况下,也会alert出’test’ 因为最终这个function还是会被创建,只是创建的时间和第一种函数创建方法不同而已。

javascript预加载技术

今天在遇到AJAX响应过慢的时候,我当是突然有个想法,因为我的这个应用,是明确的可以知道下一个XHR传递的参数的,所以,我可以先加载下下一个的,然后,需要加载下一个的时候,实际上,要马上显示出来的已经读取过来的,我就可以马上拿来用了。

当时为这一闪念感到高兴呢,但是想想,这个应用又不负责,不应当这么慢的,还是去找瓶颈优化去了,把数据库优化了。而且这个想法应该很多前人都想到了,毕竟也不是什么负责的技术。

从川大听完腾讯的宣讲会,回来的公交车上,我想了想,好像听说过一个预加载技术的名字,我想的应该就是这个吧,回来后上网查了一下,一般这个用来预加载图片,因为加载图片是比较慢的,像我这种加载200B左右的json的,估计没人用。