JavaScript核心之事件详解(表单,文档,自定义事件)

目录

1 表单事件

1.1 Input事件,select事件,change事件

1.1.1 input事件

1.1.2 select事件

1.1.3 Change事件

1.2 reset事件,submit事件

1.2.1 reset事件

1.2.2 submit事件

2 文档事件

2.1 beforeunload事件,unload事件,load事件,error事件,pageshow事件,pagehide事件

2.1.1 beforeunload事件

2.1.2 unload事件

2.1.3 load事件,error事件

2.1.4 pageshow事件,pagehide事件

2.2 DOMContentLoaded事件,readystatechange事件

2.2.1 DOMContentLoaded事件

2.2.2 readystatechange事件

2.3 scroll事件,resize事件

2.3.1 scroll事件

2.3.2 resize事件

2.4 hashchange事件,popstate事件

2.4.1 hashchange事件

2.4.2 popstate事件

2.5 cut事件,copy事件,paste事件

2.6 焦点事件

3 自定义事件和事件模拟

3.1 CustomEvent()

3.2 事件的模拟


1 表单事件

1.1 Input事件,select事件,change事件

以下事件与表单成员的值变化有关。

1.1.1 input事件

input事件当<input>、<textarea>的值发生变化时触发。此外,打开contenteditable属性的元素,只要值发生变化,也会触发input事件。

input事件的一个特点,就是会连续触发,比如用户每次按下一次按键,就会触发一次input事件。

1.1.2 select事件

select事件当在<input>、<textarea>中选中文本时触发。

// HTML代码为
// <input id="test" type="text" value="Select me!" />

var elem = document.getElementById('test');
elem.addEventListener('select', function() {
  console.log('Selection changed!');
}, false);

1.1.3 Change事件

Change事件当<input>、<select>、<textarea>的值发生变化时触发。它与input事件的最大不同,就是不会连续触发,只有当全部修改完成时才会触发,而且input事件必然会引发change事件。具体来说,分成以下几种情况。

  • 激活单选框(radio)或复选框(checkbox)时触发。
  • 用户提交时触发。比如,从下列列表(select)完成选择,在日期或文件输入框完成选择。
  • 当文本框或textarea元素的值发生改变,并且丧失焦点时触发。

下面是一个例子。

// HTML代码为
// <select size="1" onchange="changeEventHandler(event);">
//   <option>chocolate</option>
//   <option>strawberry</option>
//   <option>vanilla</option>
// </select>

function changeEventHandler(event) {
  console.log('You like ' + event.target.value + ' ice cream.');
}

1.2 reset事件,submit事件

以下事件发生在表单对象上,而不是发生在表单的成员上。

注意:如果在onsubmit和onreset事件中调用的是自定义的函数名,那么,必须在函数名的前面加“return”语句;否则,不论在函数中返回的是true还是false,当前事件返回的值一律是true值

1.2.1 reset事件

reset事件当表单重置(所有表单成员变回默认值)时触发。

1.2.2 submit事件

submit事件当表单数据向服务器提交时触发。注意,submit事件的发生对象是form元素,而不是button元素(即使它的类型是submit),因为提交的是表单,而不是按钮。

2 文档事件

2.1 beforeunload事件,unload事件,load事件,error事件,pageshow事件,pagehide事件

以下事件与网页的加载与卸载相关。

2.1.1 beforeunload事件

beforeunload事件当窗口将要关闭,或者document和网页资源将要卸载时触发。它可以用来防止用户不当心关闭网页

该事件的默认动作就是关闭当前窗口或文档。如果在监听函数中,调用了event.preventDefault(),或者对事件对象的returnValue属性赋予一个非空的值,就会自动跳出一个确认框,让用户确认是否关闭网页。如果用户点击“取消”按钮,网页就不会关闭。监听函数所返回的字符串,会显示在确认对话框之中。

window.onbeforeunload = function() {
  if (textarea.value != textarea.defaultValue) {
    return '你确认要离开吗?';
  }
};

上面代码表示,当用户关闭网页,会跳出一个确认对话框,上面显示“你确认要离开吗?”。

下面的两种写法,具有同样效果。

window.addEventListener('beforeunload', function( event ) {
  event.returnValue = '你确认要离开吗?';
});

// 等同于
window.addEventListener('beforeunload', function( event ) {
  event.preventDefault();
});

