灏天阁

事件处理

· Yin灏

事件基础

事件处理函数

var button = document.getElementById('btn');
button.onclick = function(e) {
  var e = e || window.e;
  console.log(e);
  document.write(e.srcElement ? e.srcElement : e.target); // 获取单击对象的标签名
  // [object HTMLButtonElement]
}

/*
  IE 事件模型和 DOM 事件模型对 event 对象的处理方式不同:IE 把 event 对象定义为 window 对象的一个属性,而 dom 事件模型把 event 定义为事件处理函数的默认参数。
*/

/*
  event.srcElement 表示当前的事件源,即响应事件的当前对象,这是 IE 模型用法。但是 DOM 事件模型不支持该属性,需要使用 event 对象的 target 属性。
  事件处理函数中,this 表示当前事件对象,与 event 对象的 srcElement 属性或者 target 属性所代表的意思相同。
*/

**示例:**点击改变背景色

<button id="btn" onclick="this.style.background='red'">按钮</button>

<!-- 也可以如下表示 -->

<button id="btn" onclick="(event.srcElement ? event.srcElement : event.target).style.background='blue'">按钮</button>

一些情况下,this 并非都表示当前事件对象。

下面示例分别用 this 和事件源来指定当前对象,但是会发现 this 并没有指向的事件对象按钮,而是 window 对象。

<button id="btn1" onclick="btn1()">按钮1</button>
<button id="btn2" onclick="btn2(event)">按钮2</button>
function btn1() { // 抛出错误
	this.style.background = 'red'; // 事件处理函数,函数中的 this 表示调用该函数的当前对象
}

function btn2(event) {
    event = event || window.event;
    var src = event.srcElement ? event.srcElement : event.target;
    src.style.background = 'red';
}

注册事件

element.addEventListener(String type, Function listener, Boolean useCapture);
/*
  type:注册事件的类型名
  listener: 监听函数,事件处理函数,默认传递给它的唯一参数就是 event 对象
  useCapture:如果是 true,则指定的事件处理函数将在事件传播的捕获阶段触发,如果是 false,则事件处理函数将在冒泡阶段触发。
*/
// 循环为每一个按钮添加处理函数
var btn = document.getElementsByTagName('button');
for(var i in btn) {
	btn[i].addEventListener("click", function() {
		alert(this.innerHTML);
	}, true); //为每个按钮对象注册一个事件处理函数,定义在捕获阶段进行响应
}
// 段落 p 的移入移出设置不同的处理函数
var p1 = document.getElementById('p1');
p1.addEventListener('mouseover', function() {
	this.style.background = 'blue';
});
p1.addEventListener('mouseout', function() {
	this.style.background = 'red';
})

IE 事件模型使用 attachEvent(etype, eventName)

  • etype:设置事件类型,如:onclickonkeyuponmousemove 等。

  • eventName:设置事件名称,也就是事件处理函数。

var p1 = document.getElementById('p1');
p1.attachEvent('onmouseover', function() {
	p1.style.background = 'blue';
})
p1.attachEvent('onmouseout', function() {
	p1.style.background = 'red';
})

销毁事件

element.removeEventListener(String type, Function listener, Boolean useCapture);

**示例:**删除事件监听

<input type="button" id="a" value="点我" />
<input type="button" id="b" value="删除事件" />
var a = document.getElementById('a');
var b = document.getElementById('b');

function ok() {
	alert('您好,欢迎光临!')
}

function delete_event() {
	a.removeEventListener('click', ok, false);
}

a.addEventListener('click', ok, false);
b.addEventListener('click', delete_event, false);

IE事件模型使用 detachEvent() 注销事件:

element.detachEvent(etype, eventName);

示例:IE 模型删除事件监听

var p1 = document.getElementById('p1');

var f1 = function() {
	p1.style.background = 'blue';
}

var f2 = function() {
	p1.style.background = 'red';
	p1.detachEvent('onmouseover', f1);
	p1.detachEvent('onmouseout', f2);
}

p1.attachEvent('onmouseover', f1);
p1.attachEvent('onmouseout', f2);

