Javascript在Firefox上开发的典型原因是什么?
我开发了一些JavaScript增强页面,在最近的Firefox和Safari上运行良好。 我错过了在Internet Explorer中检查,现在我发现这些页面在IE 6和7(到目前为止)上不起作用。 该脚本不知何故不执行,页面显示,如果JavaScript不存在,虽然一些JavaScript执行。 我正在使用dom操作自己的库,从YUI 2我使用YUI-Loader和XML-Http-Request,并在一个页面上使用“psupload”,这取决于JQuery。
我从Office XP安装Microsoft脚本编辑器,现在将进行debugging。 我现在也会写特定的testing。
什么是IE的典型失败点? 我可以保持睁大眼睛的方向。
我发现这个页面,显示了一些不同之处。 请访问: Quirksmode
你能从你的经历中找出一些我应该首先寻找的典型的东西吗?
以后我还会在这里提出更多的问题来处理特定的任务,但是现在我对你的经验感兴趣,为什么IE通常在Firefox中运行正常的脚本上失败
编辑:谢谢你所有这些伟大的答案!
在此期间,我已经调整了整个代码,以便它也适用于Internet Explorer。 我整合了jQuery,现在在上面build立了自己的类。 这是我的基本错误,我从一开始就没有在jQuery上构build所有的东西。 我现在有。
JSLint也帮了我很多。
许多来自不同答案的单个问题都有所帮助。
如果您发现任何错误/遗漏等,请随时更新此列表。
注意: IE9修复了很多以下的问题,所以很多这个只适用于IE8及以下,并且在一定程度上适用于IE9的怪异模式。 例如,IE9本地支持SVG, <canvas>
, <audio>
和<video>
,但您必须启用标准兼容模式才能使用它们。
一般:
-
部分加载文档的问题:将JavaScript添加到
window.onload
或类似事件是一个好主意,因为IE在部分加载的文档中不支持许多操作。 -
不同的属性 :在CSS中,它是IE中的
elm.style.cssFloat
和Firefox中的elm.style.cssFloat
。 在<label>
标签中,for
属性是通过IE中的elm.for
和Firefox中的elm.for
的。 请注意,for
在IE中保留,所以elm['for']
可能是阻止IE引发exception的更好的主意。
基本的JavaScript语言:
-
访问string中的字符 :
'string'[0]
在IE中不受支持,因为它不在原始JavaScript规范中。 使用'string'.charAt(0)
或'string'.split('')[0]
注意访问数组中的项目比在string中使用charAt
要快得多(尽pipe在第一次调用split
时会有一些初始开销。 ) -
对象结束之前的逗号:例如
{'foo': 'bar',}
在IE中是不允许的。
特定元素的问题:
-
获取IFrame的
document
:- Firefox和IE8 +:
IFrame.contentDocument
(IE开始支持这个从版本8。 ) - IE:
IFrame.contentWindow.document
- (
IFrame.contentWindow
指向两个浏览器中的window
。)
- Firefox和IE8 +:
-
canvas: IE9以前的版本不支持
<canvas>
元素。 IE不支持VML这是一个类似的技术,而explorercanvas可以为许多操作提供<canvas>
元素的原地包装。 请注意,在使用VML时,标准兼容模式下的IE8比使用Quirks模式时慢了许多倍,并且有更多的毛刺。 -
SVG: IE9本地支持SVG。 IE6-8可以支持SVG,但只有外部插件只有一些支持JavaScript操作的插件。
-
<audio>
和<video>
:仅在IE9中受支持。 -
dynamic创build单选button: IE <8有一个错误,使用
document.createElement
创build单选button不可检查。 另请参见如何在Javascript中dynamic创build一个可在所有浏览器中工作的单选button? 为了解决这个问题。 -
IE中的
<a href>
标记和onbeforeunload
冲突中的embedded式JavaScript :如果在标记的href
部分中embedded了JavaScript(例如<a href="javascript: doStuff()">
那么IE将始终显示从onbeforeunload
返回的消息除非预先删除了onbeforeunload
处理程序。另请参阅closures选项卡时询问确认 。 -
<script>
标记事件差异: IE中不支持onsuccess
和onerror
,并且被一个特定于IE的onreadystatechange
所取代,无论下载是成功还是失败,都会被触发。 更多信息请参阅JavaScript疯狂 。
元素大小/位置/滚动和鼠标位置:
-
获取元素大小/位置 :元素的宽度/高度有时在IE中是
elm.style.pixelHeight/Width
,而不是elm.offsetHeight/Width
,但在IE中都不可靠,特别是在怪异模式下,有时会给出比另一个。elm.offsetTop
和elm.offsetLeft
经常被错误地报告,导致元素的位置不正确,这就是为什么在很多情况下popup元素等几个像素被closures的原因。还要注意的是,如果元素(或元素的父元素)
display
为none
那么当访问大小/位置属性时IE会引发exception,而不是像Firefox那样返回0
。 -
获取屏幕大小 (获取屏幕的可视区域):
- Firefox:
window.innerWidth/innerHeight
- IE标准模式:
document.documentElement.clientWidth/clientHeight
- IE怪癖模式:
document.body.clientWidth/clientHeight
- Firefox:
-
文档滚动位置/鼠标位置 :这个实际上并不是由w3c定义的,所以即使在Firefox中也是非标准的。 要find
document
的scrollLeft
/scrollTop
:- Firefox和IE在怪异模式:
document.body.scrollLeft/scrollTop
- IE在标准模式下:
document.documentElement.scrollLeft/scrollTop
-
注意:其他一些浏览器也使用
pageXOffset
/pageYOffset
。function getDocScrollPos() { var x = document.body.scrollLeft || document.documentElement.scrollLeft || window.pageXOffset || 0, y = document.body.scrollTop || document.documentElement.scrollTop || window.pageYOffset || 0; return [x, y]; };
为了获得鼠标光标的位置,
mousemove
事件中的evt.clientX
和evt.clientY
将给出相对于文档的位置而不添加滚动位置,所以需要合并上一个函数:var mousepos = [0, 0]; document.onmousemove = function(evt) { evt = evt || window.event; if (typeof evt.pageX != 'undefined') { // Firefox support mousepos = [evt.pageX, evt.pageY]; } else { // IE support var scrollpos = getDocScrollPos(); mousepos = [evt.clientX+scrollpos[0], evt.clientY+scrollpos[1]]; }; };
- Firefox和IE在怪异模式:
select/范围:
-
<textarea>
和<input>
选项 :selectionStart
和selectionEnd
在IE中没有实现,并且有一个专有的“范围”系统在它的位置,也可以从textarea看到插入位置 。 -
获取文档中当前选定的文本:
- Firefox:
window.getSelection().toString()
- IE:
document.selection.createRange().text
- Firefox:
通过ID获取元素:
-
document.getElementById
也可以引用表单中的name
属性(具体取决于文档中的第一个),所以最好不要使用具有相同name
和id
不同元素。 这可以追溯到id
不是w3c标准的日子。document.all
( 一个专有的特定于IE的属性 )比document.getElementById
快得多,但它还有其他问题,因为它始终优先于id
之前的name
。 我个人使用这个代码,只是为了确保:function getById(id) { var e; if (document.all) { e = document.all[id]; if (e && e.tagName && e.id === id) { return e; }; }; e = document.getElementById(id); if (e && e.id === id) { return e; } else if (!e) { return null; } else { throw 'Element found by "name" instead of "id": ' + id; }; };
只读innerHTML的问题:
-
IE不支持设置
col
,colGroup
,frameSet
,html
,head
,style
,table
,tBody
,tFoot
,tHead
,title
和tr
元素的tHead
。 下面是一个针对表格相关元素的函数:function setHTML(elm, html) { // Try innerHTML first try { elm.innerHTML = html; } catch (exc) { function getElm(html) { // Create a new element and return the first child var e = document.createElement('div'); e.innerHTML = html; return e.firstChild; }; function replace(elms) { // Remove the old elements from 'elm' while (elm.children.length) { elm.removeChild(elm.firstChild); } // Add the new elements from 'elms' to 'elm' for (var x=0; x<elms.children.length; x++) { elm.appendChild(elms.children[x]); }; }; // IE 6-8 don't support setting innerHTML for // TABLE, TBODY, TFOOT, THEAD, and TR directly var tn = elm.tagName.toLowerCase(); if (tn === 'table') { replace(getElm('<table>' + html + '</table>')); } else if (['tbody', 'tfoot', 'thead'].indexOf(tn) != -1) { replace(getElm('<table><tbody>' + html + '</tbody></table>').firstChild); } else if (tn === 'tr') { replace(getElm('<table><tbody><tr>' + html + '</tr></tbody></table>').firstChild.firstChild); } else { throw exc; }; }; };
还要注意,在创build使用
document.createElement
时,IE需要在将<tr>
添加到<tbody>
元素之前向<table>
添加<tbody>
,例如:var table = document.createElement('table'); var tbody = document.createElement('tbody'); var tr = document.createElement('tr'); var td = document.createElement('td'); table.appendChild(tbody); tbody.appendChild(tr); tr.appendChild(td); // and so on
事件差异:
-
获取
event
variables: DOM事件不会传递给IE中的函数,并且可以作为window.event
访问。 获得该事件的一种常见方式是使用例如
elm.onmouseover = function(evt) {evt = evt||window.event}
如果evt
未定义,则默认为window.event
。 -
关键事件代码的差异:关键事件代码差异很大,但是如果你看一下Quirksmode或者JavaScript疯狂的话 ,IE浏览器并不是特定的,Safari和Opera又是不同的。
-
鼠标事件差异: IE中的
button
属性是一个允许同时允许多个鼠标button的位标志:- 左: 1(
var isLeft = evt.button & 1
) - 右: 2(
var isRight = evt.button & 2
) -
中心: 4(
var isCenter = evt.button & 4
)W3C模式(由Firefox支持)不如IE模式灵活,只允许一个button一次,左边为
0
,右边为2
,中间为1
。 请注意,正如Peter-Paul Koch 提到的那样 ,这是非常直观的,因为0
通常意味着“无button”。offsetX
和offsetY
是有问题的 ,在IE中最好避免它们。 在IE中获取offsetX
和offsetY
的更可靠的方法是获取相对定位的元素的位置并从clientX
和clientY
减去它。另外请注意,在IE中双击click事件,你需要注册一个
click
和dblclick
事件到一个函数。click
,Firefox会触发click
和dblclick
,因此需要IE特定的检测才能具有相同的行为。
- 左: 1(
-
事件处理模型中的差异:专有的IE模型和Firefox模型都支持从下往上处理事件,例如,如果
<div><span></span></div>
中的两个元素都有事件,则事件如果使用传统的例如elm.onclick = function(evt) {}
,将在span
触发div
而不是它们绑定的顺序。“捕捉”事件通常只在Firefox等支持,这将触发
div
然后span
事件自上而下的顺序。 在处理其他事件之前,IE有elm.setCapture()
和elm.releaseCapture()
用于将鼠标事件从文档redirect到元素(在这种情况下是elm
),但是它们有很多性能和其他问题,所以应该避免。-
火狐:
附加 :
elm.addEventListener(type, listener, useCapture [true/false])
分离 :elm.removeEventListener(type, listener, useCapture)
(type
是例如'mouseover'
没有on
) -
IE:在IE中只能添加一个给定types的事件 – 如果添加了多个相同types的事件,则会引发exception。 还要注意,
this
是指事件函数中的window
而不是绑定的元素(所以不太有用):附上 :
elm.attachEvent(sEvent, fpNotify)
分离 :elm.detachEvent(sEvent, fpNotify)
(sEvent
是例如'onmouseover'
)
-
-
事件属性差异:
-
停止正在被其他监听function处理的事件 :
Firefox:
evt.stopPropagation()
IE:evt.cancelBubble = true
-
停止插入字符的关键事件或停止checkbox检查:
Firefox:
evt.preventDefault()
IE:evt.returnValue = false
注意:只要在keydown
,keypress
,mousedown
,mouseup
,click
和reset
返回false
,也将防止默认值。 -
获取触发事件的元素:
Firefox:
evt.target
IE:evt.srcElement
-
从 IE中的
evt.target
获取元素的鼠标光标是Firefox中的evt.target
,如果是onmouseout
事件,evt.relatedTarget
-
将鼠标指针移动到的元素: IE中的
evt.relatedTarget
是在Firefox中的evt.relatedTarget
,如果是onmouseout
事件,evt.target
-
注意:
evt.currentTarget
(绑定事件的元素)在IE中没有等价物。
-
也请检查逗号,如这些或类似的代码(如果有的话)
var o={ 'name1':'value1', 'name2':'value2', }
Firefox的最后一个逗号(下面的值2)将被容忍,但IE不会
如果你坚持使用jQuery或YUI作为你的post被标记,你应该在浏览器之间有很小的差异…这就是框架的目的,为你照顾这些跨浏览器的差异。
举个例子,看一下quirksmode的DOM遍历页面 ,根据它IE不支持大部分东西…而真的,框架呢,例如IE不支持elem.childElementCount
,但是在jQuery中: $(elem).children().size()
可以在每个浏览器中获得这个值。 你会发现在图书馆里有东西可以处理跨浏览器的99%的不支持的情况,至less是用脚本…用CSS你可能不得不移动到图书馆的插件,一个常见的例子是得到圆angular在IE中工作…因为它没有这样的CSS支持。
但是,如果你直接开始做一些事情,比如document.XXX(thing)
,那么你不在库中,你直接在做javascript(这是所有的javascript,但是你明白了),这可能会也可能不会导致问题,这取决于IE团队在实现该特定function时多么醉</s> </s>。
使用IE浏览器,你更有可能失败的样式出现正确的比原始的JavaScript问题,animation几个像素closures,这样的事情,更多 – 所以当然在IE6。
getElementbyID也将匹配在IE中的名称属性,而不是其他浏览器,IE将select先find它。
例:
<script> var foo = document.getElementById('bar'); </script> .... <input name="bar" type="text" /> //IE will get this element <span id="bar"> Hello, World! </span> //FF,Safari,Chrome will get this element
有很多事情,但我曾经陷入的一个陷阱是,许多浏览器接受JSON没有引用的名称,而IE6和IE7没有。
{ name: "Jakob" } // will often work, but not in ie6/ie7 { "name": "Jakob" } // Better!
编辑 :澄清,这只是一个问题,当需要实际的JSON,而不是一个对象文字。 JSON是对象字面语法的一个子集,意味着数据交换格式(如XML),这就是为什么它被devise为更挑剔的原因。
不同的JavaScript支持
自1.5以来,IE不支持(大部分)添加到JavaScript的扩展。
1.6中的新function
- 数组方法 –
indexOf()
,lastIndexOf()
,every()
,filter()
,forEach()
,map()
,some()
-
for each ... in
– 迭代值而不是属性名称。
新的1.7
- 解构赋值 –
[a,b] = [1,2]
- 迭代器和发生器
-
let
和yeild
1.8的新function
- 数组方法 –
reduce()
,reduceRight()
- 用于定义函数的快捷方式。
这些东西中的一些需要你指定要运行的JavaScript的版本号(这将在IE浏览器下破解),但是像[1,2,3].indexOf(2)
这样的东西可能看起来并不那么重要,直到你试图在IE中运行它
IE和现代浏览器中的JavaScript(如Firefox)之间的主要差异可归因于CSS /(X)HTML跨浏览器背后的相同原因。 当天没有事实上的标准, IE / Netscape / Opera进行了一场草坪战争,实现了大部分的规格,但也省略了一些,并且制定了专有规格以相互优势。 我可以长篇大论,但让我们跳到IE8的发布:JavaScript被避免/嘲笑了多年,随着FF的崛起和对webcomm的蔑视,IEselect了主要关注IE6推进他们的CSS。 基本上离开了DOM支持。 IE8的DOM支持也可能是IE6,它在2001年推出….所以IE的DOM支持比现代浏览器晚了近十年。 如果您的JavaScript版本引擎有特定的JavaScript差异,那么最好的办法就是像我们处理CSS问题一样进行攻击。 定位该浏览器。 不要使用浏览器嗅探,使用function检测来嗅探您的浏览器/它的DOM支持水平。
JScript不是IE自己实现的ECMAScript; JScript是IE对Netscape JavaScript的回答,两者都是在ECMAScript之前出现的。
至于脚本元素的type属性,type =“text / javascript”是默认的标准(至less在HTML5中),所以除非脚本不是JavaScript,否则不需要type属性。
至于IE不支持innerHTML … innerHTML是由IE发明,现在仍然不是一个DOM标准。 其他浏览器已经采用它,因为它是有用的,这就是为什么你可以使用它跨浏览器。 就dynamic更改表而言,MSDN说:“由于表所需的特定结构,表和tr对象的innerText和innerHTML属性是只读的。 我不知道最初有多less是真实的,但很明显,现代浏览器在处理表格布局的复杂性时已经弄清楚了。
我强烈推荐阅读JavaScript上的PPK Jeremy Keith的DOM脚本 Douglas Crockford的JavaScript:The Good Parts和Christian Hellman的开始使用DOM脚本和Ajax的JavaScript,以便更好地理解JavaScript。
就框架/图书馆而言,如果你还没有很好地掌握JavaScript,你应该避免它们。 2年前,我陷入了jQuery陷阱,虽然我能够取得辉煌壮举,但我从来没有学过关于如何正确编写JavaScript的该死的事情。 事后看来,jQuery是一个邪恶的真棒DOM工具包,但是我没有学会正确的closures,原型inheritance等等,不仅让我的个人知识回归,我的工作开始采取巨大的性能命中,因为我没有线索wtf我正在做。
JavaScript是浏览器的语言; 如果你是客户端/前端工程师,你命令JavaScript是最重要的。 Node.js带来了JavaScript的全面倾斜,我看到它的发展每天都有巨大的进步; 服务器端JavaScript将在不久的将来成为一种标准。 我提到这一点,以进一步强调JavaScript现在和将来的重要性。
JavaScript将比Rails做出更多的浪潮。
快乐脚本!
一些本地对象是只读的,而不是真的看起来如此(你可以写给他们,但它没有效果)。 例如,一个普通的高级javascript基于通过覆盖系统方法来扩展Element
对象,比如说改变Element.prototype.appendChild()不仅仅是附加一个子节点 – 比如用父节点数据初始化它。 这将在IE6上静静地失败 – 原来的方法将被调用新的对象,而不是新的。
有些浏览器(现在我不记得了)认为HTML标签之间的换行符是文本节点,而其他浏览器却不行。 所以childNodes(n),nextSibling(),firstChild()之类的行为将会非常不同。
数组和对象文字中的尾随逗号曾经是一个问题,最近没有检查过(意思是IE8):
var a = [ 1, 2, 3, ]; var o = { a:1, b:2, c:3, };
这会导致一些额外的代码生成这样的结构服务器端。
我今天早上刚刚find一个同事,把脚本标签设置为: <script type="application/javascript">
因为他的ide自动完成function在“text / javascript”
但事实certificate,IE只是忽略了整个脚本,如果你使用“应用程序/ JavaScript”,你需要使用“文本/ JavaScript”
就在那天,Internet Explorer发现了一个奇怪的怪癖。 我正在使用YUI,并通过设置innerHTML来replace表体()的内容
Y.one('#elementId').set('innerHTML', '<tr><td>Column 1</td></tr>');
这将工作在所有的浏览器除IE浏览器。 我终于发现,你不能代替IE中的一个表的innerHTML。 我不得不使用YUI创build一个节点,然后追加该节点。
var myNode = Y.node.create('<tr><td>Column 1</td></tr>'); Y.one('#elementId').append(myNode);
这是一个有趣的搞清楚!
额外的逗号和逗号曾经是在IE浏览器通常的问题,而在FF上工作顺利。
IE对失踪非常严格“;” 所以通常是这样。
为了什么值得我在<IE9中遇到这个讨厌的问题
说你有这样的一些HTML:
<table><tr><td>some content...</td></tr></table>
并由于某种原因(我有一个很好的),你需要检索表中的所有HTML在最后closuresTR之前,你可以尝试这样的事情:
var tableHtml = document.getElementById('thetable').innerHTML; var fragment = tableHtml.substring(0, tableHtml.lastIndexOf('</tr>'));
<IE9在这里不会返回任何东西(-1),因为tableHtmlvariables包含所有的HTML标签,而lastIndexOf区分大小写。 为了解决这个问题,我不得不在lastIndexOf之前抛出一个toLowerCase()。
IE不是一个现代的浏览器,只遵循ECMAScript松散。
您提到了我不太熟悉的jQuery,但是对于一般的参考,特别是Prototype,有一点需要注意的是IE中的保留字/方法名。 我知道经常得到的东西是:
someElement.appendChild(new Element('label',{ **for**: someInput.id }).update( someLabelText );
(新元素(tagName,propertyHash)是如何在Protitype中创build新元素)。 在IE中, for:
一定是'for':
因为是一个保留字。 这是完全有道理的 – 但FireFox将容忍这一点。
另一个例子:
someElement.wrap('div').addClassName('someClass')
(Prototype中的wrap
方法将一个元素包装在另一个元素中) – 在IE中textareas上, wrap
是一个属性,必须使用Element.wrap()
而不是方法化版本
从我的经验中可以想到这两个例子。 他们是基于原型,但核心问题不是:注意任何IE认为保留字的方法/标签/标识符,但FireFox或Safari将容忍。
事实是,IE不支持JavaScript …它支持他自己的ECMAScript实现:JScript …这是一种不同的…
使用console.log()
将错误输出到Firefox错误控制台将导致您的脚本在IE中失败。 记得在IE中testing的时候拿出来。