15 个 jQuery 小技巧

通过使用jQuery中的animate 和scrollTop 方法,不用插件就可以创建一个滚动到顶部的简单动画: // Back to top $(‘.top’).click(function (e) { e.preventDefault(); $(‘html, body’).animate({scrollTop: 0}, 800); }); Back to top 改变scrollTop 的值可以更改你想要放置滚动条的位置。所有你真正需要做的是在800毫秒的时间内设置文档主体的动画,直到它滚动到文档的顶部。 注:小心scrollTop的一些错误行为。 预加载图像 如果你的网页要使用大量开始不可见的(例如,悬停的)图像,那么可以预加载这些图像: $.preloadImages = function () { for (var i = 0; i < arguments.length; i++) { $('‘).attr(‘src’, arguments[i]); } }; $.preloadImages(‘img/hover-on.png’, ‘img/hover-off.png’); 检查图像是否加载 有时为了继续脚本,你可能需要检查图像是否全部加载完毕: $(‘img’).load(function () { console.log(‘image load successful’); }); 你也可以用ID或类替换标签来检查某个特定的图像是否被加载。 […]

高性能JavaScript DOM编程以及重排与重绘

我们知道,DOM是用于操作XML 和HTML文档的应用程序接口,用脚本进行DOM操作的代价很昂贵。有个贴切的比喻,把DOM和JavaScript(这里指ECMScript)各自想 象为一个岛屿,它们之间用收费桥梁连接,ECMAScript每次访问DOM,都要途径这座桥,并交纳“过桥费”,访问DOM的次数越多,费用也就越高。 因此,推荐的做法是尽量减少过桥的次数,努力待在ECMAScript岛上。我们不可能不用DOM的接口,那么,怎样才能提高程序的效率? 1、DOM访问与修改 访问DOM元素是有代价的(“过桥费”你懂的),修改元素代价更是昂贵,因为它会导致浏览器重新计算页面的几何变化(重排和重绘)。 当然最坏的情况是在循环中访问或者修改元素,看下面两段代码: var times = 15000; // code1 console.time(1); for(var i = 0; i < times; i++) { document.getElementById(‘myDiv1’).innerHTML += ‘a’; } console.timeEnd(1); // code2 console.time(2); var str = ”; for(var i = 0; i < times; i++) { str += ‘a’; } document.getElementById(‘myDiv2’).innerHTML = str; console.timeEnd(2); 结果第一次运行的时间居然是第二次的千倍!(chrome 版本 44.0.2403.130 […]

js浮点数精度问题

大多数语言在处理浮点数的时候都会遇到精度问题,但是在JS里似乎特别严重,来看一个例子 alert(45.6*13); 结果居然是592.800000000001,当然加法之类的也会有这个问题 那这是js的错误吗? 当然不是,你的电脑做着正确的二进制浮点运算,但问题是你输入的是十进制的数,电脑以二进制运算,这两者并不是总是转化那么好的,有时候会得到正确的结果,但有时候就不那么幸运了 alert(0.7+0.1);//输出0.7999999999999999 alert(0.6+0.2);//输出0.8 你输入两个十进制数,转化为二进制运算过后再转化回来,在转化过程中自然会有损失了 但一般的损失往往在乘除运算中比较多,而JS在简单的加减法里也会出现这类问题,你也看到了,这个误差也是非常小的,但是却是不该出现的 那该怎么解决呢,ECMA4似乎给了解决方法,但是现在倒不是那么实用的 一种方法,比如0.7+0.1,先把0.1和0.7都乘10,加完之后再除10 另外可以自己写点函数来解决这个问题,自己百度谷歌一下应该有很多,但是最好还是不要用JS做一些复杂的浮点运算,毕竟JS更多的作用不在于此 <script type=“text/javascript”> // 两个浮点数求和 function accAdd(num1,num2){ var r1,r2,m; try{ r1 = num1.toString().split(‘.’)[1].length; }catch(e){ r1 = 0; } try{ r2=num2.toString().split(“.”)[1].length; }catch(e){ r2=0; } m=Math.pow(10,Math.max(r1,r2)); // return (num1*m+num2*m)/m; return Math.round(num1*m+num2*m)/m; } // 两个浮点数相减 function accSub(num1,num2){ var r1,r2,m; try{ r1 = num1.toString().split(‘.’)[1].length; }catch(e){ r1 = 0; } try{ r2=num2.toString().split(“.”)[1].length; }catch(e){ r2=0; } m=Math.pow(10,Math.max(r1,r2)); n=(r1>=r2)?r1:r2; return (Math.round(num1*m–num2*m)/m).toFixed(n); } // 两数相除 function accDiv(num1,num2){ […]