兼容 IE 事件模型和 DOM 事件模型:

var p1 = document.getElementById('p1');

var f1 = function() {
	p1.style.background = 'blue';
}

var f2 = function() {
	p1.style.background = 'red';
	if(p1.detachEvent) {
		p1.detachEvent('onmouseover', f1);
	  p1.detachEvent('onmouseout', f2);
	} else {
		p1.removeEventListener('mouseover', f1);
	  p1.removeEventListener('mouseout', f2);
	}
}

if(p1.attachEvent) {
	p1.attachEvent('onmouseover', f1);
  p1.attachEvent('onmouseout', f2);
} else {
	p1.addEventListener('mouseover', f1);
  p1.addEventListener('mouseout', f2);
}

使用 event 对象

**示例:**禁止超链接默认的跳转行为。

// <a href="https://www.baidu.com/" id="a1">禁止超链接跳转</a>
document.getElementById('a1').onclick = function(e) {
	e = e || window.event;
	var target = e.target || e.srcElement;
	if(target.nodeName !== 'A') { // 仅针对超链接起作用
		return;
	}
	if(typeof e.preventDefault === 'function') {
		e.preventDefault();
		e.stopPropagation();
	} else { // 兼容 ie 模型
		e.returnValue = false; // 禁止默认行为
		e.cancelBubble = true; // 禁止冒泡
	}
} 

事件委托

就是把目标节点的事件绑定到祖先节点上,这种简单而优雅的事件注册方式是基于事件传播过程中,逐层冒泡总能被祖先节点捕获。

这样做的好处:优化代码,提升运行性能,真正把 HTMLJavascript 分离,也能防止出现在动态添加或删除节点过程中注册的事件丢失的现象。

**示例:**点击触发每个列表项的 click 事件,动态添加的列表项也要触发 click 事件。

/*
  <button id="btn">添加</button>
  <ul id="list">
    <li>列表项1</li>
    <li>列表项2</li>
    <li>列表项3</li>
  </ul>
*/

// e.target 触发事件的元素, e.currentTarget 绑定的元素

var ul = document.getElementById('list');
ul.addEventListener('click', function(e) {
	var e = e || window.event;
	var target = e.target || e.srcElement;
	if(e.target && e.target.nodeName.toUpperCase() == 'LI') {
		alert(e.target.innerHTML);
	}
}, false); // li 冒泡被 ul 捕获到

var i = 4;
var btn = document.getElementById('btn');
btn.addEventListener('click', function() {
	var li = document.createElement('li');
	li.innerHTML = "列表项目" + i++;
	ul.appendChild(li);
})

使用鼠标事件

鼠标事件类型:

  1. click
  2. dbclick
  3. mousedown
  4. mouseout:鼠标指针位于某个元素上且将要移出元素的边界时发生
  5. mouseover:鼠标指针移出某个元素到另一个元素上时发生
  6. mouseup
  7. mousemove:鼠标在某个元素上时持续发生
var p1 = document.getElementsByTagName('p')[0];
var t = document.getElementById('text');

function f() {
  var event = event || window.event;
  t.value = event.type; // 获取当前事件类型
}
// p1.onmouseover = f;
// p1.onmouseout = f;
// p1.onmousedown = f;
// p1.onmouseup = f;
// p1.onmousemove = f;
// p1.onclick = f;
// p1.ondblclick = f;

鼠标移动

/*
  mousemove 事件类型是一个实时响应的事件,当鼠标指针的位置发生变化时,就会触发 mousemove 事件。
*/
var box = document.getElementById('box');
box.style.position = "absolute";
box.style.width = '160px';
box.style.height = '120px';
box.style.backgroundColor = 'red';

var mx, my, ox, oy;

function e(event) {
  if(!event) {
    event = window.event;
    event.target = event.srcElement;
    event.layerX = event.offsetX;
    event.layerY = event.offsetY;
  }
  event.mx = event.pageX || event.clientX + document.body.scrollLeft; // 计算鼠标指针的 x 轴距离
  event.my = event.pageY || event.clientY + document.body.scrollTop; // 计算鼠标指针的 y 轴距离
  return event;
}

