前端编程提高之旅(十八)—-移动端web流行交互技术方案研究

最后更新于:2022-04-01 09:37:28

**在停止实习后,生活最大变化在于没有项目的压力,可以根据兴趣场景,探索技术实现。这个过程对于个人来说,动力自内而外,需求自上而下,都由个人把握。** **生活在移动互联网井喷的今天,同时又关注前端技术,平常对微信端流行的交互(或者说玩法)有着特殊的敏感性。如果说之前接触MVC框架还是对单页面网站、CSS3前沿特效有一个概念的话,微信朋友圈及好友分享网页,则把国内网页构建的流行趋势,从幕后推向前台。** **乐帝通过研究移动端web流行交互,构建起了一个初步可行的技术方案,用来实现单页面与DOM动画。在这个过程中乐帝对于单页面概念、动画、技术通用实现等,有了更深层次理解。** **一、研究背景及现实需求** **1.研究背景** **如上所述,微信端朋友圈或好友消息中,分享的复杂交互的网页有两个特点:单页面切换与DOM元素特效。采用以上技术常见应用场景有:宣传、答题、讲故事。从本质上说,移动端采用如上两种交互形式,更像是替代flash在PC端的功能。单页面对应动画中的分帧的概念,DOM元素特效则对应动画中各种特效。** **2.现实需求** **如果说乐帝对前端技术的敏感算一点现实需求的话,那么乐帝近期做的答题项目以及为高中校友做一个给客户的新年祝福,更是迫切的需求。** **乐帝答题项目交互做的很简单,不断实现DOM的显示与隐藏,这正是对应动画分帧的功能。采用频繁的DOM操作,来不断进行view的切换,显然可以更加自动化。同时进入每个view页面可以更加绚丽多变,由此构成研究的现实需求。** **二、技术研究点及研究过程** **1.技术研究点** **如研究背景和需求所述,主要研究点为单页面切换与CSS3特效。在这两个研究点之外,还有一个对于浏览器全屏功能的研究,此研究点主要基于新年祝福项目中,乐帝考虑到为与PPT展现方式保持一致,需要有个全屏功能。** **2.研究过程** **首先通过搜索引擎搜索相关技术,这里乐帝推荐两个技术源:[jQuery插件库网站](http://www.jq22.com/)与[梦想天空博客园博客](http://www.cnblogs.com/lhb25/p/must-read-links-for-web-designers-and-developers-volume-12.html)。** **通过以上搜索确定了四个库或插件备选:** - 浏览器全屏插件:[jquery.fullScreen.js](https://github.com/dyygtfx/jquery.fullScreen.js) - 全屏滚动页面切换插件:[pagePiling.js](https://github.com/alvarotrigo/pagePiling.js) - DOM元素动画类库:[WOW.js](http://mynameismatthieu.com/WOW/docs.html) - 文本动画插件:Textillate.js **有了以上四个备选,逐个研究,最后确定技术方案。** **三、研究详细过程** **乐帝研究顺序依次是:全屏插件、DOM元素动画类库、全屏滚动。** **1.全屏插件的研究** **乐帝通用研究顺序可以表述为:阅读API、查看代码例子并作修改、应用到项目中。** **首先阅读了[全屏API实现原理](http://blog.csdn.net/tywali/article/details/8623938、),由此篇文章在DOM元素数据结构中确定了全屏API的位置。** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e4a838db.png)** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e4a976de.png)** **通过这半年对可视化的研究,乐帝考虑技术问题更多是从数据结构层次理解,还是有不少心得体会。全屏API构建如下:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e4aa76fb.png)** **研究[jQuery全屏插件](https://github.com/dyygtfx/jquery.fullScreen.js),发现插件文档不全,不能预期实现全屏切换。于是通读了此插件源码,源码实现非常简单:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e4ac4f9c.png)** **阅读完源码,即可根据需要,修改全屏插件,实现个性化定制。但后期乐帝考虑到,全屏除了视频使用场景外,网页还是非常不符合用户习惯的,后期技术方案并没有涉及此插件。但此研究过程,还是非常值得以后借鉴。** **2.DOM元素动画类库的研究** **乐帝本想除了WOW.js,再研究下文字特效框架Textillate.js,但考虑到文字特效本质上,也在DOM特效范畴内,就没有进一步单独研究文字特效。** **WOW.js采用了CSS3与jQuery动画互为依托,保证了兼容性。WOW.js作为类库,功能非常底层,用于构建动画要解决两个问题:** - 构造成帧,需要对DOM动画执行状态进行判断。 - 如采用事件监听成帧,需要不断加深嵌套,代码耦合性过高。 **为解决如上问题,乐帝采取了如下解决方案:** - 通过查找API例子,了解到动画监听事件:开始、结束、迭代。由此有了动画执行状态的判断。解决了成帧的问题。 - 通过采用单页面插件,每个view作为场景,由此用于减轻嵌套层级的问题。 **解决如上问题乐帝构建了采用WOW.js构建动画的方案:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e4c34a73.png)** **并对常用动画做了分类:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e4c570a6.png)** **3.全屏滚动页面切换插件研究** **通过研究pagePiling.js,此插件API基于状态构建:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e4c7b60c.png)** **API写法非常类似单页面框架,代码书写会更优雅,此插件用于构建动画中对应场景。** **四、技术方案研究结果** 通过以上研究确定了以WOW.js构建场景内帧动画、以pagePiling.js构建场景的技术方案。 以此方案构建的简单新年祝福应用下载地址为:[http://download.csdn.net/detail/yingyiledi/8281101](http://download.csdn.net/detail/yingyiledi/8281101) **五、研究的抽象过程** 乐帝回顾整个研究抽象过程如下:需求、技术选型、API阅读并结合例子修改、应用到项目中。
';

前端编程提高之旅(十七)—-jquery中表单、表格和ajax

最后更新于:2022-04-01 09:37:26

**从一个框架核心功能层面上说,实现了选择器、DOM操作、事件、动画,功能已经完备了。而任何框架写出了就是应用的,所以这篇涉及的表单与表格知识,虽在DOM操作中有所涉及,但更偏于实际项目中的应用技巧的使用。ajax部分的内容更是前端与后端交互的核心。** **一、jQuery中的表单与表格应用** **实际项目中,表单和表格分别扮演采集、提交用户输入的信息和显示列表的数据。在HTML中非常重要。下面是这部分知识导图:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e49c8051.png)** **1.表单** **(1)单行文本框** **单行文本框职能是获取用户输入的指定格式的数据。这就涉及两点交互。** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e49dce85.png)** **这两点交互都体现在获取焦点与失去焦点的变化上,当获取焦点时,单选框添加背景色、内容提示清空。失去焦点时,背景色去除,如果内容为空,内容提示恢复。** **jQuery代码如下:** ~~~ $(":input").focus(function(){ $(this).addClass("focus"); if($(this).val() ==this.defaultValue){ $(this).val(""); } }).blur(function(){ $(this).removeClass("focus"); if ($(this).val() == '') { $(this).val(this.defaultValue); } }); ~~~ **(2)多行文本框** **多行文本框一个比较重要的应用是控制滚动条的变化。如果有上滑的命令,则对文本框滚动条上移50px。反之亦然。** **jQuery代码如下:** ~~~ var $comment = $('#comment');//获取评论框 $('.up').click(function(){ //向上按钮绑定单击事件 if(!$comment.is(":animated")){//判断是否处于动画 $comment.animate({ scrollTop : "-=50" } , 400); } }) $('.down').click(function(){//向下按钮绑定单击事件 if(!$comment.is(":animated")){ $comment.animate({ scrollTop : "+=50" } , 400); } }); ~~~ **(3)复选框应用** **复选框对于用户文件管理非常重要,主要涉及三个功能:全选、全不选、反选。此三个功能都会涉及修改每个复选框checked属性。** **实现如上三个功能代码如下:** ~~~ $("#CheckedAll").click(function(){ $('[name=items]:checkbox').each(function(){ this.checked = true; }) }); //全不选 $("#CheckedNo").click(function(){ $('[name=items]:checkbox').each(function(){ this.checked=false; }); }); //反选 $("#CheckedRev").click(function(){ $('[name=items]:checkbox').each(function(){ //直接使用JS原生代码,简单实用 this.checked=!this.checked; }); ~~~ **实现上述功能仅仅是第一步,常见的全选/全不选是一个按钮,当列表选项全选时,全选又必须选中。所以全选与列表项存在着双向通信。体现在代码中,就必须做到双向控制:即除了对自身状态的修改,还需要判断是否需要修改另一个的状态。** **如下代码采用一个临时变量记录,判断是否全选中:** ~~~ //全选 $("#CheckedAll").click(function() { //所有checkbox跟着全选的checkbox走。 var self =this; $('[name=items]:checkbox').each(function(){ this.checked=self.checked; }); }); $('[name=items]:checkbox').click(function() { //定义一个临时变量,避免重复使用同一个选择器选择页面中的元素,提升程序效率。 var $tmp = $('[name=items]:checkbox'); //用filter方法筛选出选中的复选框。并直接给CheckedAll赋值。 $('#CheckedAll').attr('checked', $tmp.length == $tmp.filter(':checked').length); }); ~~~ **(4)下拉框应用** **下拉框的应用主要集中于两栏之间,下拉项在下拉框之前切换,主要涉及option标签selected属性用来筛选选中项,另外涉及dom移动。主要涉及移动选中、全部移动、双击移动。注意双击移动采用了上下文筛选移动项。** **代码如下:** ~~~ $('#add').click(function() { //获取选中的选项,删除并追加给对方 $('#select1 option:selected').appendTo('#select2'); }); $('#add_all').click(function() { //获取全部的选项,删除并追加给对方 $('#select1 option').appendTo('#select2'); }); $('#select1').dblclick(function(){ //绑定双击事件 //获取全部的选项,删除并追加给对方 $("option:selected",this).appendTo('#select2'); //追加给对方 }); ~~~ **(5)表单验证** **表单验证最为常见,需要对用户输入信息实时提示,这里采用失去焦点判断的方法,为了实时显示,同时在keyup和focus事件中,都触发blur方法,同时避免浏览器默认blur事件。** **代码如下:** ~~~ $('form :input').blur(function(){ var $parent = $(this).parent(); $parent.find(".formtips").remove(); //验证用户名 if( $(this).is('#username') ){ if( this.value=="" || this.value.length < 6 ){ var errorMsg = '请输入至少6位的用户名.'; $parent.append('<span class="formtips onError">'+errorMsg+'</span>'); }else{ var okMsg = '输入正确.'; $parent.append('<span class="formtips onSuccess">'+okMsg+'</span>'); } } //验证邮件 if( $(this).is('#email') ){ if( this.value=="" || ( this.value!="" && !/.+@.+\.[a-zA-Z]{2,4}$/.test(this.value) ) ){ var errorMsg = '请输入正确的E-Mail地址.'; $parent.append('<span class="formtips onError">'+errorMsg+'</span>'); }else{ var okMsg = '输入正确.'; $parent.append('<span class="formtips onSuccess">'+okMsg+'</span>'); } } }).keyup(function(){ $(this).triggerHandler("blur"); }).focus(function(){ $(this).triggerHandler("blur"); });//end blur ~~~ **2.表格** **(1)展开关闭** **表格的展开关闭,也即展开关闭两种状态的切换,前面动画讲过toggle方法可以切换显示隐藏,这里通过点选控制同类兄弟节点的关闭显示切换。** **代码如下:** ~~~ $('tr.parent').click(function(){ // 获取所谓的父行 $(this) .toggleClass("selected") // 添加/删除高亮 .siblings('.child_'+this.id).toggle(); // 隐藏/显示所谓的子行 }).click(); ~~~ **(2)内容筛选** **对于列表的内容筛选,主要采用过滤方法filter,筛选包含指定内容的列表项。当keyup时即筛选。** **代码如下:** ~~~ $("#filterName").keyup(function(){ $("table tbody tr") .hide() .filter(":contains('"+( $(this).val() )+"')") .show(); }).keyup(); ~~~ **二.jquery与ajax** **Ajax出现最大的意义在于,改变了以往前后端传数据需要全页面刷新来获取页面内容的方式,可以局部刷新页面。工作原理是在客户端实现了一个ajax引擎,使用户操作与服务器响应异步化。** **《jQuery攻略》上列出了ajax几大不足,但这似乎并没有影响ajax的广泛应用。作为前端开发人员,ajax带来的最大弊病就是异步响应的问题。异步响应需要开发人员时刻保持对时序执行逻辑的关注,往往很多问题都处在这里。** **jQuery提供了非常简便的ajax操作,代替了原生代码复杂冗余的操作,使ajax应用变得简单。以下是乐帝对ajax这部分内容的知识导图:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e4a2024a.png)** **1.ajax操作方法** **如上知识所示,ajax操作方法可以分为三层,由简单到复杂,由高层到底层。其中第二层最常用。这几个ajax方法底层都是对原生ajax方法的封装。但应用场景稍有不同,越是上层应用场景越具针对性,灵活性则不足。最底层$.ajax()灵活性虽足,配置起来则更为复杂。** **每个ajax操作方法都会涉及的参数有3个url(请求地址)、data(发送至服务器数据)、callback(请求完成时回调)。** **最常用的两个分别是$.get()与$.post(),两者分别对应ajax操作的get请求方式与post请求方式,这两种方式区别有三点:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e4a35a2c.png)** **$.getScript()与$.getJSON()两个ajax方法分别是针对动态加载脚本与json文件场景设定的方法。** **$.ajax()方法是上述ajax操作方法最底层的构建,此方法可以替代上述方法。** **2.参数序列化方法** **本文第一部分提到表单是获取用户数据的方式,用户填写完表单,发送到服务器就需要收集用户表单数据。如果一个个取数据还是很繁琐的。** **参数序列化方法正是自动将表单数据序列化,用于ajax请求。** **serialize()用于将DOM元素内容序列化为字符串:** ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e4a4b95a.png) **serializeArray()用于将DOM元素序列化,返回JSON格式数据:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e4a5d145.png)** **$.param()方法用来对数组或对象键值对序列化:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e4a6fe82.png)** **3.ajax请求控制方法** **这里主要涉及两个方法:Ajax请求开始时,会触发ajaxStart方法;Ajax请求结束时,会触发ajaxStop方法。常用于加强用户体验,给用户添加加载提示。** **使用示例:** ~~~ $("#send").click(function(){ $("#resText").load("test.html"); }) $("#resText").ajaxStart(function(){ console.log("ajax has started"); }); $("#resText").ajaxStop(function(){ console.log("ajax has stopped"); }); ~~~
';

前端编程提高之旅(十六)—-jquery中的动画

最后更新于:2022-04-01 09:37:23

**上一篇文章对jquery中的事件做了总结,这篇文章主要对jquery中的动画做一下总结归类。最近微信端分享中,有很多页面交互及动画做的非常受欢迎,非常符合移动端体验。看似花哨的动画从本质上都脱离不了编写动画的基本方法。乐帝将jquery动画部分内容,做了一个简单的归类。** **如下图:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e497409b.png)** **如上图所示,无论多复杂的动画,从实现上都采用这些最底层的动画方法。本篇将从动画方法和与动画状态有关的方法讲起。** **一、动画方法** **1.同时改变高、宽、不透明度方法** **这里涉及show和hide方法,动画的结果是设置元素样式display属性是否为none,然而既然说是动画,就要照顾到中间状态。这里动画方法参数都可以设置持续时间与执行完的回调方法。那么可以设置一个比较大的时间,来看看show方法与hide方法动画执行中是如何过渡的。** **典型用法如下例:** **HTML结构:** ~~~ <div id="panel"> <h5 class="head">什么是jQuery?</h5> <div class="content"> jQuery是继Prototype之后又一个优秀的JavaScript库,它是一个由 John Resig 创建于2006年1月的开源项目。jQuery凭借简洁的语法和跨平台的兼容性,极大地简化了JavaScript开发人员遍历HTML文档、操作DOM、处理事件、执行动画和开发Ajax。它独特而又优雅的代码风格改变了JavaScript程序员的设计思路和编写程序的方式。 </div> </div> ~~~ **jquery代码:** ~~~ $("#panel h5.head").click(function(){ if($(this).next().is(':visible')){ $(this).next().hide(10000); }else{ $(this).next().show(10000); } }) ~~~ **由于设置动画时间较长,可以很明显看出,动画过程中宽度、高度、透明度这三组值同时有一个变化的过程。** **2.只改变元素不透明度的方法** **这里涉及一组方法fadeIn方法与fadeOut方法,顾名思义,淡入与淡出效果,主要通过改变元素透明度实现。仍然延续上例,将判断语句中方法改成fadeIn与fadeOut方法。** ~~~ $("#panel h5.head").click(function(){ if($(this).next().is(':visible')){ $(this).next().fadeOut(10000); }else{ $(this).next().fadeIn(10000); } }) ~~~ **通过观察chrome控制台元素样式变化,会发现透明度在快速变化,最后状态为display为none或block。** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e4989656.jpg)** **3.只改变元素高度的方法** **slideUp方法与slideDown方法,这一组方法在动画执行过程中是通过改变元素高度来实现的。仍然延续上例,只将方法做修改。** ~~~ $("#panel h5.head").click(function(){ if($(this).next().is(':visible')){ $(this).next().slideUp(10000); }else{ $(this).next().slideDown(10000); } }) ~~~ **通过在浏览器控制台观察,很容易验证这组方法确实改变元素高度来过渡动画,但乐帝也发现上下padding也连带有个减小的过程。** **4.自定义动画** **animate方法与以上三组方法的区别在于,上述三组方法都脱身与animate方法,animate方法是动画的通用方法。animate方法可以有三个参数,第一个参数为动画执行要改变的样式终点值,第二个参数为动画执行时间,第三个为动画执行完的回调。** **不难看出,上述三组方法与animate方法的区别仅在于,三组方法动画执行要改变样式终点值形式已经定义好了,而animate仍需要指定。这看似麻烦的指定同时也给我们设置动画提供了极大的自由度。** ~~~ $(this).animate({width: "0px"}, 3000,function(){console.log("animation is done")}); ~~~ **这里自定义动画还可以设置累加/累减的方法:** ~~~ $(this).animate({width: "-=50px"}, 3000,function(){console.log("animation is done")}); ~~~ **5.多重动画** 多重动画只是在以上四组方法的基础上,进行业务上的排列组合构成。 **6.状态切换动画** **思维导图提到的其中三种方法:toggle方法、slideToggle方法、fadeToggle方法。这三种方法都可以切换可见性。fadeTo方法则是设置透明度。** **以slideToggle使用为例:** ~~~ $(this).next().slideToggle(); ~~~ **二、与动画状态有关的方法** **1.判断动画状态的方法** **这里并没有独立的方法,而是通过is方法判断。** ~~~ $('#mover').is(":animated") ~~~ **2.延迟动画** **实现动画延迟的方法为delay方法,参数传入需要延迟的时间。** **典型用法:** ~~~ $(this).animate({left: "400px", height:"200px" ,opacity: "1"}, 3000) .delay(1000) .animate({top: "200px" , width :"200px"}, 3000 ) .delay(2000) .fadeOut("slow"); ~~~ **3.停止动画** stop方法用于停止动画,可以传入两个布尔值做参数。第一个参数代表是否要清空未执行的动画队列,第二个参数代表是否直接将当前正在执行的动画跳转到末状态。 **常见场景当光标移入、移出时动画还没有结束,此时可以停止动画,执行此时逻辑添加的动画:** ~~~ <pre name="code" class="javascript">$("#panel").hover(function() { $(this).stop().animate({height : "150",width : "300"} , 200 ); },function() { $(this).stop().animate({height : "22",width : "60" } , 300 ); }); ~~~
';

