JavaScript学习笔记(十三)--DOM(上)

152 阅读10分钟

JavaScript组成

  • ECMASCRIPT (基础语法)

  • DOM(文档对象模型)

  • BOM(浏览器对象模型)

  • DOM(Document Object Model): 文档对象模型

    • 其实就是操作 html 中的标签的一些能力
    • 我们可以操作哪些内容
      • 获取一个元素
      • 移除一个元素
      • 创建一个元素
      • 向页面里面添加一个元素
      • 给元素绑定一些事件
      • 获取元素的属性
      • 给元素添加一些 css 样式
      • ...
  • DOM 的核心对象就是 docuemnt 对象

  • document 对象是浏览器内置的一个对象,里面存储着专门用来操作元素的各种方法

  • DOM: 页面中的标签,我们通过 js 获取到以后,就把这个对象叫做 DOM 对象

  • 获取一个元素

    • 通过 js 代码来获取页面中的标签
    • 获取到以后我们就可以操作这些标签了

    getElementById

    • getElementById 是通过标签的 id 名称来获取标签的
    • 因为在一个页面中 id 是唯一的,所以获取到的就是一个元素
	<body>
 		<div id="box"></div>
 		<script>
   			var box = document.getElementById('box')
   			console.log(box) // <div></div>
 		</script>
	</body>

  • 获取到的就是页面中的那个 id 为 box 的 div 标签

getElementsByClassName

  • getElementsByClassName 是用过标签的 class 名称来获取标签的
  • 因为页面中可能有多个元素的 class 名称一样,所以获取到的是一组元素
  • 哪怕你获取的 class 只有一个,那也是获取一组元素,只不过这一组中只有一个 DOM 元素而已

	<body>
 	<div class="box"></div>
 	<script>
  		 var box = document.getElementsByClassName('box')
    	console.log(box) // [<div></div>]
   		 console.log(box[0]) // <div></div>
 	</script>
	</body>


  • 获取到的是一组元素,是一个长得和数组一样的数据结构,但是不是数组,是伪数组
  • 这个一组数据也是按照索引排列的,所以我们想要准确的拿到这个 div,需要用索引来获取

getElementsByTagName

  • getElementsByTagName 是用过标签的 标签 名称来获取标签的
  • 因为页面中可能有多个元素的 标签 名称一样,所以获取到的是一组元素
  • 哪怕真的只有一个这个标签名,那么也是获取一组元素,只不过这一组中只有一个 DOM 元素而已

	<body>
  	<div></div>
  	<script>
    	var box = document.getElementsByTagName('div')
  		console.log(box) // [<div></div>]
  		console.log(box[0]) // <div></div>
  	</script>
  	</body>

  • 和 getElementsByClassName 一样,获取到的是一个长得很像数组的元素
  • 必须要用索引才能得到准确的 DOM 元素

querySelector

  • querySelector 是按照选择器的方式来获取元素
  • 也就是说,按照我们写 css 的时候的选择器来获取
  • 这个方法只能获取到一个元素,并且是页面中第一个满足条件的元素
	console.log(document.querySelector('div')) // 获取页面中的第一个 div 元素 
	console.log(docuemnt.querySelector('.box')) // 获取页面中第一个有 box 类名的元素
	console.log(document.querySelector('#box')) // 获取页面中第一个 id 名为 box 的元素


querySelectorAll

  • querySelectorAll 是按照选择器的方式来获取元素
  • 这个方法能获取到所有满足条件的元素,以一个伪数组的形式返回
	console.log(document.querySelectorAll('div')) // 获取页面中的所有的 div 元素 
  	  console.log(docuemnt.querySelectorAll('.box')) // 获取页面中所有有 box 类名的元素

  • 获取到的是一组数据,也是需要用索引来获取到准确的每一个 DOM 元素