// 定义鼠标事件处理函数
document.onmousedown = function(event) {
  event = e(event);
  o = event.target;
  ox = parseInt(o.offsetLeft); // 被拖放元素距离浏览器左侧的偏移量
  oy = parseInt(o.offsetTop); // 被拖放元素距离浏览器顶部的偏移量
  // console.log(ox, oy);
  mx = event.mx; // 按下鼠标指针的 x 轴坐标
  my = event.my; // 按下鼠标指针的 y 轴坐标
  document.onmousemove = move;
  document.onmouseup = stop;
}

function move(event) {
  event = e(event);
  o.style.left = ox + event.mx - mx + 'px';
  o.style.top = oy + event.my - my + 'px';
}

function stop(event) {
  event = e(event);
  ox = parseInt(o.offsetLeft);
  oy = parseInt(o.offsetTop);
  mx = event.mx;
  my = event.my;
  o = document.onmousemove = document.onmouseup = null;
}

鼠标经过

鼠标经过包括移过和移出两种时间类型,当移动鼠标指针到某个元素上时,将触发 mouseover 事件,而当把鼠标移出某个元素时,将触发 mouseout 事件,如果从父元素中移到子元素中时,也会触发父元素的 mouseover 事件。

/*
  下面嵌套 3 个 div 元素,当外层的父元素移动到内部的子元素中时,将会触发父元素的 mouseover 事件,但是不会触发 mouseout 事件
*/

/*
<div style="outline: 1px solid red; padding: 20px;width: 200px;">
  第一层
  <div style="outline: 1px solid blue; padding: 20px;">
    第二层
     <div style="outline: 1px solid black; padding: 20px;">
       第三层
     </div>
  </div>
</div>
*/
var div = document.getElementsByTagName('div');

for(var i = 0; i < div.length; i++) {
  div[i].onmouseover = function(e) {
    this.style.border = "solid blue";
  }
  div[i].onmouseout = function(e) {
    this.style.border = "solid red";
  }
}

鼠标来源

当一个事件发生后,可以使用事件对象的 target 属性来获取发生事件的节点元素,如果是 IE 模型中实现相同的目标,可以使用 srcElement 属性。

/*
  <div>div 元素</div>
*/
var div = document.getElementsByTagName('div');
div.onmouseover = function(e) {
  var e = e || window.event;
  var o = e.target || e.srcElement;
  console.log(o.tagName);
}
/*
  在 DOM 事件模型中还定义了 currentTarget 属性,当事件在传播过程中(如捕获和冒泡阶段)时,该属性值于 target 属性值不同,因此,一般在事件处理函数中,应该使用该属性,而不是 this 关键词获取当前对象。
*/
/*
  如果想用鼠标来移动某个元素,在 DOM 事件模型中可以使用 relatedTarget 属性来获取当前事件对象的相关节点元素;而在 IE 事件模型中,可以使用 fromElement 获取 mouseover 事件中鼠标移到多的元素,使用 toElement 属性获取在 mouseeout 事件中鼠标移到的文档元素。
*/
var div = document.getElementsByTagName('div');
div.onmouseover = function(e) {
  var e = e || window.event;
  var o = e.relatedTarget || e.fromElement;
  console.log(o.tagName);
}
div.onmouseout = function(e) {
  var e = e || window.event;
  var o = e.relatedTarget || e.toElement;
  console.log(o.tagName);
}

鼠标定位

