js运动(三)—— 多元素的运动

1.多元素的单一属性变化

首先,从多元素的单一属性变化开始。为方便叙述,以多个 div 的 宽度变化为例子进行说明。

整个思路比较简单,就是对每个 div 都 设置定时器并监听鼠标事件,加上js运动(一)—— sidebar(分享到)中提及的缓动知识即可。

但别看简单,在实际开发中,还是会遇到不少的陷阱,各式陷阱里面也藏着很多知识点。详见下文。

1.1 var 和 let

首先,看一下下面的代码。从 Java 编程的角度来看,似乎没有什么问题。但在实际运行时会发现,不管鼠标进入到哪个 div ,都是最后一个 div 的宽度在变化。这是怎么回事呢?
在这里插入图片描述
原来,JavaScript的变量缺少块级作用域的支持。这样,上图中var oDiv = oDivs[i]; 这句话就成了罪魁祸首。

针对这个缺陷,JavaScript 增加了 let 关键字。它不是保留关键字,只有 js 1.7 及之后的版本才支持,所以再使用时需要手动加入版本号。

加入版本号的方法:当使用Spidermonkey 或 Rhino 作为单独的解析器时,可以通过命令行指定,或者调用内置函数version(),传入一个实际版本号*100的数值,如指定 js 1.7版本需传入170;当使用Firefox,可以在script标签中指定。

<script type="application/javascript; version = 1.7">

《JavaScript权威指南》 (P272)对 let 的使用方式总结为:

  1. 可以作为变量声明,和 var 一样
  2. for 或者 for/in 循环中,作为 var 的替代方案
  3. 在语句块中定义一个新变量并显示指定它的作用域
  4. 定义一个在表达式内部作用域中的变量,这个变量只在表达式中可用

所以如果要像Java一样用,就得把 var oDiv = oDivs[i]; 改成 let oDiv = oDivs[i]; 。或者,我们直接用 oDivs[i] ,不再过渡一下,如下图所示(注意,moveTowards中第一个参数简化成了this)。
在这里插入图片描述

1.2 定时器分配

为了每个元素的运动不被其他元素干扰,需要每个元素都分配一个定时器。

在自己做的时候,我把定时器放在了缓动函数 moveTowards里面(js运动(一)—— sidebar(分享到)中缓动函数名为moveTo,因为js其实自带了一个同名的函数,所以为了不混淆,这里改成了moveTowards) 。如下图。
在这里插入图片描述
这种做法引入一个问题:当频繁的进行鼠标移入移出时,可能存在多个定时器作用于同一个元素的情况,会导致整个元素不断来回的抖动。正确的做法为,为每个元素定义一个定时器属性,在关闭定时器或者开启定时器的时候,对应该元素进行操作即可。如下图所示。
在这里插入图片描述
所以,关闭定时器就变成了clearInterval(obj.timer);

1.3 缓动速度分配

js运动(一)—— sidebar(分享到)的缓动函数中,speed 定义和赋值如下图。
在这里插入图片描述
这么定义适用于单个元素,但放到多个元素的场景时,就会出现下图中的情况。
在这里插入图片描述
其实很简单,定时器都分配了,速度当然也得各自分配。所以speed的赋值和处理应该放在定时器里。

综上,多元素单属性变化的小程序如下。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Multi Elt Sp</title>
<style>
div {width:100px; height:50px; background-color:red; margin:10px;}
</style>

<script>


window.onload =  function(){
	var oDivs = document.getElementsByTagName("div");
	for(var i=0; i < oDivs.length; i++){
		oDivs[i].timer = null;
		oDivs[i].onmouseover = function()
		{
			moveTowards(this, 400);
		}
		
		oDivs[i].onmouseout = function()
		{
			moveTowards(this, 100);
		}
	}
}