上面代码中,事件对象的returnValue属性的值,将会成为确认框的提示文字。

只要定义了beforeunload事件的监听函数,网页不会被浏览器缓存。

2.1.2 unload事件

unload事件在窗口关闭或者document对象将要卸载时触发,发生在window、body、frameset等对象上面。它的触发顺序排在beforeunload、pagehide事件后面。unload事件只在页面没有被浏览器缓存时才会触发,换言之,如果通过按下“前进/后退”导致页面卸载,并不会触发unload事件。

当unload事件发生时,document对象处于一个特殊状态。所有资源依然存在,但是对用户来说都不可见,UI互动(window.open、alert、confirm方法等)全部无效。这时即使抛出错误,也不能停止文档的卸载。

window.addEventListener('unload', function(event) {
  console.log('文档将要卸载');
});

如果在window对象上定义了该事件,网页就不会被浏览器缓存。

2.1.3 load事件,error事件

load事件在页面加载成功时触发,error事件在页面加载失败时触发注意,页面从浏览器缓存加载,并不会触发load事件

这两个事件实际上属于进度事件,不仅发生在document对象,还发生在各种外部资源上面。浏览网页就是一个加载各种资源的过程,图像(image)、样式表(style sheet)、脚本(script)、视频(video)、音频(audio)、Ajax请求(XMLHttpRequest)等等。这些资源和document对象、window对象、XMLHttpRequestUpload对象,都会触发load事件和error事件。

2.1.4 pageshow事件,pagehide事件

默认情况下,浏览器会在当前会话(session)缓存页面,当用户点击“前进/后退”按钮时,浏览器就会从缓存中加载页面

pageshow事件在页面加载时触发,包括第一次加载和从缓存加载两种情况。如果要指定页面每次加载(不管是不是从浏览器缓存)时都运行的代码,可以放在这个事件的监听函数。

第一次加载时,它的触发顺序排在load事件后面。从缓存加载时,load事件不会触发,因为网页在缓存中的样子通常是load事件的监听函数运行后的样子,所以不必重复执行。同理,如果是从缓存中加载页面,网页内初始化的JavaScript脚本(比如DOMContentLoaded事件的监听函数)也不会执行。

window.addEventListener('pageshow', function(event) {
  console.log('pageshow: ', event);
});

pageshow事件有一个persisted属性,返回一个布尔值。页面第一次加载时,这个属性是false;当页面从缓存加载时,这个属性是true。

window.addEventListener('pageshow', function(event){
  if (event.persisted) {
    // ...
  }
});

pagehide事件与pageshow事件类似,当用户通过“前进/后退”按钮,离开当前页面时触发它与unload事件的区别在于,如果在window对象上定义unload事件的监听函数之后,页面不会保存在缓存中,而使用pagehide事件,页面会保存在缓存中

pagehide事件的event对象有一个persisted属性,将这个属性设为true,就表示页面要保存在缓存中;设为false,表示网页不保存在缓存中,这时如果设置了unload事件的监听函数,该函数将在pagehide事件后立即运行。

如果页面包含frame或iframe元素,则frame页面的pageshow事件和pagehide事件,都会在主页面之前触发。

2.2 DOMContentLoaded事件,readystatechange事件

以下事件与文档状态相关。

2.2.1 DOMContentLoaded事件

当HTML文档下载并解析完成以后,就会在document对象上触发DOMContentLoaded事件。这时,仅仅完成了HTML文档的解析(整张页面的DOM生成),所有外部资源(样式表、脚本、iframe等等)可能还没有下载结束。也就是说,这个事件比load事件,发生时间早得多。

document.addEventListener("DOMContentLoaded", function(event) {
  console.log("DOM生成");
});

注意,网页的JavaScript脚本是同步执行的,所以定义DOMContentLoaded事件的监听函数,应该放在所有脚本的最前面。否则脚本一旦发生堵塞,将推迟触发DOMContentLoaded事件。

2.2.2 readystatechange事件

readystatechange事件发生在Document对象和XMLHttpRequest对象,当它们的readyState属性发生变化时触发。

document.onreadystatechange = function () {
  if (document.readyState == "interactive") {
    // ...
  }
}

IE8不支持DOMContentLoaded事件,但是支持这个事件。因此,可以使用readystatechange事件,在低版本的IE中代替DOMContentLoaded事件。