事件

  • 什么是事件?

    • 事件指的是一种行为动作: 比如 单击,双击,手指按压,滑动,移动,悬停。。。
  • 事件三要素

    • 事件源: 页面中的一个具体的标签
    • 事件类型: 行为动作(单击,双击,悬停,滑动。。。)
    • 事件处理程序: 事件发生后的一个结果
  • 事件语法

    • 事件源.事件类型 = 事件处理程序
  • 注意: - 事件处理程序的本质就是一个函数。

  • 事件类型:

    • 单击onclick
    • 双击ondblclick
  • 事件处理程序中的this关键字

    • 函数中的this指向的是外部构造函数指向的对象。
    • 事件处理程序中的this指向的是当前事件源
  • 事件-注册addEventListener

    • 语法:可以多次注册事件,不会前后覆盖;
    
    	// 给按钮注册事件,点击的时候,输出 123
       var btn = document.querySelector("#btn");
    
       // // 给对象设置 方法名 onclick
       // btn.onclick = function() {
       //   alert(1);
       // };
    
       // // *------------------------------
       // // 给对象设置 方法名 onclick,把上面覆盖了;
       // btn.onclick = function() {
       //   alert(2);
       // }
    
       // 新的注册时间的方式:可以解决 多次注册同一个事件,覆盖问题;
       // 参数:事件类型(click)没有on,事件执行函数(匿名函数)
       btn.addEventListener("click", function() {
       	alert(1);
       });
    
       btn.addEventListener("click", function() {
          alert(2);
       });
    
    

DOM-事件-三个阶段-冒泡与捕获


  // 规则需要记住,事件执行3个阶段:捕获、到达目标、冒泡
  // 捕获:从根部节点一层一层往上找,直到找到我们刚才触发的那个节点,这个过程叫捕获;
  // 到达目标:找到我们刚才触发的那个节点
  // 冒泡:从找的目标节点,一层一层往往根节点找,这个过程叫冒泡;

	// JS代码:注册事件 写不写这个代码,事件-三个阶段存在的;
  	var btn = document.querySelector("button");
  		btn.addEventListener("click", function() {
    		alert(1);
  		});

  // 过程:点击btn后,信号从根部一层一层捕获到btn目标节点,发现btn 刚好注册了点击事件,函数被执行完,信号冒泡回到根节点;

JS事件捕获与事件冒泡原型图

DOM-事件-三个阶段-冒泡与捕获-02-多级注册


	 // 规则:事件默认是在冒泡阶段执行;在冒泡的阶段,发现父级节点们也注册和用户触发的行为一样的事件,这些事件函数被执行;
	 //  同样, 如果孙子没有点击事件,但是用户对孙子有点击行为,但是爷爷注册有点击事件,爷爷同样也会触发点击事件。
		var box_3 = document.querySelector(".box_3");
		box_3.addEventListener("click", function() {
			   alert(3);
			});

  // 父亲
	    var box_2 = document.querySelector(".box_2");
      box_2.addEventListener("click", function() {
  	       alert(2);
	 });

 // 爷爷
	   var box_1 = document.querySelector(".box_1");
     box_1.addEventListener("click", function() {
        alert(1);
	 });

 // 为什么事件默认是在冒泡阶段执行?用户体验好;让用户知道自己点的地方是正常;

 // JS:控制执行过程;(了解) 已经在捕获阶段执行;
   var box_3 = document.querySelector(".box_3");
    box_3.addEventListener("click", function() {
      alert(3);
    }, true);

  // 父亲
    var box_2 = document.querySelector(".box_2");
    box_2.addEventListener("click", function() {
     alert(2);
   }, true);

  // 爷爷
  var box_1 = document.querySelector(".box_1");
     box_1.addEventListener("click", function() {
      alert(1);
    }, true);

