jQuery源码解析之你并不真的懂事件委托及target和currenttarget的区别

前言:请先回顾下我之前写的一篇文章:JavaScript之事件委托 一、事件委托(委派)含义:在#A上绑定click事件,但是让#B触发click事件,相当于在 #B 上假绑定了 click 事件 也就是说:#B 委托了 click 事件给了 #A(在 #A 上绑定) 举例: <div id="A" style="background-color: deeppink"> 这是A <div id="B" style="background-color: bisque"> 这是B <div id="C" style="background-color: aqua"> 这是C </div> <div id="D" style="background-color: blueviolet"> 这是D </div> </div></div> //在父元素上绑定click事件,但只能由子元素触发父元素上绑定的事件 $("#A").on("click" ,"#B",function (e) { console.log("点击了B,即B委托A的click事件被点击了") }) $("#A").on("click" ,"#C",function (e) { console.log(e,"点击了C,即C委托A的click事件被点击了") })二、jQuery 的事件委托顺序: 举例: (1)A、B、C 各自绑定了click事件 $("#A").on("click" ,function () { console.log("A被点击了") }) $("#B").on("click" ,function () { console.log("B被点击了") }) $("#C").on("click",function () { console.log("C被点击了") })点击 C,会依次执行 C、B、A 的click事件 ...

June 8, 2019 · 5 min · jiezi

jqzTree使用

传统网站使用jq-ztree比较多,做一些技术总结ztree网站api:http://www.treejs.cn/v3/api.php //引入css,自己的css,ztree的css<link rel="stylesheet" href="../../../css/main.css" type="text/css"><link rel="stylesheet" href="../../../css/zTreeStyle/zTreeStyle.css" type="text/css">//引入jq, ztree.core.js,<script type="text/javascript" src="../../../js/jquery-1.4.4.min.js"></script><script type="text/javascript" src="../../../js/jquery.ztree.core-3.5.js"></script>//如果勾选,引入ztree.excheck.js<script type="text/javascript" src="../../../js/jquery.ztree.excheck-3.5.js"></script>//如果编辑树【拖拽,删除,批量加载】,引入ztree.exedit.js。<script type="text/javascript" src="../../../js/jquery.ztree.exedit-3.5.js"></script><script>//编写配置代码</script>//dom文本中引用<div> //id存放树的区域,class需要添加ztree才会有效果 <ul id="treeDemo" class="ztree"> </ul></div>

June 6, 2019 · 1 min · jiezi

点击网页空白区域隐藏当前div弹框jquery内置方法closest

