elementFromPoint() 函数

今天遇到了个好玩的js函数,看题目已经了解的就可以忽略了,或许是我太土了。这个函数我在 权威指南里都没找到,晚上回来后,查了下文档,mozilla的文档W3C的

这个函数是干什么的呢?通过这个函数,我们可以通过找到文档中任意一点所对应的 element ,这个是我在看 firebug 源码的时候发现的,写了简单的代码测试了一下,目前我机器上的浏览器都支持,分别是 firefox3.6+, chrome 5+, opera 10+, IE 8.其他浏览是是否支持未知。

用法就是 var el= document.elementFromPoint(offsetX, offsetY); 就可以获得元素了,你可以把我下面的代码加入到任意网页中,看看效果便知,为了在 非firefox浏览器测试方便,加入了 firebug lite

贴代码:


<script type='text/javascript'
        src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
		<script type="text/javascript">
			if(document.addEventListener){
				document.addEventListener("mousemove",logThis,true);
			}else{
				document.attachEvent("onmousemove",logThis);
			}
			var time=0;
			function logThis(e){
				if(new Date()-time>500){
					var el= document.elementFromPoint(e.clientX, e.clientY);
					console.log(el);
					time=+new Date();
				}
			}
		</script>

根据 firebug 的源码显示,在 opera和safari中有滚动条的情况下,会表现的不一致,需要 X Y 分别要加上scroll.x和 scroll.y。firebug 里的代码是这样的

var scroll = this.getWindowScrollPosition();
return this.document.elementFromPoint(x + scroll.left, y + scroll.top);

var scroll = this.getWindowScrollPosition();

return this.document.elementFromPoint(x + scroll.left, y + scroll.top);

有兴趣的可以去看看文档,明天要去舟山玩,写的条理比较乱,不过这个函数也没什么难度,应该很容易就看懂了。

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

window.onload 和 DOMContentLoaded

相信写js的,都知道window.onload吧,但是并不是每个人都知道DOMContentLoaded,其实即使你不知道,很有可能你也经常使用了这个东西。

一般情况下,DOMContentLoaded事件要在window.onload之前执行,当DOM树构建完成的时候就会执行DOMContentLoaded事件,而window.onload是在页面载入完成的时候,才执行,这其中包括图片等元素。大多数时候我们只是想在DOM树构建完成后,绑定事件到元素,我们并不需要图片元素,加上有时候加载外域图片的速度非常缓慢。

我们可以写代码来简单测试一下这两种事件:

javascript代码(引入了jQuery1.4.1):

if(document.addEventListener){
	function DOMContentLoaded(){
		$("#status").text("DOM is ready now!");
	}
	document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
}
window.onload=function(){
			$("#status").text("DOM is ready AND wondow.onload is excute!");
}

HTML 代码 body 部分:

<h1> DOM READY's TEST </h1>
<img src="./delay.php" alt="delay image" />
<p id="status"> DOM is not ready </p>

为了清楚的看到效果,特意写个简单的php文件,提供图片延时加载,代码如下:

sleep(5);
header('Location:./delay.png');

在firefox和chrome以及opera中都可以清楚的看到,在图片未载入之前,id为status的段落已经显示了“DOM is ready now!”,然后等5秒钟后,图片加载完成后,此段落显示”DOM is ready AND wondow.onload is excute!”

此代码并不能在IE中工作,一方面是因为我使用的是addEventListener,开始前做了个判断,不存在此方法则不添加此事件。这样做的原因是IE确实是不支持DOMContentLoaded这个事件的,为了代码的简单,就没为IE写了。虽然IE没有此事件,但是我们却可以来模拟这个事件,常见的方法是判断element的doScroll如果成功则说明DOM载入完成。

常见的库中都提供了此事件的兼容各个浏览器的封装,如果你是个jQuery使用者,你可能会经常使用$(document).ready();或者$(function(){}) 这都是使用了DOMContentLoaded事件

延伸阅读:

