js 问题集锦
一、页面DOM加载完成的定义:
摘自:http://hi.baidu.com/xiaocai0923/item/ba94811eb393db0ae75c3657
首先要明确两个概念
1.window.onload:页面加载完毕,页面内所有组件(图片等)都可用。
2.dom 加载:指文档对象模型加载完毕,要先于window.onload事件。
可以看出,当页面包含大量组件(特别是图片)的情形下,以上两种加载的时间相隔将会很长,这时判断dom何时加载完成就显得特别重要
页面的一些组件(css,image,flash)不会导致页面的DOM未构建完成。只有JS会阻塞页面的DOM节点的构建
function init() { // 如果该函数被调用多次,直接返回 if (arguments.callee.done) return; // arguments.callee.done = true; // 清除对safari设置的定时器 if (_timer) clearInterval(_timer); alert(document.getElementById(“test”).id); }; // firefox和opera9.0 if (document.addEventListener) { document.addEventListener(“DOMContentLoaded”, init, false); } //ie document.write(“<script id=__ie_onload defer src=javascript:void(0)><\/script>”); var script = document.getElementById(“__ie_onload”); script.onreadystatechange = function() { if (this.readyState == “complete”) { init(); // call the onload handler } }; //Safari if (/WebKit/i.test(navigator.userAgent)) { // sniff var _timer = setInterval(function() { if (/loaded|complete/.test(document.readyState)) { init(); // call the onload handler } }, 10); } //其它浏览器直接用window.onload事件 window.onload = init;
function init() { // 如果该函数被调用多次,直接返回 if (arguments.callee.done) return; // arguments.callee.done = true; // 清除对safari设置的定时器 if (_timer) clearInterval(_timer); alert(document.getElementById(“test”).id); }; // firefox和opera9.0 if (document.addEventListener) { document.addEventListener(“DOMContentLoaded”, init, false); } //ie document.write(“<script id=__ie_onload defer src=javascript:void(0)><\/script>”); var script = document.getElementById(“__ie_onload”); script.onreadystatechange = function() { if (this.readyState == “complete”) { init(); // call the onload handler } }; //Safari if (/WebKit/i.test(navigator.userAgent)) { // sniff var _timer = setInterval(function() { if (/loaded|complete/.test(document.readyState)) { init(); // call the onload handler } }, 10); } //其它浏览器直接用window.onload事件 window.onload = init;
二、jquery $(document).ready() 与window.onload的区别
Jquery中$(document).ready()的作用类似于传统JavaScript中的window.onload方法,不过与window.onload方法还是有区别的。
1.执行时间
window.onload必须等到页面内包括图片的所有元素加载完毕后才能执行。
$(document).ready()是DOM结构绘制完毕后就执行,不必等到加载完毕。
2.编写个数不同
window.onload不能同时编写多个,如果有多个window.onload方法,只会执行一个
$(document).ready()可以同时编写多个,并且都可以得到执行
3.简化写法
window.onload没有简化写法
$(document).ready(function(){})可以简写成$(function(){});
另外,需要注意一点,由于在 $(document).ready() 方法内注册的事件,只要 DOM 就绪就会被执行,因此可能此时元素的关联文件未下载完。例如与图片有关的 html 下载完毕,并且已经解析为 DOM 树了,但很有可能图片还没有加载完毕,所以例如图片的高度和宽度这样的属性此时不一定有效。要解决这个问题,可以使用 Jquery 中另一个关于页面加载的方法 ---load() 方法。 Load() 方法会在元素的 onload 事件中绑定一个处理函数。如果处理函数绑定给 window 对象,则会在所有内容 ( 包括窗口、框架、对象和图像等 ) 加载完毕后触发,如果处理函数绑定在元素上,则会在元素的内容加载完毕后触发。
//Jquery 代码如下: $(window).load(function (){ // 编写代码 });等价于 JavaScript 中的以下代码 Window.onload = function (){ // 编写代码 }
最近在改一个嵌入在frame中的页面的时候,使用了jquery做效果,而页面本身也绑定了onload事件。改完后,Firefox下测试正常流畅,IE下就要等个十几秒jquery的效果才出现,黄花菜都凉了。
起初以为是和本身onload加载的方法冲突。网上普遍的说法是$(document).ready()是在页面DOM解析完成后执行,而onload事件是在所有资源都准备完成之后才执行,也就是说$(document).ready()是要在onload之前执行的,尤其当页面图片较大较多的时候,这个时间差可能更大。可是我这页面分明是图片都显示出来十几秒了,还不见jquery的效果出来。
删了onload加载的方法试试,结果还是一样,看来没有必要把原本的onload事件绑定也改用$(document).ready()来写。那是什么原因使得Firefox正常而IE就能呢?接着调试,发现IE下原来绑定的onload方法竟然先于$(document).ready()的内容执行,而Firefox则是先执行$(document).ready()的内容,再执行原来的onload方法。这个和网上的说法似乎不完全一致啊,呵呵,有点意思,好像越来越接近真相了。
翻翻jquery的源码看看$(document).ready()是如何实现的吧:
复制代码 代码如下:
if ( jQuery.browser.msie && window == top ) (function(){ if (jQuery.isReady) return; try { document.documentElement.doScroll("left"); } catch( error ) { setTimeout( arguments.callee, 0 ); return; } // and execute any waiting functions jQuery.ready(); })(); jQuery.event.add( window, "load", jQuery.ready );
结果很明了了,IE只有在页面不是嵌入frame中的情况下才和Firefox等一样,先执行$(document).ready()的内容,再执行原来的onload方法。对于嵌入frame中的页面,也只是绑定在load事件上执行,所以自然是在原来的onload绑定的方法执行之后才轮到。而这个页面中正好在测试环境下有一个访问不到的资源,那十几秒的延迟正是它放大出的时间差。
转自:http://www.cnblogs.com/RascallySnake/archive/2010/05/07/1729563.html
JQuery的extend扩展方法:
Jquery的扩展方法extend是我们在写插件的过程中常用的方法,该方法有一些重载原型,在此,我们一起去了解了解。
一、Jquery的扩展方法原型是:
extend(dest,src1,src2,src3...);
它的含义是将src1,src2,src3...合并到dest中,返回值为合并后的dest,由此可以看出该方法合并后,是修改了dest的结构的。如果想要得到合并的结果却又不想修改dest的结构,可以如下使用:
var newSrc=$.extend({},src1,src2,src3...)//也就是将"{}"作为dest参数。
这样就可以将src1,src2,src3...进行合并,然后将合并结果返回给newSrc了。如下例:
var result=$.extend({},{name:"Tom",age:21},{name:"Jerry",sex:"Boy"})
那么合并后的结果:
result={name:"Jerry",age:21,sex:"Boy"}
也就是说后面的参数如果和前面的参数存在相同的名称,那么后面的会覆盖前面的参数值。
二、省略dest参数
上述的extend方法原型中的dest参数是可以省略的,如果省略了,则该方法就只能有一个src参数,而且是将该src合并到调用extend方法的对象中去,如:
1、$.extend(src)
该方法就是将src合并到jquery的全局对象中去,如:
$.extend({ hello:function(){alert('hello');} });
就是将hello方法合并到jquery的全局对象中。
2、$.fn.extend(src)
该方法将src合并到jquery的实例对象中去,如:
$.fn.extend({ hello:function(){alert('hello');} });
就是将hello方法合并到jquery的实例对象中。
下面例举几个常用的扩展实例:
$.extend({net:{}});
这是在jquery全局对象中扩展一个net命名空间。
$.extend($.net,{ hello:function(){alert('hello');} })
这是将hello方法扩展到之前扩展的Jquery的net命名空间中去。
三、Jquery的extend方法还有一个重载原型:
extend(boolean,dest,src1,src2,src3...)
第一个参数boolean代表是否进行深度拷贝,其余参数和前面介绍的一致,什么叫深层拷贝,我们看一个例子:
var result=$.extend( true, {}, { name: "John", location: {city: "Boston",county:"USA"} }, { last: "Resig", location: {state: "MA",county:"China"} } );
我们可以看出src1中嵌套子对象location:{city:"Boston"},src2中也嵌套子对象location:{state:"MA"},第一个深度拷贝参数为true,那么合并后的结果就是:
result={name:"John",last:"Resig",
location:{city:"Boston",state:"MA",county:"China"}}
也就是说它会将src中的嵌套子对象也进行合并,而如果第一个参数boolean为false,我们看看合并的结果是什么,如下:
var result=$.extend( false, {}, { name: "John", location:{city: "Boston",county:"USA"} }, { last: "Resig", location: {state: "MA",county:"China"} } );
那么合并后的结果就是:
result={name:"John",last:"Resig",location:{state:"MA",county:"China"}}
以上就是$.extend()在项目中经常会使用到的一些细节。
另外: jQuery合并Json对象可以用$.merge函数
jQuery.merge( first, second )
四、JQuery 中 $(document).keydown(function(e){}) 事件的绑定和取消绑定
在网页弹出层出现的的时候,通常会绑定keydown中的keyCode == 27 来实现弹出层的隐藏。但是,在弹出层隐藏后经常会忽略实现这次隐藏的keydown事件监听。下面一段代码可以取消事件的绑定:
// 监听事件函数 function keyListen(){ var _this = this; $(document).keydown(function(e){ if(27 == e.keyCode){ _this.close(); } }); } // 弹出层调用监听函数 function drop(){ $(document).bind('keydown',this.keyListen()); // 弹出层其他操作 // do sth. } // 隐藏弹出层并取消事件监听 function close(){ $(document).unbind('keydown',this.keyListen()); // do sth. 隐藏弹出层 }
五、禁止网页右键:
网站有些原创资源不想轻易的给用户复制保存,可以禁止鼠标右键弹出菜单栏。具体代码见下“
Jquery:
$('img').bind("contextmenu",function(e){ if($.browser.msie == true){ if(0 == e.button){ return false; } }else{ if(2 == e.button){ return false; } } });
// 破解
$('img').unbind('contextmenu','');
js:
// 禁止右键
document.oncontextmenu=new Function("event.returnValue=false;");
// 禁止鼠标选中
document.onselectstart=new Function("event.returnValue=false;");
html: 禁用文字选中
<div unselect="on" onselectstart="return false;"> unselect 在 ie 下起作用, onselectstart 在chrome 和 firefox 下起作用。 </div> <div>普通DIV<div> <div unselectable="on" onselectstart="return false;">从前面一个DIV开始选,就可以选中本部分内容,只有从该DIV结束部分才能不选中</div> Firefox下的解决方案: style="-moz-user-select:none;" Chrome下的解决方案: style="-webkit-user-select:none;"
六、jquery 控制 图片中心居中
用css的table可以控制图片居中。用jquery也可以实现,使用哪种看具体情况。
<div class="content"> <img src="http://www.baidu.com/img/baidu_sylogo1.gif" alt="" class="pic" /> // 图片比可见区域大,以宽高的最小边来缩放,超出的隐藏 </div>
/* css */ .content{ width:100px; /* 可见的区域 */ height:100px; overflow:hidden; /* 超出的部分要隐藏 */ } .content .pic{ width:100px; /* 脚本执行前的样式,为了整体好看 */ height: 100px; }
/* 脚本 */ var pw = $('.content').width(); var ph = $('.content').height(); $('.content .pic').each(function(i){ var img = new Image(); img.src = $(this).attr('src'); var w = img.width; var h =img.height; var img = new Image(); img.src = $(this).attr('src'); var w = img.width; var h = img.height; if(w >= pw && h >= ph){ // 图片大于父元素,按宽高的最小值缩放 if(w >= h){ $(this).css('height', ph+'px'); var nw = Math.ceil(ph*w/h); $(this).css('width', nw+'px'); $(this).css('margin-left', (-Math.ceil((nw-pw)/2))+'px'); }else{ $(this).css('width', pw); var nh = Math.ceil(pw*h/w); $(this).css('height', nh); $(this).css('margin-top', (-Math.ceil((nh-ph)/2))+'px'); } }else{ if(w >= pw && h < ph){ // 图片宽大于父元素, 按宽缩放 $(this).css('width', pw); var nh = Math.ceil(pw*h/w); $(this).css('height', nh); $(this).css('margin-top', (Math.ceil((ph-nh)/2))+'px'); }else if(w < pw && h >= ph){ // 图片高大于父元素,按高缩放 $(this).css('height', ph+'px'); var nw = Math.ceil(ph*w/h); $(this).css('width', nw+'px'); $(this).css('margin-left', (Math.ceil((pw-nw)/2))+'px'); }else { // 宽高都小于父元素, 居中 $(this).css({'height': (w+'px'), 'width': (h+'px'), 'margin-left': Math.ceil((pw-w)/2)+'px', 'margin-top': Math.ceil((ph-h)/2)+'px'}); } } });
七、页面加载时判断某张图片是否加载完毕
对于该问题网上有几种方法,常见的jquery.load()方法我尝试失败,只有js中的onload实验成功。
见代码:
// 函数 function checkImgLoad($elem){ // 传入图片对象 var src = $elem.attr('src'); // 带判断的图片地址 var img = new Image(); // 新建一个图像对象 $elem.attr('src','加载中的过渡图片地址'); img.src = src;
if(img.complete){ // 判断图片是否给浏览器缓存了
// do 图片加载后爱做的事
return ; // 不继续后面的事了
} img.onload = function(){ // 使用onload来实现判断图片是否加载完成 $elem.attr('src', src); // 替换回加载完成的图片 // 做你在加载后爱做的事~~ }; }
// 使用 <script type="text/javascript"> checkImgLoad($('.pic')); // 注意不要放在$(function(){})中 </script>
利用HTML5新加入的history.pushState();和history.replaceState();可以完全自己维护一个历史记录列表绕开历史记录完全由浏览器控制的机制,从而实现比锚点更加完美的一种页内更新的体验。
window.history.pushState({"html":response.html,"pageTitle":response.pageTitle}, 'title', urlPath); //三个参数,分别为:状态对象,标题(目前被浏览器忽略),地址
即可在历史记录里面产生一个新的历史记录(另一个replaceState方法参数完全相同,只是替代掉当前的状态)。pushState 用于向 history 添加当前页面的记录,而 replaceState 和 pushState 的用法完全一样,唯一的区别就是它用于修改当前页面在 history 中的记录。
详解:
pushState()有三个参数:state对象,标题,URL(可选)。具体细节:
· state对象 –state对象是一个JavaScript对象,它关系到由pushState()方法创建出来的新的history实体。用以存储关于你所要插入到历史 记录的条目的相关信息。State对象可以是任何Json字符串。因为firefox会使用用户的硬盘来存取state对象,这个对象的最大存储空间为640k。如果大于这个数 值,则pushState()方法会抛出一个异常。如果确实需要更多的空间来存储,请使用本地存储。
· title—firefox现在回忽略这个参数,虽然它可能将来会被使用上。而现在最安全的使用方式是传一个空字符串,以防止将来的修改。或者可以传一个简短的标题来表示state
· URL—这个参数用来传递新的history实体的URL,注意浏览器将不会在调用pushState()方法后加载这个URL。但也许会过一会尝试加载这个URL。比如在用户重启了浏览器后,新的url可以不是绝对路径。如果是相对路径,那么它会相对于现有的url。新的url必须和现有的url同域,否则pushState()将抛出异常。这个参数是选填的,如果为空,则会被置为document当前的url。
九、实现把某块元素放大
因项目需求,要实现某个图片幻灯片在点击全屏按钮后实现全屏展示,点击退出按钮后缩回原尺寸,并且需要全屏展示和原尺寸展示的图片显示是同步的。我实现的方式是把幻灯片这块元素在原尺寸和全屏尺寸的两个div父元素中进行移动。
在实现过程中,css的样式处理比较简单,只需要注意把未确定的尺寸用百分比来确定。而脚本的控制比较麻烦,需要注意事件的绑定和解除绑定:重复绑定事件会造成事件多次触发执行。
见简代码
<div id="normal"> <!-- 这里是原比例,要固定好宽高 --> <div id="slider"> <!-- 这里是需要移动的幻灯片元素,用%比来确定宽高 --> </div> </div> <div id="full"> <!-- 这里是全屏, 也要固定好宽高 --> </div>
移动元素的js
function moveSlider(type){ if('normal' == type){ // 原尺寸显示 $('#normal').append($('#slider').detach()); } if('full' == type){ // 全屏显示 $('#full').append($('#slider').detach()); } }
$.detach()定义和用法
detach() 方法移除被选元素,包括所有文本和子节点。
这个方法会保留 jQuery 对象中的匹配的元素,因而可以在将来再使用这些匹配的元素。
detach() 会保留所有绑定的事件、附加的数据,这一点与 remove() 不同。
十、判断事件点击区域
$('#search').click(function(){ if($('#searchList',this).is(':hidden')){ $('#searchList',this).show(); $(document).bind('click', bindSearchType()); // 此处绑定 }else{ $('#searchList',this).hide(); $(document).unbind('click', bindSearchType()); // 此处解除绑定 } })
// 绑定函数 function bindSearchType(){ $(document).click(function(e){ console.log(e.target); if(e.target.id != 'searchType'){ // 判断区域,事件对象的id $('#searchList').hide(); } }); }
// 可以用其他判断方式
// $(e.target).hasClass('');
// $(e.target).has('');