原生js实现 常见的jquery的功能
节点操作
var div2 = document.querySelector("#div2"); div2.insertAdjacentHTML("beforebegin","<p>hello world</p>");//在调用元素外部前面添加一个元素 div2.insertAdjacentHTML("afterbegin","<p>hello world</p>");//在调用元素的内部添加一个子元素并取代了第一个子元素 div2.insertAdjacentHTML("beforeend","<p>hello world</p>");//在调用元素内部后面添加一个子元素 即取代了最后的子元素 div2.insertAdjacentHTML("afterend","<p>hello world</p>");//在调用元素的外部后面添加一个元素
var bes=document.getElementById("appends"), var strs= "<li>234</li>"; bes.insertAdjacentHTML('beforebegin', strs);
extend 普通深拷贝
function extend() { var length = arguments.length; var target = arguments[0] || {}; if (typeof target!="object" && typeof target != "function") { target = {}; } if (length == 1) { target = this; i--; } var hasOwn=Object.prototype.hasOwnProperty; for (var i = 1; i < length; i++) { var source = arguments[i]; for (var key in source) { // 使用for in会遍历数组所有的可枚举属性,包括原型。 if (hasOwn.call(source, key)) { target[key] = source[key]; } } } return target; }
var obj1 = {'a': 'obj2','b':'2'}; var obj2 = {name: 'obj3'}; console.log('extend',extend({},obj1,obj2),'obj1',obj1,'obj2',obj2);
jquery 深拷贝
function isArray(value) { return Array.isArray(value) || Object.prototype.toString.call(value) === '[object Array]'; } function isFunction(fn) { return Object.prototype.toString.call(fn) === "[object Function]"; } function isPlainObject(value) { if (typeof value !== 'object' || value === null) { return false } if (Object.getPrototypeOf(value) === null) { return true } var proto = value while (Object.getPrototypeOf(proto) !== null) { proto = Object.getPrototypeOf(proto) } return Object.getPrototypeOf(value) === proto } function myExtend() { var src, copyIsArray, copy, name, options, clone, target = arguments[0] || {}, i = 1, length = arguments.length, deep = false; // Handle a deep copy situation if (typeof target === "boolean") { deep = target; target = arguments[1] || {}; // skip the boolean and the target i = 2; } // Handle case when target is a string or something (possible in deep copy) if (typeof target !== "object" && !isFunction(target)) { target = {}; } // extend jQuery itself if only one argument is passed if (length === i) { target = this; --i; } for (; i < length; i++) { // Only deal with non-null/undefined values if ((options = arguments[i]) != null) { // Extend the base object for (name in options) { src = target[name]; copy = options[name]; // Prevent never-ending loop if (target === copy) { continue; } // Recurse if we're merging plain objects or arrays if (deep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) { if (copyIsArray) { copyIsArray = false; clone = src && isArray(src) ? src : []; } else { clone = src && isPlainObject(src) ? src : {}; } // Never move original objects, clone them target[name] = myExtend(deep, clone, copy); // Don't bring in undefined values } else if (copy !== undefined) { target[name] = copy; } } } } // Return the modified object return target; }
var obj1 = { a: 1, name: "张珊", title: { text: 'hello world', subtext: 'It/s my world.' } }; var obj2 = { a: 2, name: "李四", title: { subtext: 'Yes, your world.' } } //var obj3= {...{},...obj1,...obj2 } // Object.assign(目标对象,...源对象)可以有多个源对象 //var obj3 =Object.assign({},obj1,obj2) //var obj3= myExtend({},obj1,obj2) //console.log("$",$.version) //var obj3= $.extend(true,{},obj1,obj2) //var obj3= myExtend({},obj1,obj2) var obj3 = myExtend2({}, obj1, obj2) obj3.name = '王五' console.log("obj1", obj1); console.log("obj2", obj2); console.log("obj3", obj3, "title", obj3.title);
去掉JSON 中的 "undefined","null" 等字符串
function deleteEmptyProp(opt,delArr,del) { var delArr=delArr||["undefined","null",undefined,null,NaN]; for (var key in opt) { var val=opt[key]; if (delArr.includes(val)) { if(del){ delete opt[key]; }else{ var isValueNaN= (typeof val === 'number')&&(val !== val)?1:0;//判断NaN ,替换成0 if(isValueNaN){ opt[key]=0; }else{ opt[key]=""; } } } } return opt; }
var sas={a: 'duo', b: 0, c: null,d:"null",e:"undefined",f:NaN,g:undefined}
console.log((deleteEmptyProp(sas)))
var sas2={a: 'duo', b: 0, c: null,d:"null",e:"undefined",f:NaN,g:undefined,h:"NaN"}
console.log(deleteEmptyProp(sas2,["undefined","null",undefined,null,NaN]))
合理化 字符串
// "true" => true // "false" => false // "null" => null // "42" => 42 // "42.5" => 42.5 // "08" => "08" // JSON => parse if valid // String => self function deserializeValue(value) { var num try { return value ? value == "true" || ( value == "false" ? false : value == "null" ? null : !/^0/.test(value) && !isNaN(num = Number(value)) ? num : /^[\[\{]/.test(value) ? $.parseJSON(value) : value ) : value } catch(e) { return value } }
原生选择器 $('#box') 利用 bind(this)绑定
<div id="box"> <ul> <li >111 </li> <li class="lione">2222</li> <li class="lione">3333</li> </ul> </div> <div id="box2"> <p>我是AAAA</p> <p>我是BBBB</p> </div>
//选择器的实现 var element = document.querySelector('selectors'); var elementList = document.querySelectorAll('selectors'); var $=query = document.querySelector.bind(document); var queryAll = document.querySelectorAll.bind(document); //这个是最牛逼的 细细体会就会发现 var fromId = document.getElementById.bind(document); var fromClass = document.getElementsByClassName.bind(document); var fromTag = document.getElementsByTagName.bind(document); //调用 var box=$('#box'); var lioneall=queryAll('li'); var lione=$('.lione'); console.log(box.innerHTML); // console.log(lioneall[2].innerHTML); //333 box.addEventListener('click',function(){ console.log('click lione'+lione.innerHTML); //click lione 2222 });
另一个不错的 https://github.com/snandy/zchain/blob/master/%24/selector.js
/** * JavaScript Selector * Copyright (c) 2010 snandy * Blog: http://snandy.cnglogs.com * QQ群: 34580561 * * $ 获取元素, 在DOM中使用频繁的,根据2/8原则只实现最常用的四种 * * @param {Object} selector * @param {Object} context * * 1, 通过id获取,该元素是唯一的 * $('#id') * * 2, 通过className获取 * $('.cls') 获取文档中所有className为cls的元素 * $('.cls', el) * $('.cls', '#id') * $('span.cls') 获取文档中所有className为cls的span元素 * $('span.cls', el) 获取指定元素中className为cls的元素, el为HTMLElement (不推荐) * $('span.cls', '#id') 获取指定id的元素中className为cls的元素 * * 3, 通过tagName获取 * $('span') 获取文档中所有的span元素 * $('span', el) 获取指定元素中的span元素, el为HTMLElement (不推荐) * $('span', '#id') 获取指定id的元素中的span元素 * * 4, 通过attribute获取 * $('[name]') 获取文档中具有属性name的元素 * $('[name]', el) * $('[name]', '#id') * $('[name=uname]') 获取文档中所有属性name=uname的元素 * $('[name=uname]', el) * $('[name=uname]', '#id') * $('input[name=uname]') 获取文档中所有属性name=uname的input元素 * $('input[name=uname]', el) * $('input[name=uname]', '#id') */ ~function(win, doc, undefined) { // Save a reference to core methods var slice = Array.prototype.slice // selector regular expression var rId = /^#[\w\-]+/ var rTag = /^([\w\*]+)$/ var rCls = /^([\w\-]+)?\.([\w\-]+)/ var rAttr = /^([\w]+)?\[([\w]+-?[\w]+?)(=(\w+))?\]/ // For IE9/Firefox/Safari/Chrome/Opera var makeArray = function(obj) { return slice.call(obj, 0) } // For IE6/7/8 try{ slice.call(doc.documentElement.childNodes, 0)[0].nodeType } catch(e) { makeArray = function(obj) { var result = [] for (var i = 0, len = obj.length; i < len; i++) { result[i] = obj[i] } return result } } function byId(id) { return doc.getElementById(id) } function check(attr, val, node) { var reg = RegExp('(?:^|\\s+)' + val + '(?:\\s+|$)') var attribute = attr === 'className' ? node.className : node.getAttribute(attr) if (attribute) { if (val) { if (reg.test(attribute)) return true } else { return true } } return false } function filter(all, attr, val) { var el, result = [] var i = 0, r = 0 while ( (el = all[i++]) ) { if ( check(attr, val, el) ) { result[r++] = el } } return result } function query(selector, context) { var s = selector, arr = [] var context = context === undefined ? doc : typeof context === 'string' ? query(context)[0] : context if (!selector) return arr // id和tagName 直接使用 getElementById 和 getElementsByTagName // id if ( rId.test(s) ) { arr[0] = byId( s.substr(1, s.length) ) return arr } // Tag name if ( rTag.test(s) ) { return makeArray(context.getElementsByTagName(s)) } // 优先使用querySelector,现代浏览器都实现它了 if (context.querySelectorAll) { if (context.nodeType === 1) { var old = context.id, id = context.id = '__ZZ__' try { return context.querySelectorAll('#' + id + ' ' + s) } catch(e) { throw new Error('querySelectorAll: ' + e) } finally { old ? context.id = old : context.removeAttribute('id') } } return makeArray(context.querySelectorAll(s)) } // ClassName if ( rCls.test(s) ) { var ary = s.split('.') var tag = ary[0] var cls = ary[1] if (context.getElementsByClassName) { var elems = context.getElementsByClassName(cls) if (tag) { for (var i = 0, len = elems.length; i < len; i++) { var el = elems[i] el.tagName.toLowerCase() === tag && arr.push(el) } return arr } else { return makeArray(elems) } } else { var all = context.getElementsByTagName(tag || '*') return filter(all, 'className', cls) } } // Attribute if ( rAttr.test(s) ) { var result = rAttr.exec(s) var all = context.getElementsByTagName(result[1] || '*') return filter(all, result[2], result[4]) } } win.query = win.$ = query }(this, document);
offset 函数
var myOffest=function (obj){ var top=0,left=0; if(obj){ while(obj.offsetParent){ top += obj.offsetTop; left += obj.offsetLeft; obj = obj.offsetParent; } } return{ top : top, left : left } }
var jqtop=jQuery('#box2').offset().top; console.log('jQuery offsetTop 值'+jqtop); // jQuery offsetTop 值274 var jstop=document.getElementById("box2"); console.log('js offsetTop 值'+myOffest(jstop).top); // js offsetTop 值274
getOffset2(node){ var offest = { top: 0, left: 0 }; // 当前为IE11以下, 直接返回{top: 0, left: 0} if (!node.getClientRects().length) { return offest; } // 当前DOM节点的 display === 'node' 时, 直接返回{top: 0, left: 0} if (window.getComputedStyle(node)['display'] === 'none') { return offest; } // Element.getBoundingClientRect()方法返回元素的大小及其相对于视口的位置。 // 返回值包含了一组用于描述边框的只读属性——left、top、right和bottom,单位为像素。除了 width 和 height 外的属性都是相对于视口的左上角位置而言的。 // 返回如{top: 8, right: 1432, bottom: 548, left: 8, width: 1424…} offest = node.getBoundingClientRect(); var docElement = node.ownerDocument.documentElement; return { top: offest.top + window.pageYOffset - docElement.clientTop, left: offest.left + window.pageXOffset - docElement.clientLeft }; }
offset限定父类范围
function myOffest(obj,parentObj){ var top=0,left=0; if(obj){ while(obj.offsetParent){ if(parentObj&&obj&&obj.className.indexOf(parentObj)!=-1){ break; } left += obj.offsetLeft; obj = obj.offsetParent; } } //console.log("222 55 最后obj---",obj) return{ left : left } }
getOffsetParent
function getOffsetParent(element) { // NOTE: 1 DOM access here var offsetParent = element && element.offsetParent; var nodeName = offsetParent && offsetParent.nodeName; if (!nodeName || nodeName === 'BODY' || nodeName === 'HTML') { if (element) { return element.ownerDocument.documentElement; } return window.document.documentElement; } // .offsetParent will return the closest TD or TABLE in case // no offsetParent is present, I hate this job... //console.log("window.getComputedStyle(offsetParent, null).position",window.getComputedStyle(offsetParent, null).position) if (['TD', 'TABLE'].indexOf(offsetParent.nodeName) !== -1 &&window.getComputedStyle(offsetParent, null).position=== 'static') { return getOffsetParent(offsetParent); } return offsetParent; }
function getOffsetParent(elem) { var doc = elem.ownerDocument||document; var offsetParent = elem.offsetParent ; //console.log(elem,"offsetParent",offsetParent) var position= offsetParent&&window.getComputedStyle(offsetParent)[ "position"] while (offsetParent &&(offsetParent.nodeName !== 'HTML' ) &&position=== "static") { offsetParent = offsetParent.offsetParent; } var last=offsetParent|| doc.documentElement; return offsetParent|| doc.documentElement; }
//https://github.com/jquery/jquery/blob/main/src/offset.js
get_position
function get_position(elem) { var offsetParent, offset, elem = elem, parentOffset = { top: 0, left: 0 }; // Fixed elements are offset from window (parentOffset = {top:0, left: 0}, // because it is its only offset parent if (window.getComputedStyle(elem)['position'] === "fixed" ) { // Assume getBoundingClientRect is there when computed position is fixed offset = elem.getBoundingClientRect(); } else { // Get *real* offsetParent offsetParent =getOffsetParent(elem); // console.log("offsetParent nodeName",offsetParent.nodeName) //console.log("eaas",eaas) // console.log("offsetParent2",offsetParent2) // Get correct offsets offset = getOffest(elem); if ( offsetParent.nodeName !="html" ) { parentOffset =getOffest(offsetParent);; } var borderTopWidth=window.getComputedStyle(offsetParent)['borderTopWidth']||0; var borderLeftWidth=window.getComputedStyle(offsetParent)['borderLeftWidth']||0; // Add offsetParent borders parentOffset.top += parseFloat(borderTopWidth); parentOffset.left += parseFloat(borderLeftWidth) ; // Subtract parent offsets and element margins // 可以看出,$().position()的本质是目标元素的offset()减去父元素的offset(),同时还要算上目标元素的margin,因为盒子模型(关键)。 //(注意:offset()的本质是getBoundingClientRect()的top、left + pageYOffset、pageXOffset) } // Subtract parent offsets and element margins var marginTop=window.getComputedStyle(elem)['marginTop']||0; var marginLeft=window.getComputedStyle(elem)['marginLeft']||0; console.log(marginTop,"marginLeft",marginLeft) var top2=parseFloat(offset.top||0) - parseFloat(parentOffset.top||0) - parseFloat(marginTop) ; var left2=parseFloat(offset.left||0) - parseFloat(parentOffset.left||0) - parseFloat(marginLeft) return { top: top2 , left: left2 }; }
setOffset
function setOffset(elem,props,pixel){ var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition; //console.log("setOffset---",elem) //获取元素的position属性的值 var position =window.getComputedStyle(elem)['position']; if ( position === "static" ) { elem.style.position = "relative"; } //{left:8,top:16} curOffset = getOffest(elem);; //0px curCSSTop = window.getComputedStyle(elem)['top']||0;; //0px curCSSLeft = window.getComputedStyle(elem)['left']||0; calculatePosition = ( position === "absolute" || position === "fixed" ) &&( curCSSTop + curCSSLeft ).indexOf( "auto" ) > -1; if (calculatePosition ) { // curPosition = elem.position(); curPosition = get_position(position); curTop = parseFloat(curPosition.top)|| 0; curLeft= parseFloat(curPosition.left)|| 0; } else { //0 0 curTop = parseFloat( curCSSTop ) || 0; curLeft = parseFloat( curCSSLeft ) || 0; } //解析: //(1)先判断当前元素的 position 的值,没有设置 position 属性的话,默认为 relative,并获取元素的 top、left 属性的值 //(2)返回一个对象 obj,obj 的 top 是参数的 top - 默认偏移(offset)的 top + position 设置的 top(没有设置,默认为0),obj 的 left 同理。 // 也就是说 offset({top:15,;eft:15}) 的本质为: // 参数的属性减去对应的默认offset属性加上position上的对应属性。 //如果传的参数的有top值 //参数 top - offset().top + element.top var top = ( props.top - parseFloat(curOffset.top) ) + curTop; //参数 left - offset().left + element.left var left = ( props.left - parseFloat(curOffset.left) ) + curLeft; var pixel=pixel||"px" //console.log("top",top,'left',left) elem.style.top= top+pixel; elem.style.left= left+pixel; }
setOffset(p2,{top:200,left:position.left})
$(".p1").offset({top:200,left:position.left});
getOffsetRect 获取大小
function getOffsetRect(element) { var elementRect = { width: element.offsetWidth, height: element.offsetHeight, left: element.offsetLeft, top: element.offsetTop }; elementRect.right = elementRect.left + elementRect.width; elementRect.bottom = elementRect.top + elementRect.height; // 位置 return elementRect; }
找到指定父类元素
function getParent(element, className, limitClasss) { var nowName = element.getAttribute("class") || ""; var isFind = nowName && nowName.indexOf(className) !== -1 || limitClasss && nowName && nowName .indexOf(limitClasss) !== -1; if (isFind || !element.parentNode) { return element; // 已达到文档根元素,未找到指定父元素 } return getParent(element.parentNode, className); // 继续向上查找 }
找到子元素在父元素的索引值
function getChildIndex(child) { var parent = child.parentNode; for (var i = 0; i < parent.children.length; i++) { if (parent.children[i] === child) { return i; } } return -1; // 如果找不到子元素,返回-1 }
var liEle = getParent(target, 'li-item', 'ul-item');
var liIndex = 0;
if (liEle) {
liIndex = getChildIndex(liEle);
console.log("liIndex", liIndex)
}
数组 forEach的实现
var unboundForEach = Array.prototype.forEach,forEach = Function.prototype.call.bind(unboundForEach);
<div id="box3"> <p class="klasses">one</p> <p class="klasses">two</p> <p class="klasses">three</p> </div> <script> var unboundForEach = Array.prototype.forEach,forEach = Function.prototype.call.bind(unboundForEach); var box3=document.getElementById("box3"); var klasses=document.querySelectorAll('.klasses') forEach(klasses, function (el) { el.addEventListener('click', function(){ alert('点击我的序号'+this.innerHTML); //点击我的序号 one }); }); </script>
jquery的静态方法 $.each
function jQueryEach(object, callback, args) { var name, i = 0, length = object.length; if (args) { if (length == undefined) { for (name in object) { if (callback.apply(object[name], args) === false) { break } } } else { for (; i < length;) { if (callback.apply(object[i++], args) === false) { break } } } } else { if (length == undefined) { for (name in object) { if (callback.call(object[name], name, object[name]) === false) { break } } } else { for (var value = object[0]; i < length && callback.call(value, i, value) !== false; value = object[++i]) {} } } return object };
var arr1 = [ "one", "two", "three", "four", "five" ]; jQueryEach(arr1, function(index){ console.log(arr1[index]); // alert(this) }); var obj = { one:'张珊', two:'李四', three:3, four:4, five:'王五' }; jQueryEach(obj, function(key, val) { console.log('val'+obj[key]); });
Array.prototype.forEach2=function(fun,context){ var len=this.length; var context=arguments[1]; //即使为undefined,call函数也正常运行。 if(typeof fun !=="function"){ throw "输入正确函数!"; } for(var i=0;i<len;i++){ fun.apply(context,[i,this[i],this]); //fun.call(context,i,this[i],this); } }; var arr2=[5,6,7]; //arr.forEach2(function(item,index,arr){ //console.log(item,index,arr); //});
hover 方法
function hover(myElement,fnOver,fnOut,offEventFlag){ var startX=0; var startY=0; var startT=null; var isEnter=false; // 防止多次触发鼠标移入移出事件 function checkHover(e, target) { var e=e || window.event; var type=e.type.toLowerCase(); var related; // if (!related || (related !== this && !$.contains(this, related)) if (type == "mouseover") { related=e.relatedTarget||e.fromElement; return (!related || (related !== target && !contains(target, related))); } else { related=e.relatedTarget||e.toElement; return (!related || (related !== target && !contains(target, related))); } } function contains(a,b){ return a.contains ? a != b && a.contains(b) :!!(a.compareDocumentPosition(b) & 16); } myElement.addEventListener("mouseover",enterHandle,false) myElement.addEventListener("mouseout",leaveHandle,false) myElement.addEventListener("touchstart",enterHandle,false) function enterHandle(e){ var e=e || window.event; var type=e.type.toLowerCase() var touch ; if(e.touches){ touch = e.touches[0]; }else { touch = e; } //console.log("11touch",e.touch) startX = touch.clientX||touch.pageX ; startY = touch.clientY ||touch.pageY; startT=+new Date; isEnter=true; //console.log("1enterHandle",checkHover(e,this)) if(type==="mouseover"&&checkHover(e,this)||type==="touchstart"){ //console.log("2enterHandle",checkHover(e,this)) fnOver.call(this,e,type) //do someting... } if(type==="touchstart"){ document.addEventListener("touchend",leaveHandle,false) document.addEventListener("touchcancel",leaveHandle,false) } } function leaveHandle(e){ //console.log("leaveHandle") var e=e || window.event; var type=e.type.toLowerCase() var touch ; if(e.touches||e.changedTouches){ touch = e.touches[0]||e.changedTouches[0]; }else { touch = e; } var endX = touch.clientX||touch.pageX ; var endY = touch.clientY ||touch.pageY; var endT=+new Date; var canEnd=(endT - startT) < 300&&Math.abs(endX - startX) <25&&Math.abs(endY - startY) < 25; //var canEnd=Math.abs(endX - startX) <25&&Math.abs(endY - startY) < 25; if (isEnter&&type==='touchend') { fnOut.call(this,e,type) // e.preventDefault&&e.preventDefault(); document.removeEventListener("touchend",leaveHandle,false) document.removeEventListener("touchcancel",leaveHandle,false) //return false; } //type==='mouseout'&&checkHover(e,this) else if(type==='mouseout'&&checkHover(e,this)){ //console.log("mouseout",checkHover(e,this)) fnOut.call(this,e,type) //do someting... } startX=0; startY=0; isEnter=false; } var unbild=function(){ myElement.removeEventListener("mouseover",enterHandle,false) myElement.removeEventListener("mouseout",leaveHandle,false) myElement.removeEventListener("touchstart",enterHandle,false) //console.log("以销毁") } return unbild; } //---------------------
<style> *{ margin:0; padding:0;list-style: none; } body{height:4000px;} .div0{ width:200px; height:60px; margin:20px;border:1px solid #eee; user-select: none; } .div0:active{ background-color: rgba(0, 0, 0, .2); } .divtip{width:100px; height:60px; background-color: #999; color:#fff; margin:10px auto;display:none;text-align: center;} .div1{ width:200px; height:200px; border:1px solid red; } .div2 { width:100px; height:80px; border:1px solid #eee;background-color: #999; margin-left:100px ; } </style> <div style="width:300px;margin:10px auto;"> <div class="div0"> <div id="jtip" class="divtip">tip</div> </div> <div id="myElement" class="div1">鼠标悬停我 <div class="div2" id="childElement">子元素</div> </div> <button id="myElement2" style="margin-top:20px;height:30px;line-height:30px;background-color: #999;border: 0;"> 点击销毁 hover事件 </button> </div>
var element = document.getElementById('myElement'); var childElement = document.getElementById('childElement'); var myElement2=document.getElementById('myElement2'); var jtip = document.getElementById('jtip'); var timer2=null; var aaaEvent=hover(element,function(e,type){ if(timer2){ clearTimeout(timer2) } jtip.style.display="block" //console.log('进入',e,type); },function(e,type){ console.log('离开',e,type); timer2=setTimeout(function(){ jtip.style.display="none" },100) }) // console.log("aaaEvent",aaaEvent) myElement2.addEventListener('click', function(event) { console.log("销毁点击") aaaEvent(); });
--------
jQuery.fn.extend( { hover: function( fnOver, fnOut ) { return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); } } );
获取文档高度
//获取文档完整的高度 var getScrollHeight=function () { return Math.max(document.body.scrollHeight, document.documentElement.scrollHeight); } console.log('jquery文档高度'+jQuery(document).height()); //jquery文档高度1739 console.log('js文档高度'+getScrollHeight()); // js文档高度1739g滚动条
滚动条高度
document.getElementById("element").addEventListener("click",function(){ var scrollTop1 = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop; var scrollTop2 = window.scrollY||document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop; console.log('scrollTop1'+scrollTop1) console.log('scrollTop2'+scrollTop2) });
样式操作
// jQuery $('.el').addClass('class'); $('.el').removeClass('class'); $('.el').toggleClass('class'); // 原生方法 document.querySelector('.el').classList.add('class'); document.querySelector('.el').classList.remove('class'); document.querySelector('.el').classList.toggle('class'); // 原生方法 扩展添加多个 DOMTokenList.prototype.adds = function(tokens) { tokens.split(" ").forEach(function(token) { this.add(token); }.bind(this)); return this; }; // adds 添加多个 document.querySelector(".el").classList.adds("child1 child2 child3");
function hasClass(ele, cls) { return ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')); } function addClass(ele, cls) { if (!this.hasClass(ele, cls)) ele.className += " " + cls; } function removeClass(ele, cls) { if (hasClass(ele, cls)) { var reg = new RegExp('(\\s|^)' + cls + '(\\s|$)'); ele.className = ele.className.replace(reg, ' '); } }
获取jsonp
function getJSONP2(url,success,jsonpCallback,jsonp){ var jsonp=jsonp||"callback", cn=jsonpCallback||"jsonpCallback", url=url.toString(), dataString = url.indexOf('?') == -1? '?': '&', s=document.createElement("script"), head=document.head||document.getElementsByTagName("head")[0]; s.type="text/javascript"; s.charset="UTF-8";s.src=url+dataString+"_="+(+new Date)+"&"+jsonp+"="+cn; head.insertBefore(s,head.firstChild); console.log("src",s.src); setTimeout(function(){ window[cn]=function(data){ try{s.onload=s.onreadystatechange=function(){ var that=this; if((!that.readyState||that.readyState==="loaded"||that.readyState==="complete")){ success&&success(data); s.onload=s.onreadystatechange=null;if(head&&s.parentNode){ head.removeChild(s)}}}}catch(e){}finally{window[cn]=null}} },0)};
var url44="http://api.map.baidu.com/geocoder/v2/?sa=1&location=30.290466116991468,120.00192773949404&output=json&pois=1&latest_admin=1&ak=ABq5yEuwLp5Z4yWlRDGX3vEhxxjGDu8L"; getJSONP2(url44,function(data){ var data=data; data.regeocode=data.result; //var address = data.regeocode.formatted_address; console.log(data,"结果" , data.regeocode.formatted_address); })
//document.addEventListener('click',function(){ getJSONP("https://api.github.com/repos/HubSpot/pace?callback=",function(json){ alert('success!'+ json.data.id+'获取到的'+json.data.name); document.getElementById("testText").innerHTML='回调函数获取到了'+json.data.name; document.getElementById("testText").style.cssText='color:red;font-size:22px; border:1px solid #666' });
清楚字符串空格
function trimStr(str){return str.replace(/(^\s*)|(\s*$)/g,"");} function TrimAll(str,is_global){ //删除全部空格 var result; result = str.replace(/(^\s+)|(\s+$)/g,""); if(is_global.toLowerCase()=="g") { result = result.replace(/\s/g,""); } return result; }
tring.prototype.trim = function() { return this.replace(/(^\s*)|(\s*$)/g, ""); }; String.prototype.ltrim = function() { return this.replace(/(^\s*)/g, ""); }; String.prototype.rtrim = function() { return this.replace(/(\s*$)/g, ""); }; String.prototype.trimAll = function () { return this.replace(/\s+/g, ""); }
cookie的操作
function addCookie(objName,objValue,objHours){ var str = objName + "=" + escape(objValue); if(objHours > 0){ var date = new Date(); var ms = objHours*3600*1000; date.setTime(date.getTime() + ms); str += "; expires=" + date.toGMTString(); } str += "; path=/"; document.cookie = str; }; function getCookie (objName){ var arrStr = document.cookie.split("; "); for(var i = 0;i < arrStr.length;i ++){ var temp = arrStr[i].split("="); if(temp[0] == objName) return unescape(temp[1]); } };
原生ajax的操作
/** * ajax封装 * var xmlhttp = new YAjax(); * xmlhttp.request({ * url : "./demo.php", // get请求时 可以这样写 "./demo.php?name=zhangsan" * method : "POST", * data : "name=李四", // 支持json传值 {"name":"zhangsan"} get时不用该参数 * receiveType : "html", // json html or xml * timeout : 3000, // 3秒 async : true, //默认true 可省略 * beforeSend:function(){}, //请求之前回调函数 就得 这边beforesent s小写 beforesend * success : function(d) {alert(d);}, * error : function(xmlhttp){alert('timeout');} * }); */
function YAjax() { this._self = this; this.xmlhttp = this.init() } YAjax.prototype = { constructor: YAjax, init: function() { var xmlhttp = null; if (window.XMLHttpRequest) { xmlhttp = new XMLHttpRequest(); if (xmlhttp.overrideMimeType) { xmlhttp.overrideMimeType("text/xml") } } else { if (window.ActiveXObject) { var activexName = ["MSXML2.XMLHTTP", "Microsoft.XMLHTTP"]; for (var i = 0; i < activexName.length; i++) { try { xmlhttp = new ActiveXObject(activexName[i]); break } catch(e) {} } } } return xmlhttp }, extend: function(destination, source, override) { if (undefined == override) { override = true } if (typeof destination != "object" && typeof destination != "function") { if (!override) { return destination } else { destination = {} } } var property = ""; for (property in source) { if (override || !(property in destination)) { destination[property] = source[property] } } return destination }, json2String: function(jsonData) { var strArr = []; for (var k in jsonData) { strArr.push(k + "=" + jsonData[k]) } return strArr.join("&") }, request: function(opt) { var _self = this, isTimeout = false, timeFlag = 0, options = { url: "", data: "", method: "POST", receiveType: "html", timeout: 7000, async: opt.async || true, beforeSend: function() {}, success: function() { alert("define your success function") }, error: function(xmlhttp) {} }; if ("data" in opt) { if (typeof opt.data == "string") {} else { opt.data = this.json2String(opt.data) } } options = this.extend(options, opt); this.xmlhttp.onreadystatechange = function() { if (_self.xmlhttp.readyState == 2) { options.beforeSend && options.beforeSend.apply(_self.xmlhttp, arguments) } if (_self.xmlhttp.readyState == 4) { if (!isTimeout && _self.xmlhttp.status == 200) { clearTimeout(timeFlag); var t = options.receiveType.toLowerCase(); if (t == "html") { options.success(_self.xmlhttp.responseText) } else { if (t == "xml") { options.success(_self.xmlhttp.responseXML) } else { if (t == "json") { try { var obj = JSON.parse(_self.xmlhttp.responseText); options.success(obj) } catch(e) { var str = "(" + _self.xmlhttp.responseText + ")"; options.success(JSON.parse(str)) } } else {} } } } else { clearTimeout(timeFlag); options.error(_self.xmlhttp) } } }; clearTimeout(timeFlag); timeFlag = setTimeout(function() { if (_self.xmlhttp.readyState != 4) { isTimeout = true; _self.xmlhttp.abort(); clearTimeout(timeFlag) } }, options.timeout); this.xmlhttp.open(options.method.toUpperCase(), options.url, options.async); if (options.method.toUpperCase() == "POST") { this.xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); this.xmlhttp.send(options.data) } else { this.xmlhttp.send(null) } } };
原生延迟加载插件

/*! echo.js v1.7.0 | (c) 2015 @toddmotto | https://github.com/toddmotto/echo */ (function (root, factory) { if (typeof define === 'function' && define.amd) { define(function() { return factory(root); }); } else if (typeof exports === 'object') { module.exports = factory; } else { root.echo = factory(root); } })(this, function (root) { 'use strict'; var echo = {}; var callback = function () {}; var offset, poll, delay, useDebounce, unload,effectClass; var classList= 'classList' in document.documentElement ?1:0; var isHidden = function (element) { return (element.offsetParent === null); }; var inView = function (element, view) { if (isHidden(element)) { return false; } var box = element.getBoundingClientRect(); return (box.right >= view.l && box.bottom >= view.t && box.left <= view.r && box.top <= view.b); }; var debounceOrThrottle = function () { if(!useDebounce && !!poll) { return; } clearTimeout(poll); poll = setTimeout(function(){ echo.render(); poll = null; }, delay); }; echo.init = function (opts) { opts = opts || {}; var offsetAll = opts.offset || 0; var offsetVertical = opts.offsetVertical || offsetAll; var offsetHorizontal = opts.offsetHorizontal || offsetAll; var optionToInt = function (opt, fallback) { return parseInt(opt || fallback, 10); }; offset = { t: optionToInt(opts.offsetTop, offsetVertical), b: optionToInt(opts.offsetBottom, offsetVertical), l: optionToInt(opts.offsetLeft, offsetHorizontal), r: optionToInt(opts.offsetRight, offsetHorizontal) }; delay = optionToInt(opts.throttle, 80); useDebounce = opts.debounce !== false; effectClass=opts.effectClass; unload = !!opts.unload; callback = opts.callback || callback; echo.render(); if (document.addEventListener) { root.addEventListener('scroll', debounceOrThrottle, false); root.addEventListener('load', debounceOrThrottle, false); } else { root.attachEvent('onscroll', debounceOrThrottle); root.attachEvent('onload', debounceOrThrottle); } }; echo.render = function () { var nodes = document.querySelectorAll('img[data-echo], [data-echo-background]'); var length = nodes.length; var src, elem; var view = { l: 0 - offset.l, t: 0 - offset.t, b: (root.innerHeight || document.documentElement.clientHeight) + offset.b, r: (root.innerWidth || document.documentElement.clientWidth) + offset.r }; for (var i = 0; i < length; i++) { elem = nodes[i]; if (inView(elem, view)) { if (unload) { elem.setAttribute('data-echo-placeholder', elem.src); } if (elem.getAttribute('data-echo-background') !== null) { elem.style.backgroundImage = "url(" + elem.getAttribute('data-echo-background') + ")"; } else { elem.src = elem.getAttribute('data-echo'); } if (!unload) { if(effectClass){ if (classList){ elem.classList.add(effectClass); }else{ elem.className += " " + effectClass; } } elem.removeAttribute('data-echo'); elem.removeAttribute('data-echo-background'); } callback(elem, 'load'); } else if (unload && !!(src = elem.getAttribute('data-echo-placeholder'))) { if (elem.getAttribute('data-echo-background') !== null) { elem.style.backgroundImage = "url(" + src + ")"; } else { elem.src = src; } elem.removeAttribute('data-echo-placeholder'); callback(elem, 'unload'); } } if (!length) { echo.detach(); } }; echo.detach = function () { if (document.removeEventListener) { root.removeEventListener('scroll', debounceOrThrottle); } else { root.detachEvent('onscroll', debounceOrThrottle); } clearTimeout(poll); }; return echo; });
使用方法
<img src="img/blank.gif" alt="Photo" data-echo="img/photo.jpg"> <script src="../echo.js"></script> <script> echo.init({ offset: 100, throttle: 250, unload: false, callback: function (element, op) { console.log(element, 'has been', op + 'ed') } });
domready(fn)

function Domready(readyFn) { if (document.addEventListener) { document.addEventListener("DOMContentLoaded", function() { readyFn && readyFn() }, false) } else { var bReady = false; document.attachEvent("onreadystatechange", function() { if (bReady) { return } if (document.readyState == "complete" || document.readyState == "interactive") { bReady = true; readyFn && readyFn() } }); setTimeout(checkDoScroll, 1) } function checkDoScroll() { try { document.documentElement.doScroll("left"); if (bReady) { return } bReady = true; readyFn && readyFn() } catch (e) { setTimeout(checkDoScroll, 1) } } };
为元素添加on方法
Element.prototype.on = Element.prototype.addEventListener;
为元素添加trigger方法
HTMLElement.prototype.trigger = function (type, data) { var event = document.createEvent('HTMLEvents'); event.initEvent(type, true, true); event.data = data || {}; event.eventName = type; event.target = this; this.dispatchEvent(event); return this; };
获取文档完整的高度
//获取文档完整的高度 var getScrollHeight=function () { return Math.max(document.body.scrollHeight, document.documentElement.scrollHeight); }
获取当前可是范围的高度
//获取当前可是范围的高度 var getClientHeight=function () { var clientHeight = 0; if (document.body.clientHeight && document.documentElement.clientHeight) { clientHeight = Math.min(document.body.clientHeight, document.documentElement.clientHeight); } else { clientHeight = Math.max(document.body.clientHeight, document.documentElement.clientHeight); } return clientHeight; }
getScrollParent
function getScrollParent(element, includeHidden, documentObj) { let style = getComputedStyle(element); const excludeStaticParent = style.position === 'absolute'; const overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/; if (style.position === 'fixed') { return documentObj.body; } let parent = element.parentElement; while (parent) { style = getComputedStyle(parent); if (excludeStaticParent && style.position === 'static') { continue; } if (overflowRegex.test(style.overflow + style.overflowY + style.overflowX)) { return parent; } parent = parent.parentElement; } return documentObj.body; }
其他
'use strict'; module.exports = (object, onChange) => { const handler = { get(target, property, receiver) { try { return new Proxy(target[property], handler); } catch (err) { return Reflect.get(target, property, receiver); } }, defineProperty(target, property, descriptor) { onChange(); return Reflect.defineProperty(target, property, descriptor); }, deleteProperty(target, property) { onChange(); return Reflect.deleteProperty(target, property); } }; return new Proxy(object, handler); };
数组
Object.assign
/** * The Object.assign() method is used to copy the values of all enumerable own properties from one or more source * objects to a target object. It will return the target object. * This polyfill doesn't support symbol properties, since ES5 doesn't have symbols anyway * Source: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign * @function * @ignore */ if (!Object.assign) { Object.defineProperty(Object, 'assign', { enumerable: false, configurable: true, writable: true, value: function(target) { if (target === undefined || target === null) { throw new TypeError('Cannot convert first argument to object'); } var to = Object(target); for (var i = 1; i < arguments.length; i++) { var nextSource = arguments[i]; if (nextSource === undefined || nextSource === null) { continue; } nextSource = Object(nextSource); var keysArray = Object.keys(nextSource); for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) { var nextKey = keysArray[nextIndex]; var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey); if (desc !== undefined && desc.enumerable) { to[nextKey] = nextSource[nextKey]; } } } return to; } }); }
/**
* Get bounding client rect of given element
* @function
* @ignore
* @param {HTMLElement} element
* @return {Object} client rect
*/
function getBoundingClientRect(element) {
var rect = element.getBoundingClientRect();
// whether the IE version is lower than 11
var isIE = navigator.userAgent.indexOf("MSIE") != -1;
// fix ie document bounding top always 0 bug
var rectTop = isIE && element.tagName === 'HTML'
? -element.scrollTop
: rect.top;
return {
left: rect.left,
top: rectTop,
right: rect.right,
bottom: rect.bottom,
width: rect.right - rect.left,
height: rect.bottom - rectTop
};
}
-----jquery 源码阅读分析-------- start-------------
目标:根据jQuery的思路实现不用new实例化对象
首先用函数实现类,常规的方法:
var aQuery = function(age) { } aQuery.prototype = { name: function(){}, age: 18 } var a = new aQuery(); a.age; // 返回18
但jQuery未在外面用new就可以实例化对象,也就是让函数直接返回实例:
var aQuery = function(age) { return new aQuery(); // ←把new放进函数了! } aQuery.prototype = { name: function(){}, age: 18 } aQuery().age; // 但是,死循环了!
为了返回正确的实例,把aQuery类当作一个工厂方法,放到aQuery.prototype中来创建实例:
var aQuery = function(age) { return aQuery.prototype.init(); // ←专门搞个init来返回了! } aQuery.prototype = { init: function() { return this; }, name: function(){}, age: 18 } aQuery().age; // 返回18了!
但是,此时init中的this指向aQuery类,若把init函数也当作一个构造器,aQuery的prototype会被改变:
var aQuery = function(age) { return aQuery.prototype.init(age); }; aQuery.prototype = { init: function(s) { if (s) this.age = s; // ←age被改了! return this; }, name: function(){}, age: 18 }; var a = aQuery().age; // 未传值,根据原型对象的值,age是18 var b = aQuery(16).age; // 原型对象的age变为16 var c = aQuery(17).age; // 原型对象的age变为17 var d = aQuery().age; // 未传值,age仍为17不变 // “this指向aQuery的原型prototype,aQuery()得到的始终就是aQuery.prototype”,执行后aQuery()的age变为17
因此,要通过实例化init函数,让每个this指向各自的实例化对象:
var aQuery = function(age) { return new aQuery.prototype.init(age); // ←又把new放进来了! }; aQuery.prototype = { init:function(s){ if (s) this.age = s; return this; }, name:function(){}, age:18 }; var a = aQuery().age; // 未传值,返回undefined var b = aQuery(16).age; // 传入16,返回16 var c = aQuery(17).age; // 传入17,返回17 var d = aQuery().age; // 未传值,返回undefined // “this指向aQuery.prototype.init的实例, aQuery()得到的结果是aQuery.prototype.init的实例”,执行后aQuery()的age为undefined
但这样就访问不到aQuery的原型对象了;
为了让this各回各家的同时能够使用aQuery原型对象上的属性和方法,jQuery的骚操作出现了!
var aQuery = function(age) { return new aQuery.prototype.init(age); }; aQuery.prototype = { init:function(s){ if (s) this.age = s; return this; }, name:function(){}, age:18 }; aQuery.prototype.init.prototype = aQuery.prototype; // ←把aQuery的prototype赋给init的prototype! var a = aQuery().age; // 未传值,新生成的实例上没有age,访问原型对象的值,得到18 var b = aQuery(16).age; // 传入16,返回16 var c = aQuery(17).age; // 传入17,返回17 var d = aQuery().age; // 未传值,新生成的实例上没有age,访问原型对象的值,得到18 // “将init的原型和aQuery的原型关联起来,从而通过aQuery()能够访问到aQuery.prototype的属性”,执行后aQuery()的age本身应为undefined,访问原型对象得到18
extend的实现
jQuery.extend = jQuery.fn.extend = function() { var src, copyIsArray, copy, name, options, clone, target = arguments[0] || {}, // 常见用法 jQuery.extend( obj1, obj2 ),此时,target为arguments[0] i = 1, length = arguments.length, deep = false; // Handle a deep copy situation if ( typeof target === "boolean" ) { // 如果第一个参数为true,即 jQuery.extend( true, obj1, obj2 ); 的情况 deep = target; // 此时target是true target = arguments[1] || {}; // target改为 obj1 // skip the boolean and the target i = 2; } // Handle case when target is a string or something (possible in deep copy) if ( typeof target !== "object" && !jQuery.isFunction(target) ) { // 处理奇怪的情况,比如 jQuery.extend( 'hello' , {nick: 'casper})~~ target = {}; } // extend jQuery itself if only one argument is passed if ( length === i ) { // 处理这种情况 jQuery.extend(obj),或 jQuery.fn.extend( obj ) target = this; // jQuery.extend时,this指的是jQuery;jQuery.fn.extend时,this指的是jQuery.fn --i; } for ( ; i < length; i++ ) { // Only deal with non-null/undefined values if ( (options = arguments[ i ]) != null ) { // 比如 jQuery.extend( obj1, obj2, obj3, ojb4 ),options则为 obj2、obj3... // Extend the base object for ( name in options ) { src = target[ name ]; copy = options[ name ]; // Prevent never-ending loop if ( target === copy ) { // 防止自引用,不赘述 continue; } // Recurse if we're merging plain objects or arrays // 如果是深拷贝,且被拷贝的属性值本身是个对象 if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { if ( copyIsArray ) { // 被拷贝的属性值是个数组 copyIsArray = false; clone = src && jQuery.isArray(src) ? src : []; } else { 被拷贝的属性值是个plainObject,比如{ nick: 'casper' } clone = src && jQuery.isPlainObject(src) ? src : {}; } // Never move original objects, clone them target[ name ] = jQuery.extend( deep, clone, copy ); // 递归~ // Don't bring in undefined values } else if ( copy !== undefined ) { // 浅拷贝,且属性值不为undefined target[ name ] = copy; } } } } // Return the modified object return target;
-----jquery 源码阅读分析-------- end-------------
。。。。剩下的慢慢添加...