Showing posts with label event. Show all posts
Showing posts with label event. Show all posts

Monday, May 24, 2010

javascript中keyEvent按键事件说明(录自javascript权威指南第5版)

有3种按键类型,分别是keydown、keypress和keyup,它们分别对应onkeydown、onkeypress和onkeyup这几个事件处理器。
一个按键操作会产生这3个事件,依次是keydown、keypress,然后在按键释放的时候keyup。
这3个事件类型中,keypress事件是最为用户友好的:和它们相关的事件对象包含了所产生的实际字符的编码。keydown和keyup事件是较底层的,它们的按键事件包含一个和键盘所生成的硬件编码相关的“虚拟按键码”。对于ASCII字符集中的数字和字符,这些虚拟按键码和ASCII码相同。如果按下SHIFT键并按下数字2,keydown事件将通知发生了“SHITF-2”的按键事件。keypress事件会解释这一事件,说明这次按键产生了一个可打印的字符“@”。
对于不能打印的功能按键,如Backspace、Enter、Escape和箭头方向键、Page Up、Page Down以及F1到F12,它们会产生keydown和keyup事件。

在不同的浏览器中,按键事件的一些细节区别如下:


1、对于不能打印的功能按键,在Firefox中,也会产生keypress事件,在IE和Chrome中,则不会触发keypress事件,只有当按键有一个ASCII码的时候,即此字符为可打印字符或者一个控制字符的时候,keypress事件才会发生。对于这些不能打印的功能按键,可通过和keydown事件相关的keyCode来获取。
2、作为一条通用的规则,keydown事件对于功能按键来说是最有用的,而keypress事件对于可打印的按键来说是最有用的。
3、在IE中,Alt按键组合被认为是无法打印的,所以并不会触发keypress事件。
4、在Firefox中,按键事件定义有二个属性,keyCode存储了一个按键的较低层次的虚拟按键码,并且和keydown事件一起发送。charCode存储了按下一个键时所产生的可打印的字符的编码,并且和keypress事件一起发送。在Firefox中,功能按键会产生一个keypress事件,在这种情况下,charCode是0,而keyCode包含了虚拟按键码。在Firefox中,发生keydown事件时,charCode都为0,所以在keydown时获取charCode是无意义的。
5、在IE中,只有一个keyCode属性,并且它的解释也取决于事件的类型。对于keydown事件来说,keyCode是一个虚拟按键码,对于keypress事件来说,keyCode是一个字符码。
6、在Chrome中,功能键与IE中表现一样,不会触发keypress事件,对于keydown事件,也会在事件的keyCode中存储虚拟按键码,而charCode为0,与IE和Firefox表现一样,然而在发生可打印字符的keypress事件时,除了与Firefox一样,会在事件的charCode中存储实际按键编码之外,也会在keyCode中存储实际按键码,这二个值相同。
7、charCode字符码可以使用静态方数String.fromCharCode()转为字符。

Tuesday, April 14, 2009

document onmousemove event differences in different browsers

当前文档document中如果有一个iframe时,当mouse滚过当前文档进入到iframe区域中时,不同的浏览器的表现形式有所不同,safari4/opera9.6中仍然能触发mouseover事件,但是在firefox3/ie中则不触发此mouseover事件,因为mouse已经离开了当前文档。

Wednesday, October 29, 2008

Tapestry5 Page ActionLink 拼装过程

Page.createActionLink ->
LinkFactory.createActionLink ->
InvocationTarget.getPath ->
Link

其中在InvocationTarget的实现类ActionLinkTarget.getPath方法内实际拼装path,path中包括page, component, event等,如test.username:autocomplete。在LinkFactory中调用Link.addParameter方法将page activation context追加到link中。

Sunday, October 26, 2008

Explain onActivate/onPassivate of Tapestry5

用Tapestry5开发页面过程中,最常用到的二个页面方法(事件处理器)就是onActivate()和onPassivate()。
onActivate()方法是在此页面被激活后调用的,可以用此方法接收传给页面的参数(用官方的说法是以Array/List形式返回页面的activation context),此方法可以通过重载来接收数量不同的参数,并且其中参数少于等于URL InternalConstants.PAGE_CONTEXT_NAME传进来的参数数量的onActivate()方法都会被调用一次,其中无参的onActivate()方法肯定会被调用,并且是最后被调用,如后面例子所示。

注意,如果在Tapestry5中集成了tapestry-hibernate包,并且在passivate context中返回的是Hibernate实体的情况下,则可以在onActivate()中直接接收这些Hibernate实体对象,Tapestry会在passivate时自动从这些实体对象中提取id到页面URL ,并且在activate时自动转回Hibernate实体对象。Tapestry会在后台去查询数据库,如果页面很简单可以这么用,复杂页面不建议这么做。

onPassivate()方法是在页面渲染中,由LinkFactory生成每个actionLink或者返回自身页的pageLink时都会触发此事件,因此这个passivate事件在一个页面渲染中可能会重复调用多次。会将此方法返回的结果被encode后赋给InternalConstants.PAGE_CONTEXT_NAME,固化到所有返回自身页面的URL后面。对于actionLink和pageLink稍有不同的是,actionLink不管事件处理后重定向去哪,URL后都会跟上此参数,因其事件触发后先返回自身页面,而pageLink如果是指向别的页面,则不会触发此事件,URL后也不会带上此参数,如果需要传参给其他页面则通过pageLink的context参数指明参数值,如果pageLink是返回自身页面,则跟actionLink一样会在URL上直接跟上onPassivate返回的值,这样当前页面的一些变量状态就可以不用@Persist,而用onPassivate/onActivate进行传递。