前端编程提高之旅(十五)—-jquery事件

最后更新于:2022-04-01 09:37:21

**HTML与js交互主要通过用户与浏览器操作页面时引发事件。文档或某些元素发生某些变化或操作时,浏览器会生成事件。jquery增加了事件处理能力。** **jquery事件部分可以从以下五部分理解:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e494f731.png)** **一、加载DOM函数** **这里指的如下方法:** ~~~ $(document).ready(function(){ }) ~~~ **相比于传统的window.onload方法,前者在执行时机与可否多次使用上区别于后者。** **前者是在DOM完全就绪回调就会被调用,后者则是网页所有元素(包含关联文件)完全加载到浏览器后才会被调用。这里常见应用场景是,当需要对图片进行操作时,需要采用后者,因为后者才能保证图片文件被加载完成执行。** **前者可以多次使用,而后者只能使用一次,即便多次定义,后定义的函数会将之前定义的函数覆盖掉。** **二、各种事件绑定的方法** **(1)一般绑定方法:bind方法** **bind方法参数分别定义事件类型、回调函数。** **构建一个实例,HTML如下:** ~~~ <div id="panel"> <h5 class="head">什么是jQuery?</h5> <div class="content"> jQuery是继Prototype之后又一个优秀的JavaScript库,它是一个由 John Resig 创建于2006年1月的开源项目。jQuery凭借简洁的语法和跨平台的兼容性,极大地简化了JavaScript开发人员遍历HTML文档、操作DOM、处理事件、执行动画和开发Ajax。它独特而又优雅的代码风格改变了JavaScript程序员的设计思路和编写程序的方式。 </div> </div> ~~~ **bind使用代码如下:** ~~~ $("#panel h5.head").bind("click",function(){ var $content = $(this).next(); if($content.is(":visible")){ $content.hide(); }else{ $content.show(); } }) ~~~ **需要注意的是,回调函数内,this指的是带有相应行为的DOM元素,如需使用jquery中方法,徐将其转化为Jquery对象$(this)形式。** **(2)在bind基础上,简写绑定方法** **简写的绑定方法,方法名代表事件类型,参数为回调函数。** **依然延续上例,简写方法使用如下:** ~~~ $("#panel h5.head").mouseover(function(){ $(this).next().show(); }).mouseout(function(){ $(this).next().hide(); }) ~~~ **(3)合成事件** **这里合成事件有两个hover方法与toggle方法。** **hover方法用于模拟鼠标悬停事件,有两个回调函数作为参数,第一个回调代表光标移入执行,第二个回调代表光标移出执行。** **沿用上例,使用方法如下:** ~~~ $("#panel h5.head").hover(function(){ $(this).next().show(); },function(){ $(this).next().hide(); }) ~~~ **toggle方法用于模拟鼠标连续单击行为,参数为多个回调,每个回调会根据单击次序顺序循环执行。** **不过这个方法在[jquery1.9版本已经被删除](http://blog.sina.com.cn/s/blog_50042fab0101c7a9.html)了,这里并不多做讨论。** **(4)模拟操作** **模拟的是操作,也即是交互行为,从而引发事件,执行回调。这里用到的是trigger方法。** **举个例子,HTML代码:** ~~~ <button id="btn">点击我</button> <div id="test"></div> ~~~ **采用模拟操作的代码:** ~~~ $('#btn').bind("click", function(){ $('#test').append("<p>我的绑定函数1</p>"); }).bind("click", function(){ $('#test').append("<p>我的绑定函数2</p>"); }).bind("click", function(){ $('#test').append("<p>我的绑定函数3</p>"); }); $('#btn').trigger("click"); ~~~ **对DOM绑定了单击事件,采用trigger方法,模拟了click事件,从而执行了回调。** **这里trigger方法模拟操作的事件还可以是自定义事件,当然也需要同时绑定自定义事件。** **依然延续上例,采用自定义事件代码实例:** ~~~ $('#btn').bind("myClick", function(){ $('#test').append("<p>我的自定义事件.</p>"); }); $('#btn').click(function(){ $(this).trigger("myClick"); }); ~~~ **trigger方法除了可以模拟操作,还可以在第二个参数中传入数据,这在MVC框架中,view间传递数据极其常用。** ~~~ $('#btn').bind("myClick", function(event, message1, message2){ $('#test').append( "<p>"+message1 + message2 +"</p>"); }); $('#btn').click(function(){ $(this).trigger("myClick",["我的自定义","事件"]); }); ~~~ **采用triggerHandler方法可以模拟操作的同时避免触发浏览器默认操作,具体实例如下:** **html:** ~~~ <button id="old">trigger</button> <button id="new">triggerHandler</button> <input /> ~~~ **jquery代码:** ~~~ $('#old').bind("click", function(){ $("input").trigger("focus"); }); $('#new').bind("click", function(){ $("input").triggerHandler("focus"); }); $("input").focus(function(){ $("body").append("<p>focus.</p>"); }) ~~~ **对比可知,后者没有触发浏览器默认聚焦功能。** **(5)绑定多个事件** **绑定多个事件的方法可采用bind方法,在第一个参数中将事件类型空格隔开。** **使用案例如下:** ~~~ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <style> div{ width:100px; height:50px; } .over{ color:red; background:#888; } </style> <script src="../../scripts/jquery.js" type="text/javascript"></script> <script type="text/javascript"> $(function(){ $("div").bind("mouseover mouseout click", function(){ $(this).toggleClass("over"); }); }) </script> </head> <body> <div >滑入.</div> </body> </html> ~~~ **(6)给绑定事件添加命名空间** **命名空间便于管理,当元素绑定多个事件时,删除事件只需要指定命名空间,节省了代码量。** **实例:** ~~~ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <style> div{width:100px;height:50px;background:#888;color:white;} </style> <script src="../../scripts/jquery.js" type="text/javascript"></script> <script type="text/javascript"> $(function(){ $("div").bind("click.plugin",function(){ $("body").append("<p>click事件</p>"); }); $("div").bind("mouseover.plugin", function(){ $("body").append("<p>mouseover事件</p>"); }); $("div").bind("dblclick", function(){ $("body").append("<p>dblclick事件</p>"); }); $("button").click(function() { $("div").unbind(".plugin"); }) /* click,mouseover 事件被删除, */ }) </script> </head> <body> <div>test.</div> <button >根据命名空间,删除事件</button> </body> </html> ~~~ **三、移除事件** **上面提到了绑定事件的各种方法,移除事件主要采用unbind方法,第一个参数即移除事件名,第二个参数可选为移除回调函数。** **使用实例:** ~~~ $('#btn').bind("click", myFun1 = function(){ $('#test').append("<p>我的绑定函数1</p>"); }).bind("click", myFun2 = function(){ $('#test').append("<p>我的绑定函数2</p>"); }).bind("click", myFun3 = function(){ $('#test').append("<p>我的绑定函数3</p>"); }); $('#delTwo').click(function(){ $('#btn').unbind("click",myFun2);//移除第二个绑定click函数 }); ~~~ **假如事件只需执行一次,可以采用one方法进行事件绑定,用法类似bind方法。** ~~~ $('#btn').one("click", function(){ $('#test').append("<p>我的绑定函数1</p>"); }).one("click", function(){ $('#test').append("<p>我的绑定函数2</p>"); }).one("click", function(){ $('#test').append("<p>我的绑定函数3</p>"); }); ~~~ **四、事件对象属性** **事件对象,顾名思义即触发事件后,传入回调函数的对象,包含事件类型、事件目标、光标位置等与事件有关的属性。** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e49611e1.png)** **根据这些事件对象属性,便于编写关于本事件的各种操作。常用的防止多次单击、防止冒泡、默认操作都采用事件对象属性实现。** **实例:** ~~~ $("a").click(function(event) { alert("Current mouse position: " + event.pageX + ", " + event.pageY );//获取鼠标当前相对于页面的坐标 return false;//阻止链接跳转 }); ~~~ **五、事件冒泡与阻止默认行为** **(1)事件冒泡** **关于事件冒泡,即触发一个事件,整个事件会从本层元素向外部包含的元素扩散,这样就可能导致,不希望触发的元素被触发事件。** **实例:** ~~~ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>4-4-1</title> <style type="text/css"> *{margin:0;padding:0;} body { font-size: 13px; line-height: 130%; padding: 60px; } #content { width: 220px; border: 1px solid #0050D0;background: #96E555 } span { width: 200px; margin: 10px; background: #666666; cursor: pointer;color:white;display:block;} p {width:200px;background:#888;color:white;height:16px;} </style> <script src="../../scripts/jquery.js" type="text/javascript"></script> <script type="text/javascript"> $(function(){ // 为span元素绑定click事件 $('span').bind("click",function(){ var txt = $('#msg').html() + "<p>内层span元素被点击.<p/>"; $('#msg').html(txt); }); // 为div元素绑定click事件 $('#content').bind("click",function(){ var txt = $('#msg').html() + "<p>外层div元素被点击.<p/>"; $('#msg').html(txt); }); // 为body元素绑定click事件 $("body").bind("click",function(){ var txt = $('#msg').html() + "<p>body元素被点击.<p/>"; $('#msg').html(txt); }); }) </script> </head> <body> <div id="content"> 外层div元素 <span>内层span元素</span> 外层div元素 </div> <div id="msg"></div> </body> </html> ~~~ **解决事件冒泡的方法是在上述案例中,执行事件对象属性防止对象冒泡方法event.stopPropagation方法。** ~~~ $('span').bind("click",function(event){ var txt = $('#msg').html() + "<p>内层span元素被点击.<p/>"; $('#msg').html(txt); event.stopPropagation(); // 阻止事件冒泡 }); // 为div元素绑定click事件 $('#content').bind("click",function(event){ var txt = $('#msg').html() + "<p>外层div元素被点击.<p/>"; $('#msg').html(txt); event.stopPropagation(); // 阻止事件冒泡 }); ~~~ **(2)阻止浏览器默认行为** **这里采用的是事件对象属性的event.preventDefault方法。** **(3)如果既想阻止冒泡行为,又需要阻止浏览器默认行为,可以再回调中返回false,一并执行。** **实例:** ~~~ $('span').bind("click",function(event){ var txt = $('#msg').html() + "<p>内层span元素被点击.<p/>"; $('#msg').html(txt); return false; }); ~~~
';

前端编程提高之旅(十四)—-jquery DOM操作

最后更新于:2022-04-01 09:37:19

**正如乐帝在[《招聘后台投递设置联动按钮迭代开发总结》](http://blog.csdn.net/yingyiledi/article/details/41323175)提到web前端js开发分为页面间通信、页面内交互、后端接口存取数据的操作。对于一个页面开发核心在于对DOM的操作。别忘了网页从历史上和本质上来说都是在线的文档而已。DOM操作是对文档的操作,自然而然就成为网页js开发的核心了。** **传统上来说,DOM操作分为三类:DOM core、HTML-DOM、CSS-DOM。** - DOM core在任何使用DOM程序设计语言都可以使用。常见的javascript方法如getElementById()、getElementsByClassName()、getAttribute()、setAttribute()都属于此类。 - HTML-DOM是专门使用js为HTML编写脚本时用到的方法,常见的简明如element.src写法。写法上相对于DOM core要简洁不少。 - CSS-DOM主要用于获取和设置style对象各种属性。 **Jquery中DOM操作,乐帝将其分两种:HTML-DOM、CSS-DOM。** **整个DOM操作都可以从一句典型HTML语句展开,上帝说要有光,就有了hmtl元素,接着就有了附加的各种可预见的操作。如下图:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e48ea355.png)** **一、jquery中的HTML-DOM操作** **1.节点操作** **(1)插入节点** **对于节点的插入操作方法可以分两类,类似中国小学教育“把”字句与“被”字句用法,根据前面jquery对象的主被动划分。** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e4907dc9.png)** **(2)删除节点** **主要涉及两个方法的区别:remove方法与detach方法。这两个方法区别在于使用remove方法删除节点后,再次添加此节点引用,原本绑定在此节点的事件、数据都会失效。而后者可以保留下来。** **下面举个例子:** ~~~ <input type="checkbox" id="cr"/> <label for="cr">我已经阅读了上面制度.</label> ~~~ ~~~ $(document).ready(function(){ var $cr = $("#cr"); //jQuery对象 var cr = $cr.get(0); //DOM对象,获取 $cr[0] $cr.click(function(){ if(this.checked){ //DOM方式判断 alert("感谢你的支持!你可以继续操作!"); } }) }); ~~~ **上述代码后,分别加入以下代码:** ~~~ // var removeCr = $cr.remove(); var removeCr =$cr.detach(); $("body").append(removeCr); ~~~ **当采用remove方法删除节点,再对其引用添加时,没有单击事件被绑定。而采用detach方法删除节点,再添加,仍然保留了单击事件。当然涉及删除再添加并需要绑定事件的场景,目前项目中还没有遇到。** **(3)复制节点** **复制节点采取clone方法,而clone方法参数如果为true的话,除了复制节点外,节点上绑定的事件也会一并复制。仍然延续上述例子,添加如下代码:** ~~~ // var cloneCr = $cr.clone(); var cloneCr = $cr.clone(true); $("body").append(cloneCr); ~~~ **当有参数true时,被复制的元素的绑定方法也一并被被复制了,而没有参数则没有绑定方法被复制。** **(4)替换节点** **替换节点有两种方法:replaceWith方法、replaceAll方法。这两个方法也满足上述主动、被动关系。** **仍然延续上述代码,使用例子:** ~~~ $cr.replaceWith("<p>hi</p>"); ~~~ **此时替换的节点没有绑定事件,需要重新绑定事件。** **(5)包裹节点** **包裹节点又分为两类:外部包裹方法、内部包裹方法。区别在于是对选定元素进行包裹,还是对其内部元素包裹(wrapInner方法)。外部包裹又分为整个用一个方法包裹(wrapAll方法),还是每个选定元素都包裹(wrap方法)。** **对下述例子,分别采用三种方法:** ~~~ <strong title="选择你最喜欢的水果." >你最喜欢的水果是?</strong> <strong title="选择你最喜欢的水果." >你最喜欢的水果是?</strong> <ul> <li title='苹果'>苹果</li> <li title='橘子'>橘子</li> <li title='菠萝'>菠萝</li> </ul> ~~~ ~~~ // $("strong").wrap("<b></b>"); // $("strong").wrapAll("<b></b>"); $("strong").wrapInner("<b></b>"); ~~~ **通过查看HTML结构,可知包裹的区别。** **(6)遍历节点** **遍历的方法可从dom结构为树形结构理解。从树的上到下,分别会用到上级元素:祖先元素获取方法(parents方法)、父级元素获取方法(parent方法);同级元素获取的三个方法:next方法、prev方法、sibling方法、closest方法;子级元素方法:children方法。** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e491e51e.png)** **2.属性操作** **属性操作主要用到一个attr方法,用于获取设置属性;一个removeAttr方法用于移除属性。** **仍然延续上述例子,输入以下代码:** ~~~ $("li:eq(0)").attr("title","apple"); $("li:eq(0)").removeAttr("title"); ~~~ **可知属性操作方法的使用。** **3.样式操作** **这里的样式操作主要从class增删查找,连带改变class定义的css样式。** **仍延续上述例子,添加一个css类:** ~~~ .test{ color: red; } ~~~ **  对上述例子分别采用四种操作:** ** ~~~ $("li:eq(0)").addClass("test"); var hasClass = $("li:eq(0)").hasClass("test"); // $("li:eq(0)").toggleClass("test"); $("li:eq(0)").removeClass("test"); ~~~ **上述操作分别代表添加类、判断类是否存在、移除或添加类的切换、移除类。** **3.获取各种元素的值** **这里主要涉及到val方法。无论是文本框、下拉列表还是单选框,都可以返回元素值。这个统一性使得再遇到此类情况,不用再现查。** **二、CSS-DOM操作** **这里主要涉及三类方法:css方法、宽高设置的方法、定位的几个方法。** **css获得高度与height获得高度区别在于,css获得的是与样式设置有关的高度,而height获得的是元素整体高度。不会受样式影响。** **offset方法是获取元素相对当前视窗的相对便宜,返回对象包含两个属性,top与left,只对可见元素有效。scrollTop方法与scrollLeft方法用于获得元素的滚动条相对顶端和左侧的距离。** **仍延续上例,具体使用方法:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e49343c8.png)**
';

前端编程提高之旅(十三)—-jquery选择器

最后更新于:2022-04-01 09:37:16

**Jquery代表javascript和query,即对javascript查询操作的类库,jquery是web前端界的事实标准。最近读《锋利的jquery》,发现这本书最大特点是,更深层的讨论一些jquery使用情境和优化方法,对于有一定jquery使用经验的开发者有独特价值。本篇算是乐帝读此书,对选择器和DOM操作加深理解的总结。** **javascript自身包含三大弊端:** - **复杂的文档对象模型(DOM)** - **不一致的浏览器实现和便捷的开发** - **调试工具的缺乏** **Jquery存在的意义就在于很好地解决了前两个弊端,而后一个弊端也随着近些年,特别是以chrome为首的浏览器厂商大幅提升浏览器性能和功能得到了很好地解决。** jquery代码编写风格: **jquery代码核心特点:不需要显示new对象、链式调用。链式调用实际使用时,非常方便对一个对象多个操作。而不好的方面是,降低了可读性,所以写代码需要遵循一定的规则。** **单个对象较多操作,每行写一个操作:** ~~~ $(this).removeClass("mouseout") .addClss("mouseover") .stop() .fadeTo("fast,0.6") .unbind("click"); ~~~ **如果多个对象的少量操作时,每个对象写一行,涉及子元素,适当缩进。** ~~~ $(this).removeClass("mouseout") .children("li").show().end() .siblings().addClss("mouseover") .children("li").hide() ~~~ **jquery对象与DOM对象区别与联系:** **DOM对象可以使用javascript中的方法,而Jquery对象则是通过Jquery包装DOM对象后产生的对象。如果是jquery对象就可以使用jquery中的方法。** **如果获取对象是jquery对象,那么在变量前加$:** ~~~ var domObj = document.getElementsByTagName("h3")[0]; // Dom对象 var $jQueryObj = $(domObj); //jQuery对象 ~~~ **jquery对象是一个类似数组的对象,可通过两种方法得到相应dom对象:** ~~~ var $cr = $("#cr"); // 第一种 var cr =$cr[0]; // 第二种 var cr $cr.get(0); ~~~ **DOM转jquery只需要使用$()将DOM对象包装起来就可获得:** ~~~ var domObj = document.getElementsByTagName("h3")[0]; // Dom对象 var $jQueryObj = $(domObj); //jQuery对象 ~~~ **不过需要注意的是上述方法$()获取的永远是对象,即便网页上没有取到元素。如下图实例:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e48d83ea.png)** **所以此时判断是否含有元素的方法需要判断对象长度或者转化成DOM对象判断:** ~~~ if ($("#noExitst").length) { console.log("this is an object"); } else { console.log("there is no DOM element"); } ~~~ **高性能使用jquery选择器:** **jquery选择器各种选择器的使用网上已经科普很多了,这里主要从性能上讨论选择器。** **选择器的性能主要从jquery源码与javascript核心方法结合与否及遍历复杂度考虑。** **首先一点,能够预见高性能的方法是能够直接采用javascript本地方法的选择器。比如$("#id")、$("div")都有本地方法与之对应:document.getElementById()、**document.getElementByTagName()。而$(".class")、$("[attribute=value]")、$(":hidden")此类选择器都没有本地方法实现,大多使用DOM搜索方式达到效果,所以从性能上考虑,后面这些方法是比较有害的。 **总体建议是:尽量使用ID选择器、尽量给选择器指定上下文。乐帝的建议是,先使用ID选择器选定父元素,再根据标签选择器选择标签。有点类似css中先使用class再使用层级选择器的方法。** **jquery构造的多种选择器,虽然方便了使用,但却牺牲了性能。所以性能与高效上没有银弹。**
';

前端编程提高之旅(十二)—-position置入值应用

最后更新于:2022-04-01 09:37:14

**这次内推项目用到的遮罩及其页面下方按钮都涉及一个概念position置入值得概念。效果图如下:** ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e473089a.jpg) **一个元素position属性不是默认值static,那么该元素被称为定位元素。定位的元素生成定位框,其定位基于四个特性(置入值):'top','right','bottom','left'。** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e475084e.jpg)** **现在的问题在于当置入值在声明与否起什么作用。** **一、声明置入值得情形** **其置入值的参照物即为最近的申明了"position"为"absolute\relative\fixed的祖先元素。这是通常的情形。** **下面看一个例子:** ~~~ <div style=" width:500px; height:500px; background:#000; margin-left:200px;"><div style="position:absolute; left:200px; margin-left:100px; background:#F00; width:50px; height:50px;"></div></div> ~~~  当设置置入值为0时,此时内部div位置如下图: **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e475d9b8.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e476d33d.jpg)** **此时可验证上述有置入值时,结论正确,这里内部div参照物为body元素。** **二、未声明置入值情形** **如果绝对定位元素没有申明置入值,只申明了position:absolute,其自身定位以及margin的参照物即为其最近的块级、单元格(table cell)或者行内块(inline-block)祖先元素的内容框。** **依然是上述例子,将置入值取消掉,看效果如图:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e477e1fd.jpg)** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e478c8f0.jpg)** **此时没有设置置入值,内部div参照物是上层div,即第一个块级元素。** **通过以上两个例子,我们知道有置入值和没有设置置入值是有区别的,区别在于当没有设置置入值时,浏览器会默认给定置入值为auto。** **三、置入值应用** **由最上面置入值的示意图可知,每个置入值都是相对于参照物距离来定位定位元素的。那么就有一个非常有趣的情形。乐帝设置一个将上述内层div宽度和高度去掉,并设置四个置入值都为0,按照上述理论,那么这个div将会参照body填充满整个body。** **如下图:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e47a07e5.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e47ae952.jpg)** **上述这个特性常用于设置遮罩,当然此时遮罩层颜色不能这么花。例如这次项目中用到的遮罩代码:** ~~~ .search-area-container { display:block; position:absolute; left:0; right:0; top:0; bottom:0; background-color:transparent; z-index:1100; } ~~~ **这里设置的颜色为透明色。效果图如下:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e47bfa77.jpg)** **菜单栏延伸到底部代码:** ~~~ /*设置菜单栏默认隐藏,并向下延伸到底部*/ #search-area{ position:fixed; left:-60%; top:43px; bottom:0; width:60%; z-index:1111; color: #fff; background-color: #418cd5; text-shadow:none; border:none; border-radius:0; } ~~~ **职位详情页底部按钮栏延伸到三个方向代码:** ~~~ /*延伸到左右下,设置内容居中,添加一些阴影效果*/ .btn-wrap { position: fixed; bottom: 0; left: 0; right: 0; text-align: center; background-color: #f0f0f0; box-shadow: 0 -1px 2px #aaa; } ~~~
';

