Blogger Information
Blog 22
fans 0
comment 1
visits 17868
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
第六章 JS事件
刘静的博客
Original
981 people have browsed it

第六章 JS事件

事件流介绍

  1. js操作css: 脚本号的css,js与html交互通过事件完成
  2. 事件: 文档或浏览器窗口中发生一些特定的交互性瞬间
  3. 事件流(事件传播): 描述从页面中接受事件的顺序

[^IE事件流是事件冒泡流;Netscape事件流式事件捕获流]:

事件冒泡的概念

事件冒泡: 事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点(文档)

事件捕获的概念

  1. 事件捕获:有不太具体的节点更早的接收事件,而最具体的节点应该最后接收到事件

  2. 方法:addEventListener() 参数:类型,回调函数,默认false(冒泡阶段) 事件监听者

  3. 事件流:事件捕获阶段;处于目标阶段;事件冒泡阶段

    1. var box = document.getElementById('box');
    2. box.addEventListener('click',function(){
    3. box.innerHTML += 'box\n';
    4. },true)//捕获阶段
    5. document.body.addEventListener('click',function(){
    6. box.innerHTML += 'body\n';
    7. },true)//捕获阶段
    8. document.documentElement.addEventListener('click',function(){
    9. box.innerHTML += 'html\n';
    10. },true)//捕获阶段
    11. document.addEventListener('click',function(){
    12. box.innerHTML += 'document\n';
    13. },true)//捕获阶段
    14. window.addEventListener('click',function(){
    15. box.innerHTML += 'window\n';
    16. },true)//捕获阶段
    17. //冒泡阶段
    18. box.addEventListener('click',function(){
    19. box.innerHTML += 'box\n';
    20. },false)
    21. document.body.addEventListener('click',function(){
    22. box.innerHTML += 'body\n';
    23. },false)
    24. document.documentElement.addEventListener('click',function(){
    25. box.innerHTML += 'html\n';
    26. },false)
    27. document.addEventListener('click',function(){
    28. box.innerHTML += 'document\n';
    29. },false)
    30. window.addEventListener('click',function(){
    31. box.innerHTML += 'window\n';
    32. },false)

    得到的结果如下:

HTML事件处理程序

缺点: html+js无分离,后期不易维护

特别,注意不论在函数内还是函数外,this都是指向window对象

  1. console.log(this);//指向window对象
  2. function test(){
  3. // console.log(this); 指向window对象
  4. document.getElementById('box').innerHTML += '1';
  5. }

DOM_0级事件处理程序

优点:1.简单 2.跨浏览器

缺点:不能给同一个元素来绑定相同的事件处理程序,如果绑定了,会有覆盖现象

  1. var box = document.getElementById('box');
  2. box.onclick = function(){
  3. this.innerHTML += 1;
  4. }
  5. //删除事件处理程序
  6. box.onclick = null;
  7. // 垃圾回收机制
  8. //缺点:不能给同一个元素来绑定相同的事件处理程序,如果绑定了,会有覆盖现象
  9. box.onclick = function(){
  10. this.innerHTML += 2;
  11. }

DOM_2级事件处理程序

  1. addEventListener(事件名,处理程序的函数,布尔值)
  2. removeEventListener()
  3. 优点:没有事件覆盖现象

[^布尔值默认false是处于冒泡阶段,如果是true,是处于捕获阶段]:

IE8浏览器不支持DOM2处理程序

  1. //没有事件覆盖现象
  2. setTimeout(function(){
  3. box.addEventListener('click',function(){
  4. this.innerHTML += 1;
  5. },false);
  6. },10)
  7. // //监听函数传参,可以用匿名函数包装一个监听函数
  8. box.addEventListener('click',function(){
  9. //监听匿名函数
  10. test(2);
  11. },false);
  12. // box.addEventListener('click',test(3),false);
  13. function test(x){
  14. alert(x);
  15. }

无效的移除事件的方式

  1. //添加事件监听者
  2. box.addEventListener('click',function(){
  3. this.innerHTML += 1;
  4. },false);
  5. //移除事件
  6. box.removeEventListener('click',function(){
  7. this.innerHTML += 1;
  8. },false);

正确移除事件方式

  1. function handler(){
  2. this.innerHTML += 1;
  3. }
  4. box.addEventListener('click',handler,false);
  5. box.removeEventListener('click',handler,false);

