`
hax
  • 浏览: 961337 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

批量修改style采取哪种方式好(续篇)

    博客分类:
  • AJAX
阅读更多
前篇见批量修改style采取哪种方式好,主要是回答fins的提问。

下面我来说说我们实际期望怎样的编程方式。

假设一个这样的需求:

页面上有一些文本是highlight的。例如,javaeye的文章如果是点击google搜索结果过来的,javaeye的后台会自动判断出关键字,并为这些关键字包裹上标记(<span class="hilite1">关键字</span>)。

我们现在希望有这样一个功能,就是允许开启/关闭highlight。

如果关闭的话,那么大家通常可以想到的做法,就是检索所有的.hilite1的元素,然后去掉这个class。

$('span.hilite1').forEach(function(e){e.className=''});

但是这样做好之后,我们就无法再次开启highlight了!所以,我们要换种做法。一种方式是遍历所有的.hilite1然后替换为.hilite0。这样下次就可以找回来。不过正如上一篇文章中所说的,我们其实有更加经济的做法。

我们在body上加入一个class: <body class="hiliteEnabled"...> ,然后把样式改为: body.hiliteEnabled span.hilite1 {...} ,这样,当要关闭highlight的时候,差不多就只需要 document.body.className = '' ,就可以了。

Ok,我们完成这个功能了。

这个时候,有用户希望我们提供另一个很cool的功能,即在页面下方加入一个slider,然后用户可以拖动slider改变highlight部分文字的字体大小。

这就类似于我上一篇中提到的批量修改的问题了。我们有两种做法:

A.
$('#fontSizeSlider').onchange = function() {
  var size = this.value;
  $('span.hilite1').forEach(function(e){
    e.style.fontSize = size;
  });
}

B.
$('#fontSizeSlider').onchange = function() {
  var size = this.value;
  getStyle('span.hilite1').fontSize = size;
}
function getStyle(selector){
  //示意代码
  return document.styleSheets[0].cssRules[0].style;
}


单纯看的话,托各种支持selector query的library的福,A做法是很简单的。B做法也不复杂。

但是还记得我们第一个开启关闭的功能么?如果highlight被关闭了,显然,从用户的角度上说,应该也禁用修改font size的效果。乍一看这个很简单,改成select出 body.hiliteEnabled span.hilite1 然后遍历就好了。

对于B做法来说,确实如此,你只需确保getStyle()返回的是针对 body.hiliteEnabled span.hilite1 的样式对象即可。

但是注意这点对于A做法是不够的!因为你给所有的body.hiliteEnabled span.hilite1都加上了inline style,这个是不会自动消失的。所以你需要在 document.body.className = '' 之后加上清理语句 resetFontSize(false) 而在再次开启的语句 document.body.className = 'hiliteEnabled' 之后也需要加上 resetFontSize(true) 。该函数的代码如下:

function resetFontSize(flag) {
  var v = flag ? $('#fontSizeSlider').value : null;
  $('span.hilite1').forEach(function(e){
    e.style.fontSize = v;
  });
}


这个味道就不太好。这倒不是因为两个功能被交织了起来。我们原来修改body.className其实隐含了关闭/开启highlight的语义,所以fontSize生效与否受到它的影响是正常的。你可以把“document.body.className = ''; resetFontSize(false)”纳入一个disableHilite()函数中。

这里的问题实际是,每次你加入一个与highlight有关的新功能(例如我们下次可能允许大家定制hilite的颜色),你就需要修改disableHilite()/enableHilite(),加上新功能的清理和初始化代码的调用。这显然味道很不好。

各位可能会想到观察者模式了!

是的,你可以把hilite启用和禁用做成一个事件,然后其他功能都来订阅这个事件,并调用各自的初始化和清理代码。

不错不是嘛。问题都迎刃而解了!

不过还有一个问题。在这里,我们开启/禁用,只是一个二选一的问题。但是我们也可能遇到(制造出)更复杂的需求。比如假设是论坛帖子,除了整个页面的开启/禁用之外,每个回复都可以单独开启禁用hilite(即每个article元素上可以有.hiliteEnabled或.hiliteDisabled,如果没有任何一个class,则看body上是否有.hiliteEnabled),局域的设置override上层设置。这时,你就惨了,因为你的初始化/清理代码是针对整个页面写的,你必须改造成针对一个区域进行初始化和清理。你可能需要把用于遍历的selector作为事件的一个信息来传递。你的事件触发也需要重新写过,可能要让disableHilite()/enableHilite()能够接受一个参数指定操作范围,显然这个参数最好也用css selector。

Ok,这是我生造的需求,所以你会觉得不合理,不过对于程序员来说,需求一般总是不合理的。呵呵。我们这里只是举例。

其实我们从上面可以看到一个线索,那就是hilite启用与否,实际上可以取决于某个selector的模式匹配,因为我们通常把带有语义的开关存放在元素的class属性中。对于最初的简单需求来说:
匹配body.hiliteEnabled span.hilite1,就启用hilite以及hilite相关的功能,
不匹配body.hiliteEnabled span.hilite1,就禁用hilite以及hilite相关的功能。
每个hilite功能(如动态改变fontSize)去监听我们自制的hilite事件来进行初始化和清理,其实也可以等价于监听这一匹配的变化(如果我们能够监听的话)。

对于我们下面人为制造的需求,其实可以转化为:
(注:article元素表示整个页面中每个单独的帖子)
匹配body article.hiliteEnabled span.hilite1,启用hilite,
匹配body.hiliteEnabled article span.hilite1,也启用hilite,
除非匹配body article.hiliteDisabled span.hilite1,则禁用hilite。

如果写成一个单一的selector,就是:
article.hiliteEnabled span.hilite1, body.hiliteEnabled article:not(.hiliteDisabled) span.hilite1

一连串复杂的逻辑,其实就可以简化为对于这一模式匹配的监听。

这样我们期望中的代码就呼之欲出了:

首先,启用和禁用hilite,就是简单的直接对元素(body或article)上设置className。

然后我们这样写:

var hiliteSelector = new Selector('article.hiliteEnabled span.hilite1, body.hiliteEnabled article:not(.hiliteDisabled) span.hilite1');

function initHiliteFontSizeFeature() {
  hiliteSelector.addEventListener('match', function(evt){
    var hiliteSpan = evt.target;
    hiliteSpan._syncFontSize = function(evt) {
      hiliteSpan.style.fontSize = evt.target.value;
    };
    hiliteSpan.style.fontSize = $('#fontSizeSlider').value;
    $('#fontSizeSlider').addEventListener('change', hiliteSpan._syncFontSize, false);
  }, false);
  hiliteSelector.addEventListener('unmatch', function(evt){
    var hiliteSpan = evt.target;
    $('#fontSizeSlider').removeEventListener('change', hiliteSpan._syncFontSize, false);
    hiliteSpan._syncFontSize = null;
    hiliteSpan.style.fontSize = null;
  }, false);
}


也就是,如果有一个Selector API提供给我们监听match/unmatch事件的话,要做的事情就非常简单了!

理想中,Selector会产生match/unmatch事件,并自动dispatch到所有匹配的节点上。

不过,我们现在并没有这样的API……querySelector及各种library提供的,都是一次性取出符合条件的节点,而没有监听的功能。

实际上,在现有浏览器内使用JavaScript来实现这一API,是相当困难的。但是,我们知道,浏览器内部肯定有等价的功能,因为stylesheet的应用就是遵循这样的机制的。而且我们知道,IE的htc和Mozilla的XBL,正是利用这样一种机制的!未来的XBL2规范,也是如此!

在下一篇blog中,我会拿htc、xbl1和xbl2,来实现我们上面提到的例子。
12
3
分享到:
评论
2 楼 hax 2008-08-24  
因为懒惰所以没有继续写htc、xbl。不过需要补充一下,本文所说的这样一种对于selector的match和unmatch监控,恕我孤陋寡闻,其实在我写作本文之前已经有实现了。那就是jQuery的live query插件。可看http://blog.brandonaaron.net/2007/08/19/new-plugin-live-query/

其实现方法大体是用定时器检查dom的变化,从而触发match/unmatch。
1 楼 fins 2008-02-24  
非常受用! 谢谢!

另外 您能不能就 XBL多谈一谈呢? 那片太简短了 网络上相关的资料也不多

还有 很像听听您对 xforms 的看法

相关推荐

    发现的种子 科学研究的艺术 续篇

    发现的种子 科学研究的艺术 续篇 【英】 W.I.B 贝弗里奇 著 中文 译本 金吾伦 李亚东 译

    《猴子种果树》续篇作文.doc

    《猴子种果树》的故事续篇讲述了一只小猴子在经历了连续的失败后,最终通过坚持和毅力成功种植出梨树的故事。这个故事蕴含了多个重要的知识点,值得我们深入探讨。 首先,故事强调了决策的重要性。小猴子起初在乌鸦...

    物联网时代企业竞争战略(续篇)(中文版).pdf

    物联网时代企业竞争战略(续篇) 本文讨论了智能互联产品对企业的内部影响,包括制造企业几乎所有职能部门的工作的重新定义和全新的职能部门的诞生。智能互联产品将彻底改变制造企业的传统组织架构,迎来第二次工业...

    狐假虎威续篇作文.doc

    这篇文档实际上是一个寓言故事的续写,名为“狐假虎威续篇”,它基于中国古代寓言《狐假虎威》。在这个续篇中,作者通过动物角色——老虎和狐狸,传达了一些关于智慧、恐惧和欺骗的主题。以下是该文所涉及的一些知识...

    我要破解再续篇.chm

    我要破解再续篇.chm

    《狼和小羊》续篇作文.doc

    这篇文档实际上是一个儿童故事的续篇,以"《狼和小羊》续篇"为标题,描述了小羊被狼攻击后,羊群团结起来对抗狼的故事。虽然标签为"范文",但我们可以从中提取出一些教育和团队合作的相关知识点。 1. **团结的力量*...

    掉进陷阱里的狼续篇作文.doc

    该文本的标题和描述均为“掉进陷阱里的狼续篇作文”,这表明该文本是一篇续篇作文,续篇作文是一种常见的文体,旨在延续和发展前一篇作文的思想和情节。在本文中,作者通过续篇作文的形式,继续讲述狼的故事,展现了...

    FDTD案例分析续篇.pdf

    文档可能会展示如何通过调整布局参数、改变材料属性等方式来改进设备的性能指标。 ### 总结 《FDTD案例分析续篇》这一文档深入探讨了FDTD方法在不同领域的应用案例及其技术细节。从VCSEL到SPR技术,再到具体的仿真...

    Android开发入门之路(续篇).zip

    Android开发入门之路(续篇).zip

    ASP.NET MVC下的四种验证编程方式[续篇]

    在《ASP.NET MVC的四种验证编程方式》中,我们探讨了四种服务端验证方法:手工验证、使用ValidationAttribute特性、实现IValidatableObject接口以及实现IDataErrorInfo接口。本文将深入探讨ASP.NET MVC框架内部如何...

    2017新版部编本二年级上册《狐假虎威》续篇.pdf

    2017新版部编本二年级上册《狐假虎威》续篇.pdf

    java知识点总结之续篇2

    ### Java知识点总结之续篇2 #### File与流操作 1. **File**: 在Java中,`File`类用于封装一个路径名字符串,这个路径名字符串可以表示一个文件或者一个目录。通过`File`对象,我们可以对文件进行一系列的操作,如...

    发现的艺术 《科学研究的艺术》续篇

    《科学研究的艺术》续篇 第一章 论产生概念和解决问题,着重阐述创造性思维对科学的贡献和怎样的思维方式,法则和方法能促进创造性在科学发现中的作用。  第二章 讨论机遇和机会在科学发现中的角色。  科学家都...

    Verilog HDL那些事儿-时续篇

    - **基于综合语言编辑激励文件**:利用Verilog HDL语言本身的能力来编写激励文件是一种高效的方法。这种方法不仅可以提高测试的覆盖率,还能更好地理解和调试设计。 #### 六、时序设计的细化 “时序篇”强调了时序...

    编程规范续篇(C#)

    - **可维护性**:编写易于修改和扩展的代码,降低长期维护的成本。 - **最佳实践**:遵循社区广泛接受的最佳实践,如使用命名空间、接口和异常处理。 2. **术语定义** - **Pascal大小写**:每个单词首字母大写,...

    气泡聚变有续篇

    气泡聚变有续篇

Global site tag (gtag.js) - Google Analytics