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

tagName的大小写问题(QWrap选择器的一个bug)

阅读更多
今儿写程序。

对于现代Web前端来说,Selector是必备之物。对于标准浏览器,可用querySelector,而对于IE8标准模式以下,需要一个Selector引擎。

因为本项目会在代码里使用QWrap,所以虽然我写的部分代码并没有用QWrap,但既然怎样都需要一个Selector引擎,不如用QWrap。

结果发生一件悲剧的事情。

我使用html5-shim/shiv库来让IE正确解析新的HTML5标签。但是QWrap引擎居然不能正确选择出html5的元素。

尝试人肉选择,发现是可以的,那就是QWrap的Selector存在bug。

经过艰苦卓绝的斗争,最终发现问题出在这个函数的第2行:

	function(a) { 
		sFun.push('el.tagName=="' + a.toUpperCase() + '"');
		return '';
	}).replace(/([\[(].*)|#([\w\-]+)|\.([\w\-]+)/g,//id缩略写法//className缩略写法


QWrap采用了代码生成技术,即为selector拼装出对应的函数。这是一项相当陷阱先进的技术。

不难看出这里会产生一个tag的匹配,对于匹配“nav”所生成的代码应该是:el.tagName=="nav".toUpperCase() 。

通常浏览器对于所有HTML元素,其调用tagName返回的都是全大写字母。


【号外】为什么是大写?

Hax答:这是从HTML的祖先SGML那里继承下来的习惯。对于早期写SGML/HTML的人,要区分标签和正文内容的最简单方法恐怕就是把标签用大写。

不过,据圈子里有名的那个纹身佬说,HTML用大写是因为那时候技术落后,存储太紧张,全大写比较省存储空间……(大意如此,详情我记不清了,请八卦人士咨询Winter)

【/号外】


然而不幸的是,html5-shim以及我所知差不多所有的类似库,都会使用全小写。这是因为现代Web标准的主流是采用全小写。


【号外】为什么换小写了涅?

Hax答:因为这样比较不伤眼,也不伤手。

(每天面对满屏幕大写字母的伤不起啊,看UPPERCASE看到神经衰弱啊,有木有,有木有!
(只能用小键盘笔记本打字的伤不起啊,打UPPERCASE打到小手指抽筋,有木有,有木有!

【/号外】

而IE虽然对于它所能识别的HTML元素都是大小写不敏感的,但是对于通过createElement神经刀产生的新元素,它其实将其视作类XML元素,也就是大小写敏感的,所以其tagName属性将返回最初设定的大小写形式。

如何fix这个问题?

一个容易想到的方式是把html5-shim里的标签列表改为大写。不过这个方式并不管用。因为tagName返回的是最初设定的值,也就是,如果你写<SECTION>...</SECTION>,返回的是SECTION,如果你写<SECtion>...</section>返回的就是SECtion(即start tag的大小写),如果你写document.createElement('sEcTion'),返回的就是sEcTion。

显然,QWrap Selector(或任何通用脚本库)不应依赖网页作者如何书写。所以这个问题必须由QW来解决。

此外,库也不应该假设tagName一定返回大写。虽然规范规定对于HTML元素tagName应该始终返回大写,但库必须考虑兼容性(即这里所提到的IE的问题)。

此外,通用脚本库也要有前瞻性,比如考虑Selector引擎用于选择XML元素。当前各种纯JS selector engine并非namespace-aware,所以本不能选择XML元素。但HTML规范已经允许直接在HTML里混合MathML、SVG。新的浏览器也都已经支持了。

比如你可以试着在FireFox里看下述代码:
<body>
<div id="test"><math>
    <mi>x</mi>
    <mo>=</mo>
    <mfrac>
     <mrow>
      <mo form="prefix">−</mo> <mi>b</mi>
      <mo>±</mo>
      <msqrt>
       <msup> <mi>b</mi> <mn>2</mn> </msup>
       <mo>−</mo>
       <mn>4</mn> <mo>⁢</mo> <mi>a</mi> <mo>⁢</mo> <mi>c</mi>
      </msqrt>
     </mrow>
     <mrow>
      <mn>2</mn> <mo>⁢</mo> <mi>a</mi>
     </mrow>
    </mfrac>
   </math></div>
</body>


你可以看到$('test').firstChild.tagName返回的是“math”而不是“MATH”。而document.querySelector('#test math')也可以正确选择到该元素。

但是QW Selector就不能选择到math元素了。

【扩展】
假如你在这个文档里插入document.createElement('math')会发生什么呢?
注意,你插入的不是一个MathML元素,那需要通过createElementNS,加上适当的namespace(http://www.w3.org/1998/Math/MathML)才可以。你插入的其实是一个名字恰好为“math”的HTML元素,该元素的tagName返回的是全大写的“MATH”。

此时,使用getElementsByTagName('math')或document.querySelectorAll('math')会返回这两个元素。而getElementsByTagName('MATH')或document.querySelectorAll('MATH')只会返回那个正好叫做“math”的HTML元素,而不会返回真正的MathML的math元素。注:FF行为如此,而目前Chrome是两者都返回的,这应该是WebKit的bug。
【/扩展】

尽管纯JS Selector引擎主要的目的是向前兼容,但若能做到向后兼容就更好了。而QW Selector由于这个小小的大小写问题,在两方面都失败了。

好在,修正它是很容易的。

sFun.push('el.tagName=="' + a.toUpperCase() + '"');
改为
sFun.push('el.tagName.toLowerCase()=="' + a.toLowerCase() + '"');
即可。

有人可能会问,为啥全换成toLowerCase()?

之前人家木有说过吗,UPPERCASE什么的讨厌了!有木有!有木有!有木有!

说正经的,用toLowerCase()是因为标准所规定的行为就是这样的。尽管全换成toUpperCase()似乎也没有什么不一样的。

上述代码的结果和目前Chrome的行为较为一致,即即使是非HTML namespace的元素,也按照大小写不敏感的方式比较。如果要按照FF的行为,可以改为:

'isHTMLElement(el) ? tagName.toLowerCase() == "{a.toLowerCase()}" : tagName == "{a}"'

上述isHTMLElement检测一个元素是否是HTML元素,逻辑请自行查标准确定。{a}这里用模板语法,这是为了让大家看得更明白,我也省下打许多引号和加号的力气。


好了,我已经在咆哮体上浪费太多时间了,再不交活,老板要对我咆哮了。加班去鸟。。。




0
3
分享到:
评论
2 楼 satans17 2011-07-18  
1 楼 hunter3721 2011-07-17  
通用脚本库要有前瞻性!!  握手!!!

PS:
用咆哮体的人伤不起啊!!!
要排版!! 要改粗体!! 要改字号!! 有木有!! 有木有!! 有木有!!

相关推荐

    qwrap javascript框架,qwrap

    QWrap 是一个基于JavaScript的轻量级框架,旨在简化前端开发过程,提高代码的可维护性和性能。QWrap 的核心理念是提供一个简洁、高效且易于学习的API,让开发者可以快速构建交互丰富的Web应用。 在QWrap中,"wrap...

    QWrap是百度有啊前端团队推出的一个javascript框架

    QWrap,源自百度有啊前端团队的创新之作,是一个专为JavaScript开发打造的高效、轻量级框架。这个框架的设计目标是简化Web应用的构建过程,提高开发效率,并且优化前端性能。QWrap的核心理念是模块化、组件化,使得...

    IE8提示Invalid procedure call or argument 异常的解决方法

    4. QWrap代码错误点:在这个问题中,还涉及到了QWrap,这是一个在旧版浏览器中使用的兼容性封装,用于模拟一些现代浏览器提供的DOM操作方法。在这个例子中,QWrap的nativeQuery函数处理了查询选择器,但在某些情况下...

    newpages_incognito.zip

    【标题】"newpages_incognito.zip" 是一个包含与隐私浏览模式相关的文件的压缩包,很可能是某个浏览器扩展或网页应用的一部分。"incognito" 一词通常与浏览器的无痕浏览模式相关联,该模式下用户的浏览历史、Cookies...

    jquery-suggest:jQuery的建议

    jQuery-Suggest 是一个基于 jQuery 的 suggest 组件,它主要为 PC 端的搜索框提供建议词条。 为什么要做这个组件 我曾经做过一个基于 Zepto 简洁实用的 suggest 组件:。 这个组件稍微改造即可在 jQuery 下使用...

    newpages_incognito

    标题 "newpages_incognito" 暗示我们可能在探讨一个与隐私浏览模式或无痕浏览相关的项目。在这个场景下,"incognito"通常指的是浏览器的隐私浏览模式,该模式下浏览器不会保存用户的浏览历史、临时互联网文件、表单...

    newpages_incoh

    `grid9_qwrap.js` 是一个JavaScript文件,JavaScript通常用于增加网页的交互性和动态功能。在这个项目中,它可能是Grid9布局的JavaScript实现,用于处理用户交互、动画效果或者页面元素的动态行为。 `bg_search_bar...

    onclick与listeners的执行先后问题详细解剖

    这里的`W('#div1')`看起来像是一个库(可能是QWrap库)的选择器,它模拟了原生的`addEventListener`方法来添加事件监听器,并且调用了`fire('click')`来手动触发事件。因此,`addEventListener`注册的事件监听器`fun...

    浅谈JavaScript 框架分类

    mootools 1.3就是一个典型的例子,它将所有类型封装为Type类型,便于管理和扩展。 第三类是以jQuery为代表的以选择器为导向的框架。jQuery以其简洁的选择器API和强大的DOM操作能力著称,例如“无new实例化”技术、...

    HP网卡驱动

    这些文件可能属于一个网页或在线文档,用于展示或介绍HP网卡驱动的功能、优势或其他相关信息。例如,"network-broken.png"可能代表网络问题的图标,而"welcome.png"可能是引导用户安装驱动的欢迎界面。 综上所述,...

    window.event快达到全浏览器支持了,以后使用就方便了

    然而,在早期的非IE浏览器中,如Firefox、Chrome、Safari和Opera,并不直接支持`window.event`,它们通常会将事件对象作为事件处理函数的第一个参数传递,如`function(event)`中的`event`。 标题和描述中提到的情况...

    程序模型与表达_月影.pptx

    函数式编程强调将问题拆解为不可变的原子操作,如Erlang中的快速排序,它鼓励无副作用的纯函数,从而提高代码的可预测性和可测试性。在Qwrap设计中,就运用了函数式编程的思想。另一方面,面向对象编程注重通过类和...

    前端架构技术选型文档文档

    为了更好地满足不同业务需求,各大互联网公司纷纷研发了自己的前端框架,如阿里的Kissy和Arale、腾讯的JX、百度的Tangram以及360的QWrap等。本文旨在探讨前端架构技术选型的原则与方法,并通过对比分析当前主流前端...

    js 不用重复检测浏览器提高效率

    而在方式2中,浏览器检测逻辑集中在一个地方,未来维护起来更加方便。 4. 灵活性:方式1虽然牺牲了一些效率,但可以灵活地对每个元素进行判断,适应性较强。方式2则牺牲了一定的灵活性,以提高效率为代价。 具体到...

Global site tag (gtag.js) - Google Analytics