DOM-事件-三个阶段-冒泡与捕获-03-阻止冒泡


	// 体验:事件默认在冒泡阶段执行;
	// 解决:阻止冒泡;

	// 语法:需要在函数上设置形参e; e:事件对象;
	// e.stopPropagation();  阻止冒泡行为;
	// 使用:
	var box_3 = document.querySelector(".box_3");
	box_3.addEventListener("click", function(e) {
  	alert(3);
  	// 阻止冒泡:冒泡行为没了,默认事件在冒泡阶段执行就不会出现了;
  	// e:形参!!!!!!!!!!!!!!!!!!!!!!事件对象;
  	// stop:停止,掐断;
  	// Propagation:传播,冒泡这条线;
 	 	e.stopPropagation();
	 });

	// 父亲
	var box_2 = document.querySelector(".box_2");
	box_2.addEventListener("click", function(e) {
  	alert(2);
  	e.stopPropagation();
	});

	// 爷爷
	var box_1 = document.querySelector(".box_1");
	box_1.addEventListener("click", function() {
  	alert(1);
	});


	// 页面中以后写很多盒子,嵌套(父子),事件执行的三个阶段;
	// 事件默认在冒泡阶段执行:父亲注册了和用户行为一样的事件,用户触发子元素,父亲也会跟着被触发;


DOM-事件对象-属性

  • 语法:事件对象:有一些属性和方法,一些属性描述事件行为的位置的一个对象,这个对象就叫事件对象;
	box.onclick = function(e) {
   // 行为看成对象:对象,包含属性和方法,属性:描述鼠标点击的位置的属性;集合:事件对象;
  // console.log(e);

  // ------------------------------属性:
  // 
  // console.log("x", "y");

  // // 参照当前可视窗口左上角为基准点
  // console.log(e.clientX, e.clientY);

  // // 参照与body为左上角为基准点
  // console.log(e.pageX, e.pageY);

  // 点击是谁就是谁;
  // console.log(e.target);

  // current:当前,事件注册给谁就是谁;
  // console.log(e.currentTarget);

  // this: 事件注册给就是谁;
  // 类型:对象 == 对象
  // 值:地址
  console.log(e.currentTarget == this);  // true
}

DOM-事件对象-方法

  • 语法
    
    	// ------------------阻止默认行为---------------------
    	// 鼠标右键事件
    	document.oncontextmenu = function(e) {
      	// alert(1);
      	e.preventDefault();
    	}
    
    	// a标签:转跳;
    	var a = document.querySelector("a");
      a.onclick = function(e) {
      	e.preventDefault();
    	}
    
    

DOM-事件-委托-引入

  • 委托的优点

    1. 提高JavaScript性能。事件委托可以显著的提高事件的处理速度,减少内存的占用
    2. 动态的添加DOM元素,不需要因为元素的改动而修改事件绑定。
  • 不友好:每次新的LI时候,都要重新获取全部LI,重新给每个LI都注册一次事件;性能不好;

 	// 需求1:点击btn。给ul 新增一个li;
  	// 新属性:innerHTML
  	var ul = document.querySelector("ul");
   // 获取:内部HTML结构,字符串;
    console.log(ul.innerHTML);
   // 设置:设置内部HTML,
   ul.innerHTML += '<li class="son">-------------------</li>';

  // 分析:
  //  1. 获取btn
    var btn = document.querySelector("#btn");

    //  2.注册点击
    btn.onclick = function() {
      // 新增li
     ul.innerHTML = ul.innerHTML + '<li class="son">aa</li>';
   }


  	// 需求2:给每个li,点击之后弹窗1;
  	// 分析:
  	//  1. 获取所有的li 
  		var lis = document.querySelectorAll("li");
	 console.log(lis);

  	//  2. 循环 注册事件
  	for (var i = 0; i < lis.length; i++) {
    	lis[i].onclick = function() {
      	alert(1);
    	}
  	}


  	// 需求3:所有的li(已经存在的或新的),都有弹窗1;
  	// 分析:
  	//  1.点击后,新增li;
  	//  2.在点击后,把所有的li获取,注册事件;
  var btn = document.querySelector("#btn");
  	//  2.注册点击
  	btn.onclick = function() {
    	// 新增li
    ul.innerHTML = ul.innerHTML + '<li class="son">aa</li>';
    	// 再次获取全部的li
    var lis = document.querySelectorAll("li");
    for (var i = 0; i < lis.length; i++) {
      lis[i].onclick = function() {
        alert(1);
      }
    }
  }

  // 方式:性能不好;li被重复注册事件;


