零js基礎 | 程序設計 | 14 - 表单

var form = document.getElementById("form1");
var firstForm = document.forms[0];
var myForm = document.forms["form2"];
   objform.elements('objid')

获取方法

    <form action="" name='myFormname' id="myForm">
        <textarea name="" id="" cols="30" rows="10" name='textbox' id='textbox'></textarea>
        <ul>
            <li>
                <input type="radio" name="colorName" id='colorID1' value="red">Red</li>
            <li>
                <input type="radio" name="colorName" id='colorID2' value="green">Green</li>
            <li>
                <input type="radio" name="colorName" id="colorID3" value="blue">Blue</li>
        </ul>
    </form>
        var formId  = document.getElementById('myForm')
        var formName = document.getElementsByName('myFormname')[0]
        var form0 = document.forms[0]
        
        
        
        var colorId = formId.elements['colorName']
        alert(colorId.length)//3
        
        var colorName = formName.elements['colorName']
        alert(colorName.length)//3

,有3 个单选按钮,它们的name 都是"color",意味着这3 个字段是一起的。
在访问elements["color"]时,就会返回一个NodeList,其中包含这3 个元素;不过,如果访问
elements[0],则只会返回第一个元素。来看下面的例子。

var form = document.getElementById("myForm");
var colorFields = form.elements["color"];
alert(colorFields.length); //3
var firstColorField = colorFields[0];
var firstFormField = form.elements[0];
alert(firstColorField === firstFormField); //true

除了fieldset元素之外,所有表单字段都拥有相同的一组属性。由于input类型可以表示多
种表单字段,因此有些属性只适用于某些字段,但还有一些属性是所有字段所共有的。表单字段共有的
属性如下。
 disabled:布尔值,表示当前字段是否被禁用。
 form:指向当前字段所属表单的指针;只读。
 name:当前字段的名称。
 readOnly:布尔值,表示当前字段是否只读。
 tabIndex:表示当前字段的切换(tab)序号。
 type:当前字段的类型,如"checkbox"、"radio",等等。
 value:当前字段将被提

var filed = form.elements[0]
filed.value = 'auther Value'
alert(filed.form === form) //true

//把焦点设置到当前字段
field.focus();

//禁用当前字段
field.disabled = true;

//修改type 属性(不推荐,但对input来说是可行的)
field.type = "checkbox";

这就是个问题:因为会导致费用翻番。
为此,最常见的解决方案,就是在第一次单击后就禁用提交按钮。只要侦听submit 事件,并在该事件
发生时禁用提交按钮即可。以下就是这样一个例子。

EventUtil.addHandler(form, "submit", function(event){
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);

//取得提交按钮
var btn = target.elements["submit-btn"];

//禁用它
btn.disabled = true;
});

注意,不能通过onclick 事件处理程序来实现这个功能,原
因是不同浏览器之间存在“时差”:有的浏览器会在触发表单的submit 事件之前触发click 事件,
而有的浏览器则相反。对于先触发click 事件的浏览器,意味着会在提交发生之前禁用按钮,结果
永远都不会提交表单

focus()和 blur()。

EventUtil.addHandler(window, "load", function(event){
document.forms[0].elements[0].focus();
});

要注意的是,如果第一个表单字段是一个input元素,且其type 特性的值为"hidden",那么
以上代码会导致错误。另外,如果使用CSS 的display 和visibility 属性隐藏了该字段,同样也会
导致错误。

HTML5 为表单字段新增了一个autofocus 属性。在支持这个属性的浏览器中,只要设置这个属性,
不用JavaScript 就能自动把焦点移动到相应字段。例如:

<input type="text" autofocus>

检测是否支持autofocus属性

EventUtil.addHandler(window, "load", function(event) {
    var element = document.forms[0].elements[0];

    if (element.autofocus !== true) {
        element.focus();
        console.log("JS focus");
    }
});

共有字段事件

 blur:当前字段失去焦点时触发。
 change:对于input和textarea元素,在它们失去焦点且value 值改变时触发;对于
select元素,在其选项改变时触发。
 focus:当前字段获得焦点时触发。

。对于input和textarea元素,当它们从获得焦点到失去焦点且value 值改变时,
才会触发change 事件。对于select元素,只要用户选择了不同的选项,就会触发change 事件;
换句话说,不失去焦点也会触发change 事件

获得焦点时选择所有文本

var textbox = document.forms[0].elements["textbox1"];
textbox.select();

取得选择的文本selectionStart 和selectionEnd 即文本选区开头和结尾的偏移

function getSelectedText(textbox){
return textbox.value.substring(textbox.selectionStart, textbox.selectionEnd);
}

因为substring() 方法基于字符串的偏移量执行操作, 所以将selectionStart 和
selectionEnd 直接传给它就可以取得选中的文本。

IE8 及更早的版本中有一个document.selection

function getSelectedText(textbox) {
    if (typeof textbox.selectionStart == "number") {
        return textbox.value.substring(textbox.selectionStart,
            textbox.selectionEnd);
    } else if (document.selection) {
        return document.selection.createRange().text;
    }
}

input textarea

选择部分文本:
setSelectionRange()方法。
现在除select()方法之外,所有文本框都有一个setSelectionRange(第一个字符的索引,要选择的和要选择的最后一个字符之后的字符的索引)
(类似于substring()方法的两个参数)。来看一个例子。

        var forminp = document.forms[0].elements[0]
            //      console.log(form1)
            //    console.log( form1.value)
            
        
        forminp.value = 'hello world'
        
        forminp.setSelectionRange(0,forminp.value.length)   //选中hello world
        
        forminp.setSelectionRange(0,3)  //选中hel
        

IE8以下,不作讨论

textbox.value = "Hello world!";
var range = textbox.createTextRange();
//选择所有文本
range.collapse(true);
range.moveStart("character", 0);
range.moveEnd("character", textbox.value.length); //"Hello world!"
range.select();
//选择前3 个字符
range.collapse(true);
range.moveStart("character", 0);
range.moveEnd("character", 3);
range.select(); //"Hel"
//选择第4 到第6 个字符
range.collapse(true);
range.moveStart("character", 4);
range.moveEnd("character", 3);
range.select(); //"o w"

跨浏览器

function selectText(textbox, startIndex, stopIndex) {
    if (textbox.setSelectionRange) {
        textbox.setSelectionRange(startIndex, stopIndex);
    } else if (textbox.createTextRange) {
        var range = textbox.createTextRange();
        range.collapse(true);
        range.moveStart("character", startIndex);
        range.moveEnd("character", stopIndex - startIndex);
        range.select();
    }
    textbox.focus();
}

这个selectText()函数接收三个参数:要操作的文本框、要选择文本中第一个字符的索引和要选
择文本中最后一个字符之后的索引

textbox.value = "Hello world!"
//选择所有文本
selectText(textbox, 0, textbox.value.length); //"Hello world!"
//选择前3 个字符
selectText(textbox, 0, 3); //"Hel"
//选择第4 到第6 个字符
selectText(textbox, 4, 7); //"o w"

过滤输入

下列代码只允许用户输入数值。


EventUtil.addHandler(textbox, "keypress", function(event) {
    event = EventUtil.getEvent(event);
    var target = EventUtil.getTarget(event);
    var charCode = EventUtil.getCharCode(event);
    if (!/\d/.test(String.fromCharCode(charCode))) {
        EventUtil.preventDefault(event);
    }
});

。这意味着,仅考虑到屏蔽不是数值的字符还不够,还要避免屏蔽这些极为常用和必要的键

在Firefox 中,所有由非字符键触发的keypress 事件对应的字符编码为0,而在Safari 3 以前的版本中,对应的字符编码全部为8。为了让代码更通用,只要不屏蔽那些字符编码小于10 的键即可

EventUtil.addHandler(textbox, "keypress", function(event) {
    event = EventUtil.getEvent(event);
    var target = EventUtil.getTarget(event);
    var charCode = EventUtil.getCharCode(event);
    if (!/\d/.test(String.fromCharCode(charCode)) && charCode > 9) {
        EventUtil.preventDefault(event);
    }
});

还有一个问题需要处理:复制、粘贴及其他操作还要用到Ctrl 键。在除IE 之外的所有
浏览器中,前面的代码也会屏蔽Ctrl+C、Ctrl+V,以及其他使用Ctrl 的组合键。因此,最后还要添加一
个检测条件,以确保用户没有按下Ctrl 键

