JavaScript实现点击随机变色
kart
8年前
<h2>事起缘由</h2> <p>6月21日晚,古哥给咱们科普页面渲染机制、JS的一些性能优化和MV*架构,收益匪浅。在培训现场,古哥请船长当场用原生JS实现点击盒子随机变 色。船长的编程思路让人脑洞有点大开,Get到不少干货。借着这个例子,古哥不断灌输各种性能优化、浏览器兼容写法、代码整洁等概念。想不到一个如此简单 的例子竟有这么大的学问。于是培训归来当晚,当即打开电脑,把这个例子自己实现了一遍。于是便有了此文。</p> <p>下面我来讲讲我是怎么做的。</p> <h2>一. 首先搭好HTML结构和CSS样式</h2> <pre> <code class="language-html"><!DOCTYPE html> <html> <head> <title>changeColor</title> <style> .colorBox { width: 200px; height: 200px; margin: 20px; background-color : rgb(100, 200, 100); cursor: pointer; } </style> </head> <body> <div id="colorBox" class="colorBox"></div> </body> </html> </code></pre> <p>接下来肯定是要用JS控制DOM元素啦。</p> <h2>二. 创造随机颜色生成器</h2> <p>首先,需要明确使用何种色彩模式。在HTML标准中,色彩模式有十六进制、RGB、HSL、RGBA、HSLA等。其中,HSL和HSLA不能兼容IE6-8。</p> <h3>1. 创建随机数</h3> <p>因为颜色是随机生成的,因此需要用到Math.random()函数。由于Math.random()生成0~1之间的随机数(包括0不包括1), 有时并不能满足我们的需求,因此可能需要乘除一个数。如果希望得到整数,需要对随机数进行取整,可用Math.ceil()【向上取整】或 Math.floor()【向下取整】或Math.round()【四舍五入】,或者paseInt()。</p> <p>举个实用的栗子,如何获得两个数之间的随机数:</p> <pre> <code class="language-javascript">function getRandom(min, max) { return Math.round(Math.random()*(max - min) + min); } </code></pre> <p>Math.random()<em>(max – min) + min应该还是比较好理解的,用底数(min)加上随机偏移量(Math.random()</em>(max – min))就是随机数的大小。</p> <p>为了使随机数看起来更随机一些,建议使用Math.round()。因为譬如使用Math.ceil()取整的话,min能被娶到的几率非常非常 小,只有Math.random()=0时才能取到。同理Math.floor()和paseInt()将永远也取不到max值。</p> <p>当然,JavaScript中的随机数Math.random()并非真正意义上的随机,只能算“伪随机数”,这与它的产生算法有关,这里不展开讨论。不过Math.random()应付一般的不那么精准的项目中已经够用了。</p> <h3>2. 创建随机颜色</h3> <p>其实使用哪一种色彩模式的实现方式差不多,只是字符串的拼接结果不一样而已。</p> <p><strong>(1) 使用十六进制色彩模式</strong></p> <p>直接上代码:</p> <pre> <code class="language-javascript">function randomColor() { // 这里使用十六进制颜色值 var colorArr = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"]; var colorVal = "#"; for(i = 0; i < 6; i++) { colorVal += colorArr[Math.round(15*Math.random())]; } return colorVal; } </code></pre> <p><strong>(2) 使用RGB色彩模式</strong></p> <p>直接上代码:</p> <pre> <code class="language-javascript">function randomColor() { // 这里使用rgb颜色值 var colorVal = "rgb("; for(i = 0; i < 3; i++) { colorVal += Math.round(250*Math.random()); if (i < 2) { colorVal += ","; } } colorVal += ")"; return colorVal; } </code></pre> <p>当然,你还可以使用rgba定义它的透明度。</p> <p><strong>(3) 使用HSL色彩模式</strong></p> <p>直接上代码:</p> <pre> <code class="language-javascript">function randomColor() { // 这里使用HSL颜色值,但HSL不兼容IE6~8 // HSL(H, S, L),H的取值为0-360,S为0-100%,L为0-100% // 在本例子中先设定好饱和度(S)和亮度(L),随机变化色相(H) var colorVal, S = "50%", L = "50%"; colorVal = "HSL(" + Math.round(360*Math.random()) + "," + S + "," + L + ")"; return colorVal; } </code></pre> <h3>3. 绑定样式修改</h3> <p>这里需要修改盒子的background-color。我们可以直接操作<code><style></code>标签中的元素,不过最简单的当然是直接在html中标签的style属性啦。</p> <pre> <code class="language-javascript">this.style.backgroundColor = randomColor(); </code></pre> <h3>4. 将使盒子变色的函数封装</h3> <pre> <code class="language-javascript">function changeColor() { // 创造随机颜色 function randomColor() { // ... } this.style.backgroundColor = randomColor(); } </code></pre> <h2>三. 给盒子绑定changeColor()事件</h2> <h3>1. 获取DOM元素</h3> <p>获取DOM元素的几种方法可以用document.getElementById()、document.getElementByName()、 document.getElementsByTagName()。当然还有高性能的document.querySelector(),并能兼容IE8 及其以上浏览器。</p> <p>在这里,我使用原始的document.getElementById()。在一个页面中经常需要获取元素id的方法,所以可以考虑将其封装成函数。见下:</p> <pre> <code class="language-javascript">// 获取元素的id值 function getId(id) { return document.getElementById(id); } </code></pre> <h3>2. 对事件进行绑定</h3> <p><strong>(1) 使用基于DOM对象的属性方式</strong></p> <p>最让人容易想到的时间绑定方式就是ele.onclick = function(){},并且这种基于DOM对象的属性方式能轻易地兼容IE6及其以上浏览器,非常好用。</p> <pre> <code class="language-javascript">colorBox.onclick = function() { changeColor(); } </code></pre> <p><strong>(2) 使用基于DOM对象的方法方式</strong><br> 绑定事件,addEventListener不能兼容IE6-8,因此IE6-8需要使用attachEvent,除了函数名、参数的不同,还有个关于 this指针的差异。在其它高版本浏览器中,绑定的事件处理函数被调用时,this指向事件绑定的object。而IE中,this指向window对 象,通过使用call或apply可以改变this指针的指向。</p> <p>addEventListener语法:target.addEventListener(event, callback, useCapture);</p> <p>attachEvent语法:target.attachEvent(event, fn);</p> <p>另外,再来掰一掰addEventListener的事件流:</p> <p>当一个事件发生时,分为三个阶段:</p> <p>a.捕获阶段: 从根节点开始顺序而下,检测每个节点是否注册了事件处理程序。如果注册了事件处理程序,并且 useCapture 为 true,则调用该事件处理程序;(IE 中无此阶段);</p> <p>b.目标阶段: 触发在目标对象本身注册的事件处理程序,也称正常事件派发阶段;</p> <p>c.冒泡阶段: 从目标节点到根节点,检测每个节点是否注册了事件处理程序,如果注册了事件处理程序,并且 useCapture 为false,则调用该事件处理程序;</p> <p>一般地,先判断使用addEventListener,后判断attachEvent。这是因为addEventListener能兼容IE9以上 和其他绝大多数浏览器,而attachEvent只适用于IE,一般用之兼容IE6~8,而IE6~8的用户较少,因此,应优先考虑先判断 addEventListener以减短JS的渲染路径,以达到绝大多数用户的更好体验。</p> <pre> <code class="language-javascript">// 事件绑定器 // 参数:target为DOM对象,event为事件名称(不带"on"),callback为接收事件处理的函数 function bindEvent(target, event, callback) { if(window.addEventListener) { return target.addEventListener(event, callback, false); }else if(target.attachEvent) { return target.attachEvent("on"+event, function() { callback.apply(target); }); } } </code></pre> <h2>四. 调用</h2> <p>在这之前,我已将使盒子变色的方法用changeColor()封装,并将构建了一个事件绑定器bindEvent()。</p> <p>上面折腾了辣么多都还木有见到效果,别急~</p> <p>最后当然是优雅的一键调用啦。哈哈哈哈~o(^▽^)o~</p> <pre> <code class="language-javascript"> var colorBox = getId("colorBox"); bindEvent(colorBox, "click", changeColor, false); </code></pre> <p>如果页面中还有其他盒子需要变色,只需要优雅地调用一下bindEvent()即可完成扩展。</p> <h2>五. 性能优化</h2> <p>1 . 可以使用自执行函数将代码块进行封装并创建伪命名空间,只要把自己所有的函数、对象和变量都写在这个闭包函数内,那么外部就不能访问,除非你允许。</p> <p>2 . 在事件绑定中,古哥建议将事件绑定器用变量存储,那样每次调用事件绑定器时就不再需要重复判断。古哥写的事件绑定器代码如下:</p> <pre> <code class="language-javascript">var w3c = document.dispatchEvent; var bind = w3c?function(target,type,handler,phrase){ target.addEventListener(type,handler,!!phrase); } : function(target,type,handler){ target.attachEvent && target.attachEvent("on"+type,function(){ handler.apply(target); }) } bind(box,"click",handleClick); </code></pre> <p> </p> <p>来自:http://www.dengzhr.com/js/424</p> <p> </p>