关于onActivate/onPassivate更详细的内容可从以下接口和类中了解:ComponentResources/ComponentEventRequestHandler/ComponentEventRequestFilter/ComponentEventRequestHandler/ComponentEventRequestParameters/ComponentEventDispatcher/EventConstants/InternalConstants/LinkFactory/LinkFactoryImpl/PageLink/EventLink/ActionLink等。

其中ComponentEventRequestHandler可处理的事件请求形式有:

* /context/pagename:eventname -- event on the page, no action context
* /context/pagename:eventname/foo/bar -- event on the page with action context "foo", "bar"
* /context/pagename.foo.bar -- event on component foo.bar within the page, default event, no action context
* /context/pagename.foo.bar/baz.gnu -- event on component foo.bar within the page, default event, with action context "baz", "gnu"
* /context/pagename.bar.baz:eventname/foo/gnu -- event on component bar.baz within the page with action context "foo" , "gnu"


关于onActivate/onPassivate的一个测试例子:
java:
public class Test {

void onActivate() {
System.out.println("must be revoked when page be requested");
}

void onActivate(int id) {
System.out.println(id);
}

void onActivate(int id1, int id2) {
System.out.println(id1);
System.out.println(id2);
}

int onPassivate() {
System.out.println("passivate context");
return 1;
}

void onActionFromLink() {
}

void onRedirect() {
}
}
tml:
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
<a href="/Test">Test</a>
<br />
<t:actionLink t:id="link">ActionLink</t:actionLink>
<br />
<t:eventLink t:event="redirect" t:context="literal:1">EventLink1</t:eventLink>
<br />
<t:eventLink t:event="redirect">EventLink2</t:eventLink>
<br />
<t:pageLink t:page="Test">Self page Link</t:pageLink>
<br />
<t:pageLink t:page="Start">Another page Link</t:pageLink>
</html>
其中onPassivate()方法会被调用4次。
Reference:
http://tapestry.apache.org/tapestry5/apidocs/org/apache/tapestry5/internal/services/ComponentEventDispatcher.html

Saturday, October 11, 2008

Javascript event handler for text cut/paste/copy

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>some javascript element event example</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
</head>

<body>
<h3>Element.onpaste</h3>
<textarea id="editor" rows="3" cols="80" onpaste="pasteIntercept(event);">
Try pasting text into this area!
</textarea>

<h3>Log</h3>
<textarea rows="5" cols="80" id="log" readonly="true"></textarea>

<h3>Element.oncontextmenu</h3>
<div oncontextmenu="return false;" style="border: #ddd 1px solid; width: 642px; color: #369;">
prevent element context menu<br>
阻止此元素上的右键菜单弹出
</div>

<h3>Element.oncut and Element.copy</h3>
<input type="text" size="80" name="preventpaste" value="text can't be cut or copy!" oncut="return false" oncopy="return false"/>

<script type="text/javascript">
function log(txt)
{
document.getElementById("log").appendChild(document.createTextNode(txt + "\n"));
}

function pasteIntercept(event)
{
log("Pasting!");
event.preventDefault(); // event.returnValue = false; // IE
return false;
}
</script>

</body>
</html>

以上的几个方法在IE/Safari/Firefox3里都有对应的实现,在firefox3中测试通过,firefox2和opera未测试,应该还没有支持这些事件处理器。
Reference:
http://developer.mozilla.org/en/DOM/element.onpaste

Monday, October 06, 2008

Tapestry5 中事件处理完成之后可以返回的对象类型

  • Nothing: 此时方法一般是void的,当前页面会被刷新。
  • String: 要求此string指向一个PageName。
  • Class: 返回一个SomePage.class。
  • Page: 返回一个实例化的page,此page被当前页面注入。
  • Link: 返回一个实现Link接口的实例,此Link会被转化成对应的URL并重定向到此页面,Link对象一般通过页面中注入的ComponentResources中的方法生成,如ComponentResources.createPageLink、ComponentResources.createActionLink。
  • Stream: 返回一个StreamResponse对象,如提供用户pdf/excel格式的下载,更多用于ajax返回。
  • URL: 返回一个URL对象,可以重定向到一个外部链接上去。
返回除以上几种对象之外的对象都会出错。
Reference: http://tapestry.apache.org/tapestry5/tapestry-core/guide/pagenav.html

Thursday, October 02, 2008

jQuery event model test


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>test</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<script type="text/javascript" charset="utf-8" src="/lib/jquery/jquery-1.2.6.js"></script>
<style type="text/css" media="screen">
div, p {
margin: 2px;
border: #eee 1px solid;
min-height: 20px;
}
table, td {
width: 100%;
border: #ddd 1px solid;
}
.block {
position: relative;
width: 200px;
background-color: #828;
}
.data {
background-color: #333;
}
</style>
</head>
<body>
<p>Click or double click here.</p>
<span></span>
<div class="data">click here and see console log</div>
<p id="myCustom">Has an attached custom event.</p>
<button id="customButton">Trigger custom event</button>
<span style="display:none;" id="eventSpan"></span>

<div>
<p>
Binds a handler to a particular event (like click) for each matched element. Can also bind custom events.<br>
The event handler is passed an event object that you can use to prevent default behaviour. To stop both default action and event bubbling, your handler has to return false. Note that this will prevent handlers on parent elements from running but not other jQuery handlers on the same element.
</p>
<p>
In most cases, you can define your event handlers as anonymous functions (see first example). In cases where that is not possible, you can pass additional data as the second parameter (and the handler function as the third), see second example.
</p>
<button id="go">Go</button>
<button id="stop">STOP!</button>
<button id="back">Back</button>
<div class="block">div.block has animation</div>
</div>

<div class="oneClick"></div>
<div class="oneClick"></div>
<div class="oneClick"></div>
<div class="oneClick"></div>
<div class="oneClick"></div>
<div class="oneClick"></div>
<div class="oneClick"></div>
<div class="oneClick"></div>
<p id="p4click">Click a green square...</p>