前端编程提高之旅(十一)—-jquery代码的组织

最后更新于:2022-04-01 09:37:12

**最近做内推项目,虽然项目不算太大,还是遇到了一些代码组织的问题,说到底还是对整个项目的掌控力不够,为此乐帝专门在网络中搜集了一些jquery代码组织的文章并总结出两种方法来更好组织jquery代码。** **一、回调函数** **回调函数的定义:** **A callback is a function that is passed as an argument to another function and is executed after its parent function has completed.** 回调是将一个函数作为变量,传入另外一个函数。前者将会在后者执行完成后执行。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e470818a.jpg) 上述简单的回调例子,说明函数的参数可以是一个函数,这也是jquery回调函数用法的基础了。 ~~~ function hideSearchArea(callback) { $(".search-area-container").hide(); var width = searchArea.width(); searchArea.animate({ "left": -width }, 400, function () { if (callback) { callback(); } }); } btnSearch.click(function () { var keyWord = $('#keyWord').val(); var locId = $("#citySelect").val(); if (keyWord == oldSearchData.keyWord && locId == oldSearchData.locId) { hideSearchArea(); return false; }//老数据不返回数据 hideSearchArea(function () { jobList.empty(); pageNum = 1;//清空page数 getInternalRecommendJobAdList(keyWord, locId);//加载搜索项 });//采用回调,省去了传参数的问题 }); ~~~ 此次内推项目中也用到了回调,在上述代码例子中,回调的好处在于省去了复杂的函数间传参的过程,在click事件中定义的局部变量,直接传入hideSearchArea函数的回调中,省去了多余变量控制参数的传递。 **二、jquery代码组织要点** - 1.解决消除匿名函数。 - 2.以配置参数为中心。 - 3.将事件监听统一到一个函数内部。 - 4.将整个程序段封装成一个函数,对外只保留唯一接口,便于大型项目代码组织。 **下面通过一个简单的例子看一下代码组织的应用:** **html结构:** ~~~ <ul id="myFeature"> <li>hi oop</li> </ul> ~~~ jquery代码: ~~~ var myFeature = { //首先类的实例属性用this.xxx形式定义,类属性用className.xxx形式定义 //以下各函数与配置对象都为类属性 // 初始化配置Jquery对象参数,并调用事件绑定设定函数 // 类内部,使用_this对对象进行替代,便于标示 init : function(settings) { _this = myFeature; _this.config = { $items : $('#myFeature li'), $container : $('<div class="container"></div>'), }; $.extend(_this.config, settings || {}); _this.setup(); }, // 事件绑定设定函数,用于绑定事件,沟通jquery对象与响应事件 setup : function() { var item = _this.config.$items; item.each(_this.createContainer) .click(_this.showItem); }, //在li下创建div并设置数据 createContainer : function() { var $i = $(this), $c = _this.config.$container.clone() .appendTo($i); $i.data('container',"hello world"); }, showItem : function() { _this.$currentItem = $(this); _this.getContent(); }, getContent : function() { var txt = _this.$currentItem .data('container'); $(".container").html(txt); }, }; $(function(){ myFeature.init(); });  ~~~ **从这段代码不难看出,上述代码组织思想。** - 将用到的DOM对象统一写到配置(config)对象中 - **将事件监听对象统一写到安装函数(setup)中** - **配置属性及安装函数在初始化函数(init)函数中调用** - **安装函数事件监听函数采用函数回调方法,调用本类功能函数** - **在整段程序的入口调用此类的初始化(init)方法** 上述程序有个弊端,那就是在外部可以对类方法进行访问。 **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e471e935.jpg)** **对上述方法进一步优化,将整个对象改成一个自执行函数,那么返回值为初始化对象就是对外的唯一接口,内部编排也不用考虑对象引用的问题,本质上仍然是面向过程编程,但代码管理上已经做到了极好的封装。** **代码如下:** ~~~ var feature = (function() { var $items = $('#myFeature li'), $container = $('<div class="container"></div>'), $currentItem, init = function(settings){ setup(); },//初始化函数 setup= function() { var item = $items.each(createContainer) .click(showItem); },//绑定函数 createContainer = function(idx) { var $i = $(this), $c = $container.clone() .appendTo($i); $i.data('container',"hello world"); }, showItem = function(){ $currentItem = $(this); getContent(); }, getContent = function() { var txt = $currentItem .data('container'); $(".container").html(txt); }; return { init : init//对外接口 }; })(); feature.init();//初始化函数 ~~~  如上代码所示可以实现,前文所述的四个要点。但考虑到这次内推项目脚本都是针对每个页面单独执行,就没有采用这种组织方式。但整体思路上仍然采用上述方法。即只实现前三个要点,代码实现上用第二种代码写法。 **三、参考资料** **1.知乎网友:http://www.zhihu.com/question/26348002** **2.360个人图书馆:http://www.xue163.com/121/6/1212972.html** **3.csdn网友:http://blog.csdn.net/dananhai381/article/details/6709934**
';

前端编程提高之旅(十)—-表单验证插件与cookie插件

最后更新于:2022-04-01 09:37:10

实际项目开发中与用户交互的常见手法就是采用表单的形式,取得用户注册、登录等信息。而当用户注册或登录后又需要记住用户的登录状态。这就涉及到常用的两个操作:表单验证与cookie增删查找。 项目开发当然可以手动或用原生代码解决以上两个问题,而最好的实践应该是站在巨人的肩膀上,从合理性推测角度和不重复造轮子的原则上,都应该对成熟的技术,实行拿来主义,更加快速、高效、稳定的将精力放在项目逻辑实现上。 **一、表单验证插件([jQuery Validate](http://jqueryvalidation.org/))** **官方描述:只需要一行来选择表单并且应用validation插件,在每个元素上加上一些附注来指定有效规则。** **jQuery Validate可以将验证规则写到表单的内部表单项中,但这样并没有实现行为与结构的分离,可读性不好,耦合性高。一般会采用name属性来关联字段和验证规则的验证写法。** **首先我们来创建表单:** ~~~ <form class="cmxform" id="commentForm" method="get" action=""> <fieldset> <legend>一个简单的验证带验证提示的评论例子</legend> <p> <label for="cusername">姓名</label> <em>*</em><input id="cusername" name="username" size="25" /> </p> <p> <label for="cemail">电子邮件</label> <em>*</em><input id="cemail" name="email" size="11" /> </p> <p> <label for="mobile">手机</label> <em>*</em><input id="cmobile" name="mobile" size="25" /> </p> <p> <label for="curl">网址</label> <em> </em><input id="curl" name="url" size="25" value="" /> </p> <p> <label for="ccomment">你的评论</label> <em>*</em><textarea id="ccomment" name="comment" cols="22"></textarea> </p> <p> <input class="submit" type="submit" value="提交"/> </p> </fieldset> </form> ~~~ **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e45616cb.jpg)** **表单结构** **引入验证插件后,验证规则的代码:** ~~~ $("#commentForm").validate({ // 验证规则 rules: { mobile: { required: true, isMobile:true }, username: { required: true, minlength: 2 }, email: { required: true, email: true }, url:"url", comment: "required" }, // 提示信息 messages:{ mobile: { required: "手机号不能为空", minlength: "手机号至少为11位" }, username:{ required:"用户名不能为空", minlength: "用户名至少为两位" }, email: { required: "邮箱不能为空", email: "请输入正确邮箱格式" }, url:"请输入正确url格式", comment: "评论不能为空" } }); ~~~ **$("#commentForm").validate(),这句代码算是jquery插件标准用法,选择器选中dom节点构造成jquery对象,在这个对象上调用插件功能函数。** **在validate方法内包含两部分数据:一部分是验证规则“rules”,一部分是验证提示信息“messages”。两部分信息内部,又根据表单中表单项name属性关联具体验证规则。** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e4570a14.jpg)** **如上填写表单提交时,提示信息** **表单是否可以提交,可以用valid函数判断,如上情况valid返回值情况,如下图:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e458135e.jpg)** **此方法可用于表单是否提交,验证条件。** **当表单正常填写时,如下图:** ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e458f888.jpg) **返回值。如下图:** ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e46a0109.jpg) **jQuery Validate还允许用户自定义验证规则,本例中乐帝重新定义了一个手机号验证规则:** ~~~ jQuery.validator.addMethod("isMobile", function(value, element){ var length = value.length; return length == 11 && /^[1][3-8]\d{9}$/.test(value); }, "请填写正确的手机号码"); ~~~ **验证规则写法addMethod包含三部分:验证规则名,验证函数、验证返回信息。** **验证规则调用:** ~~~ mobile: { required: true, isMobile:true } ~~~ **二、cookie插件的使用** **在[cookie插件官网](https://github.com/carhartl/jquery-cookie#readme)文档中,不难发现cookie插件用法非常简单。** **引入插件文件后,构建以下结构:** ~~~ 用户名:<input type="text" name="username" id="username"/> <br/> <input type="checkbox" name="check" id="check"/>记住用户名 ~~~ **相应脚本:** ~~~ var COOKIE_NAME = 'username'; if( $.cookie(COOKIE_NAME) ){//取出cookie值 $("#username").val( $.cookie(COOKIE_NAME) ); } $("#check").click(function(){ if(this.checked){ $.cookie(COOKIE_NAME, $("#username").val() , { path: '/', expires: 10 }); }else{ $.cookie(COOKIE_NAME, null, { path: '/' });//删除cookie值,将值设为null即可 } });//通过记录是否点选单选钮,是否将数据存入cookie ~~~ **页面通过一个单选按钮选中与否记录cookie的存储于删除。** ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e46affe3.jpg) **选中单选框** ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e46be6cf.jpg) 数据存入cookie ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e46d0ce7.jpg) **取消单选框** ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e46e11ab.jpg) **cookie清空** **这里需要注意的是,cookie清空可采用两种方法,一种是移除$.removeCookie('name'),一种是本文方法将cookie相应值设置成null。**
';

前端编程提高之旅(九)—-延迟对象

最后更新于:2022-04-01 09:37:07

近日做内部推荐项目,在进入项目首页时,需要进行预调用接口判断,按照以往的习惯,应该将调用接口后执行的代码,放入ajax请求success内执行。这样当然可以,但是代码组织和可读性都没有那么高。通过查看[Jquery官网api](http://api.jquery.com/category/deferred-object/)发现了deferred object(延迟对象)这个概念。 **一、API文档的翻译:** 延迟对象,在jquery1.5被引入,是调用jQuery.Deferred()方法生成的一个链式功用对象。它可以注册多个回调到回调队列,提取回调队列,传递任何异步或同步函数的成功或失败状态。 延迟对象是一个链式的,类似于jquery对象的链式,但是它有自己的方法。在创建之后,你可以用任何下面涉及到的方法,无论是直接链式调用,还是保存这个对象到变量,并在这个变量上调用一个或多个方法。 - **deferred.always()** **添加操作方法,不管延迟对象是处于解决状态还是拒绝状态都被调用。** - **deferred.done()** **添加操作方法,只有延迟对象处于已解决状态才被调用** - **deferred.fail()** **添加操作方法,只有延迟对象处于被拒绝状态才会被调用** - **deferred.isRejected()** **判断一个延迟对象是否处于被拒绝状态** - **deferred.isResolved()** **判断一个延迟对象是否处于已解决状态。** - **deferred.notify()** **调用一个给定变量的延迟对象上的进行中的回调。** - **deferred.notifyWith()** **调用一个给定变量和上下文的延迟对象上的进行中的回调。** - **deferred.pipe()** **用来过滤and或or链式延迟的效用方法** - **deferred.progress()** **当延迟对象生成进行中通知时,添加操作被调用。** - **deferred.promise()** **返回延迟允诺对象。** - **deferred.reject()** **拒绝一个延迟对象并且调用任何给定参数的失败回调。** - **deferred.rejectWith()** **拒绝回调对象,调用任何给定上下文和参数的失败回调。** - **deferred.resolve()** **解决一个延迟对象,并且调用任何给定参数的完成回调。** - **deferred.resolveWith()** **解决一个延迟对象,并且调用任何给定上下文和参数的完成回调。** - **deferred.state()** **判断当前延迟对象状态。** - **deferred.then()** **当延迟对象被解决、拒绝或进行中添加方法被调用。** - **jQuery.Deferred()** **延迟对象的构造函数。** - **jQuery.when()** **提供一个方法执行基于一个或多个对象的回调函数,回调函数经常是代表异步事件的延迟对象** - **.promise()** **当所有的绑定到集合、队列或没有的动作被完成返回一个promise对象。** **二、延迟对象分析与使用:** **不难得出结论,以上API包括了延迟对象的构建、延迟状态设定方法、延迟状态监听方法。** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e44d47d5.jpg)** **延迟对象的数据结构** 延迟对象包括三种状态:resolved(已解决)、rejected(未解决)、progress(进行中)。这三种状态可以用延迟对象state方法实时查看。 **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e4508190.jpg)** **延迟对象状态查看** 延迟对象是jquery的回调函数解决方案,延迟顾名思义就是延迟到未来某个点再执行,类似jquery动画函数中delay方法,区别在于后者是延迟固定时间,延迟对象则是通过固定状态判断延迟执行的时机。 ~~~ var dtd = $.Deferred(); // 新建一个deferred对象   var wait = function(dtd){     var tasks = function(){       alert("执行完毕!");       dtd.resolve(); // 改变deferred对象的执行状态     };     setTimeout(tasks,5000);     return dtd;   }; $.when(wait(dtd))   .done(function(){ alert("哈哈,成功了!"); })   .fail(function(){ alert("出错啦!"); }); ~~~ **上述代码执行流程图:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e451b9c5.jpg)**  上述代码例子,延迟对象定义为一个全局对象,这样就会造成延迟状态,可以在任何时间进行更改。 **改进的方法一:** ~~~ var dtd = $.Deferred(); // 新建一个Deferred对象   var wait = function(dtd){     var tasks = function(){       alert("执行完毕!");       dtd.resolve(); // 改变Deferred对象的执行状态     };     setTimeout(tasks,5000);     return dtd.promise(); // 返回promise对象,promise对象只开放与状态无关的方法,即那三种状态方法不开放,无法设置   };   var d = wait(dtd); // 新建一个d对象,改为对这个对象进行操作   $.when(d)   .done(function(){ alert("哈哈,成功了!"); })   .fail(function(){ alert("出错啦!"); });   d.resolve(); // 此时,这个语句是无效的 ~~~ **上述代码,在wait函数返回值是一个promise对象,由下图promise对象数据结构不难发现,promise对象拥有除了修改状态方法以外延迟对象的所有方法,也就是说,promise对象无法改变执行状态,这样就能够防止外界对状态的更改。** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e452c4d9.jpg)** **promise对象的数据结构** **改进方法二:** **将延迟对象构建成函数内部的局部变量,这样更好的实现了封装,防止外部对状态进行改变。** ~~~ var wait = function(dtd){     var dtd = $.Deferred(); //在函数内部,新建一个Deferred对象     var tasks = function(){       alert("执行完毕!");       dtd.resolve(); // 改变Deferred对象的执行状态     };     setTimeout(tasks,5000);     return dtd.promise(); // 返回promise对象   };   $.when(wait())   .done(function(){ alert("哈哈,成功了!"); })   .fail(function(){ alert("出错啦!"); }); ~~~ **改进方法三:** 直接将函数名作为$.Deferred()参数传入,$.Deferred()所生成的延迟对象会作为wait函数参数传入函数。 ~~~ var wait = function(dtd){     var tasks = function(){       alert("执行完毕!");       dtd.resolve(); // 改变Deferred对象的执行状态     };     setTimeout(tasks,5000);     return dtd.promise(); // 返回promise对象   };  $.Deferred(wait)   .done(function(){ alert("哈哈,成功了!"); })   .fail(function(){ alert("出错啦!"); }); ~~~ **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e453f18c.jpg)** **$.Deferred()方式执行状态** **三、项目使用情况:** **乐帝在内推项目中使用的是第二种方法,即将延迟对象作为局部变量定义,并返回promise对象。这里promise对象与延迟对象除了不能改变状态,对延迟状态的记录及监听方法都相同。** ~~~ function loadInternalHost() { var dtd = $.Deferred(); dtd.resolve(); return dtd.promise();//在原defferred对象上返回另一个deferred对象,两个对象都会记录最初deferred对象状态,但后者不能改变状态,其他方法一致 } ~~~ **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e454fb7b.jpg)** **promise对延迟状态的记录** **当分别调用两个接口时,延迟监听函数允许为多个事件指定一个回调。** **如下代码,分别取得内推城市及内推公告两部分数据后,执行后续加载数据的动作。** ~~~ $.when(getInternalRecommendCitys(), getComment()).done(function () { getInternalRecommendJobAdList(oldSearchData.keyWord, oldSearchData.locId); }); function getInternalRecommendCitys() { var dtd = $.Deferred(); $.ajax({ type: "get", data: { "openId": openId }, dataType: "json", url: "../api/InternalInfo/InternalRecommendCitys", success: function (data) { $city = $("#citySelect"); for (var i = 1; i < data.length; i++) { $str = $("<option></option>"); $str.text(data[i].name); $str.val(data[i].value); $city.append($str); dtd.resolve(); }//将可选城市导入到选择列表 }, error: function () { dtd.resolve(); } }); return dtd.promise(); } function getComment() { var dtd = $.Deferred(); $.ajax({ type: "get", data: { "openId": openId }, dataType: "json", url: "../api/InternalInfo/GetComment", success: function (data) { $(".notice-content pre").html(data.data); dtd.resolve(); }, error: function () { dtd.resolve(); } });//获取公告数据 return dtd.promise(); } ~~~
';

前端编程提高之旅(八)—-D3.js数据可视化data join解析

最后更新于:2022-04-01 09:37:05

**[D3.js](http://d3js.org/)作为一门轻型的可视化类库,非常便于将数据与web界面元素绑定,实现可视化。乐帝d3.js入门是大体看了一遍[《d3js数据可视化实战》](http://download.csdn.net/detail/loganyang123/6781653)这本书,D3操作非常类似于jquery的使用,具体体现在两点:** - **选择器模块都采用CSS3标准** - **方法可以链式调用** **有了jquery使用基础,相信再加上以上书籍的例子,调试很容易上手使用D3.js,乐帝目前认为D3.js与jquery区别在于:D3.js独有的数据结构概念及对SVG操作方便的实现。而深入理解D3原理,以上皮毛的理解就不够用了。** **通过阅读上述书籍乐帝将D3内容划了几大块来分开理解:** **DOM操作包括:** - **选择器模块(select、selectAll等方法)及如何实现链式调用。** - **节点模块(append、remove等方法实现)** - **样式模块(attr、style等方法实现)** **数据绑定相关方法:** - **data方法** - **enter、exit等方法** **比例尺:** - **值域与输出域的实现** **更新、过渡、动画方法:** - **transition方法及连带duration、ease、delay等方法** **事件:** - **即绑定事件** **而后通读[API](https://github.com/mbostock/d3/wiki/API-Reference)发现对于理解D3.js实现机理有一些关键概念,这里关键概念涉及selection、data join、group、transition等。而乐帝读API最大心得在于,在没有一个大体概念时,千万不要去触碰源码,还是按部就班读API吧,读不懂把文档翻译一遍就懂了,乐帝是这么做的。** **这篇文章乐帝主要想讨论[data join](http://download.csdn.net/detail/yingyiledi/8074405),但在讨论之前,还是需要补充下基本概念。** **D3.js独有关键概念是selection,乐帝将其翻译成元素集。它表示从当前文档获取的元素数组。它定义了一种数据结构,或者说是对原有dom文档数据结构的修改。有了元素集之后,就可以对元素执行常规操作了,诸如属性、样式、参数、文档内容等。** **selection存在的意义在于,是它将页面元素与数据实现绑定连接,连接的数据又可以产生enter和exit子元素集,因此能够反映数据的变化,用于添加或移除元素。** **D3支持方法链,操作方法返回值是元素集,从这一句话,我们知道不管如何具体实现链式操作,我们知道返回的是元素集就够了。** **乐帝最近思考,整个web世界甚至整个世界,由两种力量驱动:数据与人。而某些高深思想或许能把人也归结为数据。D3是这两种力量的典型,数据驱动展现,人驱动交互。脱离人与数据,D3的代码,只是一堆分离的函数而已。D3紧紧拥抱数据,这就是其最大特点。** **selection介绍完了,我们开始介绍data join概念。** **由上所述,D3是数据驱动,而元素集是沟通数据与元素的集合,而data join的概念又是代表数据与元素结合三种状态的概念,由此看来,data join在D3中属于核心概念。** **[《以data join概念思考》](http://bost.ocks.org/mike/join/)这篇文章给出了对data join比较简明易懂的陈述。** **在D3中任何时候,数据与页面元素都有三种关系:数据与元素绑定时,即一一对应,这样构成的selection状态叫update selection;没有元素与之对应的数据构成的selection叫enter selection;没有数据与之对应的元素构成的selection叫exit selection,它代表将要被移除的元素集。** **如下图:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e43c8327.jpg)** **从字面上理解,D3对数据的推崇可谓毫无节操,有数据没元素叫enter,有元素没数据叫exit,代表要被踢出去的元素。** **下面来看一段代码例子:** ~~~ svg.selectAll("circle") .data(data) .enter().append("circle") .attr("cx", function(d) { return d.x; }) .attr("cy", function(d) { return d.y; }) .attr("r", 2.5); ~~~ **首先来看第一句svg.selectAll("circle"),它返回空的元素集,因为SVG容器是空的。** **** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e43d6e4b.jpg)** **然后上述空元素集与数据结合,构成新的元素集,包含enter、update、exit三种状态。因为元素集是空的,所以update和exit元素集为空,而enter状态的元素集,则包括五个占位符元素。** ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e43e56d1.jpg) selection.enter后,返回enter元素集,此时为五个绑定数据的对象。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e43f1bb8.jpg) **最后append("circle")一步,使得enter元素集实现与元素一一对应,没错构造成了上述的update元素集状态。** **由上述例子不难看出,给页面对象添加子对象不用for循环,而是采取data join的概念,用意在于,在静态展现的基础上,对update及exit做微小改动,就可以使它实现动态展现。这就意味着你可以看实时数据,允许数据集合的交互行为及温和过渡效果展现。** **任何时候运行代码,都会重新计算data join,从而保证数据与元素预期的关系。** **data join允许我们队指定状态进行操作,比如,可是设置常数值在enter上,而不是update上,通过重新选择元素最小化改变原有dom,大大提升渲染效率。** **下面乐帝展示一个[对各个状态data join操作](http://bl.ocks.org/mbostock/5779690#index.html)的例子:** **起始HTML:** ~~~ <div>update</div> <div>exit</div> ~~~ **起始D3代码:** ~~~ var dataset =["enter", "hello"]; var key = function(d) { return d || this.textContent; } var duration = 750; var div = d3.select("body").selectAll("div") .data(dataset,key); ~~~ **此时data join 三个状态:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e440ec8a.jpg)** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e4424d05.jpg)** **如上图,不难分析得到,此时data join三状态:update元素集为空,enter元素集已经有数据绑定,exit元素集有两个div元素,在第二张图中innerHTML属性中,发现恰恰是初始化HTML的两个div元素,这里采用了键函数(key)方法,键函数被调用了四次,前两次调用的是已有的div数据调用,后两次则是enter状态元素集数据调用。** **接下来对exit元素集操作:** ~~~ // // 1. exit var exitTransition = d3.transition().duration(750).each(function() { div.exit() .style("background", "red") .transition() .style("opacity", 0) .remove();//移除节点 }); ~~~ ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e4435dfe.jpg) **不难得出结论,是原有两个div将要被移除,在页面及内存中清除。** **接下来对enter的操作,这里update为空,故第二步update操作并没有实际意义。** ~~~ // 2. update var updateTransition = exitTransition.transition().each(function() { div.transition() .style("background", "orange"); }); // 3. enter var enterTransition = updateTransition.transition().each(function() { div.enter().append("div") .text(function(d) { return d; }) .style("opacity", 0) .transition() .style("background", "green") .style("opacity", 1); }); ~~~ **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e448b9b6.jpg)** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e449b9bb.jpg)** **第三步对enter元素集的操作,使其与元素绑定,并设置成绿色。** **乐帝在调试期间还发现了另外的情况:** **一开始js代码是这样的:** ~~~ var dataset =["enter", "update"]; var key = function(d) { return d || this.textContent; } var duration = 750; var div = d3.select("body").selectAll("div") .data(dataset,key); ~~~ **即dataset有一个元素与已存在的div文本内容相同,又由于键函数是处理的是dataset与div文本内容的集合,经过运行如下:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e44a8f29.jpg)** **与之上比较不难发现,这里update元素集有一个元素,enter元素集有一个enter状态元素。另看exit元素集:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e44b8116.jpg)** **exit及enter都少了一个对应状态的对象,这里乐帝猜测d3应该是将每个键值函数返回数据进行了查重操作,将两个“update”文本数据合并成一个update状态元素了。** **执行1.exit代码,此时只有exit文本的div被移除。执行2.update代码时,update文本的div被设置为背景为橘黄色。最后执行3.enter代码时,背景色为绿色的div被加入到文档。**
';

前端编程提高之旅(七)—-marionette实现todoMVC

最后更新于:2022-04-01 09:37:03

[marionetteJS](http://www.ituring.com.cn/article/31580)是在backboneJS基础上进行更简洁的操作,平常开发主要用到几个涉及到view的概念:CollectionView、CompositeView、ItemView、LayoutView。这几个概念中,用的最广的当属ItemView。ItemView相对于backbone中view的概念方便之处在于:不用显式定义render方法,而是由ItemView本身完成将数据渲染到模板,并将视图追加到el,由此可见减少了很多流程化的操作。 同时marionetteJS还有很多事件和回调方法如:onBeforeRender、onRender、onBeforeDestroy、onDestroy、onShow等。这些回调函数,都在相应事前执行前或后,被调用执行,即帮助开发人员设定好了时序的通用方法,大大简化了业务逻辑实现。 当然本文并不是详细讲述[marionetteJS](http://marionettejs.com/docs/current)的原理,而是针对使用marionetteJS实现todoMVC功能([点击下载](http://download.csdn.net/detail/yingyiledi/8050447))做讲解。由于公司根据具体业务需要,在marionetteJS上又进行了一些封装实现,不过基本功能marionetteJS没有太大出入,故如看到代码中Italent无需惊慌,只需做到理念上的理解即可。 **首先让我们看一下,项目目录、view目录及templates目录:** ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e428727c.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e43a07b5.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e43b3083.jpg) **从以上三个目录来看,相对于上一篇文章采用[backbone实现todoMVC](http://blog.csdn.net/yingyiledi/article/details/39994145)似乎没有太大差别,那么差别在哪呢?** **上篇文章中提到每个view所提供的职能:** - **设置el或tagname,用于定义在上一层view放置的位置,或包裹的标签** - **设置对应模板(Template)** - **定义交互事件,并连带定义交互函数** - **初始化函数(initialize),一般设置对collection或者model的监听,用于view之间的通信** - **渲染函数(render),用于渲染数据到模板中,设置其他一些全局函数**  **在marionetteJS中,各种view所实现的职能与上述职能类似,不同之处在于上文讲到内建Render及各执行时序上的回调,大大简化了开发。除此之外各原子视图并没有太大的变动。** **这里着重讲解的是appView:** ~~~ Talent.Layout.extend({ //layout采用region model: new Talent.Model(), template: jst['about/app-page'], initialize: function() { this.collection = new Todos([{ title: 'ada' }, { title: 'oman' }, { title: 'come' }]); //初始化collection便于调试 this.listenTo(this.collection, "add", this.addOne); this.listenTo(this.collection, "all", this.reDraw); // 监听collection的变化,执行添加和重绘 }, //这里的重绘中,用于调整部分内容显示或隐藏的逻辑 regionsClass: { input: InputView, list: TodoView, toggleAll: ToggleAllView, status: StatusView }, // 这里采用的regionsclass简写类 regions: { // main: '.page-main-region' todoApp: '#todo-header', ToggleAll: '#todo-toggleAll', status: "#todo-footer" // todoList: '#todo-list' }, // regions部分简写元素部分 ui: { // item: '.ui-item' }, events: function() { var events = {}; // events['click ' + this.ui.item] = 'eventHandler'; return events; }, onRender: function() { }, onShow: function() { var self=this; this.todoApp.show(new this.regionsClass.input({ collection: this.collection })); this.ToggleAll.show(new this.regionsClass.toggleAll({ collection: this.collection })); this.addAll(); //已经渲染完再显示,用于显示所有初始化数据 this.addStatus(); if (this.collection.length) { $("#main").show(); }; //这里用于初始化显示collection数据 }, // onshow用于app模板显示完,使用的逻辑。 reDraw: function() { if(this.collection.length==0){ this.flag = true; } if((this.collection.length>=1)&&this.flag) { this.addStatus(); this.flag = false; }//通过设置一个flag属性,标记当collection从空到有值,再重新show statusview过程 if (this.collection.length) { //渲染时执行显示或隐藏的代码 $("#main").show(); //如果collection为空的话,则清空footer } else { $("#main").hide(); } }, //时刻监听collection变化,显示或隐藏部分region addStatus:function(){ var statusView = new this.regionsClass.status({ collection: this.collection, model: new Talent.Model({ done: this.collection.done().length, remaining: this.collection.remaining().length, length:this.collection.length }) }); this.status.show(statusView); },//添加状态栏视图 addOne: function(todo) { var listItem = new this.regionsClass.list({ model: todo }); listItem.render(); $("#todo-list").append(listItem.$el); //这里不断加入新的项并渲染加入到appview中 }, addAll: function() { var self = this; _.each(self.collection.models, function(item) { self.addOne(item); //对collection每个都进行添加到appview页面中显示 }) } }); }); ~~~ appView采用的是layout类,layout混合了ItemView及Region概念,非常适合管理多个子视图。appView下面有多个视图,采用layout非常合适。 **layout有几个概念:** ~~~ regionsClass: { input: InputView, list: TodoView, toggleAll: ToggleAllView, status: StatusView }, // 这里采用的regionsclass简写类 regions: { // main: '.page-main-region' todoApp: '#todo-header', ToggleAll: '#todo-toggleAll', status: "#todo-footer" // todoList: '#todo-list' }, // regions部分简写元素部分 ~~~ **regionsClass属性可以给引入的类设置别名,regions属性则是对所要插入的区域节点设置别名。** **当需要将子视图插入到appView视图指定区域时,执行如下代码:** ~~~ this.todoApp.show(new this.regionsClass.input({ collection: this.collection })); ~~~ 这里,show方法起到的作用,是将子视图渲染并将el放到regions定义的区域节点下,则子视图被展示到appView指定区域。 上面提到marionetteJS各类视图都内建了render,那么之前采用backboneJS监听collection或者model变化所执行的this.render就受到了限制,因为除了将数据塞入模板及放到el下,我们可能还需要其他的逻辑连同执行。 **乐帝这里采用了一个自定义方法,并做事件监听。** ~~~ reDraw: function() { if(this.collection.length==0){ this.flag = true; } if((this.collection.length>=1)&&this.flag) { this.addStatus(); this.flag = false; }//通过设置一个flag属性,标记当collection从空到有值,再重新show statusview过程 if (this.collection.length) { //渲染时执行显示或隐藏的代码 $("#main").show(); //如果collection为空的话,则清空footer } else { $("#main").hide(); } }, //时刻监听collection变化,显示或隐藏部分region ~~~ **事件监听:** ~~~ this.listenTo(this.collection, "all", this.reDraw); // 监听collection的变化,执行添加和重绘 ~~~ **为了使程序更加标准,这里采用了新型的隐藏或展示视图的方式,涉及到status子视图的隐藏和展示。按照Jquery式编程,通过选取指定dom元素,采取show及hide方法,即可解决。** **这里为了充分发挥数据驱动执行方式,在statusView视图中,当collection中model长度为零时,关闭视图:** ~~~ reDraw: function() { if(this.collection.length==0){ this.close(); } var length = this.collection.length; this.model.set({ done: this.collection.done().length, remaining: this.collection.remaining().length, length:this.collection.length }); this.render(); } ~~~ **此时,视图关闭后,整个视图在dom及内存中被移除,当collection中model再次非零时,需要重启视图,就需要在appView里,重新show一个statusView。** **这里定义了一个添加statusView方法:** ~~~ addStatus:function(){ var statusView = new this.regionsClass.status({ collection: this.collection, model: new Talent.Model({ done: this.collection.done().length, remaining: this.collection.remaining().length, length:this.collection.length }) }); this.status.show(statusView); },//添加状态栏视图 ~~~ **再将如上方法,在自定义监听collection变化的回调函数中引入即可:** ~~~ reDraw: function() { if(this.collection.length==0){ this.flag = true; } if((this.collection.length>=1)&&this.flag) { this.addStatus(); this.flag = false; }//通过设置一个flag属性,标记当collection从空到有值,再重新show statusview过程 if (this.collection.length) { //渲染时执行显示或隐藏的代码 $("#main").show(); //如果collection为空的话,则清空footer } else { $("#main").hide(); } }, //时刻监听collection变化,显示或隐藏部分region ~~~ **注意这里设置了一个标志属性flag,用于记录collection是从空到增加model,这种状态才重新开启statusView子视图。** **至此,涉及到有关此篇采用marionetteJS实现todoMVC的关键问题,都已经做了阐述。** **与backbone相比,更多的规则,带来的是更少的代码,采用任何技术都是在学习成本与高效开发上做权衡。** ****
';

前端编程提高之旅(六)—-backbone实现todoMVC

最后更新于:2022-04-01 09:37:00

**乐帝当年学习backbone时,最开始是看官网todoMVC的实现,后来了解到requireJS便于管理JS代码,就对官网代码做了requireJS管理。但此时乐帝感觉此时的todoMVC仍然不够简明,为了加深对MVC架构的理解,乐帝对原有appview代码进行了重构,将相关显示模块单独提取出自称view,实现view原子化。乐帝已经将这个项目上传([下载地址](http://download.csdn.net/detail/yingyiledi/8025387))。** **加入requireJS的目录结构:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e422badf.jpg)** **这里主要用到templates用于放置view对应的模板,views则对应backbone中view文件。如果说backbone是前端MVC,那么model是对数据建立模型,collection则是对model统一管理,view则起到控制器的作用,用于填充数据到模板,并渲染模板到显示。model、collection起到M作用,view起到C的作用,模板则起到V的作用。** **然后我们看一下todoMVC的效果图:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e4244e65.jpg)** **从最终效果图,我们可以分析出,要对原有appview中解耦出原子view,就需要判断出哪些是原子view,原子view需要具备两点:** - **具有动态交互效果** - **与其他页面部分独立** **当然这里的原子view定义还值得商榷,乐帝根据以上两个原则对view进行了重新划分。** **且看views目录结构:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e4257516.jpg)** **对应模板目录结构:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e42659f9.jpg)** **需要注意的是,这里appview并没有对应的模板,而是通过设置el: "#todoapp",在index.html文件中,统一对原子view进行管理。** **下面以ToggleAllView类源代码为例子,我们分析下,原子view职能的组成:** ~~~ define([ 'jquery', 'underscore', 'backbone', 'text!templates/toggleAll.html' ], function($, _, Backbone, toggleTemplate) { var ToggleAllView = Backbone.View.extend({ toggleTemplate: _.template(toggleTemplate), events: { "click #toggle-all": "toggleAllComplete" }, initialize: function() { this.listenTo(this.collection, "all", this.render); //除了todoview与todomodel一一对应 // 其他相关操作都会监听collection }, render: function() { this.$el.html(this.toggleTemplate()); var done = this.collection.done().length; var remaining = this.collection.remaining().length; this.allCheckbox = this.$("#toggle-all")[0]; this.allCheckbox.checked = !remaining; return this; }, toggleAllComplete: function() { var done = this.allCheckbox.checked; this.collection.each(function(todo) { todo.save({ done: done }); }); //这里通过判断单选框是否选中,修改所有modeldone属性 } }); return ToggleAllView; }); ~~~ **上述代码中职能主要有如下几种:** - **设置el或tagname,用于定义在上一层view放置的位置,或包裹的标签** - **设置对应模板(Template)** - **定义交互事件,并连带定义交互函数** - **初始化函数(initialize),一般设置对collection或者model的监听,用于view之间的通信** - **渲染函数(render),用于渲染数据到模板中,设置其他一些全局函数** **由此可见,原子view将职能划分的很清楚,这也是前端MVC架构的原因,而不是之前纯脚本时代,代码间高度耦合,牵一发而动全身。** **对于学习backbone,原子view和appview各自代码都不难理解,难于理解或者它精妙之处,在于对事件的监听机制,正是这种机制,处理了view之间的通信,从而将松散的view拼装成性能优良的整理。** **todoView的监听:** ~~~ initialize: function() { this.listenTo(this.model, "change", this.render); this.listenTo(this.model, "destroy", this.remove); //当模型被删除,视图相应被移除 } ~~~ **这里对每个todoview进行与之绑定的model数据监听,修改,则重新渲染;销毁,则移除此todoview。** **再看ToggleAllView的监听:** ~~~ initialize: function() { this.listenTo(this.collection, "all", this.render); //除了todoview与todomodel一一对应 // 其他相关操作都会监听collection } ~~~ **这个监听更“狠”,只要collection有变动,就会重新渲染,以达到实时交互的效果。** **那么appview是如何管理各个子view的呢?** **且看两个appview函数:** ~~~ initialize: function() { // 初始化加入各种视图,新建视图并添加到父视图指定位置 this.footer = this.$el.find('footer'); this.main = $('#main'); this.todoCollection = new todos; inputview = new InputView({ collection: this.todoCollection }); $("#todoapp").prepend(inputview.render().el); //加入输入框 var toggleAllview = new ToggleAllView({ collection: this.todoCollection }); this.main.prepend(toggleAllview.render().el); //取得数据后,再初始化 this.allCheckbox = this.$("#toggle-all")[0]; this.listenTo(this.todoCollection, "add", this.addOne); this.listenTo(this.todoCollection, "reset", this.addAll); this.listenTo(this.todoCollection, "all", this.render); // 需要数据的视图,在获取数据后定义 this.todoCollection.fetch(); // 状态视图 statusview = new StatusView({ collection: this.todoCollection }); this.footer.append(statusview.render().el); //取得数据后,再初始化 }, render: function() { // 由于设置了all监听所有collection的操作,故添加一个项就会被渲染一次,这保证了有改动都会得到渲染到页面 var done = this.todoCollection.done().length; var remaining = this.todoCollection.remaining().length; this.allCheckbox = this.$("#toggle-all")[0]; if (this.todoCollection.length) { //渲染时执行显示或隐藏的代码 this.main.show(); this.footer.show(); this.footer.html(); //如果collection为空的话,则清空footer } else { this.main.hide(); this.footer.hide(); } }, // 实现整体显示 ~~~ **与原子view的区别,在于appview初始化函数除了监听collection变化外,还初始化各个原子view,并添加到指定界面位置,同时渲染函数根据逻辑需要,渲染整个页面。** **以上是对整个todoMVC程序的整体性架构分析,具体交互细节可查看乐帝源代码。**
';

前端编程提高之旅(五)—-写给大家看的css书

最后更新于:2022-04-01 09:36:58

自实习也有几个月的时间了,以爱奇艺实习为敲门砖,进入了目前这家公司。如果说当初能进爱奇艺是临时突击DIV+CSS的话,目前在这家公司体验到,任何技术都必须悉知原理,这样才能做到庖丁解牛,做一个内行的人。css属性和用法都摆在那里,但如果用得好,除了躬身实践,提高理论积淀就特别必要了,这本《写给大家看的css书》快速扫过一遍之后,之前工作遇到过不清楚的问题,一扫而空,所以有必要总结下。 **css层叠的理解** css是层叠样式表的英文缩写,层叠意味着样式从文档结构中一个层次传递到另一个层次,浏览器来决定,某个标签应用来自哪个来源的样式属性。 **样式的来源有三种:默认浏览器样式表、用户样式表、设计者样式表。** **默认浏览器的样式表根据浏览器不同而不同,编写样式时,需要编写重置样式表,以便设计者样式表现统一。** **用户样式表则是用户在浏览器中进行的个性化设置**。 **设计者样式表则是设计者编写的,这其中又分为内联样式、嵌入样式、链接样式。内联样式和嵌入样式并没有做到样式与内容的分离,因为样式仍然包含在同一个文档中。链接样式是唯一一种能够真正将表现样式与结构化标记分离的方式。设计者这三种样式的优先级由高到低为:内联样式、嵌入样式、链接样式。** **有了以上样式来源,层叠的工作原理:首先,找到应用每个元素和属性的全部声明,按次序和重要性排序,次序即顺序,重要性则是属性的权重和各样式的优先级,并且先按重要性排序再按顺序排序。** **在文档层次中对准标签** **一个选择符可以再多条规则中使用。选择符以自右向左的标签顺序对应,上下文选择符中只要标签前面选择符在文档层次中位于它上方某处,并以相同顺序出现就有效。类和ID选择符的好处是无需考虑文档层次的样式。类和ID应该有节制的使用,常见的是添加给包含标记主要部分的块级元素,然后以响应的ID和类名开头的上下文选择符,访问该块级元素中的标签。** **子选择符、相邻同辈选择符、属性选择符、伪类、伪元素都能为快速定位提供帮助。** **规则声明** **em计算依据是一种字体中字符的宽度,ex等于给定字体中字母x高度。在PC开发中使用em指定字体大小的原因有二:可以充分利用继承性,其次,方便视力障碍者使用。但乐帝担心重构中,改变嵌套层级容易引发连带问题,到目前为止乐帝仍然采用像素单位。** **字体和文本样式** **css产生的意义在于实现了网页结构与样式的分离。说到字体的范畴中,类似分形学,依然有结构与样式分离的属性。字体属性主要涉及文字的大小和外观。文本属性则主要涉及对字体的处理,例如行高和字母间距。** **字体集合又分为衬线和无衬线两种,主要区别在于字符笔画末端。无衬线sans-serif,更适合web设计。** **字体属性font-variant将所有字母转换成小型大写字母。其他相关字体属性如font-weight、font-size、font-style font-family从字面上就可以看出这些均为字体属性。** **文本属性中需要理解一个概念,即css会把一个盒子放到位于元素中文本周围。这个盒子叫文本盒子。text-indent用于设置文本盒子相对于包含元素的相对位置。常用的首行缩进即此属性实现。并且text-indent属性有继承值,div中设置text-indent值,则所有段落都会继承这个text-indent值,并且段落继承缩进值是基于父元素计算之后的结果。** **letter-sapacing用于调整字母间的距离,word-spacing用于调整单词之间的距离。** **text-align设置水平方向上与包含元素对齐方式,因此必须在包含元素上设置此属性。** **line-height除了设定行距外,还有实现单行文本居中的技巧。方法是将行高设置为包含元素高度,来实现垂直居中效果。** **text-transform改变元素中国文本大小写形式。** **vertical-align可以相对于基线将文本向上方或下方移动。例如可以构造化学表达式或者方程式。** **盒模型** **定位元素分为三部分:盒模型描述标记中存在的每个元素的定位控制。position属性定义了这些元素在页面上的位置关系。display属性定义元素在页面上是堆叠还是并排及是否出现。** **盒模型中涉及三个概念:边框、外边距(margin)、内边距(padding)。** **边框有三个属性:宽度、样式、颜色。边框会增大元素的尺寸,因此边框会改变布局。** **盒子内边距(padding)指盒子内容与盒子边框之间的距离。内边距处于盒子内部,设置背景色延伸可以用内边距。** **盒子外边距(margin)设置盒子与相邻元素之间的距离。多数块级元素有默认外边距,因此需要初始化。** ~~~ *{margin:0;padding:0;} ~~~ 盒子外边距有折叠外边距的问题,即上下外边距相遇时,会相互折叠,直到一个元素外边距接触到另外一个元素,通俗讲即最后外边距效果是较大的外边距,而不是累加。 **盒子的大小,通过以上内边距与外边距的定义,我们知道内边距与边框属于盒子大小,而外边距不属于,通常情况下,设置元素的width是盒子内容的宽度,而不是盒子本身的宽度,如果再设置内边距和边框,盒子宽度会增加,这个需要特别注意。** **浮动和清除** **浮动的原理:只要设置了宽度的元素,而且页面有足够空间,那么相应元素就会像栏一样排列在一起。** **在很多布局情况下,我们不想任何元素都向上浮动元素旁边,也就是让它保持在浮动元素下方。即[清除浮动](http://bbs.csdn.net/topics/390342944?page=1)。** ~~~ .clearfix:atfer{ content:"."; display:block; visibility:hidden; height:0; clear:both; } .clearfix{display:inline-block} .clearfix{display:block} ~~~ **position属性** **如上所述,position属性定义了元素在页面中的位置关系。这里要了解一个关键概念:定位环境。定位环境就是当我们使用top、left、right、bottom时移动元素时,我们会参照另外一个元素移动它,而这个作为参照的元素就是定位环境。即相对于哪个元素移动。** **通常开发时,经常设置需要作为需定位元素祖先元素的position属性为relative,就可以使需要定位的元素修改定位环境。** **相对定位作为静态元素,所占据空间及其他元素位置都不变,即只是视觉上移动,并没有脱离文档流,文档中,仍然有此元素的位置。** **绝对定位与相对定位不同之处在于,它会把元素完全移出文档流。其他文档流的内容,会随着abosolute的设置而紧跟文档流移动。** **固定定位也是脱离文档流,只不过定位环境是视口,即浏览器显示网页的窗口。** **display属性** **display:none与visiblity:hidden区别在于,前者原先占据的空间都会被移除,而后者元素占据空间会得到保留,只是不显示。** **display:block和inline可以根据语义化需要,来针对性的修改默认元素的display属性。** **浮动布局** **对带有关键宽度的元素,好的布局方法是嵌套一层内部div。别为那些构成主分栏的div直接添加视觉样式,要添加就给他们内部div添加。这也是结构与样式分形学,进一步细分。** ~~~ #main_wrapper { width:840px; /* widths of columns will change proportionately as this width is changed */ margin-left:auto; /* centers layout in browser */ margin-right:auto; /* centers layout in browser */ text-align:left; /* resets the centering hack for IE6 on the body tag */ } #header { } #nav { width:22%; /* this width + content width must total 100% */ float:left; /* floats on nav and content divs make them sit side by side */ } #content { float:left; /* floats on nav and content divs make them sit side by side */ width:78%; /* this width + nav width must total 100% */ top:0px; } #footer { clear:both; /* makes the footer sit below whichever column is longest */ } ~~~ **overflow属性用于控制元素如何处理它们包含的内容,overflow:hidden规则能够使较窄分栏在上述情况下保持宽度不变。之前乐帝处理图片等比例放缩就用到此属性。** **两栏流动式布局** **两栏流动式布局,设置min-width和max-width属性,以使布局不会失控,同时给导航栏设置定宽,使内容栏动态改变宽度。** **三栏布局** **三栏固定宽度布局,设置浮动同时设置每栏所占百分比宽度大小即可。** **三栏流动式布局文档流及关键样式如下:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e4215716.jpg)** ~~~ #threecolwrap { float:left; width:100%; background:url(../../../chapter_5/code/images/3_col_faux_art_right.gif) repeat-y right; } #twocolwrap { float:left; width:100%; display:inline; /* stops IE doubling margin on float*/ <span style="color:#ff0000;">margin-right:-170px; /* CWS - neg margin move */</span> background: url(../../../chapter_5/code/images/3_col_faux_art_left.gif) repeat-y left; } #nav { float:left; <span style="color:#ff0000;">width:150px;</span> display:inline; /* stops IE doubling margin on float*/ } #content { width:auto; <span style="color:#ff0000;">margin-left:150px; margin-right:170px;</span> } #promo { float:left; <span style="color:#ff0000;">width:170px;</span> } ~~~ **通过设置#nav和#promo定宽,并设置content外边距,实现流动三列布局。** **设计界面组件** **表格:表格涉及标签语义化,添加样式的目标是尽量添加最少的视觉因素,以保证用户对数据的理解。table标签使用border-collapse可以将默认的两个单元格双边框减少到单边框。** **表单:表单需要注意的是并列排列的表单控件,从语义关系上,可以使用无序列表来实现对齐等操作。** **列表:列表也会根据浏览器不同而不同,弥补差异的方法是,将列表的外边距和内边距都重设置为0,然后重新添加样式。** **行内元素应用内边距和边框时,不会影响包含元素,解决方法是设置display属性。**
';

前端编程提高之旅(四)—-backbone初体验

最后更新于:2022-04-01 09:36:56

来到新的实习单位,公司的业务模式、组织架构、开发技术、开发规范对于乐帝这样的职场菜鸟来说都是新的,这一周入职的这几天主要在熟悉公司采用的前端架构,前端架构基本包括:backbone、marionette、requirejs。由于乐帝在爱奇艺实习期间,自学了PHP技术,对smarty模板及thinkphp框架有一个了解,接触前端MVC还是有点基础的。 公司有一套专业的[前端培训流程清单](https://www.zybuluo.com/xzkcz/note/7353)。这周主要的工作包括:阅读[backbone简要文档](https://github.com/the5fire/backbonejs-learning-note/tree/master/chapters)、读[backbone的todoMVC例子](https://github.com/the5fire/backbonejs-learning-note/blob/master/chapters/06-backbonejs-todos-1.rst)、通读[backbone官方文档](http://backbonejs.org/)、再读backbone的todoMVC例子、阅读[marionette简要介绍](http://www.smashingmagazine.com/2013/02/11/introduction-backbone-marionette/)、按照todoMVC源码,手写了一遍todoMVC代码、阅读[backbone总结文章](http://blog.csdn.net/raptor/article/details/8578308),总算大体弄清楚了backbone的来龙去脉。 回头来看,对于提升学习效率最高的方法是:阅读简单概念的介绍并配合例子、看一个比较综合的例子对简单概念做吸收、阅读官方文档所有API、敲一遍综合例子做吸收。这样的学习顺序遵循了认识、实践的原则。乐帝基本上按照这样的顺序学习了backbone。下面代码段的注释是乐帝从整体对model、collection、view的理解。 ~~~ $(function(){ var Todo = Backbone.Model.extend({ default:function(){ return { title:"hello ledi", order: Todos.nextOrder(), done:false }; }, toggle:function(){ return this.save(done,!this.get("done")); } });//构造基本的数据模型及对数据的处理 TodoList = Backbone.Collection.extend({ model:Todo, localStorage: new Backbone.LocalStorage("todos-backbone"), done:function(){ return this.where({done:true}); }, remaining:function(){ return this.where({done:false}); }, nextOrder:function(){ if(!this.length) { return 1; } else{ return this.last().get('order')+1; } }, comparator:'order' });//作为数据模型的集合,对模型进行增删查找、排序等管理 var Todos =new TodoList; var TodoView = Backbone.View.extend({ tagName:"li", template: _.template($('#item-template').html()), events: { "click .toggle" : "toggleDone", "dblclick .view" : "edit", "click a.destroy" : "clear", "keypress .edit" : "updateOnEnter", "blur .edit" : "close" },//事件定义,控制交互部分 initialize:function(){ this.listenTo(this.model,'change',this.render); this.listenTo(this.model,'destroy',this.remove); },// 通过监听与model数据实现绑定 render:function(){ this.$el.html(this.template(this.model.toJSON())); this.$el.toggleClass('done', this.model.get('done'));//切换是否有done类,调试查看此类 this.input = this.$('.edit'); return this; },//初始化渲染模板内容,作为显示部分 toggleDone:function(){ this.model.toggle(); }, edit:function(){ this.$el.addClass(".editing"); this.input.focus(); }, close:function(){ var value = this.input.val(); if(!value) { this.clear(); } else{ this.model.save({title:value}); this.$el.removeClass(".editing"); } }, updateOnEnter:function(e){ if(e.keyCode == 13) { this.close(); } }, clear:function(){ this.model.destroy(); } });//todoview与model一一对应,对model数据进行显示及交互操作 var appView = Backbone.View.extend({ el: $("#todoapp"), statsTemplate: _.template($('#stats-template').html()),// 统计模板 events: { "keypress #new-todo": "createOnEnter", "click #clear-completed": "clearCompleted", "click #toggle-all": "toggleAllComplete" },// 监听事件 initialize:function(){ this.input = this.$("#new-todo"); this.allCheckbox = this.$("#toggle-all")[0]; this.listenTo(Todos, 'add', this.addOne); this.listenTo(Todos, 'reset', this.addAll); this.listenTo(Todos, 'all', this.render); this.footer = this.$('footer'); this.main = $('#main'); Todos.fetch(); },//监听collection的变化,实现与collection的绑定 render:function(){ var done = Todos.done().length; var remaining = Todos.remaining().length; if(!Todo.length){ this.main.show(); this.footer.show(); this.footer.html(this.statsTemplate({done: done, remaining: remaining})); } else{ this.main.hide(); this.footer.hide(); } this.allCheckbox.checked = !remaining; },// 实现整体显示 addOne:function(todo){ view = new TodoView({model:todo}); this.$("#todo-list").append(view.render().el); }, addAll:function(){ Todos.each(this.addOne,this);//这里用到collection和model沟通了 }, createOnEnter:function(e){ if(e.keyCode!=13) return; if(!this.input.val()) return; Todos.create({title:this.input.val()}); this.input.val(''); }, clearCompleted:function(){ _.invoke(Todos.done,'destroy'); return false; }, toggleAllComplete:function(){ var done = this.allCheckbox.checked; Todos.each(function(todo){todo.save(done,'done');}); } });//作为全局的view,控制多个todoview显示交互及其他显示模块的交互部分 var app = new appView; })//view基本模式:通过初始化及渲染函数进行最初显示,并通过定义事件列表,编写对应执行函数,实现交互 ~~~ **以上例子是乐帝研究的todo例子,并动手敲了一遍,对各个模块功能的理解。model提供数据模型及对数据处理;collection则是model的集合,主要以model为处理对象,对其增删查找、排序等各种操作;view则是传统后端MVC架构中的起Controller功能,主要负责控制显示、交互逻辑。** **backbone简要文档对view的论述并没有后来读的文章写得清楚,这里乐帝给出介绍view及模板的典型例子,用于理解view作为MVC核心地位的职能;** ~~~ <!-- Templates --> <script type="text/template" id="hello-template"> <div> <h3 id="hello"><%= message %></h3> <button id="toggle">Toggle</button> </div> </script> <!-- underscore自带模板引擎,通过<%= var %>接收传入的变量 --> <script type="text/javascript"> var TestView = Backbone.View.extend({ events: { 'click button#toggle' : 'toggle' }, initialize: function() { this.template = _.template($("#hello-template").html()); // 设定模板 this.render(); }, render: function() { this.$el.html(this.template({message: "hello world!"})); // 渲染模板 return this; }, toggle: function() { this.$("#hello").toggle(); return this; } }); $(function () { var v = new TestView({el: $('#hello')}); }); //这个例子中,给出了设置模板、传入参数、渲染页面、添加交互事件的方法,对于理解view功能:显示页面、添加交互,非常有帮助。 //http://blog.csdn.net/raptor/article/details/8566017 </script> ~~~ **这段代码给出的显示和交互非常简单,麻雀虽小、五脏俱全。能非常清楚的看到view用于显示页面和添加交互所要做的事情。接下来则是model和collection的例子:** ~~~ var Foo = Backbone.Model.extend({}); // 或是初始化默认数据 var Foo = Backbone.Model.extend({ defaults: { name: "hello", value: "233" } }); // 或是运行时赋值。 // 比如这样: var foo = new Foo({name:"world", value:"874"}); // 或这样: var foo = new Foo; foo.set({name:"world", value:"874"}); var FooList = Backbone.Collection.extend({ model: Foo }); var foos = new FooList; foos.add([{name:"hello", value:"233"},{name:"world", value:"874"}]); $(function () { $("#hello").text(JSON.stringify(foos.at(0))); ; }); ~~~  **这段代码给出了model及collection基本定义和使用,除此之外还会涉及增删查找、有效性验证、事件绑定等功能。由于都会涉及到后端的交互,这里乐帝涉猎不多,详情可[戳我](http://blog.csdn.net/raptor/article/details/8578308)。** **至于router则是[定义路由规则](http://blog.csdn.net/raptor/article/details/8630167),相对简单。乐帝下一步则是需要在这个基础上使用requirejs组织todoMVC代码以及阅读[marionette](http://www.smashingmagazine.com/2013/02/11/introduction-backbone-marionette/)使用其写一个todoMVC例子,并最终在talentjs上写MVC例子,真正的上手项目。**
';

前端编程提高之旅(三)—-浏览器兼容之IE6

最后更新于:2022-04-01 09:36:53

在爱奇艺实习期间,乐帝主要负责移动端活动页面的制作,由于移动浏览器是随着智能手机兴起的,这就决定了移动端不会重蹈浏览器兼容问题的覆辙,一开始就比较好的支持web标准,而纵观整个互联网行业,移动web开发还处于起步阶段,在很长一段时间pc端web作为主要的服务形式还会继续。从小来说作为一名前端开发人员,不了解浏览器兼容也会贻笑大方。这篇文章基于[《IE7web标准之道》](http://www.cnblogs.com/JustinYoung/category/126369.html),对浏览器兼容做一个概览。 时间到了2014年,大概很少人会想到IE7发布之后,时隔这么多年IE6仍然占据市场7%的浏览器份额。如果开发面向大众的产品,7%的份额仍然不可忽视。百度提供了[一个统计界面](http://tongji.baidu.com/data/browser)可查看浏览器份额,IE8和chrome占据了主要市场,在未来的一段时期内IE6仍然“发挥余热”。那么来看看IE6兼容问题。 **1.没有那么多选择器** ~~~ <style type="text/css"> a[target="_blank"]{ padding-right:16px; background:url('http://images.cnblogs.com/cnblogs_com/justinyoung/common/outLink.gif') no-repeat right; } </style> </head> <body> <p> <a href="#" title="我不会跳转到其他网站,不会再新窗口打开">我不会跳转到其他网站,不会再新窗口打开</a> </p> <p> <a href="http://www.163.com" title="我会跳转到其他的网站,会在新窗口打开" target="_blank">我会跳转到其他的网站,会在新窗口打开</a> </p> </body> ~~~ ** 这段代码给出了链接是否会跳转走的提示样式,像这样的选择器,如乐帝入行不久的人,用的都比较自然,读史使人明智,不读浏览器的进化史,不知IE6有多原始,IE6对比较复杂的css选择器都不支持,所以开发面向IE6的代码时,css选择器“简单粗暴”是比较好的方式。** ** ~~~ <style type="text/css"> #txtName{ border:1px solid #eee; } #txtName:hover{ border:1px solid black; } </style> </head> <body> <input type="text" id="txtName" /> </body> ~~~ **IE6是不支持任意元素实现伪类的。这一点比较现代的css伪类选择器的确使网页效果更绚丽。至于还有哪些不能用请参考([此页面](http://www.cnblogs.com/JustinYoung/archive/2008/02/20/IE7_wsRoad_selector.html))。** **2.IE6到现代浏览器引起布局混乱的原因** **w3c只是说,超出容器的内部不会被剪切。但是它并没有说,超出来的内容可以“撑开”容器。所以下面这个例子中IE7和FireFox的解释和渲染是正确的,而IE6则是错误的(因为它错误的认为,只有让容器内的内容“撑开”容器,才能让容器内的内容在超出时不被剪切)。** ~~~ <style type="text/css"> #div1{ border:1px solid red; width:50px; /*word-wrap: break-word; */ /*不允许内容撑开父容器,否则会出现IE6与其他浏览器布局显示不一致,造成混乱的问题,overflow: hidden;会影响内容正常显示*/ } </style> </head> <body> <div id="div1"> alonglonglonglonglonglonglonglonglongword from <a href="http://justinyoung.cnblogs.com/" title="">http://justinyoung.cnblogs.com/</a> </div> ~~~ **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e409be1a.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e40b0b3d.jpg)** **上图是用IEtest模拟IE6情况下,上述代码显示情况,下图则是现代浏览器的显示情况。不难看出,IE6对内容不会被剪切的理解的确出现了问题。** **出现bug的条件:** 1. 无论是“宽度”的内容过长,还是“高度”的内容过长,都会引发此bug。 1. 无论是文字、图片,还是任意有宽度和高度概念的“可见元素”,它们的“过宽”和“过高”都会引发此bug。 1. 任意有宽度和高度概念的“可见元素”,它们在默认状态下的“overflow”样式的值都是“Visible”(即使你没有设置这个样式)  从条件不难看出,图片或者文字内容超出父级div原有设置大小,都会触发bug。 **解决方案:** - **设置外层div的css属性“word-wrap: break-word”解决。** - **用“overflow: hidden”解决。** - **就是根据宽度,将文本截取成多段,在每段后面强制加上换行符。** **以上三种方法都有[各自劣势](http://www.cnblogs.com/JustinYoung/archive/2008/02/25/IE7_wsRoad_overflow.html),第一种会造成二次伤害:浏览器显示进一步不一致。第二种方案影响对内容的查看。第三种方案添加了交互。低三种方案函数如下:** ~~~ <script type="text/javascript"> // <![CDATA[ if(document.getElementById && !document.all) wordWarp4ff(6)/*数值6根据宽度需要发生变化*/ function wordWarp4ff(intLen){ var obj=document.getElementById("div1"); var strContent=obj.innerHTML; var strTemp=""; while(strContent.length>intLen){ strTemp+=strContent.substr(0,intLen)+" "; //每六个字符加一个空格分隔,添加到临时的字符串中 strContent=strContent.substr(intLen,strContent.length); } strTemp+=" "+strContent; obj.innerHTML=strTemp; } // ]]> </script> ~~~ [ 纵向伸展的bug](http://www.cnblogs.com/JustinYoung/archive/2008/02/25/IE7_wsRoad_overflow.html) **纵向伸展bug解决方案:只要我们让IE7和FireFox,也能像IE6中那样根据内容,自适应高度即可。如何才能让容器在IE7和FireFox中能够自适应高度呢?其实很简单,也是IE7的重要改进之一,使用“min-height”样式。虽然IE7中已经支持“min-height/min-width”和"max-height/max-width"样式。但是IE6却不认识这些"min-"、"max-"开头的样式,所以,我们还需要使用一个css hack为IE6设置一个“height”,只让IE6认识,IE7和FireFox都不认识。** ~~~ <style> * { margin: 0; padding: 0; } #header { width: 600px; /*height:50px;注释掉下面两句,放出这一句,在FireFox和IE7中便能呈现bug*/ min-height:50px;/*只设置最小高度,让IE7和FireFox自适应高度*/ _height: 50px;/*采用只有IE6才认识到css hack,让不认识min-height的IE6也有很好的兼容性。*/ background-color: red; margin:0 auto;/*居中显示*/ } #body{ width:600px; margin:0 auto;/*居中显示*/ background-color:blue; } #footer{ width:600px; margin:0 auto; background-color:#666; clear:both;/*clear:both,让footer在新的一行显示,很多朋友对clear理解的不够透彻,我以后会特意出篇文章介绍这个样式,有兴趣的朋友可以关注我的博客http://justinyoung.cnblogs.com*/ } </style> </head> <body> <div id="header"> 这里是头部的内容。<br/> 可能有网站标题,就像<a target="_blank" href="" title="">博客园</a>博客的标题、副标题。<br/> 也可能有导航栏在这里<br/> <strong>注意这句话在IE7中的显示1</strong><br/> <strong>注意这句话在IE7中的显示2</strong><br/> </div> <div id="body"> 这里是主体的内容,随便你写啦。我就写上我的博客地址吧——<a target="_blank" href="http://justinyoung.cnblogs.com/" title="IE7的web标准之道">YES!B/S!</a> <p> 专注于B/S模式的项目。姓名:杨正祎(Justin Young),程序员,专注于B/S模式的项目开发,擅长于Web标准页面设计。</p> <p>欢迎你们来为我的博客做客哦,里面有很多关于web标准方面的文章哦。请你们多多指教。</p> <p>最后还要非常华丽的署名——杨正祎</p> <p>日期当然也不能少啦——2008-2-21</p> </div><!--end: body --> <div id="footer"> 这里是footer,就放一些版权信息吧。&copy;<a target="_blank" href="http://justinyoung.cnblogs.com/" title="IE7的web标准之道">YES!B/S!</a> </div><!--end: footer --> </body> ~~~ **3.IE6重复文字bug**   ~~~ <!DOCTYPE html public "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="Keywords" content="YES!B/S!,web标准,杨正祎,博客园,实例代码" /> <meta name="Description" content="这是一个简单YES!B/S!文章示例页面,来自杨正祎的博客,http://justinyoung.cnblogs.com/" /> <title>YES!B/S!文章示例页面</title> </head> <body> <div style="width:200px;"> <div style="float:left;"></div> <!-- 如果是IE6,你将多看到一个“影”字 --> <div style="float:left;width:200px;">歌剧院的魅影</div> </div> </body> </html> ~~~ ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e41096e3.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e4118b6b.jpg) **上图是上述代码,在IEtest里IE6情况下显示情况,下面是现代浏览器展示情况,不难看出多出来一个字,即重复字bug。** **出现bug条件:** - 一个容器包含2两个具有“float”样式的子容器。 - 第二个容器的宽度大于父容器的宽度,或者父容器宽度减去第二个容器宽度的值小于3。(说到3,这里稍微多说一句——IE7还修正了IE6中的一个bug,bug名字就叫做“3像素bug”) - 在第二个容器前存在注释(这也是为什么此bug也叫做“IE6注释bug”的原因)。 乐帝查看了注释bug在ietest不能复现这样的问题,有待考证。 **解决方案:** - 改变结构,不出现【一个容器包含2两个具有“float”样式的子容器】的结构。 - 减小第二个容器的宽度,使父容器宽度减去第二个容器宽度的值大于3,例如将本文示例中第二个子容器的宽度改为197px。 - 去掉所有的注释。 ——此解决方案的评论:最直接的做法,但是“没有注释的代码”,的确不是一个好的代码写作习惯。 - 修正注释的写法。将 <!-- 这里是注释内容 -->写成<!--[if !IE]>这里是注释内容<![endif]--> ——此解决方案的评论:还不错的解决方案,但是并不是每个人都对<!--[if !IE]>这里是注释内容[endif]-->这种注释写法很欣赏。 - 在第二个容器后面加一个或者多个<div style="clear"></div>来解决。 ——此解决方案的评论:另人感觉很不爽的解决方案。但是的确能解决。影响网页效率 以上解决方案中,都是针对bug出现的条件针对性的修改,个人认为最后一条解决方案较好。 **4.IE6 div元素一直被select元素覆盖** **文章中引用了[另外一篇作者文章](http://www.cnblogs.com/JustinYoung/archive/2007/07/18/821868.html),这里面给出了出现这种情况的案例。** ~~~ <style type="text/css"> #divUp{ z-index:99; position:absolute; background-color:red; width:100; height:18; overflow:hidden; height:60px; } #ddlTest{ width:200; z-index:1; } </style> <body> <div id="divUp">aaaaaaa<br>bbbbbbb<br>ccccccc</div> <br/> <select id="ddlTest"><option>test0<option>test1<option>test2<option>test3</select> </html> ~~~ ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e412c447.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e41753b9.jpg) **上图分别是在IE6情况下和现代浏览器情况下,select与div不同上下层情况。可以看出IE6情况下,select元素不会随着z-index而隐藏到div下面。** **解决方案:** 在下拉列表上方加一个iframe,然后让div层浮在iframe上方,同时设置z-index值(div最大),这样,就能使div“盖住”下拉列表。 ~~~ <style type="text/css"> body{ font-size:small; } #zindexDiv{ position:absolute; z-index:50; width:expression(this.nextSibling.offsetWidth); height:expression(this.nextSibling.offsetHeight); top:expression(this.nextSibling.offsetTop); left:expression(this.nextSibling.offsetLeft); /*background-color:green;在ff中将这句话放出来,你就会明白京叭、狼狗、主人的比喻*/ } #divUp{ z-index:99; position:absolute; background-color:red; width:100; height:18; overflow:hidden; height:60px; } #ddlTest{ width:200; z-index:1; } </style> <body> <iframe id="zindexDiv" frameborder="0"></iframe> <div id="divUp">aaaaaaa<br>bbbbbbb<br>ccccccc</div> <br/> <select id="ddlTest"><option>test0<option>test1<option>test2<option>test3</select> </html> ~~~ 5.置换元素与行距的bug **文章首先给出了[置换元素的定义](http://www.cnblogs.com/JustinYoung/archive/2008/03/20/line-height-bug.html)。然后请看示例代码:** ~~~ <style type="text/css"> #lineheight_bug { line-height: 39px; font-size:14px; background:url('http://images.cnblogs.com/cnblogs_com/justinyoung/2008_1q/rule.gif') no-repeat; padding:0; padding-left:20px; height:435px; width:530px; border:1px solid red; } </style> </head> <body> <div id="lineheight_bug"> <p>这是一个用来演示line-height bug的实例。它来自《IE7的web标准之道——6:(修正)置换元素与行距bug》一文。而这篇文章是属于《IE7的web标准之道》系列文章的。《IE7的web标准之道》系列文章是个文章系列,主要讲解了IE7相对于IE6各个方面的修正和改进。 <img src="http://images.cnblogs.com/cnblogs_com/justinyoung/common/wedgits_red.gif" alt="这就是置换元素的一种" /> 对于网页设计者从将网页设计从IE6平稳的过渡到IE7平台有一定的指导意义。现在《IE7的web标准之道》系列文章已经出道第六篇了。前面五篇的标题分别是:《IE7的web标准之道——1:前言(兼目录)》 ,《IE7的web标准之道——2:(改进)更丰富的CSS选择符》 ,《IE7的web标准之道——3:(修正)引起页面布局混乱的祸首 》 , 《IE7的web标准之道——4:(修正)歌剧院魅影bug 》 以及《IE7的web标准之道——5:(修正)上去了!终于上去了! 》。如果你有兴趣,可以访问http://justinyoung.cnblogs.com/。谢谢。</p> </div> </body> ~~~ **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e41c9ee9.jpg)** 如上图所见,文字中因为多了一个图标,使得下面所有的文字都发生了向上的位移,造成严重的后果。 引起bug的原因: 之所以出现了这种bug,是因为IE6错误的将带有置换元素的那行文字的上下半间距,和相邻的上下两行的下上半间距合并到了一起。于是,带有置换元素的那行文字的上下行距就被减少了一半,所以页面出现了混乱。 解决方案: **对那些置换元素设置margin-top和margin-bottom。以便把被“压缩”的行间距“撑开”。** ~~~ #lineheight_bug img{ _margin:17px 0; _vertical-align: middle; } ~~~ **6.float双倍margin bug** 这个bug引发的条件极其简单,只要对块状容器元素设置了float和与float相同方向的margin值就会出现。 ~~~ <!DOCTYPE html public "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="Keywords" content="YES!B/S!,web标准,杨正祎,博客园,示例代码" /> <meta name="Description" content="这是一个简单YES!B/S!文章示例页面,来自杨正祎的博客,http://justinyoung.cnblogs.com/" /> <title>YES!B/S!文章示例页面</title> <style type="text/css"> body{ background:url('http://images.cnblogs.com/cnblogs_com/justinyoung/myPic/rule.gif') no-repeat; margin:0;padding:0; } .floatbox { float: left; width: 100px; height: 100px; background-color:deeppink; margin-top: 20px; margin-left:100px; } </style> </head> <body> <div class="floatbox"> </div> </body> </html> ~~~ ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e41e2626.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-24_56cd5e41f0c2a.jpg) **从上面网页中的网格刻度线不难看出IE6双边距bug问题。** **解决方案:** **1.只要对产生bug的容器设置一个“display:inline;”样式就可以了。作者详细分析使用[这种方法不会引起额外的问题](http://www.cnblogs.com/JustinYoung/archive/2008/03/27/double-margin-float-bug.html)。** ~~~ .floatbox { float: left; width: 100px; height: 100px; background-color:deeppink; margin-top: 20px; margin-left:100px; display:inline; /*多设置这个样式即可消除bug!*/ } ~~~ 2.**通过CSS hack修正bug** **即用只针对IE6的hack写法,多写一遍margin-left** ~~~ .floatbox { float: left; width: 100px; height: 100px; background-color:deeppink; margin-top: 20px; margin-left:100px; _margin-left:50px;/*只对IE6其作用的CSS hack,对数值减半*/ } ~~~ **7.[内容隐藏的文章](http://www.cnblogs.com/JustinYoung/archive/2008/04/01/peekaboo-bug.html),乐帝的IEtest没有复现,这里不予讨论。** **8.边线的混乱问题** ~~~ <!DOCTYPE html public "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="Keywords" content="YES!B/S!,web标准,杨正祎,博客园,实例代码" /> <meta name="Description" content="这是一个简单YES!B/S!文章示例页面,来自杨正祎的博客,http://justinyoung.cnblogs.com/" /> <title>YES!B/S!文章示例页面</title> <style type="text/css"> .divOuter {/*其实divOuter不是必须的,这里纯粹是为了提升视觉冲击力,你完全可以将其去掉*/ border:5px dashed green; padding:10px; } .divInner { border:3px solid red; padding:10px; } .testDiv1 { border:1px dotted deeppink; margin-top:10px; } .testDiv2 { border:1px solid blue; margin-top:-1px;/*这个是重点*/ } </style> </head> <body> <div class="divOuter"> <div class="divInner"> <div class="testDiv1">我是divInner里的第1个Div区域。我的margin-top是正数。</div> <div class="testDiv2">我是divInner里的第2个Div区域,我的margin-top是负值。</div> </div> </div> </body> </html> ~~~ **出现bug条件:** - **两个块状元素(#testDiv1和#testDiv2)** - **第二个块状元素(#testDiv2)有一个负值的margin-top** - **然后把这两个块状元素放在一个大的块状元素中(#divInner)** - **当然,为了他们都有可见的边框线(否则乱了你也看不到)** **解决方案:** 1. **对#divInner设置一个position:relative;** 1. **对#testDiv2也设置一个position:relative;** 1. **对#testDiv2设置一个负值的top,例如top:-1px;** **这里的解决方案总而言之就是采用相对定位,而不是margin-top实现定位,绕过问题也是解决办法。**
';

前端编程提高之旅(二)—-网站常见特效的jquery实现

最后更新于:2022-04-01 09:36:51

最好的编程语言是逻辑,前端各种交互特效的实现多可以用jquery实现,特效可以纷飞,内在逻辑则基本不变。这一篇文章主要介绍jquery实现常见特效背后的逻辑。 **1.通过类名获取元素集合** **首先来看一个js原生代码不支持的方法,通过类来获取元素集合。** ~~~ document.getElementsByClassName=function(classname){ var retnode = []; var myclass = new RegExp('\\b'+classname+'\\b');//匹配类名 var elem = this.getElementsByTagName('*');//得到所有元素 for (var j = 0; j < elem.length; j++) { var classes = elem[j].className; if (myclass.test(classes)){ retnode.push(elem[j]); } } return retnode; }//通过遍历整个文档元素类名,返回所有指定类名的数组 ~~~ **逻辑思路:** **通过构造一个类名的正则表达式,选取所有标签,并通过正则表达式与所有标签类名属性进行匹配,从而返回一个类名数组,实现通过类名得到元素集合的目标。** **实现这个方法的同时,也给在不使用jquery的情况下,选取特定序数的元素提供了便利。** **2.二级联动菜单的构造** ~~~ $("#select1").change( function() { //侦测一级菜单的change事件 var id = $("#select1").val(); if(id == 1){ //通过id判断二级菜单 $.get('index.php', null, function(data){ //get方式传值 $("span").empty(); //清空标签 $("span").append("<select><option>济南</opton><option>青岛 </opton><option>临沂</opton></select>"); //填充对应的二级菜单 }); }else{ $.get('index.php', null, function(data){ //get方式传值 $("span").empty(); //清空标签 $("span").append("<select><option>郑州</opton><option>安阳 </opton><option>洛阳</opton></select>"); //填充对应的二级菜单 }); } }); ~~~ **逻辑思路:** **根据需要设置联动菜单的元素值被改变的情况,通过ajax方法传空值给自身页面,追加相应的元素,在这里是二级菜单。这里用到了jquery两个方法:change方法和empty方法。** **change方法在选定标签值改变时,会被触发执行。empty方法则是将标签中html内容清空。** **3.链接样式和链接内容的显示隐藏** ~~~ $(function(){ $("li").each(function(index){ //遍历用户控制区域 $(this).mouseover(function(){ //获取当前用户选择区域 id = setTimeout(function(){ //使用时间事件函数实现缓动效果 更好的用户体验 $("li.tab").removeClass('tab'); //移除默认的选项按钮的样式 $(this).addClass("tab"); //给当前选项按钮添加样式 $("div.show").removeClass('show'); //移除默认的显示区域的样式 $("#tab1 div:eq("+index+")").addClass('show'); //根据选中的index索引添加样式 },300); }).mouseout(function(){ clearTimeout(id); //用户鼠标移除时同时移除时间事件 }); }); });//链接对应相应内容的隐藏和显示 ~~~ **逻辑思路:** **这里首先用到$()在其中书写函数,[这里](http://www.jb51.net/article/21660.htm)详细介绍了jquery构造函数的适用范围。通过遍历每个导航链接,当鼠标移上对应元素,首先移除所有默认选项样式,在悬浮元素加上默认选中样式。并移除当前显示样式,设置默认选项对应内容样式为显示状态。** **这里的亮点在于使用了一个显示默认样式的缓动效果,用户体验更细致。** **4.“显示更多(more)”功能的实现** ~~~ $(function(){ var $hideObj = $('ul li:gt(5):not(:last)');//选择index大于等于5的li元素,除去最后一个,隐藏掉 $hideObj.hide(); $('.showmore span').click(function(){ if(!$hideObj.is(":visible")){//判断是否可见is方法 $hideObj.show(); $('.showmore span').css("background","url(./images/up.bmp) no-repeat 0 -10");//切换状态更换图片 }else{ $hideObj.hide(); $('.showmore span').css("background","url(./images/down.bmp) no-repeat 0 0") ;//切换状态更换图片 } }); });//选择一部分隐藏掉,再将部分判断是否显示,显示或者隐藏 ~~~ **逻辑思路:** **这里首先动用了选择器除去最后一个元素所有大于5的li元素选中。通过用户点击事件,判断是否显示或者隐藏,并联动修改被点击的图标。** **亮点在选择器的掌控恰到好处,用jquery最基本的就要用熟选择器,这样会快速高效操作dom节点。这样也就不难理解css3为什么首要改革的就是选择器了,简化操作流程、正则化及语义化css选择器,使得前端工程师读和写代码更便捷。** **5.文本域“剩余字数”** **** ~~~ $(document).ready(function(){ var tarea = $("#init");// tarea.focus(function(){ $("#numtj").fadeIn(200); }).blur(function(){ $("#numtj").fadeOut("slow"); })//文本域获得焦点和失去焦点剩余字数显示或消失,用到渐入渐出 $("#init").keyup(function() { var text=$("#init").val(); var counter=text.length; $("#numtj var").text(110-counter); });// 按下键盘则实时修改剩余字数 }); ~~~ **逻辑思路:** **逻辑非常简单,获得焦点剩余字数显示,失去焦点剩余字数消失。键盘按起计算还剩字数。** **这里的亮点在于使用了fadeIn和fadeOut方法实现渐入渐出更加自然,体验更好。focus和blur事件也是针对有输入内容的元素常需处理的事件。keyup事件则与用户输入内容,实时交互有关系。** **6.鼠标移动到图片放大** ~~~ var ShowImage = function() { xOffset = 10; yOffset = 30; $("#imglist").find("img").hover(function(e) {//给图片对象添加事件及触发函数 $("<img id='imgshow' src='" + this.src + "' />").appendTo("body");//在body上添加图片对象 $("#imgshow") .css("top", (e.pageY - xOffset) + "px")//定位body显示图片的y坐标 鼠标位置加人为设置偏移量 .css("left", (e.pageX + yOffset) + "px")//定位body显示图 .fadeIn("fast"); },function() { $("#imgshow").remove(); }); $("#imglist").find("img").mousemove(function(e) { $("#imgshow").css("top",(e.pageY-xOffset) + "px") .css("left", (e.pageX + yOffset) + "px"); }); }; ~~~ **逻辑思路:** **这段代码的逻辑非常具有普遍性,e代表鼠标事件,鼠标属性pageX和pageY代表鼠标的坐标位置。整体的思路即是通过鼠标触发悬浮事件,显示或者移除与鼠标位置有一个偏移的图片,并通过设置mousemove事件来实时移动放大图位置。** **看了以上几个例子我们会发现,事件和选择器是整体交互效果核心。所以这里有必要再精通、再精通。** **7.图片轮播** ~~~ var t = 0; var n = 0; var count;//全局变量沟通函数和click事件内变量 $(document).ready(function(){ count=$("#bannerList a").length;//获取用户控制a标签的总数 $("#bannerList a:not(:first-child)").hide();//除了第一个元素都隐藏 $("#banner li").click(function(){//给存放图片的列表添加单击事件 var i = $(this).text() - 1;//获取Li元素内的值,即1,2,3,4 n = i;//i的值赋值给n if (i >= count){//判断是否大于a标签的总数 return; } var $a = $("#bannerList a").filter(":visible").fadeOut(500);//显示的都隐藏掉 $a.parent().children().eq(i).fadeIn(1000);//根据当前li元素的值显示图片 //$("#banner").css("background","");//显示按钮样式 $(this).toggleClass("show");//当前对象删除或者添加类show $(this).siblings().removeAttr("class");//当前对象删除类属性 }); //设置定时或消除定时 t = setInterval("showTime()", 3000);//每隔三秒执行一次方法 $("#banner").hover(function(){//给按钮添加鼠标滑过事件 clearInterval(t)},function(){//鼠标滑过停止播放 t = setInterval("showTime()", 3000);//鼠标滑出继续播放 }); }) function showTime() { if(n>=(count-1)){ n=0; }else{ n=++n; } $("#banner li").eq(n).trigger('click');//代码触发click事件 } ~~~ **逻辑思路:** **这段图片轮播的代码,大体可以分为三个模块:初始化模块、单击事件模块、鼠标悬浮模块、自动触发模块。初始化模块意在初始化开始轮播图片序数,单击时间模块意在确定单击事件执行被单击对象显示其他对象隐藏操作,鼠标悬浮模块意在清除或者恢复自动触发模块,以便执行单击操作,自动触发模块用于模拟单击事件。** **这段代码混乱之处在于设置的i和n都为全局变量,在函数和执行语句中游走。trigger这个事件竟然模拟了单击事件,鼠标悬浮hover用的恰到好处,不会造成与click事件的冲突。** **8.[选择城市插件](http://www.xiao-a.com/index.php/archives/1113.html)** ~~~ jQuery.fn.selectCity = function(targetObj){ var _self = this;//获取当前对象,使用此插件的选择器内对象 var targetObj = $(targetObj);//根据参数获取对象 this.click(function(){//当前对象触发单击事件 var _top=$(this).offset().top + $(this).outerHeight(true);//获取对象相对顶部真实高度 var _left=$(this).offset().left;//获取对象相对左侧真是高度 targetObj.bgiframe();//调用插件的方法bgiframe() targetObj.show().css({"position":"absolute","top":_top+"px" ,"left":_left+"px"});//显示定位 });//根据被选对象的位置,将隐藏的显示出来并设置隐藏的下方位置 targetObj.find("#selectItemClose").click(function(){ targetObj.hide();//用户单击关闭按钮隐藏选择区域 }); targetObj.find("#selectSub :checkbox").click(function(){ targetObj.find(":checkbox").attr("checked",false);//将所有的多选框设置为未被选中 $(this).attr("checked",true); _self.val($(this).val()); targetObj.hide();//设置点击选中,并传递给input对象值,隐藏选中框 }); $(document).click(function(event){ if(event.target.id!=_self.selector.substring(1)){//鼠标在页面单击时隐藏复选框对象 targetObj.hide(); } }); targetObj.click(function(e){ e.stopPropagation(); //阻止事件的传递 }); return this; } ~~~ **逻辑思路:** **页面单击对象时,则设置需要显示的对象到被单击对象的下方。并设置关闭按钮。将所有多选框设置未被选中,以选中特定栏。当鼠标单击页面时,需要显示的对象消失。** **这里比较新颖的是当鼠标不点击显示区域和单击对象时的处理,才采用了一个target.id的判断达到效果。** **9.分页插件** ~~~ var page_all = $('.page');//获取所有需要分页显示的区域,全局变量 var user_nav = $('#page_nation');//用户分页操作区域,全局变量,函数内更改也会更改值 function createActionLinks(){//动态生成分页操作区域 user_nav.append('<a href="javascript:void(0)" id="prev">上一页</a>'); for(var i=0;i<$('.page').length;i++){//显示区域的范围内遍历 user_nav.append('<a href="javascript:void(0)" class="numlink">'+(i+1)+'</a>'); } user_nav.append('<a href="javascript:void(0)" id="next">下一页</a>'); } function changeAction(page,preBtn,nextBtn){//分页实现函数 $('.page:eq('+page+')').css('display','block');//显示当前分页内容 $('.numlink:eq('+page+')').addClass('current');//当前页按钮高亮显示 var pageSize = parseInt($('.page').length - 1);//获取最大页数 if(page == 0){//显示在第一页时 preBtn.hide();//上一页按钮隐藏 nextBtn.show();//下一页按钮显示 }else if(page==pageSize){//显示在最后一页时 preBtn.show();//上一页按钮隐藏 nextBtn.hide();//下一页按钮显示 }else{ preBtn.show();//上一页按钮显示 nextBtn.show();//下一页按钮显示 } } function hideObj(){ page_all.css('display','none');//隐藏所有分页 $('.numlink').removeClass('current');//去除所有分页的样式 } $(document).ready(function(){ $('.page:eq(0)').css('display','block');//默认显示第一页 createActionLinks();//初始化创建用户操作区域 $('.numlink:eq(0)').addClass('current');//给第一个按钮添加样式 var nextBtn = $('#next');//获取下一页按钮对象 var preBtn = $('#prev');//获取上一页按钮对象 var linkAction = $('.numlink');//获取超级链接对象集合 preBtn.hide();//默认显示第一页并隐藏上一页操作按钮 var page = parseInt($('.numlink').index($('.current')));//返回当前分页值,这一句话很关键 //三种点击动作 nextBtn.click(function(){//给下一页按钮添加单击事件 hideObj();//隐藏全部分页部分 changeAction(page+1,preBtn,nextBtn);//函数每执行一次page的值减1 page = parseInt($('.numlink').index($('.current')));//返回page的值 }); preBtn.click(function(){//给上一页按钮添加单击事件 hideObj();//隐藏全部分页部分 changeAction(page-1,preBtn,nextBtn);//函数每执行一次page的值加1 page = parseInt($('.numlink').index($('.current')));//返回page的值 }) linkAction.click(function(){//给分页页码链接添加单击事件 var that = $(this);//获取当前对象 hideObj();//隐藏全部分页部分 var index = that.index() - 1;//获取当前下标值 changeAction(index,preBtn,nextBtn);//调用分页函数 page = parseInt($('.numlink').index($('.current')));//返回page的值 }) }) ~~~ **逻辑思路:** **分页属于老生长谈了,这里的实现主要从分页按钮生成模块、切换页面模块、隐藏模块、并通过ready后,初始化设置初始状态,对相应按钮加上交互操作即click事件,并调用切换页面模块实现点击切换。类似MVC架构。** **10.自定义插件注意要点** ~~~ (function($){ $.fn.changeTab = function(options){//插件需要附加到jquery.fn对象上 var defaults = { FontSize:"50px", Color:'red', FontWeight:'bold' }//设置默认的参数 var options = $.extend(defaults,options);//对参数更新 this.each(function(){ var lis = $(this);//保留this操作 lis.hover(function(){ $(this).css({ "fontSize": options.FontSize, "color": options.Color ,"fontWeight":options.FontWeight}); },function(){ $(this).css({ "fontSize": '', "color": '',"fontWeight":''}); }) }) } })(jQuery)// 插件内避免使用$ ,插件应返回一个jquery对象 便于链式操作 $(function(){ var options={ FontSize:'16px', Color:'blue', FontWeight:'bold' }//设置个性化参数 $('li').changeTab(options);//引用插件 }) ~~~ **逻辑思路:** **1.插件需要附件到jquery.fn上。** **2.插件内返回jquery对象便于链式操作。** **3.设定默认参数,并通过传参调用extend对象对参数更新。** **以上总结均为项目中常见特效的情形,可以看出来事件和选择器是需要掌握扎实的,一些编程技巧也要学会。其次这篇文章可以作为一个程序库,供使用时查看。**
';

前端编程提高之旅(一)—-插件

最后更新于:2022-04-01 09:36:49

平日收集有价值的网页颇多,而收集资料并不是一件快乐的事情,反而越多越给内心压力,困扰自己的同时,也错失了提高的时机。因此此系列文章意在探讨前端的编程所感,亦是疏解内心压力,自我提高的途径。此偏文章涉及内容均来自[网友博客](http://blog.csdn.net/blatar/article/category/1229834)。 示例一: ~~~ <!DOCTYPE html > <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <title>bar</title> <script type="text/javascript" src="http://libs.baidu.com/jquery/1.7.2/jquery.min.js"></script> <style type="text/css"> #time_back{width:300px;height:20px;border:1px solid #246;background:#CCC} #time_bar{width:0;height:20px;background:#470;color:#FFF;text-align:right;} </style> <script type="text/javascript"> $(document).ready(function(){ $("#input_time").change(function(){//输入框中数值一旦变化,则触发change函数 var rate = $(this).attr("value"); changebar("#time_bar",rate,"red"); }); /** * 更新进度条 * @ param obj 进度条id * @ param rate 进度,0-100间的整数 * @ param deadColor 当进度超过一定值(默认80)的颜色 */ function changebar(obj,rate,deadColor){ if(undefined!=deadColor&&rate>80){ $(obj).css({"background":deadColor}) } rate = rate%100 + "%"; $(obj).animate({width:rate},1000); $(obj).html(rate); } }); </script> <body> 日期:<div id="time_back" ><div id="time_bar" ></div></div> <input type="text" id="input_time" /> </body> </html> ~~~ **这个例子制作工具条的例子,非常简单。外层div,内层嵌套一个div,用animate函数设置内层背景颜色和文字,通过change方法触发使得整个页面呈现出响应和动态的效果。** 示例二: ~~~ $(document).ready(function(){ //滚动事件,滚动window元素时,调用滚动事件 $(window).scroll(function(){ //显示滚动条位置 $(".fixed").html(document.body.scrollTop||document.documentElement.scrollTop); var t = document.body.scrollTop||document.documentElement.scrollTop; //当导航条不可见时,重新定义导航条位置 if(t>100){//100为导航栏高度 $("#nav").css({ "position":"fixed", "top" :"0px", "left" :"0px" }); }else{ //回到导航条原来位置时,还原导航条位置 $("#nav").css({ "position":"", "top" :"", "left" :"" }); } }); }); ~~~ **这段代码则是通过滑动滚动条来触发函数,并且通过导航与浏览器顶部的距离作为条件,修改id为nav的位置,意在使nav这个元素始终保持在视野中的顶部。** 示例三: ~~~ $(document).ready(function(){ // 初始化内容 for(var i = 0 ; i < 3 ; i++){ $(".flow").each(function(){ $(this).append("<div style=\"width:90%;height:"+getRandom(200,300)+"px;margin:5px auto;background:#159;\"></div>"); }); } $(window).scroll(function(){ // 被卷去的高度 var scrollTop = document.body.scrollTop||document.documentElement.scrollTop; // 页面高度 var pageHeight = $(document).height(); // 可视区域高度 var viewHeight = $(window).height(); //alert(viewHeight); //当滚动到底部时 if((scrollTop+viewHeight)>(pageHeight-20)){ if(scrollTop<1000){//防止无限制的增长 for(var i = 0 ; i < 2 ; i++){ $(".flow").each(function(){ $(this).append("<div style=\"width:90%;height:"+getRandom(200,300)+"px;margin:5px auto;background:#159;\"></div>"); }); } } } }); }); /* * 获取指定范围随机数 * @param min,最小取值 * @param max,最大取值 */ function getRandom(min,max){ //x上限,y下限 var x = max; var y = min; if(x<y){ x=min; y=max; } var rand = parseInt(Math.random() * (x - y + 1) + y); return rand; } ~~~   **这段代码是典型瀑布流构造的代码,只需要加入图片到div内部即可。通过滚动条触发函数,使得在一定范围内,随机加载div宽度,连接到原有div之后,构成边拉边加载的效果。** 示例四: ~~~ $.extend({ wordScroll:function(opt,callback){ //alert("suc"); this.defaults = { objId:"", width:300, // 每行的宽度 height:100, // div的高度 liHeight:25, // 每行高度 lines:2, // 每次滚动的行数 speed:1000, // 动作时间 interval:2000, // 滚动间隔 picTimer:0, // 间隔句柄,不需要设置,只是作为标识使用 isHorizontal:false // 是否横向滚动 } //参数初始化,看是否有新的参数传入,传入则更新初始化设置 var opts = $.extend(this.defaults,opt); // 纵向横向通用 $("#"+opts.objId).css({ width:opts.width, height:opts.height, "min-height":opts.liHeight+"px", "line-height":opts.liHeight+"px", overflow:"hidden" }); $("#"+opts.objId+" li").css({ height:opts.liHeight }); if(opts.lines==0) opts.lines=1; // 横向滚动 if(opts.isHorizontal){ $("#"+opts.objId).css({ width:opts.width*opts.lines + "px" }); $("#"+opts.objId+" li").css({ "display":"block", "float":"left", "width":opts.width + "px" });//水平则行内显示 $("#"+opts.objId+" ul").css({ width:$("#"+opts.objId).find("li").size()*opts.width + "px"// 输出li选择器的数量,乘以宽度 }); // 横向使用,不够一屏则不滚动 if($("#"+opts.objId).find("li").size()<=opts.lines) return; var scrollWidth = 0 - opts.lines*opts.width; }else{ //如果不够一屏内容 则不滚动 if($("#"+opts.objId).find("li").size()<=parseInt($("#"+opts.objId).height()/opts.liHeight,10)) return; var upHeight=0-opts.lines*opts.liHeight; } // 横向滚动 function scrollLeft(){ $("#"+opts.objId).find("ul:first").animate({ marginLeft:scrollWidth },opts.speed,function(){ for(i=1;i<=opts.lines;i++){ $("#"+opts.objId).find("li:first").appendTo($("#"+opts.objId).find("ul:first")); } $("#"+opts.objId).find("ul:first").css({marginLeft:0}); }); }; // 纵向滚动 function scrollUp(){ $("#"+opts.objId).find("ul:first").animate({ marginTop:upHeight },opts.speed,function(){ for(i=1;i<=opts.lines;i++){ $("#"+opts.objId).find("li:first").appendTo($("#"+opts.objId).find("ul:first")); } $("#"+opts.objId).find("ul:first").css({marginTop:0}); }); }; //鼠标滑上焦点图时停止自动播放,滑出时开始自动播放 $("#"+opts.objId).hover(function() { clearInterval(opts.picTimer); },function() { opts.picTimer = setInterval(function() { // 判断执行横向或纵向滚动 if(opts.isHorizontal) scrollLeft(); else scrollUp(); },opts.interval); // 自动播放的间隔,单位:毫秒 }).trigger("mouseleave"); } }) ~~~ **这段代码是一个使文字横向或者纵向滚动的插件,如果想了解清楚插件的工作原理大体需要看这[两个](http://www.cnblogs.com/RascallySnake/archive/2010/05/07/1729563.html)[网页](http://www.css88.com/jqapi-1.9/jQuery.extend/)了解了extend函数的用法即可。在了解extend用法之后,就可以找一下插件了如上前三段代码的联系了。jquery插件与如上三段代码的区别在于通过extend方法后,内部的对象wordScroll就成了jquery包含的对象了,引用的时候可以这样:** ~~~ $.wordScroll({ }); ~~~ **插件内都会设置一些default属性,当有部分变量传入时,与默认重复的会被覆盖成传入的,未传入的仍会保持原样。如下代码实现此功能:** ~~~ var opts = $.extend(this.defaults,opt); ~~~ 剩余内容就是上述四段代码的共同点了,在代码内完成结构、样式、交互逻辑代码的设计,也就是说,用上extend函数和如上一行代码,就可以轻松实现jquery功能的扩展即插件。此篇引用博主的文章前几篇都是插件的使用。插件的确增加了“简单可依赖”的特点,增加了封装性,但封装涉及交互、样式和结构,降低了代码的可读性,以此看来插件的开发是把双刃剑。
';

前言

最后更新于:2022-04-01 09:36:46

> 原文出处:[前端开发提高之旅](http://blog.csdn.net/column/details/yingyiledi.html) 作者:[yingyiledi](http://blog.csdn.net/yingyiledi) **本系列文章经作者授权在看云整理发布,未经作者允许,请勿转载!** # 前端开发提高之旅 > 提供前端开发由浅入深的学习指南
';