EventUtil.addHandler(textbox, "keypress", function(event) {
    event = EventUtil.getEvent(event);
    var target = EventUtil.getTarget(event);
    var charCode = EventUtil.getCharCode(event);
    if (!/\d/.test(String.fromCharCode(charCode)) && charCode > 9 &&
        !event.ctrlKey) {
        EventUtil.preventDefault(event);
    }
});

 beforecopy:在发生复制操作前触发。
 copy:在发生复制操作时触发。
 beforecut:在发生剪切操作前触发。
 cut:在发生剪切操作时触发。
 beforepaste:在发生粘贴操作前触发。
 paste:在发生粘贴操作时触发。
在Safari、Chrome 和Firefox
中,beforecopy、beforecut 和beforepaste 事件只会在显示针对文本框的上文菜单(预期将发
生剪贴板事件)的情况下触发
,IE 则会在触发copy、cut 和paste 事件之前先行触发这些事件
在实际的事件发生之前,通过beforecopy、beforecut 和beforepaste 事件可以在向剪贴板发
送数据,或者从剪贴板取得数据之前修改数据。不过,取消这些事件并不会取消对剪贴板的操作——只
有取消copy、cut 和paste 事件,才能阻止相应操作发生。

访问剪贴板 -- 》用clipboardData 对象 IE- - window对象,这个对象是相应event 对象的属性;在Firefox、Safari 和Chorme 中,只有在处理剪贴板事件期间clipboardData 对象才有效,

,这是为了防止对剪贴板
的未授权访问;在IE 中,则可以随时访问clipboardData 对象。为了确保跨浏览器兼容性,最好只
在发生剪贴板事件期间使用这个对象。
clipboardData 对象有三个方法:getData(取得数据的格式)、setData()和clearData()
在IE 中,有两种数据格式:"text"
和"URL"。在Firefox、Safari 和Chrome 中,这个参数是一种MIME 类型;不过,可以用"text"代表
"text/plain"。

类似的其它方法也一样'

扩展getClipboardText与setClipboardText方法;

var EventUtil = {
    addHandler: function(element, type, handler) {
        if (element.addEventListener) {
            element.addEventListener(type, handler, false);
        } else if (element.attachEvent) {
            element.attachEvent("on" + type, handler);
        } else {
            element["on" + type] = handler;
        }
    },
    getEvent: function(event) {
        return event ? event : window.event;
    },
    getTarget: function(event) {
        return event.target || event.srcElement;
    },
    preventDefault: function(event) {
        if (event.preventDefault) {
            event.preventDefault();
        } else {
            event.returnValue = false;
        }
    },
    removeHandler: function(element, type, handler) {
        if (element.removeEventListener) {
            element.removeEventListener(type, handler, false);
        } else if (element.detachEvent) {
            element.detachEvent("on" + type, handler);
        } else {
            element["on" + type] = null;
        }
    },
    stopPropagation: function(event) {
        if (event.stopPropagation) {
            event.stopPropagation();
        } else {
            event.cancelBubble = true;
        }
    },
    getRelatedTarget: function(event) {
        if (event.relatedTarget) {
            return event.relatedTarget;
        } else if (event.toElement) {
            return event.toElement;
        } else if (event.fromElement) {
            return event.fromElement;
        } else {
            return null;
        }
    },
    getButton: function(event) {
        if (document.implementation.hasFeature("MouseEvents", "2.0")) {
            return event.button;
        } else {
            switch (event.button) {
                case 0:
                case 1:
                case 3:
                case 5:
                case 7:
                    return 0;
                case 2:
                case 6:
                    return 2;
                case 4:
                    return 1;
            }
        }
    },
    getWheelDelta: function(event) {
        if (event.wheelDelta) {
            return (event.wheelDelta);
        } else {
            return -event.detail * 40;
        }
    },
    getCharCode: function(event) {
        if (typeof event.charCode == "number") {
            return event.charCode;
        } else {
            return event.keyCode;
        }
    },
    getClipboardText: function(event) {
        var clipboardData = (event.clipboardData || window.clipboardData);
        return clipboardData.getData("text");
    },
    setClipboardText: function(event, value) {
        if (event.clipboardData) {
            return event.clipboardData.setData("text/plain", value);
        } else if (window.clipboardData) {
            return window.clipboardData.setData("text", value);
        }
    }
}

例如,如果一个文本框只接受数值,那么就必须检测粘贴过来的值,以确保有效。在paste
事件中,可以确定剪贴板中的值是否有效,如果无效,就可以像下面示例中那样,取消默认的行为。

EventUtil.addHandler(textbox, "paste", function(event) {
    event = EventUtil.getEvent(event);
    var text = EventUtil.getClipboardText(event);
    if (!/^\d*$/.test(text)) {
        EventUtil.preventDefault(event);
    }
});

自动切换焦点:

<form action="" name='myFormname' id="myForm">
<input type="text" name="tel1" id="txtTel1" maxlength="3">
<input type="text" name="tel2" id="txtTel2" maxlength="3">
<input type="text" name="tel3" id="txtTel3" maxlength="4">
</form>

,用户在第一个文本框中输入了3 个数字之后,焦点就会切换到第二个文
本框,再输入3 个数字

(function() {
    function tabForward(event) {
        event = EventUtil.getEvent(event);
        var target = EventUtil.getTarget(event);
        if (target.value.length == target.maxLength) {
            var form = target.form;
            for (var i = 0, len = form.elements.length; i < len; i++) {
                if (form.elements[i] == target) {
                    if (form.elements[i + 1]) {
                        form.elements[i + 1].focus();
                    }
                    return;
                }
            }
        }
    }
    var textbox1 = document.getElementById("txtTel1");
    var textbox2 = document.getElementById("txtTel2");
    var textbox3 = document.getElementById("txtTel3");
    EventUtil.addHandler(textbox1, "keyup", tabForward);
    EventUtil.addHandler(textbox2, "keyup", tabForward);
    EventUtil.addHandler(textbox3, "keyup", tabForward);
})();

HTML5验证
必填字段

<input type="text" name="username" required>

可以检查某个表单字段是否为必填字段。

var isUsernameRequired = document.forms[0].elements["username"].required;

使用下面这行代码可以测试浏览器是否支持required 属性。

var isRequiredSupported = "required" in document.createElement("input");

其它输入类型

<input type="email" name ="email">  //会把-@-当作有效电子邮件地址
<input type="url" name="homepage">

要检测浏览器是否支持这些新类型,可以在JavaScript 创建一个<input>元素,然后将type 属性
设置为"email"或"url",最后再检测这个属性的值

var input = document.createElement("input");
input.type = "email";
var isEmailSupported = (input.type == "email");

例如,想让用户只能输入0 到100 的值,而且这个值必须是5 的倍数,可以这样写代码:

<input type="number" min="0" max="100" step="5" name="count">
  1. 输入模式

HTML5 为文本字段新增了pattern 属性。这个属性的值是一个正则表达式,用于匹配文本框中的
值。例如,如果只想允许在文本字段中输入数值,可以像下面的代码一样应用约束:

<input type="text" pattern="\d+" name="count">

注意,模式的开头和末尾不用加^和$符号(假定已经有了)。这两个符号表示输入的值必须从头到
尾都与模式匹配。

使用以下代码可以检测浏览器是否支持pattern 属性。

var isPatternSupported = "pattern" in document.createElement("input");

检测有效性
用checkValidity()方法可以检测表单中的某个字段是否有

if (document.forms[0].elements[0].checkValidity()){
//字段有效,继续
} else {
//字段无效
}

可以在表单自身调用checkValidity()方法。如果所有表单字段都有
效,这个方法返回true;即使有一个字段无效,这个方法也会返回false。

if(document.forms[0].checkValidity()){
//表单有效,继续
} else {
//表单无效
}
function validity(){
console.log( document.forms[0].elements['emailName'].validity )
console.log( document.forms[0].elements['emailName'] )
}

因此,要想得到更具体的信息,就应该使用validity 属性来检测表单的有效性。下面

var input = document.forms[0].elements['emailName']
if (input.validity && !input.validity.valid) {
    if (input.validity.valueMissing) {
        alert("Please specify a value.")
    } else if (input.validity.typeMismatch) {
        alert("Please enter an email address.");
    } else {
        alert("Value is invalid.");
    }
}

禁用验证
通过设置novalidate 属性,可以告诉表单不进行验证。

<form method="post" action="signup.php" novalidate>
<!--这里插入表单元素-->
</form>