IE事件处理程序

IE: attachEvent() detachEvent()

[^IE10以下浏览器可以使用 默认冒泡]:

  1. var box = document.getElementById('box');
  2. box.attachEvent('onclick',function (){
  3. this.innerHTML += '1';
  4. })

事件绑定兼容写法

1.DOM2级事件处理程序 addEventListener() IE8不支持, ie attachEvent()

[^IE8浏览器不支持DOM2处理程序]:

2.attachEvent()内部的this指向了window,我们要对this的指向也做兼容

ie8的写法如下:

  1. var btn = document.getElementById('btn');
  2. function handler(){
  3. console.log(this.innerHTML);
  4. }
  5. btn.attachEvent('onclick',function(){
  6. handler.call(btn);//将方法绑定到对象上,这时候this指向btn对象
  7. })
  8. btn.addEventListener('click',fn,false); 默认为false,冒泡 this指向当前btn对象
  9. btn.attachEvent('onclick',fn) this指向window

全浏览器事件处理程序的兼容性代码:

  1. addEvent(btn,'click',function(){
  2. console.log(this.innerHTML);
  3. })
  4. function addEvent(target,eventType,handler){
  5. if(target.addEventListener){
  6. //chrome ff safari都可以
  7. target.addEventListener(eventType,handler,false);//事件冒泡
  8. }else{
  9. target.attachEvent('on' + eventType,function(){
  10. handler.call(target);//将方法绑定到对象上,这时候this指向对象
  11. });
  12. }
  13. }

call方法可以改变this指向问题

  1. console.log(this);//chrome ff 指向window对象
  2. var obj = {
  3. innerHTML : 'liujing'
  4. }
  5. function fn(){
  6. console.log(this.innerHTML);
  7. }
  8. //call方法可以改变this指向问题
  9. fn.call(obj);//chrome ff this指向obj对象
  10. fn.call();//chrome ff 指向window对象

事件调用顺序总结

  1. 相同点:如果同时出现HTML事件处理程序和DOM0级事件处理程序,DOM0级会覆盖HTML事件处理程序

  2. 不同点:

    2.1 chrome,safari,FF浏览器以及IE11结果: DOM0级 DOM2级
    2.2 IE9、10结果为:DOM0级 DOM2级 IE
    2.3 ie8结果: DOM0级 IE

DOM0级事件处理程序

  1. box.onclick = function(){
  2. this.innerHTML += 'DOM0级\n';
  3. }

DOM2级事件处理程序

  1. if(box.addEventListener){
  2. box.addEventListener('click',function(){
  3. this.innerHTML += 'DOM2级\n';
  4. })
  5. }

IE级事件处理程序

  1. if(box.attachEvent){
  2. box.attachEvent('onclick',function(){
  3. box.innerHTML += 'IE\n';
  4. })
  5. }

如何获取事件对象

1.如何获取事件对象
2.事件目标
3.事件代理
4.事件冒泡
5.事件流阶段 eventPhase
6.取消默认事件 //javascript:void(0);

  1. // 兼容性
  2. window.onload = function() {
  3. var box = document.getElementById('box');
  4. // 1.event对象是事件处理程序的第一个参数 ie8浏览器不兼容
  5. // ie8浏览器得到的结果是undefined,其它浏览器[object MouseEvent]
  6. box.onclick = function (e){
  7. console.log(e);
  8. this.innerHTML = e;
  9. }
  10. // 2.直接可以使用event变量 火狐浏览器低版本获取出来的是undefined
  11. box.onclick = function() {
  12. this.innerHTML = event;
  13. }
  14. // 兼容性代码
  15. box.onclick = function(e){
  16. console.log(e);
  17. // 兼容性写法
  18. e = e || window.event;//[object MouseEvent]
  19. box.innerHTML = e; //[object Event]
  20. }
  21. }

事件目标

1.currentTarget属性:返回事件当前所在的节点,正在执行的监听函数所绑定的节点