父子页面之间跨域通信的方法

由于同源策略的限制,JavaScript跨域的问题,一直是一个比较棘手的问题,为了解决页面之间的跨域通信,大家煞费苦心,研究了各种跨域方案。之前也有小网同学分享过一篇“跨域,不再纠结” 开始照着尝试时还是有些不够明白的地方,深入了解之后,这里给大家补充一点更具体的做法。 先来看看哪些情况下才存在跨域的问题: 其中编号6、7两种情况同属于主域名相同的情况,可以设置domain来解决问题,今天就不讨论这种情况了。 对于其他跨域通信的问题,我想又可以分成两类: 其一(第一种情况)是a.com下面的a.js试图请求b.com下某个接口时产生的跨域问题。 其二(第二种情况)是当a.com与b.com下面的页面成父子页面关系时试图互相通信时产生的跨域问题,典型的应用场景如a.com/a.html使用iframe内嵌了b.com/b.html,大家都知道a.html内的js脚本试图访问b.html时是会被拒绝的,反之亦然。 第一种情况,目前主流的方案是JSONP,高版本浏览器支持html5的话,还可以使用XHR2支持跨域通信的新特性。 第二种情况,目前主要是通过代理页面或者使用postMessageAPI来做,这也是今天要讨论的话题。 第二种情况,有这样一些类似的案例:a.com/a.html使用iframe内嵌了b.com/b.html,现在希望iframe的高度能自动适应b.html的高度,使iframe不要出现滚动条。我们都知道跨域了,a.html是没办法直接读取到b.html的高度的,b.html也没办法把自己的高度告诉a.html。 直接说可以用代理页面的方法搞定这个问题吧,但是怎么代理法,先来看下面这张图: b.html与a.html是不能直接通信的。我们可以在b.html下面再iframe内嵌一个proxy.html页面,因为这个页面是放在a.com下面的,与a.html同域,所以它其实是可以和a.html直接通信的,假如a.html里面有定义一个方法_callback,在proxy.html可以直接top._callback()调用它。但是b.html本身和proxy.html也是不能直接通信的,所谓代理页面的桥梁作用怎么实现呢? b.html内嵌proxy.html是通过一段类似下面这样的代码: 这个iframe的src属性b.html是有权限控制的。如果它把src设置成a.com/proxy.html?args=XXX,也就是给url加一个查询字符串,proxy.html内的js是可以读取到的。对的,这个url的查询字符串就是b.html和proxy.html之间通信的桥梁,美中不足的是每次通信都要重写一次url造成一次网络请求,这有时会对服务器及页面的运行效率产生很大的影响。同时由于参数是通过url来传递的,会有长度和数据类型的限制,搜集的资料显示: IE浏览器对URL的长度现限制为2048字节。 360极速浏览器对URL的长度限制为2118字节。 Firefox(Browser)对URL的长度限制为65536字节。 Safari(Browser)对URL的长度限制为80000字节。 Opera(Browser)对URL的长度限制为190000字节。 Google(chrome)对URL的长度限制为8182字节。 上面的方法,通过迂回战术实现了b.html跟a.html通信,但是倒过来,a.html怎么跟b.html通信呢?嵌入在b.html里面的proxy.html可以用top快速的联系上a.html,但是要想让a.html找到proxy.html就不容易了,夹在中间的 b.html生生把它们分开了,a.html没法让b.html去找到proxy.html然后返回给它。只能采用更迂回的战术了。 顺着前面b.html到a.html的通信过程,逆向的想一下,虽然a.html没有办法主动找到proxy.html,但是proxy.html可以反过来告诉a.html它在哪里: 在proxy.html加这么一段脚本: 也就是必须由proxy.html先主动发送一个消息给a.html,a.html得到proxy.html页面window的引用,就可以反过来向它发送请求了。 现在a.html可以把消息发给proxy.html了,但是proxy.html怎么把消息转送到b.html?似乎这才是难点,因为它们之间才真正有着“跨域”这一道鸿沟。 这回我们不再用前面那个iframe内嵌代理页面的方法再在proxy.html内嵌一个b.com下面的代理页面了,这样实在会给人感觉嵌的太深了,四层。但是为了跨越这道鸿沟,b.com下面也加一个代理页面是免不的。不过现在我们要利用一下window.name。window.name有一个特性,就是页面在同一个浏览器窗口(标签页)中跳转时,它一直存在而且值不会改变。比如我们在a.html中设置了window.name=”a”,然后location.href=”http://b.com/b.html”跳转后,b.html可以读取window.name的值为”a”;而且window.name的值长度一般可以到达2M,ie和firefox甚至可以达到32M,这样的存储容量,足够利用起来做跨域的数据传递了。好吧,我们现在要做的就是当proxy.html拿到a.html发送过来的数据后把这个数据写入window.name中,然后跳转到b.com下面的代理页面,我们这里假设是bproxy.html。bproxy.html读取到window.name值后,通知给它父页面b.html就简单了。我们再来看这个过程可以用图大概示意一下: 图例中绿色的双向箭头表示可以通信,橙色的双向箭头表示不能直接通信。 最后我们简单看一下双向通信的实测效果: b.html每次加载的时候都先给a.html发一个”连接请求”,让a.html可以找到proxy.html。所以页面第一次加载的时候会产生三个请求: 每次b.html向a.html发送消息的时候会产生一个请求: 每次a.html向b.html发送消息的时候会产生两个请求,其中一个是a.com/proxy.html向b.com/bproxy.html跳转产生的,另一个是b.html重新向a.html发起“连接请求”时产生的: 最后简单看一下实测的几个测试页面代码: 代码片段一,a.com/a.html: 代码片段二,a.com/proxy.html: 代码片段三,b.com/b.html: 代码片段四,b.com/bproxy.html: 好吧,现在我必须把话锋调转一下了。前面讲的这么多,也只是抛出来一些之前我们可能会采用的跨域通信方法,事实上代理页面、url传参数和window.name、甚至还有一些利用url的hash值的跨域传值方法,都能百度到不少相关资料。但它们都逃不开代理页面,也就不可避免的要产生网络请求,而事实上这并不是我们的本意,我们原本希望它们能够直接在客户端通信,避免不必要的网络请求开销——这些开销,在访问量超大的站点可能会对服务器产生相当大的压力。那么,有没有更完美一点的替代方案呢? 必须给大家推荐postMessage。postMessage 正是为了满足一些合理的、不同站点之间的内容能在浏览器端进行交互的需求而设计的。利用postMessage API实现跨域通信非常简单,我们直接看一下实例的代码: 代码片段五,A.com/a.html: 代码片段六,b.com/b.html: 代码的关键是message事件是一个拥有data(数据)和origin(来源)属性的DOM事件。data属性是发送的实际数据,origin属性是发送来源。Origin属性很关键,有了这个属性,接收方可以轻易的忽略掉来自不可信源的消息,也就能有效避免跨域通信这个开口给我们的源安全带来的隐患。接口很强大,所以代码很简单。我们可以抓包看一下,这个通信过程完全是在浏览器端的,没有产生任何的网络请求。同时这个接口目前已经得到了绝大多数浏览器的支持,包括IE8及以上版本,参见下面的图表: 但是为了覆盖ie6等低版本浏览器,我们完整的方案里面还是要包含一下兼容代码,就是最开始介绍的代理页面的方法了,但必须是以postMessage为主,这样即便最后会有某些浏览器因为这种通信产生一些网络请求,比例也是非常低的了。 作者: lyndon 来源: 腾讯大讲堂 发布时间: 2014-08-08 08:59 原文链接