DOM-事件-委托-实现

  • e.target

  • 事件默认在冒泡执行

  • 什么是事件委托?

    • 把事件注册在父级的元素身上,
    • 利用事件冒泡执行,当事件传播到已经注册了事件的父级元素身上,
    • 判断 触发事件的DOM (e.target)节点是否是指定的元素,e.target---->DOM节点;
      • e.target.nodeName=="LI"
      • e.target.className == "son"

什么时候用?当我们需要给动态创建(不是页面一开始写死的,是后期可能会变、被新增的DOM元素)的元素实现注册事件的效果的时候;


	// 事件委托:
  	//   1.不是给子元素注册事件
  	//   2.给 父级注册事件
  	//   3. 事件默认在冒泡阶段执行,点击子元素,冒泡到父级上,父级的事件也会被触发;
 	 // 委托:父级的事件  被  后代元素(判断是否是我们需要的后代元素即可) 代理;
 	 // 分析:
  	//  1.点击后,新增li;
  	//  2.在点击后,把所有的li获取,注册事件;
  	var btn = document.querySelector("#btn");
    //  2.注册点击
     btn.onclick = function() {
    // 新增li
     ul.innerHTML = ul.innerHTML + '<li class="son">aa</li>';
   }

   // 性能:事件只是注册了一次;
   // 问题:点击ul范围内所有地方,弹窗1
   // 解决: JS知道自己点击的是LI就可以了
  var ul = document.querySelector("ul");
   // 属性:DOM.nodeName  返回大写的的标签名;
   // console.log(ul.nodeName, btn.nodeName);

  ul.onclick = function(e) {
     // alert(1);
     // e.target:点击是谁就是谁,LI   DOM节点
     // console.log(e.target.className);
     // console.log(e.target.nodeName);
     // li  赋值表达式,结果:"son"
     // 如果输出的是son证明点击的就是li标签 弹出alert(1)
     if (e.target.className == "son") {
       alert(1)
     }
   }

DOM-事件-mousedown-mousemove-mouseup

  // 鼠标在某个元素上 落下的时候,触发
  box.addEventListener("mousedown", function() {
    console.log(1);
  });

  // 鼠标在某个元素上 移动,触发
  box.addEventListener("mousemove", function() {
    console.log(2);
  });

  // 鼠标在某个元素上 弹起 触发
  box.addEventListener("mouseup", function() {
    console.log(3);
  });

开关思想-抽奖

  • 在全局设置变量,初始化没有点击
  • 点击事件内部:
    • 如果没有点击:旋转下,点击的证明;
    • 点击过了:没有任何反应;
  • 缺点:就是因为你设置的全局开关,在控制台可以获取到,可以重新设置;
	  // 抽奖:点击一次;
  	// 1.获取 DOM
  	var begin = document.querySelector(".begin");
  	var zhizhen = document.querySelector(".zhizhen");

  	// 开关思想:
  	var key = true;
  	begin.onclick = function() {
    	// 关的状态,不用继续执行代码
    	if (key == false) {
      	return;
    	}
    	var num = Math.random();
      	num = num * 360;
    	zhizhen.style.transform = "rotate(" + num + "deg)";
    	key = false;
  	}
  	// 问题:key全局变量,在console 控制台 可以获取和重新设置;
  	// 解决:下个知识;


DOM-事件-解绑-抽奖


	var btn = document.getElementById('btn');
	btn.onclick = function(){
 	 btn.onclick = null;
  	console.log('谢谢惠顾');
	}

	var btn = document.getElementById('btn');
	btn.addEventListener('click',function fn(){
  	// 解绑 当前的函数
  	btn.removeEventListener('click',fn);
  	console.log('抽奖了');
	})



抽奖:


	begin.onclick = function() {
    var num = Math.random();
    num = num * 360;
    zhizhen.style.transform = "rotate(" + num + "deg)";

    // 事件解绑,下一次再次触发的时候,就没有的JS注册事件;
    begin.onclick = null;
}