属性 说明 兼容性
clientX 以浏览器左上角为原点,定位 x 轴坐标 所有浏览器,不兼容 safari
clientY 以浏览器左上角为原点,定位 y 轴坐标 所有浏览器,不兼容 safari
offsetX 以当前事件的目标对象左上角为原点,定位 x 轴坐标 所有浏览器,不兼容 Mozilla
offsetY 以当前事件的目标对象左上角为原点,定位 y 轴坐标 所有浏览器,不兼容 Mozilla
pageX 以 document 对象左上角为原点,定位 x 轴坐标 所有浏览器,不兼容 IE
pageY 以 document 对象左上角为原点,定位 y 轴坐标 所有浏览器,不兼容 IE
screenX 计算机屏幕左上角为原点,定位 x 轴坐标 所有浏览器
screenY 计算机屏幕左上角为原点,定位 y 轴坐标 所有浏览器
layerX 最近的绝对定位的父元素,如果没有 则为 document 左上角为原点,定位 x 轴坐标 Mozilla 和 Safari
layerY 最近的绝对定位的父元素,如果没有 则为 document 左上角为原点,定位 y 轴坐标 Mozilla 和 Safari

获取坐标值(各浏览器的兼容性写法)

/*
  <div id="div1">鼠标跟随</div>
*/
var pos = function(o, x, y, event) {
  var posX = 0, posY = 0;
  var e = event || window.event;
  if(e.pageX || e.pageY) {
     posX = e.pageX;
     posY = e.pageY;
  } else if(e.clientX || e.clientY) {
    posX = e.clientX + document.documentElement.scrollLeft + document.body.scrollLeft;
    posY = e.clientY + document.documentElement.scrollTop + document.body.scrollTop;
  }
  o.style.position = "absolute";
  o.style.top = (posY + y) + 'px';
  o.style.left = (posX + x) + 'px';
}

var div1 = document.getElementById('div1');
document.onmousemove = function(event) {
  pos(div1, 10, 20, event);
}

获取鼠标指针在元素内的坐标,使用 offsetX 和 offsetY 属性可以实现这样的目标,但是 Mozilla 不支持,可以选用 layerX 和 layerY 属性来兼容 Mozilla 浏览器。

/*
<input type="text" id="text">
<span style="position: absolute;">
  <div id="div1" style="width:200px;height:160px;border: 1px solid red">鼠标跟随</div>
</span>
*/
var t = document.getElementById('text');
var div1 = document.getElementById('div1');
div1.onmousemove = function(event) {
  var event = event || window.event;
  if(event.offsetX || event.offsetY) {
    t.value = event.offsetX + ' ' + event.offsetY;
  } else if(event.layerX || event.layerY) {
    t.value = (event.layerX - 1) + ' ' + (event.layerY - 1);
  }
}

鼠标按键

通过事件对象的 button 属性可以获取当前鼠标按下的键,该属性可用于 click、mousedown、mouseup 事件类型,不过不同的模型约定不同。

单击 IE 事件模型 DOM 事件模型
左键 1 0
中键 2 2
右键 4 1

下面监测右键单击操作,并阻止默认行为:

document.onclick = function(e) {
  var e = e || window.event;
  if(e.button == 2) {
    e.preventDefault();
    return false;
  }
}

使用键盘事件

  • keydown: 在键盘上按下某个键时触发,如果按住某个键,会不断触发,但是 Opera 浏览器不支持这种连续操作。
  • keypress: 按下某个键盘键,并释放的时候触发。如果按住某个键,会不断触发该事件。
  • keyup: 释放某个键盘键时触发,该事件仅在松开键盘时触发一次,不是一个持续的响应状态。

下面示例实时捕获键盘操作的各种细节,即键盘响应事件类型及对应的键值。

/*
  <textarea id="key"></textarea>
*/
var key = document.getElementById('key');
key.onkeydown = f;
key.onkeyup = f;
key.onkeypress = f;

function f(e) {
  var e = e || window.event;
  var s = e.type + " " + e.keyCode;
  key.value = s;
}

键盘事件属性

键盘事件定义了很多属性,利用这些属性可以精确控制键盘操作,键盘事件属性一般只在键盘相关事件发生时才会存在于事件对象中,但是 ctrlKey 和 shiftKey 属性除外,因为它们可以在鼠标事件中存在。如,当按下 Ctrl 或 Shift 键时单击鼠标操作。

键盘事件定义的属性