function moveTowards(obj,  iTarget){
	var speed;
	clearInterval(obj.timer);
	obj.timer = setInterval(function(){
		speed = (iTarget-obj.offsetWidth)/6;
        speed=speed>0?Math.ceil(speed):Math.floor(speed);
		
		if(obj.offsetWidth == iTarget)
			clearInterval(obj.timer);
		else
			obj.style.width = obj.offsetWidth + speed + "px";	
	},30);	
}




</script>

</head>

<body>
<div id="div1"></div>
<div id="div2"></div>
<div id="div3"></div>
</body>
</html>

单属性的变化依据上述小程序修改即可,只有一点需要特别注意,即透明度的变化。需要修改缓动函数。
根据《JavaScript权威指南》 (P423),透明度用opacity属性来处理,属性值为0~1之间的数字,1代表100%不透明(默认值),而0代表0%不透明(或100%透明)。opacity属性在当今所有浏览器中都支持,除了IE。IE提供类似的可选方法:IE特有的filter属性。让元素75%不透明,可以使用以下CSS样式:

opacity:.75
filter:alpha(opacity=75)

为了兼容性,不管在定义样式的时候还是在进行运动操作的时候,都需要两个一起写。赋值时如下。

obj.style.filter = 一个整数  //IE
obj.style.opacity = 一个小数 //others

2. offset* 的陷阱、“层叠”及计算样式的获取

2.1 offset* 量中的陷阱

js运动(一)—— sidebar(分享到)js运动(二)—— 右侧悬浮框及前文中都用到了很多 offset* 量,如offsetHeigtoffsetWidth等。但从js运动(二)—— 右侧悬浮框中知道,offset* 量是要算上元素的边框大小的(原教程中有一个错误,offsetWidth 里面不包含padding),这使得有时候运动会适得其反。

例如,一个width:200pxborder:1px solid black的元素,想让其宽度每次以1px逐一减小,则在定时器里面,

obj.style.width = obj.offsetWidth - 1 + "px";

这看似没什么问题,但在实际运行中会发现,元素的宽度不但没有逐一减少,还逐一增大了,这是怎么回事呢?看看下面的计算。

obj.offsetWidth =  obj.style.width + obj.style.border *2  = 202 px;

所以 obj.style.width = 202 -1 = 201 px; 所以 obj.offsetWidth = obj.style.width + obj.style.border *2 = 203 px; 以此类推。

所以可以看到元素的宽度在增大,与想要的结果背道而驰。

这就要说到层叠效果和计算样式了,见下文。

2.2 层叠

根据《JavaScript权威指南》(P412),

CSS中的C即为层叠的意思,该术语指示了应用于文档中任何给定元素的样式规则是各个来源的层叠效果,这些来源为:

  1. Web浏览器的默认样式表
  2. 文档的样式表
  3. 每个独立的HTML元素的style属性

style属性中的样式覆盖了样式表中的样式,文档的样式表中的样式覆盖了浏览器的默认样式,任意给定元素的视觉表现可能是来自3个来源的一个样式组合。一个元素甚至可能匹配样式表中的多个选择器,在这种情况下,所有这些选择器的关联样式属性都将应用到该元素上。

为显示文档元素,Web浏览器必须组合元素的style属性,包括来自文档样式表中所有匹配的选择器的样式值。计算的结果是一组实际用于显示元素的样式属性和值,这组值就是元素的计算样式。

2.3 计算样式

根据《JavaScript权威指南》(P432),

用浏览器窗口的getComputedStyle()方法获取一个元素的计算样式,此方法的第一个参数为需要获取样式的元素,第二个参数通常为null或者空字符串,但也是必须的。其用法如下。

window.getComputedStyle(div, null).fontsize