[^target和srcElement功能一样]:

  1. box.onclick = function (e){
  2. e = e || window.event;
  3. // console.log(e);//MouseEvent对象中的cancelBubble,cancelable;clientX;clientY属性
  4. //e,currentTarget 值是null //ie8低版本
  5. //e.srcElement值是li.item
  6. console.log(e.currentTarget);//整个ul对象
  7. var items = document.getElementsByTagName('li');
  8. items[0].innerHTML = e.currentTarget;//[object HTMLListElement]
  9. }
  10. //监听的是li的父级节点ul

2.target属性:返回的是事件的实际目标对象

[^注意:target属性 ie8不支持]:

  1. box.onclick = function (e){
  2. e = e || window.event;
  3. console.log(e.target);//<li class="item">1</li>
  4. console.log(e.target===this);//false
  5. // this对象跟e.currentTarget属性是一致的
  6. console.log(e.currentTarget===this);//true
  7. }

3.srcElement跟target功能是一致的,FF低版本的浏览器不支持

  1. box.onclick = function(e) {
  2. e = e || window.event;
  3. // console.log(e.srcElement);//<li class="item">1</li>
  4. var target = e.target || e.srcElement;
  5. target.style.backgroundColor = 'red';
  6. }
  7. box.onmouseout = function(e) {
  8. e = e || window.event;
  9. var target = e.target || e.srcElement;
  10. target.style.backgroundColor = 'lightblue';
  11. }

事件代理

css代码如下:

  1. <style type="text/css">
  2. * {
  3. padding: 0;
  4. margin: 0;
  5. }
  6. ul {
  7. list-style: none;
  8. overflow: hidden;
  9. margin-top: 80px;
  10. }
  11. ul li {
  12. float: left;
  13. width: 100px;
  14. height: 30px;
  15. text-align: center;
  16. line-height: 30px;
  17. color: #fff;
  18. background-color: #000;
  19. margin: 0 10px;
  20. }
  21. </style>

html代码:

  1. <ul id="box">
  2. <li>1</li>
  3. <li>2</li>
  4. <li>3</li>
  5. <li>4</li>
  6. <li>5</li>
  7. </ul>

javascript代码:

  1. window.onload = function() {
  2. // 常规方法实现
  3. // 1.获取标签
  4. var lis = document.getElementsByTagName('li');
  5. for(var i = 0; i < lis.length; i++){
  6. lis[i].onmouseover = function (){
  7. this.style.backgroundColor = 'blue';
  8. }
  9. lis[i].onmouseout = function (){
  10. this.style.backgroundColor = 'black';
  11. }
  12. }
  13. // 事件代理的方式实现 事件实际目标对象来实现
  14. // 事件代理应用: 事件实际目标对象target和srcElement属性完成
  15. // 优点: 提高性能以及降低代码的复杂度
  16. //ul 也被触发了
  17. var box = document.getElementById('box');
  18. box.onmouseover = function(e) {
  19. e = e || window.event;
  20. var target = e.target || e.srcElement;
  21. target.style.backgroundColor = 'blue';
  22. }
  23. box.onmouseout = function(e) {
  24. e = e || window.event;
  25. var target = e.target || e.srcElement;
  26. target.style.backgroundColor = 'black';
  27. }
  28. }

[^ul 也被触发了]:

事件代理的应用

  1. //模拟未来的某个事件来添加对应的数据
  2. var box = document.getElementById('box');
  3. setTimeout(function(){
  4. var item = document.createElement('li');
  5. item.innerHTML = '6';
  6. box.appendChild(item);
  7. },100);

target本身就会因为事件捕获而指向具体的节点

  1. // target本身就会因为事件捕获而指向具体的节点
  2. var box = document.getElementById('box');
  3. // 给未来的元素绑定事件,使用事件代理
  4. box.onmouseover = function (e){
  5. e = e || window.event;
  6. var target = e.target || e.srcElement;
  7. target.style.backgroundColor = 'blue';
  8. }
  9. box.onmouseout = function (e){
  10. e = e || window.event;
  11. var target = e.target || e.srcElement;
  12. target.style.backgroundColor = 'black';
  13. }

事件冒泡的用法

属性: bubbles; cancelBubble

方法:stopPropagation() ;stopImmediatePropagation()

1.bubbles 返回一个布尔值 表示当前事件是否会冒泡,只读 true:冒泡;false:非冒泡