属性 说明
keyCode 该属性包含键盘中对应键位的键值
charCode 该属性包含键盘中对应键位的 Unicode 编码,仅 DOM 支持
target 发生事件的节点(包含元素),仅 DOM 支持
srcElement 发生事件的元素,仅 IE 支持
shiftKey 是否按下 Shift 键,如果按下返回 true,否则 false
ctrlKey 是否按下 Ctrl 键,如果按下返回 true,否则 false
altKey 是否按下 Alt 键,如果按下返回 true,否则 false
metaKey 是否按下 Meta 键,如果按下返回 true,否则 false,仅 DOM 支持
/*
  监测 ctrl 和 shift 是否被同时按下,如果同时按下,且鼠标单击某个页面元素,则会把该元素删除。
*/
document.onclick = function(e) {
  var e = e || window.event;
  var t = e.target || e.srcElement;
  if(e.ctrlKey && e.shiftKey) { // 同时按下 Ctrl 和 Shift 键
  	t.parentNode.removeChild(t);
  }
}

keyCode 和 charCode 属性使用比较复杂,但是它们在实际开发中又比较常用,故比较这两个属性在不同事件类型和不同浏览器中的表现是非常必要的。

属性 IE 事件模型 DOM 事件模型
keyCode (keypress) 返回所有字符键的正确值,区分大写状态(65~90)和小写状态(97~122) 功能键返回正确值,而 Shift、Ctrl、Alt、PrintScreen、ScrollLock 无返回值,其他所有键值都返回 0。
keyCode (keydown) 返回所有键值(除 PrintScreen 键),字母键都以大写状态显示键值(65~90) 返回所有键值(除 PrintScreen 键),字母键都以大写状态显示键值(65~90)
keyCode (keyup) 返回所有键值(除 PrintScreen 键),字母键都以大写状态显示键值(65~90) 返回所有键值(除 PrintScreen 键),字母键都以大写状态显示键值(65~90)
charCode (keypress) 不支持该属性 返回字符键,区分大写状态(65~90)和小写状态(97~122),Shift 、Ctrl、Alt、PrintScreen、ScrollLock 键无返回值,其他键值都是 0
charCode (keydown) 不支持该属性 所有键值为 0
charCode (keyup) 不支持该属性 所有键值为 0

键位和码值对照表:

键位 码值
0~9(数字键) 48~57
Backspace(退格键) 8
Enter 13
Left arrow 37
right arrow 39
A~Z 65~90
Tab 9
Space 32
Top Arrow 38
Down arrow 40

使用方向键控制页面元素的移动效果:

/*
  <div id="box"></div>
*/
var box = document.getElementById('box');
box.style.position = "absolute";
box.style.width = "20px";
box.style.height = "20px";
box.style.backgroundColor = "red";

document.onkeydown = keydown;

function keydown(event) {
  var event = event || window.event;
  switch(event.keyCode) {
    case 37:
      box.style.left = box.offsetLeft - 5 + 'px';
      break;
    case 39:
      box.style.left = box.offsetLeft + 5 + 'px';
      break;
    case 38:
      box.style.top = box.offsetTop - 5 + 'px';
      break;
    case 40:
      box.style.top = box.offsetTop + 5 + 'px';
      break;
  }
}

键盘响应顺序

当按下键盘键时,会连续触发多个事件,它们将按如下顺序发生:

对于字符键来说,键盘事件的响应顺序:keydown -> keypress -> keyup

对于非字符键(如功能键或特殊键)来说,键盘事件的响应顺序:keydown -> keyup

如果按下字符键不放,则 keydown 和 keypress 事件将逐个持续发生,直到松开按键。

如果按下非字符键不放,则只有 keydown 事件持续发生,直到松开按键。

// 设计一个简单示例,以获取键盘事件响应顺序
// <textarea id="text" cols="26" rows="16"></textarea>
var text = document.getElementById('text');
var n = 1;
text.onkeydown = f;
text.onkeyup = f;
text.onkeypress = f;
function f(e) {
  var e = e || window.event;
  text.value += (n++) + "=" + e.type + "(keyCode=" + e.keyCode + ")\n";
}