合理使用DOMContentLoaded可以使页面更早的响应用户操作,希望IE早日原生实现这个事件,或者希望IE灭亡

WEB前端优化

刚刚读完了《高性能网站建设指南》,作者给出了网站前端优化的14条建议,第一次系统的了解到了前端性能优化需要做的事情。

简单说来,这14条建议是下面这些:

  1. 减少HTTP请求(因为HTTP1.1协议规定同一域名下默认有两个HTTP同时下载,这条是最基本的,最应该去做的)
  2. 使用内容发布网络(主要是针对静态内容,具体请搜索CDN)
  3. 添加Expires头。(告诉浏览器来缓存那些不容易变化的内容,来加快“回头客”的访问速度)
  4. 压缩组件。(使用gzip来压缩组件降低数据传输量)
  5. 将样式表放在顶部(避免白屏和闪屏等情况发生)
  6. 将脚本放在底部(脚本会阻塞并行下载,所以某些脚本可以放到底部,让页面先显示出来)
  7. 避免CSS表达式(CSS表达式计算频繁,用其他方式来实现需要CSS表达式的效果)
  8. 使用外部JavaScript和CSS(利用缓存来减少这些组件的下载)
  9. 减少DNS查找(DNS查询是要耗时间的,减少DNS查找和增加并行下载是相悖的,这点需要权衡一下)
  10. 精简JavaScript(使用工具删掉不必要的空格,注释等)
  11. 避免重定向(redirect需要耗时间,尽量避免这种方式)
  12. 移除重复脚本(大网站,开发人员多,可能会带来重复脚本的问题,可以借助服务端程序来消除)
  13. 配置ETag(ETag经常会破坏缓存规则,他比Expires头权限高。合理配置或者禁用之)
  14. 使Ajax可缓存(分析Ajax请求,缓存可以缓存的,同时合理使用一些主动Ajax,以及参照前面指导原则优化Ajax请求)

作为前端开发人员,应该具有前端优化的技能,这些优化花不了多少成本,却相当有效,比改进后台代码的某个算法带来的性能增强性价比要高多了。

网站打开速度快才是王道,速度是用户接触到这个网站的第一体验!!!

上面这些指导原则在实际生产的时候都是需要好好分析的,看自己的网站适合怎么样。

晚上尝试使用了一下HTTP长连接技术

为了不去想那些乱七八糟的事情,我基本上整个晚上都在写代码。写了一个简单的即时聊天室。性能应该很低。

只是尝试使用了一下HTTP长链接技术。等明天晚上回来放上来。同时有想了解这种东西的,可以看看我写的丑陋的代码。代码量很小,只实现了最基本的功能,目的只是为了了解是怎么实现像friendfeed这样的即时性的东西。

平时一直用js库的坏处,今晚阿里巴巴的笔试又悲剧了 。。。

让写一个事件处理的js函数,悲剧了,不会写,平时都是用jQuery,用的很爽,但是像这种考试的时候就悲剧了,以前看书的时候也看到过,但是一直没用心看,没用心记,现在转一个AdvanceED DOM scripting 书里的addEvent函数,学习学习。

function addEvent( node, type, listener ) {
    // Check compatibility using the earlier method
    // to ensure graceful degradation
    if(!isCompatible()) { return false }
    if(!(node = $(node))) return false;

    if (node.addEventListener) {
        // W3C method
        node.addEventListener( type, listener, false );
        return true;
    } else if(node.attachEvent) {
        // MSIE method
        node['e'+type+listener] = listener;
        node[type+listener] = function(){node['e'+type+listener]( window.event );}
        node.attachEvent( 'on'+type, node[type+listener] );
        return true;
    }

    // Didn't have either so return false
    return false;
};

杯具啊,平时学习这些细节注意的不够好,总是想到用的时候再来关注。但是像这种参加笔试就不行了。以后一定要注意,不论干什么事情都要细心,这一找工作,各种各样的毛病都暴露出来了,真悲剧啊,努力努力……

《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还是会被创建,只是创建的时间和第一种函数创建方法不同而已。