一. closest方法介绍1)定义closest先检查当前元素是否匹配,如果匹配则直接返回元素本身;如果不匹配就会逐级向上查找父元素,直到找到匹配选择器的元素。如果什么都没找到则返回一个空的jQuery对象。 2)closest与parent方法对比closest从当前元素开始向上匹配寻找,parent从父元素开始向下匹配寻找;closest逐级向上查找,直到发现匹配的元素后就停止了,parent一直查找到根元素,然后把匹配的元素放进一个集合中;closest返回0或1个元素,parent可能包含0个或多个元素。二. 实践:点击蒙版后隐藏当前div若点击时的对象在div上,返回的对象length为1;若点在div之外的区域,closest找不到匹配的元素返回对象length为0。因此可以实现当没有点击div时(返回对象length为0),隐藏div。 $('body').on('click', (ev)=>{ let dom1 = $(ev.target).closest('.box').length//0为没有匹配找到,1为找到 if (dom1 < 1) {//点击div之外时,将其隐藏 this.show = false //定义v-show="show"控制div显示和隐藏 }})

June 5, 2019 · 1 min · jiezi

addEventjs源码解析

前言:看两三遍即可。 在看 jQuery 源码时,发现了这段注释: //源码5235行 /* * Helper functions for managing events -- not part of the public interface. * Props to Dean Edwards' addEvent library for many of the ideas. */ jQuery.event = { }Dean Edwards 的 addEvent.js 库为 jQuery 的事件绑定提供了很多想法,我们就来看下 2005 年的 addEvent.js 。 〇、先复习下 Object 的内存地址指向的知识 let a={ } let b=a b[0]='111' console.log(a,'a55') //{0:'111'}b 改变属性,a 也会改变,因为 b 与 a 指向同一地址(b=a) 一、addEvent()作用:为目标元素绑定事件(如 click) ...

June 5, 2019 · 3 min · jiezi

2019-年了为什么我还在用-jQuery

译者按: 看来 jQuery 还是有一些用武之地的。 原文: Why I'm Still Using jQuery in 2019译者: Fundebug为了保证可读性,本文采用意译而非直译。翻译仅供学习探讨,不代表 Fundebug 观点。 许多人都在提倡: “直接用原生的 JavaScript 就好了,不需要 jQuery 了”。 You might not need jQuery尝试告诉我们,摆脱 jQuery 是一件很容易的事情。但是,它的第一个例子恰恰告诉我们用 jQuery 其实也不错,因为我们写了 10 行原生的 JavaScript 代码,其实只需要 1 行 jQuery 代码就够了。 很多 JavaScript 的 API,尤其是 DOM 相关的 API,挑战了我的审美哲学,直白点说,我觉得它们太糟糕了!el.insertAdjacentElement('afterend', other)当然也可以用,但是$(el).after(other)更加简洁。$()函数也没那么好看,我没有特别喜欢,但是它比原生的 API 好太多了。 你们如何获取某个元素的 sibling 呢?到底用nextSibling还是用nextElementSibling?它们有什么不同?各个浏览器分别支持哪个方法?当你忙着去 MDN 查文档的时候,我直接用 jQuery 的next和prev()就好了。 许多常用的 JavaScript 的标准 API 都挺奇怪的,这里我就不列出来了,大家去You might not need jQuery看看就知道了。 写代码的时候,我们总会需要使用一些常用的帮助函数,You might not need jQuery列举了很多,使用 jQuery 可以很方便的使用这些帮助函数,这样我们就不需要每次都去 Stack OverFlow 上去复制代码了。。。 ...

June 4, 2019 · 1 min · jiezi

jQuery源码解析之trigger

一、$().trigger()和$().triggerHandler() 的作用和区别 (1)trigger("focus") 触发被选元素上的指定事件(focus)以及事件的默认行为(比如表单提交);triggerHandler(xxx) 不会引起事件(比如表单提交)的默认行为 (2)trigger(xxx) 触发所有匹配元素的指定事件;triggerHandler(xxx) 只触发第一个匹配元素的指定事件 (3)trigger(xxx) 会冒泡;triggerHandler(xxx) 不会冒泡 二、$().trigger() $("#one").on("click",function () { console.log("one被点击了") }) $("#one").trigger('click')作用:看 一、(1) 源码: //触发type事件,data是自定义事件的额外参数 //源码9014行 trigger: function( type, data ) { return this.each( function() { jQuery.event.trigger( type, data, this ); } ); },解析:本质是调用的jQuery.event.trigger()方法 三、jQuery.event.trigger() 源码: //源码8850行 //type, data, this trigger: function( event, data, elem, onlyHandlers ) { var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, //冒泡路径数组 eventPath = [ elem || document ], //判断event是否有'type'属性,有则取event.type,没有则取event type = hasOwn.call( event, "type" ) ? event.type : event, //同上 namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; //当前元素 cur = lastElement = tmp = elem = elem || document; //文本内容或者是注释则不触发事件 // Don't do events on text and comment nodes if ( elem.nodeType === 3 || elem.nodeType === 8 ) { return; } //由focus/blur转变到focusin/out,现在不触发focus/blur事件 // focus/blur morphs to focusin/out; ensure we're not firing them right now //rfocusMorph:focusin focus|focusout blur if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { return; } //可以不看 if ( type.indexOf( "." ) > -1 ) { // Namespaced trigger; create a regexp to match event type in handle() namespaces = type.split( "." ); type = namespaces.shift(); namespaces.sort(); } //onclick,onfocus等等 ontype = type.indexOf( ":" ) < 0 && "on" + type; //event一般是字符串,所以一般是undefined //获取对应type类型的jQuery.event // Caller can pass in a jQuery.Event object, Object, or just an event type string event = event[ jQuery.expando ] ? event : //click,false new jQuery.Event( type, typeof event === "object" && event ); // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) //onlyHandlers一般为undefined //3 event.isTrigger = onlyHandlers ? 2 : 3; //"" event.namespace = namespaces.join( "." ); //null event.rnamespace = event.namespace ? new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : null; //清空event以防它被复用 // Clean up the event in case it is being reused event.result = undefined; //target属性为目标DOM元素 //我们一般取的e.target.value,也正是目标元素的值 if ( !event.target ) { event.target = elem; } //复制data并预先考虑event,创建handler集合 // Clone any incoming data and prepend the event, creating the handler arg list //简单点,就是 data=[event] data = data == null ? [ event ] : jQuery.makeArray( data, [ event ] ); //赋值有需要特殊处理的type // Allow special events to draw outside the lines special = jQuery.event.special[ type ] || {}; if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { return; } //提前确定事件冒泡的路径 // Determine event propagation path in advance, per W3C events spec (#9951) //冒泡至document,再到window;关注全局的ownerDocument // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { //click bubbleType = special.delegateType || type; //clickclick //如果不是focus/blur的话,获取它的父元素 if ( !rfocusMorph.test( bubbleType + type ) ) { cur = cur.parentNode; } //for循环的语法(a; b; c) //a在单次循环开始前执行 //b是单次循环的条件(这里即cur存在) //c是单次循环结束后执行 for ( ; cur; cur = cur.parentNode ) { console.log(cur,'cur8967') //将目标元素的祖先元素都push进数组 eventPath.push( cur ); tmp = cur; } //只有当tmp是document时,将window加上 // Only add window if we got to document (e.g., not plain obj or detached DOM) if ( tmp === ( elem.ownerDocument || document ) ) { eventPath.push( tmp.defaultView || tmp.parentWindow || window ); } } //触发冒泡机制 // Fire handlers on the event path i = 0; //e.stopPropagation()这是阻止冒泡的方法 //isPropagationStopped() 检查是否阻止冒泡了,返回boolean while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { lastElement = cur; event.type = i > 1 ? bubbleType : special.bindType || type; console.log(i,'lastElement8987') // jQuery handler //( dataPriv.get( cur, "events" ) || {} )[ event.type ] // 先判断cur元素的events是否有绑定click //dataPriv.get( cur, "handle" ) //再获取cur元素的click事件处理程序 //获取目标元素的触发事件的事件处理程序 handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] && //获取触发事件的处理程序 dataPriv.get( cur, "handle" ); /*让冒泡元素执行handle,这行代码是触发冒泡机制的关键*/ /*在执行click事件的处理程序后,自然就会执行e.stopPropagation(), * 从而让event.isPropagationStopped()=true*/ if ( handle ) { handle.apply( cur, data ); } //接下来处理原生的事件及处理程序 //click为onclick // Native handler handle = ontype && cur[ ontype ]; //如果有绑定原生onclick事件的话 if ( handle && handle.apply && acceptData( cur ) ) { //执行onclick事件的处理程序 event.result = handle.apply( cur, data ); if ( event.result === false ) { //阻止元素的默认行为(如提交表单submit) event.preventDefault(); } } } event.type = type; //如果没有人阻止默认行为的话,现在就阻止 /*比如触发<a>的click事件,但不会跳转*/ // If nobody prevented the default action, do it now if ( !onlyHandlers && !event.isDefaultPrevented() ) { if ( ( !special._default || special._default.apply( eventPath.pop(), data ) === false ) && acceptData( elem ) ) { //在目标上,用重复的命名调用原生DOM事件,会在window层面上影响其他元素 // Call a native DOM method on the target with the same name as the event. // Don't do default actions on window, that's where global variables be (#6170) if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { //当我们触发FOO事件(如click)时,不要重复触发它的onFOO(onclick)事件 // Don't re-trigger an onFOO event when we call its FOO() method tmp = elem[ ontype ]; //将jQuery对象的onclick属性置为null //比如<a>就不会去跳转了 if ( tmp ) { elem[ ontype ] = null; } //阻止重复触发同样的事件,因为我们已经把它冒泡了 // Prevent re-triggering of the same event, since we already bubbled it above jQuery.event.triggered = type; //如果已经执行阻止冒泡了,则为window添加阻止冒泡的监听 if ( event.isPropagationStopped() ) { lastElement.addEventListener( type, stopPropagationCallback ); } console.log(elem[ type ],'type9053') //执行type事件 elem[ type ](); if ( event.isPropagationStopped() ) { lastElement.removeEventListener( type, stopPropagationCallback ); } jQuery.event.triggered = undefined; if ( tmp ) { elem[ ontype ] = tmp; } } } } return event.result; },解析: ...

June 3, 2019 · 7 min · jiezi

jQuery源码解析之click的事件绑定

前言:这篇依旧长,请耐心看下去。 一、事件委托DOM有个事件流特性,所以触发DOM节点的时候,会经历3个阶段:(1)阶段一:Capturing 事件捕获(从祖到目标)在事件自上(document->html->body->xxx)而下到达目标节点的过程中,浏览器会检测 针对该事件的 监听器(用来捕获事件),并运行捕获事件的监听器。 (2)阶段二:Target 目标浏览器找到监听器后,就运行该监听器 (3)阶段三:Bubbling 冒泡(目标到祖)在事件自下而上(document->html->body->xxx)到达目标节点的过程中,浏览器会检测不是 针对该事件的 监听器(用来捕获事件),并运行非捕获事件的监听器。 二、$().click()作用:为目标元素绑定点击事件 源码: //这种写法还第一次见,将所有鼠标事件写成字符串再换成数组 //再一一绑定到DOM节点上去 //源码10969行 jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + "change select submit keydown keypress keyup contextmenu" ).split( " " ), function( i, name ) { //事件绑定 // Handle event binding jQuery.fn[ name ] = function( data, fn ) { return arguments.length > 0 ? //如果有参数的话,就用jQuery的on绑定 this.on( name, null, data, fn ) : //否则使用trigger this.trigger( name ); }; } );解析:可以看到,jQuery 将所有的鼠标事件都一一列举了出来,并通过jQuery.fn[ name ] = function( data, fn ) { xxx } ...

May 27, 2019 · 6 min · jiezi

jQuery源码解析之addClassremoveClasstoggleClass和hasClass

一、$().addClass()作用:向目标元素添加一个或多个类名 源码: //向目标元素添加一个或多个类名 //源码8401行 addClass: function( value ) { var classes, elem, cur, curValue, clazz, j, finalValue, i = 0; //如果addClass(value)的value是一个function //那么就通过call让目标元素this调用该function if ( isFunction( value ) ) { return this.each( function( j ) { // function(index,currentclass) // index 对应 j,作用是获取多个目标元素的下标; // currentClass 对应 getClass(this),作用是获取当前元素的类名,方便加空格 jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); } ); } //将(多个)类名转为数组形式 classes = classesToArray( value ); if ( classes.length ) { //多个目标元素 while ( ( elem = this[ i++ ] ) ) { //获取当前值 curValue = getClass( elem ); //如果目标元素是元素节点并且用空格左右包起来 " "+value+" " cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); if ( cur ) { j = 0; //一个个类名 while ( ( clazz = classes[ j++ ] ) ) { //当前元素没有和要添加的类名重复的话就添加 if ( cur.indexOf( " " + clazz + " " ) < 0 ) { cur += clazz + " "; } } //最后,确认经过处理后的类名集合是否和处理前的类名集合相同 //如果相同,就setAttribute,重新渲染,否则不重新渲染(性能优化) // Only assign if different to avoid unneeded rendering. finalValue = stripAndCollapse( cur ); if ( curValue !== finalValue ) { //最后通过setAttribute,添加类名 elem.setAttribute( "class", finalValue ); } } } } return this; },解析:(1)getClass()作用:获取目标元素的类名 ...

May 24, 2019 · 5 min · jiezi

前端小知识10点2019518

1、当给数组的index赋负数或小数时,数组的长度有无变化? let arr=[] arr[10]=11 console.log(arr.length); //11 arr[-1]=-1 console.log(arr.length) //11 arr[3.14]=3.14 console.log(arr.length) //11 //=================================== let arr1=[] arr1[2.1]=2.1 console.log(arr1[2.1],'arr144') //2.1 console.log(arr1.length,'arr145') //0 arr1[1]=1 console.log(arr1.length,'arr147') //2 //======================== let arr2=[] arr2[-1]=-1 console.log(arr2.length,'arr253') //0 //======================== let arr3=[] arr3[5]=5 console.log(arr3.length,'arr258') //6由此可见,array的length属性只计算非负整数下标!不计算负数、小数 2、antd-pro 项目热更新慢并且是在95%(emitting)时卡住怎么办? 本人实际上是less文件里多写了个逗号。。。。???? 3、less 子类名使用 active span { position: relative; .leftIcon{ border-radius: 17px; } &.active { background:rgba(94,112,231,1); .leftIcon{ background:rgba(255,255,255,1); } } }4、antd 的 Spin 组件不认识 undefined(Spin 组件的 spinning 属性只对 true/false 生效),如果是 undefined 状态会是一直读取的状态 ...

May 18, 2019 · 1 min · jiezi

jQuery源码解析之width

一、在讲之前,先弄清 boxSizing 属性(1)box-sizing 是默认值 "content-box" <body><script src="jQuery.js"></script><div id="pTwo" style="width: 55px; border:1px red solid;">这是divTwo</div><script> $("#pTwo").width() //55</script></body>$().width()的值是 55 (2)box-sizing 是 "border-box" <div id="pTwo" style="width: 55px; box-sizing: border-box; border:1px red solid;">这是divTwo</div>$().width()的值是 53 因为 border-box 是包括 border、padding、content 的,而 content-box 只包括 content。可想而知,jQuery的$().width() 中也包含了对 borderBox 的判断。 注意下div标签的默认值 二、$().width()作用:获取目标元素的宽度 源码: //源码7033行 //$.each(obj,callback(index,item){}) jQuery.each( [ "height", "width" ], function( i, dimension ) { //i:0 dimension:height //i:1 dimension:width //cssHooks是用来定义style方法的 jQuery.cssHooks[ dimension ] = { //读 //$().width() //参数:elem:目标DOM元素/computed:true/extra:"content" get: function( elem, computed, extra ) { console.log(elem, computed, extra,'extra7040') if ( computed ) { // 某些元素是有尺寸的信息的,如果我们隐式地显示它们,前提是它必须有一个display值 // Certain elements can have dimension info if we invisibly show them // but it must have a current display style that would benefit // 上面这句话的意思是,某个元素用display:none,将它从页面上去掉了,此时是获取不到它的宽度的 // 如果要获取它的宽度的话,需要隐式地显示它们,比如display:absolute,visible:hidden // 然后再去获取它的宽度 // block:false // none:true // rdisplayswap的作用是检测 none和table开头的 return rdisplayswap.test( jQuery.css( elem, "display" ) ) && // 兼容性的考虑,直接看 getWidthOrHeight // Support: Safari 8+ // Table columns in Safari have non-zero offsetWidth & zero // getBoundingClientRect().width unless display is changed. // Support: IE <=11 only // Running getBoundingClientRect on a disconnected node // in IE throws an error. // display为none的话,elem.getBoundingClientRect().width=0 // elem.getClientRects() 返回CSS边框的集合 // https://developer.mozilla.org/zh-CN/docs/Web/API/Element/getClientRects ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? swap( elem, cssShow, function() { return getWidthOrHeight( elem, dimension, extra ); } ) : //$().width()情况 //dimension:width/extra:"content" getWidthOrHeight( elem, dimension, extra ); } }, };} );解析:(1)box-sizing 是默认值,并且 display 不为 none① rdisplayswap作用:检测目标元素的display属性的值 是否为none或以table开头 ...

May 13, 2019 · 9 min · jiezi

前端插件库

原文链接:前端插件库站点:前端开发文档博客:前端插件库前端插件库DataTables官网:https://www.datatables.net/ DataTables是jQuery的JavaScript函数库,目的是强化表格操作(如搜索、排序),并自动加入组件引入表格中,使用非常灵活简便。 LazyLoad官网:Lazy Load延伸:Vanilla JavaScript Lazy Load Plugin Lazy Load帮助高度较长的网页进行延迟载入图片,尚未浏览到该部分时,不会载入视角外的图片,提高效率。 lightSlider官网:JQuery lightSliderGithub:sachinchoolur/lightslider JQuery lightSlider是一个轻量且响应式的跑马灯/幻灯片,附有缩图导览。另外还有相本功能、垂直跑马灯,应用层面广泛。 alertify.js官网:https://fabien-d.github.io/alertify.js/Github:alertify.js alertify.js是为了美化通知信息而生的JavaScript框架。 pickadate.js-日期/时间选择器官网:pickadate.js chosen官网:ChosenGithub:chosen Chosen是一个jQuery的插件,目的是让又长又笨重的下拉式选单变得更友善。 typeahead.jsGithub:typeahead.js 由Twitter推出,灵活扎实的建议列表函数库。 textillate.js官网:textillate.jsGithub:jschr/textillate textillate.js结合了很多很棒的函数库,简单应用CSS3动画特效。 Chart.js官网:Chart.js 对设计师或开发人员,浅显易懂的JavaScript图标应用。 highlight.js官网:highlight.jsGithub:highlight.js 针对Web程序所做的高亮显示上色。 jquery-loading官网:jquery-loading 起源于为了在读取或运行中,锁住特定对象,并同时保持让浏览者可以操作页面的其他部分。 jquery-validation官网:jQuery ValidationGithub:jquery-validation jQuery Validation插件让用户端检查表单变得更容易,并提供大量的定制化设定,无论导入新开发或是现有的项目都是很好的选择。 Moment.js官网:Moment.js 轻量级专门解析、验证、操作、格式化日期的JavaScript函数库,Moment.js是为浏览器和Node.js而设计,所有组件都可以在这两个环境下运行。 Animate.css官网:Animate.css 纯CSS,无需JavaScript,支持多浏览器的动画特效,即插即用。 Ace官网:Ace Ace是透过JavaScript所开发的线上程序语言编辑器插件,无论功能和性能都类似一般编辑器(Sublime、Vim和TextMate等),导入进任何网页或JavaScript应用程序都相当容易。值得一提的是Ace是由Cloud9 IDE的团队维护,并且是Mozilla Skywriter(Bespin)项目的延伸品,品质值得信赖。 PDF.js.js官网:PDF.js PDF.js是一个由HTML构建的PDF阅读器,由Mozilla Labs所推广,目标是建立一个通用的PDF平台。 reveal.js-网页变简报官网:REVEAL.JS Intro.js-网站导航官网:Intro.js 优化网站的导航功能,提供步骤指南给浏览者,强化网站的用户体验。 Fine Uploader-拖拽上传官网:fine-uploader Github:fine-uploader ...

May 12, 2019 · 1 min · jiezi

jQuery源码解析之position

position()作用:返回被选元素相对于父元素(parent)的偏移坐标 使用:直接调用$().position()即可,该方法没有 arguments(参数对象) <body><script src="jQuery.js"></script><p id="pTwo">这是divTwo</p><script> $("#pTwo").position() //{top: 0, left: 8}</script></body>源码: // 返回被选元素相对于父元素(parent)的偏移坐标 // 可以理解成被选元素设置为absolute, // 然后设置left、top的值就是相对于父元素的偏移坐标 // 源码10571行 // position() relates an element's margin box to its offset parent's padding box // This corresponds to the behavior of CSS absolute positioning position: function() { // 如果DOM元素不存在,直接返回 if ( !this[ 0 ] ) { return; } var offsetParent, offset, doc, elem = this[ 0 ], parentOffset = { top: 0, left: 0 }; // position:fixed elements are offset from the viewport, which itself always has zero offset // position:fixed的元素,是相对于浏览器窗口进行定位的, // 所以它的偏移就是getBoundingClientRect(),即获取某个元素相对于视窗的位置 if ( jQuery.css( elem, "position" ) === "fixed" ) { // Assume position:fixed implies availability of getBoundingClientRect offset = elem.getBoundingClientRect(); } // 除去position是fixed的情况 else { // 获取被选元素相对于文档(document)的偏移坐标 offset = this.offset(); // Account for the *real* offset parent, which can be the document or its root element // when a statically positioned element is identified doc = elem.ownerDocument; //定位目标元素的父元素(position不为static的元素) offsetParent = elem.offsetParent || doc.documentElement; // 如果父元素是<body>/<html>的话,将父元素重新定位为它们的父元素 // body的父元素是html,html的父元素是document while ( offsetParent && ( offsetParent === doc.body || offsetParent === doc.documentElement ) && jQuery.css( offsetParent, "position" ) === "static" ) { offsetParent = offsetParent.parentNode; } // 如果定位父元素存在,并且不等于目标元素,并且定位元素类型是 "元素类型" if ( offsetParent && offsetParent !== elem && offsetParent.nodeType === 1 ) { // Incorporate borders into its offset, since they are outside its content origin parentOffset = jQuery( offsetParent ).offset(); // 这两行代码的意思是父元素的offset()要从padding算起,不包括border // 所以需要去掉border // jQuery.css( element, "borderTopWidth", true )的 true 表示返回数字,而不带单位 px parentOffset.top += jQuery.css( offsetParent, "borderTopWidth", true ); parentOffset.left += jQuery.css( offsetParent, "borderLeftWidth", true ); } } // Subtract parent offsets and element margins // 可以看出,$().position()的本质是目标元素的offset()减去父元素的offset(),同时还要算上目标元素的margin,因为盒子模型(关键)。 //(注意:offset()的本质是getBoundingClientRect()的top、left + pageYOffset、pageXOffset) return { top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ), left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true ) }; },解析:整体上看,是一个 if(...fixed) { } esle { } 语句(1)if ( jQuery.css( elem, "position" ) === "fixed" ) ...

May 5, 2019 · 2 min · jiezi

前端小知识10点201952

1、为什么 jQuery 整体上是一个匿名函数自调用? 因为匿名函数自执行里面的所有东西都是局部的,这样引用 jQuery 时,能防止和其他的代码冲突。 2、jQuery 静态方法和实例方法的区别 $() 是调用 jQuery 方法返回的一个 jQuery 对象,$() 调用的方法是实例方法,实例方法只能为 $() 所用如:$("li").each() $ 是 jQuery 函数(方法),$ 调用的方法是静态方法/工具方法,静态方法既可以给 $() 调用,也可以给原生 javascript 调用如:$().each() 3、为什么 typeof null 的结果是 object ? console.log(typeof null) //object在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null 代表的是空指针(大多数平台下值为 0x00),因此,null的类型标签也成为了 0,typeof null就错误的返回了"object"。MDN链接:https://developer.mozilla.org... 4、undefined 不是 js 的关键字我们习惯在项目里这样判断是否为 undefined name===undefined?'a':'b'但一旦手动定义 undefined 并且赋值了,那么会覆盖掉 js 的变量类型 undefined let undefined='a'console.log(undefined) //a所以,在做项目中,不要给变量起名为 undefined,并赋值 5、利用原型,来节省内存A: function person(name){ this.name=name this.showName=function(){ console.log(this.name) } }B: ...

May 3, 2019 · 1 min · jiezi

jQuery源码解析之offset

一、offset() 作用:返回被选元素相对于文档(document)的偏移坐标 二、三种情况使用: 1、$().offset() <body><script src="jQuery.js"></script><p id="pTwo">这是divTwo</p><script> $("#pTwo").offset() //{top: 16, left: 8}</script></body>源码: //返回目标元素相对于doucument的偏移坐标, //即距离浏览器左上角的距离 // 返回偏移坐标:$(selector).offset() // 设置偏移坐标:$(selector).offset({top:value,left:value}) // 使用函数设置偏移坐标:$(selector).offset(function(index,currentoffset)) // offset() relates an element's border box to the document origin //源码10500行 //options即参数 //arguments是参数对象 offset: function( options ) { // Preserve chaining for setter //如果是有参数的,参数是undefined则返回目标元素本身, //否则为每个目标元素设置options console.log(options,arguments,'arguments10476') //$().offset()不走这里 if ( arguments.length ) { console.log('aaa','vvv10507') return options === undefined ? this : this.each( function( i ) { //为每个目标元素设置options jQuery.offset.setOffset( this, options, i ); } ); } var rect, win, //获取DOM节点 elem = this[ 0 ]; if ( !elem ) { return; } // jQuery不支持获取隐藏元素的偏移坐标。 // 同理,也无法取得隐藏元素的 border, margin, 或 padding 信息 //所以如果元素是隐藏的,默认返回0值 // Return zeros for disconnected and hidden (display: none) elements (gh-2310) // Support: IE <=11 only // Running getBoundingClientRect on a // disconnected node in IE throws an error //对IE的特殊处理 if ( !elem.getClientRects().length ) { return { top: 0, left: 0 }; } // Get document-relative position by adding viewport scroll to viewport-relative gBCR //返回元素的大小及其相对于视口的位置 //https://developer.mozilla.org/zh-CN/docs/Web/API/Element/getBoundingClientRect rect = elem.getBoundingClientRect(); //返回所属文档的默认窗口对象(只读) //原点坐标 win = elem.ownerDocument.defaultView; //pageXOffset,pageYOffset 相当于 scrollX 和 scrollY //返回文档在窗口左上角水平 x 和垂直 y 方向滚动的像素 return { //16 0 // top: rect.top + win.pageYOffset, //8 0 left: rect.left + win.pageXOffset }; },解析:由于$().offset()没有参数,所以源码里的两个 if 可以忽略,所以offset()的本质即: ...

May 3, 2019 · 4 min · jiezi

无聊之作

话不多说,直接上代码 <html> <head> <title>倒计时</title></head><body> <div id="time1"></div> <p>-----------------------</p> <div id="time2"></div></body><script src="https://code.jquery.com/jquery-3.4.0.min.js"></script><script type="text/javascript"> window.onload = function () { endTime(); } function endTime() { var myDate = new Date(); var timestamp = Date.parse(myDate) / 1000; var date = myDate.getDate(); var year = myDate.getFullYear(); var month = myDate.getMonth() + 1; var endtime1 = year + '-' + month + '-' + date + ' 12:00:00'; var endtime2 = year + '-' + month + '-' + date + ' 18:00:00'; var time1 = Date.parse(new Date(endtime1)) / 1000; var time2 = Date.parse(new Date(endtime2)) / 1000; if(time1 > timestamp) { $('#time1').text(time1 - timestamp +'S'); } if(time2 > timestamp) { $('#time2').text(time2 - timestamp +'S'); } setTimeout("endTime()", 1000); }</script></html> ...

April 29, 2019 · 1 min · jiezi

typehead-ajax-format-relay

异步搜索提示的实现 /** * 异步搜索 * @type {Bloodhound} */var bestPictures = new Bloodhound({ datumTokenizer: Bloodhound.tokenizers.obj.whitespace('data'), queryTokenizer: Bloodhound.tokenizers.whitespace, remote: { url: GUI.data.API.searchUser, rateLimitWait: 700, prepare: function(query, settings) { console.log(query); settings.type = "POST"; settings.contentType = "application/x-www-form-urlencoded; charset=UTF-8"; settings.data = { keyName: query }; return settings; }, transform: function(response){ console.log(response); return response.data } }});$('#the-basics .typeahead').typeahead(null, { name: 'best-pictures', limit: '11', display: 'data', source: bestPictures, templates: { empty: [ '<div class="empty-message">', '没有查询到相关的记录', '</div>' ].join('\n'), suggestion: Handlebars.compile('<div class="result-item">{{name}} <span class="small">{{deptName}}</span></div>') }});

April 26, 2019 · 1 min · jiezi

如何使用-jq-接收-blob-数据

如何使用 jq 接收 blob 数据⭐️ 更多前端技术和知识点,搜索订阅号 JS 菌 订阅目前 jq 用的人还是挺多的,在一些简单的促销 h5 页面,用 jq 去实现一些简单的功能还是比较方便的。本文展示如何用 JQ 去请求一个 blob 对象的 img 图片并渲染到页面上 ???? 默认 jq 的 ajax 对象中的 dataType 无法设置返回资源为 blob 那么就需要手动设置,使其能够最终请求一个 blob 对象 解决办法:使用原生 XMLHttpRequestvar xhr = new XMLHttpRequest()xhr.onreadystatechange = function () { if (this.readyState == 4 && this.status == 200) { handler(this.response) console.log(this.response, typeof this.response) var img = document.getElementById('img') var url = window.URL || window.webkitURL img.src = url.createObjectURL(this.response) }}xhr.open('GET', 'https://httpbin.org/image/png')xhr.responseType = 'blob'xhr.send()这种方法直接使用了原生的 ajax ...

April 26, 2019 · 1 min · jiezi

jQuery源码解析之clone

前言:这篇讲完后,jQuery的文档处理就告一段落了,有空我把这部分整合下,发一篇文章目录。 一、示例代码 <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>jQuery源码解析之clone()</title></head><body><script src="jQuery.js"></script><div id="divTwo"> <p id="pTwo">这是divTwo <span id="spanTwo">这是spanTwo</span> </p></div><div id="divOne"></div><script> $("#pTwo").click(function () { alert("pTwo被点击了") }) $("#spanTwo").click(function (e) { //阻止冒泡 e.stopPropagation() alert("spanTwo被点击了") }) // $("#pTwo").clone().appendTo($("#divOne")) // $("#pTwo").clone(true,false).appendTo($("#divOne")) $("#pTwo").clone(true,true).appendTo($("#divOne"))</script></body></html>二、$().clone()作用:生成被选元素的副本,包含子节点、文本和属性 注意:$('div').clone(true) 表示克隆目标节点的事件和数据$('div').clone(true,true) 表示克隆目标节点及其子节点的事件和数据 源码: jQuery.fn.extend({ //克隆目标节点及其子节点 //dataAndEvents是否克隆目标节点的事件和数据,默认是false //deepDataAndEvents是否克隆目标节点子节点的事件和数据,默认值是dataAndEvents //源码6327行 clone: function( dataAndEvents, deepDataAndEvents ) { //默认是false dataAndEvents = dataAndEvents == null ? false : dataAndEvents; //默认是dataAndEvents deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; //循环调用jQuery.clone return this.map( function() { return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); } ); },});解析:可以看到,这里还是比较简单的,需要注意的就是参数deepDataAndEvents不填的话,其值是根据参数dataAndEvents的值来定的 ...

April 25, 2019 · 3 min · jiezi

jQuery 实现支持分级标题标签的文章目录

背景首个版本https://www.ouorz.com/366 修改意见鉴于单个 H 标签设置比较麻烦纠结,而且有主题用户反馈,索性改成可以分级的啦 代码var count_ti = count_in = count_ar = count_sc = count_hr = count_e = 1;var offset = new Array;$('.article-content h3').each(function () { //each获取h3内容 $('#article-index').html($('#article-index').html() + '<li id="ti' + (count_ti++) + '"><a onclick="$(\'body\').animate({ scrollTop: $(\'#in' + (count_hr++) + '\').offset().top - 100 }, 500);"><i class="czs-circle-l"></i> ' + $(this).eq(0).html() + '</a></li>'); $(this).eq(0).attr('id', 'in' + (count_in++)); //h3添加id offset[0] = 0; offset[count_ar++] = $(this).eq(0).offset().top; //h3位置存入数组 count_e++});if (count_e !== 1) { //若存在h3标签 $(window).scroll(function () { //滑动窗口时 var scroH = $(this).scrollTop() + 130; var navH = offset[count_sc]; //从1开始获取当前h3位置 var navH_prev = offset[count_sc - 1]; //获取上一个h3位置(以备回滑) if (scroH >= navH) { //滑过当前h3位置 $('#ti' + (count_sc - 1)).attr('class', ''); $('#ti' + count_sc).attr('class', 'active'); count_sc++; //调至下一个h3位置 } if (scroH <= navH_prev) { //滑回上一个h3位置,调至上一个h3位置 $('#ti' + (count_sc - 2)).attr('class', 'active'); count_sc--; $('#ti' + count_sc).attr('class', ''); } });} else { $('.index-div').css('display', 'none')}↑ JavaScript 代码 ...

April 21, 2019 · 1 min · jiezi

jQuery + Php 实现类似 Medium 的文章页内容批注评论功能

还没时间做 WordPress 插件… 背景偶然的机会,在准备做小半谈否 API 的时候,正在寻找用户人群时看到了利器 (liqi.io) 网站 他们使用了一款 2015年(貌似是)由一个国外开发者做的句子标记插件,实现了文章中点击句子即可标记并评论的功能 同样的 Medium 也有句子标记功能,还是蛮有用的 增加了互动,访客也能参与其中… 代码没有来得及整理,先贴代码 //内容数据都保存在当前文章自定义字段$stream_ids = get_post_meta($post->ID,'stream_ids',true);$stream_contents = get_post_meta($post->ID,'stream_contents',true);$stream_users = get_post_meta($post->ID,'stream_users',true);$stream_users = explode('######',$stream_users);$stream_ids = explode('######',$stream_ids);$stream_contents = explode('######',$stream_contents);$count1 = count($stream_ids);$count2 = count($stream_contents);$count3 = count($stream_users);↑ 文章页头部获取标记内容 <?php if(wp_is_mobile()) { $is_wap = 1; } global $current_user; $user_id = $current_user -> ID; if(empty($user_id)){ $login = 0; }else{ $login = 1; }?><div class="new-single-comment-fixed" id="comment_fixed" style="<?php if(!empty($stream_contents[0]) && !$is_wap) echo 'display:unset' ?>"> <?php if($login){ ?> <input type="text" name="mark_comment" value="请输入批注,按下Enter发送" id="mark_comment" style="display:none"> <?php } ?> <div style="text-align: right;font-size: 1.4rem;padding-bottom: 10px;padding-right: 15px;display:none;" id="comment_btn"> <button style="background: #444;color: #fff;padding: 3px 20px;border-radius: 2px;" id="send_comment">确定</button> <button style="background: #eee;padding: 3px 20px;border-radius: 2px;margin-left: 10px;" id="cancel_comment">取消</button> </div> <div class="new-single-comment-div-list" style="<?php if(empty($stream_contents[0])) echo 'display:none' ?>"> <?php if($count1 == $count2 && $count2 == $count3){ for($i=0;$i<$count3;$i++){ ?><div class="new-single-comment-div hover-mark-content" style="<?php if($count3 == 1 && $i == 0) echo 'margin-bottom: 0px;'; else echo 'margin-bottom: 20px;'; ?>" id="<?php echo $stream_ids[$i]; ?>"> <h4><?php echo get_avatar($stream_users[$i],96,'','user-avatar',array('width'=>120,'height'=>120,'rating'=>'X','class'=>array('new-single-comment-img'),'extra_attr'=>'title="user-avatar"','scheme'=>'https') ); ?><?php echo get_user_by('id',$stream_users[$i])->display_name; ?></h4> <span class="new-single-comment-p"><?php echo $stream_contents[$i]; ?></span> </div> <?php }} ?></div> </div>↑ 文章页展示评论区块 ...

April 21, 2019 · 4 min · jiezi

jQuery 实现一个文章阅读进度条功能

背景阅读进度虽然没啥具体的用处,但是我突然想起来了,随便做做也是极好的 ????获取元素 offset 高度、元素高度、滑动距离就能实现了 代码var content_offtop = $('.article-content').offset().top;var content_height = $('.article-content').innerHeight();$(window).scroll(function () { if (($(this).scrollTop() > content_offtop)) { //滑动到内容部分 if (($(this).scrollTop() - content_offtop) <= content_height) { //在内容部分内滑动 this.reading_p = Math.round(($(this).scrollTop() - content_offtop) / content_height * 100); } else { //滑出内容部分 this.reading_p = 100; //确保进度条铺满 } } else { //未滑到内容部分 this.reading_p = 0; //确保进度条不显示 } $('.reading-bar').css('width', this.reading_p + '%');});↑ JavaScript 代码

April 21, 2019 · 1 min · jiezi

es5中判断多个ajax全部执行完成后进行回调

$.when() jQuery中的api$.when($.ajax({ url: url, dataType: “jsonp”, data: {“movieId”: 248906}, success: function(data) { var data = data.data.data; var actorList = data.stars.split(’,’).join(’ | ‘); var type = data.category.split(’,’).join(’ | ‘); result.push({ “actorList”: actorList, “type”: type }) }, error: function(err) { console.log(err) }}), $.ajax({ url: url, dataType: “jsonp”, data: {“movieId”: 1212492}, success: function(data) { var data = data.data.data; var actorList = data.stars.split(’,’).join(’ | ‘); var type = data.category.split(’,’).join(’ | ‘); result.push({ “actorList”: actorList, “type”: type }) }, error: function(err) { console.log(err) }}), $.ajax({ url: url, dataType: “jsonp”, data: {“movieId”: 346765}, success: function(data) { var data = data.data.data; var actorList = data.stars.split(’,’).join(’ | ‘); var type = data.category.split(’,’).join(’ | ‘); result.push({ “actorList”: actorList, “type”: type }) }, error: function(err) { console.log(err) }})).done(function() { console.log(result)})全局变量判断var ajax_done_count = 0;function ajax_done() { if(ajax_done_count === 3) { // 渲染页面 } else { // 什么都不做 }}$.ajax({success: function(data) { ajax_done_count += 1; ajax_done();}})$.ajax({success: function(data) { ajax_done_count += 1; ajax_done();}})$.ajax({success: function(data) { ajax_done_count += 1; ajax_done();}})promise.all貌似在es5中也支持promise了 …我写了一个demovar url = ‘http://xxx/xxx’;var idList = [248906, 1212492, 346765, 341139];var result = [];var promiseArr = [];function createPromise (id) { return new Promise(function(resolve, reject) { $.ajax({ url: url, dataType: “jsonp”, data: {“movieId”: id}, success: function(data) { var data = data.data.data; resolve({ “actorList”: data.stars, “type”: data.category }); }, error: function(err) { reject(err); } }) }) }idList.map(function(i) { promiseArr.push(createPromise(i))})console.log(promiseArr)Promise.all(promiseArr).then(function(data) { result = data; console.log(result); // 渲染页面}).catch(function(err) { console.log(err);}) ...

April 15, 2019 · 2 min · jiezi

jQuery之getAll()和cleanData()

前言:看 jQuery 源码的一个痛点就是调用一个函数时,里面会调用 N 个其他函数,然后这 N 个函数里面又会调用 M 个其他其他函数。。本篇文章主要是对上篇文章—jQuery源码解析之detach()/empty()/remove()/unwrap() 中两个函数 getAll和cleanData() 进行解析。一、getAll(context, tag)作用:用来获取 context 上的 tag 标签,或者是将 context 和 context 里的 tag 标签的元素合并源码: //一般是传的node,‘script’ //应该是用来获取context上的tag标签,或者是将context和context里的tag标签的元素合并 //源码4893行 function getAll( context, tag ) { // Support: IE <=9 - 11 only // Use typeof to avoid zero-argument method invocation on host objects (#15151) var ret; console.log(context,typeof context.getElementsByTagName,typeof context.querySelectorAll,‘context4894’) //如果context存在getElementsByTagName的方法的话 if ( typeof context.getElementsByTagName !== “undefined” ) { //tag:script //从context中获取script标签的节点 ret = context.getElementsByTagName( tag || “” ) console.log(tag,ret,‘ret4897’) } //DocumentFragment没有getElementsByTagName方法,但有querySelectorAll方法 else if ( typeof context.querySelectorAll !== “undefined” ) { ret = context.querySelectorAll( tag || “” ); } else { ret = []; } console.log(nodeName( context, tag ),’nodeName4909’) //nodeName() 判断两个参数的nodename是否相等 if ( tag === undefined || tag && nodeName( context, tag ) ) { return jQuery.merge( [ context ], ret ); } return ret; }注意:DocumentFragment 没有getElementsByTagName方法,但有querySelectorAll方法!二、$.merge()作用:合并两个数组内容到第一个数组源码: // Support: Android <=4.0 only, PhantomJS 1 only // push.apply(_, arraylike) throws on ancient WebKit //源码461行 //将second合并到first后面 merge: function( first, second ) { var len = +second.length, j = 0, i = first.length; //依次将second的item添加到first后面 for ( ; j < len; j++ ) { first[ i++ ] = second[ j ]; } //first可能是类数组,所以需要更新下length属性 first.length = i; return first; },需要注意的是最后的 first.length = i三、cleanData()作用:清除元素节点上的事件和数据源码: //清除elems上的数据和事件 //源码6146行 cleanData: function( elems ) { var data, elem, type, //beforeunload/blur/click/focus/focusin/focusout/ //load/mouseenter/mouseleave/pointerenter/pointerleave special = jQuery.event.special, i = 0; for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { //允许的节点类型 if ( acceptData( elem ) ) { //当有事件绑定到elem后,jQuery会给elem一个属性dataPriv.expando //该属性上面就绑定了事件和数据 if ( ( data = elem[ dataPriv.expando ] ) ) { //如果data上有事件的话 if ( data.events ) { //逐个列举data上的事件,比如click for ( type in data.events ) { // 如果special中有data.events上的事件 if ( special[ type ] ) { //调用jQuery.event.remove方法,移除elem上的event类型 jQuery.event.remove( elem, type ); // This is a shortcut to avoid jQuery.event.remove’s overhead } //应该是自定义的事件 else { //本质即elem.removeEventListener(type,handle) jQuery.removeEvent( elem, type, data.handle ); } } } // Support: Chrome <=35 - 45+ // Assign undefined instead of using delete, see Data#remove //最后将元素的dataPriv.expando属性置为undefined elem[ dataPriv.expando ] = undefined; } //dataUser应该是用户绑定的事件 if ( elem[ dataUser.expando ] ) { // 将元素的dataUser.expando属性置为undefined // Support: Chrome <=35 - 45+ // Assign undefined instead of using delete, see Data#remove elem[ dataUser.expando ] = undefined; } } } }解析:① 依次判断 elems[i] 是否是元素节点/文档节点/对象② 再判断 elem 的 dataPriv.expando 属性是否有 events 属性③ 当 events 里有 jQuery.event.special 指定的 事件类型时,使用jQuery.event.remove(elem,type)移除事件和数据④ 反之,则使用jQuery.removeEvent(elem,type,data.handle)移除事件和数据⑤ 将 elem[dataPriv.expando]置为 undefined⑥ 将 elem[dataUser.expando]置为 undefined四、acceptData()作用:判断是否是指定的节点类型,返回 true/false源码: //判断是否是指定的节点类型 //只接受元素节点1,文档节点9,任意对象 //返回true/false //源码4178行 var acceptData = function( owner ) { // Accepts only: // - Node // - Node.ELEMENT_NODE // - Node.DOCUMENT_NODE // - Object // - Any return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); }注意:Object 类型的 nodeType 是 undefined五、$.removeEvent()作用:移除 elem 上的自定义监听事件源码: //移除elem上的自定义监听事件 //源码5599行 //jQuery.removeEvent(elem,type,data.handle) jQuery.removeEvent = function( elem, type, handle ) { // This “if” is needed for plain objects if ( elem.removeEventListener ) { elem.removeEventListener( type, handle ); } }本质即调用原生JS的removeEventListener()方法(完) ...

April 15, 2019 · 3 min · jiezi

jQuery源码解析之detach()/empty()/remove()/unwrap()

前言:unwrap() 的解析请看 jQuery源码解析之replaceWith()/unwrap()empty() 的解析请看 jQuery之text()的实现一、$().empty()作用:清除被选元素的所有子节点和内容,包括事件和数据注意:该方法不会移除被选元素本身或它的属性。源码: //源码6231行 empty: function() { var elem, i = 0; for ( ; ( elem = this[ i ] ) != null; i++ ) { //如果是元素节点的话 if ( elem.nodeType === 1 ) { // Prevent memory leaks //清空内容和事件,防止内存泄漏 jQuery.cleanData( getAll( elem, false ) ); // Remove any remaining nodes //清空节点所有内容 elem.textContent = “”; } } return this; },简单实现: $("#divTwo").empty() //相当于 function customEmpty() { let elem, i = 0,array=[] //DOM节点集合,querySelectorAll //注意for循环的写法 for (; (elem = this[i]) != null; i++) { //如果是元素节点的话,清空该节点的所有内容 if (elem.nodeType === 1) { //jQuery.cleanData( getAll( elem, false ) ); elem.textContent = “”; } } //单个DOM节点,querySelector if(this[i]===undefined){ if (this.nodeType === 1) { //jQuery.cleanData( getAll( elem, false ) ); this.textContent = “”; } } return this; } customEmpty.call(divTwo)二、$().remove()作用:移除被选元素自身,包括所有的数据、事件和子节点。源码: //移除被选元素,包括所有的文本和子节点,不会保留移除元素的副本 //源码6159行 remove: function( selector ) { return remove( this, selector ); },解析:可以看到,$().remove() 方法实际上调用的是外部的大的 remove() 方法,并且只传了两个参数this、selector第三个参数是 keepData,即是否保留被移除元素的事件和数据,没有传参即默认false,在下面的 detach() 方法也是调用的 remove() 方法,并且第三个参数传了 true detach: function( selector ) { return remove( this, selector, true ); },1、remove(): //源码6037行 //$().remove()调用:selector, undefined, undefined function remove( elem, selector, keepData ) { console.log(elem,selector,’elem6041’) var node, //remove()不带参数的话,nodes=目标元素 //$(“p”).remove("#pTwo") elem=$(“p”) selector=#pTwo nodes = selector ? jQuery.filter( selector, elem ) : elem, i = 0; console.log(nodes,’nodes6061’) for ( ; ( node = nodes[ i ] ) != null; i++ ) { //如果keepData不为true,并且node为元素节点,则清除数据和事件 if ( !keepData && node.nodeType === 1 ) { jQuery.cleanData( getAll( node ) ); } //如果父节点存在,则removeChild if ( node.parentNode ) { if ( keepData && jQuery.contains( node.ownerDocument, node ) ) { setGlobalEval( getAll( node, “script” ) ); } node.parentNode.removeChild( node ); } } return elem; }解析:① nodes 是经过处理后得到的要被移除的节点集合② 循环 nodes,依次去除 nodes[i] 的事件和数据③ 找到 nodes[i] 的父节点,并调用原生JS的 removeChild() 方法,去掉 nodes[i]简单实现: //无参数====== $("#divTwo").remove() //相当于 // jQuery.cleanData(getAll(divTwo)) divTwo.parentNode.removeChild(divTwo)当 $().remove() 有参数的话(比如:$(“p”).remove("#pTwo")),就会调用jQuery.filter( selector, elem )方法2、$.filter():作用:返回符合一定条件的元素,比如$(“p”).remove("#pTwo"),就是返回所有 <p> 标签中,id=‘pTwo’ 的元素节点的集合,filter() 里面最后还调用了jQuery.find.matchesSelector()和jQuery.find.matches() 方法,这两个方法内都会调用 Sizzle()方法,而Sizzle()内调用了 select() 方法,select() 比较复杂,本文暂不贴码解析。源码: //返回符合一定条件的元素 //$(“p”).remove("#pTwo") expr=#pTwo elems=$(“p”) //源码2925行 jQuery.filter = function( expr, elems, not ) { //jQuery对象转换成DOM对象 var elem = elems[ 0 ]; if ( not ) { expr = “:not(” + expr + “)”; } //目标元素节点只有一个的情况 if ( elems.length === 1 && elem.nodeType === 1 ) { return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; } //elems 标签p的集合 //jQuery.grep,返回符合callback函数条件的数组, // 这里就是过滤掉非元素节点 return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { return elem.nodeType === 1; } ) ); };3、$.grep()作用:返回符合 callback 函数条件的数组源码: //返回符合callback函数条件的数组 //elems 标签p的集合 //callback elems.nodeType===1 //invert 指定是否反转过滤结果,默认是false grep: function( elems, callback, invert ) { var callbackInverse, matches = [], i = 0, length = elems.length, //2 callbackExpect = !invert; //true // Go through the array, only saving the items // that pass the validator function //只保存符合callback函数的条件的elem(elem.nodeType===1) for ( ; i < length; i++ ) { //false callbackInverse = !callback( elems[ i ], i ); //false true if ( callbackInverse !== callbackExpect ) { matches.push( elems[ i ] ); } } return matches; },4、有参数的$().remove()的简单实现: //有参数====== $(“p”).remove("#pTwo") //相当于 // jQuery.cleanData(getAll(divTwo)) pTwo.parentNode.removeChild( pTwo )三、$(). detach()作用:移除被选元素自身,但保留所有的数据、事件和子节点注意:该方法在 不久会将删除的元素插入DOM的情况下,很有用源码: //移除被选元素,包括所有的文本和子节点,但会保留移除元素的副本,允许它们在以后被重新插入。 //注意那个 keepData:true detach: function( selector ) { return remove( this, selector, true ); },可以看到,与 $().remove() 的区别就是该方法第三个参数传了 true 给大 remove() 方法,即 keepData function remove( elem, selector, keepData ) { var node, //remove()不带参数的话,nodes=目标元素 //$(“p”).remove("#pTwo") elem=$(“p”) selector=#pTwo nodes = selector ? jQuery.filter( selector, elem ) : elem, i = 0; for ( ; ( node = nodes[ i ] ) != null; i++ ) { //如果keepData不为true,并且node为元素节点,则清除数据和事件 if ( !keepData && node.nodeType === 1 ) { jQuery.cleanData( getAll( node ) ); } //如果父节点存在,则removeChild if ( node.parentNode ) { //$.contains:判断指定元素内是否包含另一个元素。即判断另一个DOM元素是否是指定DOM元素的后代 if ( keepData && jQuery.contains( node.ownerDocument, node ) ) { //把所有的script标签标记为已经执行 setGlobalEval( getAll( node, “script” ) ) } node.parentNode.removeChild( node ); } } return elem; }可以看到,在 keepData=true的时候,会不执行 jQuery.cleanData( getAll( node ) )方法,并且会执行 setGlobalEval( getAll( node, “script” ) )使用: //无参数====== // 该方法在 不久会将删除的元素插入DOM的情况下,很有用 let removeNodeOne=$("#divTwo").detach() //有参数====== let removeNodeTwo=$(“p”).detach("#pTwo")四、关于 getAll()、jQuery.cleanData() 我们下一篇讲。。(有点累)(完) ...

April 14, 2019 · 3 min · jiezi

关于写作那些事之利用 js 统计各大博客阅读量

在日常文章数据统计的过程中,纯手动方式已经难以应付,于是乎,逐步开始了程序介入方式进行统计.在上一节中,探索利用 csv 文件格式进行文章数据统计,本来以为能够应付一阵子,没想到仅仅一天我就放弃了.原因还不是因为我懒,需要复制文章内容,然后整理成特定的 csv 格式,最后利用已编写的 java 工具类进行统计.在这三步操作中,第一步复制文章内容最简单,第二步整理文章格式最麻烦,第三步编写 csv 工具类最技术.因此,能不能再简单点?懒癌晚期,必须继续寻求新的解决方案.关于如何利用 csv 文件处理统计数据,可以参考 https://snowdreams1006.github.io/static-semi-manual-with-csv.html实现效果慕课手记慕课手记 : https://www.imooc.com/u/52244…简书简书 : https://www.jianshu.com/u/577…博客园博客园 : https://www.cnblogs.com/snowd…腾讯云社区腾讯云社区 : https://cloud.tencent.com/dev...js 抓取分析数据下面以 chrome 浏览器为例,说明如何利用默认控制台抓取关键数据,本文需要一定的 jQuery 基础.慕课手记在目标页面右键选择检查选项,打开默认开发者控制台,点击最左侧的小鼠标箭头,然后选中关键数据,比如浏览量.此时,开发者控制台自动滚动到元素(Elements)选项卡,在目标数据上右键点击复制(Copy),接着点击复制选择器(Copy selector),现在已经定位到阅读量的节点.点击控制台(Console)选项卡,并且将选择器更改成 jQuery 选择器,即$(“复制的选择器”).text(),现在在控制台直接输出内容,看一下能否抓取到浏览量吧!现在已经成功定位到指定元素,而我们要统计的是全部文章的阅读量,因此需要定位到全部元素.$("#articlesList > div:nth-child(1) > div.item-btm.clearfix > div > div:nth-child(1) > em").text();简单分析下文章结构结合选择器分析,可以得知, 浏览,推荐和评论三者文档基本一致,唯一不同之处就是排列顺序而已,因此想要准确定位到浏览数,需要定位到第一个元素,推荐量则是第二个元素,因此类推.<div class=“r right-info”> <div class=“favorite l”> <i class=“icon sns-thumb-up-outline”></i><em> 83浏览</em> </div> <div class=“favorite l”> <i class=“icon sns-thumb-up-outline”></i><em> 1推荐</em> </div> <div class=" l"> <i class=“icon sns-comment”></i><em> 0评论</em> </div> </div>弄清楚基本文档结构后,开始着手改造选择器使其定位到全部文章的浏览量,我们做如下改造.$("#articlesList div:nth-child(1) > em").text();仅仅保留头部和尾部,再去掉中间部分 > div:nth-child(1) > div.item-btm.clearfix > div > ,这样就轻松定位到全部元素的浏览量了,是不是很简单?看到控制台输出结果,心里瞬间踏实了,这不刚好是第一页全部文章的浏览量吗?观察输出内容格式可知,我们需要将整个字符串按照空格分割成字符串数组.需要注意的是,行首还有一个空格哟,因此在分割成字符串数组前,我们先将行首的空格去除掉.// 去除空格前:" 83浏览 91浏览 114浏览 150浏览 129浏览 175浏览 222浏览 173浏览 225浏览 200浏览 201浏览 217浏览 291浏览 202浏览 229浏览 184浏览 226浏览 155浏览 153浏览 211浏览"$("#articlesList div:nth-child(1) > em").text().trim();// 去除空格后: “83浏览 91浏览 114浏览 150浏览 129浏览 175浏览 222浏览 173浏览 225浏览 200浏览 201浏览 217浏览 291浏览 202浏览 229浏览 184浏览 226浏览 155浏览 153浏览 211浏览"现在我们再将这整个字符串按照空格分割成字符串数组.// 分割字符串前: “83浏览 91浏览 114浏览 150浏览 129浏览 175浏览 222浏览 173浏览 225浏览 200浏览 201浏览 217浏览 291浏览 202浏览 229浏览 184浏览 226浏览 155浏览 153浏览 211浏览”$("#articlesList div:nth-child(1) > em”).text().trim().split(" “);// 分割字符串后: [“83浏览”, “91浏览”, “114浏览”, “150浏览”, “129浏览”, “175浏览”, “222浏览”, “173浏览”, “225浏览”, “200浏览”, “201浏览”, “217浏览”, “291浏览”, “202浏览”, “229浏览”, “184浏览”, “226浏览”, “155浏览”, “153浏览”, “211浏览”]现在我们已经够将整个字符串分割成一个个小的字符串,下面需要再将83浏览中的浏览去掉,仅仅保留数字83.$.each($("#articlesList div:nth-child(1) > em”).text().trim().split(" “),function(idx,ele){ console.log(ele.substr(0,ele.lastIndexOf(“浏览”)));});现在我们已经抓取到真正的浏览量,接下来就比较简单了,直接将这些浏览量进行累加即可,需要注意的是,这里的浏览数还是字符串类型,需要转换成数字类型才能进行累加运算哟!//阅读量var readCount = 0;$.each($("#articlesList div:nth-child(1) > em”).text().trim().split(" “),function(idx,ele){ readCount += parseInt(ele.substr(0,ele.lastIndexOf(“浏览”)));});console.log(“阅读量: " + readCount);小结我们以 chrome 浏览器为例,讲解了如何利用自带的控制台工具抓取关键数据,从页面结构分析入口,一步一个脚印提取有效数据,最终从一条数据变成多条数据,进而实现数据的累加统计.总体来说,还是比较简单的,并不需要太多的基础知识,但还是稍微总结其中涉及到的 jQuery 知识点吧!定位到具体元素: $(“这里是复制的选择器”)定位到具体元素内容: $(“这里是复制的选择器”).text()去除字符串首尾空格: $(“这里是复制的选择器”).text().trim()将字符串按照空格分割成字符串数组: $(“这里是复制的选择器”).text().trim().split(” “)截取字符串指定部分: ele.substr(0,ele.lastIndexOf(“浏览”)将字符串转化成数字类型: parseInt(ele.substr(0,ele.lastIndexOf(“浏览”)));变量累加求和: readCount += parseInt(ele.substr(0,ele.lastIndexOf(“浏览”)));完整示例://阅读量var readCount = 0;$.each($("#articlesList div:nth-child(1) > em”).text().trim().split(” “),function(idx,ele){ readCount += parseInt(ele.substr(0,ele.lastIndexOf(“浏览”)));});console.log(“阅读量: " + readCount);//推荐量var recommendCount = 0;$.each($("#articlesList div:nth-child(2) > em”).text().trim().split(” “),function(idx,ele){ recommendCount += parseInt(ele.substr(0,ele.lastIndexOf(“推荐”)));});console.log(“推荐量: " + recommendCount);//评论量var commendCount = 0;$.each($("#articlesList div:nth-child(3) > em”).text().trim().split(” “),function(idx,ele){ commendCount += parseInt(ele.substr(0,ele.lastIndexOf(“评论”)));});console.log(“评论量: " + commendCount);简书简书的文章数据不一定很规整,比如有的发布文章还没有简书钻,所以阅读量的排列顺序就是不确定的,这一点不像前面介绍的慕课手记,但是简书的关键数据前面是有小图标的,因此我们可以利用图标定位到旁边的数据.按照前面介绍的步骤,我们仍然定位到阅读量,然而 #note-44847909 > div > div > a:nth-child(2) > i 却不能直接使用,因为我们刚刚分析了,简书不能利用顺序定位只能用图标辅助定位.所以,还是先看看文档结构,尝试着直接定位到全部的阅读量小图标.经过分析文章结构,我们可以很轻松定位到全部阅读小图标,当然这是一个元素数组,并不是字符串数组哟!$("#list-container .ic-list-read”)接下来我们看一下能否正确定位到每一个小图标,进而定位到小图标左侧的阅读量.现在我们已经能够定位到全部的阅读量小图标,现在思考如何定位到旁边的真正阅读量呢?<div class=“meta”> <span class=“jsd-meta”> <i class=“iconfont ic-paid1”></i> 0.2 </span> <a target="_blank” href="/p/3441940065b5"> <i class=“iconfont ic-list-read”></i> 2 </a> <a target="_blank" href="/p/3441940065b5#comments"> <i class=“iconfont ic-list-comments”></i> 0 </a> <span><i class=“iconfont ic-list-like”></i> 1</span> <span class=“time” data-shared-at=“2019-04-12T10:39:57+08:00”>昨天 10:39</span></div>分析文章结构,我们发现阅读量是小图标的父节点的内容,这一下就简单了,我们顺藤摸瓜定位到父节点自然就能定位到阅读量了!$("#list-container .ic-list-read").each(function(idx,ele){ console.log($(ele).parent().text().trim());});现在既然已经能够定位到阅读量,那么首先累加求和就很简单了.//阅读量var readCount = 0;$("#list-container .ic-list-read").each(function(idx,ele){ readCount += parseInt($(ele).parent().text().trim());});console.log(“阅读量: " + readCount);小结首先分析文章基本结构发现,简书的阅读量需要定位到阅读量小图标,进而定位到父节点,然后父节点的内容才是真正的阅读量.定位到真正的阅读量后,一切问题迎刃而解,总结一下新增 jQuery 知识点.定位到当前节点的父节点: $(ele).parent()完整示例://阅读量var readCount = 0;$("#list-container .ic-list-read”).each(function(idx,ele){ readCount += parseInt($(ele).parent().text().trim());});console.log(“阅读量: " + readCount);//评论量var commendCount = 0;$("#list-container .ic-list-comments”).each(function(idx,ele){ commendCount += parseInt($(ele).parent().text().trim());});console.log(“评论量: " + commendCount);//喜欢量var recommendCount = 0;$("#list-container .ic-list-like”).each(function(idx,ele){ recommendCount += parseInt($(ele).parent().text().trim());});console.log(“喜欢量: " + recommendCount);博客园博客园的文章列表比较复古,传统的 table 布局,是这几个平台中最简单的,基本上不同怎么介绍.复制到阅读量选择器: #post-row-10694598 > td:nth-child(4) 此时再结合文章结构,因此我们可以得到全部文章的阅读量选择器.$("#post_list td:nth-child(4)")接下来需要遍历数组,看看能否抓取到当前页面全部文章的阅读量.$("#post_list td:nth-child(4)”).each(function(idx,ele){ console.log($(ele).text().trim());});成功抓取到阅读量,现在开始累加当前页面全部文章的阅读量.//阅读数var readCount = 0;$("#post_list td:nth-child(4)").each(function(idx,ele){ readCount += parseInt($(ele).text().trim());});console.log(“阅读数: " + readCount);小结中规中矩的传统 table 布局,只需要顺序定位到具体的元素即可,需要注意的是,博客园文章页面采用了分页,如果需要统计全部文章的阅读量,需要将每页的阅读量手动累加计算.完整示例://评论数var commendCount = 0;$("#post_list td:nth-child(3)”).each(function(idx,ele){ commendCount += parseInt($(ele).text().trim());});console.log(“评论数: " + commendCount);//阅读数var readCount = 0;$("#post_list td:nth-child(4)”).each(function(idx,ele){ readCount += parseInt($(ele).text().trim());});console.log(“阅读数: " + readCount);腾讯云社区大致分析腾讯云社区的文章结构,基本上和简书结构差不多,既可以像简书那种采用图标定位方式,也可以像慕课网和博客园那种直接顺序定位.为了较为精准的定位,现在采用图标定位方式来获取阅读量.#react-root > div:nth-child(1) > div.J-body.com-body.with-bg > section > div > section > div > div.com-log-list > section:nth-child(1) > section > div > div > span > span既然要根据图标定位,我们需要分析图标和阅读量的关系.<div class=“com-operations com-article-panel-operations”> <span class=“com-opt-link”> <i class=“com-i-view”></i> <span class=“text”>76</span> </span> <a href=“javascript:;” class=“com-opt-link”> <i class=“com-i-like”></i> <span class=“text”>3</span> </a></div>因此,我们需要做如下改造才能定位到与阅读量.$("#react-root .com-i-view”).each(function(idx,ele){ console.log($(ele).next().text().trim());});定位到阅读量,接下来就是简单的数据累加求和了.//阅读量var readCount = 0;$("#react-root .com-i-view").each(function(idx,ele){ readCount += parseInt($(ele).next().text().trim());});console.log(“阅读量: " + readCount);小结腾讯云社区和简书一样,采用的分页叠加模式,因此需要统计全部文章的话,只需要一直滚动直到加载出全部文章即可.总结一下涉及到的新增 jQuery 知识点: 获取当前节点的下一个节点: $(ele).next()完整示例://阅读量var readCount = 0;$("#react-root .com-i-view”).each(function(idx,ele){ readCount += parseInt($(ele).next().text().trim());});console.log(“阅读量: " + readCount);//点赞量var recommendCount = 0;$("#react-root .com-i-like”).each(function(idx,ele){ recommendCount += parseInt($(ele).next().text().trim());});console.log(“点赞量: " + recommendCount);小结本文通过 jQuery 方式直接抓取文章数据,简单方便,学习成本低,能够快速上手.慕课网和博客园的文章列表存在分页,如果需要统计全部文章浏览量,需要将每一页的文章累加,直到最后一页.简书和腾讯云社区的文章列表虽然也有分支,但会自动累加,所以统计全部文章时只需要先等全部文章加载完毕,再利用 js 脚本一次性统计即可.好了,本次分享到此结束,如果你觉得本文对你有所帮助,欢迎分享让更多人看到哦,对了,上一篇文章也是解决统计问题的,不过使用的是 java 读取 csv 文件方式,如果有兴趣,也可以看一看. ...

April 13, 2019 · 3 min · jiezi

SpringBoot 仿抖音短视频小程序开发(三)

SpringBoot 仿抖音短视频小程序开发(一):项目的简介(https://segmentfault.com/a/11...SpringBoot 仿抖音短视频小程序开发(二):项目功能分析与具体实现(https://segmentfault.com/a/11…源代码: SpringBoot 仿抖音短视频小程序开发 全栈式实战项目(https://gitee.com/scau_zns/sh…)短视频后台管理系统:(https://gitee.com/scau_zns/sh…小程序的后台管理系统涉及的技术栈:Bootstrap + jQuery + jGrid + SSM框架 + zookeeper一、用户列表的获取与分页前端代码: <div class=“usersList_wrapper”> <!– 用户列表展示的表格 –> <table id=“usersList”></table> <!– 底部的分页条 –> <div id=“usersListPager”></div> </div> jGrid发送请求获取数据封装好展示到页面// 用户列表 var handleList = function() { // 上下文对象路径 var hdnContextPath = $("#hdnContextPath").val(); var apiServer = $("#apiServer").val(); var jqGrid = $("#usersList"); jqGrid.jqGrid({ caption: “短视频用户列表”, url: hdnContextPath + “/users/list.action”, mtype: “post”, styleUI: ‘Bootstrap’,//设置jqgrid的全局样式为bootstrap样式 datatype: “json”, colNames: [‘ID’, ‘头像’, ‘用户名’, ‘昵称’, ‘粉丝数’, ‘关注数’, ‘获赞数’], colModel: [ { name: ‘id’, index: ‘id’, width: 30, sortable: false, hidden: false }, { name: ‘faceImage’, index: ‘username’, width: 50, sortable: false, formatter:function(cellvalue, options, rowObject) { <!– 配置的虚拟目录apiServer = http://192.168.199.150:8080 –> var src = apiServer + cellvalue; var img = “<img src=’” + src + “’ width=‘120’></img>” return img; } }, { name: ‘username’, index: ‘password’, width: 30, sortable: false }, { name: ’nickname’, index: ’nickname’, width: 30, sortable: false }, { name: ‘fansCounts’, index: ‘age’, width: 20, sortable: false }, { name: ‘followCounts’, index: ‘sexValue’, width: 20, sortable: false }, { name: ‘receiveLikeCounts’, index: ‘province’, width: 20, sortable: false, hidden: false } ], viewrecords: true, // 定义是否要显示总记录数 rowNum: 10, // 在grid上显示记录条数,这个参数是要被传递到后台 rownumbers: true, // 如果为ture则会在表格左边新增一列,显示行顺序号,从1开始递增。此列名为’rn’ autowidth: true, // 如果为ture时,则当表格在首次被创建时会根据父元素比例重新调整表格宽度。如果父元素宽度改变,为了使表格宽度能够自动调整则需要实现函数:setGridWidth height: 500, // 表格高度,可以是数字,像素值或者百分比 rownumWidth: 36, // 如果rownumbers为true,则可以设置行号 的宽度 pager: “#usersListPager”, // 分页控件的id subGrid: false // 是否启用子表格 }).navGrid(’#usersListPager’, { edit: false, add: false, del: false, search: false }); // 随着窗口的变化,设置jqgrid的宽度 $(window).bind(‘resize’, function () { var width = $(’.usersList_wrapper’).width()0.99; jqGrid.setGridWidth(width); }); // 不显示水平滚动条 jqGrid.closest(".ui-jqgrid-bdiv").css({ “overflow-x” : “hidden” }); // 条件查询所有用户列表 $("#searchUserListButton").click(function(){ var searchUsersListForm = $("#searchUserListForm"); jqGrid.jqGrid().setGridParam({ page: 1, url: hdnContextPath + “/users/list.action?” + searchUsersListForm.serialize(), }).trigger(“reloadGrid”); }); }后端获取用户列表分页数据的接口: @PostMapping("/list") @ResponseBody public PagedResult list(Users user , Integer page) { PagedResult result = usersService.queryUsers(user, page == null ? 1 : page, 10); return result; }搜索功能的实现:<!– 搜索内容 –> <div class=“col-md-12”> <br/> <form id=“searchUserListForm” class=“form-inline” method=“post” role=“form”> <div class=“form-group”> <label class=“sr-only” for=“username”>用户名:</label> <input id=“username” name=“username” type=“text” class=“form-control” placeholder=“用户名” /> </div> <div class=“form-group”> <label class=“sr-only” for=“nickname”>昵称:</label> <input id=“nickname” name=“nickname” type=“text” class=“form-control” placeholder=“昵称” /> </div> <button id=“searchUserListButton” class=“btn yellow-casablanca” type=“button”>搜 索</button> </form> </div>使用jGrid发送请求给后台 // 条件查询所有用户列表 $("#searchUserListButton").click(function(){ var searchUsersListForm = $("#searchUserListForm"); jqGrid.jqGrid().setGridParam({ page: 1, url: hdnContextPath + “/users/list.action?” + searchUsersListForm.serialize(), }).trigger(“reloadGrid”); });二、背景音乐BGM的上传、查询和删除上传 $("#file").fileupload({ pasteZone: “#bgmContent”, dataType: “json”, done: function(e, data) { console.log(data); if (data.result.status != ‘200’) { alert(“长传失败…”); } else { var bgmServer = $("#bgmServer").val(); var url = bgmServer + data.result.data; $("#bgmContent").html("<a href=’" + url + “’ target=’_blank’>点我播放</a>”); $("#path").attr(“value”, data.result.data); } } });后台接口保存BGM的方法参考上传头像的方法分页查询参考用户列表信息的分页查询多少删除BGM var deleteBgm = function(bgmId) { var flag = window.confirm(“是否确认删除???”); if (!flag) { return; } $.ajax({ url: $("#hdnContextPath").val() + ‘/video/delBgm.action?bgmId=’ + bgmId, type: “POST”, success: function(data) { if (data.status == 200 && data.msg == ‘OK’) { alert(‘删除成功~~’); var jqGrid = $("#bgmList"); jqGrid.jqGrid().trigger(“reloadGrid”); } } })}三、举报管理禁止播放var forbidVideo = function(videoId) { var flag = window.confirm(“是否禁播”); if (!flag) { return; } $.ajax({ url: $("#hdnContextPath").val() + “/video/forbidVideo.action?videoId=” + videoId, type: “POST”, async: false, success: function(data) { if(data.status == 200 && data.msg == “OK”) { alert(“操作成功”); var jqGrid = $("#usersReportsList"); //reloadGrid是重新加载表格 jqGrid.jqGrid().trigger(“reloadGrid”); } else { console.log(JSON.stringify(data)); } } })}四、后台管理系统增加或删除BGM,向zookeeper-server创建子节点,让小程序后端监听【重点】1、首先安装Zookeeper到Linux上,启动服务器2、编写zk客户端代码:import org.apache.curator.framework.CuratorFramework;import org.apache.zookeeper.CreateMode;import org.apache.zookeeper.ZooDefs.Ids;import org.slf4j.Logger;import org.slf4j.LoggerFactory;public class ZKCurator { //zk客户端 private CuratorFramework client = null; final static Logger log = LoggerFactory.getLogger(ZKCurator.class); public ZKCurator(CuratorFramework client) { this.client = client; } public void init() { client = client.usingNamespace(“admin”); try { //判断在admin命名空间下是否有bgm节点 /admin/bgm if( client.checkExists().forPath("/bgm") == null ) { //对于zk来讲,有两种类型的节点,一种是持久节点(永久存在,除非手动删除),另一种是临时节点(会话断开,自动删除) client.create().creatingParentContainersIfNeeded() .withMode(CreateMode.PERSISTENT) //持久节点 .withACL(Ids.OPEN_ACL_UNSAFE) //匿名权限 .forPath("/bgm"); log.info(“zookeeper客户端连接初始化成功”); log.info(“zookeeper服务端状态:{}",client.isStarted()); } } catch (Exception e) { log.error(“zookeeper客户端连接初始化失败”); e.printStackTrace(); } } /* * 增加或者删除Bgm,向zk-server创建子节点,供小程序后端监听 * @param bgmId * @param operType */ public void sendBgmOperator(String bgmId, String operObject) { try { client.create().creatingParentContainersIfNeeded() .withMode(CreateMode.PERSISTENT) //持久节点 .withACL(Ids.OPEN_ACL_UNSAFE) //匿名权限 .forPath("/bgm/” + bgmId, operObject.getBytes()); } catch (Exception e) { e.printStackTrace(); } }}3、在applicationContext-zookeeper.xml配置zookeeper: <!– 创建重连策列 –> <bean id=“retryPolicy” class=“org.apache.curator.retry.ExponentialBackoffRetry”> <!– 每次重试连接的等待时间 –> <constructor-arg index=“0” value=“1000”></constructor-arg> <!– 设置最大的重连次数 –> <constructor-arg index=“1” value=“5”></constructor-arg> </bean> <!– 创建zookeeper客户端 –> <bean id=“client” class=“org.apache.curator.framework.CuratorFrameworkFactory” factory-method=“newClient” init-method=“start”> <constructor-arg index=“0” value=“120.79.18.35:2181”></constructor-arg> <constructor-arg index=“1” value=“10000”></constructor-arg> <constructor-arg index=“2” value=“10000”></constructor-arg> <constructor-arg index=“3” ref=“retryPolicy”></constructor-arg> </bean> <!– 调用init方法启动 –> <bean id=“ZKCurator” class=“com.imooc.web.util.ZKCurator” init-method=“init”> <constructor-arg index=“0” ref=“client”></constructor-arg> </bean>4、上传或者删除BGM时调用VideoServiceImpl.java的方法 @Autowired private ZKCurator zKCurator; @Override public void addBgm(Bgm bgm) { String id = sid.nextShort(); bgm.setId(id); bgmMapper.insert(bgm); Map<String, String> map = new HashMap<>(); map.put(“operType”, BGMOperatorTypeEnum.ADD.type); map.put(“path”, bgm.getPath()); zKCurator.sendBgmOperator(id, JSONUtils.toJSONString(map)); } @Override public void deleteBgm(String id) { Bgm bgm = bgmMapper.selectByPrimaryKey(id); bgmMapper.deleteByPrimaryKey(id); Map<String, String> map = new HashMap<>(); map.put(“operType”, BGMOperatorTypeEnum.DELETE.type); map.put(“path”, bgm.getPath()); zKCurator.sendBgmOperator(id, JSONUtils.toJSONString(map)); }5、小程序编写代码监听zookeeper的节点,并对其做出相应的删除和上传操作【重点】初始化zookeeper客户端 private CuratorFramework client = null; final static Logger log = LoggerFactory.getLogger(ZKCuratorClient.class);// public static final String ZOOKEEPER_SERVER = “120.79.18.36:2181”; public void init() { if(client != null) { return; } //重试策略 RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 5); //创建zk客户端 120.79.18.36:2181 client = CuratorFrameworkFactory.builder().connectString(resourceConfig.getZookeeperServer()).sessionTimeoutMs(10000) .retryPolicy(retryPolicy).namespace(“admin”).build(); //启动客户端 client.start(); try { addChildWatch("/bgm"); } catch (Exception e) { e.printStackTrace(); } } 监听zk-server的节点,当短视频后台管理系统上传或者删除某个BGM的时候,小程序后台服务器通过Zookeeper监听自动下载背景音乐public void addChildWatch(String nodePath) throws Exception { final PathChildrenCache cache = new PathChildrenCache(client, nodePath, true); cache.start(); cache.getListenable().addListener(new PathChildrenCacheListener() { @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { if(event.getType().equals(PathChildrenCacheEvent.Type.CHILD_ADDED)){ log.info(“监听到事件CHILD_ADDED”); //1. 从数据库查询bgm对象,获取路径Path String path = event.getData().getPath(); String operatorObjStr = new String(event.getData().getData()); Map<String, String> map = JsonUtils.jsonToPojo(operatorObjStr, Map.class); String operatorType = map.get(“operType”); String songPath = map.get(“path”);// String[] arr = path.split("/");// String bgmId = arr[arr.length-1];// Bgm bgm = bgmService.queryBgmById(bgmId);// if(bgm == null){// return;// } //1.1 bgm所在的相对路径// String songPath = bgm.getPath(); //2. 定义保存到本地的bgm路径// String filePath = “E:\imooc_videos_dev” + songPath; String filePath = resourceConfig.getFileSpace() + songPath; //3. 定义下载的路径(播放url) String[] arrPath = songPath.split("\\"); //windows// String[] arrPath = songPath.split("/"); //linux String finalPath = “”; //3.1 处理url的斜杠以及编码 for(int i=0; i<arrPath.length;i++){ if(StringUtils.isNotBlank(arrPath[i])) { finalPath += “/”; finalPath += URLEncoder.encode(arrPath[i], “UTF-8”); } }// String bgmUrl = “http://192.168.199.150:8080/mvc” + finalPath; String bgmUrl = resourceConfig.getBgmServer() + finalPath; if(operatorType.equals(“1”)){ //下载bgm到springboot服务器 URL url = new URL(bgmUrl); File file = new File(filePath); FileUtils.copyURLToFile(url, file); client.delete().forPath(path); }else if(operatorType.equals(“2”)){ File file = new File(filePath); FileUtils.forceDelete(file); client.delete().forPath(path); } } } }); } ...

April 11, 2019 · 5 min · jiezi

jQuery源码解析之replaceWith()/unwrap()

前言: 跟 当我调用了$().append()后,jQuery内部发生了什么? 一样,replaceWith() 会经过 domManip() 和 buildFragment() 的洗礼,最后调用原生JS的方法来实现。所以,本文只讲述 jQuery 中最后对 replaceWith() 处理的相关代码。想了解domManip() 和 buildFragment()的,请看 当我调用了$().append()后,jQuery内部发生了什么?一、示例代码<body><script src=“jQuery.js”></script><div id=“divTwo”> <p id=“pTwo”>这是divTwo</p></div><div id=“divOne”> <p>这是divOne</p></div><script> let divOne = document.querySelector("#divOne") let divTwo = document.querySelector("#divTwo") let pTwo = document.querySelector("#pTwo") // ===========replaceWith============== //注意:removedNode指向已经被移除的divTwo let removedNode=$("#divTwo").replaceWith("<span>永远</span>") console.log(removedNode,‘removedNode21’) //======相当于===== let spanA=document.createElement(“span”) spanA.innerText=“永远” divTwo.parentNode.replaceChild(spanA,divTwo) // ==============unwrap================ $("#pTwo").unwrap() //======相当于===== let pTwoFather=pTwo.parentNode if(pTwoFather.nodeName.toLowerCase()!==‘body’){ pTwoFather.parentNode.replaceChild(pTwo,pTwoFather) } </script></body>二、$().replaceWith()作用:把被选元素替换为新的内容注意:$().replaceWith() 指向已经被移除的元素。源码: // 源码6324行 // 把被选元素替换为新的内容 replaceWith: function() { var ignored = []; // Make the changes, replacing each non-ignored context element with the new content return domManip( this, arguments, function( elem ) { //获取选择器的父节点 var parent = this.parentNode; //$.inArray() 函数用于在数组中查找指定值,并返回它的索引值(如果没有找到,则返回-1) //inArray() 可以看成是indexOf() //如果ignored数组中没有目标元素的话 if ( jQuery.inArray( this, ignored ) < 0 ) { //清除目标元素的事件 jQuery.cleanData( getAll( this ) ); if ( parent ) { //原生JS方法replaceChild(newnode,oldnode) 将某子节点替换成另一个 parent.replaceChild( elem, this ); } } // Force callback invocation }, ignored ); }解析:① 先找到目标元素的父节点 this.parentNode② 使用 $.cleanData() 清除目标元素上的 事件和缓存jQuery.cleanData( getAll( this ) )③ 当父节点存在时,父节点调动replaceChild(),将待替换的元素 替换到 目标元素上简单实现: //注意:removedNode指向已经被移除的divTwo let removedNode=$("#divTwo").replaceWith("<span>永远</span>") console.log(removedNode,‘removedNode21’) //======相当于===== let spanA=document.createElement(“span”) spanA.innerText=“永远” divTwo.parentNode.replaceChild(spanA,divTwo)三、$().inArray()作用:查看元素在数组中的位置源码: //源码453行,查看元素在数组中的位置 inArray: function( elem, arr, i ) { //indexOf:array.indexOf return arr == null ? -1 : indexOf.call( arr, elem, i ); },四:$().unwrap()作用:移除被选元素的父元素(父节点是body则无效)源码: //源码9798行 //移除被选元素的父元素(父节点是body则无效) unwrap: function( selector ) { //选中目标元素的父节点(除了body) this.parent( selector ).not( “body” ).each( function() { //this表示父节点 //即父节点被它的子节点替换 jQuery( this ).replaceWith( this.childNodes ); } ); return this; }解析:是在目标元素的爷爷节点上调用 replaceWith() 方法,将父节点替换成目标节点。注意:目标元素的父节点是body的话,$().unwrap()方法无效。简单实现: $("#pTwo").unwrap() //======相当于===== let pTwoFather=pTwo.parentNode if(pTwoFather.nodeName.toLowerCase()!==‘body’){ pTwoFather.parentNode.replaceChild(pTwo,pTwoFather) }五、$().parent() 作用:返回被选元素的直接父元素源码: //源码3245行 //11表示文档碎片 //返回被选元素的直接父元素 parent: function( elem ) { var parent = elem.parentNode; return parent && parent.nodeType !== 11 ? parent : null; },这个一看就懂,就不解释了(完) ...

April 9, 2019 · 2 min · jiezi

原生JS与Jquery删除iframe并释放内存-IE

当项目以tab页签方式打开多个iframe窗口时,关闭tab页签同时也需要关闭iframe并释放内存资源,主要是针对IE浏览器。原生js/** * 销毁iframe,释放iframe所占用的内存。 * @param iframe 需要销毁的iframe id */function destroyIframe(iframeID){ var iframe = document.getElementById(iframeID); //把iframe指向空白页面,这样可以释放大部分内存。 iframe.src = ‘about:blank’; try{ iframe.contentWindow.document.write(’’); iframe.contentWindow.document.clear(); }catch(e){} //把iframe从页面移除 iframe.parentNode.removeChild(iframe); }Jquery写法function destroyIframe(iframeID){ var iframe = $(’#’ + iframeID).prop(‘contentWindow’); $(’#’ + iframeID).attr(‘src’, ‘about:blank’); try{ iframe.document.write(’’); iframe.document.clear(); }catch(e){} //把iframe从页面移除 $(’#’ + iframeID).remove(); }

April 8, 2019 · 1 min · jiezi

JQ基础

什么是jQuery对象通过jQuery封装Dom对象后产生的对象jQuery的原型是 jQuery.prototype<ul> <li></li> <li></li></ul>$(’li’)的结构是什么答:$(’li’) 是一个对象,它自身的 key 有 length,它的原型(共享属性)为 jQuery.prototype。 jQuery.prototype 的 key 有 addClass、removeClass、text、html等。<div id=‘xxx’></div>var div = document.getElementById(‘xxx’)var $div = $(’#xxx’)div 和 $div的联系和区别?div 和 $div的联系:$div === $(div) ,即 $(div) 可以将 div 封装成一个jQuery对象$div[0] === div ,即 $div 的第一项就是 divdiv 和 $div的区别:div 的属性和方法有 nodetype、childNodes、firstChild等$div 的属性和方法有 addClass、removeClass、toggleClass等jq和Dom的相互转换 【待续未更。。。】

April 7, 2019 · 1 min · jiezi

jQuery,zepto源码的简单实现记录

本文记录jQuery,Zepto对js的一些封装库的知识,我希望从本文开始去深入学习js这门语言,以及在面向对象,原型上封装的使用。让自己在js方面有能够有一定进步。共勉jQuery库简单的jQuery库的实现需要先了解闭包,立即执行函数以及JavaScript原型的概念(function(window) { var jQuery = function(selector) { // 一个对象工厂,以后生成新对象就不用new了,直接执行这个方法即可 // 第一步就用new 关键字来实例化一个构造函数 return new jQuery.fn.init(selector); } // 定义构造函数 var init = jQuery.fn.init = function(selector) { var slice = Array.prototype.slice; var dom = slice.call(document.querySelectorAll(’’)); var i, len = dom.length; for(i = 0; i < len; i++) { this[i] = dom[i]; } this.length = len; this.selector = selector || ‘’; } jQuery.fn = jQuery.prototype = { // 改变构造器的指向 constructor: jQuery, css: function(key, val) { }, html: function(val) { } } // 连接初始化实例的原型 // init.prototype = jQuery.fn; jQuery.fn.init.prototype = jQuery.fn;})(window); Zepto库简单的zepto库的实现(function(window) { var zepto = {}; function Z(dom, selector) { var i, len = dom ? dom.length : 0; for(i=0; i<len; i++) { this[i] = dom[i] } this.length = len; this.selector = selector || ‘’; } zepto.Z = function(dom, selector) { // 工厂方法, 直接返回一个初始化实例 return new Z(dom, selector); } zepto.init = function(selector) { var slice = Array.prototype.slice; var dom = slice.call(document.querySelectorAll(’’)); return zepto.Z(dom, selector); } var $ = function(selector) { return zepto.init(selector); } $.fn = { // 修改构造器的指向 constructor: zepto.Z, css: function(key, value) { alert(‘css’); }, html: function(value) { alert(‘html’) } } Z.prototype = $.fn; window.$ = $;})(window);附上待学习连接:几个JS代码手写专题 ...

April 6, 2019 · 1 min · jiezi

基于 jQuery 的键盘事件监听控件

最近项目里要做一个画板,需要对键盘事件进行监听,来进行诸如撤回、重做、移动、缩放等操作,因此顺手实现了一个键盘事件监听控件,期间略有收获,整理出来,希望对大家有所帮助,更希望能获得高手的指点。1. 自动获取焦点似乎浏览器的键盘事件只能被那些可以获得焦点的元素设置监听,而通常需要监听事件的 <DIV>、<CANVAS> 元素都不能获得焦点,因此需要修改目标元素的某些属性使其可以获得焦点,另外一种可行的方法是将事件委托给诸如 <INPUT> 标签。这里采用的是第一类方法,当然,可以修改的属性也不止一种,例如,对于 <DIV> 标签可以将其 “editable” 属性设为 true,而这里采用的是给其设一个 tabindex 值。代码如下: $ele.attr(’tabindex’, 1); 另外,焦点事件的触发需要点击元素或者 TAB 切换,而这并不符合人类的直觉,因此需要监听鼠标移入事件,使目标元素“自动”地获得焦点:$ele.on(‘mouseenter’, function(){ $ele.focus();});2. 监听键盘事件由于项目面向的客户所使用的浏览器以chrome为主(实际上是36x浏览器),因此没有针对浏览器做任何适配,仅仅使用了 jQuery的事件监听: $ele.on(‘keydown’, this._keyDownHandler.bind(this)); 由于实现是控件化的,所以定义了一个私有方法 _keyDownHandler 来响应键盘的动作。3. 按键事件甄别jQuery事件监听器返回的事件对象信息较多,因此需要进行甄别,为此定义了一个私有方法 _keyCodeProcess 来处理按键function _keyCodeProcess(e){ var code = e.keyCode + ‘’; var altKey = e.altKey; var ctrlKey = e.ctrlKey; var shiftKey = e.shiftKey; var threeKey = altKey && ctrlKey && shiftKey; var ctrlAlt = altKey && ctrlKey; var altShift = altKey && shiftKey; var ctrlShift = shiftKey && ctrlKey; var keyTypeSet = this.keyTypeSet; var resStr = ‘’; if(threeKey){ resStr = keyTypeSet.threeKey[code]; } else if(ctrlAlt) { resStr = keyTypeSet.ctrlAlt[code]; } else if(ctrlShift) { resStr = keyTypeSet.ctrlShift[code]; } else if(altShift) { resStr = keyTypeSet.altShift[code]; } else if(altKey) { resStr = keyTypeSet.altKey[code]; } else if(ctrlKey) { resStr = keyTypeSet.ctrlKey[code]; } else if(shiftKey) { resStr = keyTypeSet.shiftKey[code]; } else { resStr = keyTypeSet.singleKey[code]; } return resStr }; 这里的 keyTypeSet 是一个类似于查找表的对象,里面存储了 ctrl、shift、alt按钮的各种类型组合,每种组合下又分别按照按键码存储一个自定义事件类型字符串,事件发生之后会从这里返回这个字符串,当然,没有对应自定义事件的时候,就老老实实地返回空字符串。4. 事件分发_keyCodeProcess 方法从事件中提取出了事件类型,我们提前将监听的回调函数存储在一个查找表 callback 中,并且“巧妙”地使得其键名刚好为自定义事件字符串前面加个“on”前缀,就可以方便地调用了,前述 _keyDownHandler 正是为此而设计的:function _keyDownHandler(e){ var strCommand = this._keyCodeProcess(e); var objEvent = { type: ‘’, originEvent: e.originEvent }; strCommand && this.callback‘on’ + strCommand; return null; }; 5. 事件订阅与解除订阅前面说了,我们是把回调函数存储起来适时调用的,因此需要对外暴露一个“订阅”接口,让开发者可以方便地把自己的回调函数存储到对象实例中去,为此,我定义了一个 .bind接口:function bind(type, callback, description){ var allType = this.allEventType; if(allType.indexOf(type) === -1){ throwError(‘不支持改事件类型,请先扩展该类型,或采用其他事件类型’); } if(!(callback instanceof Function)){ throwError(‘绑定的事件处理回调必须是函数类型’); } this.callback[‘on’ + type] = callback; this.eventDiscibeSet[type] = description || ‘没有该事件的描述’; return this; }; 由于是给人用的,所以顺带做了下类型检查。根据接口的“对称性”,有订阅最好也有解除订阅,因此定义了 .unbind接口,只有一句代码,实现如下:function unbind(type){ this.callback[‘on’ + type] = this._emptyEventHandler; return this; }; 6.扩展自定义事件类型键盘事件的组合丰富多彩,如果全部内置在控件中的话,会是很臃肿的,因此除了少数几个常见的组合键之外,开发者可以通过 .extendEventType 方法,来自定义组合键和返回的字符串:function extendEventType(config){ var len = 0; if(config instanceof Array){ len = config.length; while(len–){ this._setKeyComposition(config[len]); } } else { this._setKeyComposition(config); } return this; }; 其中的 ._setKeyComposition 是一个私有方法,用来写入自定义键盘事件的方法:_setKeyComposition(config){ var altKey = config.alt; var ctrlKey = config.ctrl; var shiftKey = config.shift; var threeKey = altKey && ctrlKey && shiftKey; var ctrlAlt = altKey && ctrlKey; var altShift = altKey && shiftKey; var ctrlShift = shiftKey && ctrlKey; var code = config.code + ‘’; if(threeKey){ this.keyTypeSet.threeKey[code] = config.type; } else if(ctrlAlt) { this.keyTypeSet.ctrlAlt[code] = config.type; } else if(ctrlShift) { this.keyTypeSet.ctrlShift[code] = config.type; } else if(altShift) { this.keyTypeSet.altShift[code] = config.type; } else if(altKey) { this.keyTypeSet.altKey[code] = config.type; } else if(ctrlKey) { this.keyTypeSet.ctrlKey[code] = config.type; } else if(shiftKey) { this.keyTypeSet.shiftKey[code] = config.type; } else { this.keyTypeSet.singleKey[code] = config.type; } return null; }; 这样,一个键盘事件监听控件就大功告成了,下面是完整实现代码:/** * @constructor 键盘事件监听器 * /function KeyboardListener(param){ this._init(param);}!function(){ /* * @private {String} param.ele 事件对象选择器 * / KeyboardListener.prototype._init = function _init(param){ this.$ele = $(param.ele); this._initEvents(); this._initEventType(); return null; }; /* * @private _emptyEventHandler 空白事件响应 * / KeyboardListener.prototype._emptyEventHandler = function _emptyEventHandler(){ return null; }; /* * @private _initEventType 初始化所有初始自定义事件类型 * / KeyboardListener.prototype._initEventType = function _initEventType(){ var allType = [‘up’, ‘down’, ’left’, ‘right’, ‘undo’, ‘redo’, ‘zoomIn’, ‘zoomOut’, ‘delete’]; var intLen = allType.length; this.allEventType = allType; this.callback = {}; this.eventDiscibeSet = {}; for(var intCnt = 0; intCnt < intLen; intCnt++){ this.callback[‘on’ + allType[intCnt]] = KeyboardListener.prototype._emptyEventHandler; } return null; }; /* * @private _initEvents 绑定 DOM 事件 * / KeyboardListener.prototype._initEvents = function _initEvents(){ var $ele = this.$ele; $ele.attr(’tabindex’, 1); $ele.on(‘mouseenter’, function(){ $ele.focus(); }); $ele.on(‘keydown’, this._keyDownHandler.bind(this)); this.keyTypeSet = { altKey: {}, ctrlAlt: {}, ctrlKey: {}, threeKey: {}, altShift: {}, shiftKey: {}, ctrlShift: {}, singleKey: {} }; // 支持一些内建的键盘事件类型 this.extendEventType([ { type: ‘redo’, ctrl: true, shift: true, code: 90 }, { type: ‘undo’, ctrl: true, code: 90 }, { type: ‘copy’, ctrl: true, code: 67 }, { type: ‘paste’, ctrl: true, code: 86 }, { type: ‘delete’, code: 46 }, { type: ‘right’, code: 39 }, { type: ‘down’, code: 40 }, { type: ’left’, code: 37 }, { type: ‘up’, code: 38 } ]); return null; }; /* * @private _keyDownHandler 自定义键盘事件分发 * / KeyboardListener.prototype._keyDownHandler = function _keyDownHandler(e){ var strCommand = this._keyCodeProcess(e); var objEvent = { type: ‘’, originEvent: e.originEvent }; strCommand && this.callback‘on’ + strCommand; return null; }; /* * @private _keyCodeProcess 处理按键码 * / KeyboardListener.prototype._keyCodeProcess = function _keyCodeProcess(e){ var code = e.keyCode + ‘’; var altKey = e.altKey; var ctrlKey = e.ctrlKey; var shiftKey = e.shiftKey; var threeKey = altKey && ctrlKey && shiftKey; var ctrlAlt = altKey && ctrlKey; var altShift = altKey && shiftKey; var ctrlShift = shiftKey && ctrlKey; var keyTypeSet = this.keyTypeSet; var resStr = ‘’; if(threeKey){ resStr = keyTypeSet.threeKey[code]; } else if(ctrlAlt) { resStr = keyTypeSet.ctrlAlt[code]; } else if(ctrlShift) { resStr = keyTypeSet.ctrlShift[code]; } else if(altShift) { resStr = keyTypeSet.altShift[code]; } else if(altKey) { resStr = keyTypeSet.altKey[code]; } else if(ctrlKey) { resStr = keyTypeSet.ctrlKey[code]; } else if(shiftKey) { resStr = keyTypeSet.shiftKey[code]; } else { resStr = keyTypeSet.singleKey[code]; } return resStr }; /* * @private _setKeyComposition 自定义键盘事件 * @param {Object} config 键盘事件配置方案 * @param {String} config.type 自定义事件类型 * @param {keyCode} config.code 按键的码值 * @param {Boolean} [config.ctrl] 是否与 Ctrl 形成组合键 * @param {Boolean} [config.alt] 是否与 Alt 形成组合键 * @param {Boolean} [config.shift] 是否与 Shift 形成组合键 * / KeyboardListener.prototype._setKeyComposition = function _setKeyComposition(config){ var altKey = config.alt; var ctrlKey = config.ctrl; var shiftKey = config.shift; var threeKey = altKey && ctrlKey && shiftKey; var ctrlAlt = altKey && ctrlKey; var altShift = altKey && shiftKey; var ctrlShift = shiftKey && ctrlKey; var code = config.code + ‘’; if(threeKey){ this.keyTypeSet.threeKey[code] = config.type; } else if(ctrlAlt) { this.keyTypeSet.ctrlAlt[code] = config.type; } else if(ctrlShift) { this.keyTypeSet.ctrlShift[code] = config.type; } else if(altShift) { this.keyTypeSet.altShift[code] = config.type; } else if(altKey) { this.keyTypeSet.altKey[code] = config.type; } else if(ctrlKey) { this.keyTypeSet.ctrlKey[code] = config.type; } else if(shiftKey) { this.keyTypeSet.shiftKey[code] = config.type; } else { this.keyTypeSet.singleKey[code] = config.type; } return null; }; /* * @method extendEventType 扩展键盘事件类型 * @param {Object|Array<object>} config 键盘事件配置方案 * @param {String} config.type 自定义事件类型 * @param {keyCode} config.code 按键的码值 * @param {Boolean} [config.ctrl] 是否与 Ctrl 形成组合键 * @param {Boolean} [config.alt] 是否与 Alt 形成组合键 * @param {Boolean} [config.shift] 是否与 Shift 形成组合键 * / KeyboardListener.prototype.extendEventType = function extendEventType(config){ var len = 0; if(config instanceof Array){ len = config.length; while(len–){ this._setKeyComposition(config[len]); } } else { this._setKeyComposition(config); } return this; }; /* * @method bind 绑定自定义的键盘事件 * @param {String} type 事件类型 如:[‘up’, ‘down’, ’left’, ‘right’, ‘undo’, ‘redo’, ‘delete’, zoomIn, ‘zoomOut’] * @param {Function} callback 回调函数,参数为一个自定义的仿事件对象 * @param {String} description 对绑定事件的用途进行说明 * / KeyboardListener.prototype.bind = function bind(type, callback, description){ var allType = this.allEventType; if(allType.indexOf(type) === -1){ throwError(‘不支持改事件类型,请先扩展该类型,或采用其他事件类型’); } if(!(callback instanceof Function)){ throwError(‘绑定的事件处理回调必须是函数类型’); } this.callback[‘on’ + type] = callback; this.eventDiscibeSet[type] = description || ‘没有该事件的描述’; return this; }; /* * @method unbind 解除事件绑定 * @param {String} type 事件类型 * */ KeyboardListener.prototype.unbind = function unbind(type){ this.callback[‘on’ + type] = this._emptyEventHandler; return this; };}(); ...

April 4, 2019 · 5 min · jiezi

JS/ JQ 常用小特效【持续更新中】

table 点击切换<!– 没有css 懒… –><!– HTML –> <ul class=“sideslip-head”> <li class=“active”><span>1111</span></li> <li><span>22222</span></li> <li><span>33333</span></li> </ul> <div class=“sideslip-box”> <ul class=“sideslip-titles”> <li>11111</li> </ul> <ul class=“sideslip-titles”> <li>222222</li> </ul> <ul class=“sideslip-titles”> <li>33333</li> </ul> </div>/* JQ */$(".sideslip-head>li").click(function(){ $(this).addClass(“active”).siblings().removeClass(“active”); $(".sideslip-titles").eq($(this).index()).css(“display”,“block”).siblings().css(“display”,“none”)})

April 3, 2019 · 1 min · jiezi

jQuery插件--web端轮播图

效果地址HTML<div id=“slider” class=“slider”></div><script> $(function () { $(’#slider’).sliders({ imgArr: [’./resource/banner1.jpg’, ‘./resource/banner2.jpg’, ‘./resource/banner3.jpg’], autoLoop: true, current: 1, time: 4000, vWidth: 824 }) });</script>cssJS代码;(function ($, window, document, undefined) { ‘use strict’; function Sliders(element, options) { this.element = element; this.options = { vWidth: options.vWidth || element.width(), current: options.current || 1, imgArr: options.imgArr, len: options.imgArr.length, autoLoop: options.autoLoop, time: options.time || 4000 }; this.init(); } Sliders.prototype = { constructor: Sliders, init: function () { this.createHtml(); this.bindEvent(); this.hackLoop(); }, createHtml: function () { var me = this; var content = []; var imgArr = me.options.imgArr, len = me.options.len, current = me.options.current; content.push("<button type=‘button’ class=‘preBtn’ id=‘preBtn’>上</button>"); content.push("<button type=‘button’ class=‘nextBtn’ id=‘nextBtn’>下</button>"); content.push("<ul class=‘sliderUl’>"); for (var i = 0; i < len; i++) { content.push("<li class=‘sliderLi’><img class=‘block’ src=" + imgArr[i] + “>” + i + “</li>”) } content.push("<li class=‘sliderLi’><img class=‘block’ src=" + imgArr[0] + “>” + i + “</li></ul>”); content.push("<ul class=‘pointer’>"); for (var i = 1, len1 = len + 1; i < len1; i++) { if (current !== i) { content.push("<li data-index=" + i + “></li>”); } else { content.push("<li class=‘current’ data-index=" + i + “></li>”); } } content.push("</ul>"); me.element.html(content.join(’’)); }, bindEvent: function () { var me = this; me.element.on(‘mouseenter’, ‘.sliderUl’, function () { clearInterval(me.timer); }); me.element.on(‘click’, ‘.pointer li’, function () { clearInterval(me.timer); var index = parseInt($(this).data(‘index’)); me.goPage(index); }); me.element.on(‘mouseleave’, ‘.sliderUl’, function () { me.hackLoop(); }); me.element.on(‘click’, ‘#preBtn’, function () { clearInterval(me.timer); var i = me.options.current, len = me.options.len; me.options.current = –i === 0 ? len-1 : i-1; me.loop(); }); me.element.on(‘click’, ‘#nextBtn’, function () { clearInterval(me.timer); var i = me.options.current, len = me.options.len; me.options.current = i === len ? 0 : i; me.loop(); }); }, loop: function () { var me = this; var i = me.options.current, vWidth = me.options.vWidth, len = me.options.len; console.log(i); me.element.children(’.sliderUl’).css({ “-webkit-transform”: “translateX(” + (-i * vWidth) + “px)”, “transform”: “translateX(” + (-i * vWidth) + “px)”, “transition-duration”: “0.4s”, “transition-timing-function”: “ease-in”, “transition-property”: “transform” }); me.element.children(’.pointer’).children(“li”).removeClass(“current”); me.element.children(’.pointer’).children(“li”).eq(i === len ? 0 : i).addClass(“current”); if (me.options.current === len) { setTimeout(function () { me.element.children(’.sliderUl’).css({ “-webkit-transform”: “translateX(0px)”, “transform”: “translateX(0px)”, “transition-duration”: “none”, “transition-timing-function”: “none”, “transition-property”: “none” }); //时间必须不小于动画所需时间 }, 400); } me.options.current = (len !== i) ? ++i : 1; }, hackLoop: function () { var me = this; if (me.options.autoLoop) { me.timer = setInterval(function () { me.loop(); }, me.options.time); } }, goPage: function (index) { var me = this; var vWidth = me.options.vWidth; me.options.current = index; me.element.children(’.sliderUl’).css({ “-webkit-transform”: “translateX(” + (-(index - 1) * vWidth) + “px)”, “transform”: “translateX(” + (-(index - 1) * vWidth) + “px)”, “transition-duration”: “0.4s”, “transition-timing-function”: “ease-in”, “transition-property”: “transform” }); me.element.children(’.pointer’).children(“li”).removeClass(“current”); me.element.children(’.pointer’).children(“li”).eq(index - 1).addClass(“current”); } }; $.fn.sliders = function (options) { return new Sliders($(this), options); }})(jQuery, window, document); ...

April 3, 2019 · 3 min · jiezi

jQuery之text()的实现

一、有这样一段 html<div class=“divOne”> <p>嘿嘿嘿</p></div><div class=“divOne”> <p>哈哈哈</p></div>二、jQuery 的 text() 方法(1)当直接调用 $().text()时,.text()的作用是(循环)读取(多个)目标元素的textContent/nodeValue简单实现: function readText(elem) { let node, ret = “”, i = 0, nodeType = elem.nodeType console.log(nodeType,’nodeType22’) //如果selector是类的话,会有多个目标元素,此时需要分别单个循环 //比如document.querySelectorAll(’.divOne’).nodeType ->undefined if (!nodeType) { while ((node = elem[i++])) { //单个获取 ret += readText(node) } } //元素节点,文档节点,文档碎片 else if (nodeType === 1 || nodeType === 9 || nodeType === 11) { //如果目标元素的内容是文本,则直接返回 if (typeof elem.textContent === “string”) { /jQuery没有用innerText获取文本的值,http://bugs.jquery.com/ticket/11153, 大概就是在IE8中新节点插入会保留所有回车。 所以jQuery采用了textContent获取文本值, textContent本身是dom3规范的,可以兼容火狐下的innerText问题。/ return elem.textContent } //如果节点内容不是文本,则循环子节点,并依次获取它们的文本节点 else { for (elem = elem.firstChild; elem; elem = elem.nextSibling) { ret += readText(elem) } } } //文本节点、一个文档的CDATA部分(没遇到过这个) else if (nodeType === 3 || nodeType === 4) { //返回节点值 return elem.nodeValue; } //nodeType:注释节点 8,处理指令 7 //text()方法不处理这两个类型节点 return ret }(2)当调用$().text(value)时,.text(value)的作用是为每一个符合条件的目标元素的textContent设置为 value简单实现:writeText(): function writeText(value) { let elem, i = 0; //先清空目标元素的内容 customEmpty.call(this) //循环 for (; (elem = this[i]) != null; i++) { //元素节点,文档碎片,文档节点 if (elem.nodeType === 1 || elem.nodeType === 11 || elem.nodeType === 9) { // text()方法不会解析标签 elem.textContent = value; } } //return this 方便链式调用 return this }customEmpty(): function customEmpty() { let elem, i = 0; //注意for循环的写法 for (; (elem = this[i]) != null; i++) { //如果是元素节点的话,清空该节点的所有内容 if (elem.nodeType === 1) { elem.textContent = “”; } } return this; }(3)源码实现源码:jQuery.text()总体: //源码6152行 text: function( value ) { return access( this, function( value ) { return value === undefined ? //读 //如果直接调用text()的话,就调用Sizzle.getText jQuery.text( this ) : //写 //循环 this.empty().each( function() { //先清空目标元素的内容,然后再赋值 if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { console.log(value,‘value6159’) //如果包含标签的话,需要用html()方法,text()方法不会解析标签 /jQuery没有用innerText获取文本的值,http://bugs.jquery.com/ticket/11153, 大概就是在IE8中新节点插入会保留所有回车。 所以jQuery采用了textContent获取文本值, textContent本身是dom3规范的,可以兼容火狐下的innerText问题。/ this.textContent = value; } } ) }, null, value, arguments.length ); },源码解析:① 调用text(),实际上是调用access()access 部分源码见:https://www.jianshu.com/p/645b3b4461c5也就是说:调用jQuery.access()相当于调用了fn.call( elems, value ),即自定义的方法jQuery.access(this, function(value) {xxx})② .text()的情况调用这部分源码:jQuery.text()调用的其实是Sizzle.getText(): //源码2833行 jQuery.text = Sizzle.getText;Sizzle.getText()://源码1642行getText = Sizzle.getText = function( elem ) { var node, ret = “”, i = 0, nodeType = elem.nodeType; if ( !nodeType ) { while ( (node = elem[i++]) ) { // Do not traverse comment nodes ret += getText( node ); } } //元素节点、文档节点、文档碎片 else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { // Use textContent for elements // innerText usage removed for consistency of new lines (jQuery #11153) //如果目标元素的子节点是文本节点,则直接返回它的textContent if ( typeof elem.textContent === “string” ) { /jQuery没有用innerText获取文本的值,http://bugs.jquery.com/ticket/11153, 大概就是在IE8中新节点插入会保留所有回车。 所以jQuery采用了textContent获取文本值, textContent本身是dom3规范的,可以兼容火狐下的innerText问题。/ return elem.textContent; } //如果子节点不是文本节点,则循环子节点,并依次获取它们的文本节点 else { // Traverse its children for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { ret += getText( elem ); } } } //文本节点、一个文档的CDATA部分(没遇到过这个) else if ( nodeType === 3 || nodeType === 4 ) { return elem.nodeValue; } // Do not include comment or processing instruction nodes return ret; };③ .text(value)的情况调用这部分源码:jQuery.text(value): //写 //循环 this.empty().each( function() { //先清空目标元素的内容,然后再赋值 if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { console.log(value,‘value6159’) //如果包含标签的话,需要用html()方法,text()方法不会解析标签 /jQuery没有用innerText获取文本的值,http://bugs.jquery.com/ticket/11153, 大概就是在IE8中新节点插入会保留所有回车。 所以jQuery采用了textContent获取文本值, textContent本身是dom3规范的,可以兼容火狐下的innerText问题。/ this.textContent = value; } } )empty(): //源码6231行 empty: function() { var elem, i = 0; for ( ; ( elem = this[ i ] ) != null; i++ ) { //如果是元素节点的话 if ( elem.nodeType === 1 ) { // Prevent memory leaks //清空内容和事件,防止内存泄漏 jQuery.cleanData( getAll( elem, false ) ); // Remove any remaining nodes //清空节点所有内容 elem.textContent = “”; } } return this; },④ 总结$(".divOne").text()的本质:(1)节点内容是文本,返回$(".divOne")[i].textContent(2)节点内容不是文本,循环返回$(".divOne")[i].element[j].textContent(3)节点内容是文本节点或一个文档的CDATA部分,则返回$(".divOne")[i]. nodeValue $(".divOne").text(“Hello <b>world</b>!")的本质:(1)jQuery.cleanData()(2)$(".divOne”)[i].textContent = “"(3)$(".divOne”)[i].textContent=“Hello world!“注意:text() 不会去解析 html 标签!参考:http://api.jquery.com/text/完整代码:<!DOCTYPE html><html lang=“en”><head> <meta charset=“UTF-8”> <title>jQuery之text()</title></head><body><script src=“jQuery.js”></script><div class=“divOne”> <!–<p id=“divTwo”>嘿嘿嘿</p>–> <p>嘿嘿嘿</p></div><div class=“divOne”> <p>哈哈哈</p></div><input type=“text” id=“inputOne”><script> function readText(elem) { let node, ret = “”, i = 0, nodeType = elem.nodeType console.log(nodeType,’nodeType22’) //如果selector是类的话,会有多个目标元素,此时需要分别单个循环 //比如document.querySelectorAll(’.divOne’).nodeType ->undefined if (!nodeType) { while ((node = elem[i++])) { //单个获取 ret += readText(node) } } //元素节点,文档节点,文档碎片 else if (nodeType === 1 || nodeType === 9 || nodeType === 11) { //如果目标元素的内容是文本,则直接返回 if (typeof elem.textContent === “string”) { /jQuery没有用innerText获取文本的值,http://bugs.jquery.com/ticket/11153, 大概就是在IE8中新节点插入会保留所有回车。 所以jQuery采用了textContent获取文本值, textContent本身是dom3规范的,可以兼容火狐下的innerText问题。/ return elem.textContent } //如果节点的内容不是文本,则循环子节点,并依次获取它们的文本节点 else { for (elem = elem.firstChild; elem; elem = elem.nextSibling) { ret += readText(elem) } } } //文本节点、一个文档的CDATA部分(没遇到过这个) else if (nodeType === 3 || nodeType === 4) { //返回节点值 return elem.nodeValue; } //nodeType:注释节点 8,处理指令 7 //text()方法不处理这两个类型节点 return ret } function customEmpty() { let elem, i = 0; //注意for循环的写法 for (; (elem = this[i]) != null; i++) { //如果是元素节点的话,清空该节点的所有内容 if (elem.nodeType === 1) { elem.textContent = “”; } } return this; } function writeText(value) { let elem, i = 0; //先清空目标元素的内容 customEmpty.call(this) //循环 for (; (elem = this[i]) != null; i++) { //元素节点,文档碎片,文档节点 if (elem.nodeType === 1 || elem.nodeType === 11 || elem.nodeType === 9) { // text()方法不会解析标签 elem.textContent = value; } } //return this 方便链式调用 return this } function customText(value) { return value === undefined ? //读 readText(this) : //写 writeText.call(this, value) } customText.call(document.querySelectorAll(’.divOne’)) customText.call(document.querySelectorAll(’.divOne’),“Hello <b>world</b>!”) // let p=document.createElement(‘p’) // p.innerText=‘哈哈哈’ console.log($(".divOne”).text()) // customText.call(document.querySelectorAll(’.divOne’)) // console.log(document.querySelectorAll(’.divOne’).nodeType,‘childnode81’) // console.log(document.querySelectorAll(’.divOne’)[0].textContent,‘childnode81’) // $("#divOne”).text(’<p>aaaa</p>’) // console.log(document.querySelector("#divTwo"))</script></body></html>(完) ...

April 3, 2019 · 4 min · jiezi

jQuery之html()的实现

一、有这样一段 html<div class=“divOne”> <p>嘿嘿嘿</p></div><div class=“divOne”> <p>哈哈哈</p></div>二、jQuery 的 html() 方法(1)当直接调用 $().html()时,.html()的作用是只读取第一个目标元素的innerHTML简单实现: function customHtml(value) { //默认是选取第一个目标元素 let elem = this[0] || {}, i = 0, l = this.length; //如果是html(),即使读取目标元素的innerHTML的话 if (value === undefined && elem.nodeType === 1) { return elem.innerHTML; } //xxx //xxx }(2)当调用$().html(value)时,.html()的作用是为每一个符合条件的目标元素的innerHTML设置为 value简单实现: function customHtml(value) { //默认是选取第一个目标元素 let elem = this[0] || {}, i = 0, l = this.length; //如果是html(),即使读取目标元素的innerHTML的话 if (value === undefined && elem.nodeType === 1) { return elem.innerHTML; } //根据目标元素的个数,依次对符合条件的目标元素赋值 for (; i < l; i++) { elem = this[i] || {}; if (elem.nodeType === 1) { elem.innerHTML = value; } } }(3)源码实现源码: // html()方法设置或返回被选元素的内容(innerHTML) // 当该方法用于返回内容时,则返回第一个匹配元素的内容 // 当该方法用于设置内容时,则重写所有匹配元素的内容 // http://www.runoob.com/jquery/html-html.html // 源码6203行左右 function html( value ) { //调用$().html()方法,即调用access()方法 //关于access()方法的讲解,请看:https://www.cnblogs.com/gongshunkai/p/5905917.html //access(this,function(),null,value,arguments.length) return jQuery.access( this, function( value ) { //读的话(.html())只读第一个匹配的目标元素的内容所以是this[0] //写的话(.html(xxx))会循环每个匹配的目标并将其innerHTML置为value var elem = this[ 0 ] || {}, i = 0, l = this.length; //当直接调用html(),并且目标元素是元素节点时,$().html()的本质是 selector.innerHTML if ( value === undefined && elem.nodeType === 1 ) { return elem.innerHTML; } // See if we can take a shortcut and just use innerHTML //如果能直接使用innerHTML来解析的话 //注意:IE的innerHTML会忽略开头的无作用域元素 if ( typeof value === “string” && !rnoInnerhtml.test( value ) && !wrapMap[ ( rtagName.exec( value ) || [ “”, "" ] )[ 1 ].toLowerCase() ] ) { //Hello <b>world</b>! value = jQuery.htmlPrefilter( value ); console.log(value,‘value6235’) try { for ( ; i < l; i++ ) { elem = this[ i ] || {}; // Remove element nodes and prevent memory leaks if ( elem.nodeType === 1 ) { console.log(3333,’node6261’) // getAll( elem, false ):获取原本selector内部的内容(标签) //先移除元素节点和注册的事件以防止内存泄漏 jQuery.cleanData( getAll( elem, false ) ); elem.innerHTML = value; } } //将elem置为0,是防止执行下面的if(elem)… elem = 0; // If using innerHTML throws an exception, use the fallback method } catch ( e ) {} } if ( elem ) { this.empty().append( value ) } }, null, value, arguments.length ) }源码解析:① 调用html(),实际上是调用access()access部分源码: //$().html():access(this,function(),null,value,arguments.length) //源码4051行 //关于access()方法的讲解,请看:https://www.cnblogs.com/gongshunkai/p/5905917.html var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { var i = 0, //1 len = elems.length, //true bulk = key == null; // Sets many values if ( toType( key ) === “object” ) { //xxx } else if ( value !== undefined ) { console.log(‘access->value!==undefined’,‘value4053’) chainable = true; //xxx if ( bulk ) { // Bulk operations run against the entire set //走这边 if ( raw ) { // 将elems/selector,value传入function并执行 // call(this,param) fn.call( elems, value ); //这里将 function 置为空值后,就不会执行 if(fn)…了 fn = null; // …except when executing function values } //不走这边 else { bulk = fn; fn = function( elem, key, value ) { return bulk.call( jQuery( elem ), value ); }; } } //xxx //xxx //xxx };也就是说:调用jQuery.access()相当于调用了fn.call( elems, value ),即自定义的方法jQuery.access(this, function(value) {xxx})② .html()的情况调用这部分源码: if ( value === undefined && elem.nodeType === 1 ) { return elem.innerHTML; }③ .html(“字符串”)/.html("<p>这也是字符串</p>")的情况调用这部分源码: // See if we can take a shortcut and just use innerHTML //如果能直接使用innerHTML来解析的话 //注意:IE的innerHTML会忽略开头的无作用域元素 if ( typeof value === “string” && !rnoInnerhtml.test( value ) && !wrapMap[ ( rtagName.exec( value ) || [ “”, "" ] )[ 1 ].toLowerCase() ] ) { //Hello <b>world</b>! value = jQuery.htmlPrefilter( value ); console.log(value,‘value6235’) try { for ( ; i < l; i++ ) { elem = this[ i ] || {}; // Remove element nodes and prevent memory leaks if ( elem.nodeType === 1 ) { console.log(3333,’node6261’) // getAll( elem, false ):获取原本selector内部的内容(标签) //先移除元素节点和注册的事件以防止内存泄漏 jQuery.cleanData( getAll( elem, false ) ); elem.innerHTML = value; } } //将elem置为0,是防止执行下面的if(elem)… elem = 0; // If using innerHTML throws an exception, use the fallback method } catch ( e ) {} }④ .html(这里面是标签)的情况调用这部分源码:标签: let p=document.createElement(‘p’) p.innerText=‘哈哈哈’ $(".divOne").html(p)源码: if ( elem ) { this.empty().append( value ); }⑤ 总结$(".divOne").html()的本质即 $(".divOne")[0].innerHTML$(".divOne").html(“Hello <b>world</b>!")的本质即 $(".divOne”)[i].innerHTML=“Hello <b>world</b>!"$(".divOne”).html(标签)的本质即 $(".divOne").empty().append(标签)源码:<!DOCTYPE html><html lang=“en”><head> <meta charset=“UTF-8”> <title>jQuery之html()</title></head><body><script src=“jQuery.js”></script><div class=“divOne”> <p>嘿嘿嘿</p></div><div class=“divOne”> <p>嘿嘿嘿</p></div><input type=“text” id=“inputOne”><script> function customHtml(value) { //默认是选取第一个目标元素 let elem = this[0] || {}, i = 0, l = this.length; //如果是html(),即使读取目标元素的innerHTML的话 if (value === undefined && elem.nodeType === 1) { return elem.innerHTML; } //根据目标元素的个数,依次对符合条件的目标元素赋值 for (; i < l; i++) { elem = this[i] || {}; if (elem.nodeType === 1) { elem.innerHTML = value; } } } // html()方法设置或返回被选元素的内容(innerHTML) // 当该方法用于返回内容时,则返回第一个匹配元素的内容 // 当该方法用于设置内容时,则重写所有匹配元素的内容 // http://www.runoob.com/jquery/html-html.html // 源码6203行左右 function html( value ) { //调用$().html()方法,即调用access()方法 //关于access()方法的讲解,请看:https://www.cnblogs.com/gongshunkai/p/5905917.html //access(this,function(),null,value,arguments.length) return jQuery.access( this, function( value ) { //读的话(.html())只读第一个匹配的目标元素的内容所以是this[0] //写的话(.html(xxx))会循环每个匹配的目标并将其innerHTML置为value var elem = this[ 0 ] || {}, i = 0, l = this.length; //当直接调用html(),并且目标元素是元素节点时,$().html()的本质是 selector.innerHTML if ( value === undefined && elem.nodeType === 1 ) { return elem.innerHTML; } // See if we can take a shortcut and just use innerHTML //如果能直接使用innerHTML来解析的话 //注意:IE的innerHTML会忽略开头的无作用域元素 if ( typeof value === “string” && !rnoInnerhtml.test( value ) && !wrapMap[ ( rtagName.exec( value ) || [ “”, "" ] )[ 1 ].toLowerCase() ] ) { //Hello <b>world</b>! value = jQuery.htmlPrefilter( value ); console.log(value,‘value6235’) try { for ( ; i < l; i++ ) { elem = this[ i ] || {}; // Remove element nodes and prevent memory leaks if ( elem.nodeType === 1 ) { console.log(3333,’node6261’) // getAll( elem, false ):获取原本selector内部的内容(标签) //先移除元素节点和注册的事件以防止内存泄漏 jQuery.cleanData( getAll( elem, false ) ); elem.innerHTML = value; } } //将elem置为0,是防止执行下面的if(elem)… elem = 0; // If using innerHTML throws an exception, use the fallback method } catch ( e ) {} } if ( elem ) { this.empty().append( value ); } }, null, value, arguments.length ); } customHtml.call(document.querySelectorAll(".divOne")) customHtml.call(document.querySelectorAll(".divOne"),“Hello <b>world</b>!”) // console.log($(".divOne").html()) // $(".divOne").html(“Hello <b>world</b>!”) // let p=document.createElement(‘p’) // p.innerText=‘哈哈哈gggg’ // $(".divOne").html(p) // console.log(p,‘p19’) // $("#divOne").text(’<p>aaaa</p>’) // $("#divOne").text(p)</script></body></html>(完) ...

April 3, 2019 · 5 min · jiezi

jQuery插件--分页

HTML<!DOCTYPE html><html><head> <meta charset=“utf-8”/> <title></title> <style> /* 外面盒子样式—自己定义 / .page_div{margin:20px 10px 20px 0;color:#666} / 页数按钮样式 / .page_div button{display:inline-block;min-width:30px;height:28px;cursor:pointer;color:#666;font-size:13px;line-height:28px;background-color:#f9f9f9;border:1px solid #dce0e0;text-align:center;margin:0 4px;-webkit-appearance: none;-moz-appearance: none;appearance: none;} #firstPage,#lastPage,#nextPage,#prePage{width:50px;color:#0073A9;border:1px solid #0073A9} #nextPage,#prePage{width:70px} .page_div .current{background-color:#0073A9;border-color:#0073A9;color:#FFF} / 页面数量 */ .totalPages{margin:0 10px} .totalPages span,.totalSize span{color:#0073A9;margin:0 5px} /button禁用/ .page_div button:disabled{opacity:.5;cursor:no-drop} </style></head><body ontouchstart="" onmousemove=""><div value=“1 0”></div><div id=“page” class=“page_div”></div></body><script src=“js/jquery.min.js”></script><script type=“text/javascript” src=“js/page1Me.js”></script><script> $("#page").paging({ // pageNo: 18, // totalPage: 20, // totalSize: 300, pageNum: 5, totalNum: 14, totalList: 300, callback: function (num) { console.log(num); } }); // 模拟ajax数据用以下方法,方便用户更好的掌握用法 // 参数为当前页 // ajaxTest(1); // function ajaxTest(num) { // $.ajax({ // url: “table.json”, // type: “get”, // data: {}, // dataType: “json”, // success: function(data) { // console.log(data); // //分页 // $("#page").paging({ // pageNo: num, // totalPage: data.totalPage, // totalSize: data.totalSize, // callback: function(num) { // ajaxTest(num) // } // }) // } // }) // }</script></html>JS;(function ($, window, document, undefined) { ‘use strict’; function Paging(element, options) { this.element = element; this.options = { pageNum: options.pageNum || 1, // 当前页码 totalNum: options.totalNum, // 总页码 totalList: options.totalList, // 数据总记录 callback: options.callback // 回调函数 }; this.init(); } Paging.prototype = { constructor: Paging, init: function () { this.createHtml(); this.bindEvent(); }, createHtml: function () { var me = this; var content = []; var pageNum = me.options.pageNum; var totalNum = me.options.totalNum; var totalList = me.options.totalList; content.push("<button type=‘button’ id=‘firstPage’>首页</button><button type=‘button’ id=‘prePage’>上一页</button>"); // 总页数大于6必显示省略号 if (totalNum > 6) { // 1、当前页码小于5且总页码大于6 省略号显示后面+总页码 if (pageNum < 5) { // 1与6主要看要显示多少个按钮 目前都显示5个 for (var i = 1; i < 6; i++) { if (pageNum !== i) { content.push("<button type=‘button’>" + i + “</button>”); } else { content.push("<button type=‘button’ class=‘current’>" + i + “</button>”); } } content.push(". . ."); content.push("<button type=‘button’>" + totalNum + “</button>”); } else { // 2、当前页码接近后面 中间隔3个 省略号显示后面+总页面 if (pageNum < totalNum - 3) { for (var i = pageNum - 2; i < pageNum + 3; i++) { if (pageNum !== i) { content.push("<button type=‘button’>" + i + “</button>”); } else { content.push("<button type=‘button’ class=‘current’>" + i + “</button>”); } } content.push(". . ."); content.push("<button type=‘button’>" + totalNum + “</button>”); } else { // 3、页码至少在5,最多在【totalNum - 3】的中间位置 第一页+省略号显示前面 content.push("<button type=‘button’>" + 1 + “</button>”); content.push(". . ."); for (var i = totalNum - 4; i < totalNum + 1; i++) { if (pageNum !== i) { content.push("<button type=‘button’>" + i + “</button>”); } else { content.push("<button type=‘button’ class=‘current’>" + i + “</button>”); } } } } } else { // 总页数小于6 for (var i = 1; i < totalNum + 1; i++) { if (pageNum !== i) { content.push("<button type=‘button’>" + i + “</button>”); } else { content.push("<button type=‘button’ class=‘current’>" + i + “</button>”); } } } content.push("<button type=‘button’ id=‘lastPage’>尾页</button><button type=‘button’ id=‘nextPage’>下一页</button>"); content.push("<span class=‘totalNum’> 共 " + totalNum + " 页 </span>"); content.push("<span class=‘totalList’> 共 " + totalList + " 条记录 </span>"); me.element.html(content.join(’’)); // DOM重新生成后每次调用是否禁用button setTimeout(function () { me.dis(); }, 20); }, bindEvent: function () { var me = this; me.element.off(‘click’, ‘button’); // 委托新生成的dom监听事件 me.element.on(‘click’, ‘button’, function () { var id = $(this).attr(‘id’); var num = parseInt($(this).html()); var pageNum = me.options.pageNum; if (id === ‘prePage’) { if (pageNum !== 1) { me.options.pageNum -= 1; } } else if (id === ’nextPage’) { if (pageNum !== me.options.totalNum) { me.options.pageNum += 1; } } else if (id === ‘firstPage’) { if (pageNum !== 1) { me.options.pageNum = 1; } } else if (id === ’lastPage’) { if (pageNum !== me.options.totalNum) { me.options.pageNum = me.options.totalNum; } } else { me.options.pageNum = num; } me.createHtml(); if (me.options.callback) { me.options.callback(me.options.pageNum); } }); }, dis: function () { var me = this; var pageNum = me.options.pageNum; var totalNum = me.options.totalNum; if (pageNum === 1) { me.element.children(’#firstPage, #prePage’).prop(‘disabled’, true); } else if (pageNum === totalNum) { me.element.children(’#lastPage, #nextPage’).prop(‘disabled’, true); } } }; $.fn.paging = function (options) { return new Paging($(this), options); }})(jQuery, window, document);jQuery插件友情链接// 1、代码最前面的分号,可以防止多个文件压缩合并以为其他文件最后一行语句没加分号,而引起合并后的语法错误。// 2、匿名函数(function(){})();:由于Javascript执行表达式是从圆括号里面到外面,所以可以用圆括号强制执行声明的函数。避免函数体内和外部的变量冲突。// 3、$实参:$是jquery的简写,很多方法和类库也使用$,这里$接受jQuery对象,也是为了避免$变量冲突,保证插件可以正常运行。// 4、window, document实参分别接受window, document对象,window, document对象都是全局环境下的,而在函数体内的window, document其实是局部变量,不是全局的window, document对象。这样做有个好处就是可以提高性能,减少作用域链的查询时间,如果你在函数体内需要多次调用window 或 document对象,这样把window 或 document对象当作参数传进去,这样做是非常有必要的。当然如果你的插件用不到这两个对象,那么就不用传递这两个参数了。// 5、undefined形参了,看起来是有点多余。undefined在老一辈的浏览器是不被支持的,直接使用会报错,js框架要考虑到兼容性,因此增加一个形参undefined ...

April 3, 2019 · 3 min · jiezi

jQuery的遍历结构设计之遍历同胞

前言:光看不行,需要动手操作!一、本文实现的 jQuery 的同胞接口及作用如下接口:$(selector).nextAll() 作用:向后遍历同胞,返回 selector 之后的所有同级元素接口:$(selector).prevAll() 作用:向前遍历同胞,返回 selector 之前的所有同级元素接口:$(selector).nextUntil(otherSelector) 作用: 向后遍历同胞,返回在 selector 和 otherSelector 之间的所有同级元素接口:$(selector).prevUntil(otherSelector) 作用: 向前遍历同胞,返回在 otherSelector 和 selector 之间的所有同级元素接口:$(selector).next() 作用: 返回 selector 的后一个同级元素接口:$(selector).prev() 作用:返回 selector 的前一个同级元素接口:$(selector).siblings() 作用:返回 selector 的所有同胞元素(共享一个父元素,且不包括自己)将这些接口封装在 jQuery 的迭代器中 ↓二、迭代器 迭代器可以理解为一个遍历方法,该方法的第一个参数是一个 object,该对象的每个属性都是一个 function;该方法的第二个参数是一个 callback,该回调函数能够按顺序处理 object 的每个 function,并且不暴露 object 的内部。jQuery 遍历同胞的迭代器代码: let ajQuery = {} jQuery.each({ // nextAll() 方法返回被选元素之后的所有同级元素 // 同级元素是共享相同父元素的元素 //详细请看:http://www.runoob.com/jquery/traversing-nextall.html //nextAll的本质即利用 elem.nextSibling,抓取element节点,向后递归直到elem=null nextAll:function (elem) { return dir(elem, “nextSibling”) }, //prevAll() 方法返回被选元素之前的所有同级元素 //详细请看:http://www.runoob.com/jquery/traversing-prevall.html //prevAll的本质即利用 elem.previousSibling,抓取element节点,向前递归直到elem=null prevAll:function (elem) { return dir(elem, “previousSibling”) }, //返回在类名为 “item-1” 和 “item-4” 的两个 <li> 元素之间的所有同级元素 //详细请看:http://www.runoob.com/jquery/traversing-nextuntil.html //nextUntil的本质即利用 elem.nextSibling,抓取element节点, //向后递归直到elem.nodeName.toLowerCase()===until||elem.className === until nextUntil:function (elem, until) { return dir(elem, “nextSibling”, until) }, //返回在类名为 “item-4” 和 “item-1” 的两个 <li> 元素之间的所有同级元素 //详细请看:http://www.runoob.com/jquery/traversing-prevuntil.html //prevUntil的本质即利用 elem.previousSibling,抓取element节点, //向前递归直到elem.nodeName.toLowerCase()===until||elem.className === until prevUntil: function (elem, until) { return dir(elem, “previousSibling”, until) }, //next:返回被选元素的 后一个同级元素 next: function (elem) { return sibling(elem, “nextSibling”); }, //prev:返回被选元素的 前一个同级元素 prev: function (elem) { return sibling(elem, “previousSibling”); }, //siblings:返回被选元素的 所有同胞元素(共享一个父元素,且不包括自己) siblings: function (elem) { return dir(elem, “siblings”) } }, function(key, value) { ajQuery[key] = function(elem, until) { return value(elem, until); } })三、使用 dir() 和 sibling() 方法来封装 遍历同胞 的方法dir() 封装除 $().next() 和 $().pre() 以外的 遍历同胞的方法: function dir(elem, dir, until) { let matched = [] //是否有另一目标节点 let hasUntil = until !== undefined let sibElem let isSibling=false //$().siblings()的实现有两种方法 //第一种是先 prevAll 向前遍历目标元素的同胞元素,再 nextAll向后遍历目标元素的同胞元素 //第二种是先找到父元素的第一个元素,再 nextAll向后遍历同胞元素,最后将 目标元素从数组中去掉 //这里采用的是第二种方式 if(dir===‘siblings’){ sibElem=elem isSibling=true elem=elem.parentNode.firstElementChild dir=‘nextSibling’ matched.push(elem) } //nextAll:一直nextSibling,直到elem=null,退出循环 while ((elem = elem[dir])&&elem.nodeType !== 9) { if (elem.nodeType === 1) { //nextUntil:true if (hasUntil) { console.log(elem.nodeName.toLowerCase(),elem.className,‘className44’) if (elem.nodeName.toLowerCase() === until || elem.className === until) { console.log(until,‘until46’) break } } console.log(elem,’elem50’) matched.push(elem) } } //循环完后,去除目标元素 if(isSibling){ console.log(sibElem,matched.indexOf(sibElem),‘sibElem66’) matched.splice(matched.indexOf(sibElem),1) } return matched }sibling() 封装 $().next() 和 $().pre() 方法: function sibling(elem, dir) { //过滤掉不是element类型的元素 while ((elem = elem[dir]) && elem.nodeType !== 1) { } return elem }四、完整代码及实例<!DOCTYPE html><html lang=“en”><head> <meta charset=“UTF-8”> <title>jQuery的遍历结构设计之遍历同胞</title></head><body><script src=“jQuery.js”></script><button id=“test1”>模拟遍历同胞</button><button id=“test2”>jQuery遍历同胞</button><ul class=“level-1”> <li class=“item-i”>I</li> <!–目标节点–> <li class=“item-ii”>II <ul class=“level-2”> <li class=“item-a”>A</li> <li class=“item-b”>B <ul class=“level-3”> <li class=“item-1”>1</li> <li class=“item-2”>2</li> <li class=“item-3”>3</li> <li class=“item-4”>4</li> </ul> </li> <li class=“item-c”>C</li> </ul> </li> <li class=“item-iii”>III</li></ul><script type=“text/javascript”> function dir(elem, dir, until) { let matched = [] let truncate = until !== undefined let sibElem let isSibling=false //遍历同胞元素有两种方法 //第一种是先 prevAll 向前遍历目标元素的同胞元素,再 nextAll向后遍历目标元素的同胞元素 //第二种是先找到父元素的第一个元素,再 nextAll向后遍历同胞元素,最后将 目标元素从数组中去掉 //这里采用的是第二种方式 if(dir===‘siblings’){ sibElem=elem isSibling=true elem=elem.parentNode.firstElementChild dir=‘nextSibling’ matched.push(elem) } //nextAll:一直nextSibling,直到elem=null,退出循环 while ((elem = elem[dir])&&elem.nodeType !== 9) { if (elem.nodeType === 1) { //nextUntil:true if (truncate) { console.log(elem.nodeName.toLowerCase(),elem.className,‘className44’) if (elem.nodeName.toLowerCase() === until || elem.className === until) { console.log(until,‘until46’) break } } console.log(elem,’elem50’) matched.push(elem) } } //循环完后,去除目标元素 if(isSibling){ console.log(sibElem,matched.indexOf(sibElem),‘sibElem66’) matched.splice(matched.indexOf(sibElem),1) } return matched } function sibling(elem, dir) { //过滤掉不是element类型的元素 while ((elem = elem[dir]) && elem.nodeType !== 1) { } return elem } let ajQuery = {} jQuery.each({ // nextAll() 方法返回被选元素之后的所有同级元素 // 同级元素是共享相同父元素的元素 //详细请看:http://www.runoob.com/jquery/traversing-nextall.html //nextAll的本质即利用 elem.nextSibling,抓取element节点,向后递归直到elem=null nextAll:function (elem) { return dir(elem, “nextSibling”) }, //prevAll() 方法返回被选元素之前的所有同级元素 //详细请看:http://www.runoob.com/jquery/traversing-prevall.html //prevAll的本质即利用 elem.previousSibling,抓取element节点,向前递归直到elem=null prevAll:function (elem) { return dir(elem, “previousSibling”) }, //返回在类名为 “item-1” 和 “item-4” 的两个 <li> 元素之间的所有同级元素 //详细请看:http://www.runoob.com/jquery/traversing-nextuntil.html //nextUntil的本质即利用 elem.nextSibling,抓取element节点, //向后递归直到elem.nodeName.toLowerCase()===until||elem.className === until nextUntil:function (elem, until) { return dir(elem, “nextSibling”, until) }, //返回在类名为 “item-4” 和 “item-1” 的两个 <li> 元素之间的所有同级元素 //详细请看:http://www.runoob.com/jquery/traversing-prevuntil.html //prevUntil的本质即利用 elem.previousSibling,抓取element节点, //向前递归直到elem.nodeName.toLowerCase()===until||elem.className === until prevUntil: function (elem, until) { return dir(elem, “previousSibling”, until) }, //next:返回被选元素的 后一个同级元素 next: function (elem) { return sibling(elem, “nextSibling”); }, //prev:返回被选元素的 前一个同级元素 prev: function (elem) { return sibling(elem, “previousSibling”); }, //siblings:返回被选元素的 所有同胞元素(共享一个父元素,且不包括自己) siblings: function (elem) { return dir(elem, “siblings”) } }, function(key, value) { ajQuery[key] = function(elem, until) { return value(elem, until); } }) $("#test1").click(function(){ // let item = document.querySelectorAll(’li.item-ii’)[0] // let item = document.querySelectorAll(’li.item-1’)[0] let item = document.querySelectorAll(’li.item-2’)[0] //#text 3 //nodeType=3,而不是1,所以不符合要求 // console.log(item.nextSibling,item.nextSibling.nodeType) // console.log(item.nextSibling.nextSibling,item.nextSibling.nextSibling.nodeType) // console.log(item.nextSibling.nextSibling.nextSibling,item.nextSibling.nextSibling.nextSibling.nodeType) // //null // console.log(item.nextSibling.nextSibling.nextSibling.nextSibling) // console.log(ajQuery.nextAll(item)) // console.log(ajQuery.nextAll(item)[0].className) // console.log(ajQuery.prevAll(item)[0].className) // console.log(ajQuery.nextUntil(item, ‘item-4’),‘item107’) // console.log(ajQuery.nextUntil(item, ‘item-4’)[0].className,‘item108’) // console.log(ajQuery.prevUntil(item, ‘item-2’)[0].className) // // console.log(prev(item)) // console.log(next(item)) // Siblings item.parentNode.firstElementChild console.log(ajQuery.siblings(item,‘siblings’),‘siblings151’) }) $("#test2").click(function(){ let $item = $(’li.item-2’) console.log($item.nextAll()[0].className) console.log($item.prevAll()[0].className) console.log($item.nextUntil(‘item-4’)) console.log($item.nextUntil(‘item-4’)[0].className) console.log($item.prevUntil(‘item-2’)[0].className) console.log($item.prev()) console.log($item.next()) })</script></body></html>五、总结 注: element 表示 jQuery对象selector 的原生 DOM 节点接口:$(selector).nextAll() 本质: 利用 element=element.nextSibling,抓取 element 的同胞节点,并向后递归直到 element=null,此时返回 selector 向后遍历的所有同胞节点接口:$(selector).prevAll() 本质: 利用 element=element.previousSibling,抓取 element 的同胞节点,并向前递归直到 element=null,此时返回 selector 向前遍历的所有同胞节点接口:$(selector).nextUntil(otherSelector) 本质: 利用 element=element.nextSibling,抓取 element 的同胞节点,并向后递归直到 elem.nodeName.toLowerCase()===otherSelector || elem.className === otherSelector,此时返回 selector 向后遍历到 otherSelector 之间的所有同胞节点接口:$(selector).prevUntil(otherSelector) 本质: 利用 element=element.previousSibling,抓取 element 的同胞节点,并向前递归直到 elem.nodeName.toLowerCase()===otherSelector || elem.className === otherSelector,此时返回 selector 向前遍历到 otherSelector 之间的所有同胞节点接口:$(selector).next() 本质: 利用 element=element.nextSibling 和 elem.nodeType !== 1,过滤并抓取 元素类型 的后一个同胞节点接口:$(selector).prev() 本质: 利用 element=element.previousSibling 和 elem.nodeType !== 1,过滤并抓取 元素类型 的前一个同胞节点接口:$(selector).siblings() 本质: 先找到父元素的第一个元素,再 nextAll() 向后遍历同胞元素,最后将 selector 从数组中去掉(完) ...

April 2, 2019 · 4 min · jiezi

jQuery内部对<script>标签的处理

前言:本文只分析 jQuery 调用 append(‘<script>alert(“xxx”)’) 后,jQuery 对 <script> 的处理,关于 append()、domManip()、buildFragment() 等处理 待插入元素的函数,请看:当我调用了$().append()后,jQuery内部发生了什么?1、有这样一段代码:<body> <script src=“jQuery.js”></script> <div class=“inner”></div> <script> $(’.inner’).append("<script>alert(‘append执行script’)") </script></body>2、当调用 $().append("<script>alert(‘append执行script’)") 时,jQuery 内部的流程如下:3、domManip 对 script 的处理源码: function domManip(collection, args, callback, ignored) { args = concat.apply([], args); var fragment, first, scripts, hasScripts, node, doc, i = 0, l = collection.length, iNoClone = l - 1, value = args[0] fragment = buildFragment(args, collection[0].ownerDocument, false, collection, ignored) first = fragment.firstChild if (fragment.childNodes.length === 1) { fragment = first; } scripts = jQuery.map(getAll(fragment, “script”), disableScript); //script 1 /判断是否包含<script>标签/ hasScripts = scripts.length //根据selector的个数循环 for (; i < l; i++) { node = fragment; if ( i !== iNoClone ) { node = jQuery.clone(node, true, true); //如果有script标签了,就将其复制到scripts中 if (hasScripts) { jQuery.merge(scripts, getAll(node, “script”)); } } //此时的node已经是文本节点了 callback.call(collection[i], node, i); } //如果有<script>标签,就解析script if (hasScripts) { console.log(scripts, ‘scripts5932’) //script doc = scripts[scripts.length - 1].ownerDocument; //document jQuery.map(scripts, restoreScript); for (i = 0; i < hasScripts; i++) { node = scripts[i]; if (rscriptType.test(node.type || “”) && //https://www.cnblogs.com/gongshunkai/p/5905917.html !dataPriv.access(node, “globalEval”) && jQuery.contains(doc, node)) { //这边是处理<script scr=’’>的情况的 if (node.src && (node.type || “”).toLowerCase() !== “module”) { // Optional AJAX dependency, but won’t run scripts if not present if (jQuery._evalUrl) { console.log(’_evalUrl’, ‘_evalUrl5950’) jQuery._evalUrl(node.src); } } //一般走的这边 else { console.log(doc, node.nodeType,node, ‘DOMEval5954’) //document 1 <script>alert(‘append执行script’)< /script> DOMEval(node.textContent.replace(rcleanScript, “”), doc, node); } } } } return collection; }解析:(1)domManip 的 buildFragment() 返回文档碎片 fragment 后,会调用 domManip 自定义的 callback() 回调函数,在 <div class=“inner”></div> 中插入 <script>alert(‘append执行script’) 的文本 (2)之后,根据定义的变量 scripts 的长度,判断是否有 <script> 标签scripts = jQuery.map(getAll(fragment, “script”), disableScript)(3)如果待插入元素有 <script> 标签的话,对该元素进行处理(restoreScript ) //去除type标签 function restoreScript( elem ) { if ( ( elem.type || "" ).slice( 0, 5 ) === “true/” ) { elem.type = elem.type.slice( 5 ); } else { elem.removeAttribute( “type” ); } return elem; }(4)如果 <script> 没有属性 src 的话,就执行 DOMEval() 方法:① jQuery 自个儿生成 <script> 标签② 添加 待插入元素的文本③ 执行 appendChild() 方法 //解析script标签 //code:alert(‘append执行script’) #document //doc:document //node:<script>alert(‘append执行script’)< /script> function DOMEval(code, doc, node) { doc = doc || document; let i //创建script标签 let script = doc.createElement(“script”) //添加文本 script.text = code; if (node) { //i:type/src/noModule for (i in preservedScriptAttributes) { if (node[i]) { script[i] = node[i]; } } } //解析script核心代码 doc.head.appendChild(script).parentNode.removeChild(script); }本质:可以看到 jQuery 解析 <script> 的本质即document.head.appendChild(script).parentNode.removeChild(script)4、domManip 的 callback 函数会分两种情况处理 待插入元素(1)$(’.inner’).append("<script>alert(‘append执行script’)")最后在 target.appendChild(elem) 中,插入的不是 <script> 元素,而是 文本"<script>alert(‘append执行script’)" //源码6113行左右 append: function() { return domManip( this, arguments, function( elem ) { … … 最后 console.log(elem.firstChild.nodeType,’target6016’) //3:文本节点 target.appendChild( elem ); }})},(2)$(’.inner’).append("<span>aaa</span>")最后在 target.appendChild(elem) 中,插入的是 <span>aaa</span> 元素 console.log(elem.firstChild.nodeType,’target6016’) //1:元素节点 target.appendChild( elem );5、最后的最后Github:https://github.com/AttackXiao…(完) ...

March 31, 2019 · 2 min · jiezi

黑洞效果的粒子背景效果

简介html canvas实现的粒子效果背景,鼠标点击粒子有弹动效果,设置区块产生黑洞回收粒子效果预览:http://www.jquery66.com/demos…下载地址:https://u18725144.ctfile.com/…

March 29, 2019 · 1 min · jiezi

当我调用了$().append()后,jQuery内部发生了什么?

前言:这篇我们倒着讲1、有这样一个页面:<body> <button id=“test1”>append操作</button> <table class=“inner”> <tbody></tbody> </table></body><script> $(’#test1’).click(function(){ let innerArr=document.querySelectorAll(".inner") ajQuery.append(innerArr,’<tr><td>test1</td></tr>’) })</script>注意:不要 append(<tr>test1</tr>),规范写法是 append(<tr><td>test1</td></tr>)2、像之前的文章一样,我们自定义 append() 方法 let ajQuery={} jQuery.each({ //例:’<p>Test</p>’ //源码6011行-6019行 // 在被选元素的结尾插入指定内容 /append的内部的原理,就是通过创建一个文档碎片,把新增的节点放到文档碎片中,通过文档碎片克隆到到页面上去,目的是效率更高/ append: function(nodelist, arguments) { //node是由domManip处理得到的文档碎片documentFragment,里面包含要插入的DOM节点 let callbackOne=function( node ) { console.log(node,’node149’) //this指的就是$(“xxx”) //1:元素节点,11:DocumentFragment,9:document if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { //table插入tr的额外判断 //target默认情况是selector,即document.querySelectorAll(".inner") let target = manipulationTarget( this, node ) console.log(target,node.childNodes,’node147’) //append的本质即使用原生appendChild方法在被选元素内部的结尾插入指定内容 target.appendChild( node ); } } console.log(nodelist,arguments,’this120’) return domManip( nodelist, arguments, callbackOne ); }, }, function(key, value) { ajQuery[key] = function(nodelist, arguments) { console.log(nodelist,’nodelist128’) return value(nodelist, arguments); } } )3、可以看到,append() 内部调用了 domManip 的方法,接下来重点介绍下该方法(1)什么是 domManip ?domManip() 是 jQuery DOM 的核心函数。dom 即 Dom 元素,Manip 是Manipulate 的缩写,连在一起就是 Dom 操作的意思。(2)它的作用是?domManip() 是用来处理 $().append(xxx)、$().after(xxx) 等操作 DOM 方法的参数的,统一将其处理为 DOM 类型节点,并交由 callback 函数处理,即上图的 callbackOne。注意: 本文暂不考虑参数包含 <script> 的情况,如:ajQuery.append(innerArr,"<script>alert(‘append执行script’)")4、domManip() 的三个参数:nodelist, arguments, callbackOnenodelist:即 document.querySelectorAll(".inner")arguments:即字符串 ‘<tr><td>test1</td><tr>‘callbackOne:回调函数,在 nodelist、arguments 被相应逻辑处理后会返回一个文档碎片documentFragment,该方法会对 该文档碎片进行处理注意:domMainp 函数讲解在 第 8 点。5、callbackOne()作用:将 domManip 返回的 documentFragment 插入到 selector 的内部末尾。也就是说 $().append() 的本质是 DOM节点.appendChild(处理过的documentFragment(里面包含插入的DOM节点))源码: //node是由domManip处理得到的文档碎片documentFragment,里面包含要插入的DOM节点 let callbackOne=function( node ) { console.log(node,’node149’) //this指的就是$(“xxx”) //1:元素节点,11:DocumentFragment,9:document if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { //table插入tr的额外判断 //target默认情况是selector,即document.querySelectorAll(".inner") let target = manipulationTarget( this, node ) console.log(target,node.childNodes,’node147’) //append的本质即使用原生appendChild方法在被选元素内部的结尾插入指定内容 target.appendChild( node ); } }6、callbackOne() 中的函数:manipulationTarget()作用:额外判断,当选择器是table,并且插入的元素是tr时,会查找到table下的tbody,并返回tbody源码: //源码5724行-5733行 //额外判断,当选择器是table,并且插入的元素是tr时,会查找到table下的tbody,并返回tbody //this, node function manipulationTarget( selector, node ) { console.log(node.childNodes,node.firstChild,’node73’) // 如果是table里面插入行tr if ( nodeName( selector, “table” ) && nodeName( node.nodeType !== 11 ? node : node.firstChild, “tr” ) ) { return jQuery( selector ).children( “tbody” )[ 0 ] || selector } return selector }7、manipulationTarget() 中的函数:nodeName()作用:判断两个参数的nodename是否相等源码: //源码2843行-2847行 //判断两个参数的nodename是否相等 function nodeName( selector, name ) { return selector.nodeName && selector.nodeName.toLowerCase() === name.toLowerCase(); }8、jQueryDOM 核心函数:domManip()作用:将传入的参数(dom节点元素、字符串、函数)统一转化为符合要求的DOM节点源码: //源码5597行-5586行 //作用是将传入的参数(dom节点元素、字符串、函数)统一转化为符合要求的DOM节点 //例:$(’.inner’).append(’<tr><td>Test</td></tr>’) //nodelist即$(’.inner’) //args即<tr><td>Test</td></tr> function domManip( nodelist, args, callback ) { console.log(nodelist,args,‘ignored5798’) //数组深复制成新的数组副本 //源码是:args = concat.apply( [], args ),这里没有用arguments,而是传参就改了 let argsArr = [] argsArr.push(args) console.log(argsArr,‘args31’) //l 长度,比如类名为.inner的li有两组,2 let fragment, first, node, i = 0, //l 长度,比如类名为.inner的li有两组,2 l = nodelist.length, iNoClone = l - 1 //l=2 console.log(l,’lll45’) if ( l ) { console.log(argsArr,nodelist[0].ownerDocument,nodelist,‘firstChild40’) //argsArr:<tr><td>test1</td></tr> //nodelist[0].ownerDocument:目标节点所属的文档 fragment = buildFragment(argsArr,nodelist[0].ownerDocument,false,nodelist ); first=fragment.firstChild console.log(fragment.childNodes,‘firstChild42’) //即<tr><td>test1</td></tr> if (first) { //=====根据nodelist的长度循环操作======== for ( ; i < l; i++ ) { console.log(node,fragment.childNodes,‘childNodes49’) node = fragment; if ( i !== iNoClone ) { /createDocumentFragment创建的元素是一次性的,添加之后就不能再操作了, 所以需要克隆iNoClone的多个节点/ node = jQuery.clone( node, true, true ); } console.log(nodelist[i], node.childNodes,’node50’) //call(this,param) callback.call( nodelist[i], node); } //==================== } } console.log(nodelist,’nodelist58’) return nodelist }解析:我们可以看到在 目标节点的个数 >=1 的情况下(if(l){xxx}),调用了 buildFragment() 方法,该方法作用是 创建文档碎片documentFragment,以便高效地向 目标节点 插入元素,然后根据 目标节点个数 循环地调用 callback 方法,即调用 原生 appendChild 方法插入元素。**注意:由于 createDocumentFragment 创建的元素是一次性的,添加之后就成只读的了,所以需要克隆 createDocumentFragment创建的元素,以便再次操作。**关于 documentFragment,请看文章: jQuery之documentFragment9、domManip() 中的函数 buildFragment()作用:创建文档碎片源码: //源码4857行-4945行 /创建文档碎片,原因是一般情况下,我们向DOM中添加新的元素或者节点,DOM会立刻更新。 如果向DOM添加100个节点,那么就得更新100次,非常浪费浏览器资源。 解决办法就是:我们可以创建一个文档碎片(documentFragment), documentFragment类似于一个小的DOM,在它上面使用innerHTML并在innerHTML上插入多个节点,速度要快于DOM(2-10倍), 比如:先将新添加的100个节点添加到文档碎片的innerHTML上,再将文档碎片添加到DOM上。/ //args, collection[ 0 ].ownerDocument, false, collection function buildFragment( arr, context, truefalse, selection ) { let elem,tmp, nodes = [], i = 0, l = arr.length,wrap,tag,j // createdocumentfragment()方法创建了一虚拟的节点对象,节点对象包含所有属性和方法。 //相当于document.createDocumentFragment() let fragment = context.createDocumentFragment() //l=1 console.log(l,’l87’) //============== for ( ; i < l; i++ ) { //’<tr><td></td></tr>’ elem = arr[ i ]; console.log(i,elem,’elem90’) if ( elem || elem === 0 ) { /创建div是为了处理innerHTML的缺陷(IE会忽略开头的无作用域元素), 让所有的元素都被div元素给包含起来,包括script,style等无作用域的元素/ tmp=fragment.appendChild( context.createElement( “div” ) ) //就是匹配div不支持的标签,如 tr、td等 /不支持innerHTML属性的元素,通过正则单独取出处理/ tag = ( rtagName.exec( elem ) || [ “”, "" ] )[ 1 ].toLowerCase(); /作用就是利用wrapMap让不支持innerHTML的元素通过包装wrap来支持innerHTML/ //ie对字符串进行trimLeft操作,其余是用户输入处理 //很多标签不能单独作为DIV的子元素 /td,th,tr,tfoot,tbody等等,需要加头尾/ wrap = wrapMap[ tag ] || wrapMap._default // tr: [ 2, “<table><tbody>”, “</tbody></table>” ] console.log(wrap,‘wrap152’) //将修正好的element添加进innerHTML中 //jQuery.htmlPrefilter:标签转换为闭合标签,如<table> –> <table></table> /div不支持tr、td所以需要添加头尾标签,如<div><table><tbody>xxxx</tbody></table>/ tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; // 因为warp被包装过,需要找到正确的元素父级 j = wrap[ 0 ]; //2 while ( j– ) { tmp = tmp.lastChild; } //temp:<tbody></tbody> //tmp.childNodes:tr //nodes:[] //jQuery.merge:将两个数组合并到第一个数组中 jQuery.merge( nodes, tmp.childNodes ); } } //================ // Remove wrapper from fragment fragment.textContent = “”; //需要将i重置为0 i=0 while ( ( elem = nodes[ i++ ] ) ) { fragment.appendChild( elem ) } console.log(fragment.childNodes,‘fragment105’) return fragment; }解析:(1)创建文档碎片 documentFragmentlet fragment = context.createDocumentFragment()(2)在 待插入的元素存在的情况下,先在 documentFragment 内部插入 <div></div> 标签创建div是为了处理innerHTML的缺陷(IE会忽略开头的无作用域元素),所以让所有的元素都被div元素给包含起来,包括script,style等无作用域的元素(3)但是 <div> 也有不支持的子元素,通过 wrap 筛选并包装这些子元素比如<tr>标签,会被 wrap 转为 <table><tbody></tbody></table>,再成功添加到 documentFragment 的 innerHTML 中。(4)documentFragment 在成功添加完子元素后,再卸磨杀驴,去掉包裹的节点,如上例的<div><table><tbody></tbody></table></div>,保留待插入的节点<tr><td>test1</td></tr>(5)最后返回 处理好的文档碎片 fragment10、rtagName作用:匹配div不支持的标签,如 tr、td等。源码: let rtagName = ( /<([a-z][^/\0>\x20\t\r\n\f]+)/i )11、wrapMap作用:div 不支持的标签表源码: let wrapMap = { // Support: IE <=9 only option: [ 1, “<select multiple=‘multiple’>”, “</select>” ], // XHTML parsers do not magically insert elements in the // same way that tag soup parsers do. So we cannot shorten // this by omitting <tbody> or other required elements. thead: [ 1, “<table>”, “</table>” ], col: [ 2, “<table><colgroup>”, “</colgroup></table>” ], tr: [ 2, “<table><tbody>”, “</tbody></table>” ], td: [ 3, “<table><tbody><tr>”, “</tr></tbody></table>” ], _default: [ 0, “”, "" ] }; // Support: IE <=9 only wrapMap.optgroup = wrapMap.option; wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; wrapMap.th = wrapMap.td;12、jQuery.htmlPrefilter()作用:标签转换为闭合标签,如<table>–><table></table> 源码: htmlPrefilter: function( html ) { return html.replace( rxhtmlTag, “<$1></$2>” ); }13、综上,当我调用了$(’.inner’).append(’<tr><td>test1</td></tr>’)后,jQuery内部发生的事件如下14、本篇文章的所有代码github:https://github.com/AttackXiao…代码:<!DOCTYPE html><html lang=“en”><head> <meta charset=“UTF-8”> <title>jQuery的遍历结构设计之节点操作</title></head><body><script src=“jQuery.js”></script> <button id=“test1”>append操作</button> <table class=“inner”> <!–<tbody></tbody>–> </table><script> //匹配div不支持的标签,如 tr、td等 let rtagName = ( /<([a-z][^/\0>\x20\t\r\n\f]+)/i ); //================================ let wrapMap = { // Support: IE <=9 only option: [ 1, “<select multiple=‘multiple’>”, “</select>” ], // XHTML parsers do not magically insert elements in the // same way that tag soup parsers do. So we cannot shorten // this by omitting <tbody> or other required elements. thead: [ 1, “<table>”, “</table>” ], col: [ 2, “<table><colgroup>”, “</colgroup></table>” ], tr: [ 2, “<table><tbody>”, “</tbody></table>” ], td: [ 3, “<table><tbody><tr>”, “</tr></tbody></table>” ], _default: [ 0, “”, "" ] }; // Support: IE <=9 only wrapMap.optgroup = wrapMap.option; wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; wrapMap.th = wrapMap.td; //================================ //源码5597行-5586行 //作用是将传入的参数(dom节点元素、字符串、函数)统一转化为符合要求的DOM节点 //例:$(’.inner’).append(’<tr><td>test1</tr></td>’) //nodelist(collections)即$(’.inner’) //args即<tr><td>test1</td></tr> function domManip( nodelist, args, callback ) { console.log(nodelist,args,‘ignored5798’) //数组深复制成新的数组副本 //源码是:args = concat.apply( [], args ),这里没有用arguments,而是传参就改了 let argsArr = [] argsArr.push(args) console.log(argsArr,‘args31’) //l 长度,比如类名为.inner的li有两组,2 let fragment, first, node, i = 0, //l 长度,比如类名为.inner的li有两组,2 l = nodelist.length, iNoClone = l - 1 //l=2 console.log(l,’lll45’) if ( l ) { console.log(argsArr,nodelist[0].ownerDocument,nodelist,‘firstChild40’) //argsArr:<p>Test</p> //nodelist[0].ownerDocument:目标节点所属的文档 fragment = buildFragment(argsArr,nodelist[0].ownerDocument,false,nodelist ); first=fragment.firstChild console.log(fragment.childNodes,‘firstChild42’) //即<p>Test</p> if (first) { //=====根据nodelist的长度循环操作======== for ( ; i < l; i++ ) { console.log(node,fragment.childNodes,‘childNodes49’) node = fragment; if ( i !== iNoClone ) { /createDocumentFragment创建的元素是一次性的,添加之后再就不能操作了, 所以需要克隆iNoClone的多个节点/ node = jQuery.clone( node, true, true ); } console.log(nodelist[i], node.childNodes,’node50’) //call(this,param) callback.call( nodelist[i], node); } //==================== } } console.log(nodelist,’nodelist58’) return nodelist } //源码5724行-5733行 //额外判断,当选择器是table,并且插入的元素是tr时,会查找到table下的tbody,并返回tbody //this, node function manipulationTarget( selector, node ) { console.log(node.childNodes,node.firstChild,’node73’) // 如果是table里面插入行tr if ( nodeName( selector, “table” ) && nodeName( node.nodeType !== 11 ? node : node.firstChild, “tr” ) ) { return jQuery( selector ).children( “tbody” )[ 0 ] || selector } return selector } //源码2843行-2847行 //判断两个参数的nodename是否相等 function nodeName( selector, name ) { return selector.nodeName && selector.nodeName.toLowerCase() === name.toLowerCase(); } //源码4857行-4945行 /创建文档碎片,原因是一般情况下,我们向DOM中添加新的元素或者节点,DOM会立刻更新。 如果向DOM添加100个节点,那么就得更新100次,非常浪费浏览器资源。 解决办法就是:我们可以创建一个文档碎片(documentFragment), documentFragment类似于一个小的DOM,在它上面使用innerHTML并在innerHTML上插入多个节点,速度要快于DOM(2-10倍), 比如:先将新添加的100个节点添加到文档碎片的innerHTML上,再将文档碎片添加到DOM上。/ //args, collection[ 0 ].ownerDocument, false, collection function buildFragment( arr, context, truefalse, selection ) { let elem,tmp, nodes = [], i = 0, l = arr.length,wrap,tag,j // createdocumentfragment()方法创建了一虚拟的节点对象,节点对象包含所有属性和方法。 //相当于document.createDocumentFragment() let fragment = context.createDocumentFragment() //l=1 console.log(l,’l87’) //============== for ( ; i < l; i++ ) { //’<tr><td></td></tr>’ elem = arr[ i ]; console.log(i,elem,’elem90’) if ( elem || elem === 0 ) { /创建div是为了处理innerHTML的缺陷(IE会忽略开头的无作用域元素), 让所有的元素都被div元素给包含起来,包括script,style等无作用域的元素/ tmp=fragment.appendChild( context.createElement( “div” ) ) //就是匹配div不支持的标签,如 tr、td等 /不支持innerHTML属性的元素,通过正则单独取出处理/ tag = ( rtagName.exec( elem ) || [ “”, "" ] )[ 1 ].toLowerCase(); /作用就是利用wrapMap让不支持innerHTML的元素通过包装wrap来支持innerHTML/ //ie对字符串进行trimLeft操作,其余是用户输入处理 //很多标签不能单独作为DIV的子元素 /td,th,tr,tfoot,tbody等等,需要加头尾/ wrap = wrapMap[ tag ] || wrapMap._default // tr: [ 2, “<table><tbody>”, “</tbody></table>” ] console.log(wrap,‘wrap152’) //将修正好的element添加进innerHTML中 //jQuery.htmlPrefilter:标签转换为闭合标签,如<table> –> <table></table> /div不支持tr、td所以需要添加头尾标签,如<div><table><tbody>xxxx</tbody></table>/ tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; // 因为warp被包装过,需要找到正确的元素父级 j = wrap[ 0 ]; //2 while ( j– ) { tmp = tmp.lastChild; } //temp:<tbody></tbody> //tmp.childNodes:tr //nodes:[] //jQuery.merge:将两个数组合并到第一个数组中 jQuery.merge( nodes, tmp.childNodes ); } } //================ // Remove wrapper from fragment fragment.textContent = “”; //需要将i重置为0 i=0 while ( ( elem = nodes[ i++ ] ) ) { fragment.appendChild( elem ) } console.log(fragment.childNodes,‘fragment105’) return fragment; } let ajQuery={} jQuery.each({ //例:’<tr><td>test1</td></tr>’ //源码6011行-6019行 // 在被选元素的结尾插入指定内容 /append的内部的原理,就是通过创建一个文档碎片,把新增的节点放到文档碎片中,通过文档碎片克隆到到页面上去,目的是效率更高/ append: function(nodelist, arguments) { //node是由domManip处理得到的文档碎片documentFragment,里面包含要插入的DOM节点 let callbackOne=function( node ) { console.log(node,’node149’) //this指的就是$(“xxx”) //1:元素节点,11:DocumentFragment,9:document if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { //table插入tr的额外判断 //target默认情况是selector,即document.querySelectorAll(".inner") let target = manipulationTarget( this, node ) console.log(target,node.childNodes,’node147’) //append的本质即使用原生appendChild方法在被选元素内部的结尾插入指定内容 target.appendChild( node ); } } console.log(nodelist,arguments,’this120’) return domManip( nodelist, arguments, callbackOne ); }, }, function(key, value) { ajQuery[key] = function(nodelist, arguments) { console.log(nodelist,’nodelist128’) return value(nodelist, arguments); } } ) $(’#test1’).click(function(){ let innerArr=document.querySelectorAll(".inner") ajQuery.append(innerArr,’<tr><td>test1</td></tr>’) })</script></body></html>(完) ...

March 27, 2019 · 6 min · jiezi

jQuery之documentFragment

前言:documentFragment 在 jQuery 中的 buildFragment() 方法中总是用到,不了解它的含义话,读源码会比较困难,写文章以记之。1、documentFragment含义:documentFragment 是一个轻量级的文档对象,能够提取部分文档的树或创建一个新的文档片段,换句话说有文档缓存的作用。特征:(1)documentFragment 节点不属于文档树,继承的 parentNode 属性总是 null。(这一点在查找祖先节点大有用处)(2)把一个 documentFragment 节点插入文档树时,插入的不是 documentFragment 自身,而是它的所有子孙节点。这一特点与 React.Fragment 非常类似。(React.Fragment:https://www.jianshu.com/p/d4f…)这使得 documentFragment 成了有用的占位符,暂时存放那些一次插入文档的节点。2、<document>.createDocumentFragment() 方法作用:一般情况下,我们向 DOM 中添加新的元素或者节点,DOM会立刻更新。如果向DOM添加 100 个节点,那么就得更新 100 次,非常浪费浏览器资源。解决办法就是:我们可以创建一个文档碎片(documentFragment),documentFragment 类似于一个小的 DOM,在它上面使用 innerHTML 并在 innerHTML 上插入多个节点,速度要快于 DOM(2-10 倍),举个例子:<body><script src=“jQuery.js”></script><button id=“Button1” onclick = “a1()">普通方式创建</button><button id=“Button2” onclick = “a2()">documentFragment创建</button><div id=“test1”></div><div id=“test2”></div><script type=“text/javascript”> function a1() { console.time(“普通方式创建”) for (let i = 0; i < 5000; i++) { let op = document.createElement(“span”); let oText = document.createTextNode(i); op.appendChild(oText); document.body.appendChild(op); } console.timeEnd(“普通方式创建”) } function a2() { console.time(“documentFragment创建”) let oFragmeng = document.createDocumentFragment(); //创建文档碎片 for (let i = 0; i < 5000; i++) { let op = document.createElement(“span”); let oText = document.createTextNode(i); op.appendChild(oText); oFragmeng.appendChild(op); } document.body.appendChild(oFragmeng); //最后一次性添加到document中 console.timeEnd(“documentFragment创建”) }</script>我们可以看到,在 jQuery3.3.1 中的 buildFragment 方法中,运用了这一方法,来使得 $.append() 、$.after() 等方法更加快速高效。 //源码4857行-4945行 function buildFragment( arr, context, truefalse, selection ) { let elem,tmp, nodes = [], i = 0, l = arr.length // createdocumentfragment()方法创建了一虚拟的节点对象,节点对象包含所有属性和方法。 //相当于document.createDocumentFragment() let fragment = context.createDocumentFragment() for ( ; i < l; i++ ) { elem = arr[ i ]; if ( elem || elem === 0 ) { tmp=fragment.appendChild( context.createElement( “div” ) ); tmp.innerHTML =jQuery.htmlPrefilter(elem) jQuery.merge( nodes, tmp.childNodes ); } } // Remove wrapper from fragment fragment.textContent = “”; //需要将i重置为0 i=0 while ( ( elem = nodes[ i++ ] ) ) { fragment.appendChild( elem ) } return fragment; }3、createElement() 和 createDocumentFragment 的区别(1)innerHTMLcreateElement 创建的元素可以使用 innerHTML;createDocumentFragment 创建的元素使用 innerHTML 不能达到修改文档内容的效果,只能作为一个属性(2)DOM重复操作createElement 创建的元素添加到文档后可以重复操作;createDocumentFragment 创建的元素是一次性的,添加之后就不能操作了因此,在 jQuery 源码的 domMainp() 方法中,运用了 createDocumentFragment 后,需要 clone 节点,来进行操作://源码5900行左右if ( i !== iNoClone ) { /createDocumentFragment创建的元素是一次性的,添加之后再就不能操作了, 所以需要克隆iNoClone的多个节点/ node = jQuery.clone( node, true, true ); console.log(i,iNoClone,‘iNoClone5884’) // Keep references to cloned scripts for later restoration if ( hasScripts ) { // Support: Android <=4.0 only, PhantomJS 1 only // push.apply(_, arraylike) throws on ancient WebKit jQuery.merge( scripts, getAll( node, “script” ) ); }}(完) ...

March 25, 2019 · 2 min · jiezi

jQuery 方法整理

jQuery 简介jQuery 是一个轻量级操作 DOM 的 JS 库,主要包含以下功能:HTML 元素选取和操作HTML 事件函数HTML DOM 遍历和修改CSS 操作JavaScript 特效和动画AJAX基于 jQuery 的插件jQuery 的优势在于兼容于所有主流浏览器, 包括 Internet Explorer 6!jQuery 语法$(selector).action()文档加载就绪事件$(document).ready(function() { // 代码…});// 简写方式$(function() { // 代码…});$(document).ready 与 window.onload 的区别$(document).ready 和 window.onload 都是在都是在页面加载完执行的函数,大多数情况下差别不大。$(document).ready:是 DOM 结构绘制完毕后就执行,不必等到加载完毕。 意思就是 DOM 树加载完毕,就执行,不必等到页面中图片或其他外部文件都加载完毕。并且可以写多个.ready。window.onload:是页面所有元素都加载完毕,包括图片等所有元素。只能执行一次。jQuery 选择器jQuery 选择器基于已经存在的 CSS 选择器$(’*’)$(‘p’)$(‘ul li’)$(‘ul li:last-child’)…jQuery 事件鼠标事件键盘事件表单事件文档/窗口事件clickkeypresssubmitloaddbclickkeydownchangeresizemouseenterkeyupfocusscrollmouseleave blurunloadhover 常用的 jQuery 事件方法click() 点击事件dbclick() 双击事件mouseenter() 鼠标穿过元素事件mouseleave() 鼠标离开元素事件mousedown() 鼠标移动到元素上方按下鼠标事件mouseup() 鼠标按住移动到元素上方松开鼠标事件hover() 鼠标悬停事件focus() 表单元素聚焦事件blur() 表单元素失去焦点事件submit() 表单提交事件change() 表单元素值改变事件keypress() 键盘键按住事件keydown() 键盘键按下事件keyup() 键盘键松开事件load() 指定元素加载完成式执行事件 (1.8 版本后废弃)resize() 窗口大小改变事件scroll() 滚动监听事件jQuery 效果$(selector).action(speed,callback)变量说明selector选择器action事件speed速度,毫秒,也可以为‘slow’、‘fast‘callback回掉函数显示隐藏hide() 隐藏元素show() 显示元素toggle() 显示被隐藏的元素,隐藏已显示的元素淡入淡出fadeIn() 淡入fadeOut() 淡出fadeToggle() 已淡出的元素淡入,已淡入的元素淡出fadeTo() 渐变为给定不透明度$(selector).fadeTo(speed,opacity,callback);opacity 值为 0 与 1 之间滑动slideDown() 向下滑动展开元素slideDown() 向上滑动收起元素slideToggle() 已展开元素上滑收起,已收起元素下滑展示动画$(selector).animate({params},speed,callback);参数说明是否必须params定义形成动画的 css 属性必须speed速度,毫秒,也可以为‘slow’、‘fast‘可选callback回掉函数可选实例$(“button”).click(function() { $(“div”).animate({ left: “250px”, opacity: “0.5”, height: “150px”, width: “150px” });});停止动画$(selector).stop(stopAll, goToEnd);参数说明是否必须stopAll是否应该清除动画队列,默认是 false可选goToEnd是否立即完成当前动画可选jQuery 链(Chaining)通过 jQuery,可以把动作/方法链接在一起。Chaining 允许我们在一条语句中运行多个 jQuery 方法(在相同的元素上,浏览器就不必多次查找相同的元素。$("#p1") .css(“color”, “red”) .slideUp(2000) .slideDown(2000);// “p1” 元素首先会变为红色,然后向上滑动,再然后向下滑动jQuery HTMLjQuery 提供一系列与 DOM 相关的方法,这使访问和操作元素和属性变得很容易。获取内容和属性获取内容text() 设置或返回所选元素的文本内容html() 设置或返回所选元素的内容(包括 HTML 标记)val() 设置或返回表单字段的值获取属性attr() 设置或者返回所选的属性的值// 获取属性$(’#test’).attr(‘href’)// 设置属性$(’#test’).attr(‘href’,‘http://www.baidu.com’)$(’#test’).attr({ href: ‘http://www.baidu.com’, title: ‘百度’})// 回掉函数$(’#test’).attr(‘href’, function(i, origValue){ // i 被选元素列表中当前元素的下标 // origValue 原始值 return origValue + ‘/jquery’ })添加删除元素append() 在被选元素的结尾插入内容prepend() 在被选元素的开头插入内容after() 在被选元素之后插入内容before() 在被选元素之前插入内容 remove() 删除被选元素(及其子元素)empty() 从被选元素中删除子元素jQuery remove() 方法也可接受一个参数,允许您对被删元素进行过滤。该参数可以是任何 jQuery 选择器的语法。下面的代码表示删除所有 p 元素中类名是 italic 的元素$(‘p’).remove(’.italic’)获取并设置 css 类addClass() 向被选元素添加一个或多个类removeClass() 从被选元素删除一个或多个类toggleClass() 对被选元素进行添加/删除类的切换操作css() 设置或返回样式属性// 返回样式属性$(“p”).css(“background-color”);// 设置样式属性$(“p”).css(“background-color”, “yellow”);// 或者$(“p”).css({ “background-color”: “yellow”, “font-size”: “200%” });尺寸方法width() 元素宽度height() 元素高度innerWidth() 包含 padding 宽度innerHeight() 包含 padding 高度outerWidth() 包含 padding、border 宽度outerHeight() 包含 padding、border 高度outerWidth(true) 包含 padding、border、margin 宽度outerHeight(true) 包含 padding、border、margin 高度元素遍历祖先元素:parent() 返回被选元素的直接父元素,该方法只会向上一级对 DOM 树进行遍历。parents() 返回被选元素的所有祖先元素,它一路向上直到文档的根元素 (<html>)。parentsUntil() parentsUntil() 方法返回介于两个给定元素之间的所有祖先元素。$(document).ready(function() { // div > ul > li > span $(“span”).parentsUntil(“div”); // 返回 ul 和 li});后代元素:children() 返回被选元素的所有直接子元素。find() 方法返回被选元素的后代元素,一路向下直到最后一个后代。$(document).ready(function() { $(“div”).find(“span”);});同胞元素:siblings() 返回被选元素的所有同胞元素。next() 返回被选元素的下一个同胞元素。nextAll() 返回被选元素的所有跟随的同胞元素。nextUntil() 返回介于两个给定参数之间的所有跟随的同胞元素。prev() 返回被选元素的上一个同胞元素。prevAll() 返回被选元素之前的所有的同胞元素。prevUntil() 返回介于两个给定参数之间的所有前方的同胞元素。元素过滤:first() 返回被选元素的首个元素。last() 返回被选元素的最后一个元素。eq() 返回被选元素中带有指定索引号的元素。filter() 方法允许您规定一个标准。不匹配这个标准的元素会被从集合中删除,匹配的元素会被返回。not() 方法返回不匹配标准的所有元素。 ...

March 20, 2019 · 2 min · jiezi

用vue2.x版本+adminLTE开源框架 搭建后台应用模版

1、创建工程npm install –global vue-cli 安装脚手架vue init webpack vue-adminlte 初始化webpack 项目cd vue-adminlte 切换项目文件夹下npm install 安装依赖库npm run dev 运行项目2、安装 jquerynpm install jquery —save-dev并在build/webpack.base.conf.js中, 引入webpackvar webpack = require(‘webpack’);以及在当前文件下找到 module.exports 中 》resolve 〉alias 增加 如下alias: {‘vue$’: ‘vue/dist/vue.esm.js’,’@’: resolve(‘src’),‘components’: path.resolve(__dirname, ‘../src/components’),‘jquery’:path.resolve(__dirname,’../node_modules/jquery/src/jquery’)},//并增加如下:plugins: [ new webpack.ProvidePlugin({ $: “jquery”, jQuery: “jquery”, “windows.jQuery”:“jquery” })]然后在main.js中引入:import $ from ‘jquery’window.$ = $window.jQuery = $如果报eslint的错误,可能搭建项目时开启了es > 注释掉有关eslint检查将 createLintingRule 删掉(去除eslint检查 首选项 》设置)“vetur.validation.template”: false, “eslint.enable”: false不报错之后 测试一下 看是否真正的引进了jquery3、安装bootstrapnpm i bootstrap@3.3.0 –save在build/webpack.base.conf.js中 对比一下看是否齐全:alias: {‘vue$’: ‘vue/dist/vue.esm.js’,’@’: resolve(‘src’),‘components’: path.resolve(__dirname, ‘../src/components’),‘jquery’:path.resolve(_dirname,’../node_modules/jquery/src/jquery’)},引入: 在main.js中import ‘bootstrap/dist/css/bootstrap.min.css’import ‘bootstrap/dist/js/bootstrap.min.js'4、安装 font-awesomenpm i font-awesome –save引入: 在main.js中import ‘font-awesome/css/font-awesome.css'5、如果过程中报错 This dependency was not found: * !!vue-style-loader!css-loader。。npm install stylus-loader css-loader style-loader –save-dev如果还是如下:This dependency was not found: * !!vue-style-loader!css-loader?{“sourceMap”:true}!../../node_modules/vue-loader/lib/style-compiler/index?{“vue”:true,“id”:“data-v-3c7429a2”,“scoped”:false,“hasInlineConfig”:false}!sass-loader?{“sourceMap”:true}!../../node。。安装如下代码npm i sass-loader –savenpm i node-sass –save6、下载 /安装 adminLTE GitHub:https://github.com/almasaeed2…git下载:git clone https://github.com/almasaeed2…也可npm安装:(推荐)npm i admin-lte –save-dev引入: 在main.js中import ‘admin-lte/dist/css/AdminLTE.min.css’import ‘admin-lte/dist/css/skins/_all-skins.min.css’import ‘admin-lte/dist/js/adminlte.min'7、精简你的代码 测试是否引入 bootstrap、font-awesome启动项目 npm run dev8、那就开始 引用adminLTE中的 你要用的 一些代码吧在components下新建starter.vue文件打开node-modules》admin-lte》starter.html并复制整段的wrapper内容,放到starter.html中template下再复制 body的class类名 放到index.html中的body中修改路由信息 src/router/index.js9、保存 运行 npm run dev不用惊慌 打开App.vue10、其他什么 自己需要的页面 直接拿过来 修修改改吧node-modules 》admin-lte 》dist ...

March 15, 2019 · 1 min · jiezi

工作总结(一)

主要记录一些工作上碰到的问题,不定期跟新1、因为工作调接口,后端返回字段经常变化,今天遇到这样一个问题。// 判断返回 arr ,如果是 null 或 [] 空数组就显示’’// 假设返回字段是 usersName// 初始想法,通过if语句,进行判断,确认返回值。var userName = [];var str = ‘’;if (userName) { alert(123); str = userName.join();}// 这样可以实现需求,但是忽然想到一个细节,这个方法只能判断null为false,但是判断userName 空数组则为ture。// 如果不是简单的执行join()方法,逻辑上肯定会报错。其原理是因为,null是对象空指针,// 布尔值为false,而[]空数组是一个数组对象,布尔值为ture,所以会执行if里边的语句。

March 12, 2019 · 1 min · jiezi

原生 js 获取 dom 元素 querySelector() 替代 getElementById()

原生 js 获取 dom 元素 querySelector() 替代 getElementById()替代 getElementById()很长一段时间以来,除了 jQuery 的选择器之外,我一直在用下面这几个方法获取 dom 元素document.getElementById()document.getElementsByClassName()document.getElementsByTagName()document.getElementsByName()后来才发现 querySelector() 这个方法,这个方法跟 jquery 的获取元素方法是一样的。里面填写的是 css 选择器。比如,下面这几个获取的元素是一样的:// getElementById() 方式document.getElementById(‘username’);// querySelector() 方式document.querySelector(’#username’);// jquery 方式$(’#username’)[0] // 不理解这个可以百度 jquery 与 dom 相互转换querySelector() 有两种方式querySelector( css选择器字符串 ) // 获取第一个匹配元素querySelectorAll( css选择器字符串 ) // 获取所有匹配元素效果如图:其获取元素的方式跟 jquery 很像,但取到的元素并不一样,jquery 取得的是 jquery 元素,而 querySelector() 获取的是 dom 对象。例子关于选择器,参阅: http://www.w3school.com.cn/cs…比如,现在需要获取 所有 class 以 text- 开头的元素,也就是说包含 text-success,text-danger,text-warning 等元素,就这样写:document.querySelectorAll("[class^=‘text-’]")

March 11, 2019 · 1 min · jiezi

jQuery实现记住帐号密码功能

记住密码是每个有帐号登录的网站必备的,现在说一下通过COOKIE实现的记住密码功能。<!DOCTYPE html><html><head> <meta charset=“utf-8”> <title>COOKIE</title></head><body><script type=“text/javascript” src=“jquery-3.3.1.min.js”></script><script type=“text/javascript” src=“jquery.cookie.js”></script><script type=“text/javascript”>//读取cookievar user = $.cookie(‘uu’);var pwd = $.cookie(‘pp’);$(document).ready(function(){ // 判断是否存在cookie if (user) { $(“input:text”).val(user); $(“input:password”).val(pwd); $("#che").html("<input type="checkbox" onclick="uncheck()" id="check1" checked/>"); }});// 选中记住密码function check(){ $("#che").html("<input type="checkbox" onclick="uncheck()" id="check1"/>"); // 设置为选中状态 document.getElementById(“check1”).checked=true; // 创建一个cookie并设置有效时间为 7天 $.cookie(‘uu’, $(“input:text”).val(), { expires: 7 }); $.cookie(‘pp’, $(“input:password”).val(), { expires: 7 }); }// 取消记住密码function uncheck(){ $("#che").html("<input type="checkbox" onclick="check()" id="check1"/>"); // 设置为取消状态 document.getElementById(“check1”).checked=false; // 删除cookie $.cookie(‘uu’,’’); $.cookie(‘pp’,’’); }</script><input type=“text” name=“username” placeholder=“帐号”><br/><input type=“password” name=“password” placeholder=“密码”><br/>记住密码:<div id=“che”><input type=“checkbox” onclick=“check()” id=“check1”/></div><br/></body></html>只要在表单输入帐号密码,再勾选记住密码,那么你的帐号密码就已经被存入到cookie了,有效期7天。然后你刷新页面,发现帐号密码还在表单中,不会被清空。demo:http://likeyunba.com/m/Login-…作者:TANKINGweb:http://likeyunba.com时间:2019-03-09 ...

March 9, 2019 · 1 min · jiezi

踩坑vue国际化(V18n)+ jquery国际化(jquert.i18n.properties.js)

目前公司在搞国际化,虽然刚开始接触,但还是遇到了一些问题,如对你有帮助,烦请点个赞,谢谢。先分享一下vue的国际化,目前vue的国际化采用的是vue-i18n。首先新建一个存放语言的文件目录,把提取后的中文、英文放在对应的文件中如下图:通过Vue.use调用内部install方法,最后别忘了在main.js中引入,绑定在vue实例上。在组件的html中语法:在js中语法:在js中调用 this.$i18n.locale = language(例:en_US,跟语言文件export出的对象名称保持一致)实现修改语言。注意这里:1、养成良好的变成喜欢,在写逻辑判断的时候,不要根据中文去判断,不然做国际化要改起来很麻烦。比如使用if (xxx === ‘中文’)、xxx.indexof(‘中文’)等;2、在使用V18n的时候我发现,在页面created之后mounted之前V18n才执行的,就意味着有些人在data()里面用中文初始化了一些属性,但是此时V18n还没有执行,于是一些属性被赋值成了$t(‘xxxxx’),即键值。如图,我在data中初始化了title属性,然后在created时候打印该属性,发现控制台报了2个警告,然后该属性被赋值成了键值。解决方案就是:在data中初始化时不指定默认值,在mounted的时候进行赋值就ok了之后就可以手动调用this.$i18n.locale = en_US方法实现中英文切换了,也可以根据cookie去赋值.分享一个kiwi插件,kiwi是一款提取替换中文的插件,喜欢的也可以试试,十分好用,大家可以看一下链接中的文档。最后简单说一下jq的国际化吧,引入jquery.i18n.properties.js文件,配置项如下: function loadI18nProperties(lang) { $.i18n.properties({ name: ‘strings’, // 对应国际化文件名称 path: ‘/static/js/i18n/’, // 对应国际化文件目录 mode : ‘map’, //用Map的方式使用资源文件中的值 language: lang, // 调用国际化语言 callback: function() { // 回调函数 } }); $(document).ready(function() { loadI18nProperties(‘zh_CN’); });name属性指的是国际化的文件名,jq的存放语言的文件是.properties为后缀的,以上面的例子,语言文件名为strings_en_US、strings_zh_CN。如果path写的不对的话,会报一个跨域的错误,提示让你去修改服务器配置文件去支持.properties文件,这里注意一下就好了。其他的在html中使用自定义属性方式赋值,js中就是简单的变量的方式。修改语言,就是将loadI18nProperties(lang)传递参数就去就可以了。// html<h1 data-i18n=“UserName”></h1>// js中var Title = $.i18n.prop(‘js.UserName’);

March 8, 2019 · 1 min · jiezi

如何使用jQuery封装插件

jQuery官方给了一套对象级别开发插件的模板 。如果使用的是 sublimeText 编辑器,推荐安装插件 jQuery,在文件中输入 plugin + Enter 会自动生成代码片段。安装成功后在 js 文件中输入 plugin ,会出现下图所示内容: 选择 plugin (method basic),出现以下内容:(function($) {// What does the pluginName plugin do?$.fn.pluginName = function(options) { if (!this.length) { return this; } var opts = $.extend(true, {}, $.fn.pluginName.defaults, options); this.each(function() { var $this = $(this); }); return this;};// default options$.fn.pluginName.defaults = { defaultOne: true, defaultTwo: false, defaultThree: ‘yay!’};})(jQuery);给插件起个名字,添加到红框内 ,在绿框内设置所需的参数,在蓝框内编写插件的主方法。 在 HTML 中调用该插件:引入 jQuery 和插件 js 文件,选择 DOM 元素,调用插件。可以参考下面这个封装插件的实例:<!DOCTYPE html><html lang=“en”><head> <meta charset=“UTF-8”> <title>jQplugin</title></head><body> <div class=“box”> <input type=“button” class=“btn1” value=“btn1”> <input type=“button” class=“btn2” value=“btn2”> </div></body><script src=“https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script><script>// 插件封装(function($) { // What does the pluginName plugin do? $.fn.pluginName = function(options) { if (!this.length) { return this; } var opts = $.extend(true, {}, $.fn.pluginName.defaults, options); this.each(function() { var $this = $(this); $(opts.btn1).click(function(event) { alert($(this).val()); }); $(opts.btn2).click(function(event) { alert($(this).val()); }); }); return this; }; // default options $.fn.pluginName.defaults = { btn1: null, btn2: null };})(jQuery);// 调用插件$(function() { $(".box”).pluginName({ btn1: “.btn1”, btn2: “.btn2” })});</script></html>期待您的关注! ...

February 18, 2019 · 1 min · jiezi

九种跨域方式实现原理

前言前后端数据交互经常会碰到请求跨域,什么是跨域,以及有哪几种跨域方式,这是本文要探讨的内容。一、什么是跨域?1.什么是同源策略及其限制内容?同源策略是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。所谓同源是指”协议+域名+端口”三者相同,即便两个不同的域名指向同一个ip地址,也非同源。 同源策略限制内容有:Cookie、LocalStorage、IndexedDB 等存储性内容DOM 节点AJAX 请求发送后,结果被浏览器拦截了但是有三个标签是允许跨域加载资源<img src=XXX><link href=XXX><script src=XXX>2.常见跨域场景当协议、子域名、主域名、端口号中任意一个不相同时,都算作不同域。不同域之间相互请求资源,就算作“跨域”。常见跨域场景如下图所示: 特别说明两点:第一:如果是协议和端口造成的跨域问题“前台”是无能为力的。第二:在跨域问题上,仅仅是通过“URL的首部”来识别而不会根据域名对应的IP地址是否相同来判断。“URL的首部”可以理解为“协议, 域名和端口必须匹配”。这里你或许有个疑问:请求跨域了,那么请求到底发出去没有?跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。你可能会疑问明明通过表单的方式可以发起跨域请求,为什么 Ajax 就不会?因为归根结底,跨域是为了阻止用户读取到另一个域名下的内容,Ajax 可以获取响应,浏览器认为这不安全,所以拦截了响应。但是表单并不会获取新的内容,所以可以发起跨域请求。同时也说明了跨域并不能完全阻止 CSRF,因为请求毕竟是发出去了。二、跨域解决方案1.jsonp1) JSONP原理利用<script> 标签没有跨域限制的漏洞,网页可以得到从其他来源动态产生的 JSON 数据。JSONP请求一定需要对方的服务器做支持才可以。2) JSONP和AJAX对比JSONP和AJAX相同,都是客户端向服务器端发送请求,从服务器端获取数据的方式。但AJAX属于同源策略,JSONP属于非同源策略(跨域请求)3) JSONP优缺点JSONP优点是简单兼容性好,可用于解决主流浏览器的跨域数据访问的问题。缺点是仅支持get方法具有局限性,不安全可能会遭受XSS攻击。4) JSONP的实现流程声明一个回调函数,其函数名(如show)当做参数值,要传递给跨域请求数据的服务器,函数形参为要获取目标数据(服务器返回的data)。创建一个<script>标签,把那个跨域的API数据接口地址,赋值给script的src,还要在这个地址中向服务器传递该函数名(可以通过问号传参:?callback=show)。服务器接收到请求后,需要进行特殊的处理:把传递进来的函数名和它需要给你的数据拼接成一个字符串,例如:传递进去的函数名是show,它准备好的数据是show(‘我不爱你’)。最后服务器把准备的数据通过HTTP协议返回给客户端,客户端再调用执行之前声明的回调函数(show),对返回的数据进行操作。在开发中可能会遇到多个 JSONP 请求的回调函数名是相同的,这时候就需要自己封装一个 JSONP函数。// index.htmlfunction jsonp({ url, params, callback }) { return new Promise((resolve, reject) => { let script = document.createElement(‘script’) window[callback] = function(data) { resolve(data) document.body.removeChild(script) } params = { …params, callback } // wd=b&callback=show let arrs = [] for (let key in params) { arrs.push(${key}=${params[key]}) } script.src = ${url}?${arrs.join('&amp;')} document.body.appendChild(script) })}jsonp({ url: ‘http://localhost:3000/say’, params: { wd: ‘Iloveyou’ }, callback: ‘show’}).then(data => { console.log(data)})上面这段代码相当于向http://localhost:3000/say?wd=Iloveyou&callback=show这个地址请求数据,然后后台返回show(‘我不爱你’),最后会运行show()这个函数,打印出’我不爱你’// server.jslet express = require(’express’)let app = express()app.get(’/say’, function(req, res) { let { wd, callback } = req.query console.log(wd) // Iloveyou console.log(callback) // show res.end(${callback}('我不爱你'))})app.listen(3000)5) jQuery的jsonp形式JSONP都是GET和异步请求的,不存在其他的请求方式和同步请求,且jQuery默认就会给JSONP的请求清除缓存。$.ajax({url:“http://crossdomain.com/jsonServerResponse",dataType:"jsonp",type:"get",//可以省略jsonpCallback:“show”,//->自定义传递给服务器的函数名,而不是使用jQuery自动生成的,可省略jsonp:“callback”,//->把传递函数名的那个形参callback,可省略success:function (data){console.log(data);}});2.corsCORS 需要浏览器和后端同时支持。IE 8 和 9 需要通过 XDomainRequest 来实现。浏览器会自动进行 CORS 通信,实现 CORS 通信的关键是后端。只要后端实现了 CORS,就实现了跨域。服务端设置 Access-Control-Allow-Origin 就可以开启 CORS。 该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。虽然设置 CORS 和前端没什么关系,但是通过这种方式解决跨域问题的话,会在发送请求时出现两种情况,分别为简单请求和复杂请求。1) 简单请求只要同时满足以下两大条件,就属于简单请求条件1:使用下列方法之一:GETHEADPOST条件2:Content-Type 的值仅限于下列三者之一:text/plainmultipart/form-dataapplication/x-www-form-urlencoded请求中的任意 XMLHttpRequestUpload 对象均没有注册任何事件监听器; XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问。2) 复杂请求不符合以上条件的请求就肯定是复杂请求了。 复杂请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为”预检”请求,该请求是 option 方法的,通过该请求来知道服务端是否允许跨域请求。我们用PUT向后台请求时,属于复杂请求,后台需做如下配置:// 允许哪个方法访问我res.setHeader(‘Access-Control-Allow-Methods’, ‘PUT’)// 预检的存活时间res.setHeader(‘Access-Control-Max-Age’, 6)// OPTIONS请求不做任何处理if (req.method === ‘OPTIONS’) { res.end() }// 定义后台返回的内容app.put(’/getData’, function(req, res) { console.log(req.headers) res.end(‘我不爱你’)})接下来我们看下一个完整复杂请求的例子,并且介绍下CORS请求相关的字段// index.htmllet xhr = new XMLHttpRequest()document.cookie = ’name=xiamen’ // cookie不能跨域xhr.withCredentials = true // 前端设置是否带cookiexhr.open(‘PUT’, ‘http://localhost:4000/getData’, true)xhr.setRequestHeader(’name’, ‘xiamen’)xhr.onreadystatechange = function() { if (xhr.readyState === 4) { if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) { console.log(xhr.response) //得到响应头,后台需设置Access-Control-Expose-Headers console.log(xhr.getResponseHeader(’name’)) } }}xhr.send()//server1.jslet express = require(’express’);let app = express();app.use(express.static(__dirname));app.listen(3000);//server2.jslet express = require(’express’)let app = express()let whitList = [‘http://localhost:3000’] //设置白名单app.use(function(req, res, next) { let origin = req.headers.origin if (whitList.includes(origin)) { // 设置哪个源可以访问我 res.setHeader(‘Access-Control-Allow-Origin’, origin) // 允许携带哪个头访问我 res.setHeader(‘Access-Control-Allow-Headers’, ’name’) // 允许哪个方法访问我 res.setHeader(‘Access-Control-Allow-Methods’, ‘PUT’) // 允许携带cookie res.setHeader(‘Access-Control-Allow-Credentials’, true) // 预检的存活时间 res.setHeader(‘Access-Control-Max-Age’, 6) // 允许返回的头 res.setHeader(‘Access-Control-Expose-Headers’, ’name’) if (req.method === ‘OPTIONS’) { res.end() // OPTIONS请求不做任何处理 } } next()})app.put(’/getData’, function(req, res) { console.log(req.headers) res.setHeader(’name’, ‘jw’) //返回一个响应头,后台需设置 res.end(‘我不爱你’)})app.get(’/getData’, function(req, res) { console.log(req.headers) res.end(‘我不爱你’)})app.use(express.static(__dirname))app.listen(4000)上述代码由http://localhost:3000/index.html向http://localhost:4000/跨域请求,正如我们上面所说的,后端是实现 CORS 通信的关键。3.postMessagepostMessage是HTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,它可用于解决以下方面的问题:页面和其打开的新窗口的数据传递多窗口之间消息传递页面与嵌套的iframe消息传递上面三个场景的跨域数据传递postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。otherWindow.postMessage(message, targetOrigin, [transfer]);message: 将要发送到其他 window的数据。targetOrigin:通过窗口的origin属性来指定哪些窗口能接收到消息事件,其值可以是字符串””(表示无限制)或者一个URI。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。transfer(可选):是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。接下来我们看个例子: http://localhost:3000/a.html页面向http://localhost:4000/b.html传递“我爱你”,然后后者传回”我不爱你”。// a.html <iframe src=“http://localhost:4000/b.html” frameborder=“0” id=“frame” onload=“load()"></iframe> //等它加载完触发一个事件 //内嵌在http://localhost:3000/a.html <script> function load() { let frame = document.getElementById(‘frame’) frame.contentWindow.postMessage(‘我爱你’, ‘http://localhost:4000’) //发送数据 window.onmessage = function(e) { //接受返回数据 console.log(e.data) //我不爱你 } } </script>// b.html window.onmessage = function(e) { console.log(e.data) //我爱你 e.source.postMessage(‘我不爱你’, e.origin) }4.websocketWebsocket是HTML5的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。WebSocket和HTTP都是应用层协议,都基于 TCP 协议。但是 WebSocket 是一种双向通信协议,在建立连接之后,WebSocket 的 server 与 client 都能主动向对方发送或接收数据。同时,WebSocket 在建立连接时需要借助 HTTP 协议,连接建立好了之后 client 与 server 之间的双向通信就与 HTTP 无关了。原生WebSocket API使用起来不太方便,我们使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。我们先来看个例子:本地文件socket.html向localhost:3000发生数据和接受数据// socket.html<script> let socket = new WebSocket(‘ws://localhost:3000’); socket.onopen = function () { socket.send(‘我爱你’);//向服务器发送数据 } socket.onmessage = function (e) { console.log(e.data);//接收服务器返回的数据 }</script>// server.jslet express = require(’express’);let app = express();let WebSocket = require(‘ws’);//记得安装wslet wss = new WebSocket.Server({port:3000});wss.on(‘connection’,function(ws) { ws.on(‘message’, function (data) { console.log(data); ws.send(‘我不爱你’) });})5. Node中间件代理(两次跨域)实现原理:同源策略是浏览器需要遵循的标准,而如果是服务器向服务器请求就无需遵循同源策略。 代理服务器,需要做以下几个步骤:接受客户端请求 。将请求 转发给服务器。拿到服务器 响应 数据。将 响应 转发给客户端。 我们先来看个例子:本地文件index.html文件,通过代理服务器http://localhost:3000向目标服务器http://localhost:4000请求数据。// index.html(http://127.0.0.1:5500) <script src=“https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script> <script> $.ajax({ url: ‘http://localhost:3000’, type: ‘post’, data: { name: ‘xiamen’, password: ‘123456’ }, contentType: ‘application/json;charset=utf-8’, success: function(result) { console.log(result) // {“title”:“fontend”,“password”:“123456”} }, error: function(msg) { console.log(msg) } }) </script> // server1.js 代理服务器(http://localhost:3000)const http = require(‘http’)// 第一步:接受客户端请求const server = http.createServer((request, response) => { // 代理服务器,直接和浏览器直接交互,需要设置CORS 的首部字段 response.writeHead(200, { ‘Access-Control-Allow-Origin’: ‘’, ‘Access-Control-Allow-Methods’: ‘’, ‘Access-Control-Allow-Headers’: ‘Content-Type’ }) // 第二步:将请求转发给服务器 const proxyRequest = http .request( { host: ‘127.0.0.1’, port: 4000, url: ‘/’, method: request.method, headers: request.headers }, serverResponse => { // 第三步:收到服务器的响应 var body = ’’ serverResponse.on(‘data’, chunk => { body += chunk }) serverResponse.on(’end’, () => { console.log(‘The data is ’ + body) // 第四步:将响应结果转发给浏览器 response.end(body) }) } ) .end()})server.listen(3000, () => { console.log(‘The proxyServer is running at http://localhost:3000’)})// server2.js(http://localhost:4000)const http = require(‘http’)const data = { title: ‘fontend’, password: ‘123456’ }const server = http.createServer((request, response) => { if (request.url === ‘/’) { response.end(JSON.stringify(data)) }})server.listen(4000, () => { console.log(‘The server is running at http://localhost:4000’)})上述代码经过两次跨域,值得注意的是浏览器向代理服务器发送请求,也遵循同源策略,最后在index.html文件打印出{“title”:“fontend”,“password”:“123456”}6.nginx反向代理实现原理类似于Node中间件代理,需要你搭建一个中转nginx服务器,用于转发请求。使用nginx反向代理实现跨域,是最简单的跨域方式。只需要修改nginx的配置即可解决跨域问题,支持所有浏览器,支持session,不需要修改任何代码,并且不会影响服务器性能。实现思路:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。先下载nginx,然后将nginx目录下的nginx.conf修改如下:// proxy服务器server { listen 80; server_name www.domain1.com; location / { proxy_pass http://www.domain2.com:8080; #反向代理 proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名 index index.html index.htm; # 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用 add_header Access-Control-Allow-Origin http://www.domain1.com; #当前端只跨域不带cookie时,可为 add_header Access-Control-Allow-Credentials true; }}最后通过命令行nginx -s reload启动nginx// index.htmlvar xhr = new XMLHttpRequest();// 前端开关:浏览器是否读写cookiexhr.withCredentials = true;// 访问nginx中的代理服务器xhr.open(‘get’, ‘http://www.domain1.com:81/?user=admin', true);xhr.send();// server.jsvar http = require(‘http’);var server = http.createServer();var qs = require(‘querystring’);server.on(‘request’, function(req, res) { var params = qs.parse(req.url.substring(2)); // 向前台写cookie res.writeHead(200, { ‘Set-Cookie’: ’l=a123456;Path=/;Domain=www.domain2.com;HttpOnly’ // HttpOnly:脚本无法读取 }); res.write(JSON.stringify(params)); res.end();});server.listen(‘8080’);console.log(‘Server is running at port 8080…’);7.window.name + iframewindow.name属性的独特之处:name值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。其中a.html和b.html是同域的,都是http://localhost:3000;而c.html是http://localhost:4000 // a.html(http://localhost:3000/b.html) <iframe src=“http://localhost:4000/c.html” frameborder=“0” onload=“load()” id=“iframe”></iframe> <script> let first = true // onload事件会触发2次,第1次加载跨域页,并留存数据于window.name function load() { if(first){ // 第1次onload(跨域页)成功后,切换到同域代理页面 let iframe = document.getElementById(‘iframe’); iframe.src = ‘http://localhost:3000/b.html’; first = false; }else{ // 第2次onload(同域b.html页)成功后,读取同域window.name中数据 console.log(iframe.contentWindow.name); } } </script>b.html为中间代理页,与a.html同域,内容为空。 // c.html(http://localhost:4000/c.html) <script> window.name = ‘我不爱你’ </script>总结:通过iframe的src属性由外域转向本地域,跨域数据即由iframe的window.name从外域传递到本地域。这个就巧妙地绕过了浏览器的跨域访问限制,但同时它又是安全操作。8.location.hash + iframe实现原理: a.html欲与c.html跨域相互通信,通过中间页b.html来实现。 三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。具体实现步骤:一开始a.html给c.html传一个hash值,然后c.html收到hash值后,再把hash值传递给b.html,最后b.html将结果放到a.html的hash值中。 同样的,a.html和b.html是同域的,都是http://localhost:3000;而c.html是http://localhost:4000 // a.html <iframe src=“http://localhost:4000/c.html#iloveyou”></iframe> <script> window.onhashchange = function () { //检测hash的变化 console.log(location.hash); } </script> // b.html <script> window.parent.parent.location.hash = location.hash //b.html将结果放到a.html的hash值中,b.html可通过parent.parent访问a.html页面 </script> // c.html console.log(location.hash); let iframe = document.createElement(‘iframe’); iframe.src = ‘http://localhost:3000/b.html#idontloveyou’; document.body.appendChild(iframe);9.document.domain + iframe该方式只能用于二级域名相同的情况下,比如 a.test.com 和 b.test.com 适用于该方式。 只需要给页面添加 document.domain =‘test.com’ 表示二级域名都相同就可以实现跨域。实现原理:两个页面都通过js强制设置document.domain为基础主域,就实现了同域。我们看个例子:页面a.zf1.cn:3000/a.html获取页面b.zf1.cn:3000/b.html中a的值// a.html<body> helloa <iframe src=“http://b.zf1.cn:3000/b.html" frameborder=“0” onload=“load()” id=“frame”></iframe> <script> document.domain = ‘zf1.cn’ function load() { console.log(frame.contentWindow.a); } </script></body> // b.html<body> hellob <script> document.domain = ‘zf1.cn’ var a = 100; </script></body>三、总结CORS支持所有类型的HTTP请求,是跨域HTTP请求的根本解决方案JSONP只支持GET请求,JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。不管是Node中间件代理还是nginx反向代理,主要是通过同源策略对服务器不加限制。日常工作中,用得比较多的跨域方案是cors和nginx反向代理 ...

February 12, 2019 · 4 min · jiezi

使用webpack打包多页jquery项目

虽然已经2019年了不过有一些项目还是需要用到jquery的不过考虑到使用jquery的一堆兼容性问题也为了可以顺利地使用ES6来撸代码研究使用webpack+babel打包代码来发布几个重点:1.为了将模块分割加载,不至于一个js文件过大,一个页面中使用多个js文件2.由于是多页项目(多个html),每个页面使用的js文件都不一致基于以上两点,需要配置多个入口文件3.会把小图片转换成base64,所以可能css转成的js文件会比较大,所以css文件都单独设置入口js例如,我们有三个页面:index、share、assist三个页面有通用的css文件:common.css设置入口文件时,可以这样设置entry: { // 通用css commoncss: path.resolve(__dirname, ‘./src/css/common.css.js’), // 主页 indexcss: path.resolve(__dirname, ‘./src/css/index.css.js’), index: path.resolve(__dirname, ‘./src/index.js’), // 页1 sharecss: path.resolve(__dirname, ‘./src/css/share.css.js’), share: path.resolve(__dirname, ‘./src/share.js’), // 页2 assistcss: path.resolve(__dirname, ‘./src/css/assist.css.js’), assist: path.resolve(__dirname, ‘./src/assist.js’),}其中,common.css.js文件中,只有几行代码import ‘../css/base.css’;import ‘../css/plugin.css’;import ‘../css/common.css’;common.css.js文件结束由于会有一些图片的base64,所以大小大约100+KB类似的还有index.css.js和share.css.js和assist.css.jsindex.css.js如下import ‘../css/index.css’;对,就一句话打包出来的js文件大小就看引入了多少小图片了,一般几百KB然后,要使用三个webpack的插件const HtmlWebpackPlugin = require(‘html-webpack-plugin’);const CopyWebpackPlugin = require(‘copy-webpack-plugin’);const jquery = require(‘jquery’);HtmlWebpackPlugin 用于打包出多个html文件CopyWebpackPlugin 用于img标签,后面说jquery 就是jquery,全局引用webpack.config.js里的plugins配置如下plugins: [ new webpack.ProvidePlugin({ $:“jquery” }), new CopyWebpackPlugin([{ from: __dirname + ‘/src/public/’ }]), // 吧src下public文件夹下的所有内容直接拷贝到dist(输出目录)下 new HtmlWebpackPlugin({ filename: ‘index.htm’, template: ‘src/index.html’, chunks: [‘commoncss’, ‘indexcss’, ‘index’], inject: ’true’, hash: true, }), new HtmlWebpackPlugin({ filename: ‘share.htm’, template: ‘src/share.html’, chunks: [‘commoncss’, ‘sharecss’, ‘share’], inject: ’true’, hash: true, }), new HtmlWebpackPlugin({ filename: ‘assist.htm’, template: ‘src/assist.html’, chunks: [‘commoncss’, ‘assistcss’, ‘assist’], inject: ’true’, hash: true, })]src目录下的文件如下index.js assist.js share.js是三个文件分别的入口文件index.html assist.html share.html是三个文件的模板,html代码可以写在这里(当然想用模板文件也是可以的,只要HtmlWebpackPlugin插件支持)dist文件夹如下(为什么是htm而不是html,是为了便于读者区分模板文件和输出文件)我们知道,webpack打包不会打包HtmlWebpackPlugin的template里的img标签下的图片,所以在html里使用了img标签的图片都要放在public文件夹下,CopyWebpackPlugin这个组件会直接把图片复制过去关于HtmlWebpackPlugin的具体参数的细则可以上网搜一下,很多这方面的内容其他的比如loader、babel不在这篇文章想说的重点之列,不赘述最后,附上webpack.config.js文件 let actName = ‘yourProjectName’;// let actKV = { name: actName, entry: { // 通用css commoncss: path.resolve(__dirname, ‘./src/css/common.css.js’), // 主页 indexcss: path.resolve(__dirname, ‘./src/css/index.css.js’), index: path.resolve(__dirname, ‘./src/index.js’), // 分享页1 sharecss: path.resolve(__dirname, ‘./src/css/share.css.js’), share: path.resolve(__dirname, ‘./src/share.js’), // 分享页2 assistcss: path.resolve(__dirname, ‘./src/css/assist.css.js’), assist: path.resolve(__dirname, ‘./src/assist.js’), } }; return { entry: actKV.entry, target: “web”, output: { path: path.resolve(__dirname + ‘/dist/’+actName), // publicPath: ‘.\’, filename: ‘js/[name].js’, // chunkFilename: “[name].chunk.[hash].js”, }, plugins: [ new webpack.ProvidePlugin({ $:“jquery” }), new CopyWebpackPlugin([{ from: __dirname + ‘/src/public/’ }]), new HtmlWebpackPlugin({ filename: ‘index.htm’, template: ‘src/index.html’, chunks: [‘commoncss’, ‘indexcss’, ‘index’], inject: ’true’, hash: true, }), new HtmlWebpackPlugin({ filename: ‘share.htm’, template: ‘src/share.html’, chunks: [‘commoncss’, ‘sharecss’, ‘share’], inject: ’true’, hash: true, }), new HtmlWebpackPlugin({ filename: ‘assist.htm’, template: ‘src/assist.html’, chunks: [‘commoncss’, ‘assistcss’, ‘assist’], inject: ’true’, hash: true, }) ], mode: ‘development’, node: { __filename: true, __dirname: true }, devtool: isProduction ? ‘source-map’:‘inline-source-map’, devServer:{ inline: true, open: true, historyApiFallback: true, // host: ip.address(), host: ’localhost’, progress: true, contentBase: “./dist/”, port: 3430, historyApiFallback:true, publicPath:’/src/’, proxy: { ‘’: { target: ‘http://127.0.0.1:3430’, secure: false } }, }, resolve: { alias: { }, extensions: [’.js’, ‘.less’, ‘.css’, ‘.vue’, ‘.jsx’], }, externals: { }, module: { rules: [{ test: /.vue$/, loader: ‘vue-loader’, }, { test: /.js$/, include: path.join(__dirname,’/src’), exclude: path.resolve(__dirname, ’node_modules’), use:[ { loader: ‘babel-loader’, query: { presets: [’es2015’] } } ] }, { test: /.xml$/, loader: “xml-loader” }, { test: /.(css|less)$/, loader: “style-loader!css-loader”, }, { test: /.(png|jpg|jpeg|gif|icon|webp)$/, loader: ‘url-loader’, options: { limit: 16384, name: ‘images/[name].[hash:5].[ext]’, } }, { test: /.(woff|woff2|svg|eot|ttf)??.$/, loader: “file-loader?&name=assets/fonts/[name].[ext]” }, { test: /.txt$/, loader: “text-loader” },{ test: /.jsx$/, exclude: /node_modules/, loaders: [‘jsx-loader’, ‘babel-loader’] }] }, } ...

January 31, 2019 · 2 min · jiezi

JQ获取当前窗口的宽度值

$(window).height(); //浏览器当前窗口可视区域高度 $(document).height(); //浏览器当前窗口文档的高度 $(document.body).height();//浏览器当前窗口文档body的高度 $(document.body).outerHeight(true);//浏览器当前窗口文档body的总高度 包括border padding margin $(window).width(); //浏览器当前窗口可视区域宽度 $(document).width();//浏览器当前窗口文档对象宽度 $(document.body).width();//浏览器当前窗口文档body的高度 $(document.body).outerWidth(true);//浏览器当前窗口文档body的总宽度 包括border padding margin 提升自我的js基础 可以去牛客网刷题哦!!!

January 28, 2019 · 1 min · jiezi

前端基础入门六(JQuery进阶)

jQuery特殊属性操作val方法val方法用于设置和获取表单元素的值,例如input、textarea的值//设置值$("#name").val(“张三”);//获取值$("#name").val();html方法与text方法html方法相当于innerHTML text方法相当于innerText//设置内容$(“div”).html(“<span>这是一段内容</span>”);//获取内容$(“div”).html()//设置内容$(“div”).text(“<span>这是一段内容</span>”);//获取内容$(“div”).text()width方法与height方法设置或者获取高度//带参数表示设置高度$(“img”).height(200);//不带参数获取高度$(“img”).height();获取网页的可视区宽高//获取可视区宽度$(window).width();//获取可视区高度$(window).height();scrollTop与scrollLeft设置或者获取垂直滚动条的位置//获取页面被卷曲的高度$(window).scrollTop();//获取页面被卷曲的宽度$(window).scrollLeft();offset方法与position方法offset方法获取元素距离document的位置,position方法获取的是元素距离有定位的父元素的位置。//获取元素距离document的位置,返回值为对象:{left:100, top:100}$(selector).offset();//获取相对于其最近的有定位的父元素的位置。$(selector).position();jQuery事件机制JavaScript中已经学习过了事件,但是jQuery对JavaScript事件进行了封装,增加并扩展了事件处理机制。jQuery不仅提供了更加优雅的事件处理语法,而且极大的增强了事件的处理能力。简单事件绑定>>bind事件绑定>>delegate事件绑定>>on事件绑定(推荐)简单事件注册click(handler) 单击事件mouseenter(handler) 鼠标进入事件mouseleave(handler) 鼠标离开事件缺点:不能同时注册多个事件bind方式注册事件//第一个参数:事件类型//第二个参数:事件处理程序$(“p”).bind(“click mouseenter”, function(){ //事件响应方法});缺点:不支持动态事件绑定delegate注册委托事件// 第一个参数:selector,要绑定事件的元素// 第二个参数:事件类型// 第三个参数:事件处理函数$(".parentBox").delegate(“p”, “click”, function(){ //为 .parentBox下面的所有的p标签绑定事件});缺点:只能注册委托事件,因此注册时间需要记得方法太多了on注册事件(重点)jQuery1.7之后,jQuery用on统一了所有事件的处理方法。最现代的方式,兼容zepto(移动端类似jQuery的一个库),强烈建议使用。on注册简单事件// 表示给$(selector)绑定事件,并且由自己触发,不支持动态绑定。$(selector).on( “click”, function() {});on注册委托事件// 表示给$(selector)绑定代理事件,当必须是它的内部元素span才能触发这个事件,支持动态绑定$(selector).on( “click”,“span”, function() {});on注册事件的语法:// 第一个参数:events,绑定事件的名称可以是由空格分隔的多个事件(标准事件或者自定义事件)// 第二个参数:selector, 执行事件的后代元素(可选),如果没有后代元素,那么事件将有自己执行。// 第三个参数:data,传递给处理函数的数据,事件触发的时候通过event.data来使用(不常使用)// 第四个参数:handler,事件处理函数$(selector).on(events[,selector][,data],handler);事件解绑// 解绑匹配元素的所有事件$(selector).off();// 解绑匹配元素的所有click事件$(selector).off(“click”);触发事件$(selector).click(); //触发 click事件$(selector).trigger(“click”);jQuery事件对象jQuery事件对象其实就是js事件对象的一个封装,处理了兼容性。//screenX和screenY 对应屏幕最左上角的值//clientX和clientY 距离页面左上角的位置(忽视滚动条)//pageX和pageY 距离页面最顶部的左上角的位置(会计算滚动条的距离)//event.keyCode 按下的键盘代码//event.data 存储绑定事件时传递的附加数据//event.stopPropagation() 阻止事件冒泡行为//event.preventDefault() 阻止浏览器默认行为//return false:既能阻止事件冒泡,又能阻止浏览器默认行为。

January 22, 2019 · 1 min · jiezi

前端基础入门五(掌握jQuery的常用api,实现动态效果)

jQuery基本概念学习目标:学会如何使用jQuery,掌握jQuery的常用api,能够使用jQuery实现常见的效果。为什么要学习jQuery?【01-让div显示与设置内容.html】使用javascript开发过程中,有许多的缺点:1. 查找元素的方法太少,麻烦。2. 遍历伪数组很麻烦,通常要嵌套一大堆的for循环。3. 有兼容性问题。4. 想要实现简单的动画效果,也很麻烦5. 代码冗余。jQuery初体验【02-让div显示与设置内容.html】$(document).ready(function () { $("#btn1").click(function () { //隐式迭代:偷偷的遍历,在jQuery中,不需要手动写for循环了,会自动进行遍历。 $(“div”).show(200); }); $("#btn2").click(function () { $(“div”).text(“我是内容”); });});优点总结:1. 查找元素的方法多种多样,非常灵活2. 拥有隐式迭代特性,因此不再需要手写for循环了。3. 完全没有兼容性问题。4. 实现动画非常简单,而且功能更加的强大。5. 代码简单、粗暴。什么是jQuery?jQuery的官网 http://jquery.com/ jQuery就是一个js库,使用jQuery的话,会比使用JavaScript更简单。js库:把一些常用到的方法写到一个单独的js文件,使用的时候直接去引用这js文件就可以了。(animate.js、common.js)我们知道了,jQuery其实就是一个js文件,里面封装了一大堆的方法方便我们的开发,其实就是一个加强版的common.js,因此我们学习jQuery,其实就是学习jQuery这个js文件中封装的一大堆方法。jQuery的入口函数使用jQuery的三个步骤:1. 引入jQuery文件2. 入口函数3. 功能实现关于jQuery的入口函数://第一种写法$(document).ready(function() { });//第二种写法$(function() { });jQuery入口函数与js入口函数的对比1. JavaScript的入口函数要等到页面中所有资源(包括图片、文件)加载完成才开始执行。2. jQuery的入口函数只会等待文档树加载完成就开始执行,并不会等待图片、文件的加载。jQuery对象与DOM对象的区别(重点)DOM对象:使用JavaScript中的方法获取页面中的元素返回的对象就是dom对象。jQuery对象:jquery对象就是使用jquery的方法获取页面中的元素返回的对象就是jQuery对象。jQuery对象其实就是DOM对象的包装集(包装了DOM对象的集合(伪数组))DOM对象与jQuery对象的方法不能混用。DOM对象转换成jQuery对象:【联想记忆:花钱】var $obj = $(domObj);// $(document).ready(function(){});就是典型的DOM对象转jQuery对象jQuery对象转换成DOM对象:var $li = $(“li”);//第一种方法(推荐使用)$li[0]//第二种方法$li.get(0)选择器什么是jQuery选择器jQuery选择器是jQuery为我们提供的一组方法,让我们更加方便的获取到页面中的元素。注意:jQuery选择器返回的是jQuery对象。jQuery选择器有很多,基本兼容了CSS1到CSS3所有的选择器,并且jQuery还添加了很多更加复杂的选择器。【查看jQuery文档】jQuery选择器虽然很多,但是选择器之间可以相互替代,就是说获取一个元素,你会有很多种方法获取到。所以我们平时真正能用到的只是少数的最常用的选择器。基本选择器总结:跟css的选择器用法一模一样。层级选择器跟CSS的选择器一模一样。过滤选择器筛选选择器(方法)

January 21, 2019 · 1 min · jiezi

你有多久没有用jQuery

jquery虽然已经过时了,在我看来,它虽然比不上React/Vue这些框架,但它依旧是一个很棒的JavaScript库,和lodash之类的库一样好一起来回顾一下吧~<html> <head> <script src=“http://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.3.1.min.js"></script> <script src=“https://cdn.bootcss.com/babel-core/6.1.19/browser.js"></script> <style> .msg { display: inline; border: 1px solid #aabbcc; padding: 10px; } </style> </head> <body> <p class=“msg”><span>123</span></p> <input type=“text” id=“input” /> <button id=“click”>button</button> <div class=“father”> <div class=“son”>son</div> </div> <div class=“a”>a <div class=“b”>b <div class=“c”>c</div> <div class=“e”>e</div> <div class=“f”>f</div> </div> </div> </body> <script> $(document).ready(() => { console.log(this); // window $(“p”).click(function() { console.log(this) // p标签 }) var a = $(’.msg’).text() // 所选元素的文本内容 console.log(a) $(’.son’).parent() var b = $(’.msg’).html() console.log(b) // 所选元素的内容 包含内部html标记 $(’#click’).click(() => { var c = $(’#input’).val() // 返回表单字段的值 console.log(c) }) var d = $(’#input’).attr(’type’) // 获取元素属性值 console.log(d) $(’.msg’).text(‘456’); //设置所选元素的文本内容 $(’.msg’).html(’<span>456</span>’) // 设置所选元的内容 包含html标记 $(’#input’).attr(’type’, ‘password’) // 设置所选元素的属性 $(’#input’).attr({ ’type’: ’text’, ‘placeholder’: ‘123’ }) // 可以同时设置多个属性 $(’.msg’).append(‘some text append’) // 被选元素结尾插入内容 $(’.msg’).prepend(‘some text prepended’) // 被选元素开头插入内容 // 添加若干个元素 var str1 = ‘<h3>abc</h3>’ var str2 = ‘<h1>bcd</h1>’ $(’.msg’).append(str1, str2) $(’.msg’).after(’<span>after</span>’) // 区别appen,此方法是在元素后面追加新元素,appen是在元素里追加 $(’.msg’).before(’<span>before</span>’) // 区别prepend 同上 $(’.father’).empty() // 删除被选元素的所有子元素 相当于清空 成为空标签 $(’.father’).remove() // 删除被选元素及其所有子元素 $(’.father’).remove(’.child’) // 带一个过滤功能 $(’.msg’).addClass(‘class1 class2 class3’) // 添加多个类名 空格区分 $(’.msg’).removeClass(‘class2’) // 删除指定类名 $(’.msg’).toggleClass(‘class4’) // 删除或者添加类 切换操作 比如点击button进行切换 var e = $(’.msg’).css(‘border’) // 返回元素的指定css样式 console.log(e); $(’.msg’).css(‘backgound-color’, ‘#ddeeff’) // 设置元素的css样式 $(’.msg’).css({ ‘backgound-color’: ‘#ddeeff’, ‘margin-top’: ‘10px’, }) // 和设置html属性一样,可以设置多个css属性 $(’.msg’).width() // 获取指定元素的宽度 => 标准盒模型中 width值 $(’.msg’).height() // 标准盒模型中 height值 //区别 innerWidth outerWidth … eg $(’#click’).width(50).height(50) // 设置指定元素的宽高 $(document).width() // 浏览器的宽度 $(document).height() // 浏览器的高度 // 获取被选元素的直接父元素 $(’.c’).parent().css(‘border’, ‘1px solid #ddeeff’) var f = $(’.c’).parents(); // 获取被选元素的所有祖先元素一直到html console.log(f) $(’.c’).parents(’.example’) // 同样加一个过滤 $(’.c’).parentsUntil(‘html’) // .c 和 html 之间的元素 $(’.b’).children() // 被选元素的所有直接子元素, 注意直接子元素可以有多个,但直接父元素确只能有一个 $(’.b’).children(’.c’) // 同样加一个过滤 $(’.b’).find(‘span’) // 返回被选元素的所有后代元素是span的元素 $(’.b’).find(’’) // 同样加一个css修饰器 即为返回所有的后代元素 $(’.c’).siblings(); // 返回被选元素的所有同胞元素 同样可以过滤下 $(’.c’).next() // 返回被选元素的下一个同胞元素 $(’.c’).nextAll() // 返回被选元素后跟随的同胞元素 // 类似 还有 prev() prevAll() prevUntil() $(‘p’).filter(’.a’) // 返回类名为a的p元素 $(‘p’).not(’.a’) // 返回类名不为a的p元素 // Jquery 加载 //$(’.f’).load(’./text.txt’) // Ajax / $.get(‘http://apis.juhe.cn/mobile/get?phone=17854234676&key=87b3fd95fb8a004442ca5195304a6954',(data, status) => { console.log(data, status); }) $.post(‘url’, {data}, (data, status) => { }) */ // Ajax 请求 $.ajax({ type: ‘get’, data: {}, // post请求参数 url: ‘http://apis.juhe.cn/mobile/get?phone=17854234676&key=87b3fd95fb8a004442ca5195304a6954', dataType: ‘jsonp’, success: (data) => { console.log(data); }, error: (err) => {} }) // 事件 $(’#input’).keyup(function() { console.log(123); }) }) </script></html> ...

January 17, 2019 · 2 min · jiezi

jq为a标签绑定的onclick事件在移动端不响应

问题描述项目用的jQuery + Bootstrap进行开发,其中用到Bootstrap的导航栏当页面在移动端打开的时候,导航栏会收缩,就是这样的:其中我为下拉菜单的a标签在js中绑定了onclick事件:jQuery(document).ready(function($){ ‘use strict’; $(’#see’).click(seeArrange) // 会议安排 function seeArrange () {} //…}一切在PC端运行正常,但是在移动端的时候,a标签绑定的onclick事件在移动端就没有响应。解决在网上看了很多方法之后,我觉得可能是移动端收缩之后的那个导航栏容器里的a没有绑定到点击事件。因此我就换成在html里绑定事件,结果就点击正常了。。。// html <li><a href=“javascript:void(0);” id=‘see’ onclick=“seeArrange();">会议议程</a></li>// jsjQuery(document).ready(function($){ ‘use strict’; // $(’#see’).click(seeArrange) // 会议安排 // …}function seeArrange () {}总结其实具体原因我不知道是什么,烦请知道的大佬们提点一下我。网上找的各种方法也说一下吧,做个笔记很多人是说移动端不支持点击事件,换成touch事件或者tap事件试试。$(”*").bind(“click”,function(){}); // 在页面初始化时,为其添加事件绑定参考IOS微信浏览器点击事件不起作用问题移动端微信内置浏览器(或QQ浏览器)无法识别onclick事件的解决

January 10, 2019 · 1 min · jiezi

Ajax上传文件/照片时报错TypeError :Illegal invocation

问题Ajax上传文件/照片时报错TypeError :Illegal invocation解决网上搜索问题,错误原因可能有以下几个,依次检查:请求类型有误,如post请求,但在后台设置的是get请求参数有误。 如没有传参,或是参数对应不上去File类型的参数被预先处理了检查后发现应该时原因3,故修改代码,设置$.ajax的processData: false:getToken().then( res => { console.log(‘获取七牛云token后上传图片’) if(!res.hasOwnProperty(‘data’)) return // 整理参数 var formData = new FormData() formData.append(’token’, res.data) formData.append(‘file’, file) $.ajax({ url: ‘’, type: ‘POST’, contentType: ‘multipart/form-data’, processData: false, // 增加这一行,不处理参数 data: formData, success: function (result) { console.log(result) } })})参考使用AJAX实现文件上传时Illegal invocation错误jquery ajax报Uncaught TypeError :Illegal invocation

January 9, 2019 · 1 min · jiezi

jquery动态设置select

jQuery获取Select选择的Text和Value: $("#select_id").change(function(){//code…}); //为Select添加事件,当选择其中一项时触发 var checkText=$("#select_id").find(“option:selected”).text(); //获取Select选择的Text var checkValue=$("#select_id").val(); //获取Select选择的Value var checkIndex=$("#select_id “).get(0).selectedIndex; //获取Select选择的索引值 var maxIndex=$("#select_id option:last”).attr(“index”); //获取Select最大的索引值 jQuery添加/删除Select的Option项:$("#select_id").append("<option value=‘Value’>Text</option>"); //为Select追加一个Option(下拉项) $("#select_id").prepend("<option value=‘0’>请选择</option>"); //为Select插入一个Option(第一个位置) $("#select_id option:last").remove(); //删除Select中索引值最大Option(最后一个) $("#select_id option[index=‘0’]").remove(); //删除Select中索引值为0的Option(第一个) $("#select_id option[value=‘3’]").remove(); //删除Select中Value=‘3’的Option $("#select_id option[text=‘4’]").remove(); //删除Select中Text=‘4’的Option 内容清空:$("#charCity").empty();

January 6, 2019 · 1 min · jiezi

jQuery 中的显式迭代(explicitly iterate)与隐式迭代(implicit iteration)

jQuery 中的显式迭代(explicitly iterate)与隐式迭代(implicit iteration)迭代 = 遍历。我按照我的理解表述一下:显式迭代就是说,某个 jQuery 方法只能一个一个地、依次地操作当前轮到的那个元素。隐式迭代的意思是,某个 jQuery 方法可以一下子就把对象里(符合条件的)所有的元素都进行某个操作,神不知鬼不觉的就把对象里所有的元素都办了。你也不知道当前操作到哪个元素了,因为一次性就都操作好了。先举一个隐式迭代的例子:比如这一段代码:html:<ul> <li>foo</li> <li>bar</li></ul>js:$( “li” ).addClass( “bar” );这就是隐式迭代,不需要你关心当前轮到哪个元素了,因为一下子就把所有的 li 元素都加上 bar 这个 class 了。还是这个功能,我如果想使用显式迭代的方式来写,该怎么写呢?$( “li” ).each(function() { $( this ).addClass( “foo” );});对 li 一个一个地添加类,这样写功能上虽然也能实现,但是显然这样写比较啰嗦,并不推荐这种写法。下面我再举一个只能用显式迭代来写的例子:(HTML 部分的代码和上面一样)$( “li” ).each(function( index ) { console.log( index + “: " + $( this ).text() );});.each()方法就是典型的显式迭代:首先我用选择器选中了所有的 li 元素,形成了一个对象,这样对象是长这样的(类似于 json 格式):[ { 0:li } , { 1:li } ]每个对象还有自己的一大堆属性……(太多了,只截取一部分)跑题了,回到 .each() 方法。当遍历到第一个 li 元素的时候,就打印第一条信息;遍历到第二个 li 的时候,再打印第二条信息。再回顾一下刚才那一段代码:$( “li” ).each(function( index ) { console.log( index + “: " + $( this ).text() );});index 是一个形参,表示当前轮到的那个元素的下标。既然是形参,不用 index,用 i 也行。那么问题来了:为什么这个 function 里写一个参数,这个参数就代表下标了呢?答案是:.each() 方法的 function,它有2个参数。.each 方法的写法是这样的:$(selector).each(function( index , element ){ //do something})function 的第一个参数是整数的下标,第二个参数用于:返回当前读取到的那个元素,比如刚才的代码,element 就是返回每个 li。所以还是刚才的代码,我们也可以这样写:$( “li” ).each(function( i,element ) { console.log($(element)); console.log( i + “: " + $(element).text() );});有时候我们不写 element,只是因为使用 this 关键词可以替代 element。this 等于 element,同样,$(this)和$(element)是一样的。使用$()选择器选中 this 或者 element 元素之后,我们就可以使用 jQuery 提供的 api,很方便的操作他们,比如,使用 text()方法读取元素的文本内容,或者使用 css()方法修改样式,等等。总结一下,本文一开始说明了什么是显式迭代,什么是隐式迭代以及二者的区别,然后提到了一个显式迭代方法 .each() 的具体用法。 ...

January 5, 2019 · 1 min · jiezi

用JS和JQuery分别获取表单元素的值(select,checkbox,radio)

废话不多说直接上代码HTML部分<html !DOCTYPE> <head> <title>表单元素值获取</title> </head> <body> <div> <h1>下拉列表</h1> <select id=“weekday”> <option>—</option> <option>星期一</option> <option>星期二</option> <option>星期三</option> <option>星期四</option> <option>星期五</option> </select> </div> <div> <h1>多选框</h1> <input type=“checkbox” name=“hobby” value=“swimming”><span>swimming</span> <input type=“checkbox” name=“hobby” value=“singing”><span>singing</span> <input type=“checkbox” name=“hobby” value=“drawing”><span>drawing</span> <input type=“checkbox” name=“hobby” value=“travelling”><span>travelling</span> </div> <div> <h1>单选框</h1> <input type=“radio” name=“sex” value=“boy”><span>boy</span> <input type=“radio” name=“sex” value=“girl”><span>girl</span> </div> <div> <input type=“button” value=“点击获取下拉列表的值” onclick=“getSelectorValue()"> <input type=“button” value=“点击获取多选框的值” onclick=“getCheckboxValue()"> <input type=“button” value=“点击获取单选框的值” onclick=“getRadioValue()"> </div> </body></html>用JavaScript的方法解释说明部分在注释中<script> //用原生JS获取 var getSelectorValue = function(){ var selector = document.getElementById(“weekday”); //先获取到整个下拉列表,在获取到被选中元素的索引,最后得到值 var value = selector.options[selector.selectedIndex].value; console.log(value); } var getCheckboxValue = function(){ var checkbox = document.getElementsByName(“hobby”); console.log(checkbox);//是一个节点列表 checkbox.forEach(element => { if(element.checked){ //获取到被选中的元素节点 console.log(element); //获取节点中的value属性 console.log(element.value); } }); } var getRadioValue = function(){ var radio = document.getElementsByName(“sex”); console.log(radio);//是一个节点列表 radio.forEach(element => { if(element.checked){ //获取到被选中的元素节点 console.log(element); //获取节点中的value属性 console.log(element.value); } }); } </script>用JQuery的方法<script src=“https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script><script> //用JQuery获取 var getSelectorValue = function(){ var value = $("#weekday”).val(); console.log(value); } var getCheckboxValue = function(){ //获取到所有被选中的列表 var checkbox = $(“input[name=‘hobby’]:checked”); for(var i=0; i<checkbox.length; i++){ console.log(checkbox[i].value); } } var getRadioValue = function(){ //因为是单选可以直接获取到被选中的值 var value = $(“input[name=‘sex’]:checked”).val(); console.log(value); } </script>Practice Makes Perfect ...

January 5, 2019 · 1 min · jiezi

补全一段jQuery代码

补全下面代码: window.jQuery = ??? window.$ = jQuery var $div = $(‘div’) $div.addClass(‘red’) // 可将所有 div 的 class 添加一个 red $div.setText(‘hi’) // 可将所有 div 的 textContent 变为 hi补全后: window.jQuery = function (nodeOrSelector) { let nodes = {} let temp = document.querySelectorAll(nodeOrSelector) //伪数组 for (let i = 0; i < temp.length; i++) { nodes[i] = temp[i] } nodes.length = temp.length // 设置所有 div 的 textContent nodes.setText = function (text) { for (let i = 0; i < nodes.length; i++) { nodes[i].textContent = text } } // 为所有 div 添加 class nodes.addClass = function (className) { for (let i = 0; i < nodes.length; i++) { nodes[i].classList.add(className) } } return nodes } window.$ = jQuery var $div = $(‘div’) $div.addClass(‘red’) // 可将所有 div 的 class 添加一个 red $div.setText(‘hi’) // 可将所有 div 的 textContent 变为 hi主要思路是,window.$ = jQuery所以window.jQuery要等于一个函数,这个函数接收一个参数,返回一个对象,返回的对象包含两个方法,这两个方法可以添加text文本和添加class类 ...

December 22, 2018 · 1 min · jiezi

解析JQuery中each方法的使用

each() 方法规定为每个匹配元素规定运行的函数。写的十分的全面细致,具有一定的参考价值,对此有需要的朋友可以参考学习下。如有不足之处,欢迎批评指正。概述:each() 方法规定为每个匹配元素规定运行的函数。返回 false 可用于及早停止循环,相当于break。返回 true 可以结束本次循环,相当于continue。语法:$(selector).each(function(index,element){ }) index - 选择器的 index 位置 element - 当前的元素(也可使用 “this” 选择器) $(selector).each(function(){ }) $.each(array,function(Key,Value){ })1.遍历js数组$(function(){ var array=[“aaa”,“bbb”,“ccc”]; $.each(array,function(i,j){ alert(i+":"+j); //i表示索引,j代表值 });})2.遍历Object对象var obj = new Object();obj.name=“zs”;$.each(obj, function(name, value) { alert(this); //this指向当前属性的值,等价于value alert(name); //name表示Object当前属性的名称 alert(value); //value表示Object当前属性的值});//欢迎加入前端全栈开发交流圈一起吹水聊天学习交流:8643058603.遍历JSON对象var json ={“name”:“zhangSan”,“role”:“student”};$.each(json,function(key,value){ alert(key+":"+value);});4.遍历由多个JSON对象组成的数组var json =[{“name”:“Amy”,“role”:“student”},{“name”:“Tom”,“role”:“student”}];$.each(json, function(index, value) { alert(“index="+index+"\n” +“name:"+value.name+"\n”+“role:"+value.role+"\n”);});//欢迎加入前端全栈开发交流圈一起吹水聊天学习交流:8643058605.遍历jQuery对象<head> <meta charset=“utf-8” /> <title>遍历jQuery对象</title> <script src=“js/jquery-1.12.4.js”></script> <script type=“text/javascript”> $(function(){ $(“input[type=‘button’]”).bind(“click”,function(){ $(“li”).each(function(){ alert($(this).text()) }); }); }); </script> </head> <body> <input type=“button” value=“触发事件”/> <ul> <li>first</li> <li>second</li> </ul> </body>//欢迎加入前端全栈开发交流圈一起吹水聊天学习交流:864305860结语感谢您的观看,如有不足之处,欢迎批评指正。 ...

December 19, 2018 · 1 min · jiezi

状态码为200时 jQuery ajax报错

问题现象HTTP 状态码为 200 OK 时, jquery ajax报错2. 问题原因jquery ajax的dataType字段包含:json, 但是服务端返回的数据不是规范的json格式,导致jquery解析json字符串报错,最终导致ajax报错。jQuery ajax 官方文档上说明:“json”: Evaluates the response as JSON and returns a JavaScript object. Cross-domain “json” requests are converted to “jsonp” unless the request includes jsonp: false in its request options. The JSON data is parsed in a strict manner; any malformed JSON is rejected and a parse error is thrown. As of jQuery 1.9, an empty response is also rejected; the server should return a response of null or {} instead. (See json.org for more information on proper JSON formatting.)设置dataType为json时,jquery就会去解析响应体为JavaScript对象。跨域的json请求会被转化成jsonp, 除非设置了jsonp: false。JSON数据会以严格模式去解析,任何不规范的JSON字符串都会解析异常并抛出错误。从jQuery 1.9起,一个空的响应也会被抛出异常。服务端应该返回一个null或者{}去代替空响应。参考json.org, 查看更多内容3. 解决方案这个问题的原因有两个。后端返回的json数据格式不规范HTTP状态码为200,但是返回空的响应所以后端在返回结果时,不要使用空的响应,也不应该去手动拼接JSON字符串,而应该交给响应的库来实现JSON序列化字符串工作。方案1:如果后端确定响应体中不返回数据,那么就把状态码设置为204,而不是200。状态码为204时,jQuery就知道响应体中没有数据,也不需要去解析。我一直逼着后端同事这么做。方案2:如果后端接口想返回200,那么请返回一个null或者{}去代替空响应方案3:别用jQuery的ajax,换个其他的库试试4. 参考Ajax request returns 200 OK, but an error event is fired instead of successjQuery.ajax

December 14, 2018 · 1 min · jiezi

几个非常实用的JQuery代码片段

jQuery是一个兼容多浏览器的javascript库,核心理念是write less,do more(写得更少,做得更多)。jQuery使用户能更方便地处理HTML(标准通用标记语言下的一个应用)、events、实现动画效果,并且方便地为网站提供AJAX交互。jQuery还有一个比较大的优势是,它的文档说明很全,而且各种应用也说得很详细,同时还有许多成熟的插件可供选择。jQuery能够使用户的html页面保持代码和html内容分离,也就是说,不用再在html里面插入一堆js来调用命令了,只需要定义id即可,jQuery已经成为最流行的javascript库,下面给大家推荐几款常用的JQuery代码。1、管理搜索框的值现在各大网站都有搜索框,而搜索框通常都有默认值,当输入框获取焦点时,默认值消失。而一旦输入框失去焦点,而输入框里又没有输入新的值,输入框里的值又会恢复成默认值,如果往输入框里输入了新值,则输入框的值为新输入的值。这种特效用JQuery很容易实现:$("#searchbox") .focus(function(){$(this).val(’’)}) .blur(function(){ var $this = $(this); // ‘请搜索…‘为搜索框默认值 ($this.val() === ‘’)? $this.val(‘请搜索…’) : null; });2、反序访问JQuery对象里的元素在某些场景下,我们可能需要反序访问通过JQuery选择器获取到的页面元素对象,这个怎么实现呢?看下面代码://要掌握JQuery对象的get方法 以及数组的reverse方法即可var arr = $(’#nav’).find(’li’).get().reverse();$.each(arr,function(index,ele){ …. … });3、克隆table header到表格的最下面为了让table具有更好的可读性,我们可以将表格的header信息克隆一份到表格的底部,这种特效通过JQuery就很容易实现:var $tfoot = $(’<tfoot></tfoot>’); $($(’thead’).clone(true, true).children().get().reverse()).each(function(){ $tfoot.append($(this));});$tfoot.insertAfter(’table thead’);4、使用JQuery重绘图片的大小关于图片大小的重绘,你可以在服务端来实现,也可以通过JQuery在客户端实现。$(window).bind(“load”, function() { // IMAGE RESIZE $(’#product_cat_list img’).each(function() { var maxWidth = 120; var maxHeight = 120; var ratio = 0; var width = $(this).width(); var height = $(this).height(); if(width > maxWidth){ ratio = maxWidth / width; $(this).css(“width”, maxWidth); $(this).css(“height”, height * ratio); height = height * ratio; } var width = $(this).width(); var height = $(this).height(); if(height > maxHeight){ ratio = maxHeight / height; $(this).css(“height”, maxHeight); $(this).css(“width”, width * ratio); width = width * ratio; } }); //$("#contentpage img").show(); // IMAGE RESIZE});5、滚动时动态加载页面内容有些网站的网页内容不是一次性加载完毕的,而是在鼠标向下滚动时动态加载的,这是怎么做到的呢?看下面代码:var loading = false;$(window).scroll(function(){ if((($(window).scrollTop()+$(window).height())+250)>=$(document).height()){ if(loading == false){ loading = true; $(’#loadingbar’).css(“display”,“block”); $.get(“load.php?start="+$(’#loaded_max’).val(), function(loaded){ $(‘body’).append(loaded); $(’#loaded_max’).val(parseInt($(’#loaded_max’).val())+50); $(’#loadingbar’).css(“display”,“none”); loading = false; }); } }});$(document).ready(function() { $(’#loaded_max’).val(50);}); ...

November 3, 2018 · 1 min · jiezi

使用 javascript 替换 jQuery

使用 javascript 替换 jQueryjQuery 曾风靡一个时代,大大降低了前端开发的门槛,丰富的插件也是前端开发者得心应手的武器库,但是,这个时代终于要落幕了。随着 JS 标准和浏览器的进步,jQuery 的很多精华被原生 JS 吸收,我们直接使用原生 API 就可以用类似手法来处理以前需要 jQuery 的问题。在新的 Web 项目中,如果不需要支持过于陈旧的浏览器版本,那么的确没有必要使用 jQuery。下面就探讨如何用JavaScript(ES6)标准语法,取代jQuery的一些主要功能。选取元素选择器查询常用的 class、id、属性 选择器都可以使用 document.querySelector 或 document.querySelectorAll 替代。document.querySelector 返回第一个匹配的 Elementdocument.querySelectorAll 返回所有匹配的 Element 组成的 NodeList。jQuery:var $ele = $(“selector”);Native:let ele = document.querySelectorAll(“selector”);选择器模式选择器示例示例说明.class.intro选择所有class=“intro"的元素#id#firstname选择所有id=“firstname"的元素**选择所有元素elementp选择所有<p>元素element,elementdiv,p选择所有<div>元素和<p>元素element elementdiv p选择<div>元素内的所有<p>元素element>elementdiv>p选择所有父级是<div>元素的 <p>元素element+elementdiv+p选择所有紧接着<div>元素之后的<p>元素[attribute=value]a[target=_blank]选择所有使用target="_blank"的<a>元素[attribute^=value]a[src^=“http”]选择每一个src属性的值以"http"开头的<a>元素[attribute$=value]a[src$=".jpg”]选择每一个src属性的值以”.jpg"结尾的<a>元素:first-childul li:first-child选择<ul>元素下的首个<li>元素:nth-child(n)ul li:nth-child(3)选择<ul>元素下的第三个<li>元素:last-childul li:last-child选择<ul>元素下的最后一个<li>元素DOM 树查询jQueryNative方法说明$ele.parent()ele.parentNode元素的直接父元素$ele.children()ele.childNodes元素的所有直接子元素$ele.find(“a”)ele.querySelectorAll(“a”)元素的后代元素$ele.prev()ele.previousElementSibling元素的上一个同胞元素$ele.next()ele.nextElementSibling元素的下一个同胞元素DOM 操作DOM本身就具有很丰富的操作方法,可以取代jQuery提供的操作方法。内容和属性jQueryNative方法说明var text = $ele.text()let text = ele.innerText获取所选元素的文本内容$ele.text(“text”)ele.innerText = “text"设置所选元素的文本内容var html = $ele.html()let html = ele.innerHTML获取所选元素的HTML内容$ele.html("<div>html</div>")ele.innerHTML = “<div>html</div>“设置所选元素的HTML内容var input = $ele.val()let input = ele.value获取表单字段的值$ele.val(“input”)ele.value = “input"设置表单字段的值var href = $ele.attr(“href”)let href = ele.getAttribute(“href”)获取元素的属性值$ele.attr(“href”, “/")ele.setAttribute(“href”, “/")设置元素的属性值修改 DOM 树jQueryNative方法说明$parent.append($ele)parent.appendChild(ele)在被选元素的结尾插入内容$parent.prepend($ele)parent.insertBefore(ele, parent.firstChild)在被选元素的开头插入内容$ele.after(html)ele.insertAdjacentHTML(“afterend”, html)在被选元素之后插入内容$ele.before(html)ele.insertAdjacentHTML(“beforebegin”, html)在被选元素之前插入内容$ele.remove()ele.parentNode.removeChild(ele)删除被选元素及其子元素$ele.empty()ele.innerHTML = null从被选元素中删除子元素$ele.clone()ele.cloneNode(true)拷贝被选元素$ele.replaceWith(html)ele.outerHTML = html指定HTML替换被选元素CSS 样式设置 StyleHTML DOM 允许 JavaScript 改变 HTML 元素的样式,Native API 提供了如下几种方式:ele.setAttribute 直接修改 DOM style 属性改变样式ele.style.cssText 通过 cssText 修改 Style 属性ele.style.property 通过 style 对象读写行内 CSS 样式jQuery:var size = $ele.css(“font-size”); // 返回第一个匹配元素的 CSS 属性值$ele.css(“font-size”, “2rem”); // 为所有元素设置指定的 CSS 属性值Native:let size = getComputedStyle(ele)[“font-size”]; // 获取当前元素计算后的 CSS 属性值ele.style.setProperty(“font-size”, “2rem”); // 设置当前元素的某个内联样式ele.style.removeProperty(“font-size”); // 移除当前元素的某个内联样式设置 ClassjQueryNative方法说明$ele.hasClass(className)ele.classList.contains(className)检查元素是否包含指定的类名$ele.addClass(className)ele.classList.add(className)向元素增加一个或多个类名$ele.removeClass(className)ele.classList.remove(className)从元素中移除一个或多个类$ele.toggleClass(className)ele.classList.toggle(className)对元素的一个或多个类进行切换事件方法绑定事件jQuery:$ele.on(“click”, function (evt) { console.log(evt.target);});Native:ele.addEventListener(“click”, evt => { console.log(evt.target);});解除绑定jQuery:$ele.off(“click”);Native:ele.removeEventListener(“click”, func);如果要移除事件,addEventListener 必须使用外部函数,绑定匿名函数的事件是无法移除的。模拟触发jQuery:$ele.trigger(“click”);Native:let event = document.createEvent(“MouseEvents”);event.initMouseEvent(“click”);ele.dispatchEvent(event);模拟事件:首先通过 document.createEvent 方法创建 Event 对象。然后利用 Event 对象的 init 方法对其进行初始化。最后使用 dispatchEvent 方法触发 Event 对象。详见:JavaScript 事件——“模拟事件”的注意要点AjaxjQuery$.ajax({ url: “http://apis.juhe.cn/ip/ip2addr", type: “GET”, data: { “key”: “80701ec21437ca36ca466af27bb8e8d3”, “ip”: “220.181.57.216” }, dataType: “json”, success: function (data) { console.log(data); }});XHR 封装window.ajax = async function (params, callback) { let url = params.url; let method = params.method; let data = params.data; let body = new FormData(); for (let key in data) { if (data.hasOwnProperty(key)) { body.append(key, data[key]); } } let xhr = new XMLHttpRequest(); xhr.timeout = 3000; xhr.open(method, url, true); xhr.addEventListener(“readystatechange”, evt => { if (xhr.readyState === 4) { if (xhr.status === 200) { callback(xhr.response); } else { throw xhr.statusText; } } }); xhr.send(body);};ajax({ url: “http://apis.juhe.cn/ip/ip2addr", method: “GET”, data: { “key”: “80701ec21437ca36ca466af27bb8e8d3”, “ip”: “220.181.57.216” } },function (resp) { var json = JSON.parse(resp); console.log(json); })Fetch APIXMLHttpRequest 并不是专为 Ajax 而设计的. 虽然各种框架对 XHR 的封装已经足够好用, 但更好用的 API 是 fetch 。/* 构造请求对象 /let request = new Request( “http://apis.juhe.cn/ip/ip2addr", { method: “GET”, body: { “key”: “80701ec21437ca36ca466af27bb8e8d3”, “ip”: “220.181.57.216” }, headers: new Headers() });/ 处理响应对象 */fetch(request) .then(response => response.json()) .then(function (data) { console.log(data); }) .catch(function (error) { console.log(error); });详见:fetch用法说明工具ArrayjQueryNative方法说明$.isArray(array)Array.isArray(array)判断参数是否为一个数组$.inArray(item, array)array.includes(item)判断值是否在指定数组中$.makeArray(objlist)Array.from(objlist)将类数组对象转换为数组$.merge(array1, array2)array1.concat(array2)合并两个数组(有区别)$.each(array, function (i, item) {}array.forEach((item, i) => {})遍历指定的对象和数组合并数组时,merge 会改变原数组的内容,而 concat 不会修改原数组,只会返回合并后的数组MethodjQueryNative方法说明$.now()Date.now()返回当前时间戳$.trim(context)context.trim()移除字符串头尾空白$.type(parameter)typeof parameter检测参数的内部类型$.parseJSON(jsonstr)JSON.parse(jsonstr)将JSON转换为JS对象$ele.data(“key”, “value”)ele.dataset.key = “value"在指定的元素上存储数据$.map(array, function (item, i) {})array.map((item, i) => {})将数组转化为处理后的新数组 ...

September 30, 2018 · 2 min · jiezi

js实现 web页面的滚动条下拉时加载更多

js实现 web页面的滚动条下拉时加载更多在手机上,数据列表的分页都是下拉到底部的时候会加载更多,但是,去年三月份的时候遇到了客户要求web页面也要下拉加载更多的需求,于是按照web页面在滚动条下拉时加载更多内容(个人项目经验)文中的代码实现了这个下拉加载,很简单的,代码如下:var totalPages;//总页数var pageno = 0;//当前页数$(function(){ $(window).scroll(function() { var scrollTop = $(this).scrollTop(),scrollHeight = $(document).height(),windowHeight = $(this).height(); var positionValue = (scrollTop + windowHeight) - scrollHeight; if (positionValue == 0) { //do something if (pageno < totalPages - 1) { pageno++; doSomething(pageno); } else { alert(‘没有更多了’); } } });); function doSomething(pageno) { var url = “”;//分页列表的接口 var data = { size: 5, start: pageno, }; $.getJSON(url, data, function (rtn) { });}但是,今天测试人员发现,当浏览器缩放了或者屏幕显示设置缩放时,就不能下拉加载了。时隔一年多,真是惊人@_@经过调试,发现是有缩放时positionValue的值就无法等于0了,没法继续加载更多了,这时看到一篇文章下拉加载更多DEMO(js实现)中讲到:如果等滚动条拉到底部时再加载,会影响用户体验。因为一般动态加载的时候都需要向服务端请求资源,这时需要时间。一个更佳的方式是,当滚动条距离底部一定距离(C)时,就动态加载更多,向服务端请求资源。也就是预加载,预读取。公式如下this.scrollHeight - C == $(this).scrollTop() + $(this).height()看完后收到启发,于是将positionValue的值设为大于等于-10,这里的10也就是滚动条距离底部一定距离(C)的值。果然,没问题了,有缩放时也可以正常实现下拉加载。于是,记录下来,分享给大家,共勉。另外提醒一点,$(window).scroll(function()监听滚动事件不执行这个问题中的采纳答案提到:html,body的高度样式如果设置为100%,$(window).scroll方法将检测不到正确的滚出高度(0),导致滚动监听事件失效,设置html,body{ height:auto }可以解决。代码var totalPages;//总页数var pageno = 0;//当前页数var C = 10;//滚动条距离底部的距离$(function(){ $(window).scroll(function() { var scrollTop = $(this).scrollTop(),scrollHeight = $(document).height(),windowHeight = $(this).height(); var positionValue = (scrollTop + windowHeight) - scrollHeight; if (positionValue >= -C) { //do something if (pageno < totalPages - 1) { pageno++; doSomething(pageno); } else { alert(‘没有更多了’); } } });); function doSomething(pageno) { var url = “”;//分页列表的接口 var data = { size: 5, start: pageno, }; $.getJSON(url, data, function (rtn) { });}相关参考web页面在滚动条下拉时加载更多内容(个人项目经验)下拉加载更多DEMO(js实现)$(window).scroll(function()监听滚动事件不执行 ...

August 30, 2018 · 1 min · jiezi