使用页面事件

页面初始化

load 事件类型在页面完全加载完毕之后触发,在页面所有内容全部加载之前,任何 DOM 操作都不会发生。

// 直接为 window 对象注册页面初始化事件处理函数
/*

方法一:
  
  window.onload = f;
  
  function f() {
    alert('页面加载完毕')
  }
  
方法二:
  
  <body onload = "f()">

*/

// 如果页面同时只用两种 load 方式,并不会发生冲突,也不会两次触发事件。body 元素定义的页面初始化事件属性会被覆盖。

在实际开发中,load 事件类型经常需要调用附带参数的函数,但是 load 事件类型不能够直接调用函数,要解决这个问题,有以下两种办法:

// <body onload="f('Hi')">
function f(a) {
    alert(a);
}
window.onload = function() {
    f("Hi")
}
function f(a) {
    alert(a);
}

也可以使用闭包函数形式

window.onload = f('Hi');
function f(a) {
    return function () {
        alert(a)
    }
}

通过这种方法,可以实现在 load 事件类型上绑定更多的响应回调函数:

window.onload = function() {
    f1();
    f2();
}

也可以通过时间注册的方式来实现:

if(window.addEventListener) {
  window.addEventListener('load', f1, false)
  window.addEventListener('load', f2, false)
} else {
  window.attachEvent('load', f1, false)
  window.attachEvent('load', f1, false)
}

结构初始化

在传统事件模型中,load 是页面中最早被触发的事件,但是他要等到页面所有资源(文件/图片等)加载完毕才会触发。

可以使用 DOMContentLoaded 事件类型,作为 DOM 标准事件,它是在 DOM 文档结构加载完毕的时候触发的,因此要比 load 事件要早,目前,Mozilla 和 Opera 新版本已经支持该属性,IE 和 Safari 还不支持。

window.onload = f1;
if(document.addEventListener) {
  document.addEventListener('DOMContentLoaded', f, false)
}

function f() {
  console.log('我提前执行了');
}

function f1() {
  console.log('页面初始化完毕');
}

/*
  我提前执行了
  页面初始化完毕
*/

由于 IE 事件模型不支持 DOMContentLoaded 事件类型,为了实现兼容,需要运用一点小技巧,即在文档中写入一个新的 script 元素,但是该元素会延迟到文件最后加载,然后,使用 Script 对象的 onreadystatechange 方法进行类似的 readyState 检查后及时调用载入事件。

if(window.ActiveXObject) {
  document.write("<script id=ie_onload defer src=javascript:void(0)><\/script>");
  document.getElementById('ie_onload').onreadystatechange = function() {
    // 判断脚本标签状态
    if(this.readyState == 'complete') { // 如果状态已完成,则说明文档结构加载完毕
      this.onreadystatechange = null; // 清空当前方法
      f();
    }
  }
}

针对 Safari,可以使用 setInterval() 函数周期性的检查 document 对象的 readyState 属性,随时监控文档是否加载完毕,如果完成则调用回调函数。

if(/WebKit/i.test(navigator.userAgent)) {
  var _timer = setInterval(function() {
    if(/loaded|complete/.test(document.readyState)) {
      clearInterval(_timer);
      f();
    }
  }, 10)
}

把 3 端条件合并在一起即可实现兼容不同浏览器的 DOMContentLoaded 事件处理函数。

页面卸载

unload 表示卸载的意思,通过超链接、前进或后退按钮等方式从一个页面跳转到其他页面,或者关闭浏览器窗口时触发。

window.onunload = f;

function f() {
  alert(888)
}

在 unload 事件类型中无法有效阻止默认行为,因为该事件结束后,页面将不复存在。关闭或离开页面之前只有很多的时间来执行事件处理函数,使用该事件类型的最佳方式是取消该页面的对象引用。

beforeunload 事件类型更加人性化,如果返回字符串信息,那么该字符串会显示在一个确认对话框中,用来询问用户是否离开当前页面。

window.onbeforeunload = function(e) {
  return '你的数据还没保存呢!'
}

窗口重置

