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 。)
  • 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中不支持onsuccessonerror ,并且被一个特定于IE的onreadystatechange所取代,无论下载是成功还是失败,都会被触发。 更多信息请参阅JavaScript疯狂 。


元素大小/位置/滚动和鼠标位置:

  • 获取元素大小/位置 :元素的宽度/高度有时在IE中是elm.style.pixelHeight/Width ,而不是elm.offsetHeight/Width ,但在IE中都不可靠,特别是在怪异模式下,有时会给出比另一个。

    elm.offsetTopelm.offsetLeft经常被错误地报告,导致元素的位置不正确,这就是为什么在很多情况下popup元素等几个像素被closures的原因。

    还要注意的是,如果元素(或元素的父元素) displaynone那么当访问大小/位置属性时IE会引发exception,而不是像Firefox那样返回0

  • 获取屏幕大小 (获取屏幕的可视区域):

    • Firefox: window.innerWidth/innerHeight
    • IE标准模式: document.documentElement.clientWidth/clientHeight
    • IE怪癖模式: document.body.clientWidth/clientHeight
  • 文档滚动位置/鼠标位置 :这个实际上并不是由w3c定义的,所以即使在Firefox中也是非标准的。 要finddocumentscrollLeft / 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.clientXevt.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]]; }; }; 

select/范围:

  • <textarea><input>选项selectionStartselectionEnd在IE中没有实现,并且有一个专有的“范围”系统在它的位置,也可以从textarea看到插入位置 。

  • 获取文档中当前选定的文本:

    • Firefox: window.getSelection().toString()
    • IE: document.selection.createRange().text

通过ID获取元素:

  • document.getElementById也可以引用表单中的name属性(具体取决于文档中的第一个),所以最好不要使用具有相同nameid不同元素。 这可以追溯到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不支持设置colcolGroupframeSethtmlheadstyletabletBodytFoottHeadtitletr元素的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 

事件差异:

  • 获取eventvariables: 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”。

      offsetXoffsetY是有问题的 ,在IE中最好避免它们。 在IE中获取offsetXoffsetY的更可靠的方法是获取相对定位的元素的位置并从clientXclientY减去它。

      另外请注意,在IE中双击click事件,你需要注册一个clickdblclick事件到一个函数。 click ,Firefox会触发clickdblclick ,因此需要IE特定的检测才能具有相同的行为。

  • 事件处理模型中的差异:专有的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
      注意:只要在keydownkeypressmousedownmouseupclickreset返回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]
  • 迭代器和发生器
  • letyeild

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的时候拿出来。