2.3 scroll事件,resize事件

以下事件与窗口行为有关。

2.3.1 scroll事件

scroll事件在文档或文档元素滚动时触发。

由于该事件会连续地大量触发,所以它的监听函数之中不应该有非常耗费计算的操作。推荐的做法是使用requestAnimationFrame或setTimeout控制该事件的触发频率,然后可以结合customEvent抛出一个新事件。

(function() {
  var throttle = function(type, name, obj) {
    var obj = obj || window;
    var running = false;
    var func = function() {
      if (running) { return; }
      running = true;
      requestAnimationFrame(function() {
        obj.dispatchEvent(new CustomEvent(name));
        running = false;
      });
    };
    obj.addEventListener(type, func);
  };

  // 将scroll事件重定义为optimizedScroll事件
  throttle("scroll", "optimizedScroll");
})();

window.addEventListener("optimizedScroll", function() {
  console.log("Resource conscious scroll callback!");
});

上面代码中,throttle函数用于控制事件触发频率,requestAnimationFrame方法保证每次页面重绘(每秒60次),只会触发一次scroll事件的监听函数。改用setTimeout方法,可以放置更大的时间间隔。

(function() {
  window.addEventListener("scroll", scrollThrottler, false);

  var scrollTimeout;
  function scrollThrottler() {
    if ( !scrollTimeout ) {
      scrollTimeout = setTimeout(function() {
        scrollTimeout = null;
        actualScrollHandler();
      }, 66);
    }
  }

  function actualScrollHandler() {
    // ...
  }
}());

上面代码中,setTimeout指定scroll事件的监听函数,每66毫秒触发一次(每秒15次)。

2.3.2 resize事件

resize事件在改变浏览器窗口大小时触发,发生在window、body、frameset对象上面。

var resizeMethod = function(){
  if (document.body.clientWidth < 768) {
    console.log('移动设备');
  }
};

window.addEventListener("resize", resizeMethod, true);

该事件也会连续地大量触发,所以最好像上面的scroll事件一样,通过throttle函数控制事件触发频率。

2.4 hashchange事件,popstate事件

以下事件与文档的URL变化相关。

2.4.1 hashchange事件

