依韵
1902eaaa46517d3c4ab771a70d4f034a539f6e6e-cds.bitcron.com
2019-12-30T13:24:57Z
2019
2019-12-30T13:24:57Z
life/2019/12-28-summary-for-2019
依韵
<p class="md_block">
<span class="md_line md_line_start md_line_end">2019似乎眨眼间就过去了,这一定是加班太多,导致我都没有时间来感慨生活了。</span>
</p>
<!--more-->
<a name="more"></a>
<h2 id="toc_0" class="h16">丰富认知</h2><h3 id="toc_1" class="h16">听得到</h3>
<p class="md_block">
<span class="md_line md_line_start md_line_end">罗胖是不是贩卖焦虑的我不知道,知识付费是不是大忽悠我也不在乎。不过我只是在上下班路上听听得到,遇到有共鸣的,自己也做点小笔记而已。</span>
</p>
<p class="md_block">
<span class="md_line md_line_dom_embed md_line_with_image md_line_start md_line_end"><img class="md_compiled " src="https://qiniu.cdswyda.com/images/blog/2019/12/20191228182303.jpg" alt="" title="" ></span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">“纸上得来终觉浅,绝知此事要躬行”。你要想练真本事,学大智慧,还不得靠自己。 就像我们程序员,没踏实地写过代码,能成长嘛。</span>
</p>
<h3 id="toc_2" class="h16">读书</h3>
<p class="md_block md_block_as_opening md_has_block_below md_has_block_below_ul">
<span class="md_line md_line_start md_line_end">“书是人类进步的阶梯”多读书,总是好的,万一就用用上了呢。2019大致读了下面这些书:</span>
</p>
<ul>
<li class="md_li"><span>刺杀骑士团长
</span></li>
<li class="md_li"><span>刻意练习
</span></li>
<li class="md_li"><span>亲密关系
</span></li>
<li class="md_li"><span>如何阅读一本书
</span></li>
<li class="md_li"><span>城南旧事
</span></li>
<li class="md_li"><span>浮生六记
</span></li>
<li class="md_li"><span>腾讯传
</span></li>
<li class="md_li"><span>周鸿祎自诉:我的互联网方法论
</span></li>
<li class="md_li"><span>图解http
</span></li>
<li class="md_li"><span>球状闪电
</span></li>
<li class="md_li"><span>思考快与慢
</span></li>
<li class="md_li"><span>万万没想到
</span></li>
<li class="md_li"><span>历史的第三种读法
</span></li>
<li class="md_li"><span>浪潮之巅
</span></li>
<li class="md_li"><span>学会写作(刘杨)
</span></li>
<li class="md_li"><span>HTTP2基础教程
</span></li>
</ul>
<p class="md_block">
<span class="md_line md_line_start md_line_end">差不多是一月一本的进度。虽然量比较少,但是与我来说,有空就读读书已经基本成了小习惯,也算是一种进步,特此记录。</span>
</p>
<p class="md_block md_block_as_opening md_has_block_below md_has_block_below_ul">
<span class="md_line md_line_start md_line_end">上面所列出的图书中,有以下基本是我个人极为不推荐的:</span>
</p>
<ul>
<li class="md_li"><span>浮生六记: 在微信读书里面看的这本书,评论中有一句非常经典“渣男界的泥石流”,想看他是怎么渣的可以去读读。
</span></li>
<li class="md_li"><span>周鸿祎自诉 我的互联网方法论:兴许是我没读懂,感觉没讲到什么内容,实际是在360做宣传。
</span></li>
</ul>
<p class="md_block">
<span class="md_line md_line_start md_line_end">总结来说就是读的有点少,尤其是技术类的。2020 keep。</span>
</p>
<h2 id="toc_3" class="h16">关于工作</h2>
<p class="md_block">
<span class="md_line md_line_start md_line_end">2019是我在这公司的第三年了,顺利升为了二星工程师,很多想法,只奈公司太大,项目紧迫,很多东西都不能真正落实。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">与我个人成长而言,值得说道的项目有如下两个:表单设计器和组件库重写升级。</span>
</p>
<h3 id="toc_4" class="h16">表单设计器</h3>
<p class="md_block">
<span class="md_line md_line_start md_line_end">简单来说就是可视化创建表单,动态配置或创建字段信息,然后生成实际真实可用的页面。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">在这个产品中,终于力排众议用上了Vue技术栈。也算是一个里程碑式的时刻了,为什么这么说呢?公司可一直用的是ES3,我在公司三年,写代码的的百分之九十也是ES3。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">布局层面上,摒弃了旧版本上利用富文本编辑器中表格来布局的传统,使用行列并支持内部再次嵌套的模式来实现。边框等的配置使用最内层的元素边框来拼接。相关的拖拽交互应用了 <a class="md_compiled" href="https://github.com/SortableJS/Sortable">Sortable</a> 来实现。边框的配置以及拖拽时的吸附,在整个开发过程中也是下了不少功夫,受益良多。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">表单控件的配置性上是此设计器最大的亮点。业务上表单上所用的控件肯定各有特色,表单设计器引擎本身肯定无法穷举。因此对表单控件的配置做了进一步抽象,设计器本身不提供任何表单控件配置的实现。需要哪些控件、每个控件有些什么配置,均为外部配置文件控制,大大提示了表单设计器的适用场景。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">大致如下图所示:</span>
</p>
<p class="md_block">
<span class="md_line md_line_dom_embed md_line_with_image md_line_start md_line_end"><img class="md_compiled " src="https://qiniu.cdswyda.com/images/blog/2019/12/2019-12-30-15-15-28.jpg" alt="" title="" ></span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">如上所示的文本输入框,整个右侧配置面板均是左侧的json文件生成。配置面板本身只用负责将相应属性的修改反应到对应的控件属性值上即可。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">此外还有支持撤销还原的操作栈、事件配置、连动设值、动态显隐藏等,简单说实现这些个功能点其实都不算太难,但是要考虑作为通用产品,要处理的细节还是不少,具体就不在这里一一赘述了。</span>
</p>
<h3 id="toc_5" class="h16">组件库升级</h3>
<p class="md_block">
<span class="md_line md_line_start md_line_end">因为原来的组件库样式老旧,有点跟不上新时代了。因此在今年对组件库进行了升级。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">本次升级中,样式部分全部重新写,js实现上重写了百分之五十以上的代码。设计大多参考了 <a class="md_compiled" href="https://ant.design/">Ant Design</a>。 组件开发升级过程中研究了一部分 iview 的源码,为表格新增了很多功能,目前来说 iview 表格所具备的功能,我们的组件库也全部具备了。所不同的是, iview 是用 vue 写的,我们的组件库还是jQuery的。但是有啥关系呢?这些东西对客户和使用者是透明的,作为公司的框架开发人员,服务好我们的开发人员,支持好我们的项目开发才是最关键的。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">组件库的升级过程中,顺手花了些功夫,把代码从ES3全面迁移到了ES5,(不支持的环境下自动用 shim),数组上的方法基本可以随便玩了,是爽了不少。虽然不知道这套组件库生命周期还能有多久,在其位谋其事,我尽力了。</span>
</p>
<hr>
<p class="md_block md_block_as_opening md_has_block_below md_has_block_below_ol">
<span class="md_line md_line_start md_line_end">在这两个项目中,我也尽个人绵薄之力,将整个流程弄上了现在的自动化。简单来说也就是如下几步:</span>
</p>
<ol>
<li class="md_li"><span>svn仓库迁移到私有git上, git主分支提交/合并的时候,自动同步代码到svn仓库。
</span></li>
<li class="md_li"><span>每个小功能都一用 commit 再用 Jenkins 做持续集成,出错则报警。
</span></li>
<li class="md_li"><span>主分支有提交且持续集成通过的情况下,演示服务器拉取更新,构建再自动更新替换演示系统。
</span></li>
</ol>
<p class="md_block">
<span class="md_line md_line_start md_line_end">搞了整个流程的自动化,沟通过程轻松了不少。 演示系统上始终是最新且可用的,即方便沟通和测试。领导要看嘛,演示系统有,git提交记录也清楚明白。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">程序员就要敢于偷懒,不做无意义的重复工作。不过省下来的时间,似乎最后又都花在了写新的代码上。💁♂</span>
</p>
<h2 id="toc_6" class="h16">未来</h2>
<p class="md_block">
<span class="md_line md_line_start md_line_end">未来从来都不远,下一秒、明天都是未来,所以重要的是在路上,现在在干什么。Just Keep。</span>
</p>
终身幼儿园
2018-10-14T13:36:57Z
reading/2018/10/lifelong-kindergarten
依韵
<p class="md_block">
<span class="md_line md_line_start md_line_end">无意间听到的一本推荐书,感觉说法很有趣,因此买来读了一下,其中提出的关于学习的观点,我觉得还是非常值得我们参考的。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">不过需要吐槽的一点是,看这本书,我一直有种作者这是在给编程积木还有 Scratch 做广告的感觉。非常严重,好评度-1。</span>
</p>
<h2 id="toc_0" class="h16">核心观点</h2><blockquote class="blockquote_lines_1">
<p class="md_block">
<span class="md_line md_line_start md_line_end">I believe that the best way to help people understand the world is to provide them with opportunities to actively explore, experiment, and express themselves.</span>
</p>
</blockquote>
<p class="md_block">
<span class="md_line md_line_start md_line_end">提出了一个“X型人”的概念,大抵就是说那种爱创新、敢折腾的人。未来更需要这样的人,为什么呢?一个原因就够了——未来的职业可能有 2/3 都是目前还不存在的。</span>
</p>
<p class="md_block md_block_as_opening md_has_block_below md_has_block_below_ol">
<span class="md_line md_line_start md_line_end">书中指出我们对创造力有以下几个误解:</span>
</p>
<ol>
<li class="md_li"><span>创造力只是一种艺术表达
</span></li>
<li class="md_li"><span>只有很少的一部人具有创造力
</span></li>
<li class="md_li"><span>创造力来源于洞察力
</span></li>
<li class="md_li"><span>创造力无法被教授
</span></li>
</ol>
<p class="md_block">
<span class="md_line md_line_start md_line_end">其中第三个 “创造力来源于洞察力”,作者以阿基米德在浴缸里洗澡时想到浮力定律、牛顿被苹果砸到想到了万有引力,这两个例子来说明:创造力并非是某个时刻的顿悟。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">创造力不是顿悟怎么就能说明不需要洞察力了呢?理解不了,我觉得创造力就是基于洞察力的,不观察谈什么创造。 </span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">不要作者给出的创造力的说法是:造力来源于某一类别的艰苦工作,那综合了好奇的探索、有趣的实验和系统的调查。新想法和新见解看起来就像是闪电般突然出现的,但它们通常发生在想象、创造、游戏、分享和反思的许多个周期之后,也就是在经历了创造性学习螺旋的多次迭代之后才会出现。 这个结论我是接受的,“创造力来源于洞察力”我觉得是对的,就算不是来源,两者也至少是正相关的。</span>
</p>
<p class="md_block md_has_block_below md_has_block_below_blockquote">
<span class="md_line md_line_start md_line_end">还有目前很多家长恐慌的电子产品,作者则是很积极的态度:</span>
</p>
<blockquote class="blockquote_lines_1">
<p class="md_block">
<span class="md_line md_line_start md_line_end">科技是在你出生之后发明的所有东西。对于成长在今天的孩子来说,笔记本电脑和手机不是高科技工具,它们就像蜡笔和水彩画一样,是日常生活中的工具。</span>
</p>
</blockquote>
<p class="md_block">
<span class="md_line md_line_start">正视电子产品,它不是洪水猛兽,仅仅是一个工具,而且是日常工具。<br /></span>
<span class="md_line md_line_end">不要试图抵制,就单单是电子科技产品的普及程度,就没可能抵制;而且这完全是一个发展方向,为什么要抵制?如果要抵制就好比是想要社会回到没有电的时代、没有科技的时代。这完全是不可能的,既然如此,为何不敞开胸怀?</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">与其担心他们使用,倒不如放开给使用,注意引导如何使用即可。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">比如有电子词典还非得让孩子去翻纸质的大厚词典就没必要,但不想要思考,只想百度一下,那就不可取。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">培养创造力、以及适合未来的“X型人” 要使用“4P” 学习法,即:项目(Project)、热情(Passion),同伴(Peers),游戏(Play)。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">或许这个和作者理想的幼儿园教学很相似,所以叫做终身幼儿园吧。想象力和创造力最丰富的时候就是幼儿期了,即使目前的幼儿园教育和理想中的还是不一样,但想想,幼儿园的教育方式实际和之后的教育确实相差很远。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">因为接受能力的限制,对幼儿进行教育时,不是你告诉他这是什么什么,这要怎么怎么的,更多的是示范、然后他们自己动手实践,这个过程中会多很多创新的实践。 用作者书中的话说就是 <strong>想象 -> 创造 -> 游戏 -> 分享 -> 反思 -> 再想象</strong> 的循环。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">孩子的想象力真的是无穷的,我两岁多的女儿,随便拿个什么东西都能开始想象,进行自己的游戏。比如拿着一块码表,当成是手机给动画片里的各种人物打电话,能模拟一个简单而完整的对话;把佩奇和苏茜放在玩具汽车上,再让她们摔下去,然后是哪里受伤了,包扎、住院等等;每个场景都简单而完整,虚构而真实。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">这就是孩子的想象力,作者希望一直以像幼儿园实践一样的方式来教学,保持我们的想象力并培养创造力,核心点是以 “4P”中的项目来组织一切。</span>
</p>
<h2 id="toc_1" class="h16">项目 Project</h2>
<p class="md_block">
<span class="md_line md_line_start md_line_end">一种创客思维,仅仅提倡在做的过程中学到东西,即实践,这是不够的。需要在亲身实践的过程中创造点东西出来,最有价值的不是创造了什么,而是这一系列过程中获得的经验。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">我的理解是这可以说是更高层次的实践,对实践的更高要求,不单单是做,而是从思考、规划、设计、行动、试用、改造和优化等一系列流程的综合实践。想象小孩玩的时候,他自己就是一个上帝视角,掌控操作着一切,这个过程中,孩子不单单是一个参与者,而是整个知识体系的构建者。可理解为译者最后总结的“我有”到“我是”的升华。</span>
</p>
<p class="md_block md_has_block_below md_has_block_below_blockquote">
<span class="md_line md_line_start md_line_end">所以这个的核心就是,孩子是积极的知识建构者,而不是被动的接受者。孩子的想法并不是外来的,而是他们自己创造出来的。 虽然项目的名义负责人只有一个,但每个成员都要有这种主导者的心态。就像孩子的团结协作一样,他们不懂、不理解办公室政治,也完全不需要这一套。</span>
</p>
<blockquote class="blockquote_lines_1">
<p class="md_block">
<span class="md_line md_line_start md_line_end">不要问这个玩具能为你的孩子做些什么,要问你的孩子能拿这个玩具做些什么。</span>
</p>
</blockquote>
<p class="md_block md_has_block_below md_has_block_below_blockquote">
<span class="md_line md_line_start md_line_end">作者还非常提倡编程和写作:</span>
</p>
<blockquote class="blockquote_lines_2">
<p class="md_block">
<span class="md_line md_line_start md_line_end">编程是一种流畅的表达方式,表达得更流畅,无论是通过写作还是通过编程程,都可以帮助你开发思维,发出自己的声音,并培养你的身份认同感。</span>
</p>
</blockquote><blockquote>
<p class="md_block">
<span class="md_line md_line_start md_line_end">在写作的过程中,你学会了组织、提炼和反思自己的想法。当你成为一个更好的写作者时,你也就成了一个更好的思考者。</span>
</p>
</blockquote>
<h2 id="toc_2" class="h16">热情 Passion</h2>
<p class="md_block md_has_block_below md_has_block_below_blockquote">
<span class="md_line md_line_start md_line_end">这块的内容我觉得可以直接理解为 <strong>内驱力</strong> 。</span>
</p>
<blockquote class="blockquote_lines_1">
<p class="md_block">
<span class="md_line md_line_start md_line_end">丹尼尔·平克在《驱动力》一书中描述了这种差异:“奖励可以带来短期的提升,就像咖啡因的刺激可以让你多坚持几个小时一样。但这种影响会逐渐消失,更糟糕的是,这会降低一个人继续从事这个项目的长期动力。”</span>
</p>
</blockquote>
<p class="md_block md_has_block_below md_has_block_below_blockquote">
<span class="md_line md_line_start md_line_end">你是被动的还是主动的,这个对事情的长远发展影响非常大。</span>
</p>
<blockquote class="blockquote_lines_1">
<p class="md_block">
<span class="md_line md_line_start md_line_end">一些幼儿园的孩子在纸上作画时被授予“优秀玩家”的证书,而其他孩子则没有。两周后,孩子们被要求画更多的画,但不再有任何证书作为奖励。结果那些在第一次获得了证书的孩子这时表现得更不感兴趣,画画的时间更少。</span>
</p>
</blockquote>
<p class="md_block">
<span class="md_line md_line_start md_line_end">毫无疑问,内驱力是必要的,找到真实的内驱力非常关键,更告诉我们想要孩子干嘛的时候不能单纯地通过奖励来促进,引导他们为自己找一个必须这么做的原因才最有效。</span>
</p>
<h2 id="toc_3" class="h16">伙伴 Peers</h2>
<p class="md_block">
<span class="md_line md_line_start md_line_end">分工合作已经有上千年的历史了,而且越来越必要。团结协作得到的成果通常是1+1>2的,互相合作的环境中,不仅能互补反馈,同时也能不断激发自己的新的灵感。</span>
</p>
<p class="md_block md_block_as_opening md_has_block_below md_has_block_below_ul">
<span class="md_line md_line_start md_line_end">此处作者给出了针对老师应该扮演好的四种角色:</span>
</p>
<ul>
<li class="md_li"><span>催化剂: 加快、促进
</span></li>
<li class="md_li"><span>顾问: 实时提供建议、而不做干预
</span></li>
<li class="md_li"><span>连接者: 讲想法进行连接共享、提供外部支持
</span></li>
<li class="md_li"><span>协作者:帮助进行合作互动
</span></li>
</ul>
<p class="md_block">
<span class="md_line md_line_start md_line_end">用作者的话总结就是:<strong>最小干预教育</strong></span>
</p>
<h2 id="toc_4" class="h16">游戏 Play</h2>
<p class="md_block">
<span class="md_line md_line_start md_line_end">作者指出两者角色,计划者 还是 修补匠。 </span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">你是要大致规划好,再开始行动。还是动起来再说,出了问题慢慢再改。 前者高效率、后者高创造性。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">其实此处不一定要计划好,拿软件开发这块来说,有一个最小可行产品的说法,别管那么多计划和功能,先做出来再说,有价值慢慢改,哪怕推翻重写也无所谓,无价值放弃损失也比较小。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">此处的游戏实际是说的一种态度,把你的项目当成玩具一样来把玩,让它更好。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">你是要玩游戏呢,还是自己设计开发一个游戏出来?</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">你是要玩着游戏的时候爽、之后无尽的自责空虚呢, 还是愿意像玩游戏一样沉浸到项目中去,创造价值的同时获得快乐呢?</span>
</p>
<h2 id="toc_5" class="h16">个性化学习</h2>
<p class="md_block md_has_block_below md_has_block_below_blockquote">
<span class="md_line md_line_start md_line_end">作者希望形成自己的个性学习方案。</span>
</p>
<blockquote class="blockquote_lines_1">
<p class="md_block">
<span class="md_line md_line_start md_line_end">我希望学习者能对自己的学习方式、学习内容、学习时间和学习地点有更多的控制权。当学习者拥有更多的选择和控制权时,学习就可以建立在兴趣和热情的基础上,从而变得更加个人化,更有动力,也更有意义。</span>
</p>
</blockquote>
<p class="md_block">
<span class="md_line md_line_start md_line_end">我还看过另一种说法的,因此对这个个性化学习并不是很赞同。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">另一种说法是:<strong>什么东西用什么方法学习都是有固定套路的</strong>,所谓的适合每个人有适合自己的不同的学习方法,实际上是每个人的学习能力本身不同,自己感觉适合自己的学习方法只是在学习的过程中自我感觉良好而已。<strong>所谓适合自己的学习方法,只是能让自己学习的更舒服而已,并不能保障自己真实的学习效果</strong>。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">不过学的舒服,所以愿意继续进行下去,似乎也比自己感觉痛苦而放弃动好的多,这么来看,选择适合自己的(实际是自我感觉良好的)也确实不错。</span>
</p>
<h2 id="toc_6" class="h16">“我有” VS “我是”</h2>
<p class="md_block md_has_block_below md_has_block_below_blockquote">
<span class="md_line md_line_start md_line_end">最后的译者总结:</span>
</p>
<blockquote class="blockquote_lines_2">
<p class="md_block">
<span class="md_line md_line_start md_line_end">“我有”者靠技术来帮助自己占有更多的东西,“我是”者则是靠技术来帮助自己成为自己。不仅是技术,还包括文化、制度等其他创新形式,它们在出现的初期,都能让这两种人获益:“我有”者得到了更多的东西,“我是”者成为更好的人。但是随着这些支持因素的进一步发展,比如财富越来越多、信息流通速度越来越快、生活越来越方便,“我有”者会过度沉溺于物质丰富所带来的刺激,被越来越多的欲望吞噬,而“我是”者则能在这些因素的支持下,把人生过得越来越充实。</span>
</p>
</blockquote><blockquote>
<p class="md_block">
<span class="md_line md_line_start md_line_end">现在人人都在说升级:产业升级、消费升级、认知升级。但在我看来,在物质快速升级的同时,我们最需要的其实是心理升级。</span>
</p>
</blockquote>
简单粗暴而又不失优雅地在vue项目中使用monaco
2018-08-19T14:48:34Z
use-monaco-editor-in-vue
依韵
<blockquote class="blockquote_lines_1">
<p class="md_block">
<span class="md_line md_line_start md_line_end">monaco-editor 官方实际是有ESM的支持的,当时没注意到 -_-||,实际项目请优先采用,详情请点击<a class="md_compiled" href="https://github.com/Microsoft/monaco-editor/blob/master/docs/integrate-esm.md">Integrating the ESM version of the Monaco Editor (https://github.com/Microsoft/monaco-editor/blob/master/docs/integrate-esm.md)</a></span>
</p>
</blockquote>
<p class="md_block">
<span class="md_line md_line_start md_line_end"><a class="md_compiled" href="https://microsoft.github.io/monaco-editor/index.html">monaco-editor</a>是一款直接在vscode中使用的编辑器,其强大之处就不用多说了。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">然而其本身并未提供直接在vue中打包使用的机制,虽然有<a class="md_compiled" href="https://github.com/matt-oconnell/vue-monaco-editor">vue-monaco-editor</a>,但其本身是基于react的移植版,而且很久未更新,不知道有多少坑,不敢用。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">然而好处是monaco-editor直接提供了在浏览器以script调用的形式,那么我基于此进行改造即可。</span>
</p>
<p class="md_block md_block_as_opening md_has_block_below md_has_block_below_ol">
<span class="md_line md_line_start md_line_end">大概思路如下:</span>
</p>
<ol>
<li class="md_li"><span>提供加载方法,在调用前以script的形式动态加载资源,完成后暴露,供后续复用
</span></li>
<li class="md_li"><span>再次调用直接复用,无需再次加载
</span></li>
</ol>
<p class="md_block">
<span class="md_line md_line_start md_line_end">那么首先提供一个通用的加载方法:</span>
</p>
<p class="md_block">
<span class="md_line md_line_dom_embed md_line_start md_line_end"><strong>load-monaco.js</strong></span>
</p>
<div class="codehilite code_lang_js highlight"><pre><span></span><span class="kr">const</span> <span class="nx">MONACO_PATH</span> <span class="o">=</span> <span class="s1">'./static/js/monaco-editor/min/vs'</span><span class="p">;</span>
<span class="c1">// 处理 monaco-editor 资源的加载</span>
<span class="kr">export</span> <span class="k">default</span> <span class="kd">function</span> <span class="nx">loadMonaco</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nb">window</span><span class="p">.</span><span class="nx">__MONACO_PROMISE__</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nb">window</span><span class="p">.</span><span class="nx">__MONACO_PROMISE__</span><span class="p">;</span>
<span class="p">}</span>
<span class="kr">const</span> <span class="nx">scriptStr</span> <span class="o">=</span> <span class="sb">`</span>
<span class="sb"> require.config({</span>
<span class="sb"> paths: {</span>
<span class="sb"> 'vs': '</span><span class="si">${</span><span class="nx">MONACO_PATH</span><span class="si">}</span><span class="sb">'</span>
<span class="sb"> }</span>
<span class="sb"> });</span>
<span class="sb"> require(['vs/editor/editor.main'], function () {</span>
<span class="sb"> window.__monaco_editor__ = monaco;</span>
<span class="sb"> });</span>
<span class="sb"> `</span><span class="p">;</span>
<span class="kr">const</span> <span class="nx">editorPromise</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="c1">// loader 加载</span>
<span class="kr">const</span> <span class="nx">loaderScript</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s1">'script'</span><span class="p">);</span>
<span class="nx">loaderScript</span><span class="p">.</span><span class="nx">id</span> <span class="o">=</span> <span class="s1">'monaco-editor-loader'</span><span class="p">;</span>
<span class="nx">loaderScript</span><span class="p">.</span><span class="nx">src</span> <span class="o">=</span> <span class="nx">MONACO_PATH</span> <span class="o">+</span> <span class="s1">'/loader.js'</span><span class="p">;</span>
<span class="nx">loaderScript</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">loaderScript</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
<span class="nx">resolve</span><span class="p">(</span><span class="s1">'loader'</span><span class="p">);</span>
<span class="p">};</span>
<span class="nx">loaderScript</span><span class="p">.</span><span class="nx">onerror</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">loaderScript</span><span class="p">.</span><span class="nx">onerror</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
<span class="nx">reject</span><span class="p">(</span><span class="s1">'monaco-editor资源加载失败'</span><span class="p">);</span>
<span class="p">};</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">loaderScript</span><span class="p">);</span>
<span class="p">})</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span>
<span class="c1">// 依赖资源加载</span>
<span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="kr">const</span> <span class="nx">editorScript</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s1">'script'</span><span class="p">);</span>
<span class="nx">editorScript</span><span class="p">.</span><span class="nx">text</span> <span class="o">=</span> <span class="nx">scriptStr</span><span class="p">;</span>
<span class="nx">editorScript</span><span class="p">.</span><span class="nx">onerror</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">editorScript</span><span class="p">.</span><span class="nx">onerror</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
<span class="nx">reject</span><span class="p">(</span><span class="s1">'monaco-editor资源加载失败'</span><span class="p">);</span>
<span class="p">};</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">editorScript</span><span class="p">);</span>
<span class="nx">resolve</span><span class="p">(</span><span class="s1">'editor'</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">})</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span>
<span class="c1">// 加载检测</span>
<span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="kd">let</span> <span class="nx">timer</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="kd">function</span> <span class="nx">check</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// 已经加载则直接成功</span>
<span class="k">if</span> <span class="p">(</span><span class="nb">window</span><span class="p">.</span><span class="nx">__monaco_editor__</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">clearTimeout</span><span class="p">(</span><span class="nx">timer</span><span class="p">);</span>
<span class="nx">resolve</span><span class="p">();</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="c1">// 否则继续检测 但总次数不超过1000</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">count</span><span class="o">++</span> <span class="o"><</span> <span class="mi">1000</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">setTimeout</span><span class="p">(</span><span class="nx">check</span><span class="p">,</span> <span class="mi">30</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nx">reject</span><span class="p">(</span><span class="s1">'monaco-editor资源加载失败'</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nx">check</span><span class="p">();</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="k">return</span> <span class="p">(</span><span class="nb">window</span><span class="p">.</span><span class="nx">__MONACO_PROMISE__</span> <span class="o">=</span> <span class="nx">editorPromise</span><span class="p">);</span>
<span class="p">}</span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">大意是在全局 <code>window</code> 下以一个名为 <code>__MONACO_PROMISE__</code> 的promise来处理资源的加载。其内部实现了一个monaco-editor 自身资源的loader标签加载。在这个loader加载完成后,再创建一个script标签,使用monaco自身的loader去加载其必须资源(实现代码参manaco的demo)。 全部完成后,解决此promise。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">之后的调用也可以一直使用此promise,那么使用的时候直接这样就可以了:</span>
</p>
<div class="codehilite code_lang_js highlight"><pre><span></span><span class="kr">import</span> <span class="nx">loadMonaco</span> <span class="nx">from</span> <span class="s1">'../utils/load-monaco.js'</span><span class="p">;</span>
<span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
<span class="nx">mounted</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">loadMonaco</span><span class="p">().</span><span class="nx">then</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">editor</span> <span class="o">=</span> <span class="nx">__monaco_editor__</span><span class="p">.</span><span class="nx">editor</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">$refs</span><span class="p">.</span><span class="nx">codeEditor</span><span class="p">,</span> <span class="p">{</span>
<span class="nx">value</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">codeSource</span><span class="p">,</span>
<span class="nx">language</span><span class="o">:</span> <span class="s1">'html'</span><span class="p">,</span>
<span class="nx">theme</span><span class="o">:</span> <span class="s1">'vs-dark'</span><span class="p">,</span>
<span class="nx">automaticLayout</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="nx">autoIndent</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="nx">autoClosingBrackets</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="nx">acceptSuggestionOnEnter</span><span class="o">:</span> <span class="s1">'on'</span><span class="p">,</span>
<span class="nx">colorDecorators</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="nx">dragAndDrop</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="nx">formatOnPaste</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="nx">formatOnType</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="nx">mouseWheelZoom</span><span class="o">:</span> <span class="kc">true</span>
<span class="p">});</span>
<span class="p">})</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">还有一点可以优化,上面的 <strong>load-monaco.js</strong> 中指定的 <code>MONACO_PATH</code> 是 <code>./static/js/monaco-editor/min/vs</code>,而我们的 <code>monaco-editor</code> 肯定是npm安装的,源码在 node_modules,我们需要将其自动同步过来。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">同样,写一个模块单独处理此资源的拷贝:</span>
</p>
<div class="codehilite code_lang_js highlight"><pre><span></span><span class="kr">const</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">'path'</span><span class="p">)</span>
<span class="kr">const</span> <span class="nx">fs</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">'fs'</span><span class="p">)</span>
<span class="kr">const</span> <span class="nx">monacoToFolder</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">'../static/js/monaco-editor'</span><span class="p">)</span>
<span class="kr">const</span> <span class="nx">monacoFromFolder</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="s1">'../node_modules/monaco-editor'</span><span class="p">)</span>
<span class="kd">function</span> <span class="nx">mkDirExist</span><span class="p">(</span><span class="nx">path</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">fs</span><span class="p">.</span><span class="nx">existsSync</span><span class="p">(</span><span class="nx">path</span><span class="p">))</span> <span class="p">{</span>
<span class="nx">fs</span><span class="p">.</span><span class="nx">mkdirSync</span><span class="p">(</span><span class="nx">path</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">copy</span><span class="p">(</span><span class="nx">src</span><span class="p">,</span> <span class="nx">dist</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">fs</span><span class="p">.</span><span class="nx">createReadStream</span><span class="p">(</span><span class="nx">src</span><span class="p">).</span><span class="nx">pipe</span><span class="p">(</span><span class="nx">fs</span><span class="p">.</span><span class="nx">createWriteStream</span><span class="p">(</span><span class="nx">dist</span><span class="p">));</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">copyFolder</span><span class="p">(</span><span class="nx">src</span><span class="p">,</span> <span class="nx">output</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">mkDirExist</span><span class="p">(</span><span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">output</span><span class="p">));</span>
<span class="nx">src</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">src</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">temp</span> <span class="o">=</span> <span class="s1">''</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">fs</span><span class="p">.</span><span class="nx">existsSync</span><span class="p">(</span><span class="nx">src</span><span class="p">))</span> <span class="p">{</span>
<span class="nx">fs</span><span class="p">.</span><span class="nx">readdirSync</span><span class="p">(</span><span class="nx">src</span><span class="p">).</span><span class="nx">forEach</span><span class="p">((</span><span class="nx">file</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">temp</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">src</span><span class="p">,</span> <span class="nx">file</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">fs</span><span class="p">.</span><span class="nx">statSync</span><span class="p">(</span><span class="nx">temp</span><span class="p">).</span><span class="nx">isDirectory</span><span class="p">())</span> <span class="p">{</span>
<span class="nx">copyFolder</span><span class="p">(</span><span class="nx">temp</span><span class="p">,</span> <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">output</span><span class="p">,</span> <span class="nx">file</span><span class="p">))</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nx">copy</span><span class="p">(</span><span class="nx">temp</span><span class="p">,</span> <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">output</span><span class="p">,</span> <span class="nx">file</span><span class="p">))</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="c1">// 已经存在则直接成功</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">fs</span><span class="p">.</span><span class="nx">existsSync</span><span class="p">(</span><span class="nx">monacoToFolder</span><span class="p">))</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'[dev output]: monaco-editor 目录已经存在,直接开始构建!'</span><span class="p">);</span>
<span class="nx">resolve</span><span class="p">()</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="c1">// 否则进行拷贝</span>
<span class="k">try</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'[dev output]: monaco-editor 资源尚不存在,开始拷贝!'</span><span class="p">);</span>
<span class="nx">copyFolder</span><span class="p">(</span><span class="nx">monacoFromFolder</span><span class="p">,</span> <span class="nx">monacoToFolder</span><span class="p">)</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'[dev output]: monaco-editor 资源拷贝完成,开始构建!'</span><span class="p">);</span>
<span class="nx">resolve</span><span class="p">()</span>
<span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">reject</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">})</span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">修改 <code>webpack.dev.conf.js</code> ,启动dev服务时,首先处理资源,完成后再开始。而如果资源已经存在,则这个promise会直接成功,以加快构建速度(比直接配置 <code>CopyWebpackPlugin</code> 快多了)。</span>
</p>
<div class="codehilite code_lang_js highlight"><pre><span></span><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">'./copy-monaco'</span><span class="p">).</span><span class="nx">then</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span> <span class="c1">// 新增</span>
<span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="c1">// ...</span>
<span class="p">})</span>
<span class="p">})</span> <span class="c1">// 新增</span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">然后修改 <code>build/build.js</code> ,使得其在构建时也自动从node_modules 拷贝最新的代码过来:</span>
</p>
<div class="codehilite code_lang_js highlight"><pre><span></span><span class="nx">rm</span><span class="p">(</span><span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">config</span><span class="p">.</span><span class="nx">build</span><span class="p">.</span><span class="nx">assetsRoot</span><span class="p">,</span> <span class="nx">config</span><span class="p">.</span><span class="nx">build</span><span class="p">.</span><span class="nx">assetsSubDirectory</span><span class="p">),</span> <span class="nx">err</span> <span class="o">=></span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="k">throw</span> <span class="nx">err</span>
<span class="nx">require</span><span class="p">(</span><span class="s1">'./copy-monaco'</span><span class="p">).</span><span class="nx">then</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span> <span class="c1">// 新增</span>
<span class="k">return</span> <span class="nx">webpack</span><span class="p">(</span><span class="nx">webpackConfig</span><span class="p">,</span> <span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">stats</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="c1">// ... </span>
<span class="p">})</span>
<span class="p">})</span> <span class="c1">// 新增</span>
<span class="p">})</span>
</pre></div>
<!--block_code_end-->
聊聊IE动态加载资源那点事
2018-08-18T14:16:05Z
about-loading-css-in-ie
依韵
<blockquote class="blockquote_lines_1">
<p class="md_block">
<span class="md_line md_line_start md_line_end">此文所描述的场景,在现代浏览器以及项目中应该是遇不到的。所描述的问题是:老项目需要动态换肤、且存在组件资源懒加载的情况下可能遇到预设的皮肤样式无效的问题。</span>
</p>
</blockquote>
<p class="md_block">
<span class="md_line md_line_start md_line_end">在一个css文件中,如果存在相同的选择器,且内部设置了相同的css属性值,最终生效的为最后一个(属性值无 <code>important</code>),简单地可以理解为覆盖。 在不同的css文件中,这个现象依然存在,比如a、b两个css文件中都有相同的规则定义,如果b后引用(加载),则生效的为b。</span>
</p>
<div class="codehilite code_lang_css highlight"><pre><span></span><span class="c">/* a.css */</span>
<span class="nt">body</span> <span class="p">{</span>
<span class="nb">background</span><span class="o">:</span> <span class="nb">red</span><span class="p">;</span>
<span class="p">}</span>
<span class="c">/* b.css */</span>
<span class="nt">body</span> <span class="p">{</span>
<span class="nb">background</span><span class="o">:</span> <span class="nb">blue</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">比如如上的两个css文件,和以下的html中的引入片段</span>
</p>
<div class="codehilite code_lang_html highlight"><pre><span></span><span class="p"><</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">"stylesheet"</span> <span class="na">href</span><span class="o">=</span><span class="s">"./a.css"</span><span class="p">></span>
<span class="p"><</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">"stylesheet"</span> <span class="na">href</span><span class="o">=</span><span class="s">"./b.css"</span><span class="p">></span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">如无其他css的影响,最终页面的中展现的body区域应该是 蓝色 (<code>blue</code>) 。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">既然有这样的规则,那么我我们将一个css文件以 <code>link</code> 标签的形式插入到另一个已经存在的css中会是什么情况呢?代码示意如下:</span>
</p>
<div class="codehilite code_lang_html highlight"><pre><span></span><span class="cp"><!DOCTYPE html></span>
<span class="p"><</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">"zh-CN"</span><span class="p">></span>
<span class="p"><</span><span class="nt">head</span><span class="p">></span>
<span class="p"><</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">"UTF-8"</span><span class="p">></span>
<span class="p"><</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">"viewport"</span> <span class="na">content</span><span class="o">=</span><span class="s">"width=device-width, initial-scale=1.0"</span><span class="p">></span>
<span class="p"><</span><span class="nt">meta</span> <span class="na">http-equiv</span><span class="o">=</span><span class="s">"X-UA-Compatible"</span> <span class="na">content</span><span class="o">=</span><span class="s">"ie=edge"</span><span class="p">></span>
<span class="p"><</span><span class="nt">title</span><span class="p">></span>Document<span class="p"></</span><span class="nt">title</span><span class="p">></span>
<span class="p"><</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">"stylesheet"</span> <span class="na">href</span><span class="o">=</span><span class="s">"./reset.css"</span><span class="p">></span>
<span class="c"><!-- 这是一个已经预设了所有的情况的皮肤文件 --></span>
<span class="p"><</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">"stylesheet"</span> <span class="na">id</span><span class="o">=</span><span class="s">"skin-all"</span> <span class="na">href</span><span class="o">=</span><span class="s">"./skin-all.css"</span><span class="p">></span>
<span class="p"></</span><span class="nt">head</span><span class="p">></span>
<span class="p"><</span><span class="nt">body</span><span class="p">></span>
<span class="c"><!-- elements --></span>
<span class="p"><</span><span class="nt">script</span><span class="p">></span>
<span class="kd">function</span> <span class="nx">loadComponentStyle</span><span class="p">(</span><span class="nx">componentId</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">link</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s1">'link'</span><span class="p">);</span>
<span class="nx">link</span><span class="p">.</span><span class="nx">rel</span> <span class="o">=</span> <span class="s1">'stylesheet'</span><span class="p">;</span>
<span class="nx">link</span><span class="p">.</span><span class="nx">id</span> <span class="o">=</span> <span class="s1">'component-style-'</span> <span class="o">+</span> <span class="nx">componentId</span><span class="p">;</span>
<span class="nx">link</span><span class="p">.</span><span class="nx">href</span> <span class="o">=</span> <span class="s1">'xxx了路径'</span> <span class="o">+</span> <span class="nx">componentId</span> <span class="o">+</span> <span class="s1">'.css'</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">skinLink</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">'skin-all'</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">skinLink</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">skinLink</span><span class="p">.</span><span class="nx">insertAdjacentElement</span><span class="p">(</span><span class="s1">'beforebegin'</span><span class="p">,</span> <span class="nx">link</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">getElementsByTagName</span><span class="p">(</span><span class="s1">'head'</span><span class="p">)[</span><span class="mi">0</span><span class="p">].</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">link</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// 在某个特殊情况,需要动态载入某个组件,并引入其样式文件</span>
<span class="nx">loadComponentStyle</span><span class="p">(</span><span class="s1">'component-a'</span><span class="p">);</span>
<span class="p"></</span><span class="nt">script</span><span class="p">></span>
<span class="p"></</span><span class="nt">body</span><span class="p">></span>
<span class="p"></</span><span class="nt">html</span><span class="p">></span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">简单来说其使用场景就是,一个皮肤文件中已经预设了所有需要使用的皮肤样式并已经直接使用。但是一些组件使用频率不高,无需直接引入,而且这组件需要也需要应用已经预设的皮肤。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">以上代码中,陌生的可能是 <code>insertAdjacentElement</code> ,这是一个dom原生操作的api,兼容IE8。简介如下:</span>
</p>
<p class="md_block md_block_as_opening md_has_block_below md_has_block_below_ul">
<span class="md_line md_line_start md_line_end"><code>elementA.insertAdjacentElement(position, elementB);</code> 可以将 <code>elementB</code> 插入在 <code>elementA</code> 的指定位置。其中参数 <code>position</code> 的取值为以下值之一:</span>
</p>
<ul>
<li class="md_li"><span><code>beforebegin</code> : 在该元素本身的前面。
</span></li>
<li class="md_li"><span><code>afterbegin</code> :只在该元素当中, 在该元素第一个子孩子前面。
</span></li>
<li class="md_li"><span><code>beforeend</code> :只在该元素当中, 在该元素最后一个子孩子后面。
</span></li>
<li class="md_li"><span><code>afterend</code> : 在该元素本身的后面。
</span></li>
</ul>
<p class="md_block">
<span class="md_line md_line_start md_line_end">可视化位置如下:</span>
</p>
<div class="codehilite code_lang_html highlight"><pre><span></span><span class="c"><!-- beforebegin --></span>
<span class="p"><</span><span class="nt">p</span><span class="p">></span>
<span class="c"><!-- afterbegin --></span>
foo
<span class="c"><!-- beforeend --></span>
<span class="p"></</span><span class="nt">p</span><span class="p">></span>
<span class="c"><!-- afterend --></span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">所以这个需求就简单这样实现了,所利用就是前面说描述的,link引入资源的先后位置,验证一切(chrome、firefox、safari)正常,然而直到遇到了IE。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">IE下的问题是生效的是后续动态加载的组件中样式,而非预想中的皮肤样式。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">而问题的根源应该就是在IE中,将link元素动态插入到某一个之前,并不能、是的后面的优先级较高。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">排坑进行时:</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end"><strong>猜想1:IE下并非link的先后位置顺序决定,而是加载顺序决定。</strong> 即上例中的皮肤样式是直接加载的,而某组件的样式是动态引入的,先引入的皮肤样式,后引入的组将样式,因此组件样式优先级更高。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">为了验证这个猜想,只用在加载组件资源完成后,再重新加载一次皮肤样式即可。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">我们将上面的例子中的 <code>loadComponentStyle</code> 方法进行改造,在新插入的样式加载完成后重新加载皮肤样式文件:</span>
</p>
<div class="codehilite code_lang_js highlight"><pre><span></span><span class="kd">function</span> <span class="nx">loadComponentStyle</span><span class="p">(</span><span class="nx">componentId</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">link</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s1">'link'</span><span class="p">);</span>
<span class="nx">link</span><span class="p">.</span><span class="nx">rel</span> <span class="o">=</span> <span class="s1">'stylesheet'</span><span class="p">;</span>
<span class="nx">link</span><span class="p">.</span><span class="nx">id</span> <span class="o">=</span> <span class="s1">'component-style-'</span> <span class="o">+</span> <span class="nx">componentId</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">skinLink</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">'skin-all'</span><span class="p">);</span>
<span class="nx">link</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nx">link</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
<span class="c1">// 在新插入的样式加载完成后重新加载皮肤样式文件</span>
<span class="nx">reloadSkinStyle</span><span class="p">();</span>
<span class="p">}</span>
<span class="nx">link</span><span class="p">.</span><span class="nx">href</span> <span class="o">=</span> <span class="s1">'component-id-path'</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">skinLink</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">skinLink</span><span class="p">.</span><span class="nx">insertAdjacentElement</span><span class="p">(</span><span class="s1">'beforebegin'</span><span class="p">,</span> <span class="nx">link</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">getElementsByTagName</span><span class="p">(</span><span class="s1">'head'</span><span class="p">)[</span><span class="mi">0</span><span class="p">].</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">link</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">reloadSkinStyle</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">skinStyle</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">'skin-all'</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">skinStyle</span><span class="p">.</span><span class="nx">getAttribute</span><span class="p">(</span><span class="s1">'href'</span><span class="p">);</span>
<span class="nx">skinStyle</span><span class="p">.</span><span class="nx">href</span> <span class="o">=</span> <span class="s1">''</span><span class="p">;</span>
<span class="nx">setTimeout</span><span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nx">skinStyle</span><span class="p">.</span><span class="nx">href</span> <span class="o">=</span> <span class="nx">path</span><span class="p">;</span>
<span class="p">},</span> <span class="mi">17</span><span class="p">);</span>
<span class="p">}</span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">验证结果:<strong>失败,最终生效的效果依然是组件的样式。</strong></span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end"><strong>猜想2:在IE下link的引入样式文件优先级是由加入页面的位置决定的。</strong> 开始的情况,以及猜想1中验证的情况都是组件的样式后加入页面,因此优先级较高。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">我们对上面代码中的 <code>reloadSkinStyle</code> 再做改进即可:</span>
</p>
<div class="codehilite code_lang_js highlight"><pre><span></span><span class="kd">function</span> <span class="nx">reloadSkinStyle</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">skinStyle</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">'skin-all'</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">head</span> <span class="o">=</span><span class="nb">document</span><span class="p">.</span><span class="nx">getElementsByTagName</span><span class="p">(</span><span class="s1">'head'</span><span class="p">)[</span><span class="mi">0</span><span class="p">];</span>
<span class="nx">head</span><span class="p">.</span><span class="nx">removeChild</span><span class="p">(</span><span class="nx">skinStyle</span><span class="p">);</span>
<span class="nx">head</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">skinStyle</span><span class="p">);</span>
<span class="p">}</span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">验证结果:<strong>通过,通过将skin移除后再重新放入,优先级变高。</strong></span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">因此,大概估计IE与众不同,优先级的决定并非位置顺序或者是加载顺序,而是相应的link标签引入dom树的先后顺序。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">至此,问题原因已经基本确定,但是猜想中的验证代码并不能作为解决方案。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">那么IE下这样的需求要如何处理呢?最终应用的方案是下面这样的:</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">利用css原生的 <code>@import</code> 。预先在页面的皮肤link之前埋入一个空的style标签。然后动态加载组件样式时,通过 <code>@import component-style-path</code> 的形式在这个style中引入组件样式。代码示意如下:</span>
</p>
<div class="codehilite code_lang_html highlight"><pre><span></span><span class="cp"><!DOCTYPE html></span>
<span class="p"><</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">"zh-CN"</span><span class="p">></span>
<span class="p"><</span><span class="nt">head</span><span class="p">></span>
<span class="p"><</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">"UTF-8"</span><span class="p">></span>
<span class="p"><</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">"viewport"</span> <span class="na">content</span><span class="o">=</span><span class="s">"width=device-width, initial-scale=1.0"</span><span class="p">></span>
<span class="p"><</span><span class="nt">meta</span> <span class="na">http-equiv</span><span class="o">=</span><span class="s">"X-UA-Compatible"</span> <span class="na">content</span><span class="o">=</span><span class="s">"ie=edge"</span><span class="p">></span>
<span class="p"><</span><span class="nt">title</span><span class="p">></span>Document<span class="p"></</span><span class="nt">title</span><span class="p">></span>
<span class="p"><</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">"stylesheet"</span> <span class="na">href</span><span class="o">=</span><span class="s">"./reset.css"</span><span class="p">></span>
<span class="c"><!-- 预先放置一个style --></span>
<span class="p"><</span><span class="nt">style</span> <span class="na">id</span><span class="o">=</span><span class="s">"lazy-load-style"</span><span class="p">></</span><span class="nt">style</span><span class="p">></span>
<span class="c"><!-- 这是一个已经预设了所有的情况的皮肤文件 --></span>
<span class="p"><</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">"stylesheet"</span> <span class="na">id</span><span class="o">=</span><span class="s">"skin-all"</span> <span class="na">href</span><span class="o">=</span><span class="s">"./skin-all.css"</span><span class="p">></span>
<span class="p"></</span><span class="nt">head</span><span class="p">></span>
<span class="p"><</span><span class="nt">body</span><span class="p">></span>
<span class="c"><!-- elements --></span>
<span class="p"><</span><span class="nt">script</span><span class="p">></span>
<span class="kd">function</span> <span class="nx">loadComponentStyle</span><span class="p">(</span><span class="nx">componentId</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">link</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s1">'link'</span><span class="p">);</span>
<span class="nx">link</span><span class="p">.</span><span class="nx">rel</span> <span class="o">=</span> <span class="s1">'stylesheet'</span><span class="p">;</span>
<span class="nx">link</span><span class="p">.</span><span class="nx">id</span> <span class="o">=</span> <span class="s1">'component-style-'</span> <span class="o">+</span> <span class="nx">componentId</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">style</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">'skin-all'</span><span class="p">);</span>
<span class="c1">// ie-8 下不支持style的innerText操作</span>
<span class="k">if</span> <span class="p">(</span><span class="s1">'styleSheet'</span> <span class="k">in</span> <span class="nx">style</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">style</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'type'</span><span class="p">,</span> <span class="s1">'text/css'</span><span class="p">);</span>
<span class="nx">style</span><span class="p">.</span><span class="nx">styleSheet</span><span class="p">.</span><span class="nx">cssText</span> <span class="o">+=</span> <span class="nx">style</span><span class="p">.</span><span class="nx">styleSheet</span><span class="p">.</span><span class="nx">cssText</span> <span class="o">+</span> <span class="s1">'@import "component-id-path";'</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nx">style</span><span class="p">.</span><span class="nx">innerText</span> <span class="o">+=</span> <span class="nx">style</span><span class="p">.</span><span class="nx">innerText</span> <span class="o">+</span> <span class="s1">'@import "component-id-path";'</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// 在某个特殊情况,需要动态载入某个组件,并引入其样式文件</span>
<span class="nx">loadComponentStyle</span><span class="p">(</span><span class="s1">'component-a'</span><span class="p">);</span>
<span class="p"></</span><span class="nt">script</span><span class="p">></span>
<span class="p"></</span><span class="nt">body</span><span class="p">></span>
<span class="p"></</span><span class="nt">html</span><span class="p">></span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">这个方案基本可以覆盖解决前面的问题,只在IE下使用此方案,其他浏览器仍然走正常的link插入到皮肤样式之前即可。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">只是这种方案在IE下还有一个坑,那就是每次有新的组件中动态加载,即每次对这个占位的style的内容进行修改时,都会涉及整个style便签引入的所有css资源的重新加载(其他浏览器则不会)。唉,与众不同的IE,就只能这样着吧。</span>
</p>
请求速度快的时候出现loading,而较快时不出现
2018-07-25T16:37:20Z
show-loading-when-slow
依韵
<h1 id="toc_0" class="h16">请求速度快的时候出现loading,而较快时不出现</h1>
<p class="md_block">
<span class="md_line md_line_start md_line_end">这是两年前我刚入职不久后收到的一个需求,恰逢近日有人问起,特此记录分享。</span>
</p>
<h2 id="toc_1" class="h16">需求场景描述</h2>
<p class="md_block">
<span class="md_line md_line_start md_line_end">通常在发起请求加载数据时,会在页面上出现 loading 的动画,让用户知道正在加载数据,而不是网站出了什么问题,反正是挺常见且常用的手段。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">通常的 loading 效果都是有一些动画的,如构造近似的进度条、转圈圈等,通常这个loading效果在最终移除的时候会有一个渐隐的效果,这就导致了一个“弊端” —— 给用户看起来,加了 loading 效果会比有没有的情况下慢。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">当然作为开发者的我们都知道,这个实际并没有影响,但是不得不承认就肉眼来看,在请求相对较快的情况下,出现loading的视觉效果确实比没有的情况下慢。慢一些的情况下,当然还是有 loading 更加人性化。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">就这样一个需求诞生了 —— 在请求快的情况下出现loading,而较快的情况下不会出现loading。</span>
</p>
<h2 id="toc_2" class="h16">实现过程</h2>
<p class="md_block">
<span class="md_line md_line_start md_line_end">当时刚入前端,基本还是小白状态。 收到这个需求,心里简直是万马奔腾呀。这不是异想天开吗?我怎么能在请求发起前就知道这个请求需要多久,这<strong>完全</strong>、<strong>根本<strong>、</strong>绝对</strong>不可能嘛。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">P.S. 当时还处于小白状态,不知道可以寻求万能网络的帮助。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">但是需求是领导提的,不是产品,不是客户,没有怼回去的可能。硬着头皮,只能是做。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">嗯? js 里面不是有个 <code>setTimeout</code> 的东西吗,这货可以让一段代码在指定时间之后进行,那我直接让在请求前迟延一段时间出现遮罩不就行了吗?</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">实现代码非常简单,如下:</span>
</p>
<div class="codehilite code_lang_js highlight"><pre><span></span><span class="kd">var</span> <span class="nx">loadingTimer</span> <span class="o">=</span> <span class="nx">setTimeout</span><span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nx">epoint</span><span class="p">.</span><span class="nx">showLoading</span><span class="p">();</span>
<span class="p">},</span> <span class="mi">200</span><span class="p">);</span>
<span class="nx">$</span><span class="p">.</span><span class="nx">ajax</span><span class="p">({</span>
<span class="nx">url</span><span class="o">:</span> <span class="s1">'abc.xyz'</span>
<span class="p">}).</span><span class="nx">always</span><span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nx">clearTimeout</span><span class="p">(</span><span class="nx">loadingTimer</span><span class="p">);</span>
<span class="nx">epoint</span><span class="p">.</span><span class="nx">hideLoading</span><span class="p">();</span>
<span class="p">});</span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">简单到不可思议吧? 请求发起前使用定时器迟延一段时间出现 loading ,在请求完成时清除定时器,并隐藏 loading 。简单明了,效果达成。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">这个需求就这么完成了,但是自己心里一直觉得这需求不合理,实现也根本是凑出来的,并非正途。(这里其实有点问题,后文会提到)</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">直到去年在阿里D2大会上,看到淘宝也用这种方案,请求快的时候没有 loading ,而较慢时才会出现 loading 。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">这么看来:需求不是合理呀,还是要相信——存在即合理呀。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">还有也不宜“妄自菲薄,引喻失义” 呀,毕竟自己想出来的东西(或许并非首创,但当时确实是自己想出来的,没有其他参考)也和大厂采用的方案相吻合。</span>
</p>
<h2 id="toc_3" class="h16">弊端和注意问题</h2>
<p class="md_block">
<span class="md_line md_line_start md_line_end">虽然(我的)这种方案和大厂的如出一辙,但是不得不承认,其毕竟是投机做法。原谅我的个人偏见,总觉得使用 <code>setTimeout</code> 来凑出各种效果都不是正途。包括我们还采用的一种情况:按钮上既有单机事件,又有双击事件,但是要求双击时不能触发单击的处理函数,方案则是单击事件中执行的代码使用 <code>setTimeout</code> 处理,双击事件中清除单击事件中的定时器再执行自身的代码。虽然效果完美实现,但 我 仍 然 觉 得 —— <strong>这并非正途</strong>。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">投机自然是有代价的,这里也不例外。有一个很明显的副作用。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">注意到上面代码中迟延出现遮罩的时间了吗? 我给出的示例代码中就是那个 <code>200</code> 。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">设想以下情况:如果绝大多数请求的响应时间都分布在 200ms 左右会出现什么情况? 我替你描述吧~ loading 很晚才出来,而且一出来马上就没了,不管有没有渐出或者渐隐的动画,如果出现这种情况,效果毕竟大打折扣,还不如直接有或者直接没有呢。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">所以此处问题的核心在于,关于请求快慢的时间阈值的界定。我厂所使用的快慢阈值是 <code>200ms</code> ,而淘宝毕竟大厂,优化好,阈值是 <code>100ms</code> 。 这个时间阈值决不可照搬硬套,请务必统计使用场景下请求的响应时间,务必避开时间集中的值。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">当然不管怎么调整这个请求快慢的阈值都不可能完全避免那个 loading 可能为一闪而逝的情况,或许这就是投机上注定要承担的风险吧。但是选定合理的阈值,即可提高体验、又能尽可能地减低不良情况的出现概率,何乐而不为呢?</span>
</p>
c++学习笔记 const 和 function
2018-02-13T11:24:13Z
the-const-and-function-of-cpp
依韵
<h2 id="toc_0" class="h16">const</h2><div class="codehilite code_lang_c++ highlight"><pre><span></span><span class="c1">// const.cpp: 定义控制台应用程序的入口点。</span>
<span class="c1">//</span>
<span class="cp">#include</span> <span class="cpf">"stdafx.h"</span><span class="cp"></span>
<span class="cp">#include</span> <span class="cpf"><iostream></span><span class="cp"></span>
<span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>
<span class="kt">void</span> <span class="nf">output1</span><span class="p">(</span><span class="kt">int</span> <span class="n">a</span><span class="p">,</span> <span class="kt">int</span> <span class="n">b</span><span class="p">);</span>
<span class="kt">void</span> <span class="nf">output2</span><span class="p">(</span><span class="k">const</span> <span class="kt">int</span> <span class="o">&</span><span class="n">a</span><span class="p">,</span> <span class="k">const</span> <span class="kt">int</span> <span class="o">&</span><span class="n">b</span><span class="p">);</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
<span class="p">{</span>
<span class="c1">// 1 const base</span>
<span class="kt">int</span> <span class="k">const</span> <span class="n">a</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="k">const</span> <span class="kt">int</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="c1">// a = 11; // 禁止修改 </span>
<span class="c1">// b = 22; // 禁止修改</span>
<span class="c1">// ===========================================</span>
<span class="c1">// 2 const pointer</span>
<span class="kt">int</span> <span class="n">c</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> <span class="c1">// 变量x</span>
<span class="k">const</span> <span class="kt">int</span> <span class="n">d</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span>
<span class="k">const</span> <span class="kt">int</span> <span class="o">*</span><span class="n">p1</span> <span class="o">=</span> <span class="o">&</span><span class="n">c</span><span class="p">;</span> <span class="c1">// 指针指向x 指针禁止修改 但指针指向可变</span>
<span class="c1">// *p1 = 33; // 禁止修改 </span>
<span class="n">p1</span> <span class="o">=</span> <span class="o">&</span><span class="n">d</span><span class="p">;</span> <span class="c1">// 指针p1不可更改对应的值,但是指针指向可变</span>
<span class="n">cout</span> <span class="o"><<</span> <span class="s">"修改后*p1 "</span> <span class="o"><<</span> <span class="o">*</span><span class="n">p1</span> <span class="o"><<</span> <span class="n">endl</span><span class="p">;</span>
<span class="kt">int</span> <span class="o">*</span><span class="k">const</span> <span class="n">p2</span> <span class="o">=</span> <span class="o">&</span><span class="n">c</span><span class="p">;</span>
<span class="o">*</span><span class="n">p2</span> <span class="o">=</span> <span class="mi">33</span><span class="p">;</span>
<span class="n">cout</span> <span class="o"><<</span> <span class="s">"*p2修改c "</span> <span class="o"><<</span> <span class="n">c</span> <span class="o"><<</span> <span class="n">endl</span><span class="p">;</span>
<span class="c1">//p2 = &d; // 禁止修改</span>
<span class="c1">// 总结:</span>
<span class="c1">// const 修饰的是距离const最近的</span>
<span class="c1">// const int *p1 = &c; 此时 const 修饰 *p1 禁止指针修改值 即指针为只读指针 但是指针可以重新指向</span>
<span class="c1">// int *const p2 = &c; 此时 const 修饰 p2 禁止指针指向修改 指针本身可读可写</span>
<span class="k">const</span> <span class="kt">int</span> <span class="n">e</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
<span class="c1">// int *p3 = &e;</span>
<span class="c1">// 上一行禁止 原因: e为常量,但是指针指向e,指针可读可写,存在矛盾风险,禁止</span>
<span class="k">const</span> <span class="kt">int</span> <span class="o">*</span><span class="n">p3</span> <span class="o">=</span> <span class="o">&</span><span class="n">e</span><span class="p">;</span>
<span class="c1">// 上一行则允许,因为指针p3只可读 并不可写</span>
<span class="n">cout</span> <span class="o"><<</span> <span class="s">"指针p3 "</span> <span class="o"><<</span> <span class="o">*</span><span class="n">p3</span> <span class="o"><<</span> <span class="n">endl</span><span class="p">;</span>
<span class="c1">// int *const p4 = &e; </span>
<span class="c1">// 上行依然禁止,原因同上,此时不可变的仅仅是指针p4的指向 并非指针p3本身</span>
<span class="c1">// 但是p3仍然存在一个风险 即p3指向可变,如:</span>
<span class="n">p3</span> <span class="o">=</span> <span class="o">&</span><span class="n">c</span><span class="p">;</span>
<span class="n">cout</span> <span class="o"><<</span> <span class="s">"*p3指针指向被修改 "</span> <span class="o"><<</span> <span class="o">*</span><span class="n">p3</span> <span class="o"><<</span> <span class="n">endl</span><span class="p">;</span>
<span class="c1">// 因此可以同时是的使得指针和指向都不可变</span>
<span class="k">const</span> <span class="kt">int</span> <span class="o">*</span><span class="k">const</span> <span class="n">p5</span> <span class="o">=</span> <span class="o">&</span><span class="n">e</span><span class="p">;</span>
<span class="c1">// *p5 = 55; // 禁止</span>
<span class="c1">// p5 = &c; // 禁止</span>
<span class="c1">// ==============================</span>
<span class="c1">// 3 const reference</span>
<span class="kt">int</span> <span class="n">m</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">n</span> <span class="o">=</span> <span class="mi">100</span><span class="p">;</span>
<span class="n">output1</span><span class="p">(</span><span class="n">m</span><span class="p">,</span> <span class="n">n</span><span class="p">);</span>
<span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">100</span><span class="p">;</span>
<span class="n">output2</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">);</span>
<span class="n">system</span><span class="p">(</span><span class="s">"pause"</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">output1</span><span class="p">(</span><span class="kt">int</span> <span class="n">a</span><span class="p">,</span> <span class="kt">int</span> <span class="n">b</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 此处意外地修改了传入的参数</span>
<span class="n">a</span> <span class="o">*=</span> <span class="n">a</span><span class="p">;</span>
<span class="n">b</span> <span class="o">*=</span> <span class="n">b</span><span class="p">;</span>
<span class="n">cout</span> <span class="o"><<</span> <span class="n">a</span> <span class="o"><<</span> <span class="s">","</span> <span class="o"><<</span> <span class="n">b</span> <span class="o"><<</span> <span class="n">endl</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">output2</span><span class="p">(</span><span class="k">const</span> <span class="kt">int</span> <span class="o">&</span><span class="n">a</span><span class="p">,</span> <span class="k">const</span> <span class="kt">int</span> <span class="o">&</span><span class="n">b</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 这时候传入的参数是对实参的静态引用</span>
<span class="c1">// 以下的两行修改则会直接报错</span>
<span class="c1">// a *= a;</span>
<span class="c1">// b *= b;</span>
<span class="n">cout</span> <span class="o"><<</span> <span class="n">a</span> <span class="o"><<</span> <span class="s">","</span> <span class="o"><<</span> <span class="n">b</span> <span class="o"><<</span> <span class="n">endl</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<!--block_code_end--><h2 id="toc_1" class="h16">function</h2><h3 id="toc_2" class="h16">函数默认值</h3>
<p class="md_block">
<span class="md_line md_line_start md_line_end">c++中函数支持默认值,但是有一个规则限制,即:若存在默认值,则有默认值的参数必须在最右端。</span>
</p>
<div class="codehilite code_lang_c++ highlight"><pre><span></span><span class="kt">void</span> <span class="nf">fun</span><span class="p">(</span><span class="kt">int</span> <span class="n">a</span><span class="p">,</span> <span class="kt">int</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">2</span><span class="p">,</span> <span class="kt">int</span> <span class="n">c</span><span class="p">);</span> <span class="c1">// 错误,默认实参不在形参列表的结尾</span>
<span class="c1">// 正确的应为:</span>
<span class="kt">void</span> <span class="nf">fun</span><span class="p">(</span><span class="kt">int</span> <span class="n">a</span><span class="p">,</span> <span class="kt">int</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">2</span><span class="p">,</span> <span class="kt">int</span> <span class="n">c</span> <span class="o">=</span> <span class="mi">3</span><span class="p">);</span>
<span class="c1">// 可以这么理解,如果给一个参数指定了默认值</span>
<span class="c1">// 则必须给这个参数之后的每个参数都加入默认值。</span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">另外推荐,在函数声明时加入默认值,而在定义的时候不要。</span>
</p>
<h3 id="toc_3" class="h16">重载</h3>
<p class="md_block md_has_block_below md_has_block_below_ol">
<span class="md_line md_line_start md_line_end">老概念了,三个要点</span>
</p>
<ol>
<li class="md_li"><span>同一个作用域
</span></li>
<li class="md_li"><span>同名函数
</span></li>
<li class="md_li"><span>形参的个数不同或者某个参数的类型不同
</span></li>
</ol>
<h3 id="toc_4" class="h16">内联函数</h3>
<p class="md_block">
<span class="md_line md_line_start">内联函数用 <code>inline</code> 关键字修饰,内联函数在编译后调用时和普通函数有所不同。<br /></span>
<span class="md_line md_line_end">没有寻找函数、返回结果的步骤,取而代之的是在编译时,直接使用实参替换,并执行内联函数中的代码。</span>
</p>
<p class="md_block">
<span class="md_line md_line_dom_embed md_line_with_image md_line_start md_line_end"><img class="md_compiled " src="https://qiniu.cdswyda.com/images/201802/inline-function.png" alt="http://qiniu.cdswyda.com/images/201802/inline-function.png" title="" ></span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">这样的特性使得内联函数拥有较高的效率,尤其是在循环中需要重复多次使用的情景下。</span>
</p>
<p class="md_block md_block_as_opening md_has_block_below md_has_block_below_ul">
<span class="md_line md_line_start md_line_end">但是内联函数并非在任和情况下都可以使用,有如下限制:</span>
</p>
<ul>
<li class="md_li"><span>内联编译是建议性质的,具体还是由编译器决定
</span></li>
<li class="md_li"><span>只是用于逻辑简单的情景,不能包含 <code>for</code> 、 <code>while</code> 循环等复杂的结构。
</span></li>
<li class="md_li"><span>递归函数无法使用内联方式
</span></li>
</ul>
<div class="codehilite code_lang_c++ highlight"><pre><span></span><span class="c1">// inline</span>
<span class="kr">inline</span> <span class="kt">double</span> <span class="nf">f1</span><span class="p">(</span><span class="kt">double</span> <span class="n">a</span><span class="p">,</span> <span class="kt">double</span> <span class="n">b</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">double</span> <span class="nf">f2</span><span class="p">(</span><span class="kt">double</span> <span class="n">a</span><span class="p">,</span> <span class="kt">double</span> <span class="n">b</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">int</span> <span class="nf">main</span> <span class="p">()</span> <span class="p">{</span>
<span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">const</span> <span class="kt">double</span> <span class="n">pi</span> <span class="o">=</span> <span class="mf">3.141592652</span><span class="p">;</span>
<span class="kt">clock_t</span> <span class="n">t1</span> <span class="o">=</span> <span class="n">clock</span><span class="p">();</span>
<span class="k">do</span> <span class="p">{</span>
<span class="n">f2</span><span class="p">(</span><span class="n">pi</span><span class="p">,</span> <span class="n">pi</span><span class="p">);</span>
<span class="p">}</span> <span class="k">while</span> <span class="p">(</span><span class="n">i</span><span class="o">++</span> <span class="o"><</span> <span class="mi">100000000</span><span class="p">);</span>
<span class="n">cout</span> <span class="o"><<</span> <span class="s">"normal "</span> <span class="o"><<</span> <span class="p">(</span><span class="n">clock</span><span class="p">()</span> <span class="o">-</span> <span class="n">t1</span><span class="p">)</span> <span class="o">*</span> <span class="mf">1.0</span> <span class="o">/</span> <span class="n">CLOCKS_PER_SEC</span> <span class="o">*</span> <span class="mi">1000</span> <span class="o"><<</span> <span class="n">endl</span><span class="p">;</span>
<span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="kt">clock_t</span> <span class="n">t2</span> <span class="o">=</span> <span class="n">clock</span><span class="p">();</span>
<span class="k">do</span> <span class="p">{</span>
<span class="n">f1</span><span class="p">(</span><span class="n">pi</span><span class="p">,</span> <span class="n">pi</span><span class="p">);</span>
<span class="p">}</span> <span class="k">while</span> <span class="p">(</span><span class="n">i</span><span class="o">++</span> <span class="o"><</span> <span class="mi">100000000</span><span class="p">);</span>
<span class="n">cout</span> <span class="o"><<</span> <span class="s">"inline "</span> <span class="o"><<</span> <span class="p">(</span><span class="n">clock</span><span class="p">()</span> <span class="o">-</span> <span class="n">t2</span><span class="p">)</span> <span class="o">*</span> <span class="mf">1.0</span> <span class="o">/</span> <span class="n">CLOCKS_PER_SEC</span> <span class="o">*</span> <span class="mi">1000</span> <span class="o"><<</span> <span class="n">endl</span><span class="p">;</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">代码见 <a class="md_compiled" href="https://github.com/cdswyda/cpp-base/blob/master/function/function.cpp">https://github.com/cdswyda/cpp-base/blob/master/function/function.cpp</a></span>
</p>
带你开发一个二维周视图日历
2017-12-10T14:52:23Z
javascript/2017/2017-12-08-two-dimension-week-calendar
依韵
<p class="md_block">
<span class="md_line md_line_start md_line_end">即之前实现了一个月视图日历,我们今天来实现一个二维周视图的日历。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">以下进行分析其中的关键部分。</span>
</p>
<h2 id="toc_0" class="h16">结构准备</h2>
<p class="md_block">
<span class="md_line md_line_start md_line_end">不同之处在于其在日历的基础上还有一个分类轴,用于展示不同的类目,主要用于一周内的日程安排、会议安排等。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">二维则和之前单独的有所不同,二维日历再切换日期时不用全部重新渲染,分类是不用变的,仅仅改变显示的日期即可。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">而且由于是二维的,插入的内容必定是同时属于一个分类和一个时间段的,内容肯定是可以跨越时间(即日期轴)的,因此不能直接将插入的内容像开始的日历一样直接放置在日历的格子中。而要进行单独的处理。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">另外,只要分类不变,日期和分类构成的网格是不用重绘的。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">考虑到以上情况,插入内容的和网格是需要分开来的,我将现成的日历弄成一下3D效果示意:</span>
</p>
<p class="md_block">
<span class="md_line md_line_dom_embed md_line_with_image md_line_start md_line_end"><img class="md_compiled " src="https://qiniu.cdswyda.com/images/201712/week-calendar.png" alt="" title="" ></span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">即插入内容的层是单独放置在时间和分类构成的网格上方的。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">基于以上分析,先构建如下基本结构:</span>
</p>
<table class="codehilite code_lang_html with_lines highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36</pre></div></td><td class="code"><div class="codehilite code_lang_html with_lines highlight"><pre><span></span><span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"ep-weekcalendar border"</span><span class="p">></span>
<span class="c"><!-- 头部 --></span>
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"ep-weekcalendar-header"</span><span class="p">></span>
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"ep-weekcalendar-header-left"</span><span class="p">></</span><span class="nt">div</span><span class="p">></span>
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"ep-weekcalendar-header-center"</span><span class="p">></span>
<span class="p"><</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">"ep-weekcalendar-header-btn ep-weekcalendar-header-btn-prev"</span><span class="p">></</span><span class="nt">span</span><span class="p">></span>
<span class="p"><</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">"ep-weekcalendar-title"</span><span class="p">></span>2017年12月04日 - 10日<span class="p"></</span><span class="nt">span</span><span class="p">></span>
<span class="p"><</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">"ep-weekcalendar-header-btn ep-weekcalendar-header-btn-next"</span><span class="p">></</span><span class="nt">span</span><span class="p">></span>
<span class="p"></</span><span class="nt">div</span><span class="p">></span>
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"ep-weekcalendar-header-right"</span><span class="p">></</span><span class="nt">div</span><span class="p">></span>
<span class="p"></</span><span class="nt">div</span><span class="p">></span>
<span class="c"><!-- 主体 --></span>
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"ep-weekcalendar-body"</span><span class="p">></span>
<span class="c"><!-- 分类区域 --></span>
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"ep-weekcalendar-category-area"</span><span class="p">></span>
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"ep-weekcalendar-category-header"</span><span class="p">></span>
<span class="p"><</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">"ep-weekcalendar-category-title"</span><span class="p">></span>车辆<span class="p"></</span><span class="nt">span</span><span class="p">></span>
<span class="p"></</span><span class="nt">div</span><span class="p">></span>
<span class="p"><</span><span class="nt">ul</span> <span class="na">class</span><span class="o">=</span><span class="s">"ep-weekcalendar-category-list"</span><span class="p">></span>
<span class="p"></</span><span class="nt">ul</span><span class="p">></span>
<span class="p"></</span><span class="nt">div</span><span class="p">></span>
<span class="c"><!-- 内容区域 --></span>
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"ep-weekcalendar-time-area"</span><span class="p">></span>
<span class="c"><!-- 每周日期渲染区域。切换日期时重新绘制内容 --></span>
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"ep-weekcalendar-weeks"</span><span class="p">></</span><span class="nt">div</span><span class="p">></span>
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"ep-weekcalendar-main"</span><span class="p">></span>
<span class="c"><!-- 分类和内容构建的网格区域,仅在分类改变时进行调整 --></span>
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"ep-weekcalendar-grid"</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span>
<span class="c"><!-- 可插入任意内容的区域,日期切换时清空,根据使用需求插入内容 --></span>
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"ep-weekcalendar-content"</span><span class="p">></</span><span class="nt">div</span><span class="p">></span>
<span class="p"></</span><span class="nt">div</span><span class="p">></span>
<span class="p"></</span><span class="nt">div</span><span class="p">></span>
<span class="p"></</span><span class="nt">div</span><span class="p">></span>
<span class="c"><!-- 底部 --></span>
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"ep-weekcalendar-footer"</span><span class="p">></</span><span class="nt">div</span><span class="p">></span>
<span class="p"></</span><span class="nt">div</span><span class="p">></span>
</pre></div>
</td></tr></table>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">结构如上,实现代码就不用展示了。</span>
</p>
<h2 id="toc_1" class="h16">绘制实现</h2>
<p class="md_block">
<span class="md_line md_line_start md_line_end">初始好了必要的结构,我们接着进行日历的绘制工作。</span>
</p>
<h3 id="toc_2" class="h16">分类绘制</h3>
<p class="md_block">
<span class="md_line md_line_start md_line_end">首先要处理的是分类,周视图中,一周的天数是固定的,确定好分类才能绘制出主体部分的网格。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">对于分类,暂时考虑如下必要数据格式:</span>
</p>
<table class="codehilite code_lang_js with_lines highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4
5</pre></div></td><td class="code"><div class="codehilite code_lang_js with_lines highlight"><pre><span></span><span class="p">{</span>
<span class="nx">id</span><span class="o">:</span> <span class="s1">'cate-1'</span><span class="p">,</span> <span class="c1">// 分类ID</span>
<span class="nx">name</span><span class="o">:</span> <span class="s1">'法拉利'</span><span class="p">,</span> <span class="c1">// 分类名称</span>
<span class="nx">content</span><span class="o">:</span> <span class="s1">'苏E00000'</span> <span class="c1">// 分类的具体描述</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">实现如下:</span>
</p>
<table class="codehilite code_lang_js with_lines highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65</pre></div></td><td class="code"><div class="codehilite code_lang_js with_lines highlight"><pre><span></span><span class="p">{</span>
<span class="c1">// 设置分类数据</span>
<span class="nx">setCategory</span><span class="o">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="nx">data</span> <span class="k">instanceof</span> <span class="nb">Array</span><span class="p">))</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">throwError</span><span class="p">(</span><span class="s1">'分类数据必须是一个数组'</span><span class="p">);</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_categoryData</span> <span class="o">=</span> <span class="nx">data</span><span class="p">;</span>
<span class="c1">// 绘制分类</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_renderCatagories</span><span class="p">();</span>
<span class="c1">// 绘制其他需要改变的部分</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_renderChanged</span><span class="p">();</span>
<span class="p">},</span>
<span class="c1">// 左侧分类渲染</span>
<span class="nx">_renderCatagories</span><span class="o">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_categoryListEl</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="s1">''</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>
<span class="nx">data</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">_categoryData</span><span class="p">,</span>
<span class="nx">node</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s1">'li'</span><span class="p">),</span>
<span class="nx">cataEl</span><span class="p">;</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">className</span> <span class="o">=</span> <span class="s1">'ep-weekcalendar-category'</span><span class="p">;</span>
<span class="c1">// 用行作为下标记录当前分类id集合</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_categoryIndexs</span> <span class="o">=</span> <span class="p">[];</span>
<span class="c1">// id为键记录索引</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_categoryReocrds</span> <span class="o">=</span> <span class="p">{};</span>
<span class="k">while</span> <span class="p">(</span><span class="nx">i</span> <span class="o"><</span> <span class="nx">data</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_categoryIndexs</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">data</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">id</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_categoryReocrds</span><span class="p">[</span><span class="nx">data</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">id</span><span class="p">]</span> <span class="o">=</span> <span class="nx">i</span><span class="p">;</span>
<span class="nx">cataEl</span> <span class="o">=</span> <span class="nx">node</span><span class="p">.</span><span class="nx">cloneNode</span><span class="p">(</span><span class="kc">true</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_rendercategory</span><span class="p">(</span><span class="nx">data</span><span class="p">[</span><span class="nx">i</span><span class="p">],</span> <span class="nx">cataEl</span><span class="p">);</span>
<span class="nx">i</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">},</span>
<span class="nx">_rendercategory</span><span class="o">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">cate</span><span class="p">,</span> <span class="nx">cateEl</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">cateEl</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'data-cateid'</span><span class="p">,</span> <span class="nx">cate</span><span class="p">.</span><span class="nx">id</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">titleEl</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s1">'span'</span><span class="p">),</span>
<span class="nx">contentEl</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s1">'span'</span><span class="p">);</span>
<span class="nx">titleEl</span><span class="p">.</span><span class="nx">className</span> <span class="o">=</span> <span class="s1">'title'</span><span class="p">;</span>
<span class="nx">contentEl</span><span class="p">.</span><span class="nx">className</span> <span class="o">=</span> <span class="s1">'content'</span><span class="p">;</span>
<span class="nx">titleEl</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="nx">cate</span><span class="p">.</span><span class="nx">name</span><span class="p">;</span>
<span class="nx">contentEl</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="nx">cate</span><span class="p">.</span><span class="nx">content</span><span class="p">;</span>
<span class="nx">cateEl</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">titleEl</span><span class="p">);</span>
<span class="nx">cateEl</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">contentEl</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">fire</span><span class="p">(</span><span class="s1">'categoryRender'</span><span class="p">,</span> <span class="p">{</span>
<span class="nx">categoryEl</span><span class="o">:</span> <span class="nx">cateEl</span><span class="p">,</span>
<span class="nx">titleEl</span><span class="o">:</span> <span class="nx">titleEl</span><span class="p">,</span>
<span class="nx">contentEl</span><span class="o">:</span> <span class="nx">contentEl</span>
<span class="p">});</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_categoryListEl</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">cateEl</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">fire</span><span class="p">(</span><span class="s1">'agterCategoryRender'</span><span class="p">,</span> <span class="p">{</span>
<span class="nx">categoryEl</span><span class="o">:</span> <span class="nx">cateEl</span><span class="p">,</span>
<span class="nx">titleEl</span><span class="o">:</span> <span class="nx">titleEl</span><span class="p">,</span>
<span class="nx">contentEl</span><span class="o">:</span> <span class="nx">contentEl</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">上面通过设置分类数据 <code>setCategory</code> 作为入口,调用绘制分类方法,其中还调用了 <code>_renderChanged</code> 此方法用于重新绘制日历的可变部分,如标题、日期和其中的内容,会在之后进行介绍。</span>
</p>
<h3 id="toc_3" class="h16">日期绘制</h3>
<p class="md_block">
<span class="md_line md_line_start md_line_end">上面已经准备好了分类轴,还需要绘制出日期轴,对于周视图而言,一周的实现就非常简单了,根据一周的开始日期,依次渲染7天即可。 注意在绘制过程中提供日期的必要信息给相应事件,一遍使用者能够在事件中进行个性化处理。</span>
</p>
<table class="codehilite code_lang_js with_lines highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77</pre></div></td><td class="code"><div class="codehilite code_lang_js with_lines highlight"><pre><span></span><span class="p">{</span>
<span class="c1">// 渲染日历的星期</span>
<span class="nx">_renderWeeks</span><span class="o">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_weeksEl</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="s1">''</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>
<span class="nx">currDate</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">_startDate</span><span class="p">.</span><span class="nx">clone</span><span class="p">(),</span>
<span class="nx">node</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s1">'div'</span><span class="p">),</span>
<span class="nx">week</span><span class="p">;</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">className</span> <span class="o">=</span> <span class="s1">'ep-weekcalendar-week'</span><span class="p">;</span>
<span class="c1">// 单元格列作为下标记录日期</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_dateRecords</span> <span class="o">=</span> <span class="p">[];</span>
<span class="k">while</span> <span class="p">(</span><span class="nx">i</span><span class="o">++</span> <span class="o"><</span> <span class="mi">7</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 更新记录日期</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_dateRecords</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">currDate</span><span class="p">.</span><span class="nx">clone</span><span class="p">());</span>
<span class="nx">week</span> <span class="o">=</span> <span class="nx">node</span><span class="p">.</span><span class="nx">cloneNode</span><span class="p">(</span><span class="kc">true</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_renderWeek</span><span class="p">(</span><span class="nx">currDate</span><span class="p">,</span> <span class="nx">week</span><span class="p">);</span>
<span class="nx">currDate</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">'day'</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// 切换日期 需要重绘内容区域</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_rednerContent</span><span class="p">();</span>
<span class="p">},</span>
<span class="nx">_renderWeek</span><span class="o">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">date</span><span class="p">,</span> <span class="nx">node</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">dateText</span> <span class="o">=</span> <span class="nx">date</span><span class="p">.</span><span class="nx">format</span><span class="p">(</span><span class="s1">'YYYY-MM-DD'</span><span class="p">),</span>
<span class="nx">day</span> <span class="o">=</span> <span class="nx">date</span><span class="p">.</span><span class="nx">isoWeekday</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">day</span> <span class="o">></span> <span class="mi">5</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">className</span> <span class="o">+=</span> <span class="s1">' weekend'</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">date</span><span class="p">.</span><span class="nx">isSame</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">today</span><span class="p">,</span> <span class="s1">'day'</span><span class="p">))</span> <span class="p">{</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">className</span> <span class="o">+=</span> <span class="s1">' today'</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'data-date'</span><span class="p">,</span> <span class="nx">dateText</span><span class="p">);</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'date-isoweekday'</span><span class="p">,</span> <span class="nx">day</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">ev</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">fire</span><span class="p">(</span><span class="s1">'dateRender'</span><span class="p">,</span> <span class="p">{</span>
<span class="c1">// 当前完整日期</span>
<span class="nx">date</span><span class="o">:</span> <span class="nx">dateText</span><span class="p">,</span>
<span class="c1">// iso星期</span>
<span class="nx">isoWeekday</span><span class="o">:</span> <span class="nx">day</span><span class="p">,</span>
<span class="c1">// 显示的文本</span>
<span class="nx">dateText</span><span class="o">:</span> <span class="s1">'周'</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">_WEEKSNAME</span><span class="p">[</span><span class="nx">day</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="s1">' '</span> <span class="o">+</span> <span class="nx">date</span><span class="p">.</span><span class="nx">format</span><span class="p">(</span><span class="s1">'MM-DD'</span><span class="p">),</span>
<span class="c1">// classname</span>
<span class="nx">dateCls</span><span class="o">:</span> <span class="nx">node</span><span class="p">.</span><span class="nx">className</span><span class="p">,</span>
<span class="c1">// 日历el</span>
<span class="nx">el</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">el</span><span class="p">,</span>
<span class="c1">// 当前el</span>
<span class="nx">dateEl</span><span class="o">:</span> <span class="nx">node</span>
<span class="p">});</span>
<span class="c1">// 处理事件的修改</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="nx">ev</span><span class="p">.</span><span class="nx">dateText</span><span class="p">;</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">className</span> <span class="o">=</span> <span class="nx">ev</span><span class="p">.</span><span class="nx">dateCls</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_weeksEl</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">node</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">fire</span><span class="p">(</span><span class="s1">'afterDateRender'</span><span class="p">,</span> <span class="p">{</span>
<span class="c1">// 当前完整日期</span>
<span class="nx">date</span><span class="o">:</span> <span class="nx">dateText</span><span class="p">,</span>
<span class="c1">// iso星期</span>
<span class="nx">isoWeekday</span><span class="o">:</span> <span class="nx">day</span><span class="p">,</span>
<span class="c1">// 显示的文本</span>
<span class="nx">dateText</span><span class="o">:</span> <span class="nx">node</span><span class="p">.</span><span class="nx">innerHTML</span><span class="p">,</span>
<span class="c1">// classname</span>
<span class="nx">dateCls</span><span class="o">:</span> <span class="nx">node</span><span class="p">.</span><span class="nx">className</span><span class="p">,</span>
<span class="c1">// 日历el</span>
<span class="nx">el</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">el</span><span class="p">,</span>
<span class="c1">// 当前el</span>
<span class="nx">dateEl</span><span class="o">:</span> <span class="nx">node</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<!--block_code_end--><h3 id="toc_4" class="h16">网格和内容</h3>
<p class="md_block">
<span class="md_line md_line_start md_line_end">上面已经准备好了二维视图中的两个轴,接着进行网格和内容层的绘制即可。</span>
</p>
<p class="md_block">
<span class="md_line md_line_dom_embed md_line_start md_line_end"><strong>网格</strong></span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">此处以分类为Y方向(行),日期为X方向(列)来进行绘制:</span>
</p>
<table class="codehilite code_lang_js with_lines highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48</pre></div></td><td class="code"><div class="codehilite code_lang_js with_lines highlight"><pre><span></span><span class="p">{</span>
<span class="c1">// 右侧网格</span>
<span class="nx">_renderGrid</span><span class="o">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_gridEl</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="s1">''</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">rowNode</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s1">'div'</span><span class="p">),</span>
<span class="nx">itemNode</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s1">'span'</span><span class="p">),</span>
<span class="nx">rowsNum</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">_categoryData</span><span class="p">.</span><span class="nx">length</span><span class="p">,</span>
<span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>
<span class="nx">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>
<span class="nx">row</span><span class="p">,</span> <span class="nx">item</span><span class="p">;</span>
<span class="nx">rowNode</span><span class="p">.</span><span class="nx">className</span> <span class="o">=</span> <span class="s1">'ep-weekcalendar-grid-row'</span><span class="p">;</span>
<span class="nx">itemNode</span><span class="p">.</span><span class="nx">className</span> <span class="o">=</span> <span class="s1">'ep-weekcalendar-grid-item'</span><span class="p">;</span>
<span class="k">while</span> <span class="p">(</span><span class="nx">i</span> <span class="o"><</span> <span class="nx">rowsNum</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">row</span> <span class="o">=</span> <span class="nx">rowNode</span><span class="p">.</span><span class="nx">cloneNode</span><span class="p">();</span>
<span class="nx">row</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'data-i'</span><span class="p">,</span> <span class="nx">i</span><span class="p">);</span>
<span class="nx">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">while</span> <span class="p">(</span><span class="nx">j</span> <span class="o"><</span> <span class="mi">7</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">item</span> <span class="o">=</span> <span class="nx">itemNode</span><span class="p">.</span><span class="nx">cloneNode</span><span class="p">();</span>
<span class="c1">// 周末标识</span>
<span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">dayStartFromSunday</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">j</span> <span class="o">===</span> <span class="mi">0</span> <span class="o">||</span> <span class="nx">j</span> <span class="o">===</span> <span class="mi">6</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">item</span><span class="p">.</span><span class="nx">className</span> <span class="o">+=</span> <span class="s1">' weekend'</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">j</span> <span class="o">></span> <span class="mi">4</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">item</span><span class="p">.</span><span class="nx">className</span> <span class="o">+=</span> <span class="s1">' weekend'</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nx">item</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'data-i'</span><span class="p">,</span> <span class="nx">i</span><span class="p">);</span>
<span class="nx">item</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'data-j'</span><span class="p">,</span> <span class="nx">j</span><span class="p">);</span>
<span class="nx">row</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">item</span><span class="p">);</span>
<span class="nx">j</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_gridEl</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">row</span><span class="p">);</span>
<span class="nx">i</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">rowNode</span> <span class="o">=</span> <span class="nx">itemNode</span> <span class="o">=</span> <span class="nx">row</span> <span class="o">=</span> <span class="nx">item</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_dom_embed md_line_start md_line_end"><strong>内容</strong></span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">理论上来说,二维要支持跨行、跨列两种情况,即内容区域应该为一整块元素。但是结合到实际情况,跨时间的需求普遍存在(一个东西在一段时间内被连续使用)。跨分类并没有多大的实际意义,本来就要分开以分类来管理,再跨分类,又变得复杂了。而且即使一定要实现一段时间内同时在使用多个东西,也是可以直接实现的(分类A在XX时间段内被使用,B在XX时间段内被使用,只是此时XX正好相同而已)。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">因此此处仅处理跨时间情况,可将内容按行即分类进行绘制,这样在插入内容部件时,可以简化很多计算。</span>
</p>
<table class="codehilite code_lang_js with_lines highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37</pre></div></td><td class="code"><div class="codehilite code_lang_js with_lines highlight"><pre><span></span><span class="p">{</span>
<span class="c1">// 右侧内容</span>
<span class="nx">_rednerContent</span><span class="o">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_contentEl</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="s1">''</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>
<span class="nx">node</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s1">'div'</span><span class="p">),</span>
<span class="nx">row</span><span class="p">;</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">className</span> <span class="o">=</span> <span class="s1">'ep-weekcalendar-content-row'</span><span class="p">;</span>
<span class="k">while</span> <span class="p">(</span><span class="nx">i</span> <span class="o"><</span> <span class="k">this</span><span class="p">.</span><span class="nx">_categoryData</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">row</span> <span class="o">=</span> <span class="nx">node</span><span class="p">.</span><span class="nx">cloneNode</span><span class="p">();</span>
<span class="nx">row</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'data-i'</span><span class="p">,</span> <span class="nx">i</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_contentEl</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">row</span><span class="p">);</span>
<span class="o">++</span><span class="nx">i</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">row</span> <span class="o">=</span> <span class="nx">node</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
<span class="p">},</span>
<span class="c1">// 日期切换时清空内容</span>
<span class="nx">_clearContent</span><span class="o">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">rows</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">_contentEl</span><span class="p">.</span><span class="nx">childNodes</span><span class="p">,</span>
<span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">while</span> <span class="p">(</span><span class="nx">i</span> <span class="o"><</span> <span class="nx">rows</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">rows</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">innerHTML</span> <span class="o">&&</span> <span class="p">(</span><span class="nx">rows</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="s1">''</span><span class="p">);</span>
<span class="o">++</span><span class="nx">i</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// 部件数据清空</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_widgetData</span> <span class="o">=</span> <span class="p">{};</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">如果一定要实现跨行跨列的情况,直接将内容绘制成一整块元素即可,但是在点击事件和插入内容部件时,需要同时计算对应的分类和日期时间。</span>
</p>
<h2 id="toc_5" class="h16">难点实现</h2><h3 id="toc_6" class="h16">内容部件插入</h3>
<p class="md_block">
<span class="md_line md_line_start md_line_end">我们实现这个二维周视图日历的主要目的就是要支持插入任意的内容,上面已经准备好了插入内容的dom元素,这里要做的就是将数据绘制成dom放置在合适的位置。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">考虑必要的内容部件数据结构如下:</span>
</p>
<table class="codehilite code_lang_js with_lines highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4
5
6
7
8
9</pre></div></td><td class="code"><div class="codehilite code_lang_js with_lines highlight"><pre><span></span><span class="p">{</span>
<span class="nx">id</span><span class="o">:</span> <span class="s1">'数据标识'</span><span class="p">,</span>
<span class="nx">categoryId</span><span class="o">:</span> <span class="s1">'所属分类标识'</span><span class="p">,</span>
<span class="nx">title</span><span class="o">:</span> <span class="s1">'名称'</span><span class="p">,</span>
<span class="nx">content</span><span class="o">:</span> <span class="s1">'内容'</span><span class="p">,</span>
<span class="nx">start</span><span class="o">:</span> <span class="s1">'开始日期时间'</span>
<span class="nx">end</span><span class="o">:</span> <span class="s1">'结束日期时间'</span>
<span class="nx">bgColor</span><span class="o">:</span> <span class="s1">'展示的背景色'</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">由于上面在内容区域是直接按照分类作为绘制的,因此拿到数据后,对应的分类就已经存在了。重点要根据指定的开始和结束时间计算出开始和结束位置。</span>
</p>
<p class="md_block md_block_as_opening md_has_block_below md_has_block_below_ul">
<span class="md_line md_line_start md_line_end">考虑如下:</span>
</p>
<ul>
<li class="md_li"><span>考虑响应式,位置计算按照百分比计算
</span></li>
<li class="md_li"><span>一周的总时间是固定的,开始日期时间和这周开始日期时间的差额占总时间的百分比即开始位置的百分比
</span></li>
<li class="md_li"><span>结束日期时间和开始时间的差额占总时间的百分比即为结束时间距离最左侧的百分比
</span></li>
<li class="md_li"><span>注意处理开始和结束时间溢出本周的情况
</span></li>
</ul>
<p class="md_block">
<span class="md_line md_line_start md_line_end">因此关于位置计算可以用如下代码处理:</span>
</p>
<table class="codehilite code_lang_js with_lines highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56</pre></div></td><td class="code"><div class="codehilite code_lang_js with_lines highlight"><pre><span></span><span class="p">{</span>
<span class="c1">// 日期时间分隔符 默认为空 对应格式为 '2017-11-11 20:00'</span>
<span class="c1">// 对于'2017-11-11T20:00' 这样的格式务必指定正确的日期和时间之间的分隔符T</span>
<span class="nx">_dateTimeSplit</span><span class="o">:</span><span class="s1">' '</span><span class="p">,</span>
<span class="c1">// 一周分钟数</span>
<span class="nx">_WEEKMINUTES</span><span class="o">:</span> <span class="mi">7</span> <span class="o">*</span> <span class="mi">24</span> <span class="o">*</span> <span class="mi">60</span><span class="p">,</span>
<span class="c1">// 一周秒数</span>
<span class="nx">_WEEKSECONDS</span><span class="o">:</span> <span class="mi">7</span> <span class="o">*</span> <span class="mi">24</span> <span class="o">*</span> <span class="mi">3600</span><span class="p">,</span>
<span class="c1">// 一天的分钟数秒数</span>
<span class="nx">_DAYMINUTES</span><span class="o">:</span> <span class="mi">24</span> <span class="o">*</span> <span class="mi">60</span><span class="p">,</span>
<span class="nx">_DAYSCONDS</span><span class="o">:</span> <span class="mi">24</span> <span class="o">*</span> <span class="mi">3600</span><span class="p">,</span>
<span class="c1">// 计算位置的精度 取值second 或 minute</span>
<span class="nx">posUnit</span><span class="o">:</span> <span class="s1">'second'</span><span class="p">,</span>
<span class="c1">// 计算指定日期的分钟或秒数</span>
<span class="nx">_getNumByUnits</span><span class="o">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">dateStr</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">temp</span> <span class="o">=</span> <span class="nx">dateStr</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">_dateTimeSplit</span><span class="p">),</span>
<span class="nx">date</span> <span class="o">=</span> <span class="nx">temp</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
<span class="c1">// 处理左侧溢出</span>
<span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">_startDate</span><span class="p">.</span><span class="nx">isAfter</span><span class="p">(</span><span class="nx">date</span><span class="p">,</span> <span class="s1">'day'</span><span class="p">))</span> <span class="p">{</span>
<span class="c1">// 指定日期在开始日期之前</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// 右侧溢出直接算作第7天即可</span>
<span class="kd">var</span> <span class="nx">times</span> <span class="o">=</span> <span class="p">(</span><span class="nx">temp</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">||</span> <span class="s1">''</span><span class="p">).</span><span class="nx">split</span><span class="p">(</span><span class="s1">':'</span><span class="p">),</span>
<span class="nx">days</span> <span class="o">=</span> <span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">startDate</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">currDate</span> <span class="o">=</span> <span class="nx">startDate</span><span class="p">.</span><span class="nx">clone</span><span class="p">(),</span>
<span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>
<span class="nx">d</span> <span class="o">=</span> <span class="nx">moment</span><span class="p">(</span><span class="nx">date</span><span class="p">,</span> <span class="s1">'YYYY-MM-DD'</span><span class="p">);</span>
<span class="k">while</span> <span class="p">(</span><span class="nx">i</span> <span class="o"><</span> <span class="mi">7</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">currDate</span><span class="p">.</span><span class="nx">isSame</span><span class="p">(</span><span class="nx">d</span><span class="p">,</span> <span class="s1">'day'</span><span class="p">))</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">i</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nx">currDate</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">'day'</span><span class="p">);</span>
<span class="o">++</span><span class="nx">i</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nx">console</span> <span class="o">&&</span> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span> <span class="o">&&</span> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">'计算天数时出错!'</span><span class="p">);</span>
<span class="k">return</span> <span class="nx">i</span><span class="p">;</span>
<span class="p">}(</span><span class="k">this</span><span class="p">.</span><span class="nx">_startDate</span><span class="p">)),</span>
<span class="nx">hours</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">times</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="mi">10</span><span class="p">)</span> <span class="o">||</span> <span class="mi">0</span><span class="p">,</span>
<span class="nx">minutes</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">times</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="mi">10</span><span class="p">)</span> <span class="o">||</span> <span class="mi">0</span><span class="p">,</span>
<span class="nx">seconds</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">times</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span> <span class="mi">10</span><span class="p">)</span> <span class="o">||</span> <span class="mi">0</span><span class="p">,</span>
<span class="c1">// 对应分钟数</span>
<span class="nx">result</span> <span class="o">=</span> <span class="nx">days</span> <span class="o">*</span> <span class="k">this</span><span class="p">.</span><span class="nx">_DAYMINUTES</span> <span class="o">+</span> <span class="nx">hours</span> <span class="o">*</span> <span class="mi">60</span> <span class="o">+</span> <span class="nx">minutes</span><span class="p">;</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">posUnit</span> <span class="o">==</span> <span class="s1">'minute'</span> <span class="o">?</span> <span class="nx">result</span> <span class="o">:</span> <span class="p">(</span><span class="nx">result</span> <span class="o">*</span> <span class="mi">60</span> <span class="o">+</span> <span class="nx">seconds</span><span class="p">);</span>
<span class="p">},</span>
<span class="c1">// 计算日期时间的百分比位置</span>
<span class="nx">_getPos</span><span class="o">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">dateStr</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">p</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">_getNumByUnits</span><span class="p">(</span><span class="nx">dateStr</span><span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">posUnit</span> <span class="o">==</span> <span class="s1">'minute'</span> <span class="o">?</span> <span class="k">this</span><span class="p">.</span><span class="nx">_WEEKMINUTES</span> <span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">_WEEKSECONDS</span><span class="p">);</span>
<span class="k">return</span> <span class="nx">p</span> <span class="o">></span> <span class="mi">1</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="nx">p</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">上面就拿到了一个数据所对应的开始位置和结束位置。基本上是已经完成了,但是还需要再处理一个情况:相同分类下的时间冲突问题。</span>
</p>
<p class="md_block md_block_as_opening md_has_block_below md_has_block_below_ol">
<span class="md_line md_line_start md_line_end">考虑以如下方式进行:</span>
</p>
<ol>
<li class="md_li"><span>没添加一个就记录下其数据
</span></li>
<li class="md_li"><span>新增的如果和当前分类下已有的存在时间重叠,则认为冲突。
</span></li>
</ol>
<p class="md_block">
<span class="md_line md_line_start md_line_end">实现如下:</span>
</p>
<table class="codehilite code_lang_js with_lines highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30</pre></div></td><td class="code"><div class="codehilite code_lang_js with_lines highlight"><pre><span></span><span class="p">{</span>
<span class="cm">/**</span>
<span class="cm"> * 检查是否发生重叠</span>
<span class="cm"> *</span>
<span class="cm"> * @param {Object} data 当前要加入的数据</span>
<span class="cm"> * @returns false 或 和当前部件重叠的元素数组</span>
<span class="cm"> */</span>
<span class="nx">_checkOccupied</span><span class="o">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">_widgetData</span><span class="p">[</span><span class="nx">data</span><span class="p">.</span><span class="nx">categoryId</span><span class="p">])</span> <span class="p">{</span>
<span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>
<span class="nx">cate</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">_widgetData</span><span class="p">[</span><span class="nx">data</span><span class="p">.</span><span class="nx">categoryId</span><span class="p">],</span>
<span class="nx">len</span> <span class="o">=</span> <span class="nx">cate</span><span class="p">.</span><span class="nx">length</span><span class="p">,</span>
<span class="nx">result</span> <span class="o">=</span> <span class="kc">false</span><span class="p">,</span>
<span class="nx">occupied</span> <span class="o">=</span> <span class="p">[];</span>
<span class="k">for</span> <span class="p">(;</span> <span class="nx">i</span> <span class="o"><</span> <span class="nx">len</span><span class="p">;</span> <span class="o">++</span><span class="nx">i</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 判断时间是否存在重叠</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">start</span> <span class="o"><</span> <span class="nx">cate</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">end</span> <span class="o">&&</span> <span class="nx">data</span><span class="p">.</span><span class="nx">end</span> <span class="o">></span> <span class="nx">cate</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">start</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">occupied</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">cate</span><span class="p">[</span><span class="nx">i</span><span class="p">]);</span>
<span class="nx">result</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">result</span> <span class="o">?</span> <span class="nx">occupied</span> <span class="o">:</span> <span class="kc">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">完成以上两步就可以往我们的内容区域中插入了</span>
</p>
<table class="codehilite code_lang_js with_lines highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117</pre></div></td><td class="code"><div class="codehilite code_lang_js with_lines highlight"><pre><span></span><span class="p">{</span>
<span class="c1">// 缓存widget数据</span>
<span class="nx">_cacheWidgetData</span><span class="o">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">_widgetData</span><span class="p">[</span><span class="nx">data</span><span class="p">.</span><span class="nx">categoryId</span><span class="p">])</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_widgetData</span><span class="p">[</span><span class="nx">data</span><span class="p">.</span><span class="nx">categoryId</span><span class="p">]</span> <span class="o">=</span> <span class="p">[];</span>
<span class="p">}</span>
<span class="c1">// 记录当前的</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_widgetData</span><span class="p">[</span><span class="nx">data</span><span class="p">.</span><span class="nx">categoryId</span><span class="p">].</span><span class="nx">push</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
<span class="p">},</span>
<span class="c1">// 新增一个小部件</span>
<span class="nx">addWidget</span><span class="o">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">row</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">_contentEl</span><span class="p">.</span><span class="nx">childNodes</span><span class="p">[</span><span class="k">this</span><span class="p">.</span><span class="nx">_categoryReocrds</span><span class="p">[</span><span class="nx">data</span><span class="p">.</span><span class="nx">categoryId</span><span class="p">]];</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">row</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">throwError</span><span class="p">(</span><span class="s1">'对应分类不存在,添加失败'</span><span class="p">);</span>
<span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// 先查找是否含有</span>
<span class="kd">var</span> <span class="nx">$aim</span> <span class="o">=</span> <span class="nx">jQuery</span><span class="p">(</span><span class="s1">'.ep-weekcalendar-content-widget[data-id="'</span> <span class="o">+</span> <span class="nx">data</span><span class="p">.</span><span class="nx">id</span> <span class="o">+</span> <span class="s1">'"]'</span><span class="p">,</span> <span class="nx">row</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">$aim</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 已经存在则不添加</span>
<span class="k">return</span> <span class="nx">$aim</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
<span class="p">}</span>
<span class="c1">// 创建部件</span>
<span class="kd">var</span> <span class="nx">widget</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s1">'div'</span><span class="p">),</span>
<span class="nx">title</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s1">'span'</span><span class="p">),</span>
<span class="nx">content</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s1">'p'</span><span class="p">),</span>
<span class="nx">startPos</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">_getPos</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">start</span><span class="p">),</span>
<span class="nx">endPos</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">_getPos</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">end</span><span class="p">),</span>
<span class="nx">_data</span> <span class="o">=</span> <span class="p">{</span>
<span class="nx">categoryId</span><span class="o">:</span> <span class="nx">data</span><span class="p">.</span><span class="nx">categoryId</span><span class="p">,</span>
<span class="nx">id</span><span class="o">:</span> <span class="nx">data</span><span class="p">.</span><span class="nx">id</span><span class="p">,</span>
<span class="nx">start</span><span class="o">:</span> <span class="nx">startPos</span><span class="p">,</span>
<span class="nx">end</span><span class="o">:</span> <span class="nx">endPos</span><span class="p">,</span>
<span class="nx">el</span><span class="o">:</span> <span class="nx">widget</span><span class="p">,</span>
<span class="nx">data</span><span class="o">:</span> <span class="nx">data</span>
<span class="p">};</span>
<span class="nx">widget</span><span class="p">.</span><span class="nx">className</span> <span class="o">=</span> <span class="s1">'ep-weekcalendar-content-widget'</span><span class="p">;</span>
<span class="nx">title</span><span class="p">.</span><span class="nx">className</span> <span class="o">=</span> <span class="s1">'ep-weekcalendar-content-widget-title'</span><span class="p">;</span>
<span class="nx">content</span><span class="p">.</span><span class="nx">className</span> <span class="o">=</span> <span class="s1">'ep-weekcalendar-content-widget-content'</span><span class="p">;</span>
<span class="nx">widget</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">title</span><span class="p">);</span>
<span class="nx">widget</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">content</span><span class="p">);</span>
<span class="c1">// 通过绝对定位,指定其left和right来拉开宽度的方式来处理响应式</span>
<span class="c1">// 可以通过样式设置一个最小宽度,来避免时间段过小时其中文本无法显示的问题</span>
<span class="nx">widget</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">left</span> <span class="o">=</span> <span class="nx">startPos</span> <span class="o">*</span> <span class="mi">100</span> <span class="o">+</span> <span class="s1">'%'</span><span class="p">;</span>
<span class="nx">widget</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">right</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="nx">endPos</span><span class="p">)</span> <span class="o">*</span> <span class="mi">100</span> <span class="o">+</span> <span class="s1">'%'</span><span class="p">;</span>
<span class="nx">data</span><span class="p">.</span><span class="nx">bgColor</span> <span class="o">&&</span> <span class="p">(</span><span class="nx">widget</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">backgroundColor</span> <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">bgColor</span><span class="p">);</span>
<span class="nx">data</span><span class="p">.</span><span class="nx">id</span> <span class="o">&&</span> <span class="nx">widget</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'data-id'</span><span class="p">,</span> <span class="nx">data</span><span class="p">.</span><span class="nx">id</span><span class="p">);</span>
<span class="nx">widget</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'data-start'</span><span class="p">,</span> <span class="nx">data</span><span class="p">.</span><span class="nx">start</span><span class="p">);</span>
<span class="nx">widget</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'data-end'</span><span class="p">,</span> <span class="nx">data</span><span class="p">.</span><span class="nx">end</span><span class="p">);</span>
<span class="nx">title</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">title</span><span class="p">;</span>
<span class="nx">data</span><span class="p">.</span><span class="nx">content</span> <span class="o">&&</span> <span class="p">(</span><span class="nx">content</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">content</span><span class="p">);</span>
<span class="nx">widget</span><span class="p">.</span><span class="nx">title</span> <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">title</span><span class="p">;</span>
<span class="c1">// 检查是否发生重叠</span>
<span class="kd">var</span> <span class="nx">isoccupied</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">_checkOccupied</span><span class="p">(</span><span class="nx">_data</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">isoccupied</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 触发重叠事件</span>
<span class="kd">var</span> <span class="nx">occupiedEv</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">fire</span><span class="p">(</span><span class="s1">'widgetoccupied'</span><span class="p">,</span> <span class="p">{</span>
<span class="nx">occupiedWidgets</span><span class="o">:</span> <span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">arr</span> <span class="o">=</span> <span class="p">[];</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">l</span> <span class="o">=</span> <span class="nx">isoccupied</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="nx">l</span><span class="p">;</span> <span class="o">++</span><span class="nx">i</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">arr</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">isoccupied</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">el</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">arr</span><span class="p">;</span>
<span class="p">})(),</span>
<span class="nx">currWidget</span><span class="o">:</span> <span class="nx">widget</span><span class="p">,</span>
<span class="nx">widgetData</span><span class="o">:</span> <span class="nx">data</span>
<span class="p">});</span>
<span class="c1">// 取消后续执行</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">occupiedEv</span><span class="p">.</span><span class="nx">cancel</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// 缓存数据</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_cacheWidgetData</span><span class="p">(</span><span class="nx">_data</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">addEv</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">fire</span><span class="p">(</span><span class="s1">'widgetAdd'</span><span class="p">,</span> <span class="p">{</span>
<span class="nx">widgetId</span><span class="o">:</span> <span class="nx">data</span><span class="p">.</span><span class="nx">id</span><span class="p">,</span>
<span class="nx">categoryId</span><span class="o">:</span> <span class="nx">data</span><span class="p">.</span><span class="nx">categoryId</span><span class="p">,</span>
<span class="nx">start</span><span class="o">:</span> <span class="nx">data</span><span class="p">.</span><span class="nx">start</span><span class="p">,</span>
<span class="nx">end</span><span class="o">:</span> <span class="nx">data</span><span class="p">.</span><span class="nx">end</span><span class="p">,</span>
<span class="nx">startPos</span><span class="o">:</span> <span class="nx">startPos</span><span class="p">,</span>
<span class="nx">endPos</span><span class="o">:</span> <span class="nx">endPos</span><span class="p">,</span>
<span class="nx">widgetEl</span><span class="o">:</span> <span class="nx">widget</span>
<span class="p">});</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">addEv</span><span class="p">.</span><span class="nx">cancel</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">row</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">widget</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">fire</span><span class="p">(</span><span class="s1">'afterWidgetAdd'</span><span class="p">,</span> <span class="p">{</span>
<span class="nx">widgetId</span><span class="o">:</span> <span class="nx">data</span><span class="p">.</span><span class="nx">id</span><span class="p">,</span>
<span class="nx">categoryId</span><span class="o">:</span> <span class="nx">data</span><span class="p">.</span><span class="nx">categoryId</span><span class="p">,</span>
<span class="nx">start</span><span class="o">:</span> <span class="nx">data</span><span class="p">.</span><span class="nx">start</span><span class="p">,</span>
<span class="nx">end</span><span class="o">:</span> <span class="nx">data</span><span class="p">.</span><span class="nx">end</span><span class="p">,</span>
<span class="nx">startPos</span><span class="o">:</span> <span class="nx">startPos</span><span class="p">,</span>
<span class="nx">endPos</span><span class="o">:</span> <span class="nx">endPos</span><span class="p">,</span>
<span class="nx">widgetEl</span><span class="o">:</span> <span class="nx">widget</span>
<span class="p">});</span>
<span class="k">return</span> <span class="nx">widget</span><span class="p">;</span>
<span class="p">},</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<!--block_code_end--><h3 id="toc_7" class="h16">点击事件和范围选择</h3>
<p class="md_block">
<span class="md_line md_line_start md_line_end">此控件不仅用于结果展示,还要可用于点击进行添加,需要处理其点击事件,但是由于要展示内容,内容是覆盖在分类和日期构成的网格之上的,用户的点击是点击不到网格元素的,必须要根据点击的位置进行计算来获取所点击的日期和所在分类。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">同时,由于展示的部件都是时间范围的,因此点击返回某天和某个分类是不够的,还需要能够支持鼠标按下拖动再松开,来直接选的一段时间,效果如下图:</span>
</p>
<p class="md_block">
<span class="md_line md_line_dom_embed md_line_with_image md_line_start md_line_end"><img class="md_compiled " src="https://qiniu.cdswyda.com/images/201801/show.gif" alt="" title="" ></span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">考虑到以上需求,点击事件不能直接使用 <code>click</code> 来实现,考虑使用 <code>mousedown</code> 和 <code>mouseup</code> 来处理点击事件,同时需要在 <code>mousemove</code> 中实时给出用户响应。</span>
</p>
<table class="codehilite code_lang_js with_lines highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110</pre></div></td><td class="code"><div class="codehilite code_lang_js with_lines highlight"><pre><span></span><span class="p">{</span>
<span class="nx">_initEvent</span><span class="o">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">me</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span>
<span class="c1">// 点击的行索引</span>
<span class="kd">var</span> <span class="nx">row</span><span class="p">,</span>
<span class="c1">// 开始列索引</span>
<span class="nx">columnStart</span><span class="p">,</span>
<span class="c1">// 结束列索引</span>
<span class="nx">columnEnd</span><span class="p">,</span>
<span class="c1">// 是否在按下、移动、松开的click中</span>
<span class="nx">isDurringClick</span> <span class="o">=</span> <span class="kc">false</span><span class="p">,</span>
<span class="c1">// 是否移动过 用于处理按下没有移动直接松开的过程</span>
<span class="nx">isMoveing</span> <span class="o">=</span> <span class="kc">false</span><span class="p">,</span>
<span class="nx">$columns</span><span class="p">,</span>
<span class="c1">// 网格左侧宽度</span>
<span class="nx">gridLeft</span><span class="p">,</span>
<span class="c1">// 每列的宽度</span>
<span class="nx">columnWidth</span>
<span class="nx">jQuery</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">el</span><span class="p">)</span>
<span class="c1">// 按下鼠标 记录分类和开始列</span>
<span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">'mousedown.weekcalendar'</span><span class="p">,</span> <span class="s1">'.ep-weekcalendar-content-row'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">isDurringClick</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="nx">gridLeft</span> <span class="o">=</span> <span class="nx">jQuery</span><span class="p">(</span><span class="nx">me</span><span class="p">.</span><span class="nx">_gridEl</span><span class="p">).</span><span class="nx">offset</span><span class="p">().</span><span class="nx">left</span><span class="p">;</span>
<span class="nx">columnWidth</span> <span class="o">=</span> <span class="nx">jQuery</span><span class="p">(</span><span class="nx">me</span><span class="p">.</span><span class="nx">_gridEl</span><span class="p">).</span><span class="nx">width</span><span class="p">()</span> <span class="o">/</span> <span class="mi">7</span><span class="p">;</span>
<span class="nx">jQuery</span><span class="p">(</span><span class="nx">me</span><span class="p">.</span><span class="nx">_gridEl</span><span class="p">).</span><span class="nx">find</span><span class="p">(</span><span class="s1">'.ep-weekcalendar-grid-item'</span><span class="p">).</span><span class="nx">removeClass</span><span class="p">(</span><span class="nx">me</span><span class="p">.</span><span class="nx">_selectedCls</span><span class="p">);</span>
<span class="nx">row</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">getAttribute</span><span class="p">(</span><span class="s1">'data-i'</span><span class="p">);</span>
<span class="nx">$columns</span> <span class="o">=</span> <span class="nx">jQuery</span><span class="p">(</span><span class="nx">me</span><span class="p">.</span><span class="nx">_gridEl</span><span class="p">).</span><span class="nx">find</span><span class="p">(</span><span class="s1">'.ep-weekcalendar-grid-row'</span><span class="p">).</span><span class="nx">eq</span><span class="p">(</span><span class="nx">row</span><span class="p">).</span><span class="nx">children</span><span class="p">();</span>
<span class="nx">columnStart</span> <span class="o">=</span> <span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">pageX</span> <span class="o">-</span> <span class="nx">gridLeft</span><span class="p">)</span> <span class="o">/</span> <span class="nx">columnWidth</span> <span class="o">>></span> <span class="mi">0</span><span class="p">;</span>
<span class="p">});</span>
<span class="c1">// 移动和松开 松开鼠标 记录结束列 触发点击事件 </span>
<span class="c1">// 不能直接绑定在日期容器上 否则鼠标移出日历后,松开鼠标,实际点击已经结束,但是日历上处理不到。</span>
<span class="nx">jQuery</span><span class="p">(</span><span class="s1">'body'</span><span class="p">)</span>
<span class="c1">// 点击移动过程中 实时响应选中状态</span>
<span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">'mousemove.weekcalendar'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">isDurringClick</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">isMoveing</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="c1">// 当前列索引</span>
<span class="kd">var</span> <span class="nx">currColumn</span><span class="p">;</span>
<span class="c1">// mousemoveTimer = setTimeout(function () {</span>
<span class="nx">currColumn</span> <span class="o">=</span> <span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">pageX</span> <span class="o">-</span> <span class="nx">gridLeft</span><span class="p">)</span> <span class="o">/</span> <span class="nx">columnWidth</span> <span class="o">>></span> <span class="mi">0</span><span class="p">;</span>
<span class="c1">// 修正溢出</span>
<span class="nx">currColumn</span> <span class="o">=</span> <span class="nx">currColumn</span> <span class="o">></span> <span class="mi">6</span> <span class="o">?</span> <span class="mi">6</span> <span class="o">:</span> <span class="nx">currColumn</span><span class="p">;</span>
<span class="nx">currColumn</span> <span class="o">=</span> <span class="nx">currColumn</span> <span class="o"><</span> <span class="mi">0</span> <span class="o">?</span> <span class="mi">0</span> <span class="o">:</span> <span class="nx">currColumn</span><span class="p">;</span>
<span class="nx">$columns</span><span class="p">.</span><span class="nx">removeClass</span><span class="p">(</span><span class="nx">me</span><span class="p">.</span><span class="nx">_selectedCls</span><span class="p">);</span>
<span class="c1">// 起止依次选中</span>
<span class="kd">var</span> <span class="nx">start</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">min</span><span class="p">(</span><span class="nx">columnStart</span><span class="p">,</span> <span class="nx">currColumn</span><span class="p">),</span>
<span class="nx">end</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">max</span><span class="p">(</span><span class="nx">columnStart</span><span class="p">,</span> <span class="nx">currColumn</span><span class="p">);</span>
<span class="k">do</span> <span class="p">{</span>
<span class="nx">$columns</span><span class="p">.</span><span class="nx">eq</span><span class="p">(</span><span class="nx">start</span><span class="p">).</span><span class="nx">addClass</span><span class="p">(</span><span class="nx">me</span><span class="p">.</span><span class="nx">_selectedCls</span><span class="p">);</span>
<span class="p">}</span> <span class="k">while</span> <span class="p">(</span><span class="o">++</span><span class="nx">start</span> <span class="o"><=</span> <span class="nx">end</span><span class="p">);</span>
<span class="p">})</span>
<span class="c1">// 鼠标松开</span>
<span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">'mouseup.weekcalendar'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">isDurringClick</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">var</span> <span class="nx">startIndex</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span>
<span class="nx">endIndex</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
<span class="nx">columnEnd</span> <span class="o">=</span> <span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">pageX</span> <span class="o">-</span> <span class="nx">gridLeft</span><span class="p">)</span> <span class="o">/</span> <span class="nx">columnWidth</span> <span class="o">>></span> <span class="mi">0</span><span class="p">;</span>
<span class="nx">columnEnd</span> <span class="o">=</span> <span class="nx">columnEnd</span> <span class="o">></span> <span class="mi">6</span> <span class="o">?</span> <span class="mi">6</span> <span class="o">:</span> <span class="nx">columnEnd</span><span class="p">;</span>
<span class="c1">// 没有移动过时</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">isMoveing</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">startIndex</span> <span class="o">=</span> <span class="nx">endIndex</span> <span class="o">=</span> <span class="nx">columnEnd</span><span class="p">;</span>
<span class="c1">// 直接down up 没有move的过程则只会有一个选中的,直接以结束的作为处理即可</span>
<span class="nx">$columns</span><span class="p">.</span><span class="nx">eq</span><span class="p">(</span><span class="nx">columnEnd</span><span class="p">).</span><span class="nx">addClass</span><span class="p">(</span><span class="nx">me</span><span class="p">.</span><span class="nx">_selectedCls</span><span class="p">)</span>
<span class="p">.</span><span class="nx">siblings</span><span class="p">().</span><span class="nx">removeClass</span><span class="p">(</span><span class="nx">me</span><span class="p">.</span><span class="nx">_selectedCls</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nx">startIndex</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">min</span><span class="p">(</span><span class="nx">columnStart</span><span class="p">,</span> <span class="nx">columnEnd</span><span class="p">);</span>
<span class="nx">endIndex</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">max</span><span class="p">(</span><span class="nx">columnStart</span><span class="p">,</span> <span class="nx">columnEnd</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// 触发点击事件</span>
<span class="nx">me</span><span class="p">.</span><span class="nx">fire</span><span class="p">(</span><span class="s1">'cellClick'</span><span class="p">,</span> <span class="p">{</span>
<span class="c1">// 分类id </span>
<span class="nx">categoryId</span><span class="o">:</span> <span class="nx">me</span><span class="p">.</span><span class="nx">_categoryIndexs</span><span class="p">[</span><span class="nx">row</span><span class="p">],</span>
<span class="c1">// 时间1</span>
<span class="nx">startDate</span><span class="o">:</span> <span class="nx">me</span><span class="p">.</span><span class="nx">_dateRecords</span><span class="p">[</span><span class="nx">startIndex</span><span class="p">].</span><span class="nx">format</span><span class="p">(</span><span class="s1">'YYYY-MM-DD'</span><span class="p">),</span>
<span class="c1">// 日期2</span>
<span class="nx">endDate</span><span class="o">:</span> <span class="nx">me</span><span class="p">.</span><span class="nx">_dateRecords</span><span class="p">[</span><span class="nx">endIndex</span><span class="p">].</span><span class="nx">format</span><span class="p">(</span><span class="s1">'YYYY-MM-DD'</span><span class="p">),</span>
<span class="c1">// 行索引</span>
<span class="nx">rowIndex</span><span class="o">:</span> <span class="nx">row</span><span class="p">,</span>
<span class="c1">// 列范围</span>
<span class="nx">columnIndexs</span><span class="o">:</span> <span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">i</span><span class="p">,</span> <span class="nx">j</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">arr</span> <span class="o">=</span> <span class="p">[];</span>
<span class="k">while</span> <span class="p">(</span><span class="nx">i</span> <span class="o"><=</span> <span class="nx">j</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">arr</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">i</span><span class="o">++</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">arr</span><span class="p">;</span>
<span class="p">}(</span><span class="nx">startIndex</span><span class="p">,</span> <span class="nx">endIndex</span><span class="p">))</span>
<span class="p">});</span>
<span class="nx">row</span> <span class="o">=</span> <span class="nx">columnStart</span> <span class="o">=</span> <span class="nx">columnEnd</span> <span class="o">=</span> <span class="nx">isMoveing</span> <span class="o">=</span> <span class="nx">isDurringClick</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">此过程要注意的问题是:<code>mousedown</code> 必须绑定在日历上,而 <code>mouseup</code> 和 <code>mousemove</code> 则不能绑定在日历上,具体原因已经写在上面代码注释中了。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">另外需要注意,由于范围点击选择使用了 <code>mousedown</code> 和 <code>mouseup</code> 来模拟,那么日历内容区域中插入的数据部件的点击事件也要用 <code>mousedown</code> 和 <code>mouseup</code> 来模拟,因为 <code>mouseup</code> 触发比 <code>click</code> 早,如果使用 <code>click</code> ,会导致先触发日历上的日期点击或日期范围点击。</span>
</p>
<h2 id="toc_8" class="h16">使用</h2>
<p class="md_block">
<span class="md_line md_line_start md_line_end">此日历实现基于一个控件基类扩展而来,其必要功能仅为一套事件机制,可参考<a class="md_compiled" href="/post/20171027">实现一套自定义事件机制</a></span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">实测一下效果吧:</span>
</p>
<table class="codehilite code_lang_html with_lines highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59</pre></div></td><td class="code"><div class="codehilite code_lang_html with_lines highlight"><pre><span></span><span class="p"><</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">"week-calendar"</span> <span class="na">style</span><span class="o">=</span><span class="s">"width:100%;height:80vh"</span><span class="p">></</span><span class="nt">div</span><span class="p">></span>
<span class="p"><</span><span class="nt">script</span><span class="p">></span>
<span class="kd">var</span> <span class="nx">calendar</span> <span class="o">=</span> <span class="nx">epctrl</span><span class="p">.</span><span class="nx">init</span><span class="p">(</span><span class="s1">'WeekCalendar'</span><span class="p">,</span> <span class="p">{</span>
<span class="nx">el</span><span class="o">:</span> <span class="s1">'#week-calendar'</span><span class="p">,</span>
<span class="nx">categoryTitle</span><span class="o">:</span> <span class="s1">'车辆'</span><span class="p">,</span>
<span class="nx">category</span><span class="o">:</span> <span class="p">[{</span>
<span class="nx">id</span><span class="o">:</span> <span class="s1">'cate-1'</span><span class="p">,</span>
<span class="nx">name</span><span class="o">:</span> <span class="s1">'法拉利'</span><span class="p">,</span>
<span class="nx">content</span><span class="o">:</span> <span class="s1">'苏E00000'</span>
<span class="p">},</span> <span class="p">{</span>
<span class="nx">id</span><span class="o">:</span> <span class="s1">'cate-2'</span><span class="p">,</span>
<span class="nx">name</span><span class="o">:</span> <span class="s1">'Lamborghini'</span><span class="p">,</span>
<span class="nx">content</span><span class="o">:</span> <span class="s1">'苏E00001'</span>
<span class="p">},</span> <span class="p">{</span>
<span class="nx">id</span><span class="o">:</span> <span class="s1">'cate-3'</span><span class="p">,</span>
<span class="nx">name</span><span class="o">:</span> <span class="s1">'捷豹'</span><span class="p">,</span>
<span class="nx">content</span><span class="o">:</span> <span class="s1">'苏E00002'</span>
<span class="p">},</span> <span class="p">{</span>
<span class="nx">id</span><span class="o">:</span> <span class="s1">'cate-4'</span><span class="p">,</span>
<span class="nx">name</span><span class="o">:</span> <span class="s1">'宾利'</span><span class="p">,</span>
<span class="nx">content</span><span class="o">:</span> <span class="s1">'苏E00003'</span>
<span class="p">},</span> <span class="p">{</span>
<span class="nx">id</span><span class="o">:</span> <span class="s1">'cate-5'</span><span class="p">,</span>
<span class="nx">name</span><span class="o">:</span> <span class="s1">'SSC'</span><span class="p">,</span>
<span class="nx">content</span><span class="o">:</span> <span class="s1">'苏E00004'</span>
<span class="p">}],</span>
<span class="nx">events</span><span class="o">:</span> <span class="p">{</span>
<span class="c1">// 日期变化时触发</span>
<span class="nx">dateChanged</span><span class="o">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">data</span> <span class="o">=</span> <span class="p">{</span>
<span class="nx">start</span><span class="o">:</span> <span class="nx">e</span><span class="p">.</span><span class="nx">startDate</span><span class="p">,</span>
<span class="nx">end</span><span class="o">:</span> <span class="nx">e</span><span class="p">.</span><span class="nx">endDate</span><span class="p">,</span>
<span class="p">};</span>
<span class="c1">// 获取数据并逐个添加到日历上</span>
<span class="nx">getData</span><span class="p">(</span><span class="nx">data</span><span class="p">).</span><span class="nx">done</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">$</span><span class="p">.</span><span class="nx">each</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">i</span><span class="p">,</span> <span class="nx">item</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">calendar</span><span class="p">.</span><span class="nx">addWidget</span><span class="p">(</span><span class="nx">item</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="p">},</span>
<span class="c1">// 部件重叠时触发</span>
<span class="nx">widgetOccupied</span><span class="o">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 冲突时禁止继续添加</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">widgetData</span><span class="p">.</span><span class="nx">categoryId</span> <span class="o">+</span> <span class="s1">'分类下id为'</span> <span class="o">+</span> <span class="nx">e</span><span class="p">.</span><span class="nx">widgetData</span><span class="p">.</span><span class="nx">id</span> <span class="o">+</span> <span class="s1">'的部件和现有部件有重叠,取消添加'</span><span class="p">);</span>
<span class="nx">e</span><span class="p">.</span><span class="nx">cancel</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="nx">calendar</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">'dateClick'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">alert</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">({</span>
<span class="s1">'开始时间'</span><span class="o">:</span> <span class="nx">e</span><span class="p">.</span><span class="nx">startDate</span><span class="p">,</span>
<span class="s1">'结束时间'</span><span class="o">:</span> <span class="nx">e</span><span class="p">.</span><span class="nx">endDate</span><span class="p">,</span>
<span class="s1">'分类id'</span><span class="o">:</span> <span class="nx">e</span><span class="p">.</span><span class="nx">categoryId</span><span class="p">,</span>
<span class="s1">'行索引'</span><span class="o">:</span> <span class="nx">e</span><span class="p">.</span><span class="nx">rowIndex</span><span class="p">,</span>
<span class="s1">'列索引范围'</span><span class="o">:</span> <span class="nx">e</span><span class="p">.</span><span class="nx">columnIndexs</span>
<span class="p">},</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">4</span><span class="p">));</span>
<span class="p">});</span>
<span class="p"></</span><span class="nt">script</span><span class="p">></span>
</pre></div>
</td></tr></table>
<!--block_code_end-->
<ul>
<li class="md_li"><span><a class="md_compiled" href="https://github.com/cdswyda/component-frame/tree/master/component/weekcalendar">github</a>
</span></li>
<li class="md_li"><span><a class="md_compiled" href="https://cdswyda.github.io/component-frame/component/weekcalendar/demo/week.html">demo</a>
</span></li>
</ul>
带你开发一个日历控件
2017-12-10T02:52:23Z
2017121010
依韵
<p class="md_block">
<span class="md_line md_line_start md_line_end">日历控件多的不胜枚举,为什么我们还要再造一个轮子呢?</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">因为大多数日历控件都是用于选择日期的,有种需求是要在日历上展示各种各样的内容,这样的日历控件较少,而且试用下来并不满意。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">因此就再造一个轮子,现在带你一起基于使用之前完成的组件机制来开发一个日历控件。</span>
</p>
<p class="md_block">
<span class="md_line md_line_dom_embed md_line_start md_line_end"><strong>需求</strong></span>
</p>
<p class="md_block md_block_as_opening md_has_block_below md_has_block_below_ul">
<span class="md_line md_line_start md_line_end">简单把需求整理如下:</span>
</p>
<ul>
<li class="md_li"><span>月视图
</span></li>
<li class="md_li"><span>支持在日历中每一天中插入任意的内容
</span></li>
<li class="md_li"><span>相关点击事件
</span></li>
<li class="md_li"><span>获取日历当前视图的开始和结束日期
</span></li>
<li class="md_li"><span>获取设置选中的日期
</span></li>
</ul>
<h2 id="toc_0" class="h16">实现分析</h2>
<p class="md_block">
<span class="md_line md_line_start md_line_end">首先我们拿系统中自带的日历观察一下,看看日历的特征到底是怎么样的。</span>
</p>
<p class="md_block">
<span class="md_line md_line_dom_embed md_line_with_image md_line_start md_line_end"><img class="md_compiled " src="https://qiniu.cdswyda.com/images/201712/win10-calandar.png" alt="" title="" ></span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">一个月中有 28 到 31 天不等,但是为了保证完整的结构,日历中会有部分上一月和下一月的日期,总结下来,一个月中显示的必定是整整6周的日期。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">那么只要得到当月的开始日期就可以绘制日历了。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">如何计算当月日历视图中的开始日期呢? 前面已经分析了,为了保证完整,它显示了上一月的部分天数,那么只用从当月的1号开始往前推算就可以了。</span>
</p>
<pre class="lang_show"><code>开始日期 = 当月1号的日期 - 当月1号的星期
结束日期 = 开始日期 + 42天</code></pre>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">这个问题搞清楚了,感觉实现这么一个日历就没什么大阻碍了,开始动工吧!</span>
</p>
<h2 id="toc_1" class="h16">必要结构准备</h2>
<p class="md_block">
<span class="md_line md_line_start md_line_end">首先构建如下所示的基本结构</span>
</p>
<p class="md_block">
<span class="md_line md_line_dom_embed md_line_with_image md_line_start md_line_end"><img class="md_compiled " src="https://qiniu.cdswyda.com/images/201712/calandar-structure.png" alt="" title="" ></span>
</p>
<p class="md_block md_block_as_opening md_has_block_below md_has_block_below_ul">
<span class="md_line md_line_start md_line_end">其中:</span>
</p>
<ul>
<li class="md_li"><span>头部左右为个性化区域,用于实际使用时放置任意内容。中间用于显示当前月份和切换按钮
</span></li>
<li class="md_li"><span>主体区域中用绘制整个日历
<ul>
<li class="md_li"><span>thead 中绘制周一至周日 或周日至周一的星期,这段内容是不会随月份切换而改变的,可以直接准备好
</span></li>
<li class="md_li"><span>tbody 中用于绘制可变的日期,准备好容器留空即可。
</span></li>
</ul>
</span></li>
<li class="md_li"><span>脚部区域用于实际使用时放置任意各项化内容
</span></li>
<li class="md_li"><span>menu区域用于切换日期时弹出的面板
</span></li>
</ul>
<h2 id="toc_2" class="h16">绘制日历</h2>
<p class="md_block">
<span class="md_line md_line_start md_line_end">在初始化好日历结构后就可以开始绘制日历了。</span>
</p>
<h3 id="toc_3" class="h16">计算一个月中的开始日期和结束日期</h3>
<p class="md_block">
<span class="md_line md_line_start md_line_end">首先完成开始和结束时间的计算</span>
</p>
<table class="codehilite code_lang_js with_lines highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23</pre></div></td><td class="code"><div class="codehilite code_lang_js with_lines highlight"><pre><span></span><span class="p">{</span>
<span class="c1">// 初始化当前月份的开始日期和结束日期</span>
<span class="nx">_initStartEnd</span><span class="o">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="c1">// 当月1号</span>
<span class="kd">var</span> <span class="nx">currMonth</span> <span class="o">=</span> <span class="nx">moment</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">currMonth</span><span class="p">,</span> <span class="s1">'YYYY-MM'</span><span class="p">),</span>
<span class="c1">// 当月1号是周几 the ISO day of the week with 1 being Monday and 7 being Sunday.</span>
<span class="nx">firstDay_weekday</span> <span class="o">=</span> <span class="nx">currMonth</span><span class="p">.</span><span class="nx">isoWeekday</span><span class="p">(),</span>
<span class="nx">startDateOfMonth</span><span class="p">,</span>
<span class="nx">endDateOfMonth</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">dayStartFromSunday</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 开始为周一 则向前减少周几的天数-1即为 开始的日期</span>
<span class="nx">startDateOfMonth</span> <span class="o">=</span> <span class="nx">currMonth</span><span class="p">.</span><span class="nx">subtract</span><span class="p">(</span><span class="nx">firstDay_weekday</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="s1">'day'</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="c1">// 开始为周日 则直接向前周几的天数即可</span>
<span class="nx">startDateOfMonth</span> <span class="o">=</span> <span class="nx">currMonth</span><span class="p">.</span><span class="nx">subtract</span><span class="p">(</span><span class="nx">firstDay_weekday</span><span class="p">,</span> <span class="s1">'day'</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">endDateOfMonth</span> <span class="o">=</span> <span class="nx">startDateOfMonth</span><span class="p">.</span><span class="nx">clone</span><span class="p">().</span><span class="nx">add</span><span class="p">(</span><span class="mi">41</span><span class="p">,</span> <span class="s1">'day'</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">startDateOfMonth</span> <span class="o">=</span> <span class="nx">startDateOfMonth</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">endDateOfMonth</span> <span class="o">=</span> <span class="nx">endDateOfMonth</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">由于要处理很多日期,而JavaScript中关于日期处理时,不同浏览器下差异较大,因此直接使用 moment.js 来对日期进行统一处理。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">由于使用习惯不同,一周的开始到底是周一还是周日是不确定的,因此直接作为配置即可。</span>
</p>
<h3 id="toc_4" class="h16">绘制一月中的日期</h3>
<p class="md_block">
<span class="md_line md_line_start md_line_end">上面已经计算得到了一个月的开始日期和结束日期,那么只用遍历进行绘制即可。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">由于我们使用了表格实现,因此需要按行绘制。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">实现如下:</span>
</p>
<table class="codehilite code_lang_js with_lines highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107</pre></div></td><td class="code"><div class="codehilite code_lang_js with_lines highlight"><pre><span></span><span class="p">{</span>
<span class="c1">// 日历可变部分的渲染</span>
<span class="nx">_render</span><span class="o">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_initStartEnd</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">weeks</span> <span class="o">=</span> <span class="mi">6</span><span class="p">,</span>
<span class="nx">days</span> <span class="o">=</span> <span class="mi">7</span><span class="p">,</span>
<span class="nx">curDate</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">startDateOfMonth</span><span class="p">.</span><span class="nx">clone</span><span class="p">(),</span>
<span class="nx">tr</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">start</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">startDateOfMonth</span><span class="p">.</span><span class="nx">format</span><span class="p">(</span><span class="s1">'YYYY-MM-DD'</span><span class="p">),</span>
<span class="nx">end</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">endDateOfMonth</span><span class="p">.</span><span class="nx">format</span><span class="p">(</span><span class="s1">'YYYY-MM-DD'</span><span class="p">);</span>
<span class="c1">// 清空 并开始新的渲染</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_clearDays</span><span class="p">();</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_renderTitle</span><span class="p">();</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="nx">weeks</span><span class="p">;</span> <span class="o">++</span><span class="nx">i</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">tr</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s1">'tr'</span><span class="p">);</span>
<span class="nx">tr</span><span class="p">.</span><span class="nx">className</span> <span class="o">=</span> <span class="s1">'ep-calendar-week'</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_daysBody</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">tr</span><span class="p">);</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">j</span> <span class="o"><</span> <span class="nx">days</span><span class="p">;</span> <span class="o">++</span><span class="nx">j</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 渲染一天 并递增</span>
<span class="k">this</span><span class="p">.</span><span class="nx">_renderDay</span><span class="p">(</span><span class="nx">curDate</span><span class="p">,</span> <span class="nx">tr</span><span class="p">);</span>
<span class="nx">curDate</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">'day'</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">},</span>
<span class="c1">// 每天的渲染</span>
<span class="nx">_renderDay</span><span class="o">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">date</span><span class="p">,</span> <span class="nx">currTr</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">td</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s1">'td'</span><span class="p">),</span>
<span class="nx">tdInner</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s1">'div'</span><span class="p">),</span>
<span class="nx">text</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s1">'span'</span><span class="p">),</span>
<span class="nx">day</span> <span class="o">=</span> <span class="nx">date</span><span class="p">.</span><span class="nx">isoWeekday</span><span class="p">(),</span>
<span class="c1">// 返回的月份是0-11</span>
<span class="nx">month</span> <span class="o">=</span> <span class="nx">date</span><span class="p">.</span><span class="nx">month</span><span class="p">()</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
<span class="nx">tdInner</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">text</span><span class="p">);</span>
<span class="nx">td</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">tdInner</span><span class="p">);</span>
<span class="nx">td</span><span class="p">.</span><span class="nx">className</span> <span class="o">=</span> <span class="s1">'ep-calendar-date'</span><span class="p">;</span>
<span class="nx">tdInner</span><span class="p">.</span><span class="nx">className</span> <span class="o">=</span> <span class="s1">'ep-calendar-date-inner'</span><span class="p">;</span>
<span class="c1">// 完整日期</span>
<span class="nx">td</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'data-date'</span><span class="p">,</span> <span class="nx">date</span><span class="p">.</span><span class="nx">format</span><span class="p">(</span><span class="s1">'YYYY-MM-DD'</span><span class="p">));</span>
<span class="c1">// 对应的iso星期</span>
<span class="nx">td</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s1">'data-isoweekday'</span><span class="p">,</span> <span class="nx">day</span><span class="p">);</span>
<span class="c1">// 周末标记text.className</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">day</span> <span class="o">===</span> <span class="mi">6</span> <span class="o">||</span> <span class="nx">day</span> <span class="o">===</span> <span class="mi">7</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">td</span><span class="p">.</span><span class="nx">className</span> <span class="o">+=</span> <span class="s1">' ep-calenday-weekend'</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// 非本月标记</span>
<span class="c1">// substr 在ie8下有问题</span>
<span class="c1">// if (month != parseInt(this.currMonth.substr(-2))) {</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">month</span> <span class="o">!=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">currMonth</span><span class="p">.</span><span class="nx">substr</span><span class="p">(</span><span class="mi">5</span><span class="p">),</span> <span class="mi">10</span><span class="p">))</span> <span class="p">{</span>
<span class="nx">td</span><span class="p">.</span><span class="nx">className</span> <span class="o">+=</span> <span class="s1">' ep-calendar-othermonth'</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// 今天标记</span>
<span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">today</span> <span class="o">==</span> <span class="nx">date</span><span class="p">.</span><span class="nx">format</span><span class="p">(</span><span class="s1">'YYYY-MM-DD'</span><span class="p">))</span> <span class="p">{</span>
<span class="nx">td</span><span class="p">.</span><span class="nx">className</span> <span class="o">+=</span> <span class="s1">' ep-calendar-today'</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// 每天渲染时发生 还未插入页面</span>
<span class="kd">var</span> <span class="nx">renderEvent</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">fire</span><span class="p">(</span><span class="s1">'cellRender'</span><span class="p">,</span> <span class="p">{</span>
<span class="c1">// 当天的完整日期</span>
<span class="nx">date</span><span class="o">:</span> <span class="nx">date</span><span class="p">.</span><span class="nx">format</span><span class="p">(</span><span class="s1">'YYYY-MM-DD'</span><span class="p">),</span>
<span class="c1">// 当天的iso星期</span>
<span class="nx">isoWeekday</span><span class="o">:</span> <span class="nx">day</span><span class="p">,</span>
<span class="c1">// 日历dom</span>
<span class="nx">el</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">el</span><span class="p">,</span>
<span class="c1">// 当前单元格</span>
<span class="nx">tdEl</span><span class="o">:</span> <span class="nx">td</span><span class="p">,</span>
<span class="c1">// 日期文本</span>
<span class="nx">dateText</span><span class="o">:</span> <span class="nx">date</span><span class="p">.</span><span class="nx">date</span><span class="p">(),</span>
<span class="c1">// 日期class</span>
<span class="nx">dateCls</span><span class="o">:</span> <span class="s1">'ep-calendar-date-text'</span><span class="p">,</span>
<span class="c1">// 需要注入的额外的html</span>
<span class="nx">extraHtml</span><span class="o">:</span> <span class="s1">''</span><span class="p">,</span>
<span class="nx">isHeader</span><span class="o">:</span> <span class="kc">false</span>
<span class="p">});</span>
<span class="c1">// 处理对dayText内容和样式的更改</span>
<span class="nx">text</span><span class="p">.</span><span class="nx">innerText</span> <span class="o">=</span> <span class="nx">renderEvent</span><span class="p">.</span><span class="nx">dateText</span><span class="p">;</span>
<span class="nx">text</span><span class="p">.</span><span class="nx">className</span> <span class="o">=</span> <span class="nx">renderEvent</span><span class="p">.</span><span class="nx">dateCls</span><span class="p">;</span>
<span class="c1">// 添加新增内容</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">renderEvent</span><span class="p">.</span><span class="nx">extraHtml</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">jQuery</span><span class="p">(</span><span class="nx">renderEvent</span><span class="p">.</span><span class="nx">extraHtml</span><span class="p">).</span><span class="nx">appendTo</span><span class="p">(</span><span class="nx">tdInner</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">currTr</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">renderEvent</span><span class="p">.</span><span class="nx">tdEl</span><span class="p">);</span>
<span class="c1">// 每天渲染后发生 插入到页面</span>
<span class="k">this</span><span class="p">.</span><span class="nx">fire</span><span class="p">(</span><span class="s1">'afterCellRender'</span><span class="p">,</span> <span class="p">{</span>
<span class="nx">date</span><span class="o">:</span> <span class="nx">date</span><span class="p">.</span><span class="nx">format</span><span class="p">(</span><span class="s1">'YYYY-MM-DD'</span><span class="p">),</span>
<span class="nx">isoWeekday</span><span class="o">:</span> <span class="nx">day</span><span class="p">,</span>
<span class="nx">el</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">el</span><span class="p">,</span>
<span class="nx">tdEl</span><span class="o">:</span> <span class="nx">td</span><span class="p">,</span>
<span class="nx">dateText</span><span class="o">:</span> <span class="nx">text</span><span class="p">.</span><span class="nx">innerText</span><span class="p">,</span>
<span class="nx">dateCls</span><span class="o">:</span> <span class="nx">text</span><span class="p">.</span><span class="nx">className</span><span class="p">,</span>
<span class="nx">extraHtml</span><span class="o">:</span> <span class="nx">renderEvent</span><span class="p">.</span><span class="nx">extraHtml</span><span class="p">,</span>
<span class="nx">isHeader</span><span class="o">:</span> <span class="kc">false</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">直接从开始日期往后依次画出42天即可。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">为了灵活性,在绘制的不同时机触发了不同的事件,在使用时可绑定相应的事件,在其中进行个性化操作。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">也为了使用了方便和灵活性,直接在绘制日期时,在相应的dom上加入了所对应的日期和星期属性。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">在此过程中需要对日期是否周末、是否本月、是否是选中的、是否是今天等进行相应的标记处理。</span>
</p>
<h3 id="toc_5" class="h16">绘制其他内容</h3>
<p class="md_block">
<span class="md_line md_line_start md_line_end">除了上面所述之外此外还要绘制出年月选择、标题等,这些实际就是给已经有的dom元素中更改内容而已,就不再展开了。</span>
</p>
<h3 id="toc_6" class="h16">切换月份的实现</h3>
<p class="md_block">
<span class="md_line md_line_start md_line_end">上面已经基本绘制出了一个日历,切换月份实际就更简单了,只用根据新的月份重新计算开始日期,清空原来的内容,重新进行绘制即可。</span>
</p>
<table class="codehilite code_lang_js with_lines highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31</pre></div></td><td class="code"><div class="codehilite code_lang_js with_lines highlight"><pre><span></span><span class="p">{</span>
<span class="c1">// 设置月份</span>
<span class="nx">setMonth</span><span class="o">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">ym</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">date</span> <span class="o">=</span> <span class="nx">moment</span><span class="p">(</span><span class="nx">ym</span><span class="p">,</span> <span class="s1">'YYYY-MM'</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">date</span><span class="p">.</span><span class="nx">isValid</span><span class="p">())</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">oldMonth</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">currMonth</span><span class="p">,</span>
<span class="nx">aimMonth</span> <span class="o">=</span> <span class="nx">date</span><span class="p">.</span><span class="nx">format</span><span class="p">(</span><span class="s1">'YYYY-MM'</span><span class="p">);</span>
<span class="c1">// 月份变动前</span>
<span class="k">this</span><span class="p">.</span><span class="nx">fire</span><span class="p">(</span><span class="s1">'beforeMonthChange'</span><span class="p">,</span> <span class="p">{</span>
<span class="nx">el</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">el</span><span class="p">,</span>
<span class="nx">oldMonth</span><span class="o">:</span> <span class="nx">oldMonth</span><span class="p">,</span>
<span class="nx">newMonth</span><span class="o">:</span> <span class="nx">aimMonth</span>
<span class="p">});</span>
<span class="k">this</span><span class="p">.</span><span class="nx">currMonth</span> <span class="o">=</span> <span class="nx">aimMonth</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">render</span><span class="p">();</span>
<span class="c1">// 月份变动后</span>
<span class="k">this</span><span class="p">.</span><span class="nx">fire</span><span class="p">(</span><span class="s1">'afterMonthChange'</span><span class="p">,</span> <span class="p">{</span>
<span class="nx">el</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">el</span><span class="p">,</span>
<span class="nx">oldMonth</span><span class="o">:</span> <span class="nx">oldMonth</span><span class="p">,</span>
<span class="nx">newMonth</span><span class="o">:</span> <span class="nx">aimMonth</span>
<span class="p">});</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="nx">ym</span> <span class="o">+</span> <span class="s1">'是一个不合法的日期'</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<!--block_code_end--><h2 id="toc_7" class="h16">事件的处理</h2>
<p class="md_block">
<span class="md_line md_line_start md_line_end">要处理的事件较多,此处仅仅以日期的点击作为示意。</span>
</p>
<table class="codehilite code_lang_js with_lines highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23</pre></div></td><td class="code"><div class="codehilite code_lang_js with_lines highlight"><pre><span></span><span class="p">{</span>
<span class="c1">// 初始化事件</span>
<span class="nx">_initEvent</span><span class="o">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">my</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span>
<span class="nx">jQuery</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">el</span><span class="p">)</span>
<span class="c1">// 日期单元格</span>
<span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="s1">'.ep-calendar-date'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">date</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">getAttribute</span><span class="p">(</span><span class="s1">'data-date'</span><span class="p">),</span>
<span class="nx">ev</span> <span class="o">=</span> <span class="nx">my</span><span class="p">.</span><span class="nx">fire</span><span class="p">(</span><span class="s1">'dayClick'</span><span class="p">,</span> <span class="p">{</span>
<span class="nx">ev</span><span class="o">:</span> <span class="nx">e</span><span class="p">,</span>
<span class="nx">date</span><span class="o">:</span> <span class="nx">date</span><span class="p">,</span>
<span class="nx">day</span><span class="o">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">getAttribute</span><span class="p">(</span><span class="s1">'data-isoweekday'</span><span class="p">),</span>
<span class="nx">el</span><span class="o">:</span> <span class="nx">my</span><span class="p">.</span><span class="nx">el</span><span class="p">,</span>
<span class="nx">tdEl</span><span class="o">:</span> <span class="k">this</span>
<span class="p">});</span>
<span class="c1">// 如果修改事件对象的cancel为true后 则不进行后续的选中操作</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">ev</span><span class="p">.</span><span class="nx">cancel</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">my</span><span class="p">.</span><span class="nx">setSelected</span><span class="p">(</span><span class="nx">date</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">})</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">由于日期所对应的dom元素始终会添加和移除,直接把事件绑定在日期的dom元素上,则必须在每次新增后重新绑定事件,十分麻烦。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">直接使用事件代理机制,将事件绑定在整个日历的dom上即可,这样事件只用在创建时初始化一次即可,简单、高效、省内存。</span>
</p>
<h2 id="toc_8" class="h16">使用</h2>
<p class="md_block">
<span class="md_line md_line_start md_line_end">我们新增这个控件的主要目的就是要支持在日历中绘制任意内容,怎么使用呢?</span>
</p>
<table class="codehilite code_lang_js with_lines highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33</pre></div></td><td class="code"><div class="codehilite code_lang_js with_lines highlight"><pre><span></span><span class="kd">var</span> <span class="nx">testCalendar</span> <span class="o">=</span> <span class="nx">epctrl</span><span class="p">.</span><span class="nx">init</span><span class="p">(</span><span class="s1">'Calendar'</span><span class="p">,</span> <span class="p">{</span>
<span class="nx">el</span><span class="o">:</span> <span class="s1">'#date'</span><span class="p">,</span>
<span class="c1">// 资源加载过程中的事件需要直接在这里指定</span>
<span class="nx">events</span><span class="o">:</span> <span class="p">{</span>
<span class="nx">beforeSourceLoad</span><span class="o">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 资源加载前,在加入我们的皮肤样式文件</span>
<span class="nx">e</span><span class="p">.</span><span class="nx">cssUrl</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="s1">'./test-skin.css'</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="c1">// 日期部分渲染前 支持动态获取数据</span>
<span class="nx">testCalendar</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">'beforeDateRender'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">startDate</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">startDate</span><span class="p">,</span>
<span class="nx">endDate</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">endDate</span><span class="p">;</span>
<span class="c1">// 如果需要动态获取数据</span>
<span class="c1">// 则将获取数据的ajax加到事件对象的ajax属性上即可</span>
<span class="c1">// 日期渲染的cellRender事件将在ajax成功获取数据后执行</span>
<span class="nx">e</span><span class="p">.</span><span class="nx">ajax</span> <span class="o">=</span> <span class="nx">$</span><span class="p">.</span><span class="nx">ajax</span><span class="p">({</span>
<span class="nx">url</span><span class="o">:</span> <span class="s1">'getDateInfo.xxx'</span><span class="p">,</span>
<span class="c1">// 将当月视图的开始和结束时间传递过去</span>
<span class="nx">data</span><span class="o">:</span> <span class="p">{</span>
<span class="nx">start</span><span class="o">:</span> <span class="nx">startDate</span><span class="p">,</span>
<span class="nx">end</span><span class="o">:</span> <span class="nx">endDate</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="c1">// 控制渲染过程 可插入任意内容或修改原来的内容</span>
<span class="nx">testCalendar</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">'cellRender'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">e</span><span class="p">.</span><span class="nx">isHeader</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 如:周五周六则插入周末 否则插入工作日</span>
<span class="nx">e</span><span class="p">.</span><span class="nx">extraHtml</span> <span class="o">=</span> <span class="s1">'<div>'</span> <span class="o">+</span> <span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">isoWeekday</span> <span class="o">></span> <span class="mi">5</span> <span class="o">?</span> <span class="s1">'周末'</span><span class="o">:</span> <span class="s1">'工作日'</span><span class="p">)</span> <span class="o">+</span> <span class="s1">'</div>'</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">});</span>
</pre></div>
</td></tr></table>
<!--block_code_end--><h2 id="toc_9" class="h16">总结</h2>
<p class="md_block">
<span class="md_line md_line_start md_line_end">以上就是关于一个月视图日历控件核心步骤了。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">此日历实现基于一个控件基类扩展而来,其必要功能仅为一套事件机制,可参考<a class="md_compiled" href="/post/20171027">实现一套自定义事件机制</a></span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">上面只分析了关键步骤,和核心代码,为了方便使用和扩展性,实际代码中还要处理很多问题。源码和文档如下,感兴趣可以阅读:<a class="md_compiled" href="https://github.com/cdswyda/component-frame/tree/master/component/calendar">月视图日历</a></span>
</p>
网页中文本朗读功能开发实现分享
2017-12-09T06:52:23Z
2017120914
依韵
<p class="md_block">
<span class="md_line md_line_start md_line_end">前几天完成了一个需求,在网页中完成鼠标指向哪里,就用语音读出所指的文本。如果是按钮、链接、文本输入框,则还还要给出是什么的提醒。同时针对大段的文本,不能整段的去读,要按照标点符号进行断句处理。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">重点当然就是先获取到当前标签上的文本,再把文本转化成语音即可。</span>
</p>
<h2 id="toc_0" class="h16">标签朗读</h2>
<p class="md_block">
<span class="md_line md_line_start md_line_end">这个很简单了,只用根据当前是什么标签,给出提示即可。</span>
</p>
<div class="codehilite code_lang_js highlight"><pre><span></span><span class="c1">// 标签朗读文本</span>
<span class="kd">var</span> <span class="nx">tagTextConfig</span> <span class="o">=</span> <span class="p">{</span>
<span class="s1">'a'</span><span class="o">:</span> <span class="s1">'链接'</span><span class="p">,</span>
<span class="s1">'input[text]'</span><span class="o">:</span> <span class="s1">'文本输入框'</span><span class="p">,</span>
<span class="s1">'input[password]'</span><span class="o">:</span> <span class="s1">'密码输入框'</span><span class="p">,</span>
<span class="s1">'button'</span><span class="o">:</span> <span class="s1">'按钮'</span><span class="p">,</span>
<span class="s1">'img'</span><span class="o">:</span> <span class="s1">'图片'</span>
<span class="p">};</span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">还有需要朗读的标签,继续再添加即可。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">然后根据标签,返回前缀文本即可。</span>
</p>
<div class="codehilite code_lang_js highlight"><pre><span></span><span class="cm">/**</span>
<span class="cm"> * 获取标签朗读文本</span>
<span class="cm"> * @param {HTMLElement} el 要处理的HTMLElement</span>
<span class="cm"> * @returns {String} 朗读文本</span>
<span class="cm"> */</span>
<span class="kd">function</span> <span class="nx">getTagText</span><span class="p">(</span><span class="nx">el</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">el</span><span class="p">)</span> <span class="k">return</span> <span class="s1">''</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">tagName</span> <span class="o">=</span> <span class="nx">el</span><span class="p">.</span><span class="nx">tagName</span><span class="p">.</span><span class="nx">toLowerCase</span><span class="p">();</span>
<span class="c1">// 处理input等多属性元素</span>
<span class="k">switch</span> <span class="p">(</span><span class="nx">tagName</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="s1">'input'</span><span class="o">:</span>
<span class="nx">tagName</span> <span class="o">+=</span> <span class="s1">'['</span> <span class="o">+</span> <span class="nx">el</span><span class="p">.</span><span class="nx">type</span> <span class="o">+</span> <span class="s1">']'</span><span class="p">;</span>
<span class="k">break</span><span class="p">;</span>
<span class="k">default</span><span class="o">:</span>
<span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// 标签的功能提醒和作用应该有间隔,因此在最后加入一个空格</span>
<span class="k">return</span> <span class="p">(</span><span class="nx">tagTextConfig</span><span class="p">[</span><span class="nx">tagName</span><span class="p">]</span> <span class="o">||</span> <span class="s1">''</span><span class="p">)</span> <span class="o">+</span> <span class="s1">' '</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">获取完整的朗读文本就更简单了,先取标签的功能提醒,再取标签的文本即可。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">文本内容优先取 <code>title</code> 其次 <code>alt</code> 最后 <code>innerText</code>。</span>
</p>
<div class="codehilite code_lang_js highlight"><pre><span></span><span class="cm">/**</span>
<span class="cm"> * 获取完整朗读文本</span>
<span class="cm"> * @param {HTMLElement} el 要处理的HTMLElement</span>
<span class="cm"> * @returns {String} 朗读文本</span>
<span class="cm"> */</span>
<span class="kd">function</span> <span class="nx">getText</span><span class="p">(</span><span class="nx">el</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">el</span><span class="p">)</span> <span class="k">return</span> <span class="s1">''</span><span class="p">;</span>
<span class="k">return</span> <span class="nx">getTagText</span><span class="p">(</span><span class="nx">el</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="nx">el</span><span class="p">.</span><span class="nx">title</span> <span class="o">||</span> <span class="nx">el</span><span class="p">.</span><span class="nx">alt</span> <span class="o">||</span> <span class="nx">el</span><span class="p">.</span><span class="nx">innerText</span> <span class="o">||</span> <span class="s1">''</span><span class="p">);</span>
<span class="p">}</span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">这样就可以获取到一个标签的功能提醒和内容的全部带朗读文本了。</span>
</p>
<h2 id="toc_1" class="h16">正文分隔</h2>
<p class="md_block">
<span class="md_line md_line_start md_line_end">接下来要处理的就是正文分隔了,在这个过程中,踩了不少坑,走了不少弯路,好好记录一下。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">首先准备了正文分隔的配置:</span>
</p>
<div class="codehilite code_lang_js highlight"><pre><span></span><span class="c1">// 正文拆分配置</span>
<span class="kd">var</span> <span class="nx">splitConfig</span> <span class="o">=</span> <span class="p">{</span>
<span class="c1">// 内容分段标签名称</span>
<span class="nx">unitTag</span><span class="o">:</span> <span class="s1">'p'</span><span class="p">,</span>
<span class="c1">// 正文中分隔正则表达式</span>
<span class="nx">splitReg</span><span class="o">:</span> <span class="sr">/[,;,;。]/g</span><span class="p">,</span>
<span class="c1">// 包裹标签名</span>
<span class="nx">wrapTag</span><span class="o">:</span> <span class="s1">'label'</span><span class="p">,</span>
<span class="c1">// 包裹标签类名</span>
<span class="nx">wrapCls</span><span class="o">:</span> <span class="s1">'speak-lable'</span><span class="p">,</span>
<span class="c1">// 高亮样式名和样式</span>
<span class="nx">hightlightCls</span><span class="o">:</span> <span class="s1">'speak-help-hightlight'</span><span class="p">,</span>
<span class="nx">hightStyle</span><span class="o">:</span> <span class="s1">'background: #000!important; color: #fff!important'</span>
<span class="p">};</span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">最开始想的就是直接按照正文中的分隔标点符号进行分隔就好了呀。</span>
</p>
<p class="md_block md_block_as_opening md_has_block_below md_has_block_below_ol">
<span class="md_line md_line_start md_line_end">想法如下:</span>
</p>
<ol>
<li class="md_li"><span>获取段落全部文本
</span></li>
<li class="md_li"><span>使用 <code>split(分隔正则表达式)</code> 方法将正文按照标点符号分隔成小段
</span></li>
<li class="md_li"><span>每个小段用标签包裹放回去即可
</span></li>
</ol>
<p class="md_block">
<span class="md_line md_line_start md_line_end">然而理想很丰满,现实很骨感。</span>
</p>
<p class="md_block md_block_as_opening md_has_block_below md_has_block_below_ol">
<span class="md_line md_line_start md_line_end">两个大坑如下:</span>
</p>
<ol>
<li class="md_li"><span><code>split</code> 方法进行分隔,分隔后分隔字符就丢了,也就是说把原文的一些标点符号给弄丢了。
</span></li>
<li class="md_li"><span>如果段落内还存在其他标签,而这个标签内部也正好存在待分隔的标点符号,那包裹分段标签时直接破换了原标签的完整性。
</span></li>
</ol>
<p class="md_block">
<span class="md_line md_line_start md_line_end">关于第一个问题,丢失标点的符号,考虑过逐个标点来进行和替换 <code>split</code> 分隔方法为逐个字符循环来做。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">前者问题是原本一次完成的工作分成了多次,效率太低。第二种感觉效率更低了,分隔本来是很稀疏的,但是却要变成逐个字符出判断处理,更关键的是,分隔标点的位置要插入包裹标签,会导致字符串长度变化,还要处理下标索引。代码是机器跑的,或许不会觉得烦,但是我真的觉得好烦。如果这么干,或许以后哪个AI或者同事看到这样的代码,说不定会说“这真是个傻xxxx”。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">第二个问题想过很多办法来补救,如先使用正则匹配捕获内容中成对的标签,对标签内部的分隔先处理一遍,然后再处理整个的。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">想不明白问题二的,可参考一下待分隔的段落:</span>
</p>
<div class="codehilite code_lang_html highlight"><pre><span></span><span class="p"><</span><span class="nt">p</span><span class="p">></span>这是一段测试文本,这里有个链接。<span class="p"><</span><span class="nt">a</span><span class="p">></span>您好,可以点击此处进行跳转<span class="p"></</span><span class="nt">a</span><span class="p">></span>还有其他内容其他内容容其他内容容其他内容,容其他内容。<span class="p"></</span><span class="nt">p</span><span class="p">></span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">如先使用<code>/<((\w+?)>)(.+?)<\/\2(?=>)/g</code> 正则,依次捕获段落内被标签包裹的内容,对标签内部的内容先处理。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">但是问题又来了,这么处理的都是字符串,在js中都是基本类型,这些操作进行的时候都是在复制的基础上进行的,要修改到原字符串里去,还得记录下原本的开始结束位置,再将新的插进去。繁,还是繁,但是已经比之前逐个字符去遍历的好,正则捕获中本来就有了匹配的索引,直接用即可,还能接受。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">但是这只是处理了段落内部标签的问题,段落内肯定还有很多文本是没有处理呢,怎么办?</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">正则匹配到了只是段落内标签的结果啊,外面的没有啊。哦,对,有匹配到的索引,上次匹配到的位置加上上次处理的长度,就是一段直接文本的开始。下一次匹配到的索引-1就是这段直接文本的结束。这只是匹配过程中的,还有首尾要单独处理。又回到烦的老路上去了。。。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">这么烦,一个段落分隔能这么繁琐,我不信!</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">突然想到了,有文本节点这么个东西,删繁就简嘛,正则先到边上去,直接处理段落的所有节点不就行了。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">文本节点则分隔直接包裹,标签节点则对内容进行包裹,这种情况下处理的直接是dom,更省事。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">文本节点里放标签?这是在开玩笑么,是也不是。文本节点里确实只能放文本,但是我把标签直接放进去,它会自动转义,那最后再替换出来不就行了。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">好了,方案终于有了,而且这个方案逻辑多简单,代码逻辑自然也不会烦。</span>
</p>
<div class="codehilite code_lang_js highlight"><pre><span></span><span class="cm">/**</span>
<span class="cm"> * 正文内容分段处理</span>
<span class="cm"> * @param {jQueryObject/HTMLElement/String} $content 要处理的正文jQ对象或HTMLElement或其对应选择器</span>
<span class="cm"> */</span>
<span class="kd">function</span> <span class="nx">splitConent</span><span class="p">(</span><span class="nx">$content</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">$content</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="nx">$content</span><span class="p">);</span>
<span class="nx">$content</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="nx">splitConfig</span><span class="p">.</span><span class="nx">unitTag</span><span class="p">).</span><span class="nx">each</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">index</span><span class="p">,</span> <span class="nx">item</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">$item</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="nx">item</span><span class="p">),</span>
<span class="nx">text</span> <span class="o">=</span> <span class="nx">$</span><span class="p">.</span><span class="nx">trim</span><span class="p">(</span><span class="nx">$item</span><span class="p">.</span><span class="nx">text</span><span class="p">());</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">text</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">nodes</span> <span class="o">=</span> <span class="nx">$item</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">childNodes</span><span class="p">;</span>
<span class="nx">$</span><span class="p">.</span><span class="nx">each</span><span class="p">(</span><span class="nx">nodes</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">i</span><span class="p">,</span> <span class="nx">node</span><span class="p">)</span> <span class="p">{</span>
<span class="k">switch</span> <span class="p">(</span><span class="nx">node</span><span class="p">.</span><span class="nx">nodeType</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="mi">3</span><span class="o">:</span>
<span class="c1">// text 节点</span>
<span class="c1">// 由于是文本节点,标签被转义了,后续再转回来</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">data</span> <span class="o">=</span> <span class="s1">'<'</span> <span class="o">+</span> <span class="nx">splitConfig</span><span class="p">.</span><span class="nx">wrapTag</span> <span class="o">+</span> <span class="s1">'>'</span> <span class="o">+</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="nx">splitConfig</span><span class="p">.</span><span class="nx">splitReg</span><span class="p">,</span> <span class="s1">'</'</span> <span class="o">+</span> <span class="nx">splitConfig</span><span class="p">.</span><span class="nx">wrapTag</span> <span class="o">+</span> <span class="s1">'>$&<'</span> <span class="o">+</span> <span class="nx">splitConfig</span><span class="p">.</span><span class="nx">wrapTag</span> <span class="o">+</span> <span class="s1">'>'</span><span class="p">)</span> <span class="o">+</span>
<span class="s1">'</'</span> <span class="o">+</span> <span class="nx">splitConfig</span><span class="p">.</span><span class="nx">wrapTag</span> <span class="o">+</span> <span class="s1">'>'</span><span class="p">;</span>
<span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="mi">1</span><span class="o">:</span>
<span class="c1">// 元素节点</span>
<span class="kd">var</span> <span class="nx">innerHtml</span> <span class="o">=</span> <span class="nx">node</span><span class="p">.</span><span class="nx">innerHTML</span><span class="p">,</span>
<span class="nx">start</span> <span class="o">=</span> <span class="s1">''</span><span class="p">,</span>
<span class="nx">end</span> <span class="o">=</span> <span class="s1">''</span><span class="p">;</span>
<span class="c1">// 如果内部还有直接标签,先去掉</span>
<span class="kd">var</span> <span class="nx">startResult</span> <span class="o">=</span> <span class="sr">/^<\w+?>/</span><span class="p">.</span><span class="nx">exec</span><span class="p">(</span><span class="nx">innerHtml</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">startResult</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">start</span> <span class="o">=</span> <span class="nx">startResult</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
<span class="nx">innerHtml</span> <span class="o">=</span> <span class="nx">innerHtml</span><span class="p">.</span><span class="nx">substr</span><span class="p">(</span><span class="nx">start</span><span class="p">.</span><span class="nx">length</span><span class="p">);</span>
<span class="p">}</span>
<span class="kd">var</span> <span class="nx">endResult</span> <span class="o">=</span> <span class="sr">/<\/\w+?>$/</span><span class="p">.</span><span class="nx">exec</span><span class="p">(</span><span class="nx">innerHtml</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">endResult</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">end</span> <span class="o">=</span> <span class="nx">endResult</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
<span class="nx">innerHtml</span> <span class="o">=</span> <span class="nx">innerHtml</span><span class="p">.</span><span class="nx">substring</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nx">endResult</span><span class="p">.</span><span class="nx">index</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// 更新内部内容</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="nx">start</span> <span class="o">+</span>
<span class="s1">'<'</span> <span class="o">+</span> <span class="nx">splitConfig</span><span class="p">.</span><span class="nx">wrapTag</span> <span class="o">+</span> <span class="s1">'>'</span> <span class="o">+</span>
<span class="nx">innerHtml</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="nx">splitConfig</span><span class="p">.</span><span class="nx">splitReg</span><span class="p">,</span> <span class="s1">'</'</span> <span class="o">+</span> <span class="nx">splitConfig</span><span class="p">.</span><span class="nx">wrapTag</span> <span class="o">+</span> <span class="s1">'>$&<'</span> <span class="o">+</span> <span class="nx">splitConfig</span><span class="p">.</span><span class="nx">wrapTag</span> <span class="o">+</span> <span class="s1">'>'</span><span class="p">)</span> <span class="o">+</span>
<span class="s1">'</'</span> <span class="o">+</span> <span class="nx">splitConfig</span><span class="p">.</span><span class="nx">wrapTag</span> <span class="o">+</span> <span class="s1">'>'</span> <span class="o">+</span>
<span class="nx">end</span><span class="p">;</span>
<span class="k">break</span><span class="p">;</span>
<span class="k">default</span><span class="o">:</span>
<span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="c1">// 处理文本节点中被转义的html标签</span>
<span class="nx">$item</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="nx">$item</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">innerHTML</span>
<span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="k">new</span> <span class="nb">RegExp</span><span class="p">(</span><span class="s1">'<'</span> <span class="o">+</span> <span class="nx">splitConfig</span><span class="p">.</span><span class="nx">wrapTag</span> <span class="o">+</span> <span class="s1">'>'</span><span class="p">,</span> <span class="s1">'g'</span><span class="p">),</span> <span class="s1">'<'</span> <span class="o">+</span> <span class="nx">splitConfig</span><span class="p">.</span><span class="nx">wrapTag</span> <span class="o">+</span> <span class="s1">'>'</span><span class="p">)</span>
<span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="k">new</span> <span class="nb">RegExp</span><span class="p">(</span><span class="s1">'</'</span> <span class="o">+</span> <span class="nx">splitConfig</span><span class="p">.</span><span class="nx">wrapTag</span> <span class="o">+</span> <span class="s1">'>'</span><span class="p">,</span> <span class="s1">'g'</span><span class="p">),</span> <span class="s1">'</'</span> <span class="o">+</span> <span class="nx">splitConfig</span><span class="p">.</span><span class="nx">wrapTag</span> <span class="o">+</span> <span class="s1">'>'</span><span class="p">);</span>
<span class="nx">$item</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="nx">splitConfig</span><span class="p">.</span><span class="nx">wrapTag</span><span class="p">).</span><span class="nx">addClass</span><span class="p">(</span><span class="nx">splitConfig</span><span class="p">.</span><span class="nx">wrapCls</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">}</span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">上面代码中最后对文本节点中被转义的包裹标签替换似乎有点麻烦,但是没办法,ES5之前JavaScript并不支持正则的后行断言(也就是正则表达式中“后顾”)。所以没办法对包裹标签前后的 <code><</code> 和 <code>></code> 进行精准替换,只能连同标签名一起替换。</span>
</p>
<h2 id="toc_2" class="h16">事件处理</h2>
<p class="md_block">
<span class="md_line md_line_start md_line_end">在上面完成了文本获取和段落分隔,下面要做的就是鼠标移动上去时获取文本触发朗读即可,移开时停止朗读即可。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">鼠标移动,只读一次,基于这两点原因,使用 <code>mouseenter</code> 和 <code>mouseleave</code> 事件来完成。</span>
</p>
<p class="md_block md_block_as_opening md_has_block_below md_has_block_below_ol">
<span class="md_line md_line_start md_line_end">原因:</span>
</p>
<ol>
<li class="md_li"><span>不冒泡,不会触发父元素的再次朗读
</span></li>
<li class="md_li"><span>不重复触发,一个元素内移动时不会重复触发。
</span></li>
</ol>
<div class="codehilite code_lang_js highlight"><pre><span></span><span class="cm">/**</span>
<span class="cm"> * 在页面上写入高亮样式</span>
<span class="cm"> */</span>
<span class="kd">function</span> <span class="nx">createStyle</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">'speak-light-style'</span><span class="p">))</span> <span class="k">return</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">style</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s1">'style'</span><span class="p">);</span>
<span class="nx">style</span><span class="p">.</span><span class="nx">id</span> <span class="o">=</span> <span class="s1">'speak-light-style'</span><span class="p">;</span>
<span class="nx">style</span><span class="p">.</span><span class="nx">innerText</span> <span class="o">=</span> <span class="s1">'.'</span> <span class="o">+</span> <span class="nx">splitConfig</span><span class="p">.</span><span class="nx">hightlightCls</span> <span class="o">+</span> <span class="s1">'{'</span> <span class="o">+</span> <span class="nx">splitConfig</span><span class="p">.</span><span class="nx">hightStyle</span> <span class="o">+</span> <span class="s1">'}'</span><span class="p">;</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">getElementsByTagName</span><span class="p">(</span><span class="s1">'head'</span><span class="p">)[</span><span class="mi">0</span><span class="p">].</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">style</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// 非正文需要朗读的标签 逗号分隔</span>
<span class="kd">var</span> <span class="nx">speakTags</span> <span class="o">=</span> <span class="s1">'a, p, span, h1, h2, h3, h4, h5, h6, img, input, button'</span><span class="p">;</span>
<span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">on</span><span class="p">(</span><span class="s1">'mouseenter.speak-help'</span><span class="p">,</span> <span class="nx">speakTags</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">$target</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">);</span>
<span class="c1">// 排除段落内的</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">$target</span><span class="p">.</span><span class="nx">parents</span><span class="p">(</span><span class="s1">'.'</span> <span class="o">+</span> <span class="nx">splitConfig</span><span class="p">.</span><span class="nx">wrapCls</span><span class="p">).</span><span class="nx">length</span> <span class="o">||</span> <span class="nx">$target</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="s1">'.'</span> <span class="o">+</span> <span class="nx">splitConfig</span><span class="p">.</span><span class="nx">wrapCls</span><span class="p">).</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// 图片样式单独处理 其他样式统一处理</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">nodeName</span><span class="p">.</span><span class="nx">toLowerCase</span><span class="p">()</span> <span class="o">===</span> <span class="s1">'img'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">$target</span><span class="p">.</span><span class="nx">css</span><span class="p">({</span>
<span class="nx">border</span><span class="o">:</span> <span class="s1">'2px solid #000'</span>
<span class="p">});</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nx">$target</span><span class="p">.</span><span class="nx">addClass</span><span class="p">(</span><span class="nx">splitConfig</span><span class="p">.</span><span class="nx">hightlightCls</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// 开始朗读</span>
<span class="nx">speakText</span><span class="p">(</span><span class="nx">getText</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">));</span>
<span class="p">}).</span><span class="nx">on</span><span class="p">(</span><span class="s1">'mouseleave.speak-help'</span><span class="p">,</span> <span class="nx">speakTags</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">$target</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">$target</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="s1">'.'</span> <span class="o">+</span> <span class="nx">splitConfig</span><span class="p">.</span><span class="nx">wrapCls</span><span class="p">).</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// 图片样式</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">nodeName</span><span class="p">.</span><span class="nx">toLowerCase</span><span class="p">()</span> <span class="o">===</span> <span class="s1">'img'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">$target</span><span class="p">.</span><span class="nx">css</span><span class="p">({</span>
<span class="nx">border</span><span class="o">:</span> <span class="s1">'none'</span>
<span class="p">});</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nx">$target</span><span class="p">.</span><span class="nx">removeClass</span><span class="p">(</span><span class="nx">splitConfig</span><span class="p">.</span><span class="nx">hightlightCls</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// 停止语音</span>
<span class="nx">stopSpeak</span><span class="p">();</span>
<span class="p">});</span>
<span class="c1">// 段落内文本朗读</span>
<span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">on</span><span class="p">(</span><span class="s1">'mouseenter.speak-help'</span><span class="p">,</span> <span class="s1">'.'</span> <span class="o">+</span> <span class="nx">splitConfig</span><span class="p">.</span><span class="nx">wrapCls</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">$</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nx">addClass</span><span class="p">(</span><span class="nx">splitConfig</span><span class="p">.</span><span class="nx">hightlightCls</span><span class="p">);</span>
<span class="c1">// 开始朗读</span>
<span class="nx">speakText</span><span class="p">(</span><span class="nx">getText</span><span class="p">(</span><span class="k">this</span><span class="p">));</span>
<span class="p">}).</span><span class="nx">on</span><span class="p">(</span><span class="s1">'mouseleave.speak-help'</span><span class="p">,</span> <span class="s1">'.'</span> <span class="o">+</span> <span class="nx">splitConfig</span><span class="p">.</span><span class="nx">wrapCls</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">$</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nx">removeClass</span><span class="p">(</span><span class="nx">splitConfig</span><span class="p">.</span><span class="nx">hightlightCls</span><span class="p">);</span>
<span class="c1">// 停止语音</span>
<span class="nx">stopSpeak</span><span class="p">();</span>
<span class="p">});</span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">注意要把针对段落的语音处理和其他地方的分开。为什么? 因为段落是个块级元素,鼠标移入段落中的空白时,如:段落前后空白、首行缩进、末行剩余空白等,是不应该触发朗读的,如果不阻止掉,进行这些区域将直接触发整段文字的朗读,失去了我们对段落文本内分隔的意义,而且,无论什么方式转化语音都是要时间的,大段内容可能需要较长时间,影响语音输出的体验。</span>
</p>
<h2 id="toc_3" class="h16">文本合成语音</h2>
<p class="md_block">
<span class="md_line md_line_start md_line_end">上面我们是直接使用了 <code>speakText(text)</code> 和 <code>stopSpeak()</code> 两个方法来触发语音的朗读和停止。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">我们来看下如何实现这个两个功能。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">其实现代浏览器默认已经提供了上面功能:</span>
</p>
<div class="codehilite code_lang_js highlight"><pre><span></span><span class="kd">var</span> <span class="nx">speechSU</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">window</span><span class="p">.</span><span class="nx">SpeechSynthesisUtterance</span><span class="p">();</span>
<span class="nx">speechSU</span><span class="p">.</span><span class="nx">text</span> <span class="o">=</span> <span class="s1">'你好,世界!'</span><span class="p">;</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">speechSynthesis</span><span class="p">.</span><span class="nx">speak</span><span class="p">(</span><span class="nx">speechSU</span><span class="p">);</span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">复制到浏览器控制台看看能不能听到声音呢?(需要Chrome 33+、Firefox 49+ 或 IE-Edge)</span>
</p>
<p class="md_block md_block_as_opening md_has_block_below md_has_block_below_ul">
<span class="md_line md_line_start md_line_end">利用一下两个API即可:</span>
</p>
<ul>
<li class="md_li"><span><code>SpeechSynthesisUtterance</code> 用于语音合成
<ul>
<li class="md_li"><span><code>lang</code> : 语言 Gets and sets the language of the utterance.
</span></li>
<li class="md_li"><span><code>pitch</code> : 音高 Gets and sets the pitch at which the utterance will be spoken at.
</span></li>
<li class="md_li"><span><code>rate</code> : 语速 Gets and sets the speed at which the utterance will be spoken at.
</span></li>
<li class="md_li"><span><code>text</code> : 文本 Gets and sets the text that will be synthesised when the utterance is spoken.
</span></li>
<li class="md_li"><span><code>voice</code> : 声音 Gets and sets the voice that will be used to speak the utterance.
</span></li>
<li class="md_li"><span><code>volume</code> : 音量 Gets and sets the volume that the utterance will be spoken at.
</span></li>
<li class="md_li"><span><code>onboundary</code> : 单词或句子边界触发,即分隔处触发 Fired when the spoken utterance reaches a word or sentence boundary.
</span></li>
<li class="md_li"><span><code>onend</code> : 结束时触发 Fired when the utterance has finished being spoken.
</span></li>
<li class="md_li"><span><code>onerror</code> : 错误时触发 Fired when an error occurs that prevents the utterance from being succesfully spoken.
</span></li>
<li class="md_li"><span><code>onmark</code> : Fired when the spoken utterance reaches a named SSML "mark" tag.
</span></li>
<li class="md_li"><span><code>onpause</code> : 暂停时触发 Fired when the utterance is paused part way through.
</span></li>
<li class="md_li"><span><code>onresume</code> : 重新播放时触发 Fired when a paused utterance is resumed.
</span></li>
<li class="md_li"><span><code>onstart</code> : 开始时触发 Fired when the utterance has begun to be spoken.
</span></li>
</ul>
</span></li>
<li class="md_li"><span><code>SpeechSynthesis</code> : 用于朗读
<ul>
<li class="md_li"><span><code>paused</code> : <strong>Read only</strong> 是否暂停 A Boolean that returns true if the SpeechSynthesis object is in a paused state.
</span></li>
<li class="md_li"><span><code>pending</code> : <strong>Read only</strong> 是否处理中 A Boolean that returns true if the utterance queue contains as-yet-unspoken utterances.
</span></li>
<li class="md_li"><span><code>speaking</code> : <strong>Read only</strong> 是否朗读中 A Boolean that returns true if an utterance is currently in the process of being spoken — even if SpeechSynthesis is in a paused state.
</span></li>
<li class="md_li"><span><code>onvoiceschanged</code> : 声音变化时触发
</span></li>
<li class="md_li"><span><code>cancel()</code> : 情况待朗读队列 Removes all utterances from the utterance queue.
</span></li>
<li class="md_li"><span><code>getVoices()</code> : 获取浏览器支持的语音包列表 Returns a list of SpeechSynthesisVoice objects representing all the available voices on the current device.
</span></li>
<li class="md_li"><span><code>pause()</code> : 暂停 Puts the SpeechSynthesis object into a paused state.
</span></li>
<li class="md_li"><span><code>resume()</code> : 重新开始 Puts the SpeechSynthesis object into a non-paused state: resumes it if it was already paused.
</span></li>
<li class="md_li"><span><code>speak()</code> : 读合成的语音,参数必须为<code>SpeechSynthesisUtterance</code>的实例 Adds an utterance to the utterance queue; it will be spoken when any other utterances queued before it have been spoken.
</span></li>
</ul>
</span></li>
</ul>
<p class="md_block md_block_as_opening md_has_block_below md_has_block_below_ul">
<span class="md_line md_line_start md_line_end">详细api和说明可参考:</span>
</p>
<ul>
<li class="md_li"><span><a class="md_compiled" href="https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesisUtterance">MDN - SpeechSynthesisUtterance</a>
</span></li>
<li class="md_li"><span><a class="md_compiled" href="https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis">MDN - SpeechSynthesis</a>
</span></li>
</ul>
<p class="md_block">
<span class="md_line md_line_start md_line_end">那么上面的两个方法可以写为:</span>
</p>
<div class="codehilite code_lang_js highlight"><pre><span></span><span class="kd">var</span> <span class="nx">speaker</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">window</span><span class="p">.</span><span class="nx">SpeechSynthesisUtterance</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">speakTimer</span><span class="p">,</span>
<span class="nx">stopTimer</span><span class="p">;</span>
<span class="c1">// 开始朗读</span>
<span class="kd">function</span> <span class="nx">speakText</span><span class="p">(</span><span class="nx">text</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">clearTimeout</span><span class="p">(</span><span class="nx">speakTimer</span><span class="p">);</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">speechSynthesis</span><span class="p">.</span><span class="nx">cancel</span><span class="p">();</span>
<span class="nx">speakTimer</span> <span class="o">=</span> <span class="nx">setTimeout</span><span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nx">speaker</span><span class="p">.</span><span class="nx">text</span> <span class="o">=</span> <span class="nx">text</span><span class="p">;</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">speechSynthesis</span><span class="p">.</span><span class="nx">speak</span><span class="p">(</span><span class="nx">speaker</span><span class="p">);</span>
<span class="p">},</span> <span class="mi">200</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// 停止朗读</span>
<span class="kd">function</span> <span class="nx">stopSpeak</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">clearTimeout</span><span class="p">(</span><span class="nx">stopTimer</span><span class="p">);</span>
<span class="nx">clearTimeout</span><span class="p">(</span><span class="nx">speakTimer</span><span class="p">);</span>
<span class="nx">stopTimer</span> <span class="o">=</span> <span class="nx">setTimeout</span><span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">speechSynthesis</span><span class="p">.</span><span class="nx">cancel</span><span class="p">();</span>
<span class="p">},</span> <span class="mi">20</span><span class="p">);</span>
<span class="p">}</span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">因为语音合成本来是个异步的操作,因此在过程中进行以上处理。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">现代浏览器已经内置了这个功能,两个API接口兼容性如下:</span>
</p>
<table>
<thead>
<tr>
<th>Feature</th>
<th>Chrome</th>
<th>Edge</th>
<th>Firefox (Gecko)</th>
<th>Internet Explorer</th>
<th>Opera</th>
<th>Safari</th>
</tr>
</thead>
<tbody>
<tr>
<td>(WebKit) Basic</td>
<td>support 33</td>
<td>(Yes)</td>
<td>49 (49)</td>
<td>No support</td>
<td>?</td>
<td>7</td>
</tr>
</tbody>
</table>
<p class="md_block md_has_block_below md_has_block_below_ul">
<span class="md_line md_line_start md_line_end">如果要兼容其他浏览器或者需要一种完美兼容的解决方案,可能就需要服务端完成了,根据给定文本,返回相应语音即可,<a class="md_compiled" href="http://yuyin.baidu.com/docs">百度语音 http://yuyin.baidu.com/docs</a>就提供这样的服务。</span>
</p>
<ul>
<li class="md_li"><span><a class="md_compiled" href="https://github.com/cdswyda/show/tree/master/demo/speaker">cdswyda - 网页文本朗读实现 - github</a>
</span></li>
<li class="md_li"><span><a class="md_compiled" href="https://cdswyda.github.io/show/demo/speaker/"> cdswyda - 网页文本朗读实现 - demo</a>
</span></li>
</ul>
window.onload 触发时机问题
2017-12-07T13:51:47Z
20171207
依韵
<p class="md_block">
<span class="md_line md_line_start md_line_end">本文关键点: <code>window.onload</code> 和 页面上 <code>ajax</code> 的成功回调到底哪个先触发。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">答案是<strong>不确定</strong>。</span>
</p>
<h2 id="toc_0" class="h16">问题详情</h2>
<p class="md_block">
<span class="md_line md_line_start md_line_end">之前遇到一个现象,在父页面弹出一个Dialog加载一个子页面,在onload回调中传递一个参数给子页面,子页面异步ajax成功回调中要使用这个变量。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">然而出现的情况是在ajax的成功回调中大多数情况下是取不到这个在onload传来的值,但是偶尔又是可以的。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">查阅此Dialog源码,以上逻辑可以进行如下简化。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">父页面:</span>
</p>
<div class="codehilite code_lang_html highlight"><pre><span></span><span class="p"><</span><span class="nt">iframe</span> <span class="na">id</span><span class="o">=</span><span class="s">"iframe"</span> <span class="na">src</span><span class="o">=</span><span class="s">"./iframe.html"</span> <span class="na">onload</span><span class="o">=</span><span class="s">"onLoad()"</span> <span class="na">frameborder</span><span class="o">=</span><span class="s">"0"</span><span class="p">></</span><span class="nt">iframe</span><span class="p">></span>
<span class="p"><</span><span class="nt">script</span><span class="p">></span>
<span class="kd">function</span> <span class="nx">onLoad</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'iframe load'</span><span class="p">);</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">getElementsByTagName</span><span class="p">(</span><span class="s1">'iframe'</span><span class="p">)[</span><span class="mi">0</span><span class="p">].</span><span class="nx">contentWindow</span><span class="p">.</span><span class="nx">onLoad</span><span class="p">(</span><span class="s1">'load'</span><span class="p">);</span>
<span class="p">}</span>
<span class="p"></</span><span class="nt">script</span><span class="p">></span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">子页面:</span>
</p>
<div class="codehilite code_lang_html highlight"><pre><span></span><span class="p"><</span><span class="nt">script</span><span class="p">></span>
<span class="nx">$</span><span class="p">.</span><span class="nx">ajax</span><span class="p">(</span><span class="s1">'./test.json'</span><span class="p">).</span><span class="nx">done</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">a</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'ajax结果'</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a</span><span class="p">);</span>
<span class="p">});</span>
<span class="kd">function</span> <span class="nx">onLoad</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'window onload'</span><span class="p">)</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">e</span><span class="p">);</span>
<span class="p">};</span>
<span class="p"></</span><span class="nt">script</span><span class="p">></span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">由于iframe的 <code>onload</code> 即要加载页面的 <code>window.onload</code> ,因此情况可以进一步简化为一个页面中到底是 <code>window.onload</code> 先触发还是 <code>ajax</code> 的成功回调先触发。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">测试代码:</span>
</p>
<div class="codehilite code_lang_html highlight"><pre><span></span><span class="p"><</span><span class="nt">script</span><span class="p">></span>
<span class="nx">$</span><span class="p">.</span><span class="nx">ajax</span><span class="p">(</span><span class="s1">'./test.json'</span><span class="p">).</span><span class="nx">done</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">a</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'ajax结果'</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a</span><span class="p">);</span>
<span class="p">});</span>
<span class="kd">function</span> <span class="nx">onLoad</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'window onload'</span><span class="p">)</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">e</span><span class="p">);</span>
<span class="p">};</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="nx">onLoad</span><span class="p">;</span>
<span class="p"></</span><span class="nt">script</span><span class="p">></span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">这个页面除了在测试的script之前引入了jQuery没有其他代码,应该毫无疑问,是 <code>window.onload</code> 先触发,之后才是 <code>ajax</code> 的成功结果。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">结果也证明是 <code>window.onload</code> 先触发,上面代码在浏览器运行结果为:</span>
</p>
<div class="codehilite code_lang_js highlight"><pre><span></span><span class="c1">// window onload</span>
<span class="c1">// Event {}</span>
<span class="c1">// ajax结果</span>
<span class="c1">// {}</span>
</pre></div>
<!--block_code_end-->
<p class="md_block md_has_block_below md_has_block_below_blockquote">
<span class="md_line md_line_start md_line_end">MDN上关于 <code>window.onload</code> 有如下解释:</span>
</p>
<blockquote class="blockquote_lines_1">
<p class="md_block">
<span class="md_line md_line_start md_line_end">The load event fires at the end of the document loading process. At this point, all of the objects in the document are in the DOM, and all the images, scripts, links and sub-frames have finished loading.</span>
</p>
</blockquote>
<p class="md_block">
<span class="md_line md_line_start md_line_end">那么问题就来了,如果必然是 window.onload 先触发,那么是不可能出现最开始的问题的。</span>
</p>
<h2 id="toc_1" class="h16">伪解释</h2>
<p class="md_block">
<span class="md_line md_line_start md_line_end">继续修改测试代码,再加上一些东西:</span>
</p>
<div class="codehilite code_lang_html highlight"><pre><span></span><span class="p"><</span><span class="nt">script</span><span class="p">></span>
<span class="nx">$</span><span class="p">.</span><span class="nx">ajax</span><span class="p">(</span><span class="s1">'./test.json'</span><span class="p">).</span><span class="nx">done</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">a</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'ajax结果'</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a</span><span class="p">);</span>
<span class="p">});</span>
<span class="kd">function</span> <span class="nx">onLoad</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'window onload'</span><span class="p">)</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">e</span><span class="p">);</span>
<span class="p">};</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="nx">onLoad</span><span class="p">;</span>
<span class="c1">// 其他代码xxx</span>
<span class="c1">// 模拟一个一分钟循环</span>
<span class="kd">var</span> <span class="nx">t1</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">getTime</span><span class="p">();</span>
<span class="k">while</span><span class="p">(</span><span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">getTime</span><span class="p">()</span> <span class="o">-</span> <span class="nx">t1</span> <span class="o"><</span> <span class="mi">1</span> <span class="o">*</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">)</span> <span class="p">{</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">querySelectorAll</span><span class="p">(</span><span class="s1">'*'</span><span class="p">);</span>
<span class="p">}</span>
<span class="p"></</span><span class="nt">script</span><span class="p">></span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">写入一个一分钟的循环后,结果发生了改变:</span>
</p>
<div class="codehilite code_lang_js highlight"><pre><span></span><span class="c1">// ajax结果</span>
<span class="c1">// {}</span>
<span class="c1">// window onload</span>
<span class="c1">// Event {}</span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">这么来看就奇怪了呀, <code>ajax</code> 的成功比 <code>window.onload</code> 先触发。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">关于这个现象,我也没找到权威的解释。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">自己给了一个“合理”的解析:</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end"><code>window.onload</code> 会在当前任务队列的最后一个触发。如最开始的例子,<code>ajax</code> 异步,尚未给出结果,页面需要等待的所有内容已经完成,任务队列为空,因此 <code>window.onload</code> 触发。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">而后面这个由于 <code>ajax</code> 后面还有很长的代码要执行,这段代码推迟了 <code>onload</code> 的触发,同时这段代码还未执行完成时,之前异步的ajax已经返回了结果,成功回调的代码已经被加到了任务队列,因此 <code>ajax</code> 回调执行后才触发 <code>window.onload</code>。</span>
</p>
<h2 id="toc_2" class="h16">再验证</h2>
<p class="md_block">
<span class="md_line md_line_start md_line_end">为了进一步验证我上面的想法,那么只要保证页面资源执行完成时,ajax还没有解决即可。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">因此还是上面的代码,但是将请求的内容换成一个真实接口,这个真实接口返回的数据更晚即可。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">使用php暂停120s再返回结果,代码如下:</span>
</p>
<div class="codehilite code_lang_php highlight"><pre><span></span><span class="o"><?</span><span class="nx">php</span>
<span class="nb">sleep</span><span class="p">(</span><span class="mi">120</span><span class="p">);</span>
<span class="k">echo</span> <span class="s1">'{"response":"two minutes later."}'</span>
<span class="cp">?></span><span class="x"></span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">结果却是如上面估计的一样:</span>
</p>
<div class="codehilite code_lang_js highlight"><pre><span></span><span class="c1">// window onload</span>
<span class="c1">// Event {}</span>
<span class="c1">// ajax结果</span>
<span class="c1">// {"response":"two minutes later."}</span>
</pre></div>
<!--block_code_end-->
<p class="md_block">
<span class="md_line md_line_start md_line_end">可以说明之前的“合理”解释确实是合理的。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">所以异步的 <code>ajax</code> 和 <code>window.onload</code> 到底哪个会先触发是不确定,和你js代码(或者其他onload要等待的资源,如一个图片加载很慢等)以及这个 <code>ajax</code> 的解决时间有关系。</span>
</p>
<p class="md_block">
<span class="md_line md_line_start md_line_end">因此这种情况下的传值就不能以这种方式进行,可以换成更稳妥的方式,如直接跨页面操作或者放在url进行传递。</span>
</p>