document.forms[0].noValidate = true; //禁用验证

如果一个表单中有多个提交按钮,为了指定点击某个提交按钮不必验证表单,可以在相应的按钮上
添加formnovalidate 属性。

<form method="post" action="foo.php">
<!--这里插入表单元素-->
<input type="submit" value="Regular Submit">
<input type="submit" formnovalidate name="btnNoValidate"
value="Non-validating Submit">
</form>
//禁用验证
document.forms[0].elements["btnNoValidate"].formNoValidate = true;

选择框脚本

select option创建
add(newOption,relOption):插入新的option,其位置相关项relOption之前
multiple,布尔值,允许多项选择,等价于html中的multiple特性;
remove(index),移除给定位置的选项
size:选择框中可见的行数,等价于html中的size特性;
选择的type属性不是'select-one'就是'select-mutiple'这取决于HTML代码中有没有multiple特性;选择框的value属性由当前选中项决定,相应规则如下:

如果没有选中的项,则选择框的value 属性保存空字符串。
如果有一个选中项,而且该项的value 特性已经在HTML 中指定,则选择框的value 属性等
于选中项的value 特性。即使value 特性的值是空字符串,也同样遵循此条规则。
如果有一个选中项,但该项的value 特性在HTML 中未指定,则选择框的value 属性等于该
项的文本。
如果有多个选中项,则选择框的value 属性将依据前两条规则取得第一个选中项的值。
以下面的选择框为例:

var sel = document.getElementById('selLocation');
console.log(sel.value)
<select name="location" id="selLocation">
<option value="Sunnyvale, CA" selected>Sunnyvale</option>  // Sunnyvale, CA
<option value="" selected>Sunnyvale</option> // 空 ''
<option selected>Australia</option> //没有value , //Australia
</select>

每个<option>元素都有一个HTMLOptionElement 对象表示,有下列属性:

index:当前选项在options 集合中的索引。
label:当前选项的标签;等价于HTML 中的label 特性。
selected:布尔值,表示当前选项是否被选中。将这个属性设置为true 可以选中当前选项。
text:选项的文本。
value:选项的值(等价于HTML 中的value 特性)。

访问值:DOM方法(不推荐,效率低)

var selectbox = document.forms[0].elements["location"];
var text = selectbox.options[0].firstChild.nodeValue;
var value = selectbox.options[0].getAttribute('value')
console.log(value)

选项属性代码:(推荐)

var selectbox = document.forms[0].elements["location"];
var text = selectbox.options[0].text;
var value = selectbox.options[0].value;
console.log(value) 

对于只允许选择一项的选择框,访问选中项的最简单方式,就是使用选择框的selectedIndex 属性

//选中的项
var selectedOption = selectbox.options[selectbox.selectedIndex]

console.log(selectedOption) //<option selected="">Australia</option>

console.log('selected index: '+ selectbox.selectedIndex)
console.log('selected text: '+ selectedOption.text)
console.log('selected value: '+ selectedOption.value)

设置select

selectbox.options[0].selected = true;

//但设置多个 

selectbox.options[1].selected = true;
selectbox.options[0].selected = true; //后面覆盖前面

添加option选项一:

var newOption = document.createElement('option')
newOption.appendChild(document.createTextNode('option text'))
newOption.setAttribute('value','Option value')
selectbox.appendChild(newOption)

添加option选项二:Option构造函数,第二个参数可选(IE8不支持)

var newoption = new Option('option txt','option value')
selectbox.appendChild(newoption)

添加option选项三:
add(要添加的新选项,位于新选项之后的选项),为第二个参数传undefined

var nnoption = new Option('ooption ttt','option value')
selectbox.add(nnoption,undefined)  //最佳

移除选项

//1
selectbox.removeChild(selectbox.options[0])
//2
selectbox.remove(0)
//3
selectbox.options[0] = null

//移除所有
function clearSelect(selectbox){
  for(var i=0;i<selectbox.options.length;i++){
    selectbox.remove(i)
  } 
}
clearSelect(selectbox)

移动和重排选项
一个select的option移到另一个select的options
如果为appendChild()方法传入一个文档中已有的元素,那么就从该元素的父节点中移除它,再把它添加到指定的位置

var sel = document.getElementById('selLocation');
var sel2 = document.getElementById('selLocation2');
sel2.appendChild(sel.options['caname'])

要将选择框中的某一项移动到特定位置,最合适的DOM 方法就是insertBefore();appendChild()方法只适用于将选项添加到选择框的最后。要在选择框中向前移动一个选项的位置,可以使用以下代码:

//向前移动一个选项

var optionMoveTo = sel.options[1];
sel.insertBefore(optionMoveTo,sel.options[optionMoveTo.index-1])

表单select序列化

function serialize(form) {
    var parts = [],
        field = null,
        i,
        len,
        j,
        optLen,
        option,
        optValue;
    for (i = 0, len = form.elements.length; i < len; i++) {
        field = form.elements[i];
        switch (field.type) {
            case "select-one":
            case "select-multiple":
                if (field.name.length) {
                    for (j = 0, optLen = field.options.length; j < optLen; j++) {
                        option = field.options[j];
                        if (option.selected) {
                            optValue = "";
                            if (option.hasAttribute) {
                                optValue = (option.hasAttribute("value") ?
                                    option.value : option.text);
                            } else {
                                optValue = (option.attributes["value"].specified ?
                                    option.value : option.text);
                            }
                            parts.push(encodeURIComponent(field.name) + "=" +
                                encodeURIComponent(optValue));
                        }
                    }
                }
                break;
            case undefined: //字段集
            case "file": //文件输入
            case "submit": //提交按钮
            case "reset": //重置按钮
            case "button": //自定义按钮
                break;
            case "radio": //单选按钮
            case "checkbox": //复选框
                if (!field.checked) {
                    break;
                }
                /* 执行默认操作 */
            default:
                //不包含没有名字的表单字段
                if (field.name.length) {
                    parts.push(encodeURIComponent(field.name) + "=" +
                        encodeURIComponent(field.value));
                }
        }
    }
    return parts.join("&");
}

马上变成可编辑选项,contenteditable

 <div class="editable" id="richedit" contenteditable></div>

也可在关闭

var div = document.getElementById("richedit");
div.contentEditable = "true";

富文编辑略。。。。

零js基礎 | 程序設計 | 13 - 事件

<input type="button" value="Click Me" onclick="try{showMessage();}catch(ex){}">

使用try catch捕获错误

        try {
            var dd = document.getElemedntById('dd')

        } catch (ex) {
            alert(123)
        }

addEventListener(),最后这个布尔值参数如果是true,表示在捕获阶段调用事件处理程序;如果是false,表示在冒泡阶段调用事件处理程

try {
    btn.addEventListener('click', function() {
        alert(this.id)
    }, false)
} catch (ex) {
    btn.attachEvent('onclick', function() {
        alert(123)
    })
}

removeEventListener(),要使用命名函数才能移除


btnfn = function() {
    alert(this.id)
}

btn.addEventListener('click', btnfn, false)

btn.removeEventListener('click',btnfn,false)

attachEvent()与detachEvent(),1、this作用域为window; 2、执行顺序由后往前;

btn.attachEvent('onclick',function(){
    alert(this)  //特别注意,这里是window
})

btn.attachEvent("onclick", function(){
alert("Clicked"); //弹2
});
btn.attachEvent("onclick", function(){
alert("Hello world!"); //弹1
});

兼容方法:

var EventUtil = {
    addHandler: function(element, type, handler) {
        if (element.addEventListener) {
            element.addEventListener(type, handler, false);
        } else if (element.attachEvent) {
            element.attachEvent("on" + type, handler);
        } else {
            element["on" + type] = handler;
        }
    },
    removeHandler: function(element, type, handler) {
        if (element.removeEventListener) {
            element.removeEventListener(type, handler, false);
        } else if (element.detachEvent) {
            element.detachEvent("on" + type, handler);
        } else {
            element["on" + type] = null;
        }
    }
};

var handler = function() {
    alert('abc')
}

EventUtil.addHandler(btn, 'click', handler)
EventUtil.removeHandler(btn, "click", handler);

事件对象

对象this 始终等于currentTarget 的值,而target 则只包含事件的实
际目标。如果直接将事件处理程序指定给了目标元素,则this、currentTarget 和target 包含相同
的值。来看下面的例子。

