原生DOM兼容特性
浏览器主要也就是IE有点独特,所以把IE重点区分开
名称 | 主流 | IE | |
---|---|---|---|
内文本 | innerText | textContent | |
请求对象 | XMLHttpRequest | ActiveXObject["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"] | |
监听事件添加 | addEventListener(DOM2), ['on'+eventName](DOM0) | attachEvent | |
监听事件移除 | removeEventListener(DOM2),['on'+eventName]=null(DOM0) | detachEvent | |
事件对象 | function(e) e | window.event(IE7之前) | |
阻止默认事件 | preventDefault | e.returnValue=false | |
阻止冒泡 | stopPropagation | e.cancelBubble=true | |
键盘事件键盘编码 | e.charCode | e.keyCode | |
获取剪贴板的文本 | e.clipboardData | window.clipboardData | |
设置剪贴板文本 | e.clipboardData.setData("text/plain",value); | window.clipboardData.setData("text",value); | |
触发事件的元素 | e.target | e.srcElement | |
获取样式 | getComputedStyle(obj,false)[attr];(Firefox浏览器) obj.style.attr(只对filter,opacity等有效)obj.style[attr] | obj.currentStyle[attr]; | |
窗口左边的位置 | window.screenLeft | window.screenX | |
页面视口大小 | window.innerHeight | if(document.compatMode=="CSS1Compat")window.documentElement.clientHeight; if(document.compatMode=="BackCompat")window.body.clientHeight | |
获取元素 | document.getElementById(id); | document.all[id];(IE5) | |
返回指定的属性 | ele.getAttribute(attr) | ele.attribute[attr] | |
ele是否存在指定属性 attr | ele.hasAttribute(attr) | ele.attributes[attr].specified; | |
鼠标滚动,正数表示向上滚动 | function getWheelDelta(e){ if(e.wheelData){ return (client.engine.opera&&client.engine.opera<9.5)?-e.wheelData:e.wheelData;}else { return -e.detail*40;//firefox}} | ||
提取选中的文本 | textbox.value.subString(textbox.selectionStart,textbox.selectionEnd); | document.selection.createRange().text;(IE8之前没有selectionStart,selectionEnd属性 | |
设置文本选中 | textbox.setSelectionRange(startIndex,stopIndex); | var range=textbox.createTextRange();range.collapse(true);range.moveStart("character",0);range.moveEnd("character",stopIndex-startIndex);range.select() |
下面是积累下来的一些兼容函数,可以当做模板用
添加多个onload事件
function addLoadEvent(func){ var oldonload=window.onload; if(typeof window.onload!= 'function'){ window.onload=func; } else{ window.onload=function(){ oldonload(); func(); } }}
处理ActiveXObject/XMLHttpRequest问题
//第一种写法,《js高级程序设计》的写法 惰性载入技巧function createXHR(){ if(typeof XMLHttpRequest!="undefined"){//XMLHttpRequest createXHR=function(){ return new XMLHttpRequest(); }; }else if(typeof ActiveXObject!="undefined"){//IE ActiveXObject createXHR=function(){ if(typeof arguments.callee.activeXString!="string"){ var versions=["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"],//IE i,len; for(i=0,len=versions.length;i
请求对象的属性和方法设置
var xhr=createXHR();xhr.onreadystatechange=function(){//firfox引入onlaod,readyState==4时触发,代替onreadystatechange if(xhr.readyState==4){ try{ if((xhr.status>=200&&xhr.status<300)||xhr.status==304){ alert(xhr.responseText); }else { alert("unsuccessful"); } }catch(ex){} }}xhr.onload=function(){ if((xhr.status>=200&&xhr.status<300)||xhr.status==304){ alert(xhr.responseText); }else { alert("unsuccessful"); }}xhr.onprogress=function(event){ if(event.lengthComputable){//进度信息是否可用 console.log("Received"+event.position+"of"+event.totalSize); }}xhr.onerror=function(){ alert("error");}xhr.timeout=1000;xhr.ontimeout=function(){ alert("timeout");}xhr.open("get","example.php",true);xhr.overrideMimeType("text/xml");xhr.send(null);//GET
发送表单数据
var form=document.getElementById("info");xhr.send(serialize(form));//第一种写法xhr.send(new FormData(form));//第二种写法
跨浏览器的CORS
function createCORSRequest(method,url){ var xhr=new XMLHttpRequest(); if("withCredentials" in xhr){ xhr.open(method,url,true); }else if(typeof XDomainRequest !="undefined"){ xhr=new XDomainRequest(); xhr.open(method,url); }else { xhr=null; } return xhr;}var request=createCORSRequest("get","http://www.somewhere");if(request){ request.onload=function(){}; request.send();}
跨浏览器事件处理程序
var eventUtil={ // 页面加载完成后 readyEvent : function(fn) { if (fn==null) { fn=document; } var oldonload = window.onload; if (typeof window.onload != 'function') { window.onload = fn; } else { window.onload = function() { oldonload(); fn(); }; } }, addEventHandler: function (obj, eventName, handler) { if (document.attachEvent) {//IE obj.attachEvent("on" + eventName, handler); } else if (document.addEventListener) {//DOM2级 obj.addEventListener(eventName, handler, false);//false- 默认。事件句柄在冒泡阶段执行 } else{//DOM0级 obj['on'+eventName]=handler; } }, removeEventHandler:function(obj, eventName, handler){ if (document.attachEvent) {//IE obj.detachEvent("on" + eventName, handler); } else if (document.addEventListener) {//DOM2级 obj.removeEventListener(eventName, handler, false); } else{//DOM0级 obj['on'+eventName]=null; } }, //获取event对象的引用,取到事件的所有信息,确保随时能使用event; getEvent: function (e) { var ev = e || window.event; if (!ev) { var c = this.getEvent.caller; while (c) { ev = c.arguments[0]; if (ev && Event == ev.constructor) { break; } c = c.caller; } } return ev; }, //事件类型 getType: function (e) { return e.type; }, //调用事件的元素 getElement: function (e) { return e.target|| e.srcElement; }, //阻止默认事件 preventDefault: function (e) { e= this.getEvent(e); if(e.preventDefault){ e.preventDefault(); } else { return e.returnValue=false;//IE } }, //阻止冒泡 stopPropagation:function(e) { if(e.stopPropagation){ e.stopPropagation(); } else { e.cancelBubble=true;//IE } }, //键盘事件键盘的编号 getCharCode:function (e){ if(typeof e.charCode=="number")return e.charCode; else return e.keyCode; }, //获取剪贴板的文本 getClipbordText:function(e){ var clipboardData=(e.clipboardData||window.clipboardData); return clipboardData.getData("text"); }, //设置剪贴板文本 setClipboardText:function(e,value){ if(e.clipboardData){ return e.clipboardData.setData("text/plain",value); }else if(window.clipboardData){ return window.clipboardData.setData("text",value); } },}
处理target/srcelemnt问题,代替this
function getActivatedObject(e) { var obj; if (!e) { // early version of IE obj = window.event.srcElement; } else if (e.srcElement) { // IE 7 or later obj = e.srcElement; } else { // DOM Level 2 browser obj = e.target; } return obj;}
实现insertAfter
/*** 把newElement插在targetElement后面 ,js的API只有insertBefore,没有insertAfter*/function insertAfter(newElement,targetElement) { var parent = targetElement.parentNode; if (parent.lastChild == targetElement) { parent.appendChild(newElement); } else { parent.insertBefore(newElement,targetElement.nextSibling); }}
给element加上类名
function addClass(element,value) { if (!element.className) { element.className = value; } else { newClassName = element.className; newClassName+= " "; newClassName+= value; element.className = newClassName; }}
判断是不是数组
function isArray(arg) { //return Object.prototype.toString.call(arr)=='[Object Array]';这种方法也可以 if (typeof arg == 'object') { //所有数组都有一个包含单词'arry'的constructor,最后的i表示不区分大小写 var criteria = arg.constructor.toString().match(/array/i); return (criteria != null); } return false;}
IE10之前不支持docunment.getElementByClassName
function getByClass(clsName,parent){ if(docunment.getElementByClassName) return docunment.getElementByClassName(clsName); //IE10之前 var oParent=parent?document.getElementById(parent):document, eles=[], elements=oParent.getElementsByTagName('*'); for(var i=0,l=elements.length;i
获取css样式
function getStyle(obj,attr){ if(obj.currentStyle) {//IE 浏览器 return obj.currentStyle[attr]; }else{//Firefox浏览器 return getComputedStyle(obj,false)[attr]; }}
手写动画
//动画 startMove(oLi,{width:400,height:200,opacity:100})function startMove(obj,json,fn){ clearInterval(obj.timer); obj.timer=setInterval(function () { for(var attr in json){ var cur=0; if(attr=='opacity'){ cur=Math.round(parseFloat(getStyle(obj,attr))*100); } else { cur=parseInt(getStyle(obj,attr)); } var speed=(json[attr]-cur)/8; speed=speed>0?Math.ceil(speed):Math.floor(speed); var flag=true; if(cur!=json[attr]){//使得所有属性做完运动才结束 flag=false; } if(attr=='opacity'){ obj.style.filter='alpha(opacity:'+(cur+speec)+')'; obj.style.opacity=(cur+speed)/100; } else{ obj.style[attr]=(cur+speed)+'px'; } } if(flag){ clearInterval(obj.timer); if(fn){ fn(); } } })}
取得窗口左边和上边的位置
var leftPos=(typeof window.screenLeft =="number")?window.screenLeft:window.screenX;
取得页面视口大小
var pageWidth=window.innerWidth, pageHeight=window.innerHeight; if(typeof pageHeight!="number"){ if(document.compatMode=="CSS1Compat"){//标准模式 pageHeight=window.documentElement.clientHeight; pageWidth=window.documentElement.clientWidth; } else {//BackCompat pageHeight=window.body.clientHeight; pageWidth=window.body.clientWidth; } }
检测插件方法
/**** 检测插件方法一,IE下无效*/function hasPlugin(name){ name=name.toLowerCase(); for(var i=0;i
获取元素
function getElement(id){ if(document.getElementById){ return document.getElementById(id); }else if(document.all){//IE5前 return document.all[id]; }else { throw new Error("no way to retrieve element!"); }}
检查对象的某个特性是否存在
function isHostMethod(object,property){ var t =typeof object[property]; return t=="function"|| (!!(t=="object")&&object[property])|| t=="unknown";//不懂}
对象转换成数组
function convertToArray(nodes){ var array=null; try{ array=Array.propotype.slice.call(nodes,0);//IE8前无效 }catch(ex){ for(var i=0,len=nodes.length;i
返回指定的属性
/*** * 返回指定的属性 IE ele.attribute[]* Element.getAttribute()*/function outputAttribute(ele){ var pairs=new Array(), attrname,attrvalue,u,len; for(i=0,len=ele.attribute.length;i
ele是否存在指定属性
/*** * ele是否存在指定属性 attr*/function hasattribute(ele,attr){ if(ele.hasAttribute){ return ele.hasAttribute(attr); }else {//IE return ele.attributes[attr].specified; }}
ele是否符合选择器selector
/*** ele是否符合选择器selector*/function matchesSelector(ele,selector){ if(ele.matchesSelector){ return ele.matchesSelector(selector); }else if(ele.msmatchesSelector){ return ele.msmatchesSelector(selector); }else if(ele.mozmatchesSelector){ return ele.mozmatchesSelector(selector); }else if(ele.webkitmatchesSelector){ return ele.webkitmatchesSelector(selector); }else{ throw new Error("not support"); }}
获取内文本
//innerText/textContentfunction getInnerText(ele){ return (typeof ele.innerText=="string")? ele.innerText:ele.textContent;}
获取鼠标事件的父元素
function getRelatedTarget(e){ if(e.relatedTarget) return e.relatedTarget; else if(e.fromElement) return e.fromElement;//mouseover else if(e.toElement) return e.toElement;//mouseout else return null; }
探测按的是鼠标的哪个键
function getButton(e){ if(document.implementation.hasFeature("MouseEvents","2.0")){ return e.button; }else { switch(e.button){ case 0 : case 1: case 3: case 5: case 7: return 0; case 2: case 6:return 2; case 4:return 1; } } }
鼠标滚动事件
//鼠标滚动,正数表示向上滚动function getWheelDelta(e){ if(e.wheelData){ return (client.engine.opera&&client.engine.opera<9.5)? -e.wheelData:e.wheelData; }else { return -e.detail*40;//firefox } }
提取选中的文本
function getSelectedText(textbox){ if(typeof selectionStart=="number"){ return textbox.value.subString(textbox.selectionStart,textbox.selectionEnd); }else if(document.selection){//IE8之前没有selectionStart,selectionEnd属性 return document.selection.createRange().text; }}
设置文本选中
function selectText(textbox,startIndex,stopIndex){ if(textbox.setSelectionRange){ textbox.setSelectionRange(startIndex,stopIndex); }else if(textbox.createTextRange){//IE var range=textbox.createTextRange(); range.collapse(true); range.moveStart("character",0); range.moveEnd("character",stopIndex-startIndex); range.select(); }}
bind方法对老版本的浏览器不起作用
Function.prototype.bind = Function.prototype.bind || function(context){ var self = this; return function(){ return self.apply(context, arguments); }; }
包装cookie
//cookievar cookieUtil={ // 创建cookie setcookie:function (name, value, expires, path, domain, secure) { var cookieText = encodeURIComponent(name) + '=' + encodeURIComponent(value); if (expires instanceof Date) { cookieText += '; expires=' + expires.toGMTString(); } if (path) { cookieText += '; path=' + path; } if (domain) { cookieText += '; domain=' + domain; } if (secure) { cookieText += '; secure'; } document.cookie = cookieText;}, // 获取cookie getcookie:function (name) { var cookieName = encodeURIComponent(name) + '='; var cookieStart = document.cookie.indexOf(cookieName); var cookieValue = null; if (cookieStart > -1) { var cookieEnd = document.cookie.indexOf(';', cookieStart); if (cookieEnd == -1) { cookieEnd = document.cookie.length; } cookieValue = decodeURIComponent(document.cookie.substring(cookieStart + cookieName.length, cookieEnd)); } return cookieValue;}, // 删除cookie unsetcookie:function (name,path,domain,secure) { this.setcookie(name,"",new Date(0),path,domain,secure);}}
包装子cookie
//子cookievar subcookieUtil={ get:function(name,subname){ var subcookie=getAll(name); if(subcookie){ return subcookie[subname]; }else { return null; } }, getAll:function(name){ var cookieName = encodeURIComponent(name) + '='; var cookieStart = document.cookie.indexOf(cookieName); var cookieValue = null; var subcookie,result={}; var len,i,parts; if (cookieStart > -1) { var cookieEnd = document.cookie.indexOf(';', cookieStart); if (cookieEnd == -1) { cookieEnd = document.cookie.length; } cookieValue = document.cookie.substring(cookieStart + cookieName.length, cookieEnd); if(cookieValue.length>0){ subcookie=cookieValue.split('&'); len=subcookie.length; for(i=0;i0&&subcookies.hasOwnProperty(subname)){ subcookiesParts.push(encodeURIComponent(subname)+" ='+encodeuricomponent(subcookies[subname])); if(subcookiesparts.length> 0){ cookieText+=subcookiesParts.join('&'); if (expires instanceof Date) { cookieText += '; expires=' + expires.toGMTString(); } if (path) { cookieText += '; expires=' + expires; } if (domain) { cookieText += '; domain=' + domain; } if (secure) { cookieText += '; secure'; } } document.cookie = cookieText; }, unset:function(name,subname,path,domain,secure){ var subcookies=this.getAll(name); if(subcookies){ delete subcookies[subname]; this.setAll(name,subcookies,null,path,domain,secure); } }, unsetAll:function(name,path,domain,secure){ this.setAll(name,null,new Date(0),path,domain,secure); }}
indexedDB
var indexedDB=window.indexedDB||window.mozIndexedDB||window.msIndexedDB||window.webkitIndexedDB;var idbRequest=indexedDB.open('vvv');idbRequest.onsuccess=function(event){ database=event.target.result;}idbRequest.onerror=function(event){ alert(event.target.errorCode);}
手写typeof
function type(obj) { var toString = Object.prototype.toString; var map = { '[object Boolean]' : 'boolean', '[object Number]' : 'number', '[object String]' : 'string', '[object Function]' : 'function', '[object Array]' : 'array', '[object Date]' : 'date', '[object RegExp]' : 'regExp', '[object Undefined]': 'undefined', '[object Null]' : 'null', '[object Object]' : 'object' }; if(obj instanceof Element) {//因为对不同标签,toString会返回对应不同标签的构造函数 return 'element'; } return map[toString.call(obj)];}
深度克隆
/*** * 深度克隆方法一,用的是instanceof*/function clone(Obj) { var buf; if (Obj instanceof Array) { buf = []; // 创建一个空的数组 var i = Obj.length; while (i--) { buf[i] = clone(Obj[i]); } return buf; } else if (Obj instanceof Object){ buf = {}; // 创建一个空对象 for (var k in Obj) { // 为这个对象添加新的属性 buf[k] = clone(Obj[k]); } return buf; }else{ return Obj; }}/*** * 深度拷贝方法二,用的是 toString*/function deepClone(data) { var t = type(data), o, i, ni; if(t === 'array') { o = []; }else if( t === 'object') { o = {}; }else { return data; } if(t === 'array') { for (i = 0, ni = data.length; i < ni; i++) { o.push(deepClone(data[i])); } return o; }else if( t === 'object') { for( i in data) { o[i] = deepClone(data[i]); } return o; }}//通过JSON.stringify一下,然后再JSON.parse一下,就能实现深拷贝。但是数据类型只支持基本数值类型。var obj = { a: 'a', b: function(){console.log('b')}}JSON.stringify(obj);// "{"a":"a"}"
组合使用构造函数模式和原型模式创建对象
//组合使用构造函数模式和原型模式创建对象function Person(name,age){ this.name=name; this.age=age; this.friends=["may","john"];}Person.prototype={ constructor:Person,//字面量形式的原型默认构造函数是object,所以在这里要指定constructor sayName=function(){ alert(this.name); }}
组合继承
//组合继承funcion super(name){ this.name=name; this.color=["red","blue"]; }Super.prototype.sayname=function(){ alert(this.name);}function Sub(age){ Super.call(this); this.age=age;}Sub.prototype=new Super();//Sub.prototype.constructor=Sub;//这个很重要!!Sub.prototype.sayage=function(){ alert(this.age);}
观察者模式
//观察者模式function EventTarget(){ this.handlers = {}; } EventTarget.prototype = { constructor: EventTarget, addHandler: function(type, handler){ if (typeof this.handlers[type] == "undefined"){ this.handlers[type] = []; } this.handlers[type].push(handler); }, fire: function(event){//执行 if (!event.target){ event.target = this; } if (this.handlers[event.type] instanceof Array){ var handlers = this.handlers[event.type]; for (var i=0, len=handlers.length; i < len; i++){ handlers[i](event); } } }, removeHandler: function(type, handler){ if (this.handlers[type] instanceof Array){ var handlers = this.handlers[type]; for (var i=0, len=handlers.length; i < len; i++){ if (handlers[i] === handler){ break; } } handlers.splice(i, 1); } }};
分享一个题目
//[附加题] 请实现下面的自定义事件 Event 对象的接口,功能见注释(测试1)//该 Event 对象的接口需要能被其他对象拓展复用(测试2)//测试1Event.on('test', function (result) { console.log(result);});Event.on('test', function () { console.log('test');});Event.emit('test', 'hello world'); // 输出 'hello world' 和 'test'// 测试2var person1 = {};var person2 = {};Object.assign(person1, Event);Object.assign(person2, Event);person1.on('call1', function () { console.log('person1');});person2.on('call2', function () { console.log('person2');});person1.emit('call1'); //输出 'person1'person1.emit('call2'); // 没有输出person2.emit('call1'); // 没有输出person2.emit('call2'); // 输出 'person2'var Event = { // 通过on接口监听事件eventName // 如果事件eventName被触发,则执行callback回调函数 on: function (eventName, callback) { //你的代码 if(!this.handles){ //Object.assign(target, source);//这个是ES6的新对象方法,用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。 Object.defineProperty(this, "handles", { value: {}, enumerable: false,//关键! configurable: true, writable: true }) } if(!this.handles[eventName]){ this.handles[eventName]=[]; } this.handles[eventName].push(callback); }, // 触发事件 eventName emit: function (eventName) { //你的代码 if(this.handles[arguments[0]]){ for(var i=0;i50 && nHeight > 50) { aResult.push(oNode); } } var aChildNodes = oNode.childNodes; if (aChildNodes.length > 0) { for (var i = 0, l = aChildNodes.length; i < l; i++) { var oTmp = aChildNodes[i]; aResult = aResult.concat(traverse(oTmp)); } } return aResult;}