resize 事件类型是在浏览器窗口被重置时触发,如果用户调整窗口大小,或者最大化、最小化、恢复窗口大小显示时触发 resize 事件。利用该事件可以跟踪窗口大小的变化以便动态调整页面元素的显示大小。

// 跟踪窗口大小变化及时调整页面内红色盒子的大小,使其始终保持与窗口固定比例的大小显示
// <div id="box"></div>
var box = document.getElementById('box');
box.style.position = "absolute";
box.style.backgroundColor = "red";
box.style.width = w() * 0.8 + "px";
box.style.height = h() * 0.8 + 'px';

window.onresize = function() {
  box.style.width = w() * 0.8 + "px";
  box.style.height = h() * 0.8 + 'px';
}

function w() {
  if(window.innerWidth) { // 兼容 DOM
    return window.innerWidth;
  } else if((document.body) && (document.body.clientWidth)) { // 兼容 IE
    return document.body.clientWidth;
  }
}

function h() {
  if(window.innerHeight) { // 兼容 DOM
    return window.innerHeight;
  } else if((document.body) && (document.body.clientHeight)) { // 兼容 IE
    return document.body.clientHeight;
  }
}

页面滚动

// 随着页面滚动,控制红色小盒子始终在同一个位置
/*
<div class="layout">
  <div id="box"></div>
</div>
*/
var box = document.getElementById('box');
box.style.position = "absolute";
box.style.backgroundColor = "red";
box.style.width = "200px";
box.style.height = "160px";

window.onload = f;
window.onscroll = f;

function f(event) {
  console.log(document.body.scrollLeft, document.body.scrollTop)
  box.style.left = 100 + parseInt(document.documentElement.scrollLeft || document.body.scrollLeft) + 'px';
  box.style.top = 100 + parseInt(document.documentElement.scrollTop || document.body.scrollTop) + 'px';
}

错误处理

error 事件类型是在 JS 代码发生的错误的时候触发,利用该事件可以捕获并处理错误信息。

/*
  当页面发生编译错误时,将会触发 error 事件注册的时间处理函数,并弹出错误提示。
*/
window.onerror = function () {
  alert("错误原因:" + arguments[0] + "\n错误URL:" + arguments[1] + "\n错误引号:" + arguments[2]);
  return true; //禁止浏览器显示标准错误信息
}

a.innerHTML = ""; // 制造错误

使用 UI 事件

焦点处理

// <input type="text" /><input type="text" />
var o = document.getElementsByTagName('input');
for (var i = 0; i < o.length; i++) {
  o[i].onfocus = function() {
    this.style.borderStyle = "outset";
  }
  o[i].onblur = function() {
    this.style.borderStyle = "inset";
  }
}
// 页面 load 之后,给 input 元素添加焦点
/*
<form id="myform" method="post" action="#">
    姓名:<input type="text" name="name" />
    密码:<input type="password" name="pass" />
</form>
*/
var form = document.getElementById('myform');
var field = form.elements['name'];
window.onload = function() {
  field.focus();
}

选择文本

当在文本框或文本区域内选择文本时,将触发 select 事件,通过该事件,可以设计用户选择操作的交互行为。

IE9+OperaFirefoxChromeSafari 中,只有用户选择了文本且释放鼠标,才会触发 select 事件;但是在 IE8 及更早版本中,只要用户选择了一个字母,不必释放鼠标,就会触发 select 事件,另外在调用 select() 方法时也会触发 select 事件。

// 当在第一个文本框选择文本时,会在第二个文本框出现被选择的文本内容
/*
  <input type="text" id="a" value="请随意选择字符串" />
  <input type="text" id="b" />
*/
var a = document.getElementsByTagName('input')[0];
var b = document.getElementsByTagName('input')[1];

a.onselect = function() {
  if(document.selection) { // 兼容 IE
    o = document.selection.createRange(); // 创建一个选择区域
    if(o.text.length > 0) {
      b.value = o.text;
    }
  } else { // 兼容 DOM
    p1 = a.selectionStart; // 获取文本框选择的初始位置
    p2 = a.selectionEnd; // 获取文本框中选择的结束位置
    b.value = a.value.substring(p1, p2);
  }
}