[^大部分事件都会冒泡,但是focus blur scroll事件不会冒泡]:

  1. var btn = document.getElementById('btn');
  2. var test = document.getElementById('test');
  3. btn.onclick = function(e) {
  4. e = e || window.event;
  5. console.log(e.bubbles);//true
  6. }
  7. test.onfocus = function (e){
  8. e = e || window.event;
  9. console.log(e.bubbles);//false
  10. }

2.stopPropagation()表示取消事件的进一步冒泡 无返回值,但是无法阻止同一事件的其它监听函数被调用

[^ie8浏览器不支持]:

  1. btn.onclick = function(e){
  2. e = e || window.event;
  3. //阻止冒泡
  4. e.stopPropagation();
  5. this.innerHTML = '阻止冒泡';
  6. }
  7. document.body.onclick = function(e){
  8. e = e || window.event;
  9. console.log('body');
  10. }
  11. btn.addEventListener('click',function(e){
  12. e = e || window.event;
  13. e.stopPropagation();
  14. this.innerHTML = '修改了';
  15. },false);
  16. btn.addEventListener('click',function(e){
  17. e = e || window.event;
  18. this.style.backgroundColor = 'blue';//上面没有阻止这个操作
  19. },false);
  20. document.body.onclick = function (e){
  21. e = e || window.event;
  22. console.log('body');//阻止这个操作
  23. }

3.stopImmediatePropagation()既可以阻止冒泡,也可以阻止同一事件的其它监听函数被调用

  1. btn.addEventListener('click', function(e) {
  2. e = e || window.event;
  3. e.stopImmediatePropagation();
  4. this.innerHTML = '修改了';
  5. }, false);
  6. btn.addEventListener('click', function(e) {
  7. e = e || window.event;
  8. this.style.backgroundColor = 'blue';
  9. }, false);
  10. document.body.onclick = function(e) {
  11. e = e || window.event;
  12. console.log('body');
  13. }

4.cancelBubble 属性用于阻止冒泡 可读写 默认值为false,当设置为true,cancelBubble可以取消事件冒泡

  1. btn.onclick = function(e) {
  2. e = e || window.event;
  3. if(e.stopPropagation){
  4. e.stopPropagation();
  5. }else{
  6. e.cancelBubble = true;//全局浏览器支持
  7. }
  8. this.innerHTML = '修改了';
  9. }
  10. document.body.onclick = function(e) {
  11. e = e || window.event;
  12. console.log('body');
  13. }

兼容stopPropagation() stopImmediatePropagation()ie8不支持

[^e.cancelBubble = true;全浏览器都支持 不是标准写法]:

  1. var handler = function (e){
  2. e = e || window.event;
  3. if(e.stopPropagation){
  4. e.stopPropagation();
  5. }else{
  6. e.cancelBubble = ture;
  7. }
  8. }

事件流阶段属性

e.eventPhase 0 表示事件没有发生 1 表示捕获阶段 2目标阶段 3冒泡阶段

  1. var btn = document.getElementById('btn');
  2. // 2 目标阶段
  3. btn.onclick = function (e){
  4. e = e || window.event;
  5. this.innerHTML = e.eventPhase + '阶段';
  6. console.log(e.eventPhase);
  7. }
  1. // 1 捕获阶段
  2. document.body.addEventListener('click',function (e){
  3. e = e || window.event;
  4. console.log(e.eventPhase);
  5. },true);
  1. // 3 冒泡阶段
  2. document.body.addEventListener('click',function (e){
  3. e = e || window.event;
  4. console.log(e.eventPhase);
  5. },false);

取消默认事件

事件对象中两个方法 阻止默认事件: 方法:preventDefault() 属性:returnValue兼容ie8以下浏览器 return fasle

  1. var apeland = document.getElementById('apeland');
  2. // 1.preventDefault() ie8不支持
  3. apeland.onclick = function (e){
  4. e = e || window.event;
  5. // 阻止冒泡
  6. // e.stopPropagation();
  7. // 阻止默认事件 ie8以下不支持
  8. // e.preventDefault();
  9. // 阻止默认事件 FF ie8以上不支持 ie9不支持
  10. // e.returnValue = false;
  11. // 兼容性写法
  12. if(e.preventDefault){
  13. e.preventDefault();
  14. }else{
  15. //兼容IE8以下的浏览器
  16. e.returnValue = false;
  17. }
  18. // 阻止默认事件
  19. return false;
  20. }