谈谈Javascript中的void操作符

由于JS表达式偏啰嗦,于是最近便开始采用Coffeescript来减轻负担。举个栗子,当我想取屋子里的第一条dog时,首先要判断house对象是否存在,然后再判断house.dogs是否存在,最后取house.dogs[0]。在JS需要这么写 1 2 var dog = (typeof house !== ‘undefined &amp;&amp; house !== null) &amp;&amp; house.dogs &amp;&amp; house.dogs[0] 在Coffee中,我只需要这么写: 1 dog = house?.dogs?[0]; 写到这里,读者会问,这跟标题《Javascript中的void》有一毛钱关系?Coffee的本质就是JS,之所以Coffee能工作的很好,是因为它生成出了高效而且健壮的JS代码,我们可以看看它的生成结果。 1 2 3 4 5 var dog, _ref; dog = typeof house !== “undefined” &amp;&amp; house !== null ? (_ref = house.dogs) != null ? _ref[0] : void 0 : void […]

js中function参数默认值

–在dreamweaver做网站时,函数定义是在一个*.js文件中,其中定义了一个func,有四个参数,function func(string1,url,flag,icon),然后在另一个asp中调用它func(a,b),那flag和icon的值是什么,怎么定义默认值?谢谢! –默认值应该是 undefined 在函数内预设数可以用 arguments[i] i就是你参数的位置 第一个为0 所以要设定 flag 的默认值 则可以这么写 function func(string1,url,flag,icon){ if(!arguments[2]) flag = “123”; if(!arguments[3]) icon = “456”; } 你试试 应该是这样 ——————————————————————————– 今天遇到一个问题,需要调用一个JS函数,想在函数中给它一个默认参数,以为跟其他语言一样。 <script> function test(id=0){ alert(id); } </script> <input type=”button” value=”test” onclick=”test()”> 运行结果报错,JS中不能这样传默认参数,上网查了一下,可以借助于arguments 实参数组,参考下例:   <script> function test(a){ var b=arguments[1]?arguments[1]:50 return a+’:’+b } alert(test(5)) alert(test(5,9)) </script> 跟其他语言的一点小区别。。 –var b=arguments[1]?arguments[1]:50 还可以写做: […]

解决setInterval计时器不准的问题

在js中如果打算使用setInterval进行倒数,计时等功能,往往是不准确的,因为setInterval的回调函数并不是到时后立即执行,而是等系统计算资源空闲下来后才会执行.而下一次触发时间则是在setInterval回调函数执行完毕之后才开始计时,所以如果setInterval内执行的计算过于耗时,或者有其他耗时任务在执行,setInterval的计时会越来越不准,延迟很厉害. 下面的代码可以说明这个问题 var startTime = new Date().getTime(); var count = 0; //耗时任务 setInterval(function(){ var i = 0; while(i++ < 100000000); }, 0); setInterval(function(){ count++; console.log(new Date().getTime() – (startTime + count * 1000)); }, 1000); 代码里输出了setInterval触发时间和应该正确触发时间的延迟毫秒数 176 340 495 652 807 961 1114 1268 1425 1579 1734 1888 2048 2201 2357 2521 2679 2834 2996 …… […]