文档对象模型(Document Object Model,简称 DOM),是 W3C 组织推荐的处理可扩展标记语言(HTML或者XML)的标准编程接口,W3C 已经定义了一系列的 DOM 接口,通过这些 DOM 接口可以改变网页的内容、结构和样式。
DOM 把以上内容都看做是对象
DOM在我们实际开发中主要用来操作元素。
我们如何来获取页面中的元素呢?
获取页面中的元素可以使用以下几种方式:
使用 getElementByld() 方法可以获取带ID的元素对象
doucument.getElementByld('id名')
使用 console.dir() 可以打印我们获取的元素对象,更好的查看对象里面的属性和方法。
示例
<div id="time">2019-9-9</div>
<script>
// 1.因为我们文档页面从上往下加载,所以得先有标签,所以script写在标签下面
// 2.get 获得 element 元素 by 通过 驼峰命名法
// 3.参数 id是大小写敏感的字符串
// 4.返回的是一个元素对象
var timer=document.getElementById('time');
console.log(timer);
// 5. console.dir 打印我们的元素对象,更好的查看里面的属性和方法
console.dir(timer);
</script>
根据标签名获取,使用 getElementByTagName() 方法可以返回带有指定标签名的对象的集合
doucument.getElementsByTagName('标签名');
<ul>
<li>知否知否,应是等你好久</li>
<li>知否知否,应是等你好久</li>
<li>知否知否,应是等你好久</li>
<li>知否知否,应是等你好久</li>
<li>知否知否,应是等你好久</li>
</ul>
<script>
// 1.返回的是获取过来元素对象的集合 以伪数组的形式存储
var lis=document.getElementsByTagName('li');
console.log(lis);
console.log(lis[0]);
// 2.依次打印,遍历
for (var i=0; i < lis.length; i++) {
console.log(lis[i]);
}
// 3.如果页面中只有 1 个 li,返回的还是伪数组的形式
// 4.如果页面中没有这个元素,返回的是空伪数组
</script>
还可以根据标签名获取某个元素(父元素)内部所有指定标签名的子元素,获取的时候不包括父元素自己
element.getElementsByTagName('标签名')
ol.getElementsByTagName('li');
注意:父元素必须是单个对象(必须指明是哪一个元素对象),获取的时候不包括父元素自己
<script>
//element.getElementsByTagName('标签名'); 父元素必须是指定的单个元素
var ol=document.getElementById('ol');
console.log(ol.getElementsByTagName('li'));
</script>
根据类名返回元素对象合集
document.getElementsByClassName('类名');
根据指定选择器返回第一个元素对象
document.querySelector('选择器');
// 切记里面的选择器需要加符号
// 类选择器.box
// id选择器 #nav
var firstBox=document.querySelector('.box');
根据指定选择器返回所有元素对象
document.querySelectorAll('选择器');
注意:
querySelector 和 querySelectorAll 里面的选择器需要加符号,比如: document.querySelector('#nav');
<script>
// 1. getElementsByClassName 根据类名获得某些元素集合
var boxs=document.getElementsByClassName('box');
console.log(boxs);
// 2. querySelector 返回指定选择器的第一个元素对象 切记 里面的选择器需要加符号 .box #nav
var firstBox=document.querySelector('.box');
console.log(firstBox);
var nav=document.querySelector('#nav');
console.log(nav);
var li=document.querySelector('li');
console.log(li);
// 3. querySelectorAll()返回指定选择器的所有元素对象集合
var allBox=document.querySelectorAll('.box');
console.log(allBox);
var lis=document.querySelectorAll('li');
console.log(lis);
</script>
返回body元素对象
document.body;
返回html元素对象
document.documentElement;
JavaScript 使我们有能力创建动态页面,而事件是可以被 JavaScript 侦测到的行为。
简单理解: 触发— 响应机制。
网页中的每个元素都可以产生某些可以触发 JavaScript 的事件,例如,我们可以在用户点击某按钮时产生一个事件,然后去执行某些操作。
<script>
// 点击一个按钮,弹出对话框
// 1. 事件是有三部分组成 事件源 事件类型 事件处理程序 我们也称为事件三要素
//(1) 事件源 事件被触发的对象 谁 按钮
var btn=document.getElementById('btn');
//(2) 事件类型 如何触发 什么事件 比如鼠标点击(onclick) 还是鼠标经过 还是键盘按下
//(3) 事件处理程序 通过一个函数赋值的方式 完成
btn.onclick=function() {
alert('点秋香');
}
</script>
<script>
// 执行事件步骤
// 点击div 控制台输出 我被选中了
// 1. 获取事件源
var div=document.querySelector('div');
// 2.绑定事件 注册事件
// div.onclick
// 3.添加事件处理程序
div.onclick=function() {
console.log('我被选中了');
}
</script>
鼠标事件 | 触发条件 |
onclick | 鼠标点击左键触发 |
onmouseover | 鼠标经过触发 |
onmouseout | 鼠标离开触发 |
onfocus | 获得鼠标焦点触发 |
onblur | 失去鼠标焦点触发 |
onmousemove | 鼠标移动触发 |
onmouseup | 鼠标弹起触发 |
onmousedown | 鼠标按下触发 |
JavaScript 的 DOM 操作可以改变网页内容、结构和样式,我们可以利用 DOM 操作元素来改变元素里面的内容 、属性等。注意以下都是属性
从起始位置到终止位置的内容,但它去除html标签,同时空格和换行也会去掉。
element.innerText
起始位置到终止位置的全部内容,包括HTML标签,同时保留空格和换行
element.innerHTML
<body>
<div></div>
<p>
我是文字
<span>123</span>
</p>
<script>
// innerText 和 innerHTML的区别
// 1. innerText 不识别html标签,去除空格和换行
var div=document.querySelector('div');
div.innerText='<strong>今天是:</strong> 2019';
// 2. innerHTML 识别html标签 保留空格和换行的
div.innerHTML='<strong>今天是:</strong> 2019';
// 这两个属性是可读写的 可以获取元素里面的内容
var p=document.querySelector('p');
console.log(p.innerText);
console.log(p.innerHTML);
</script>
</body>
// img.属性
img.src="xxx";
input.value="xxx";
input.type="xxx";
input.checked="xxx";
input.selected=true / false;
input.disabled=true / false;
我们可以通过 JS 修改元素的大小、颜色、位置等样式。
// element.style
div.style.backgroundColor='pink';
div.style.width='250px';
// element.className
注意:
<body>
<div class="first">文本</div>
<script>
// 1. 使用 element.style 获得修改元素样式 如果样式比较少 或者 功能简单的情况下使用
var test=document.querySelector('div');
test.onclick=function() {
// this.style.backgroundColor='purple';
// this.style.color='#fff';
// this.style.fontSize='25px';
// this.style.marginTop='100px';
// 让我们当前元素的类名改为了 change
// 2. 我们可以通过 修改元素的className更改元素的样式 适合于样式较多或者功能复杂的情况
// 3. 如果想要保留原先的类名,我们可以这么做 多类名选择器
// this.className='change';
this.className='first change';
}
</script>
</body>
如果有同一组元素,我们相要某一个元素实现某种样式,需要用到循环的排他思想算法:
<body>
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>
<script>
// 1. 获取所有按钮元素
var btns=document.getElementsByTagName('button');
// btns得到的是伪数组 里面的每一个元素 btns[i]
for (var i=0; i < btns.length; i++) {
btns[i].onclick=function() {
// (1) 我们先把所有的按钮背景颜色去掉 干掉所有人
for (var i=0; i < btns.length; i++) {
btns[i].style.backgroundColor='';
}
// (2) 然后才让当前的元素背景颜色为pink 留下我自己
this.style.backgroundColor='pink';
}
}
//2. 首先先排除其他人,然后才设置自己的样式 这种排除其他人的思想我们成为排他思想
</script>
</body>
element.属性;
element.getAttribute('属性');
element.属性='值';
element.setAttribute('属性','值');
element.removeAttribute('属性');
<body>
<div id="demo" index="1" class="nav"></div>
<script>
var div=document.querySelector('div');
// 1. 获取元素的属性值
// (1) element.属性
console.log(div.id);
//(2) element.getAttribute('属性') get得到获取 attribute 属性的意思 我们程序员自己添加的属性我们称为自定义属性 index
console.log(div.getAttribute('id'));
console.log(div.getAttribute('index'));
// 2. 设置元素属性值
// (1) element.属性='值'
div.id='test';
div.className='navs';
// (2) element.setAttribute('属性', '值'); 主要针对于自定义属性
div.setAttribute('index', 2);
div.setAttribute('class', 'footer'); // class 特殊 这里面写的就是class 不是className
// 3 移除属性 removeAttribute(属性)
div.removeAttribute('index');
</script>
</body>
自定义属性目的:
H5规定自定义属性 data-开头作为属性名并赋值
<div data-index="1"></>
// 或者使用JavaScript设置
div.setAttribute('data-index',1);
<body>
<div getTime="20" data-index="2" data-list-name="andy"></div>
<script>
var div=document.querySelector('div');
console.log(div.getAttribute('getTime'));
div.setAttribute('data-time', 20);
console.log(div.getAttribute('data-index'));
console.log(div.getAttribute('data-list-name'));
// h5新增的获取自定义属性的方法 它只能获取data-开头的
// dataset 是一个集合里面存放了所有以data开头的自定义属性
console.log(div.dataset);
console.log(div.dataset.index);
console.log(div.dataset['index']);
// 如果自定义属性里面有多个-链接的单词,我们获取的时候采取 驼峰命名法
console.log(div.dataset.listName);
console.log(div.dataset['listName']);
</script>
</body>
获取元素通常使用两种方式:
1.利用DOM提供的方法获取元素 | 2.利用节点层级关系获取元素 |
document.getElementById() | 利用父子兄节点关系获取元素 |
document.getElementsByTagName() | 逻辑性强,但是兼容性较差 |
document.querySelector 等 | |
逻辑性不强,繁琐 |
这两种方式都可以获取元素节点,我们后面都会使用,但是节点操作更简单
一般的,节点至少拥有三个基本属性
网页中的所有内容都是节点(标签、属性、文本、注释等),在DOM 中,节点使用 node 来表示。
HTML DOM 树中的所有节点均可通过 JavaScript 进行访问,所有 HTML 元素(节点)均可被修改,也可以创建或删除。
一般的,节点至少拥有nodeType(节点类型)、nodeName(节点名称)和nodeValue(节点值)这三个基本属性。
我们在实际开发中,节点操作主要操作的是元素节点
利用 DOM 树可以把节点划分为不同的层级关系,常见的是父子兄层级关系。
node.parentNode
<body>
<!-- 节点的优点 -->
<div>我是div</div>
<span>我是span</span>
<ul>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
</ul>
<div class="demo">
<div class="box">
<span class="erweima">×</span>
</div>
</div>
<script>
// 1. 父节点 parentNode
var erweima=document.querySelector('.erweima');
// var box=document.querySelector('.box');
// 得到的是离元素最近的父级节点(亲爸爸) 如果找不到父节点就返回为 null
console.log(erweima.parentNode);
</script>
</body>
parentNode.childNodes(标准)
parentNode.children(非标准)
<body>
<ul>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
</ul>
<ol>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
</ol>
<script>
// DOM 提供的方法(API)获取
var ul=document.querySelector('ul');
var lis=ul.querySelectorAll('li');
// 1. 子节点 childNodes 所有的子节点 包含 元素节点 文本节点等等
console.log(ul.childNodes);
console.log(ul.childNodes[0].nodeType);
console.log(ul.childNodes[1].nodeType);
// 2. children 获取所有的子元素节点 也是我们实际开发常用的
console.log(ul.children);
</script>
</body>
parentNode.firstChild
parentNode.lastChild
<body>
<ol>
<li>我是li1</li>
<li>我是li2</li>
<li>我是li3</li>
<li>我是li4</li>
<li>我是li5</li>
</ol>
<script>
var ol=document.querySelector('ol');
// 1. firstChild 第一个子节点 不管是文本节点还是元素节点
console.log(ol.firstChild);
console.log(ol.lastChild);
// 2. firstElementChild 返回第一个子元素节点 ie9才支持
console.log(ol.firstElementChild);
console.log(ol.lastElementChild);
// 3. 实际开发的写法 既没有兼容性问题又返回第一个子元素
console.log(ol.children[0]); //第一个子元素节点
console.log(ol.children[ol.children.length - 1]);//最后一个子元素节点
</script>
</body>
parentNode.firstElementChild
parentNode.lastElementChild
实际开发中,firstChild 和 lastChild 包含其他节点,操作不方便,而 firstElementChild 和 lastElementChild 又有兼容性问题,那么我们如何获取第一个子元素节点或最后一个子元素节点呢?
解决方案
// 数组元素个数减1 就是最后一个元素的索引号
parentNode.chilren[parentNode.chilren.length - 1]
<body>
<ol>
<li>我是li1</li>
<li>我是li2</li>
<li>我是li3</li>
<li>我是li4</li>
</ol>
<script>
var ol=document.querySelector('ol');
// 1.firstChild 获取第一个子结点的,包含文本结点和元素结点
console.log(ol.firstChild);
// 返回的是文本结点 #text(第一个换行结点)
console.log(ol.lastChild);
// 返回的是文本结点 #text(最后一个换行结点)
// 2. firstElementChild 返回第一个子元素结点
console.log(ol.firstElementChild);
// <li>我是li1</li>
// 第2个方法有兼容性问题,需要IE9以上才支持
// 3.实际开发中,既没有兼容性问题,又返回第一个子元素
console.log(ol.children[0]);
// <li>我是li1</li>
console.log(ol.children[3]);
// <li>我是li4</li>
// 当里面li个数不唯一时候,需要取到最后一个结点时这么写
console.log(ol.children[ol.children.length - 1]);
</script>
</body>
node.nextSibling
node.previousSibling
node.nextElementSibling
node.previousElementSibling
示例
<body>
<div>我是div</div>
<span>我是span</span>
<script>
var div=document.querySelector('div');
// 1.nextSibling 下一个兄弟节点 包含元素节点或者 文本节点等等
console.log(div.nextSibling); // #text
console.log(div.previousSibling); // #text
// 2. nextElementSibling 得到下一个兄弟元素节点
console.log(div.nextElementSibling); //<span>我是span</span>
console.log(div.previousElementSibling);//null
</script>
</body>
如何解决兼容性问题 ?
答:自己封装一个兼容性的函数
function getNextElementSibling(element) {
var el=element;
while(el=el.nextSibling) {
if(el.nodeType===1){
return el;
}
}
return null;
}
document.createElement('tagName');
node.appendChild(child)
node.insertBefore(child,指定元素)
示例
<body>
<ul>
<li>123</li>
</ul>
<script>
// 1. 创建节点元素节点
var li=document.createElement('li');
// 2. 添加节点 node.appendChild(child) node 父级 child 是子级 后面追加元素 类似于数组中的push
// 先获取父亲ul
var ul=document.querySelector('ul');
ul.appendChild(li);
// 3. 添加节点 node.insertBefore(child, 指定元素);
var lili=document.createElement('li');
ul.insertBefore(lili, ul.children[0]);
// 4. 我们想要页面添加一个新的元素分两步: 1. 创建元素 2. 添加元素
</script>
</body>
node.removeChild(child)
node.cloneNode()
示例
<body>
<ul>
<li>1111</li>
<li>2</li>
<li>3</li>
</ul>
<script>
var ul=document.querySelector('ul');
// 1. node.cloneNode(); 括号为空或者里面是false 浅拷贝 只复制标签不复制里面的内容
// 2. node.cloneNode(true); 括号为true 深拷贝 复制标签复制里面的内容
var lili=ul.children[0].cloneNode(true);
ul.appendChild(lili);
</script>
</body>
三种动态创建元素的区别
区别:
<body>
<div class="innner"></div>
<div class="create"></div>
<script>
// 2. innerHTML 创建元素
var inner=document.querySelector('.inner');
// 2.1 innerHTML 用拼接字符串方法
for (var i=0; i <=100; i++) {
inner.innerHTML +='<a href="#">百度</a>';
}
// 2.2 innerHTML 用数组形式拼接
var arr=[];
for (var i=0; i <=100; i++) {
arr.push('<a href="#">百度</a>');
}
inner.innerHTML=arr.join('');
// 3.document.createElement() 创建元素
var create=document.querySelector('.create');
var a=document.createElement('a');
create.appendChild(a);
</script>
</body>
总结:不同浏览器下, innerHTML 效率要比 createElement 高
对于DOM操作,我们主要针对子元素的操作,主要有
给元素添加事件,称为注册事件或者绑定事件。
注册事件有两种方式:传统方式和方法监听注册方式
传统注册方式 | 方法监听注册方式 |
利用 on 开头的事件 onclick | w3c 标准推荐方式 |
<button onclick="alert("hi")"></button> | addEventListener() 它是一个方法 |
btn.onclick=function() {} | IE9 之前的 IE 不支持此方法,可使用 attachEvent() 代替 |
特点:注册事件的唯一性 | 特点:同一个元素同一个事件可以注册多个监听器 |
同一个元素同一个事件只能设置一个处理函数,最后注册的处理函数将会覆盖前面注册的处理函数 | 按注册顺序依次执行 |
eventTarget.addEventListener(type,listener[,useCapture])
该方法接收三个参数:
<body>
<button>传统注册事件</button>
<button>方法监听注册事件</button>
<button>ie9 attachEvent</button>
<script>
var btns=document.querySelectorAll('button');
// 1. 传统方式注册事件
btns[0].onclick=function() {
alert('hi');
}
btns[0].onclick=function() {
alert('hao a u');
}
// 2. 事件监听注册事件 addEventListener
// (1) 里面的事件类型是字符串 所以加引号 而且不带on
// (2) 同一个元素 同一个事件可以添加多个侦听器(事件处理程序)
btns[1].addEventListener('click', function() {
alert(22);
})
btns[1].addEventListener('click', function() {
alert(33);
})
// 3. attachEvent ie9以前的版本支持
btns[2].attachEvent('onclick', function() {
alert(11);
})
</script>
</body>
eventTarget.attachEvent(eventNameWithOn,callback)
该方法接收两个参数:
兼容性处理的原则:首先照顾大多数浏览器,再处理特殊浏览器
function addEventListener(element, eventName, fn) {
// 判断当前浏览器是否支持 addEventListener 方法
if (element.addEventListener) {
element.addEventListener(eventName, fn); // 第三个参数 默认是false
} else if (element.attachEvent) {
element.attachEvent('on' + eventName, fn);
} else {
// 相当于 element.onclick=fn;
element['on' + eventName]=fn;
}
eventTarget.removeEventListener(type,listener[,useCapture]);
该方法接收三个参数:
eventTarget.detachEvent(eventNameWithOn,callback);
该方法接收两个参数:
eventTarget.onclick=null;
事件删除示例:
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<script>
var divs=document.querySelectorAll('div');
divs[0].onclick=function() {
alert(11);
// 1. 传统方式删除事件
divs[0].onclick=null;
}
// 2.removeEventListener 删除事件
divs[1].addEventListener('click',fn); //里面的fn不需要调用加小括号
function fn(){
alert(22);
divs[1].removeEventListener('click',fn);
}
// 3.IE9 中的删除事件方式
divs[2].attachEvent('onclick',fn1);
function fn1() {
alert(33);
divs[2].detachEvent('onclick',fn1);
}
</script>
</body>
function removeEventListener(element, eventName, fn) {
// 判断当前浏览器是否支持 removeEventListener 方法
if (element.removeEventListener) {
element.removeEventListener(eventName, fn); // 第三个参数 默认是false
} else if (element.detachEvent) {
element.detachEvent('on' + eventName, fn);
} else {
element['on' + eventName]=null;
}
加深理解:
我们向水里面扔一块石头,首先它会有一个下降的过程,这个过程就可以理解为从最顶层向事件发生的最具体元素(目标点)的捕获过程;之后会产生泡泡,会在最低点( 最具体元素)之后漂浮到水面上,这个过程相当于事件冒泡。
两个盒子嵌套,一个父盒子一个子盒子,我们的需求是当点击父盒子时弹出 father ,当点击子盒子时弹出 son
<body>
<div class="father">
<div class="son">son盒子</div>
</div>
<script>
// dom 事件流 三个阶段
// 1. JS 代码中只能执行捕获或者冒泡其中的一个阶段。
// 2. onclick 和 attachEvent(ie) 只能得到冒泡阶段。
// 3. 捕获阶段 如果addEventListener 第三个参数是 true 那么则处于捕获阶段 document -> html -> body -> father -> son
var son=document.querySelector('.son');
son.addEventListener('click', function() {
alert('son');
}, true);
var father=document.querySelector('.father');
father.addEventListener('click', function() {
alert('father');
}, true);
</script>
</body>
但是因为DOM流的影响,我们点击子盒子,会先弹出 father,之后再弹出 son
这是因为捕获阶段由 DOM 最顶层节点开始,然后逐级向下传播到到最具体的元素接收
<body>
<div class="father">
<div class="son">son盒子</div>
</div>
<script>
// 4. 冒泡阶段 如果addEventListener 第三个参数是 false 或者 省略 那么则处于冒泡阶段 son -> father ->body -> html -> document
var son=document.querySelector('.son');
son.addEventListener('click', function() {
alert('son');
}, false);
var father=document.querySelector('.father');
father.addEventListener('click', function() {
alert('father');
}, false);
document.addEventListener('click', function() {
alert('document');
})
</script>
</body>
我们点击子盒子,会弹出 son、father、document
这是因为冒泡阶段开始时由最具体的元素接收,然后逐级向上传播到到 DOM 最顶层节点
eventTarget.onclick=function(event) {
// 这个 event 就是事件对象,我们还喜欢的写成 e 或者 evt
}
eventTarget.addEventListener('click', function(event) {
// 这个 event 就是事件对象,我们还喜欢的写成 e 或者 evt
})
简单理解:
事件发生后,跟事件相关的一系列信息数据的集合都放到这个对象里面
这个对象就是事件对象 event,它有很多属性和方法,比如“
谁绑定了这个事件
鼠标触发事件的话,会得到鼠标的相关信息,如鼠标位置
键盘触发事件的话,会得到键盘的相关信息,如按了哪个键
<body>
<div>123</div>
<script>
// 事件对象
var div=document.querySelector('div');
div.onclick=function(e) {
// console.log(e);
// console.log(window.event);
// e=e || window.event;
console.log(e);
}
// 1. event 就是一个事件对象 写到我们侦听函数的 小括号里面 当形参来看
// 2. 事件对象只有有了事件才会存在,它是系统给我们自动创建的,不需要我们传递参数
// 3. 事件对象 是 我们事件的一系列相关数据的集合 跟事件相关的 比如鼠标点击里面就包含了鼠标的相关信息,鼠标坐标啊,如果是键盘事件里面就包含的键盘事件的信息 比如 判断用户按下了那个键
// 4. 这个事件对象我们可以自己命名 比如 event 、 evt、 e
// 5. 事件对象也有兼容性问题 ie678 通过 window.event 兼容性的写法 e=e || window.event;
</script>
</body>
事件对象本身的获取存在兼容问题:
解决:
e=e || window.event;
事件对象属性方法 | 说明 |
e.target | 返回触发事件的对象 标准 |
e.srcElement | 返回触发事件的对象 非标准 ie6-8使用 |
e.type | 返回事件的类型 比如click mouseover 不带on |
e.cancelBubble | 该属性阻止冒泡,非标准,ie6-8使用 |
e.returnValue | 该属性阻止默认行为 非标准,ie6-8使用 |
e.preventDefault() | 该方法阻止默认行为 标准 比如不让链接跳转 |
e.stopPropagation() | 阻止冒泡 标准 |
e.target 和 this 的区别:
<body>
<div>123</div>
<a href="http://www.baidu.com">百度</a>
<form action="http://www.baidu.com">
<input type="submit" value="提交" name="sub">
</form>
<script>
// 常见事件对象的属性和方法
// 1. 返回事件类型
var div=document.querySelector('div');
div.addEventListener('click', fn);
div.addEventListener('mouseover', fn);
div.addEventListener('mouseout', fn);
function fn(e) {
console.log(e.type);
}
// 2. 阻止默认行为(事件) 让链接不跳转 或者让提交按钮不提交
var a=document.querySelector('a');
a.addEventListener('click', function(e) {
e.preventDefault(); // dom 标准写法
})
// 3. 传统的注册方式
a.onclick=function(e) {
// 普通浏览器 e.preventDefault(); 方法
// e.preventDefault();
// 低版本浏览器 ie678 returnValue 属性
// e.returnValue;
// 我们可以利用return false 也能阻止默认行为 没有兼容性问题 特点: return 后面的代码不执行了, 而且只限于传统的注册方式
return false;
alert(11);
}
</script>
</body>
事件冒泡:开始时由最具体的元素接收,然后逐级向上传播到到 DOM 最顶层节点
事件冒泡本身的特性,会带来的坏处,也会带来的好处,需要我们灵活掌握。
e.stopPropagation();
e.cancelBubble=true;
<body>
<div class="father">
<div class="son">son儿子</div>
</div>
<script>
// 常见事件对象的属性和方法
// 阻止冒泡 dom 推荐的标准 stopPropagation()
var son=document.querySelector('.son');
son.addEventListener('click', function(e) {
alert('son');
e.stopPropagation(); // stop 停止 Propagation 传播
e.cancelBubble=true; // 非标准 cancel 取消 bubble 泡泡
}, false);
var father=document.querySelector('.father');
father.addEventListener('click', function() {
alert('father');
}, false);
document.addEventListener('click', function() {
alert('document');
})
</script>
</body>
if(e && e.stopPropagation){
e.stopPropagation();
}else{
window.event.cancelBubble=true;
}
e.target 与 this 的区别
<body>
<div>123</div>
<ul>
<li>abc</li>
<li>abc</li>
<li>abc</li>
</ul>
<script>
// 常见事件对象的属性和方法
// 1. e.target 返回的是触发事件的对象(元素) this 返回的是绑定事件的对象(元素)
// 区别 : e.target 点击了那个元素,就返回那个元素 this 那个元素绑定了这个点击事件,那么就返回谁
var div=document.querySelector('div');
div.addEventListener('click', function(e) {
console.log(e.target);
console.log(this);
})
var ul=document.querySelector('ul');
ul.addEventListener('click', function(e) {
// 我们给ul 绑定了事件 那么this 就指向ul
console.log(this);
console.log(e.currentTarget);
// e.target 指向我们点击的那个对象 谁触发了这个事件 我们点击的是li e.target 指向的就是li
console.log(e.target);
})
// 了解兼容性
// div.onclick=function(e) {
// e=e || window.event;
// var target=e.target || e.srcElement;
// console.log(target);
// }
// 2. 了解 跟 this 有个非常相似的属性 currentTarget ie678不认识
</script>
</body>
事件对象本身的获取存在兼容问题:
解决方案
<body>
<div>123</div>
<script>
// 事件对象
var div=document.querySelector('div');
div.onclick=function(e) {
// e=e || window.event;
console.log(e);
// 事件对象也有兼容性问题 ie678 通过 window.event 兼容性的写法 e=e || window.event;
}
</body>
<body>
<ul>
<li>知否知否,点我应有弹框在手!</li>
<li>知否知否,点我应有弹框在手!</li>
<li>知否知否,点我应有弹框在手!</li>
<li>知否知否,点我应有弹框在手!</li>
<li>知否知否,点我应有弹框在手!</li>
</ul>
<script>
// 事件委托的核心原理:给父节点添加侦听器, 利用事件冒泡影响每一个子节点
var ul=document.querySelector('ul');
ul.addEventListener('click', function(e) {
// alert('知否知否,点我应有弹框在手!');
// e.target 这个可以得到我们点击的对象
e.target.style.backgroundColor='pink';
// 点了谁,就让谁的style里面的backgroundColor颜色变为pink
})
</script>
</body>
以上案例:给 ul 注册点击事件,然后利用事件对象的 target 来找到当前点击的 li,因为点击 li,事件会冒泡到 ul 上, ul 有注册事件,就会触发事件监听器。
鼠标事件 | 触发条件 |
onclick | 鼠标点击左键触发 |
onmouseover | 鼠标经过触发 |
onmouseout | 鼠标离开触发 |
onfocus | 获得鼠标焦点触发 |
onblur | 失去鼠标焦点触发 |
onmousemove | 鼠标移动触发 |
onmouseup | 鼠标弹起触发 |
onmousedown | 鼠标按下触发 |
<body>
<h1>我是一段不愿意分享的文字</h1>
<script>
// 1. contextmenu 我们可以禁用右键菜单
document.addEventListener('contextmenu', function(e) {
e.preventDefault(); // 阻止默认行为
})
// 2. 禁止选中文字 selectstart
document.addEventListener('selectstart', function(e) {
e.preventDefault();
})
</script>
</body>
鼠标事件对象 | 说明 |
e.clientX | 返回鼠标相对于浏览器窗口可视区的X坐标 |
e.clientY | 返回鼠标相对于浏览器窗口可视区的Y坐标 |
e.pageX(重点) | 返回鼠标相对于文档页面的X坐标 IE9+ 支持 |
e.pageY(重点) | 返回鼠标相对于文档页面的Y坐标 IE9+ 支持 |
e.screenX | 返回鼠标相对于电脑屏幕的X坐标 |
e.screenY | 返回鼠标相对于电脑屏幕的Y坐标 |
示例:
<body>
<script>
// 鼠标事件对象 MouseEvent
document.addEventListener('click', function(e) {
// 1. client 鼠标在可视区的x和y坐标
console.log(e.clientX);
console.log(e.clientY);
console.log('---------------------');
// 2. page 鼠标在页面文档的x和y坐标
console.log(e.pageX);
console.log(e.pageY);
console.log('---------------------');
// 3. screen 鼠标在电脑屏幕的x和y坐标
console.log(e.screenX);
console.log(e.screenY);
})
</script>
</body>
键盘事件 | 触发条件 |
onkeyup | 某个键盘按键被松开时触发 |
onkeydown | 某个键盘按键被按下时触发 |
onkeypress | 某个键盘按键被按下时触发,但是它不识别功能键,比如 ctrl shift 箭头等 |
<body>
<script>
// 常用的键盘事件
//1. keyup 按键弹起的时候触发
// document.onkeyup=function() {
// console.log('我弹起了');
// }
document.addEventListener('keyup', function() {
console.log('我弹起了');
})
//3. keypress 按键按下的时候触发 不能识别功能键 比如 ctrl shift 左右箭头啊
document.addEventListener('keypress', function() {
console.log('我按下了press');
})
//2. keydown 按键按下的时候触发 能识别功能键 比如 ctrl shift 左右箭头啊
document.addEventListener('keydown', function() {
console.log('我按下了down');
})
// 4. 三个事件的执行顺序 keydown -- keypress -- keyup
</script>
</body>
键盘事件对象 属性 | 说明 |
keyCode | 返回该键值的ASCII值 |
心宇1,陈长福2,刘蓉1,王美清1
(1.福州大学 数学与计算机科学学院,福建 福州 350108;2.福建库易信息科技有限责任公司,福建 福州 350000)
由于人工抽取网页信息效率低、成本高,因此根据对大量网页结构的观察,提出基于网页文档对象模型DOM树节点路径相似度的正文抽取方法。依据同网站下的网页结构相同的特点去除网页噪声得到网页的主题内容,然后结合正文节点在DOM树中的路径的相似度抽取正文。通过对不同类型的中文新闻网站上的1 000个网页进行实验,结果表明该方法对于97.6%的网页都能够去除大部分噪声并保持正文内容的完整性,正文抽取结果有93.30%的准确率和95.59%的召回率。所提算法对不同类型的网页都有较好的适应性。
随着互联网技术的快速发展,网页成为人们获取信息的重要来源之一。然而,网页上的数据是海量的,单纯依靠人工手段获取网页信息效率较低,因此需要借助软件对网页信息进行全部或部分地自动过滤和分类。目前常用的自动网页信息获取方法是正文内容抽取,该类方法是一种被广泛应用于互联网数据挖掘的技术,它的目标是从互联网庞大的数据中提取有意义的和有价值的信息,可以用于信息搜索、Web文档分类、数据挖掘、机器翻译、文本摘要等。
常用的正文抽取方法可以分为以下4类:(1)传统的归纳总结正文抽取方法:根据一些信息模式,从特定的信息源中提取相关内容[1]。此方法效率较低、需要较多的手动操作,独立性以及适应性较差。(2)基于网页布局[2]和视觉[3-4]的正文抽取:该方法很大程度上依赖于网页的风格或者结构。当涉及到有更复杂的嵌套关系的网页时会出现偏差。(3)基于语义单元[5]或者数据挖掘、机器学习[6]的正文抽取:通过使用分词和文本分类,虽然准确率有所提高,但是解决方案比较复杂。(4)基于统计的正文抽取[7]:该方法简单而且具有更好的通用性,但是较低的精确度限制了它的进一步应用。此外,它不能处理短文本、表格文本以及有较长评论的文本。
FINN A等[8]提出正文抽取(Body Text Extrac tion,BTE) 算法,将网页中的文字和标签作为序列,抽取序列中文字最多和标签最少的连续的内容。PINTO D等[9]提出文档斜率曲线(Document Slope Curves,DSC) 算法,在FINN的方法的基础上使用窗口方法实现多正文抽取。MANTRATZIS C等[10]提出链接定额过滤(Link Quota Filters,LQE) 算法,通过网页结构分析,分离正文和导航目录等超链接。DEBNATH S等[11]提出特征提取器(Feature Extractor,FE)算法,选择包含有一定特征的文本、图像而且重复出现次数较少的内容块。GOTTRON T等[12]提出正文代码模糊(Content Code Blurring,CCB)算法,选择相同格式的长文本作为网页的正文。刘利等[13]提出基于多特征融合的网页正文信息抽取,从网页的多个特征和设计习惯入手定位正文位置。王利等[14]提出基于内容相似度的正文抽取,根据树节点中文本内容与各级标题的相似度判定小块文本信息的有效性,由此进行网页清洗和正文抽取。
分析网页信息会发现,网页中包含大量与网页主题无关的噪声内容,如广告链接、导航栏、版权信息等。在正文抽取过程中,这些网页噪声会影响抽取效果,因此需要通过去噪方式对网页进行预处理。常用的网页去噪方法有:
YI L等[15]提出用风格树(Style Tree,ST)来表达网页的结构和内容特征,出现相同特征次数多的部分更有可能是噪声数据。GIBSON D等[16]提出Shingle和模板Hash方法。这两种算法的缺点是计算量较大。WANG J Y等[17]提出的主题数据提取(Datarich Section Extraction,DSE)算法,该算法通过从上到下比较两棵相同模板的文档对象模型 (Document Object Model,DOM)树,去除树中相同的部分,剩下的部分作为网页的主题内容。
根据对现有方法的总结以及对网页特征的分析,本文提出基于DOM树节点路径相似度的正文抽取方法,对于不同结构的网页都有较好的适应性,对来源于新浪、网易、搜狐、腾讯等大型门户网站以及多家各类型网站的1 000个网页进行了抽取实验,实验结果表明本文方法有较好的抽取准确度。
1网页去噪
目前,大部分网页的源代码是以超文本标记语言 (Hyper Text Markup Language,HTML)的形式存在的。对于同一网站下的不同网页,它们由同一个模板生成,因此这些网页具有相似的结构,而这些网页中相同的部分就是噪声内容,它们与网页所要表达的主题没有关系。本文在DSE算法的基础上,首先将与网页无关的标签及相关代码删除,然后通过将某个网页与同一网站下的2个或多个网页进行对比去除相同部分,从而达到去除噪声的目的。
1.1删除无关的标签
网页源代码包含了以不同的标签括起来的各段代码。例如,网页标题和一些修饰性代码主要嵌在标签<head>和</head>的内部,网页主题内容包含在<body>和</body>标签之间,客户端脚本则包含在<script>和</script>标签之间。通过对大量HTML文本的研究和分析,发现以下几类标签与网页主题内容的相关性很低,在对比网页之前可以将这部分内容过滤掉以提高后续的对比速度。
<head>与</head>标签以及它们之间的内容。
<script></script>标签。该标签中内容的主要功能是定义客户端脚本,与网页所要表达的内容关系不大,也可以将其删除,类似地,<noScript></noScript>也可删除。
大部分网页通过层叠样式表(Cascading Style Sheets,CSS)来调整页面的布局,<style></style>标签用于定义HTML文档的样式信息,同样可以删除。
注释标签<!--注释内容-->、<!注释内容>只是为网站编辑提供说明,并不会在浏览器中显示,也可删除。
在预处理过程中利用正则表达式删除以上噪声代码。正则表达式通过使用单个字符串来描述、匹配一系列符合某个句法规则的网页源代码。符合匹配规则的源代码将被删除。
删除完无关标签后,再删除空白行,这样完成了去噪的第一步。
1.2通过网页对比去除噪声
网页对比可以通过对比它们的 DOM树来实现。DOM是文档中数据和结构的一个树形表示, 它定义了表示和修改文档所需的对象、这些对象的行为和属性以及这些对象之间的关系。DOM实际上是以面向对象方式描述的文档模型。它可以以一种独立于平台和语言的方式访问和修改一个文档的内容和结构。图1给出了一个文档的DOM树的结构图。
通过HTML解析(如使用解析器htmlcxx)可以将HTML文档转换为DOM树结构。假设要处理的是某网站的网页URL1,随机选取该网站下的另外两个网页URL2和URL3,获得它们的DOM树。然后分别对比DOM1\DOM2以及DOM1\DOM3, 输出不同的节点。
对比算法的基本思路是:按深度遍历3棵树的节点,为每个节点设置深度、路径、文本内容、是否为tag(HTML标签)。以第1个网页作为目标与另外两个网页进行对比,如果3个节点深度相同,则判断节点的文本内容是否相同,相同的加入模板集合中,不同的加入网页内容集合中;如果3个节点深度不同,则根据不同情况对相应的节点进行处理,其中网页1的节点加入到网页内容集合中。直到3个网页都遍历到end节点为止。最后得到的就是网页1的主题内容, 过滤了噪声部分。
算法伪代码如下:
for(i=begin1 : end1; j=begin2 : end2; k=begin3 : end3)
{
if(depth1==depth2==depth3)
if(i->text==j->text==k->text)
i加入模板集合;
else
i加入内容集合;
while(depth1 > depth2 || depth1 > depth3)
i++;
}
while(depth1 < depth2)
j++;
while(depth1 < depth3)
k++;
2正文抽取
HTML文档转换成DOM树以后,每个节点都有唯一确定的路径。网页中不同内容块的节点在DOM树中的公共路径较少,而同一内容块的节点的公共路径很长。本文以这些路径之间的相似度作为不同节点是否属于同一内容块的依据。所有的主题内容都在叶子节点上,记所有叶子节点的路径为:
其中TAi为文本节点内容。
例如:
<html>
<body>
<div>
<p>This is the first block.</p>
<p>This is the second block.</p>
<p>This is the third block.</p>
</div>
<div>
<p>test1</p>
</div>
</body>
</html>
这段网页源代码中的 “This is the first block”节点的路径为:
“This is the second block”节点的路径为:
记深度相同的节点A、B的相似度为
0TA≠TB,depth为节点的深度,则任意两个节点A、B的路径的相似度可以定义为:
其中nA、nB分别表示节点A、B的深度。
通过对大量网页的研究发现,正文内容节点大都拥有共同的父节点或者祖父节点,取阈值Th=1-12depth(maxl)-2,其中,maxl为P中字符最多的节点;depth为节点深度,即路径Pi中的元素个数。记集合P中字符最多的节点为L,与P中其他节点计算相似度,大于阈值的作为正文内容。
3实验结果分析
本文从新浪、网易、搜狐、腾讯等大型门户网站以及多家各类型网站中抽取了1 000个网页作为测试数据,采用基于网页DOM树节点路径相似度的正文抽取方法进行实验,去噪结果和正文抽取结果如表1所示。
从表1的统计结果可以看出,有97.6%的网页清洗掉了大部分的噪声并且完整保留了网页中的有效信息;对于新浪、网易等门户网站的抽取结果较好,都有90%以上的准确率和95%以上的召回率;对于其他不同结构的网站,本文的正文抽取方法也都能适用,很好地实现了网页正文抽取的工作,并且有着较高的准确率和召回率。
为了验证本文方法的有效性,以上述的1 000个网页作为样本,将本文方法与BTE、DSC、FE、LQF、CCB等算法进行对比实验,实验结果如表2所示。
由表2可以看出,本文提出的方法相对于现有的统计方法有更好的准确率和召回率。
互联网的发展为用户带来了一个包含丰富信息的巨型数据库,但是如何识别其中的有效数据是应用的关键。本文的正文抽取方法利用网页DOM树节点路径相似的特点实现正文抽取,为之后的数据分类、分析等工作奠定了基础。
4结论
本文根据新闻正文内容在网页中相对集中且同网站的新闻页面有相同模板的特点,提出基于网页DOM树节点路径相似度的正文抽取方法,先用正则表达式删除网页源代码中与正文内容无关的代码,然后将得到的网页转换为DOM树,再将目标网页的DOM树与另外两个网页的DOM树进行对比去除噪声,最后,根据节点路径相似度来抽取正文内容。该方法对来自不同网站的数据能够快速、准确地抽取正文内容,适用于结构变化不大的网页,但是对正文内容较少的网页抽取效果仍有待提高。下一步主要工作是加入内容节点与标题节点的路径之间的距离判断节点是否为正文,以提高算法的准确度。
参考文献
[1] KUSHMERICK N, WELD D S, DOORENBOS R. Wrapper induction for information extraction[C].IJCAI 1997: Proceedings of the 1997 International Joint Conference on Artificial Intelligence,1997:729-737.
[2] FU L, MENG Y, XIA Y J, et al. Web content extraction based on webpage layout analysis[C]. ITCS 2010: Proceedings of the 2010 Second International Conference on Information Technology and Computer Science, 2010: 40-43.
[3] CAI D, YU S P, WEN J R, et al. VIPS: a vision based on page segmentation algorithm[R].Microsoft Co., Tech. Report, 2003.
[4] WANG J Q, CHEN Q C, WANG X L, et al. Basic semantic units based web page content extraction[C]. SMC 2008: Proceedings of the 2008 IEEE International Conference on Systems, Man and Cybernetics, Piscataway,NJ: IEEE Press, 2008:1489-1494.
[5] UZUN E, AGUN H V, YERLIKAYA T. Web content extraction by using decision tree learning[C]. SIU 2012: Signal Processing and Communications Applications Conference, 2012: 1-4.
[6] PAN D H, QIUS G, YIN D W. Web page content extraction method based on link density and statistic[C]. WiCOM 2008: Wireless Communications, Networking and Mobile Computing, Dalian, China, IEEE Press, 2008:1-4.
[7] REIS D C, GOLGHER P B. Automatic web news extraction using tree edit distance[C]. Proc. WWW 2004: The 13th International Conference on World Wide Web, New York: ACM, 2004: 502-511.
[8] FINN A, KUSHMERICK N, SMYTH B. Fact or fiction: Con tent classification for digital libraries[C]. Proc of the 2nd DELOS Network of Excellence Workshop on Personalization and Recommender Systems in Digital Libraries. Dublin, Ireland, 2001: 1-6.
[9] PINTO D, BRANSTEIN M, COLEMAN R, et al. QuASM: A system for question answering using semistructured data[C]. Proc of the 2nd ACM/ IEEECS Joint Conference on Digital Libraries. Portland, USA, 2002: 46-55.
[10] MANTRATZIS C, ORGUN M, CASSIDY S. Separating XHTML content from navigation clutter using DOMstructure block analysis[C]. Proc of the 16th ACM Conference on Hypertext and Hypermedia, Salzburg, Austria, 2005: 145-147.
[11] DEBNATH S, MITRA P, GILES C L. Automatic extraction of informative blocks from webpages[C]. Proc of the ACM Symposium on Applied Computing, SantaFe, USA, 2005: 1722-1726.
[12] GOTTRON T. Content code blurring: A new approach to content extraction[C]. Proc of the 19th International Conference on Database and Expert Systems Applications, Turin, Italy, 2008: 29-33.
[13] 刘利, 戴齐, 尹红风,等. 基于多特征融合的网页正文信息抽取[J]. 计算机应用与软件, 2014, 31(7):47-49.
[14] 王利, 刘宗田, 王燕华,等. 基于内容相似度的网页正文提取[J]. 计算机工程, 2010, 36(6):102-104.
[15] YI L,LIU B,LI X. Eliminating noise information in web pages for data mining[C]. SIGKDD 2003: Proceedings of the 9th ACM SIGKDD International Conference on Knowledge Discovery and Data Mining, New York: ACM, 2003:296-305.
[16] GIBSON D,PUNERA K,TOMKINS A. The volume and evolution of web page templates[C]. Proc. WWW 2005: Special Interest Tracks and Posters of the 14th International Conference on World Wide Web, New York: ACM, 2005:830-839.
[17] WANG J Y, LOCHOVSKY F H. Datarich section extraction from HTML pages[C]. WISE 2002: Proceedings of the 3rd International Conference on Web Information Systems Engineering (Workshops), Los Alamitos, CA: IEEE Computer Society, 2002: 313-322.
AET会员年终大福利!
一个HTML文档都会被浏览器解析转化出一个Dom树,Dom树中的每一项根据DOM模型解析为树形结构中一个个的节点,程序员就可以通过JavaScript语言动态操作每一个节点,此节主要了解掌握一些有关节点的知识和使用方法
之前说过使用document的方法可以快速获取元素节点如下:
1 快速获取HTML中的节点(主要是元素节点)
1.1 document.getElementById()根据元素的Id获得指定对象
1.2 document.getElementsByName()根据元素的name属性获得对象
1.3 document.getElementsByTagName()根据指定的标签名称获得其元素数组
1.4 document.getElementsByClassName()获取所有指定类名的元素数组
具体的使用方式在240章节的快速获取页面元素里面已经说过不再赘述
在一个HTML网页的Dom树中会有很多的节点,当操作具体的节点时,可以通过节点属性的方式快速便捷获取指定节点进行增删改查等操作
2 通过节点的属性获取相关的节点
通过此方法获取的节点不一定是元素节点,还有其他类型的节点,比如空文本节点
2.1 获取当前节点的父节点 parentNode
每一个节点都有一个parentNode属性用于获取当前节点(多为获取当前元素节点)的父节点,如果不存在,则返回null
获取当前节点的父节点方式为:当前节点.parentNode;如果想获取其"爷爷"辈的节点只能在其后面再加个点parentNode,再往上再加以此类推
获取父节点
通过document.getElementById("UList").parentNode 的方法获取到列表的父节点为body元素节点
2.2 获取当前节点的子级节点 childNodes
获取当前节点的子级节点,childNodes只获取当前节点的第一层子节点,是一个只读的数组NodeList对象即节点对象的数组,可以通过循环遍历其子节点
获取方式 当前节点.childNodes 注意:获取的节点中会有空文本节点
获取子级节点
通过IE浏览器可以查看其空文本节点
以下几种方式获取的节点有可能是空文本节点
2.3firstChild与lastChild
获取当前节点子级节点的第一个(childNodes[0])或最后一个的节点(childNodes[length-1])
获取当前节点下的首尾节点
2.4 nextSibling
· 获取当前节点的下一个兄弟节点
2.5 previousSibling
获取当前节点的上一个兄弟节点
获取上下兄弟节点
*请认真填写需求信息,我们会在24小时内与您取得联系。