<button id="button1">Button #1</button>
<button id="button2">Button #2</button>
<div><span id="spanFirst">0</span> button #1 clicks.</div>
<div><span id="spanLast">0</span> button #2 clicks.</div>

<button id="old">.trigger("focus")</button>
<button id="new">.triggerHandler("focus")</button><br/><br/>
<input type="text" value="To Be Focused" class="handler"/>

<script type="text/javascript" charset="utf-8">
$("p").bind("click", function(e){
console.log(e);
var str = "( " + e.pageX + ", " + e.pageY + " )";
$("span").text("Click happened! " + str);
});
$("p").bind("dblclick", function(){
$("span").text("Double-click happened in " + this.tagName);
});

function handler(event) {
console.log(event.data.foo);
}

// You can pass some extra data before the event handler:
$("div.data").bind("click", {foo: "bar"}, handler)

// To cancel a default action and prevent it from bubbling up, return false:
$("form").bind("submit", function() {return false; })

// Can bind custom events too.
$("p").bind("myCustomEvent", function(e, myName, myValue){
$(this).text(myName + ", hi there!");
$("#eventSpan").stop().css("opacity", 1)
.text("myName = " + myName)
.fadeIn(30).fadeOut(1000);
});
$("#customButton").click(function () {
$("#myCustom").trigger("myCustomEvent", [ "John" ]);
});

// Start animation
$("#go").click(function(){
$(".block").animate({left: '+=100px'}, 2000);
});

// Stops all the currently running animations on all the specified elements.
// If any animations are queued to run, then they will begin immediately.
// Stop animation when button is clicked
$("#stop").click(function(){
$(".block").stop();
});

// Start animation in the opposite direction
$("#back").click(function(){
$(".block").animate({left: '-=100px'}, 2000);
});

var n = 0;
$("div.oneClick").one("click", function(){
var index = $("div.oneClick").index(this);
$(this).css({ borderStyle:"inset",
cursor:"auto" }).text("this div be clicked");
$("#p4click").text("Div at index #" + index + " clicked." +
" That's " + ++n + " total clicks.");
});

$("#button1").click(function () {
update($("#spanFirst"));
});
$("#button2").click(function () {
$("#button1").trigger('click');
update($("#spanLast"));
});

function update(j) {
var n = parseInt(j.text(), 0);
j.text(n + 1);
}

// To pass arbitrary data to an event:
// $("p").click( function (event, a, b) {
// when a normal click fires, a and b are undefined
// for a trigger like below a refers too "foo" and b refers to "bar"
// } ).trigger("click", ["foo", "bar"]);
$("#p4click").bind("myEvent", function (event, message1, message2) {
console.log(message1 + ' ' + message2 + " from element: " + this.id);
});
$("#p4click").trigger("myEvent", ["Hello","World!"]);

// This particular method triggers all bound event handlers on an element (for a specific event type) WITHOUT executing the browsers default actions.
$("#old").click(function(){
$("input.handler").trigger("focus");
});
$("#new").click(function(){
$("input.handler").triggerHandler("focus");
});
$("input.handler").focus(function(){
console.log(arguments[0]); // Pass along a fake event by jQuery and no need to fix fake event
$("<span>Focused!</span>").appendTo("body").fadeOut(3000);
});
</script>
</body>
</html>

Sunday, August 17, 2008

AutoCompleter from local based on Prototype


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>completer demo</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<script src="/lib/prototype.js" type="text/javascript"></script>
<style type="text/css">
body {
font: 11px Lucida Grande, Verdana, Arial, Helvetica, sans serif;
}
</style>
</head>
<body>
<br/>
<div id="content">
<form>
input "a" for test:
<input id="message" name="message" size="30" type="text" />
<input type="submit" value="submit"/>
</form>
<script type="text/javascript">
//<![CDATA[
var contacts = ["aa", "aaa", "aaaa", "abb", "abbb", "ac", "acc", "accc", "aac", "aaac", "bc", "ad", "aad", "aaad", "aaaad", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "中文字体", "测试~!@#$%^&*()/?\"\\';", "北京奥运会", "姚明&刘翔"];