btn.onclick = function(event){
    alert(event.currentTarget === this)
    alert(event.target === this)
}
document.body.onclick = function(event) {
    alert(event.currentTarget == document.body) //true
    alert(this === document.body) //true
    alert(event.target == document.getElementById('mybtn')) //true
}

使用event.type判断不同事件;

var handler = function(event) {
    switch (event.type) {
        case 'click':
            alert('clicked')
            break;
        case 'mouseover':
            alert('mouseovered')
            break;
        case 'mouseout':
            alert('mouseouted');
            break;
    }
}
btn.onclick = handler;
btn.onmouseover = handler;
btn.onmouseout = handler;
btn.onclick = function(event) {
    alert('clicked')
    event.stopPropagation()
}
document.body.onclick = function(event) {
    alert('body clicked')
}

IE下的事件;

所以不能认为this 会始终等于事件目标。故而,最好还是使用event.srcElement 比较保

var btn = document.getElementById("myBtn");
btn.onclick = function(){
alert(window.event.srcElement === this); //true
};
btn.attachEvent("onclick", function(event){
alert(event.srcElement === this); //false
});

cancelBubble 属性与DOM 中的stopPropagation()方法作用相同,都是用来停止事
件冒泡的。由于IE 不支持事件捕获,因而只能取消事件冒泡;但stopPropagatioin()可以同时取消
事件捕获和冒泡

所以综合起来的跨浏览器事件代码为:


var EventUtil = {
    addHandler: function(element, type, handler) {
        if (element.addEventListener) {
            element.addEventListener(type, handler, false);
        } else if (element.attachEvent) {
            element.attachEvent("on" + type, handler);
        } else {
            element["on" + type] = handler;
        }
    },
    getEvent: function(event) {
        return event ? event : window.event;
    },
    getTarget: function(event) {
        return event.target || event.srcElement;
    },
    preventDefault: function(event) {
        if (event.preventDefault) {
            event.preventDefault();
        } else {
            event.returnValue = false;
        }
    },
    removeHandler: function(element, type, handler) {
        if (element.removeEventListener) {
            element.removeEventListener(type, handler, false);
        } else if (element.detachEvent) {
            element.detachEvent("on" + type, handler);
        } else {
            element["on" + type] = null;
        }
    },
    stopPropagation: function(event) {
        if (event.stopPropagation) {
            event.stopPropagation();
        } else {
            event.cancelBubble = true;
        }
    }
};

//使用
btn.onclick = function(event) {
    event = EventUtil.getEvent(event);
};
btn.onclick = function(event) {
    event = EventUtil.getEvent(event);
    var target = EventUtil.getTarget(event);
};
var link = document.getElementById("myLink");
link.onclick = function(event) {
    event = EventUtil.getEvent(event);
    EventUtil.preventDefault(event);
};
var btn = document.getElementById("myBtn");
btn.onclick = function(event) {
    alert("Clicked");
    event = EventUtil.getEvent(event);
    EventUtil.stopPropagation(event);
};
document.body.onclick = function(event) {
    alert("Body clicked");
};

图片加载完后显示弹出

var image = document.getElementById('myimage')
EventUtil.addHandler(image,'load',function(event){
    event = EventUtil.getEvent(event);
    alert(EventUtil.getTarget(event).src);
})

EventUtil.addHandler(window,'load',function(){
    var image = new Image();
    EventUtil.addHandler(image,'load',function(event){
        alert('Image Loaded')
    })
    image.src = 'smail.gif'
})

script外部地址加载完成

EventUtil.addHandler(window,'load',function(){
 var _srcipt = document.createElement('script')
 EventUtil.addHandler(_srcipt,'load',function(event){
     alert('loaded')
 })
 _srcipt.src = 'http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js'
 document.body.appendChild(_srcipt)
})

css外部检测,低版本chrome/firefox可能不支持,未验证

EventUtil.addHandler(window, 'load', function() {
    var _css = document.createElement('link')
    _css.type = 'text/css'
    _css.rel = 'stylesheet'
    EventUtil.addHandler(_css, 'load', function(event) {
        alert('loaded')
    })
    _css.href = 'https://assets-cdn.github.com/assets/frameworks-827a4e004aa05724993ea7616e4af53894825443811a270e2b9bce76e3453f19.css'
    document.body.appendChild(_srcipt)
})

resize事件,推荐写法

EventUtil.addHandler(window, "resize", function(event) {
    alert("Resized");
});

EventUtil.addHandler(window, "scroll", function(event) {
    if (document.compatMode == "CSS1Compat") {
        alert(document.documentElement.scrollTop);
    } else {
        alert(document.body.scrollTop);
    }
});

焦点事件会在页面元素获得或失去焦点时触发。利用这些事件并与document.hasFocus()方法及
document.activeElement 属性配合,可以知晓用户在页面上的行踪。有以下6 个焦点事件。

 blur:在元素失去焦点时触发。这个事件不会冒泡;所有浏览器都支持它。
 DOMFocusIn:在元素获得焦点时触发。这个事件与HTML 事件focus 等价,但它冒泡。只有
Opera 支持这个事件。DOM3 级事件废弃了DOMFocusIn,选择了focusin。
 DOMFocusOut:在元素失去焦点时触发。这个事件是HTML 事件blur 的通用版本。只有Opera
支持这个事件。DOM3 级事件废弃了DOMFocusOut,选择了focusout。
focus:在元素获得焦点时触发。这个事件不会冒泡;所有浏览器都支持它。
 focusin:在元素获得焦点时触发。这个事件与HTML 事件focus 等价,但它冒泡。支持这个
事件的浏览器有IE5.5+、Safari 5.1+、Opera 11.5+和Chrome。
 focusout:在元素失去焦点时触发。这个事件是HTML 事件blur 的通用版本。支持这个事件
的浏览器有IE5.5+、Safari 5.1+、Opera 11.5+和Chrome。

当焦点从页面中的一个元素移动到另一个元素,会依次触发下列事件:
(1) focusout 在失去焦点的元素上触发;
(2) focusin 在获得焦点的元素上触发;
(3) blur 在失去焦点的元素上触发;
(4) DOMFocusOut 在失去焦点的元素上触发;
(5) focus 在获得焦点的元素上触发;
(6) DOMFocusIn 在获得焦点的元素上触发。

事件都不冒泡

var _mybtn = document.getElementById('mybtn')

EventUtil.addHandler(_mybtn, 'focus', function() { //获得
    alert(1)
})
EventUtil.addHandler(_mybtn, 'blur', function() {//失去
    alert(2)
})
EventUtil.addHandler(_mybtn, 'focusin', function() {//获得
    alert(1)
})
EventUtil.addHandler(_mybtn, 'focusout', function() {//失去
    alert(2)
})

鼠标与滚轮事件

click/dblclick/mouseenter/mouseleave/mousemove/mouseout/mouseover/mouseup/

执行顺序:
(1) mousedown
(2) mouseup
(3) click
(4) mouseup
(5) dblclick
可以使用类似下列代码取得鼠标事件的客户端坐标信息:

var div = document.getElementById("myDiv");
EventUtil.addHandler(div, "click", function(event){
event = EventUtil.getEvent(event);
alert("Client coordinates: " + event.clientX + "," + event.clientY);
});

var div = document.getElementById("myDiv");
EventUtil.addHandler(div, "click", function(event){
event = EventUtil.getEvent(event);
alert("Page coordinates: " + event.pageX + "," + event.pageY);
});

在页面没有滚动的情况下,pageX 和pageY 的值与clientX 和clientY 的值相等。

滚动情况下:page 一定大于 client

EventUtil.addHandler(document.body, "click", function(event) {
    event = EventUtil.getEvent(event);
    console.log("Page coordinates: " + event.pageX + "," + event.pageY);
});

EventUtil.addHandler(document.body, "click", function(event) {
    event = EventUtil.getEvent(event);
    console.log("client coordinates: " + event.clientX + "," + event.clientY);
});

IE8不支持page,所以可用client + scroll 相加计算出

var div = document.getElementById("myDiv");
EventUtil.addHandler(div, "click", function(event) {
    event = EventUtil.getEvent(event);
    var pageX = event.pageX,
        pageY = event.pageY;
    if (pageX === undefined) {
        pageX = event.clientX + (document.body.scrollLeft ||
            document.documentElement.scrollLeft);
    }
    if (pageY === undefined) {
        pageY = event.clientY + (document.body.scrollTop ||
            document.documentElement.scrollTop);
    }
    alert("Page coordinates: " + pageX + "," + pageY);
});