字段值变化监测

change 事件类型是在表单元素的值发生变化时触发,它主要用于 inputselecttextarea 元素。

对于 inputtextarea 元素来说,当他们失去焦点且 value 值改变时触发,对于 select 元素,在其选项改变时触发,也就是说不失去焦点也会触发。

var a = document.getElementsByTagName('input')[0];
var b = document.getElementsByTagName('input')[1];

a.onchange = function() {
  b.value = this.value;
}
/*
<select>
  <option value="http://www.baidu.com">百度</option>
  <option value="http://www.google.cn">谷歌</option>
</select>
*/
var a = document.getElementsByTagName('select')[0];

a.onchange = function() {
  window.open(this.value, "")
}
// 示例:只允许用户输入数字
/*
    <form id="myform" method="post" action="javascript:alert('表单提交啦!')">
      <p>
        <label for="txtNumbers">请输入数字:</label><br>
        <input type="text" id="txtNumbers" name="numbers" />
      </p>
      <p>
        <input type="submit" value="提交表单" id="submit-btn" />
      </p>
    </form>
*/
var form = document.getElementById('myform');
var numbers = form.elements['numbers'];

numbers.onfocus = function(event) {
  event = event || window.event;
  var target = event.target || event.srcElement;
  target.style.backgroundColor = "yellow";
}

numbers.onblur = function(event) {
  event = event || window.event;
  var target = event.target || event.srcElement;
  if(/[^\d]/.test(target.value)) {
    target.style.backgroundColor = "red";
  } else {
    target.style.backgroundColor = "";
  }
}

numbers.onchange = function(event) {
  event = event || window.event;
  var target = event.target || event.srcElement;
  if(/[^\d]/.test(target.value)) {
    target.style.backgroundColor = "red";
  } else {
    target.style.backgroundColor = "";
  }
}

numbers.focus();

提交表单

/*
    <form id="form1" name="form1" method="post" action="">
      <input type="text" id="t" name="t" />
      <input type="submit" value="submit" />
    </form>
*/
var t = document.getElementsByTagName('input')[0];
var f = document.getElementsByTagName('form')[0];

f.onsubmit = function(e) {
  alert(t.value);
  return false;
}

// 当页面没有 submit 按钮时,按 Enter 一样可以触发。
// 在 <textarea> 中按 Enter 只会换行,不会触发
// 验证表单文本框中是否有内容提交,否则阻止提交
var t = document.getElementsByTagName('input')[0];
var f = document.getElementsByTagName('form')[0];

f.onsubmit = function(e) {
  if(t.value.length < 1) {
    var event = e || window.event;
    if(event.preventDefault) {
      event.preventDefault();
    } else {
      event.returnValue = false;
    }
  }
}
// 禁止 Enter 提交 form 表单
var t = document.getElementsByTagName('input')[0];

t.onkeypress = function(e) {
  var e = e || window.event;
  return e.keyCode != 13;
}
// 调用 submit() 方法提交表单
var t = document.getElementsByTagName('input')[0];
var f = document.getElementsByTagName('form')[0];

t.onkeypress = function(e) {
  var e = e || window.event;
  return e.keyCode != 13;
}

重置表单

/*
    <form id="form1" name="form1" method="post" action="">
      <input type="text" id="t" name="t" />
      <input type="reset"/>
    </form>
*/
var t = document.getElementsByTagName('input')[0];
var f = document.getElementsByTagName('form')[0];

f.onreset = function(e) {
  alert(t.value);
}
// 文本输入框如果输入 10 个字符以上就不允许重置了
var t = document.getElementsByTagName('input')[0];
var f = document.getElementsByTagName('form')[0];

f.onreset = function(e) {
  if(t.value.length > 10) {
    var event = e || window.event;
    if(event.preventDefault) {
      event.preventDefault();
    } else {
      event.returnValue = false;
    }
  }
}

- Book Lists -