AutoFieldFromLocal = Class.create({
initialize: function(element, items){
this.element = $(element);
this.value = this.element.value;
this.items = items;
this.timer = null;
this.observer = null;
this.counter = 0;
this.index = 0;
this.listElements = [];
this.listsObserved = false;
this.maskShowed = false;
this.divId = "auto_complete_for_" + this.element.id;
this.listsId = "lists_of_" + this.element.id;
this.maskId = "mask_" + this.element.id;
this.element.setAttribute('autocomplete', 'off');
this.insertAfter();
this.observerStart();
},
insertAfter: function(){
new Insertion.After(this.element,
'<div id="' + this.divId + '" style="position:absolute;background-color:#fff;border:1px solid #888;margin:0;padding:0;display:none;height:200px;opacity:0.90;overflow:auto;overflow-x:hidden;z-index:1;filter:alpha(opacity=90);">' +
'<ul id="' + this.listsId + '" style="margin:0;padding:0;list-style-type: none;width:100%;"></ul></div>' +
'<div id="' + this.maskId + '" style="position:absolute;background-color:blue;border:1px solid #888;margin:0;padding:0;display:none;height:0;opacity:0;z-index:2;filter:alpha(opacity=0);"/>'
);
this.completerDiv = $(this.divId);
this.list = $(this.listsId);
this.mask = $(this.maskId);
},
observerStart: function(){
// safari/ie中监听keypress时,arrow keys箭头无法正确激活绑定的事件,改用keydown监听
// Arrow keys no longer result in keypress events, now processed in keydown default event handler
// Reference: https://lists.webkit.org/pipermail/webkit-dev/2007-December/002992.html
// keydown/keypress区别参考ppk的文章: http://www.quirksmode.org/dom/events/keys.html
if (Prototype.Browser.WebKit || Prototype.Browser.IE) Event.observe(this.element, 'keydown', this.renderLists.bindAsEventListener(this));
else Event.observe(this.element, 'keypress', this.renderLists.bindAsEventListener(this));
Event.observe(this.element, 'blur', this.onBlured.bindAsEventListener(this));
Event.observe(document, 'mousemove', this.hideMask.bindAsEventListener(this));
},
addListsObserver: function(){
var self = this;
this.listElements.each(function(li){
Event.observe(li, 'mouseover', self.onMouseOverList.bindAsEventListener(self));
Event.observe(li, 'click', self.updateField.bindAsEventListener(self));
});
this.hideMask();
},
hideMask: function() {
if (this.maskShowed) {
this.maskShowed = false;
this.mask.style.height = '0px';
this.mask.style.display = "none";
}
},
showMask: function(){
if (!this.maskShowed) {
this.maskShowed = true;
this.mask.style.height = this.completerDiv.clientHeight + 'px';
this.mask.style.display = "block";
}
},
onBlured: function(){
var self = this;
if (this.timer) clearTimeout(this.timer);
this.timer = setTimeout(function(){
self.hideMask();
self.completerDiv.hide();
}, 100);
},
updateField: function(){
this.element.focus();
if(this.listElements.length > 0 && this.index != -1)
this.element.value = this.listElements[this.index].innerHTML.replace(/&/, "&");
this.completerDiv.hide();
this.hideMask();
},
listsShowed: function(){
return this.completerDiv.style.display.toLowerCase() == 'block';
},
onSelectList: function(){
this.listElements.each(function(li){
li.style.backgroundColor = "#fff";
});
this.listElements[this.index].style.backgroundColor = "#ffb";
},
divScrollTop: function() {
var maxScrollTop = this.completerDiv.scrollHeight - this.completerDiv.clientHeight;
var scrollBottom = this.completerDiv.clientHeight + this.completerDiv.scrollTop;
var listHeight = this.listElements[0] ? this.listElements[0].clientHeight : 20;
// TODO , get li.clientHeight after insert li element and then remove it if ul list has no li child.
if (this.index == 0)
this.completerDiv.scrollTop = 0;
else if (this.index == this.listElements.length - 1)
this.completerDiv.scrollTop = maxScrollTop;
else if (this.index * listHeight < this.completerDiv.scrollTop)
this.completerDiv.scrollTop = this.index * listHeight;
else if (this.index * listHeight >= scrollBottom)
this.completerDiv.scrollTop = this.completerDiv.scrollTop + listHeight;
},
markNextList: function(){
this.showMask();
if(this.index < this.counter - 1) this.index++
else this.index = 0;
this.divScrollTop();
this.onSelectList();
},
markPrevList: function(){
this.showMask();
if(this.index > 0 ) this.index--
else this.index = this.counter - 1;
this.divScrollTop();
this.onSelectList();
},
onMouseOverList: function(event){
var elementLI = Event.findElement(event, 'LI');
this.index = this.listElements.indexOf(elementLI);
this.listElements.each(function(li){
li.style.backgroundColor = "#fff";
});
elementLI.style.backgroundColor = "#ffb";
// this.divScrollTop();
},
renderLists: function(event){
switch(event.keyCode) {
case Event.KEY_LEFT:
case Event.KEY_RIGHT:
return;
case Event.KEY_ESC:
this.completerDiv.hide();
this.hideMask();
// esc will also empty input field in ie by default, prevent default event.
Event.stop(event);
this.index = -1;
return;
case Event.KEY_TAB:
case Event.KEY_RETURN:
this.updateField();
Event.stop(event);
return;
case Event.KEY_DOWN:
if (this.listsShowed()) {
this.markNextList();
return;
}
case Event.KEY_UP:
if (this.listsShowed()) {
this.markPrevList();
return;
}
}
if (this.observer) clearTimeout(this.observer);
var self = this;
this.observer = setTimeout(function(){self.onFieldChange(event)}, 100);
},
onFieldChange: function(event){
var value = this.element.value.strip();
var autoWidth = this.element.getWidth();
var find = false;
var listItems = "";
this.counter = 0;
this.index = -1;
var self = this;
if (Prototype.Browser.IE || this.value != value || event.keyCode == Event.KEY_DOWN) {
this.value = value;
this.items.each(function(item){
if (item.include(self.value)) {
self.counter ++;
// li element defaults line-height is different in different browser:
// safari 13px, firefox3 14px
// li width must be set 100% and display block for ie, and ie scrollbar movement has related with li width, sign...
// font size should be little than 14px in ie
listItems += "<li style='line-height:14px;padding:3px;margin:0;display:block;width:100%;cursor:default;'>" + item + "</li>";
if (!find) find = true;
}
});
}
if (find) {
this.list.innerHTML = listItems;
var pos = Position.cumulativeOffset(this.element);
this.completerDiv.style.width = this.mask.style.width = (autoWidth - 2) + "px";
this.completerDiv.style.left = this.mask.style.left = pos[0] + "px";
this.completerDiv.style.top = this.mask.style.top = (pos[1] + this.element.getHeight() - 1) + "px";
this.completerDiv.style.display = "block";
this.listElements = this.list.childElements();
this.addListsObserver();
} else {
this.completerDiv.hide();
this.hideMask();
}
}
});

var af = new AutoFieldFromLocal('message', contacts);
//]]>
</script>
</div>

</body>
</html>