hashchange事件在URL的hash部分(即#号后面的部分,包括#号)发生变化时触发。如果老式浏览器不支持该属性,可以通过定期检查location.hash属性,模拟该事件,下面就是代码。

(function(window) {
  if ( "onhashchange" in window.document.body ) { return; }

  var location = window.location;
  var oldURL = location.href;
  var oldHash = location.hash;

  // 每隔100毫秒检查一下URL的hash
  setInterval(function() {
    var newURL = location.href;
    var newHash = location.hash;

    if ( newHash != oldHash && typeof window.onhashchange === "function" ) {
      window.onhashchange({
        type: "hashchange",
        oldURL: oldURL,
        newURL: newURL
      });

      oldURL = newURL;
      oldHash = newHash;
    }
  }, 100);

})(window);

hashchange事件对象除了继承Event对象,还有oldURL属性和newURL属性,分别表示变化前后的URL。

2.4.2 popstate事件

popstate事件在浏览器的history对象的当前记录发生显式切换时触发注意,调用history.pushState()或history.replaceState(),并不会触发popstate事件该事件只在用户在history记录之间显式切换时触发,比如鼠标点击“后退/前进”按钮,或者在脚本中调用history.back()、history.forward()、history.go()时触发。

该事件对象有一个state属性,保存history.pushState方法和history.replaceState方法为当前记录添加的state对象。

window.onpopstate = function(event) {
  console.log("state: " + event.state);
};
history.pushState({page: 1}, "title 1", "?page=1");
history.pushState({page: 2}, "title 2", "?page=2");
history.replaceState({page: 3}, "title 3", "?page=3");
history.back(); // state: {"page":1}
history.back(); // state: null
history.go(2);  // state: {"page":3}

上面代码中,pushState方法向history添加了两条记录,然后replaceState方法替换掉当前记录。因此,连续两次back方法,会让当前条目退回到原始网址,它没有附带state对象,所以事件的state属性为null,然后前进两条记录,又回到replaceState方法添加的记录。

浏览器对于页面首次加载,是否触发popstate事件,处理不一样,Firefox不触发该事件。

2.5 cut事件,copy事件,paste事件

以下三个事件属于文本操作触发的事件。

注意:在onbeforecopy和oncopy事件中调用的是自定义函数名,那么,必须在函数名的前面加return语句;否则不论在函数中返回的true,还是false,当前事件返回值一律是true,也就是允许复制。cut和paste同样

  • cut事件:在将选中的内容从文档中移除,加入剪贴板后触发。
  • copy事件:在选中的内容加入剪贴板后触发。
  • paste事件:在剪贴板内容被粘贴到文档后触发。

这三个事件都有一个clipboardData只读属性该属性存放剪贴的数据,是一个DataTransfer对象

2.6 焦点事件

焦点事件发生在Element节点和document对象上面,与获得或失去焦点相关。它主要包括以下四个事件。

  • focus事件:Element节点获得焦点后触发,该事件不会冒泡。
  • blur事件:Element节点失去焦点后触发,该事件不会冒泡。
  • focusin事件:Element节点将要获得焦点时触发,发生在focus事件之前。该事件会冒泡。Firefox不支持该事件。
  • focusout事件:Element节点将要失去焦点时触发,发生在blur事件之前。该事件会冒泡。Firefox不支持该事件。

这四个事件的事件对象,带有target属性(返回事件的目标节点)和relatedTarget属性(返回一个Element节点)。对于focusin事件,relatedTarget属性表示失去焦点的节点;对于focusout事件,表示将要接受焦点的节点;对于focus和blur事件,该属性返回null。

由于focus和blur事件不会冒泡,只能在捕获阶段触发,所以addEventListener方法的第三个参数需要设为true。

form.addEventListener("focus", function( event ) {
  event.target.style.background = "pink";
}, true);
form.addEventListener("blur", function( event ) {
  event.target.style.background = "";
}, true);

上面代码设置表单的文本输入框,在接受焦点时设置背景色,在失去焦点时去除背景色。

浏览器提供一个FocusEvent构造函数,可以用它生成焦点事件的实例。

var focusEvent = new FocusEvent(typeArg, focusEventInit);

上面代码中,FocusEvent构造函数的第一个参数为事件类型,第二个参数是可选的配置对象,用来配置FocusEvent对象。

3 自定义事件和事件模拟

除了浏览器预定义的那些事件,用户还可以自定义事件,然后手动触发。

// 新建事件实例
var event = new Event('build');

// 添加监听函数
elem.addEventListener('build', function (e) { ... }, false);

// 触发事件
elem.dispatchEvent(event);

上面代码触发了自定义事件,该事件会层层向上冒泡。在冒泡过程中,如果有一个元素定义了该事件的监听函数,该监听函数就会触发。

3.1 CustomEvent()

Event构造函数只能指定事件名,不能在事件上绑定数据。如果需要在触发事件的同时,传入指定的数据,需要使用CustomEvent构造函数生成自定义的事件对象。

var event = new CustomEvent('build', { 'detail': 'hello' });
function eventHandler(e) {
  console.log(e.detail);
}

上面代码中,CustomEvent构造函数的第一个参数是事件名称,第二个参数是一个对象,该对象的detail属性会绑定在事件对象之上。

下面是另一个例子。

var myEvent = new CustomEvent("myevent", {
  detail: {
    foo: "bar"
  },
  bubbles: true,
  cancelable: false
});

el.addEventListener('myevent', function(event) {
  console.log('Hello ' + event.detail.foo);
});

el.dispatchEvent(myEvent);

IE不支持这个方法,可以用下面的垫片函数模拟。

(function () {
  function CustomEvent ( event, params ) {
    params = params || { bubbles: false, cancelable: false, detail: undefined };
    var evt = document.createEvent( 'CustomEvent' );
    evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
    return evt;
   }

  CustomEvent.prototype = window.Event.prototype;

  window.CustomEvent = CustomEvent;
})();

3.2 事件的模拟

有时,需要在脚本中模拟触发某种类型的事件,这时就必须使用这种事件的构造函数。

下面是一个通过MouseEvent构造函数,模拟触发click鼠标事件的例子。

function simulateClick() {
  var event = new MouseEvent('click', {
    'bubbles': true,
    'cancelable': true
  });
  var cb = document.getElementById('checkbox');
  cb.dispatchEvent(event);
}

猜你喜欢

转载自blog.csdn.net/u012060033/article/details/89787476
今日推荐