事件对象中的坐标位置

事件对象中提供了:clientX/Y,x/y,offsetX/Y,screenX/Y,pageX/pageY

clientX/Y和x/y: 相对于浏览器(浏览器的有效区域)的X轴和Y轴的距离

  1. this.innerHTML = `clientX:${e.clientX};clientY:${e.clientY};X:${e.x};Y:${e.y}`;
  2. //clientX:8;clientY:8;X:8;Y:8

screenX/Y:相对于显示器屏幕的X轴和Y轴的距离

  1. this.innerHTML = `screenX:${e.screenX};screenY:${e.screenY};`;
  2. //screenX:8;screenY:110;

pageX/Y: 相对于页面的X轴和Y轴的距离 滚动和clientY不同

  1. this.innerHTML = `pageX:${e.pageX};pageY:${e.pageY};`;
  2. //pageX:8;pageY:1316;

offsetX/Y:相对于事件源的X轴和Y轴的距离

  1. this.innerHTML = `offsetX:${e.offsetX};offsetY:${e.offsetY};`;
  2. //offsetX:0;offsetY:0;

事件总结

事件
1.事件流
描述的是从页面接收事件的顺序
IE事件流是事件冒泡流,Netscape的事件流是事件捕获流
2.事件流阶段
(1)事件捕获阶段 (2)处于目标阶段 (3)事件冒泡阶段
事件捕获阶段:从最不具体的节点(window/document)接收事件 往具体的节点进行传播
事件冒泡阶段:从具体的节点开始接收事件,逐级往上传递到最不具体的节点。
3.事件对象
3.1 e.eventPhase 描述事件发生的阶段
事件捕获阶段 1
处于目标阶段 2
事件冒泡阶段 3
3.2 事件目标
e.currentTarget === this

​ e.target
​ e.srcElement(FF浏览器兼容)

​ //兼容性代码

  1. var target = e.target || e.srcElement;

​ 3.3 事件代理 也叫事件委托
​ 3.4 取消默认行为

  1. e.preventDefault();//ie8不兼容
  2. e.returnValue = false;//兼容
  3. return false;

​ 3.5 事件冒泡

  1. e.bubbles
  2. blur focus scroll三个事件返回值为false
  3. e.stopPropagation(); //常用
  4. e.stopImmediatePropagation();
  5. e.cancelBubble = true;
  6. var handler = function(e){
  7. e = e || window.event;
  8. if(e.stopPropagation){
  9. e.stopPropagation();
  10. }else{
  11. e.cancelBubble = true;
  12. }
  13. }

​ 4.事件处理程序

​ 1) HTML事件处理程序
​ 2) DOM0级事件处理程序

  1. btn.onclick = function(e){
  2. e = e || window.event;
  3. }
  4. //有事件覆盖现象
  5. btn.onclick = function(e){
  6. e = e || window.event;
  7. }

​ 3) DOM2级事件处理程序

  1. btn.addEventListener('click',function (){
  2. },false);
  3. btn.addEventListener('click',function (){
  4. },false);
  5. var handler = function(){
  6. ....
  7. }
  8. btn.addEventListener('click',handler,false);
  9. btn.removeEventListener('click',handler)

​ 4) IE事件处理程序

  1. btn.attachEvent('onclick',function(){
  2. //在ie中小心this 这个this指向了window
  3. })
  4. btn.detachEvent('onclick',function(){
  5. })
  6. 处理this的指向问题,函数中的call(target);
  1. 事件对象中的属性 坐标位置
    5.1 clientX/Y x/y
    1. 相对于当前的浏览器(有效的浏览器区域)的x轴和y轴的距离,与滚动条无关
    5.2 screenX/Y
    1. 相对于显示器屏幕的x轴和y轴的距离
    5.3 pageX/Y
    1. 相对于页面的x轴和y轴的距离,如果有滚动条,包含整个页面,与滚动条有关
    5.4 offsetX/Y
    1. 相对于事件源x轴和y轴的距离

放大镜效果结构样式搭建