需要注意一下以下几点:
1、在safari/ie中对于arrow keys事件的处理方式和keydown/keypress二者区别。
2、在ie中esc默认行为会将input框中的值一起cancel。
3、在firefox中mouseover事件的控制,在用arrow keys移动选项时,用一个蒙板挡住了mouse,以防触发mouseover事件。
4、setTimeout()方法中的this需要小心其指向全局的window。
5、当y轴出现滚动条时,其实际可以滚动的长度不是element.scrollHeight,而应该是此值减去element.clientHeight。
6、IE中控制滚动条用overflow:auto; overflow-x:hidden;
7、IE中滚动条和LI元素的宽度有关系,要控制滚动条则需要设置LI宽度为100%。
8、对&作特殊处理。

Friday, February 22, 2008

Javascript event delegation

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>JavaScript Techniques</title>
<script type="text/javascript">
var addListener = function() {
if ( window.addEventListener ) {
return function(el, type, fn) {
el.addEventListener(type, fn, false);
};
} else if ( window.attachEvent ) {
return function(el, type, fn) {
var f = function() {
fn.call(el, window.event);
};
el.attachEvent('on'+type, f);
};
} else {
return function(el, type, fn) {
element['on'+type] = fn;
}
}
}();

</script>
</head>
<body>
<div id='d1'>test</div>
<ul id="example">
<li id="li0">foo</li>
<li id="li1">bar</li>
<li id="li2">baz</li>
<li id="li3">thunk</li>
</ul>

<script type="text/javascript">
addListener(document.getElementById('d1'), 'click', getUserNameById);
function getUserNameById(e) {
alert('api function getUserNameById');
getUserNameById(this.id);
}
var element = document.getElementById('example');
addListener(element, 'click', handleClick);
function handleClick(e) {
var element = e.target || e.srcElement;
alert('target: ' + e.target + " currentTarget: " + e.currentTarget + " id: " + element.id + " this: " + this + " eventPhase: " + e.eventPhase);
}
</script>
</body>
</html>
event.target是指事件发生时的对象,可以是document中的任何一个node,包括text node。
如果在捕捉阶段或者起泡阶段处理事件,事件event.target仍然都是指向发生事件的node上,但currentTarget却不是指向此node。
如上例子中点击的event.target是li元素时,ul上的事件处理是发生在起泡阶段(第3阶段),而不是在AT_TARGET(2)阶段。此时的event.target指向li元素,event.currentTarget则是指向ul元素。
callback中的this与event.currentTarget在目前实现的addEventListener方法中是指向相同的元素,但最好不要用this.
Reference:
http://www.digital-web.com/articles/seven_javascript_techniques/

Saturday, January 26, 2008

addEventListener's phase and sequence


<html>
<head><title> addEventListener Capturing_phase and Bubbling_phase and At_target </title></head>
<body>
<div id="a1">
<div id="a2"> <input type="button" name="input1" id="i1" value="click here"> </div>
</div>
<script type="text/javascript" charset="utf-8">
function doc(event) {
alert("currentTarget=" + event.currentTarget.id + "; target=" + event.target + "; eventPhase=" + event.eventPhase);
}
var a1 = document.getElementById("a1");
var a2 = document.getElementById("a2");
var i1 = document.getElementById("i1");
a1.addEventListener('click', doc, true);
a2.addEventListener('click', doc, false);
i1.addEventListener('click', doc, false);
</script>
</body>
</html>

addEventListener的第3个参数是指在capturing_phase是否捕获此动作,因此上面会按"a1->i1->a2"的顺序弹出提示,这个顺序与dom文档节点顺序和addEventListener第3个参数有关,与javascript代码中的addEventListener的顺序无关。
要在事件流的所有阶段侦听某一事件,您必须调用 addEventListener() 两次,第一次调用时将 useCapture 设置为 true,第二次调用时将 useCapture 设置为 false。如:
a1.addEventListener('click', doc, false);
此时在capturing phase和bubbling phase都会有a1的事件提示。

Sunday, August 05, 2007

prototype javascript library event observe example


<div id="d1">
<a href="http://www.google.com" id="a1" target="_blank" onclick="testEvent(event); return false;">www.google.com</a>
<a href="http://www.baidu.com" id="a2" target="_blank">www.baidu.com</a>
<a href="http://www.yahoo.com" id="a3" target="_blank" onclick="alert(this); return false;">www.yahoo.com</a>
</div>
<br/>
<script type="text/javascript" charset="utf-8">
function testEvent(event) {
var e = Event.element(event);
alert(Event.findElement(event, 'div').id);
alert(e.id);
//alert(Event.pointerX(event));
//alert(Event.pointerY(event));
}

/*for(var p in Event){
doc('Event["' + p + '"] = ' + Event[p]);
}*/

var handler = function (e) {
alert(e.which);
Event.stop(e); // prevent pop window to www.baidu.com
};

Event.observe('a2', 'click', handler, true);
Event.stopObserving('a2', 'click', handler, true); // cancel observe , click will pop window to www.baidu.com
</script>

prototype javascript library Element event observe example


<a href="http://www.baidu.com" id="a1"> baidu </a>
<script type="text/javascript" charset="utf-8">
/*Event.observe('a1', 'click', function (o) {
alert(o.eventPhase + o.type);
//Event.stop(o);
}, false);

Event.observe('a1', 'click', function (o) {
alert(o.eventPhase);
Event.stop(o);
}, true);*/

/*Object.extend(Element.prototype, {
click: function (callback) {
this.onclick = callback;
},
mouseout: function (callback) {
this.onmouseout = function (evt) {
alert(evt.eventPhase);
callback.apply(this, $A(arguments));
}
}
});*/