鼠标相对于整个屏幕坐标位置,screenX 和 screenY

var div = document.getElementById("myDiv");
EventUtil.addHandler(div, "click", function(event){
event = EventUtil.getEvent(event);
alert("Screen coordinates: " + event.screenX + "," + event.screenY);
});

虽然鼠标事件主要是使用鼠标来触发的,但在按下鼠标时键盘上的某些键的状态也可以影响到所要
采取的操作。这些修改键就是Shift、Ctrl、Alt 和Meta(在Windows 键盘中是Windows 键,在苹果机中
是Cmd 键),它们经常被用来修改鼠标事件的行为。DOM 为此规定了4 个属性,表示这些修改键的状
态:shiftKey、ctrlKey、altKey 和metaKey。这些属性中包含的都是布尔值,如果相应的键被按
下了,则值为true,否则值为false。当某个鼠标事件发生时,通过检测这几个属性就可以确定用户
是否同时按下了其中的键。来看下面的

var div = document.getElementById("myDiv");
EventUtil.addHandler(div, "click", function(event) {
    event = EventUtil.getEvent(event);
    var keys = new Array();
    if (event.shiftKey) {
        keys.push("shift");
    }
    if (event.ctrlKey) {
        keys.push("ctrl");
    }
    if (event.altKey) {
        keys.push("alt");
    }
    if (event.metaKey) {
        keys.push("meta");
    }
    alert("Keys: " + keys.join(","));
});

DOM通过event 对象的relatedTarget 属性提供了相关元素的信
只对于mouseover和mouseout 事件才包含值,其它值是null
IE8及之前版本不支持relatedTarget

mouseover -->IE 的fromElement
mouseout --》IE 的toElement