innerHTML

  • 获取元素内部的 HTML 结构

	<body>
 		<div>
 			<p>
             <span>hello</span>
 			</p>
 	   </div>
​
 	<script>
 		var div = document.querySelector('div')
 		console.log(div.innerHTML)
  	/*
   
      	<p>
        	<span>hello</span>
      	</p>
  
  	*/
 	</script>
	</body>

  • 设置元素的内容

    
    	<body>
        <div></div>
    

​ '

- 设置完以后,页面中的 div 元素里面就会嵌套一个 p 元素

**innerText**
- 获取元素内部的文本(只能获取到文本内容,获取不到 html 标签)

```javascript
  <body>
    <div>
    <p>
  	 <span>hello</span>
   </p>
   </div><script>
 		var div = document.querySelector('div')
 	    console.log(div.innerText) // hello
 </script>
 </body>


  • 可以设置元素内部的文本

	<body>
	  <div></div>
​
	<script>
  		var div = document.querySelector('div')
  		div.innerText = '<p>hello</p>'
	</script>
   </body>


  • 设置完毕以后,会把 <p>hello</p> 当作一个文本出现在 div 元素里面,而不会把 p 解析成标签

操作表单属性

  • value值

	1. 获取表单控件中的值
  	<input type = 'text' value = '123'>
  	<script type="text/javascript">
  		var input = document.querySelector('input');
      	alert(input.value);
  	</script>

  	2. 设置表单控件的值
  	<script type="text/javascript">
  		var input = document.querySelector('input');
      	input.value = '值';
  	</script>

getAttribute

  • 获取元素的某个属性(包括自定义属性)
	<body>
	<div a="100" class="box"></div>
​
	<script>
  		var div = document.querySelector('div')
  		console.log(div.getAttribute('a')) // 100
  		console.log(div.getAttribute('class')) // box
	</script>
   </body> 

setAttribute

  • 给元素设置一个属性(包括自定义属性)
    
       <body>
    	<div></div>
    	<script>
    		var div = document.querySelector('div')
    		div.setAttribute('a', 100)
    		div.setAttribute('class', 'box')
    		console.log(div) // <div a="100" class="box"></div>
    	</script>
    	</body>
    
    

removeAttribute

  • 直接移除元素的某个属性

<body>
 <div a="100" class="box"></div>
​
 <script>
   var div = document.querySelector('div')
   div.removeAttribute('class')
   console.log(div) // <div a="100"></div>
 </script>
</body>

style

  • 专门用来给元素添加 css 样式的
  • 添加的都是行内样式

	<body>
 	<div></div>
​
 	<script>
  	 var div = document.querySelector('div')
   	 div.style.width = "100px"
   	 div.style.height = "100px"
   	 div.style.backgroundColor = "pink"
   	 console.log(div) 
 	// <div style="width: 100px; height: 100px; background-color: pink;">		</div>
    </script>
    </body>


  • 页面中的 div 就会变成一个宽高都是100,背景颜色是粉色

className

  • 专门用来操作元素的 类名的
	<body>
 	<div class="box"></div>
	​
 	<script>
   		var div = document.querySelector('div')
  	 	console.log(div.className) // box
 	</script>
	</body>
  • 也可以设置元素的类名,不过是全覆盖式的操作
	<style type="text/css">	
		.one {
			width: 200px;
			height: 200px;
			background-color: red;
		}
	</style>
  <body>	
	 <input type="button" value="点我加样式">
	 <div>我是一个没有样式的div</div>
	 <script type="text/javascript">	
		//1. 获取按钮事件源
		var btn = document.querySelector('input');

		//2. 给按钮注册事件
		btn.onclick = function () {

			//3. 获取div
			var div = document.querySelector('div');

			//4. 给元素动态添加样式
			div.className = 'one';
		}
	 </script>
  </body>
  • H5中新增的方式:

    • Dom.classList.add() 添加类名
    • Dom.classList.remove() 移除类名
    • Dom.classList.toggle() 切换类名
    • Dom.classList.contains() 是否包含