var evts = ("blur,focus,load,resize,scroll,unload,click,dblclick," +
"mousedown,mouseup,mousemove,mouseover,mouseout,change,select," +
"submit,keydown,keypress,keyup,error
").split(",");
evts.each(function (e) {
Element.prototype[e] = function (handler) {
this['on' + e] = handler;
}
});
var a1 = $('a1');
a1.click(function (e) {
alert(e.type);
Event.stop(e);
});
a1.click(function (e) {
alert(e.eventPhase);
Event.stop(e);
}); // will override before a1.click handler
a1.mouseout(function (e) {
alert(e.type);
});
</script>

event observe o.eventPhase #=> 2
Event.stop will prevent event performed, browser will not go to baidu.com

JavaScript Dom 修正


<script language="JavaScript" type="Text/JavaScript">
<!--
if(window.Event){// 修正Event的DOM
/*
IE5 MacIE5 Mozilla Konqueror2.2 Opera5
event yes yes yes yes yes
event.returnValue yes yes no no no
event.cancelBubble yes yes no no no
event.srcElement yes yes no no no
event.fromElement yes yes no no no
*/
Event.prototype.__defineSetter__("returnValue",function(b){//
if(!b)this.preventDefault();
return b;
});
Event.prototype.__defineSetter__("cancelBubble",function(b){// 设置或者检索当前事件句柄的层次冒泡
if(b)this.stopPropagation();
return b;
});
Event.prototype.__defineGetter__("srcElement",function(){
var node=this.target;
while(node.nodeType!=1)node=node.parentNode;
return node;
});
Event.prototype.__defineGetter__("fromElement",function(){// 返回鼠标移出的源节点
var node;
if(this.type=="mouseover")
node=this.relatedTarget;
else if(this.type=="mouseout")
node=this.target;
if(!node)return;
while(node.nodeType!=1)node=node.parentNode;
return node;
});
Event.prototype.__defineGetter__("toElement",function(){// 返回鼠标移入的源节点
var node;
if(this.type=="mouseout")
node=this.relatedTarget;
else if(this.type=="mouseover")
node=this.target;
if(!node)return;
while(node.nodeType!=1)node=node.parentNode;
return node;
});
Event.prototype.__defineGetter__("offsetX",function(){
return this.layerX;
});
Event.prototype.__defineGetter__("offsetY",function(){
return this.layerY;
});
}
if(window.Document){// 修正Document的DOM
/*
IE5 MacIE5 Mozilla Konqueror2.2 Opera5
document.documentElement yes yes yes yes no
document.activeElement yes null no no no

*/
}
if(window.Node){// 修正Node的DOM
/*
IE5 MacIE5 Mozilla Konqueror2.2 Opera5
Node.contains yes yes no no yes
Node.replaceNode yes no no no no
Node.removeNode yes no no no no
Node.children yes yes no no no
Node.hasChildNodes yes yes yes yes no
Node.childNodes yes yes yes yes no
Node.swapNode yes no no no no
Node.currentStyle yes yes no no no

*/
Node.prototype.replaceNode=function(Node){// 替换指定节点
this.parentNode.replaceChild(Node,this);
}
Node.prototype.removeNode=function(removeChildren){// 删除指定节点
if(removeChildren)
return this.parentNode.removeChild(this);
else{
var range=document.createRange();
range.selectNodeContents(this);
return this.parentNode.replaceChild(range.extractContents(),this);
}
}
Node.prototype.swapNode=function(Node){// 交换节点
var nextSibling=this.nextSibling;
var parentNode=this.parentNode;
node.parentNode.replaceChild(this,Node);
parentNode.insertBefore(node,nextSibling);
}
}
if(window.HTMLElement){
HTMLElement.prototype.__defineGetter__("all",function(){
var a=this.getElementsByTagName("*");
var node=this;
a.tags=function(sTagName){
return node.getElementsByTagName(sTagName);
}
return a;
});
HTMLElement.prototype.__defineGetter__("parentElement",function(){
if(this.parentNode==this.ownerDocument)return null;
return this.parentNode;
});
HTMLElement.prototype.__defineGetter__("children",function(){
var tmp=[];
var j=0;
var n;
for(var i=0;i<this.childNodes.length;i++){
n=this.childNodes[i];
if(n.nodeType==1){
tmp[j++]=n;
if(n.name){
if(!tmp[n.name])
tmp[n.name]=[];
tmp[n.name][tmp[n.name].length]=n;
}
if(n.id)
tmp[n.id]=n;
}
}
return tmp;
});
HTMLElement.prototype.__defineGetter__("currentStyle", function(){
return this.ownerDocument.defaultView.getComputedStyle(this,null);
});
HTMLElement.prototype.__defineSetter__("outerHTML",function(sHTML){
var r=this.ownerDocument.createRange();
r.setStartBefore(this);
var df=r.createContextualFragment(sHTML);
this.parentNode.replaceChild(df,this);
return sHTML;
});
HTMLElement.prototype.__defineGetter__("outerHTML",function(){
var attr;
var attrs=this.attributes;
var str="<"+this.tagName;
for(var i=0;i
attr=attrs[i];
if(attr.specified)
str+=" "+attr.name+'="'+attr.value+'"';
}
if(!this.canHaveChildren)
return str+">";
return str+">"+this.innerHTML+"";
});
HTMLElement.prototype.__defineGetter__("canHaveChildren",function(){
switch(this.tagName.toLowerCase()){
case "area":
case "base":
case "basefont":
case "col":
case "frame":
case "hr":
case "img":
case "br":
case "input":
case "isindex":
case "link":
case "meta":
case "param":
return false;
}
return true;
});

HTMLElement.prototype.__defineSetter__("innerText",function(sText){
var parsedText=document.createTextNode(sText);
this.innerHTML=parsedText;
return parsedText;
});
HTMLElement.prototype.__defineGetter__("innerText",function(){
var r=this.ownerDocument.createRange();
r.selectNodeContents(this);
return r.toString();
});
HTMLElement.prototype.__defineSetter__("outerText",function(sText){
var parsedText=document.createTextNode(sText);
this.outerHTML=parsedText;
return parsedText;
});
HTMLElement.prototype.__defineGetter__("outerText",function(){
var r=this.ownerDocument.createRange();
r.selectNodeContents(this);
return r.toString();
});
HTMLElement.prototype.attachEvent=function(sType,fHandler){
var shortTypeName=sType.replace(/on/,"");
fHandler._ieEmuEventHandler=function(e){
window.event=e;
return fHandler();
}
this.addEventListener(shortTypeName,fHandler._ieEmuEventHandler,false);
}
HTMLElement.prototype.detachEvent=function(sType,fHandler){
var shortTypeName=sType.replace(/on/,"");
if(typeof(fHandler._ieEmuEventHandler)=="function")
this.removeEventListener(shortTypeName,fHandler._ieEmuEventHandler,false);
else
this.removeEventListener(shortTypeName,fHandler,true);
}
HTMLElement.prototype.contains=function(Node){// 是否包含某节点
do if(Node==this)return true;
while(Node=Node.parentNode);
return false;
}
HTMLElement.prototype.insertAdjacentElement=function(where,parsedNode){
switch(where){
case "beforeBegin":
this.parentNode.insertBefore(parsedNode,this);
break;
case "afterBegin":
this.insertBefore(parsedNode,this.firstChild);
break;
case "beforeEnd":
this.appendChild(parsedNode);
break;
case "afterEnd":
if(this.nextSibling)
this.parentNode.insertBefore(parsedNode,this.nextSibling);
else
this.parentNode.appendChild(parsedNode);
break;
}
}
HTMLElement.prototype.insertAdjacentHTML=function(where,htmlStr){
var r=this.ownerDocument.createRange();
r.setStartBefore(this);
var parsedHTML=r.createContextualFragment(htmlStr);
this.insertAdjacentElement(where,parsedHTML);
}
HTMLElement.prototype.insertAdjacentText=function(where,txtStr){
var parsedText=document.createTextNode(txtStr);
this.insertAdjacentElement(where,parsedText);
}
HTMLElement.prototype.attachEvent=function(sType,fHandler){
var shortTypeName=sType.replace(/on/,"");
fHandler._ieEmuEventHandler=function(e){
window.event=e;
return fHandler();
}
this.addEventListener(shortTypeName,fHandler._ieEmuEventHandler,false);
}
HTMLElement.prototype.detachEvent=function(sType,fHandler){
var shortTypeName=sType.replace(/on/,"");
if(typeof(fHandler._ieEmuEventHandler)=="function")
this.removeEventListener(shortTypeName,fHandler._ieEmuEventHandler,false);
else
this.removeEventListener(shortTypeName,fHandler,true);
}
}
//-->
</script>

此程序由某文章回帖处转过来的,未知原作者。

Sunday, July 29, 2007

addEvent of Javascript


<script type="text/javascript" charset="utf-8">
// written by Dean Edwards, 2005
// with input from Tino Zijdel, Matthias Miller, Diego Perini

// http://dean.edwards.name/weblog/2005/10/add-event/

function addEvent(element, type, handler) {
if (element.addEventListener) {
element.addEventListener(type, handler, false); // copy handler function.
} else {
// assign each event handler a unique ID
if (!handler.$$guid) handler.$$guid = addEvent.guid++;
// create a hash table of event types for the element
if (!element.events) element.events = {};
// create a hash table of event handlers for each element/event pair
var handlers = element.events[type];
if (!handlers) {
handlers = element.events[type] = {};
// store the existing event handler (if there is one, else this value is 'undefined')
if (element["on" + type]) {
//alert('exist on' + type + ' handler! copy existing event handler to handlers[0]');
handlers[0] = element["on" + type];
}
}
// store the event handler in the hash table
handlers[handler.$$guid] = handler;
// assign a global event handler to do all the work
element["on" + type] = handleEvent;
}
};
// a counter used to create unique IDs, Static variable: addEvent.guid
addEvent.guid = 1;

function removeEvent(element, type, handler) {
if (element.removeEventListener) {
element.removeEventListener(type, handler, false);
} else {
// delete the event handler from the hash table
if (element.events && element.events[type]) {
delete element.events[type][handler.$$guid];
}
}
};

function handleEvent(event) {
var returnValue = true;
// grab the event object (IE uses a global event object)
event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event);
// get a reference to the hash table of event handlers
var handlers = this.events[event.type];
// execute each event handler
for (var i in handlers) {
this.$$handleEvent = handlers[i];
if (this.$$handleEvent(event) === false) {
returnValue = false;
}
}
return returnValue;
};