css代码

  1. <style type="text/css">
  2. *{margin:0px;padding:0px;}
  3. #box{width:430px;height:430px;border:1px solid #DDD;position:relative;margin:50px;}
  4. #small_box{width:430px;height:430px;position:relative;}
  5. #small_box #mask{position:absolute;width:210px;height:210px;background:url(images/dotted.png) repeat;top:0px;left:0px;display:none;}
  6. #big_box{position:absolute;left:440px;top:0px;width:430px;height:430px;border:1px solid #ddd;overflow:hidden;display:none;}
  7. #big_box img{position:absolute;z-index:5;}
  8. </style>

html代码

  1. <div id="box">
  2. <div id="small_box">
  3. <img src="images/photo.jpg">
  4. <span id="mask"></span>
  5. </div>
  6. <div id="big_box">
  7. <img src="images/photo01.jpg">
  8. </div>
  9. </div>

javascript代码

  1. window.onload = function(){
  2. //1.获取需要的标签
  3. var box = document.getElementById('box');
  4. var small_box = box.children[0];
  5. var big_box = box.children[1];
  6. var small_img = small_box.children[0];
  7. var mask = small_box.children[1];
  8. var big_img = big_box.children[0];
  9. //2.监听鼠标移动
  10. small_box.onmouseover = function(){
  11. //2.1让遮罩层和大盒子显示出来
  12. mask.style.display = 'block';
  13. big_box.style.display = 'block';
  14. //2.2监听鼠标移动
  15. small_box.onmousemove = function(e){
  16. e = e || window.event;
  17. //2.3求出小盒子移动的水平和垂直的距离
  18. var moveX = e.clientX - small_box.offsetLeft - box.offsetLeft - mask.offsetWidth*0.5;
  19. //small_box.offsetLeft 为0
  20. var moveY = e.clientY - small_box.offsetTop - box.offsetTop - mask.offsetHeight*0.5;
  21. //2.4移动边界处理
  22. if(moveX < 0){
  23. moveX = 0;
  24. }else if(moveX >= small_box.offsetWidth - mask.offsetWidth){
  25. moveX = small_box.offsetWidth - mask.offsetWidth;
  26. }
  27. if(moveY < 0){
  28. moveY = 0;
  29. }else if(moveY >= small_box.offsetWidth - mask.offsetWidth){
  30. moveY = small_box.offsetHeight - mask.offsetHeight;
  31. }
  32. //2.5让小盒子移动起来
  33. mask.style.left = moveX + 'px';
  34. mask.style.top = moveY + 'px';
  35. //2.6大图移动起来
  36. //公式:moveX/大图移动的距离 = (small_box宽度-mask宽度)/(big_img宽度-big_box宽度);
  37. // var x = moveX / (small_box.offsetWidth - mask.offsetWidth);
  38. // var y = moveY / (big_img.offsetWidth - big_box.offsetWidth);
  39. var scale = (big_img.offsetWidth - big_box.offsetWidth)/(small_box.offsetWidth - mask.offsetWidth);
  40. // console.log(small_box.offsetWidth);//430
  41. // console.log(mask.offsetWidth);//210
  42. // console.log(big_img.offsetWidth);//800
  43. // console.log(big_box.offsetWidth);//432
  44. // big_img.style.left = - x * (big_img.offsetWidth - big_box.offsetWidth) +'px';
  45. // big_img.style.top = - y * (big_img.offsetHeight - big_box.offsetHeight) +'px';
  46. big_img.style.left = - moveX * scale +'px';
  47. big_img.style.top = - moveY * scale +'px';
  48. // var lw = mask.offsetLeft;
  49. // var lh = mask.offsetTop;
  50. // var rate = big_img.offsetWidth/big_box.offsetWidth;
  51. // // var rate = small_box.offsetWidth/mask.offsetWidth;
  52. // var newX = lw * 1.68;
  53. // var newY = lh * 1.68;
  54. // big_img.style.left = -newX +'px';
  55. // big_img.style.top = -newY +'px';
  56. }
  57. }
  58. small_box.onmouseout = function(e){
  59. mask.style.display = 'none';
  60. big_box.style.display = 'none';
  61. }
  62. }
Statement of this Website
The copyright of this blog article belongs to the blogger. Please specify the address when reprinting! If there is any infringement or violation of the law, please contact admin@php.cn Report processing!
All comments Speak rationally on civilized internet, please comply with News Comment Service Agreement
0 comments
Author's latest blog post
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!