该方法的返回值是一个CSSStyleDeclaration 对象,这个对象的特征为:

  1. 计算样式的属性是只读
  2. 计算样式的值是绝对值,类似百分比和点之类相对的单位将全部转换为绝对值。有指定尺寸的值将会是以 px 为后缀的字符串。颜色将会以“rgb(#,#,#)”或“rgba(#,#,#,#)”的格式返回
  3. 不计算符合样式,如可以查询marginLeft 但不能查询 margin
  4. 计算样式的cssText属性未定义

这个方法在IE中存在兼容性问题,对于IE,使用元素的currentStyle属性。它不是真正的计算样式,因为没有将相对值转换成绝对值,会返回带相对性单位(如%或em)的尺寸或者非精确的颜色值(如red)。

为了兼容IE和其他浏览器,可以定义一个函数获取元素的计算样式。如下。

function getStyle(obj, name) {
	if(obj.currentStyle){
		return obj.currentStyle[name];
	}else{
		return getComputedStyle(obj, false)[name];
	}
}

注意,所得结果中带单位,使用时一定要用parseInt将结果转换成数字。

3. 多元素的运动

现实开发中,一个页面里可能有多个元素在动,而且光怪陆离。这就是接下来要是的多元素的多属性变化。

既然是多元素、多属性,所以元素和属性都要作为参数传入缓动函数。另外,需要注意的是,透明度的缓动和尺寸的缓动还是有所区别的,所以应该区分处理。

function moveTowards(obj, attr, iTarget) {
	clearInterval(obj.timer);
	obj.timer = setInterval(function (){
		var cur = 0;
		if(attr == 'opacity'){
			cur = Math.round(parseFloat(getStyle(obj, attr)) * 100);
		}else{
			cur = parseInt(getStyle(obj, attr));
		}
		
		var speed = (iTarget-cur) / 6;
		speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
		
		if(cur == iTarget){
			clearInterval(obj.timer);
		}else{
			if(attr == 'opacity') {
				obj.style.filter = 'alpha(opacity:' + (cur + speed) + ')';
				obj.style.opacity = (cur + speed) / 100;
			}else{
				obj.style[attr] = cur + speed + 'px';
			}
		}
	}, 30);
}

需要注意的地方:

  1. parseFloat在计算并储存时,可能出现小数点后多位的情况,需要用Math.round取整;
  2. getStyle所得结果中带单位,使用时一定要用parseInt将结果转换成数字;
  3. IE与其他浏览器对透明度的定义不一致,需要考虑兼容性。

4.总结

以上即为今天的内容。初初入门,小小程序还是会遇到不少问题,不得不感叹,小程序也可能蕴藏大知识,手跟上脑,才能学会。

参考文献

【1】《JavaScript权威指南》
【2】智能社:JavaScript教程——从入门到精通 ( https://ke.qq.com/course/152997?taid=766917950461349
【3】js运动(一)—— sidebar(分享到)( https://blog.csdn.net/qq_31305965/article/details/89676391
【4】js运动(二)—— 右侧悬浮框( https://blog.csdn.net/qq_31305965/article/details/89886644 )

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: HTML DOM(文档对象模型)是一种编程接口,允许程序访问和操作HTML文档中的元素。使用JavaScript来操作HTML DOM可以让网页具有交互性和动态性。 在JavaScript中,可以使用getElementById,getElementsByTagName和getElementsByClassName等方法来获取页面上的元素。例如,通过getElementById("myId")可以获取id为"myId"的元素。 操作元素的属性和内容也很简单,可以使用如下方法: - 修改元素的属性:element.attribute = new value - 访问元素的属性:element.attribute - 修改元素的内容:element.innerHTML = new content - 访问元素的内容:element.innerHTML 通过操作HTML DOM,可以实现各种交互效果和动态效果。例如,可以在点击按钮时显示或隐藏元素,或在输入框中输入文本时实时更新页面内容等。 ### 回答2: HTML DOM是JavaScript操作网页中元素和属性的主要方式之一。在HTML DOM中,文档元素是指HTML页面中所有元素的根节点。文档元素的操作包括获取元素、创建元素、删除元素、修改元素属性等。 获取元素是HTML DOM操作的重要一环。在JavaScript中,可以使用getElementById()、getElementsByTagName()、getElementsByClassName()等方法获得HTML页面中的元素。其中getElementById()方法可以通过元素的ID获取该元素的引用,常用于根据ID修改元素的属性或内容。而getElementsByTagName()方法可以通过标签名获取HTML页面中所有该标签的元素,例如获取所有的h1元素,常用于对整个页面的控制。getElementsByClassName()方法可以通过class属性获取HTML页面中所有具有该class的元素。 创建元素是向HTML页面中添加元素的重要方式。可以使用createElement()方法创建一个新元素,然后使用appendChild()方法将其添加到现有元素中。例如,创建一个新的p元素,并将其添加到body元素中可以使用以下代码: var newPara = document.createElement("p"); var textNode = document.createTextNode("This is a new paragraph."); newPara.appendChild(textNode); document.body.appendChild(newPara); 删除元素也是HTML DOM中的一个常用操作。可以使用removeChild()方法删除现有元素,例如: var parent = document.getElementById("parent-id"); var child = document.getElementById("child-id"); parent.removeChild(child); 修改元素属性是HTML DOM中的另一个核心操作。可以使用setAttribute()方法修改元素的属性值,例如: var element = document.getElementById("my-id"); element.setAttribute("class", "new-class"); 可以通过HTML DOM操作文档元素,实现对页面元素和属性的各种操作。掌握HTML DOM的知识,可以更好地控制和管理HTML页面。 ### 回答3: HTML DOM是JavaScript操作网页元素的主要方式之一,它的核心在于文档对象模型(DOM,Document Object Model),需要熟悉DOM才能灵活操作网页元素。文档元素是HTML页面中的根元素,它是其他所有元素的祖先元素,也是HTML DOM操作的基础。 一、获取元素 在JavaScript中,我们可以通过各种方式来获取文档元素,例如getElementById()、getElementsByTagName()、getElementsByClassName()等。其中,通过id、标签名和类名获取元素是最常用的种方法。 1. getElementById() 此方法用于获取具有指定id属性的元素,其语法为document.getElementById(id)。id属性在整个HTML文档中应该是唯一的。 示例:获取id为“demo”的元素 ``` var demo = document.getElementById("demo”); ``` 2. getElementsByTagName() 此方法用于获取具有指定标签名的元素,其语法为document.getElementsByTagName(tagname)。 示例:获取所有p元素 ``` var pList = document.getElementsByTagName("p"); ``` 3. getElementsByClassName() 此方法用于获取具有指定类名的元素,其语法为document.getElementsByClassName(classname)。 示例:获取类名为“red”的元素 ``` var redList = document.getElementsByClassName("red"); ``` 二、改变元素 有很多种方法可以改变文档元素的属性和样式,下面列举一些常见的方法: 1. 修改元素的文本内容 我们可以通过innerHTML属性和innerText属性来修改元素的文本内容。 innerHTML属性用于改变元素的HTML内容,其语法为element.innerHTML=new html content; innerText属性用于改变元素的纯文本内容,其语法为element.innerText=new text content; 2. 修改元素的样式 我们可以通过style属性来修改元素的样式,其中style属性包含了元素的所有样式属性。 示例:将id为“demo”的元素背景色改为红色 ``` document.getElementById("demo").style.backgroundColor = "red"; ``` 3. 创建新元素 我们可以使用document.createElement()方法来创建新元素,并通过appendChild()方法将其加入到指定的元素内。 示例:创建一个新的p元素 ``` var newP = document.createElement("p"); ``` 4. 删除元素 我们可以使用parentNode.removeChild()方法来删除指定的元素。 示例:删除id为“demo”的元素 ``` var demo = document.getElementById("demo"); demo.parentNode.removeChild(demo); ``` 、总结 通过这篇文章的学习,我们了解了HTML DOM的基础知识和基本操作方法,例如获取元素、改变元素的属性和样式以及创建和删除元素等。掌握这些操作方法可以帮助我们更好地操作网页元素,从而实现丰富多彩的网页效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值