function fixEvent(event) {
// add W3C standard event methods, IE has not those methods
event.preventDefault = fixEvent.preventDefault;
event.stopPropagation = fixEvent.stopPropagation;
return event;
};
fixEvent.preventDefault = function() {
this.returnValue = false;
};
fixEvent.stopPropagation = function() {
this.cancelBubble = true;
};

// This little snippet fixes the problem that the onload attribute on the body-element will overwrite
// previous attached events on the window object for the onload event
if (!window.addEventListener) {
document.onreadystatechange = function() {
if (window.onload && window.onload != handleEvent) {
addEvent(window, 'load', window.onload);
window.onload = handleEvent;
}
}
}
</script>

<a href="http://www.baidu.com" id="a1"> baidu </a>

<script type="text/javascript" charset="utf-8">
var a1 = $('a1');
var handler = function (e) {
alert('handler:' + e.type);
Event.stop(e);
};
var otherHandler = function (e) {
alert('otherHandler: ' + e.type + e.eventPhase);
Event.stop(e);
};
var handlerSelf = function (event) {
alert('too much recursion');
//a1.onmouseover(event);
}
a1.onmouseover = handlerSelf; // this handler will override by addEvent function.
addEvent(a1, 'mouseover', handler); // addEvent.guid = 1(handler), element.events['mouseover'][0] = element['on' + 'mouseover'] = handlerSelf, element.events['mouseover'][1] = handler
addEvent(a1, 'click', handler); // addEvent.guid = 1(handler), element.events['click'][1] = handler
addEvent(a1, 'click', otherHandler); // addEvent.guid = 2(otherHandler), element.events['click'][2] = otherHandler