继续完善

        var EventUtil = {
            addHandler: function(element, type, handler) {
                if (element.addEventListener) {
                    element.addEventListener(type, handler, false);
                } else if (element.attachEvent) {
                    element.attachEvent("on" + type, handler);
                } else {
                    element["on" + type] = handler;
                }
            },
            getEvent: function(event) {
                return event ? event : window.event;
            },
            getTarget: function(event) {
                return event.target || event.srcElement;
            },
            preventDefault: function(event) {
                if (event.preventDefault) {
                    event.preventDefault();
                } else {
                    event.returnValue = false;
                }
            },
            removeHandler: function(element, type, handler) {
                if (element.removeEventListener) {
                    element.removeEventListener(type, handler, false);
                } else if (element.detachEvent) {
                    element.detachEvent("on" + type, handler);
                } else {
                    element["on" + type] = null;
                }
            },
            stopPropagation: function(event) {
                if (event.stopPropagation) {
                    event.stopPropagation();
                } else {
                    event.cancelBubble = true;
                }
            },
            getRelatedTarget: function(event) {
                if (event.relatedTarget) {
                    return event.relatedTarget;
                } else if (event.toElement) {
                    return event.toElement;
                } else if (event.fromElement) {
                    return event.fromElement;
                } else {
                    return null;
                }
            }

        };

    var div = document.getElementById("myDiv");
    EventUtil.addHandler(div, "mouseout", function(event) {
    event = EventUtil.getEvent(event);
    var target = EventUtil.getTarget(event);
    var relatedTarget = EventUtil.getRelatedTarget(event);
    alert("Moused out of " + target.tagName + " to " + relatedTarget.tagName);
    //Moused out of DIV to BODY

鼠标按钮
DOM的button 属性可能有如下3 个值:0 表示主鼠标按钮,1 表示中间的鼠
标按钮(鼠标滚轮按钮),2 表示次鼠标按钮。在常规的设置中,主鼠标按钮就是鼠标左键,而次鼠标
按钮就是鼠标右
最常见的做法就是将IE 模型规范化为DOM 方式

添加getButton

var EventUtil = {
    addHandler: function(element, type, handler) {
        if (element.addEventListener) {
            element.addEventListener(type, handler, false);
        } else if (element.attachEvent) {
            element.attachEvent("on" + type, handler);
        } else {
            element["on" + type] = handler;
        }
    },
    getEvent: function(event) {
        return event ? event : window.event;
    },
    getTarget: function(event) {
        return event.target || event.srcElement;
    },
    preventDefault: function(event) {
        if (event.preventDefault) {
            event.preventDefault();
        } else {
            event.returnValue = false;
        }
    },
    removeHandler: function(element, type, handler) {
        if (element.removeEventListener) {
            element.removeEventListener(type, handler, false);
        } else if (element.detachEvent) {
            element.detachEvent("on" + type, handler);
        } else {
            element["on" + type] = null;
        }
    },
    stopPropagation: function(event) {
        if (event.stopPropagation) {
            event.stopPropagation();
        } else {
            event.cancelBubble = true;
        }
    },
    getRelatedTarget: function(event) {
        if (event.relatedTarget) {
            return event.relatedTarget;
        } else if (event.toElement) {
            return event.toElement;
        } else if (event.fromElement) {
            return event.fromElement;
        } else {
            return null;
        }
    },
    getButton: function(event) {
        if (document.implementation.hasFeature("MouseEvents", "2.0")) {
            return event.button;
        } else {
            switch (event.button) {
                case 0:
                case 1:
                case 3:
                case 5:
                case 7:
                    return 0;
                case 2:
                case 6:
                    return 2;
                case 4:
                    return 1;
            }
        }
    }
};

var div = document.getElementById("myDiv");
EventUtil.addHandler(div, "mousedown", function(event) {
    event = EventUtil.getEvent(event);
    alert(EventUtil.getButton(event)); //左0 中1 右2
});

鼠标滚轮:
mousewheel 事件时显示wheelDelta 的值,
ff:支持:DOMMouseScroll

EventUtil.addHandler(document, "mousewheel", function(event){
event = EventUtil.getEvent(event);
console.log(event.wheelDelta); //上120,下-120
});

//ff

EventUtil.addHandler(window, "DOMMouseScroll", function(event) {
    event = EventUtil.getEvent(event); // 上-3  下3
    alert(event.detail);
});

整合添加:

var EventUtil = {
addHandler: function(element, type, handler) {
    if (element.addEventListener) {
        element.addEventListener(type, handler, false);
    } else if (element.attachEvent) {
        element.attachEvent("on" + type, handler);
    } else {
        element["on" + type] = handler;
    }
},
getEvent: function(event) {
    return event ? event : window.event;
},
getTarget: function(event) {
    return event.target || event.srcElement;
},
preventDefault: function(event) {
    if (event.preventDefault) {
        event.preventDefault();
    } else {
        event.returnValue = false;
    }
},
removeHandler: function(element, type, handler) {
    if (element.removeEventListener) {
        element.removeEventListener(type, handler, false);
    } else if (element.detachEvent) {
        element.detachEvent("on" + type, handler);
    } else {
        element["on" + type] = null;
    }
},
stopPropagation: function(event) {
    if (event.stopPropagation) {
        event.stopPropagation();
    } else {
        event.cancelBubble = true;
    }
},
getRelatedTarget: function(event) {
    if (event.relatedTarget) {
        return event.relatedTarget;
    } else if (event.toElement) {
        return event.toElement;
    } else if (event.fromElement) {
        return event.fromElement;
    } else {
        return null;
    }
},
getButton: function(event) {
    if (document.implementation.hasFeature("MouseEvents", "2.0")) {
        return event.button;
    } else {
        switch (event.button) {
            case 0:
            case 1:
            case 3:
            case 5:
            case 7:
                return 0;
            case 2:
            case 6:
                return 2;
            case 4:
                return 1;
        }
    }
},
getWheelDelta: function(event) {
    if (event.wheelDelta) {
        return (event.wheelDelta);
    } else {
        return -event.detail * 40;
    }
}
}

//使用
//handleMouseWheel()函数可以用作两个事件的处理程序(如果指定的事件不存在,则为该事件指定处
理程序的代码就会静默地失败)
function handleMouseWheel(event){
    event = EventUtil.getEvent(event);
    var delta = EventUtil.getWheelDelta(event);
    alert(delta); //下-120,上120
}
EventUtil.addHandler(document, "mousewheel", handleMouseWheel);
EventUtil.addHandler(document, "DOMMouseScroll", handleMouseWheel);

键盘与文本
键盘事件:
keydown当用户按下键盘上的任意键时触发,而且如果按住不放的话,会重复触发此事件
keypress:当用户按下键盘上的字符键时触发,而且如果按住不放的话,会重复触发此事件。
按下Esc 键也会触发这个事件。Safari 3.1 之前的版本也会在用户按下非字符键时触发keypress
事件。
keyup:当用户释放键盘上的键时触发。
文本事件:textInput,对keypress补充

按下字符:keydown--》keypress【前两个是文本变化之前】--》keyup
连续按:连接触发:keydown--》keypress

按下非字符:keydown --》keyup
也有shiftKey、ctrlKey、altKey 和metaKey[非IE] 属性

键码
在发生keydown 和keyup 事件时,event 对象的keyCode 属性中会包含一个代码

var textbox = document.getElementById("myText");
EventUtil.addHandler(textbox, "keyup", function(event) {
    event = EventUtil.getEvent(event);
    alert(event.keyCode); //显示keyCode值
});
var EventUtil = {
    addHandler: function(element, type, handler) {
        if (element.addEventListener) {
            element.addEventListener(type, handler, false);
        } else if (element.attachEvent) {
            element.attachEvent("on" + type, handler);
        } else {
            element["on" + type] = handler;
        }
    },
    getEvent: function(event) {
        return event ? event : window.event;
    },
    getTarget: function(event) {
        return event.target || event.srcElement;
    },
    preventDefault: function(event) {
        if (event.preventDefault) {
            event.preventDefault();
        } else {
            event.returnValue = false;
        }
    },
    removeHandler: function(element, type, handler) {
        if (element.removeEventListener) {
            element.removeEventListener(type, handler, false);
        } else if (element.detachEvent) {
            element.detachEvent("on" + type, handler);
        } else {
            element["on" + type] = null;
        }
    },
    stopPropagation: function(event) {
        if (event.stopPropagation) {
            event.stopPropagation();
        } else {
            event.cancelBubble = true;
        }
    },
    getRelatedTarget: function(event) {
        if (event.relatedTarget) {
            return event.relatedTarget;
        } else if (event.toElement) {
            return event.toElement;
        } else if (event.fromElement) {
            return event.fromElement;
        } else {
            return null;
        }
    },
    getButton: function(event) {
        if (document.implementation.hasFeature("MouseEvents", "2.0")) {
            return event.button;
        } else {
            switch (event.button) {
                case 0:
                case 1:
                case 3:
                case 5:
                case 7:
                    return 0;
                case 2:
                case 6:
                    return 2;
                case 4:
                    return 1;
            }
        }
    },
    getWheelDelta: function(event) {
        if (event.wheelDelta) {
            return (event.wheelDelta);
        } else {
            return -event.detail * 40;
        }
    },
    getCharCode: function(event) {
        if (typeof event.charCode == "number") {
            return event.charCode;
        } else {
            return event.keyCode;
        }
    }
}

var textbox = document.getElementById("myText");
EventUtil.addHandler(textbox, "keypress", function(event) {
    event = EventUtil.getEvent(event);
    alert(EventUtil.getCharCode(event));//值出字符编码
});

之后可以使用String.fromCharCode(number),再转为正常字符;

所有非字符

特殊情况: ff o 分号键keyCode = 59 【ASCII分号编码】; ie safari分号键 = 186;【按键编码】

由于存在跨浏览器问题,因此本书不推荐使用key、keyIdentifier 或char。

textInput事件
替代keypress 的textInput 事件的行为稍有不同。
区别一:就是任何可以获得焦点的元素都可以触发keypress 事件,但只有可编辑区域才能触发textInput事件。
区别二:textInput 事件只会在用户按下能够输入实际字符的键时才会被触发,
而keypress事件则在按下那些能够影响文本显示的键时也会触发(例如退格键)。

由于textInput 事件主要考虑的是字符,因此它的event 对象中还包含一个data 属性,这个属
性的值就是用户输入的字符(而非字符编码)。换句话说,用户在没有按上档键的情况下按下了S 键,
data 的值就是"s",而如果在按住上档键时按下该键,data 的值就是"S"。

var textbox = document.getElementById("myText");
EventUtil.addHandler(textbox, "textInput", function(event){
event = EventUtil.getEvent(event);
alert(event.data); //按下什么输出什么
});

HTML5事件

  1. contextmenu 事件显示菜单,支持contextmenu 事件的浏览器有IE、Firefox、Safari、Chrome 和Opera 11+。
EventUtil.addHandler(window, "load", function(event) {
    var div = document.getElementById("myDiv");
    EventUtil.addHandler(div, "contextmenu", function(event) {
        event = EventUtil.getEvent(event);
        EventUtil.preventDefault(event);
        var menu = document.getElementById("myMenu");
        menu.style.left = event.clientX + "px";
        menu.style.top = event.clientY + "px";
        menu.style.visibility = "visible";
    });
    EventUtil.addHandler(document, "click", function(event) {
        document.getElementById("myMenu").style.visibility = "hidden";
    });
});
  1. beforeunload 事件,是为了让开发人员有可能在页面卸载前阻止这一操作,为了显示这个弹出对话框,必须将event.returnValue 的值设置为要显示给用户的字符串(对IE 及Fiefox 而言),同时作为函数的值返回
    [

returnValue Boolean 读/写 默认值为true,但将其设置为false就可以取消事件的默认行为(与
DOM中的preventDefault()方法的作用相同)
]

EventUtil.addHandler(window, "beforeunload", function(event) {
    event = EventUtil.getEvent(event);
    var message = "I'm really going to miss you if you go.";
    event.returnValue = message;
    return message;
});
  1. DOMContentLoaded 事件 DOM加载完成后触发;
    要处理DOMContentLoaded 事件,可以为document 或window 添加相应的事件处理程序(尽管

这个事件会冒泡到window,但它的目标实际上是document)。来看下面的例子。
注:这个事件始终都会在load 事件之前触发

EventUtil.addHandler(document, "DOMContentLoaded", function(event){
    alert("Content loaded");
});

不支持的浏览器使用:

setTimeout(function(){
//在此添加事件处理程序
}, 0);
  1. readystatechange 事件
    支持readystatechange 事件的每个对象都有一个readyState 属性

uninitialized(未初始化):对象存在但尚未初始化。
loading(正在加载):对象正在加载数据。
loaded(加载完毕):对象加载数据完成。
interactive(交互):可以操作对象了,但还没有完全加载。
complete(完成):对象已经加载完毕。

不支持则会跳过,所以一般readystatechange事件经常会少于4次;而readyState则总是不连续;

EventUtil.addHandler(document, "readystatechange", function(event) {
    if (document.readyState == "interactive") {
        alert("Content loaded");
    }
});

同时检测交互和完成阶

EventUtil.addHandler(document, "readystatechange", function(event) {
    if (document.readyState == "interactive" || document.readyState == "complete") {
        EventUtil.removeHandler(document, "readystatechange", arguments.callee);
        alert("Content loaded");
    }
});

对于上面的代码来说,当readystatechange 事件触发时,会检测document.readyState 的值,
看当前是否已经进入交互阶段或完成阶段。如果是,则移除相应的事件处理程序以免在其他阶段再执行。
注意,由于事件处理程序使用的是匿名函数,因此这里使用了arguments.callee 来引用该函数。然
后,会显示一个警告框,说明内容已经加载完毕。这样编写代码可以达到与使用DOMContentLoaded
十分相近的效

下面展示了一段加载外部JavaScript/css 文件的代码

//js
EventUtil.addHandler(window, "load", function() {
    var script = document.createElement("script");
    EventUtil.addHandler(script, "readystatechange", function(event) {
        event = EventUtil.getEvent(event);
        var target = EventUtil.getTarget(event);
        if (target.readyState == "loaded" || target.readyState == "complete") {
            EventUtil.removeHandler(target, "readystatechange", arguments.callee);
            alert("Script Loaded");
        }
    });
    script.src = "example.js";
    document.body.appendChild(script);
});

//css
EventUtil.addHandler(window, "load", function() {
    var link = document.createElement("link");
    link.type = "text/css";
    link.rel = "stylesheet";
    EventUtil.addHandler(script, "readystatechange", function(event) {
        event = EventUtil.getEvent(event);
        var target = EventUtil.getTarget(event);
        if (target.readyState == "loaded" || target.readyState == "complete") {
            EventUtil.removeHandler(target, "readystatechange", arguments.callee);
            alert("CSS Loaded");
        }
    });
    link.href = "example.css";
    document.getElementsByTagName("head")[0].appendChild(link);
});
  1. pageshow 和pagehide 事件

(略)

  1. hashchange 事件
    必须要把hashchange 事件处理程序添加给window 对象,然后URL 参数列表只要变化就会调用

它。此时的event 对象应该额外包含两个属性:oldURL 和newURL。这两个属性分别保存着参数列表
变化前后的完整URL。例如:

EventUtil.addHandler(window, "hashchange", function(event){
    alert("Old URL: " + event.oldURL + "\nNew URL: " + event.newURL);
}); //前后变化,完整url

EventUtil.addHandler(window, "hashchange", function(event){
    alert("Current hash: " + location.hash);
});//当前 #后字符

检测是否支持

var isSupported = ("onhashchange" in window) && (document.documentMode ===
undefined || document.documentMode > 7);

13.4.8 设备事件

  1. orientationchange 事件

苹果公司为移动Safari 中添加了orientationchange 事件,以便开发人员能够确定用户何时将设
备由横向查看模式切换为纵向查看模式。移动Safari 的window.orientation 属性中可能包含3 个值:
0 表示肖像模式,90 表示向左旋转的横向模式(“主屏幕”按钮在右侧),-90 表示向右旋转的横向模
式(“主屏幕”按钮在左侧)。相关文档中还提到一个值,即180 表示iPhone 头朝下;但这种模式至今
尚未得到支持。图13-10 展示了window.orientation 的每个值的含义

只要用户改变了设备的查看模式,就会触发orientationchange 事件。此时的event 对象不包
含任何有价值的信息,因为唯一相关的信息可以通过window.orientation 访问到。下面是使用这个
事件的典型示例

EventUtil.addHandler(window, "load", function(event) {
    var div = document.getElementById("myDiv");
    div.innerHTML = "Current orientation is " + window.orientation;
//改变时触发
    EventUtil.addHandler(window, "orientationchange", function(event) {
        div.innerHTML = "Current orientation is " + window.orientation;
    });
});
//横屏
window.orientation == 90 || window.orientation == -90
//竖屏
window.orientation == 0 || window.orientation == 180
//粗略检测,并不准,ie都返回false
alert(document.implementation.hasFeature("orientationchange", "2.0"))
  1. deviceorientation 事件
  2. 事件的意图是告诉开发人员设备在空间中朝向哪儿,而不是如何移动;

设备在三维空间中是靠x、y 和z 轴来定位的 三个值都是0

x轴: 左 --》 右;
y轴: 下 --》 上;
z轴: 后 --》 前;

事件对象包含以下5 个属性。
 alpha:在围绕z 轴旋转时(即左右旋转时),y 轴的度数差;是一个介于0 到360 之间的浮点数。
 beta:在围绕x 轴旋转时(即前后旋转时),z 轴的度数差;是一个介于180 到180 之间的浮点数。
 gamma:在围绕y 轴旋转时(即扭转设备时),z 轴的度数差;是一个介于90 到90 之间的浮点数。
 absolute:布尔值,表示设备是否返回一个绝对值。
 compassCalibrated:布尔值,表示设备的指南针是否校准过。

13.4.9 触摸与手势事件

touchstart/touchmove/touchend/touchcancel
每個event對象都有以下常見屬性;
bubbles、cancelable、view、clientX、clientY、screenX、screenY、detail、altKey、shiftKey、ctrlKey 和metaKey。

touches:表示当前跟踪的触摸操作的Touch 对象的数组。
 targetTouchs:特定于事件目标的Touch 对象的数组。
 changeTouches:表示自上次触摸以来发生了什么改变的Touch 对象的数组。
每个Touch 对象包含下列属性。

clientX:触摸目标在视口中的x 坐标。
 clientY:触摸目标在视口中的y 坐标。
 identifier:标识触摸的唯一ID。
 pageX:触摸目标在页面中的x 坐标。
 pageY:触摸目标在页面中的y 坐标。
 screenX:触摸目标在屏幕中的x 坐标。
 screenY:触摸目标在屏幕中的y 坐标。
 target:触摸的DOM 节点目标。
使用这些属性可以跟踪用户对屏幕的

function handleTouchEvent(event) {
    if (event.touches.length == 1) {
        var _output = document.getElementById('output')
        switch (event.type) {
            case 'touchstart':
                _output.innerHTML = 'Touch started' + event.touches[0].clientX + ',' + event.touches[0].clientY
                break;
            case 'touchend':
                _output.innerHTML += '<br> Touche ended' + event.changedTouches[0].clientX + ':' +
                    event.changedTouches[0].clientY + ';';
                break;
            case 'touchmove':
                event.preventDefault();
                _output.innerHTML += '<br> Touch move' +
                    event.changedTouches[0].clientX + ':' +
                    event.changedTouches[0].clientY + ';';
                break;
        }
    }
}

EventUtil.addHandler(document, 'touchstart', handleTouchEvent)
EventUtil.addHandler(document, 'touchend', handleTouchEvent)
EventUtil.addHandler(document, 'touchmove', handleTouchEvent)

當touchstart發生: 觸摸信息輸出到

中 當touchmove發生:取消默認行為,阻止滾動,輸出變化信息
當touchend發生時:輸出有關觸摸操作信息,在touchend發生時:touches集合中就沒有任何信息,因為不存在活動觸摸操作,此時必須使用changeTouches集合

發生順序如下:

(1) touchstart
(2) mouseover
(3) mousemove(一次)
(4) mousedown
(5) mouseup
(6) click
(7) touchend

  1. 手势事件

當兩個手指觸摸時,就會產生手勢,手勢通常會改變顯示項大小,或旋轉顯示項,三個事件

 gesturestart:当一个手指已经按在屏幕上而另一个手指又触摸屏幕时触发。
 gesturechange:当触摸屏幕的任何一个手指的位置发生变化时触发。
 gestureend:当任何一个手指从屏幕上面移开时触发。

当一个手指在屏幕时,触发touchstart
另一个手指放在屏幕,先触发gesturestart事件,随后触发该手指的touchstart
如果一个或两个手指在屏幕移动,触发gesturechange事件,但只要有一手指移开,触发gestureend事件,
最后再触发该手指的touchend事件;

每个event对象包含::
bubbles、cancelable、view、clientX、clientY、screenX、screenY、detail、altKey、shiftKey、
ctrlKey 和metaKey、还有两个额外属性:rotation、scale

rotation: 表示手指变化引起的旋转角度, -值逆时针,+ 顺时针(该值从0开始)
scale: 表示两个手指距离变化情况,如内收缩会缩短距离,从1开始;

function handleTouchEvent(event) {

    var _output = document.getElementById('output')
    switch (event.type) {
        case 'gesturestart':
            _output.innerHTML = 'Gesture started rotation = ' + event.rotation + ', scale' + event.scale;
            break;
        case 'gestureend':
            _output.innerHTML += '<br> Gesture end rotation = ' + event.rotation + ', scale' + event.scale;
            break;
        case 'gesturechange':
            event.preventDefault();
            _output.innerHTML += '<br> Gesture change rotation = ' + event.rotation + ', scale' + event.scale;
            break;
    }
}

EventUtil.addHandler(document, 'gesturestart', handleTouchEvent)
EventUtil.addHandler(document, 'gestureend', handleTouchEvent)
EventUtil.addHandler(document, 'gesturechange', handleTouchEvent)

13.5.1 事件委托

var list = document.getElementById('myLinks')
EventUtil.addHandler(list, 'click', function(event) { //父级绑定click
    event = EventUtil.getEvent(event)
    var target = EventUtil.getTarget(event)
    switch (target.id) {  //检测目标元素属性
        case 'doSomething':
            document.title = 'i cahnge the document is ttile'
            break;
        case 'goSomewhere':
            location.href = 'http://www.x.com';
            break;
        case 'sayHi':
            alert('hi')
            break;
    }
})
var btn = document.getElementById('myBtn')
btn.onclick = function(){
    document.getElementById('myDiv').innerHTML = 'processing'  //按钮被替换了,但click事件还在
}

//所以
var btn = document.getElementById('myBtn')
btn.onclick = function(){
    btn.onclick = null; //先移除
    document.getElementById('myDiv').innerHTML = 'processing'  //按钮被替换了,但click事件还在
}

13.6.1 DOM中的事件模拟

  1. 模拟鼠标事件
    创建新的鼠标事件对象并为其指定必要的信息,就可以模拟鼠标事件。创建鼠标事件对象的方法是

为createEvent()传入字符串"MouseEvents"。返回的对象有一个名为initMouseEvent()方法,
用于指定与该鼠标事件有关的信息。这个方法接收15 个参数,分别与鼠标事件中每个典型的属性一一
对应;这些参数的含义如下。

对应;这些参数的含义如下。
 type(字符串):表示要触发的事件类型,例如"click"。
 bubbles(布尔值):表示事件是否应该冒泡。为精确地模拟鼠标事件,应该把这个参数设置为
true。
 cancelable(布尔值):表示事件是否可以取消。为精确地模拟鼠标事件,应该把这个参数设
置为true。
 view(AbstractView):与事件关联的视图。这个参数几乎总是要设置为document.defaultView。
 detail(整数):与事件有关的详细信息。这个值一般只有事件处理程序使用,但通常都设置为0。
 screenX(整数):事件相对于屏幕的X 坐标。
 screenY(整数):事件相对于屏幕的Y 坐标。
 clientX(整数):事件相对于视口的X 坐标。
 clientY(整数):事件想对于视口的Y 坐标。
 ctrlKey(布尔值):表示是否按下了Ctrl 键。默认值为false。
 altKey(布尔值):表示是否按下了Alt 键。默认值为false。
 shiftKey(布尔值):表示是否按下了Shift 键。默认值为false。
 metaKey(布尔值):表示是否按下了Meta 键。默认值为false。
 button(整数):表示按下了哪一个鼠标键。默认值为0。
 relatedTarget(对象):表示与事件相关的对象。这个参数只在模拟mouseover 或mouseout
时使用。

var btn = document.getElementById('myBtn')

//创建事件对象
var event = document.createEvent('mouseEvents')

//初始化
event.initMouseEvent("click", true, true, document.defaultView, 0, 0, 0, 0, 0,
false, false, false, false, 0, null);

//触发
btn.dispatchEvent(event)
  1. 模拟键盘事件
    一、DOM3 级规定,调用createEvent()并传入"KeyboardEvent"就可以创建一个键盘事件。返回的

事件对象会包含一个initKeyEvent()方法,这个方法接收下列参数
 type(字符串):表示要触发的事件类型,如"keydown"。
 bubbles(布尔值):表示事件是否应该冒泡。为精确模拟鼠标事件,应该设置为true。
 cancelable(布尔值):表示事件是否可以取消。为精确模拟鼠标事件,应该设置为true。
 view (AbstractView ):与事件关联的视图。这个参数几乎总是要设置为document.
defaultView。
 key(布尔值):表示按下的键的键码。
 location(整数):表示按下了哪里的键。0 表示默认的主键盘,1 表示左,2 表示右,3 表示
数字键盘,4 表示移动设备(即虚拟键盘),5 表示手柄。
 modifiers(字符串):空格分隔的修改键列表,如"Shift"。
 repeat(整数):在一行中按了这个键多少次。

DOM3级不提倡使用keypress 事件,因此只能利用这种技术来模拟keydown 和keyup 事件。

var textbox = document.getElementById("myTextbox"),
    event;
//以DOM3 级方式创建事件对象
if (document.implementation.hasFeature("KeyboardEvents", "3.0")) {
    event = document.createEvent("KeyboardEvent");
    //初始化事件对象
    event.initKeyboardEvent("keydown", true, true, document.defaultView, "a", 0, "Shift", 0);
}
//触发事件
textbox.dispatchEvent(event);

这个例子模拟的是按住Shift 的同时又按下A 键。在使用document.createEvent
("KeyboardEvent")之前,应该先检测浏览器是否支持DOM3 级事件;其他浏览器返回一个非标准的
KeyboardEvent 对象。

二、在Firefox 中,调用createEvent()并传入"KeyEvents"就可以创建一个键盘事件。返回的事件
对象会包含一个initKeyEvent()方法,这个方法接受下列10 个参数。

 type(字符串):表示要触发的事件类型,如"keydown"。
 bubbles(布尔值):表示事件是否应该冒泡。为精确模拟鼠标事件,应该设置为true。
 cancelable(布尔值):表示事件是否可以取消。为精确模拟鼠标事件,应该设置为true。
 view(AbstractView):与事件关联的视图。这个参数几乎总是要设置为document.default-
View。
 ctrlKey(布尔值):表示是否按下了Ctrl 键。默认值为false。
 altKey(布尔值):表示是否按下了Alt 键。默认值为false。
 shiftKey(布尔值):表示是否按下了Shift 键。默认值为false。
 metaKey(布尔值):表示是否按下了Meta 键。默认值为false。
 keyCode(整数):被按下或释放的键的键码。这个参数对keydown 和keyup 事件有用,默认
值为0。
 charCode(整数):通过按键生成的字符的ASCII 编码。这个参数对keypress 事件有用,默
认值为0。

//只适用于Firefox
var textbox = document.getElementById("myTextbox")
//创建事件对象
var event = document.createEvent("KeyEvents");
//初始化事件对象
event.initKeyEvent("keypress", true, true, document.defaultView, false, false,
false, false, 65, 65);
//触发事件
textbox.dispatchEvent(event);

三、其它浏览器
首先创建了一个通用事件,然后调用initEvent()对其进行初始化,最后又为其添加了
键盘事件的具体信息。在此必须要使用通用事

var textbox = document.getElementById("myTextbox");
//创建事件对象
var event = document.createEvent("Events");
//初始化事件对象
event.initEvent(type, bubbles, cancelable);
event.view = document.defaultView;
event.altKey = false;
event.ctrlKey = false;
event.shiftKey = false;
event.metaKey = false;
event.keyCode = 65;
event.charCode = 65;
//触发事件
textbox.dispatchEvent(event);
  1. 模拟其他事件
    模拟变动事件, 可以使用createEvent("MutationEvents") 创建一个包含

initMutationEvent() 方法的变动事件对象。这个方法接受的参数包括: type 、bubbles 、
cancelable、relatedNode、preValue、newValue、attrName 和attrChange。下面来看一个模
拟变动事件的例子。

var event = document.createEvent("MutationEvents");
event.initMutationEvent("DOMNodeInserted", true, false, someNode, "","","",0);
targ et.dispatchEvent(event);

以上代码模拟了DOMNodeInserted

  1. 自定义DOM 事件

创建新的自定义事件,可以调用createEvent("CustomEvent")。返回的对象有一个名为initCustomEvent()的方法

var div = document.getElementById("myDiv"),
    event;
EventUtil.addHandler(div, "myevent", function(event) {
    alert("DIV: " + event.detail);
});
EventUtil.addHandler(document, "myevent", function(event) {
    alert("DOCUMENT: " + event.detail);
});
if (document.implementation.hasFeature("CustomEvents", "3.0")) {
    event = document.createEvent("CustomEvent");
    event.initCustomEvent("myevent", true, false, "Hello world!");
    div.dispatchEvent(event);
}

创建了一个冒泡事件"myevent"。而event.detail 的值被设置成了一个简单的字符串,
然后在

元素和document 上侦听这个事件。因为initCustomEvent()方法已经指定这个事件应
该冒泡,所以浏览器会负责将事件向上冒泡到document。

支持自定义DOM事件的浏览器有IE9+和Firefox 6+。

13.6.2 IE中的事件模拟
IE8之前的版本
思路相似:先创建event 对象,然后为其指定相应的信息,然后再使用该对象来触发事件
调用document.createEventObject()方法可以在IE 中创建event 对象。但与DOM方式不同的是,这个方法不接受参数,结果会返回一个通用的event 对象
最后一步就是在目标上调用fireEvent()方法

fireEvent(事件处理程序的名称,event对象)

在调用fireEvent()方法时,会自动为event 对象添加srcElement 和type 属性

其他属性则都是必须通过手工添加的。换句话说,模拟任
何IE 支持的事件都采用相同的模式。例如,下面的代码模拟了在一个按钮上触发click 事件过程

var btn = document.getElementById("myBtn");
//创建事件对象
var event = document.createEventObject();
//初始化事件对象
event.screenX = 100;
event.screenY = 0;
event.clientX = 0;
event.clientY = 0;
event.ctrlKey = false;
event.altKey = false;
event.shiftKey = false;
event.button = 0;
//触发事件
btn.fireEvent("onclick", event);

//触发keypress事件

var textbox = document.getElementById("myTextbox");
//创建事件对象
var event = document.createEventObject();
//初始化事件对象
event.altKey = false;
event.ctrlKey = false;
event.shiftKey = false;
event.keyCode = 65;
//触发事件
textbox.fireEvent("onkeypress", event);