/*
var evts = ("blur,focus,load,resize,scroll,unload,click,dblclick," +
"mousedown,mouseup,mousemove,mouseover,mouseout,change,select," +
"submit,keydown,keypress,keyup,error").split(",");
evts.each(function (e) {
Element.prototype[e] = function (callback) {
addEvent(this, e, callback);
};
});

var handler = function (evt) {
alert(evt.type + ': ' + evt.eventPhase);
Event.stop(evt);
return false;
};

a1['mouseover'](handler);
removeEvent(a1, 'mouseover', handler); // won't alert evt.type, unless comment this line.

a1.click(handler); // alert evt.type
*/
</script>

Sunday, July 15, 2007

Javascript keyCode


<script type="text/javascript" charset="utf-8">
<!--
function doc (argument) {
document.write(argument);
document.write("<br />\n");
}

function isNumberInput(field, event)
{
var key, keyChar;
if (window.event) {
key = window.event.keyCode;
alert(key);
} else if (event) {
key = event.which;
code = event.keyCode;
var d1 = document.getElementById('d1');
d1.appendChild(document.createTextNode(key + "(key)=" + String.fromCharCode(event.which) + "(code) \n"));
// event.shiftKey and event.altKey, Mozilla provides a mapping for the keys, so you could do something. like:
if (event.keyCode == event.DOM_VK_TAB){
alert('TAB is pressed');
} else if (event.keyCode == event.DOM_VK_F1){
alert('F1 is pressed');
}
} else {
return true;
}
// Check for special characters like backspace(8), enter return(13), tab(9), escape(27) etc.
if (key == null || key == 0 || key == 8 || key == 13 || key == 27) {
return true;
}

// Check to see if it's a number
keyChar = String.fromCharCode(key);
if (/\d/.test(keyChar))
{
window.status = "";
return true;
}
else
{
window.alert("Field accepts numbers only.");
return false; // not input error keypress
}
}
//-->
</script>
<div id="d1">

</div>
<form name="testform" id="testform" action="#" method="get">
Robot Serial Number:
<input type="text" name="serialnumber" id="serialnumber" size="10" maxlength="10"
onkeypress="return isNumberInput(this, event);" title="Serial number contains only digits" />
</form>

Using Keyboard Events

Prior to the release of Netscape 4.0, JavaScript programs couldn't detect keyboard
actions—just mouse actions. This made it difficult to create some types of programs
in JavaScript. For example, games were difficult to play using Go Left and Go Right
buttons.

Thankfully, JavaScript 1.2 and later can detect keyboard actions. The main event
handler for this purpose is onKeyPress, which occurs when a key is pressed and
released, or held down. As with mouse buttons, you can detect the down and up parts
of the keypress with the onKeyDown and onKeyUp event handlers.

Of course, you may find it useful to know which key the user pressed. You can find
this out with the event object, which is sent to your event handler when the event
occurs. In Netscape, the event.which property stores the ASCII character code for the
key that was pressed. In Internet Explorer, event.keyCode serves the same purpose.

NOTE

ASCII (American Standard Code for Information Interchange) is the standard numeric
code used by most computers to represent characters. It assigns the numbers 0–128 to
various characters—for example, the capital letters A through Z are ASCII values 65
to 90.

Displaying Typed Characters

If you'd rather deal with actual characters than key codes, you can use the
fromCharCode string method to convert them. This method converts a numeric ASCII code
to its corresponding string character. For example, the following statement converts
Netscape's event.which property to a character and stores it in the key variable:

Key = String.fromCharCode(event.which);

Since Internet Explorer and Netscape have different ways of returning the key code,
displaying keys browser-independently is a bit harder. However, you can create a
script that displays keys for either browser. The following function will display
each key in the status line:

function DisplayKey(e) {
if (e.keyCode) keycode=e.keyCode;
else keycode=e.which;
character=String.fromCharCode(keycode);
window.status += character;
}

The DisplayKey function receives the event object from the event handler and stores
it in the variable e. It checks whether the e.keyCode property exists, and stores it
in the keycode variable if present. Otherwise, it assumes the browser is Netscape and
assigns keycode to the e.which property.

Key Pressed Javascript Key Code
backspace 8
tab 9
enter 13
shift 16
ctrl 17
alt 18
pause/break 19
caps lock 20
escape 27
page up 33
page down 34
end 35
home 36
left arrow 37
up arrow 38
right arrow 39
down arrow 40
insert 45
delete 46
0 48
1 49
2 50
3 51
4 52
5 53
6 54
7 55
8 56
9 57
a 65
b 66
c 67
d 68
e 69
f 70
g 71
h 72
i 73
j 74
k 75
l 76
m 77
n 78
o 79
p 80
q 81
r 82
s 83
t 84
u 85
v 86
w 87
x 88
y 89
z 90
left window key 91
right window key 92
select key 93
numpad 0 96
numpad 1 97
numpad 2 98
numpad 3 99
numpad 4 100
numpad 5 101
numpad 6 102
numpad 7 103
numpad 8 104
numpad 9 105
multiply 106
add 107
subtract 109
decimal point 110
divide 111
f1 112
f2 113
f3 114
f4 115
f5 116
f6 117
f7 118
f8 119
f9 120
f10 121
f11 122
f12 123
num lock 144
scroll lock 145
semi-colon 186
equal sign 187
comma 188
dash 189
period 190
forward slash 191
grave accent 192
open bracket 219
back slash 220
close braket 221
single quote 222