Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[集] A collection of confusing problems met when developing JavaScript under IE #32

Open
aleen42 opened this issue Aug 18, 2017 · 21 comments
Assignees
Labels

Comments

@aleen42
Copy link
Owner

aleen42 commented Aug 18, 2017

This is a task for collecting some confusing problems during developing under IE, mostly when we really get confused with IE8!! To avoid struggling with IE, it's smart to do this as early as I can.

这个 Task 主要是为了收集 IE 开发中所遇到困惑,尤其当我们被 IE8 所折腾的时候。因此,为了避免陷入与 IE 的斗争,我理应尽早地去执行该任务。

Reference Articles:

引用文章:

@aleen42 aleen42 self-assigned this Aug 18, 2017
@aleen42 aleen42 added the Tasks! label Aug 18, 2017
@aleen42
Copy link
Owner Author

aleen42 commented Aug 22, 2017

Content-Editable Elements under IE10(IE10 下的可编辑元素)

In the case when we implementing an editor, we may need to consider such a case which I have met recently. Assume that there is an element which has the attribute contenteditable with true as its value, and the element has contained a large table at the same time:

如果你想要实现一个编辑器,最近我所遇到的一个问题可能会引起您的注意。当一个元素设置有 contenteditable 属性为 true ,且包含着一个溢出的表格时:

<div contenteditable="true">
    <table width="2000px"></table>
</div>

A strange thing may happen when there is also an empty tag <br> under IE10. With inputting words by clicking the keyboard, the browser will not render characters immediately after you release the keyboard key. That's terrible!

IE10 下奇怪的事就此发生!一个空标签 <br> 竟会导致你所输入的东西延迟渲染,这是何其糟糕的体验啊。

<div contenteditable="true">
    <br>
    <table width="2000px"></table>
</div>

In order to work around such a weird occasion, we may have two ways:

为了解决该问题,我们有两种办法:

  1. Since a Content-Editable element contains a large table, it is recommended to replace all empty elements <br> with a paragraph element <p>.

    既然空标签 <br> 无法与一个溢出的表格共存于同一个可编辑元素,那么我们可以通过把其替换成段落元素 <p> 来解决此类问题

  2. Another simple way is to set a CSS property overflow: auto to make a trick.

    此外,还有一种更为方便的方法是,直接为这个可编辑元素设置 CSS 属性 overflow: auto 即可解决以上所描述的怪异问题

@aleen42
Copy link
Owner Author

aleen42 commented Sep 25, 2017

Detect the type of data in clipboard under IE(IE 下粘贴板数据类型检查)

Recently, I have met a requirement where I need to implement a pop-up notification for suggesting users install upload plugins under IE when they are copying a picture. Under WebKit browsers, it can be easily implemented by attaching a paste event onto a content-editable element, and get items via (e.clipboardData || e.originalEvent.clipboardData).items;. What confuses me is that IE does not support such a feature. Therefore, we should use another way to check whether a user is copying a picture.

最近,我遇到了这样的一个需求:检查用户是否粘贴图片,并以此提醒用户安装上传插件。在 Webkit 内核的浏览器下,我可以轻松地通过对可编辑元素进行 paste 事件绑定,以获取相应的粘贴数据 (e.clipboardData || e.originalEvent.clipboardData).items; 来判断粘贴板是否粘贴有一图片来实现。然而,困扰我的是 IE 并没有这样的一个特性。因此,我们需要另外想一些办法来判断。

Actually, under IE, we can only use the method window.clipboardData.getData('Text') to access the clipboard of the system. What it means is that we should know how it acts when users copy different things:

实际上,在 IE 下我们只能通过方法 window.clipboardData.getData('Text') 来访问系统级粘贴板。这就意味着,我们需要了解用户在拷贝不同东西的情况下该方法到底会返回什么给我们:

  1. When copying a string, the method will return the text content after being called.

    当拷贝一段字符串时,方法会原封不动地返回该文本

  2. When copying nothing, the method will return an empty content after being called.

    当粘贴板为空时,方法会返回一个空字符串

  3. When copying a file or a picture through some screenshot tools, the method will return null after being called.

    当粘贴一个文件或使用截图工具粘贴一张图片时,方法会返回 null

After my testing between IE8 to IE11, the method should return according to the scenario above. Therefore, if we want to implement such a requirement under IE, we can just check whether window.clipboardData.getData('Text') returns null.

经过 IE8 到 IE11 下的验证,该方法都会按照以上情况来返回不同的结果。因此,为了实现 IE 下检测用户粘贴的是否为一张图片,我们只需要判断方法是否返回 null 即可。

@aleen42
Copy link
Owner Author

aleen42 commented Sep 25, 2017

Two weird scenarios when listening to a property changing under IE(IE 下监听属性改变事件的两种奇怪场景)

Today, I have met a horrible thing when using propertychange event handler under IE. Some strange things always happen in the following situations:

今天在 IE 下使用 propertychange 事件尝试监听 input 元素的属性改变时,竟发生了一些奇怪的事情:

  1. Under IE8, first input inside textarea, won't fire coresponding propertychange event if it is set with empty value before. To put it simple, we can reproduce such a occasion with following snippet:

    在 IE8 下,若在监听事件 propertychange 绑定前设置 textarea 的值为空字符串,则首次输入文字并不会触发该事件。通过以下的例子,我们可以简单地重现一下:

    <body>
            <textarea></textarea>
            <script type="text/javascript">
            (function () {
                    var input = document.getElementsByTagName('textarea')[0];
                    /** the following code has side effect */
                    input.value = '';
                    input.attachEvent('onpropertychange', function () {
                            console.log('changed');
                    });
            })();
            </script>
    </body>
  2. Under IE9, properychange is not enough for registering an event handler when using jQuery:

    IE9 下,当我们使用 jQuery 来绑定 propertychange 事件时,单单使用字符串 "propertychange" 并不能进行事件注册:

    <body>
            <textarea></textarea>
            <script type="text/javascript" src="jquery.js"></script>
            <script type="text/javascript">
            (function () {
                    var $input = $(document).find('textarea');
                    /** even `bind` method */
                    $input.on('propertychange', function (e) {
                         /** won't fire by any input */
                         console.log('changed')
                    });
            })();
            </script>
    </body>

To work around two weird scenarios, there are some approaches:

因此为了解决这两种奇怪的场景,我们有以下几种方法:

  1. Under IE8, it's not recommended to set value with empty string for input elements like textarea. If you do need to, add the following code before your setting to do a trick:

    IE8 下不建议在绑定 propertychange 事件前设置像 textarea 这类 input 元素的值为空字符串。若不得不设置,则可以通过添加以下的代码片段来绕开此问题:

    /** set with a non-empty string before */
    input.value = ' ';
    input.value = '';
  2. As for IE9, we can avoid it by using plain JavaScript to attach events rather than using bind or on in jQuery:

    至于 IE9 的问题,我们可以直接使用原生 JavaScript 来绕开此问题:

    $input[0].attachEvent('onpropertychange', function (e) {
        console.log('changed');
    });

    But what if you do prefer to use jQuery without any plain JavaScript code snippet, you can bind input propertychange instead:

    但倘若你想保留 jQuery 的代码风格而不想使用任何的原生 JavaScript,则需要绑定 input propertychange

    /** also work for `bind` */
    $input.on('input propertychange', function (e) {
        console.log('changed');
    });

Repository owner locked and limited conversation to collaborators Sep 25, 2017
@aleen42
Copy link
Owner Author

aleen42 commented Sep 25, 2017

Automatically adjust table under IE8(IE8 下表格宽度自适配)

Assume that there is a table with being set as table-layout: fixed, which has resized its content by hiding a column. Under IE8, such a table will not adjust its width to fit the table container outside, while it does under Chrome, Firefox, or even IE7.

假设有一个表格设置有 table-layout: fixed,正常来说若这个表格的内容发生了变化(如少了一列),表格的内容会自动适配表格容器的宽度填充整个表格。Chrome、Firefox 乃至 IE7 都有这样的一种行为,然而 IE8 却并非如此。

To work around this problem, we can simply use the following snippet, so that we can make the table adjustable for its changing contents:

当然,我们可以简单地通过以下代码片段来解决这个问题,以使得表格能自适配宽度。

table.style.display = 'inline-block';
setTimeout(function () {
    /** macrotasks executed in next event loop */
    table.style.display = '';
}, 0);

@aleen42
Copy link
Owner Author

aleen42 commented Feb 27, 2018

Two global functions definition ways may be different under IE8(IE8 两种全局函数定义会有所不同)

As a common sense of coding JavaScript, it is not a wise choice to define functions (or variables) globally, as developers struggle with defining conflict problems when using shared functions, especially when the project has become larger and larger, like SPA.

不要定义全局函数(或变量)早已成为 JavaScript 开发中的一个常识。源于使用共享函数时,大量的全局定义可能会使得开发者深陷于变量定义冲突的问题,尤其是当项目像单页面应用(SPA,Single-page Applications)那样越来越庞大的时候,身同体会。

However, it seems to be the only choice when you have to communicate with IE add-ons. For example, I have a task needing add-ons to handle asynchronously, and tell the result to me with a callback method named done(result). In such a case, the most common way you may meet is where add-ons developers use FireBreath to implement this. With this framework, you may have to define a global function, and pass the string of the function name to add-ons, so that they can callback result for you with calling your defined functions.

然而,当你需要跟 IE 插件进行数据交互时,定义全局函数似乎是唯一可行的方法。举个例子来说,我需要插件异步处理一个任务,并通过回调告诉我结果。这种情况下,最寻常的做法是插件开发者会使用 FireBreath 来实现,而该框架下,你需要定义一个全局函数,并把函数名以字符串的形式传递给插件。这样,插件才能把结果回调至你所定义的函数。

function scope() {
    /** function scope but not global one */
    document.getElementById('#ie-plugin').runJSFunc('test', result); /** nothing happens */

    /** throw "Method Invoke Failed" */
    function test(result) {
        /** handle with result */
    };
}

Ooops, there is something wrong under IE8!!

不会吧,IE8 怎么这么傲娇!!

You can't define the function globally with window:

竟不能通过 window 对象来定义全局函数:

try {
    document.getElementById('#ie-plugin').runJSFunc('test', result);
} catch (e) {
    console.log(e.message); /** => Method Invoke Failed. */
}

/** throw "Method Invoke Failed" */
window.test = function (results) {
    /** handle with results */
};

That's why I marked this with document here, in order to tell you how to solve this with following workarounds:

这就是为何我要在此进行文档记录,以告知诸位如何用下面的方式解决问题:

  1. Define it directly (only be suggested when not defining in global scope)

    直接定义(仅建议在非全局情况下使用)

    test = function (results) {
        /** handle with results */
    };
  2. Use the official simple way (only use when it is in the global scope)

    使用官方简单的函数定义(仅局限于全局作用域下使用)

    function test(results) {
        /** handle with results */
    }

@aleen42
Copy link
Owner Author

aleen42 commented Feb 28, 2018

Weird phenomena during loading IE add-ons(加载 IE 插件时出现的奇象)

Recently, I have met some weird things when trying to load IE add-ons under IE 8 and 9. Since the IE add-on was designed as an object element in HTML, developers around this add-on had guided me to load their plugin within front-end developing following this tutorial:

最近,在 IE8 和 IE9 下尝试加载 IE 插件时,又遇到了一些奇象。既然 IE 插件是被设计成 HTML 中的 object 对象,当时那边的开发者们就引导我用以下的方法去加载该插件:

  1. Firstly, define a function to handle after the plugin has been loaded:

    首先定义插件加载完成后的处理函数:

    window.pluginLoaded = function () {
        /** the plugin has been loaded */
    };
  2. Append the object element into your HTML files:

    添加元素到 HTML 文件里

    <object id="ie-addon" type="application/x-xxxplugin">
        <param name="onload" value="pluginLoaded" />
    </object>

However, due to some real developing requirements, I had to dynamically inject elements into DOM, and the first plan I designed was to use appendChild method to do so:

然而,基于现实中的若干开发需求,我只能动态地往 DOM 插入该对象元素。而使用 appendChild 则是我首先想到的方案:

const obj = document.createElement('object');
obj.id = 'ie-addon';
obj.setAttribute('type', 'application/x-xxxplugin');
const param = document.createElement('param');
param.setAttribute('name', 'onload');
param.setAttribute('value', 'pluginLoaded');
obj.appendChild(param);

/** `pluginLoaded` has not been invoked under IE9 */
document.body.appendChild(obj);

The code snippet above seemed to run well under IE8, while something wrong happened when ran it under IE9. What I found was that the pluginLoaded method was not invoked at all, and I could still call inner methods defined inside this plugin object, like plugin.xxx(). After calling the inner method, another wierd thing happened, and the method did not do return data as usual. As a workaround, I changed to use innerHTML to load this plugin:

上述的代码片段在 IE8 下似乎毫无瑕疵,而在 IE9 中却发生了奇怪的事情。我当时发现,pluginLoaded 函数根本就没有被触发到,而 plugin 内所定义的内部函数却能被调到,如 plugin.xxx()。即便如此,内部函数在调用后也没有像以往一样返回数据。因此,我转而尝试通过 innerHTML 来加载插件:

const obj = document.createElement('object');
obj.id = 'ie-addon';
obj.setAttribute('type', 'application/x-xxxplugin');
const param = document.createElement('param');
param.setAttribute('name', 'onload');
param.setAttribute('value', 'pluginLoaded');
obj.appendChild(param);

document.body.innerHTML += obj.outerHTML;

Cool, it really worked under IE9, BUT the plugin has broken down under IE8, as it was loaded more than once. After some confusing inspections, I doubted that the plugin had been already initialized once the object element was created. That's why I finally only use the following plain:

果真,方案在 IE9 确实行得通, IE8 下竟由于插件被多次加载导致插件崩溃。经过多番检查,我怀疑是因为插件在 object 元素创建后,即被初始化,这也就为何说明我最后用了以下唯一可行的方案:

document.body.innerHTML += `
    <object id="ie-addon" type="application/x-xxxplugin">
        <param name="onload" value="pluginLoaded" />
    </object>
`;

Additionally, register a loaded event handler on the current Window will unexpectedly result in memory leak under IE11 (version 11.431.16299.0), which means that the handler will be unexpectedly triggered while sending an XHR request. The detailed stack has been shown as followed:

此外,在 IE11(版本 11.431.16299.0)下若注册一个全局的 loaded 处理事件在当前 Window 对象,将会导致内存泄漏,即在 Xhr 请求发送时会意外调用到该处理事件。详细的调用堆栈如下:

[Main Thread]
window.pluginLoaded [row: 280, col: 1], plugin.js
Anonymous function [row: 1, col: 130], script block (29)
[Aynchronous Caller]
window.__FB_CALL_627080683 [row: 1, col: 99], script block (29)
[Aynchronous Caller]
send [row: 9664, col: 1], jquery.js
ajax [row: 9215, col: 1], jquery.js
simpleCall [row: 206, col: 1], xhr.js
doGet [row: 1585, col: 1], service.js
Anonymous function [row: 1581, col: 1], service.js
Anonymous function [row: 3305, col: 1], jquery.js
fire [row: 3148, col: 1], jquery.js
self.add [row: 3194, col: 1], jquery.js
Anonymous function [row: 3304, col: 1], jquery.js
each [row: 384, col: 1], jquery.js

To avoid unexpected calling by IE11, we may also need to release such a loaded handler by setting it as null after callback:

为了避免这种非预期内的意外发生,我们需要在 loaded 事件回调时释放该事件注册:

window.pluginLoaded = function () {
    /** any callback here ... */
    window.pluginLoaded = null; /** avoid memory leak */
};

@aleen42 aleen42 changed the title [集] A collection of confusing problems under IE [集] A collection of confusing problems met when developing JavaScript under IE Feb 28, 2018
@aleen42
Copy link
Owner Author

aleen42 commented May 7, 2018

Notify downloading JSON responses under IE8/IE9 ?(IE8/IE9 下提示下载 JSON 返回请求?)

When posting a file to the server, like uploading, we usually use multipart/form-data to construct our requests and wait for responses from the server, formatted with JSON in common. However, there will be troubles when requesting under IE8/IE9, where users will receive a notification to download the response as a file after requesting. This is also one of the most common problems arisen when using jQuery-File-Upload. That's because you have received a response with setting application/json or test/x-json as its Content-Type header. To avoid undesired download dialogue, we must change our server to respond with text/plain. And then, the client should receive a string value, which can be parsed as a JSON object, to check responses of the server. (Note: after responding with text contents, the client should also set text or html as its requesting data types.)

当我们想上传一个文件给服务器时,大多数情况下我们通用的方法是使用 multipart/form 的方式来构造请求,并等待服务器返回一个 JSON 对象以判断其响应结果。然而在 IE8/IE9 下,用户会为此收到一个下载通知,提示下载返回的信息。这也是使用 jQuery-File-Upload 最为常见的问题之一。原因在于返回头设置了其 Content-Type 头部为 application/jsontest/x-json。为了避免该非预期内的下载框,我们必须改造我们的服务器返回一个 text/plain 的响应,并由客户端去解析 JSON。(注:响应文本后,客户端在请求时需同时更改其请求数据类型为 texthtml

Why not text/html? Due to XSS, where hackers can modify the response to inject scripts.

为何不用 text/html?源于 XSS 攻击问题。因为,黑客可以通过修改接口的请求返回注入脚本。

@aleen42
Copy link
Owner Author

aleen42 commented Jul 31, 2018

Unable to select text through a content-editable element under IE?(IE 下无法在可编辑元素内选择文本?)

When implementing an editor, which should be compatible through different browsers, especially IEs, you may need to take this issue tracker as a consideration, as I recently found that I could not select text from bottom to top, through a content-editable element within iframes under IE11 when the area was scrollable (such like a quite long paragraph). After debugging, this issue resulted because the implementation of the editor ignored to set elements as content-editable, rather than switching on the designed mode.

倘若你要实现一个兼容不同浏览器,尤其是 IE 浏览器的编辑器,你就要考虑以下我所遇到的问题。最近,我发现在 IE11 下一个 iframe 内的可编辑元素在可滚动的情况下,无法自下而上选择文本。后来通过调试发现,该问题是因为编辑器在实现元素可编辑化时,针对 IE11 错误把元素的 designMode 打开而非设置 contentEditable

var iframeBody = docuemnt.querySelector('.j-editor').contentWindow.document.body;
iframeBody.designMode = 'on'; /** result in the problem of selecting text */

Instead of setting designMode with on value, you may have to use the following snippet to work around (even under all versions of IE):

为了解决该问题,需要设置 contentEditabletrue,而非打开 designMode(所有 IE 都需要如此处理):

iframeBody.contentEditable = true;

Similarly, I also found a documented issue under the Stack Overflow here.

此外,我在 Stack Overflow 上也找到类似问题记录,详情可参考此处

@aleen42
Copy link
Owner Author

aleen42 commented Apr 17, 2019

Some problems about option tag under IE8/IE9(IE8 / 9 下 option 标签的一些问题)

  1. Lose comments between option tag:

    option 标签之间的备注会丢失:

    // unexpected output => "<SELECT><OPTION selected value=1></OPTION></SELECT>"
    console.log($('<select><option value="1"><!-- comments --></option></select>')[0].outerHTML);
  2. Do not trust outerHTML of new Option:

    不要信任 new OptionouterHTML 属性:

    console.log(new Option('text', 'value').outerHTML); // => "<OPTION value=value></OPTION>"

@aleen42
Copy link
Owner Author

aleen42 commented Apr 19, 2019

Naming anonymous function problems under IE8(IE8 下慎重对匿名函数进行命名)

const test = s;
function s() { console.log('outside'); }

const obj = {
    s: function s() { console.log('inside'); }
};

test(); /** unexpected output => "inside" */

@aleen42
Copy link
Owner Author

aleen42 commented Apr 25, 2019

Some shim may have wrong implementation around Object.assign()Object.assign() 的部分 shim 会产生错误实现)

For example, failed to define toString prototype of Object under IE8 when using core-js@2.5.3:

例如,当使用 core-js@2.5.3 shim 时,在 IE8 下无法通过 Object.assign() 正常定义一个对象的 toString 属性:

function test() {}

Object.assign(test.prototype, {
    toString: () => 'test instance',
});
console.log(`${new test()}`); /** unexpected output => "[object Object]" */

test.prototype.toString = () => 'test instance';
console.log(`${new test()}`); /** expected output => "test instance" */

Due to the non-enumerable property toString under IE8 as described here. To solve the problem, we have to override the shim method Object.assign():

原因如这里所描述的一样, IE8 下含有不可枚举的属性,如 toString。为了解决这个问题,我们需要额外重写 Object.assign()

let fn = Object.assign;
Object.assign = function () {
    const [target, ...sources] = [].slice.call(arguments, 0);
    const result = fn.apply(this, [target, ...sources]);

    [
        'valueOf',
        'isPrototypeOf',
        'toString',
        'hasOwnProperty',
        'toLocaleString',
        'propertyIsEnumerable',
    ].forEach(property => {
        if (property === 'propertyIsEnumerable'
            || !({[property] : null}).propertyIsEnumerable(property)) {
            /** non-enumerable properties */
            sources.forEach(source => source
                                      && source.hasOwnProperty(property)
                                      && (result[property] = source[property]));
        }
    });

    return result;
};

@aleen42
Copy link
Owner Author

aleen42 commented Apr 25, 2019

Don't try to prevent default events and cancel events bubbling in another event loop under IE8(IE8 下不要尝试在另一个事件循环去禁止默认事件及事件冒泡)

document.onclick = function (e) {
    e = e || window.event;
    setTimeout(function () {
        e.returnValue = false; /** thrown error: Member Not Found. */
    });
};

Due to the COM object has destroyed its members after callback, referred from "Professional JavaScript for Web Developers"

根据《Professional JavaScript for Web Developers》的描述,COM 组件会在回调后自行销毁,而导致相关异常抛出。

Of course, you cannot also access some properties of event itself in the next event loop.

当然你在下一事件循环也无法访问 event 本身的部分属性。

document.onclick = function (e) {
    e = e || window.event;
    setTimeout(function () {
        console.log(e.type); /** thrown error: Member Not Found. */
    });
};

In such a case, there is a workaround:

此情况并不是无法解决:

document.onclick = function (e) {
    e = e || window.event;
    var type = e.type;
    setTimeout(function () {
        console.log(type); /** => "click" */
    });
};

@aleen42
Copy link
Owner Author

aleen42 commented May 7, 2019

Keyup events won't fire when there is a button within the same Form under IE8/IE9/IE10(IE8/IE9/IE10 下若 input 标签所属 Form 含有 button 元素则无法监听 Keyup 事件)

Recently, I found that when I was focusing on an input element, the Form of which had a button element, I couldn't get it fired on listening keyup events after pressing the enter key. To reproduce such a bug, I have simplified it as the following case:

最近,我发现在同一个 Form 表单内,含有 button 及 input 元素的时候,input 标签输入文字后按回车并不会触发所监听的 keyup 事件。为了重新构造该问题,下面举一个较为简单的例子:

<input type="text" class="input-wrapper" value="" />
<button>test</button>
$('input.input-wrapper').on('keyup', e => {
    console.log(e.keyCode); /** cannot not print 13 when pressing "enter" key */
});

It seemed like the button element had captured the event and did not propagate to the input element.

从现象来看,像是 button 元素捕获了事件而没有往 input 元素传递。

There are two ways to work around such a problem:

为了解决该问题,有两个办法:

  1. Listen on keypress or keydown event instead:

    改用 keypresskeydown 事件进行监听:

    $('input.input-wrapper').on('keydown', e => { console.log(e.keyCode); });
    $('input.input-wrapper').on('keypress', e => { console.log(e.keyCode); });
  2. Wrap the input elements with an empty form:

    在 input 元素外包裹一层空 Form 表单:

    <form action="javascript: void(0);">
        <input type="text" class="input-wrapper" value="" />
    </form>
    <button>test</button>

@aleen42
Copy link
Owner Author

aleen42 commented May 10, 2019

Some problems around IPv6 address you may need to know under IE8/IE9 (IE8 / 9 下一些关于 IPv6 地址的问题)

  1. Form action need to be set with correct address format constructed with [::1] rather than ::1. Otherwise, the browser should throw an error: "Invalid Arguments."

    Form 表单的 action 属性必须设置 [::1] 而不能是 ::1。否则,浏览器会抛出异常("Invalid Arguments."

  2. When calling postMessage() during iframes, the origin parameter should also be passed with correct address format like case 1.

    在 Iframe 内调用 postMessage() 时,origin 的传参亦如上所述

So if you want to avoid those kinds of problems, the smart way to construct URL is:

为了避免各种各样的问题,最好的方法是统一使用一个函数去构造:

/**
 *  -----------------------------------------------------------------
 *  | location.host | document.domain |  location.port |   browser   |
 *  | ::1:8080      |     [::1]       |      8080      |     IE8     |
 *  | ::1:8080      |     [::1]       |      8080      |     IE9     |
 *  | [::1]:8080    |     [::1]       |      8080      |     IE10    |
 *  | [::1]:8080    |     [::1]       |      8080      |     IE11    |
 *  | [::1]:8080    |     [::1]       |      8080      |   Chrome    |
 *  | [::1]:8080    |     [::1]       |      8080      |   Firefox   |
 *  -----------------------------------------------------------------
 */
const {protocol, port} = location;
const fullRequest = path => `${protocol}//${document.domain}${port ? `:${port}` : ''}/${path}`;

@aleen42
Copy link
Owner Author

aleen42 commented Oct 16, 2019

Array.prototype.splice is not fully supported under IE8(IE8 下 Array.prototype.splice 并非实现完全正确)

Array.prototype.splice(start[, deleteCount[, item1[, item2[, ...]]]])

As defined in MDN, Array.prototype.splice should be supported under IE8. However, I recently found that Array.prototype.splice method did not act as expected when passing only one argument (start). As mentioned in MDN, when deleteCount is omitted, all of the elements from start through the end of the array will be deleted, but it seemed like it was not under IE8.

虽然 MDN 上声明 IE8 是支持 Array.prototype.splice,但我最近发现当且仅当传一个参数(start)的时候实现上会有所不同。按照 MDN 的正确定义,当 deleteCount 参数不传时,从 start 位置到数组末尾的所有元素都理应被删掉,可IE8 上却并非如此。

console.log((arr => { arr.splice(0); return arr; })([1, 2, 3])); /** => [1, 2, 3] under IE8, while [] under Chrome */

To work around this wierd situation, I have wrapped this method with AOP:

为了解决这个异常情况,我们必须用 AOP 的方式重新包装一下函数:

const splice = Array.prototype.splice;
Array.prototype.splice = function (start, deleteCount, ...args) {
    return splice.apply(this, [
        start,
        arguments.length === 1 ? this.length : deleteCount,
        ...args,
    ]);
};

Within ES3:

ES3 下的实现:

var splice = Array.prototype.splice;
Array.prototype.splice = function (start, deleteCount) {
    for (var _len = arguments.length, args = Array(_len > 2
        ? _len - 2
        : 0), _key = 2; _key < _len; _key++) {
        args[_key - 2] = arguments[_key];
    }

    return splice.apply(this, [
        start,
        arguments.length === 1 ? this.length : deleteCount,
    ].concat(args));
};

Notice: MDN has updated its document about the problem

注:MDN 已经就问题更新相关文档

@aleen42
Copy link
Owner Author

aleen42 commented Dec 13, 2019

clearTimeout and clearInterval is not a Function under IE8(IE8 下 clearTimeout 和 clearInterval 并非函数)

({}).toString.call(clearTimeout); // => [object Object] under IE8, while [object Function] under IE9/Chrome
({}).toString.call(clearInterval); // => [object Object] under IE8, while [object Function] under IE9/Chrome

It means that we should notice that do not use Function.prototype.bind() at all like the following case:

这意味着即便使用了 shim 实现 bind,我们仍需要慎重使用 Function.prototype.bind()

const timer = setTimeout(() => {});
const _clear = callback => callback && callback();

_clear(clearTimeout.bind(window, timer));

To workaround this, you can still also wrap them by the AOP way:

为了解决该问题,仍然可以 AOP:

if (({}).toString.call(window.clearInterval) === '[object Object]') {
    function _wrap(fn) { return function (id) { fn(id) } }
    window.clearInterval = _wrap(window.clearInterval);
    window.clearTimeout= _wrap(window.clearTimeout);
}

Notice: MDN has updated its document about the problem

注:MDN 已经就问题更新相关文档

@aleen42
Copy link
Owner Author

aleen42 commented Aug 17, 2020

A pseudo-element with some scrollbar styles defined will crash IE9(IE9 下一个伪元素若含有某些 scrollbar 样式定义会导致崩溃)

Recently, I have found that if there are some defined scrollbar styles, you may suddenly crash IE9 when using CSSStyleDeclaration.getPropertyValue() to access the scrollbar style of a pseudo element. For example:

最近我发现如果定义了某些滚动条样式,使用 CSSStyleDeclaration.getPropertyValue() 去访问某个伪元素的该类样式会导致 IE9 突然崩溃。例如:

<!doctype html>
<html lang="en">
  <head>
    <style>
        html {
            SCROLLBAR-FACE-COLOR: #E1E1E1;
            SCROLLBAR-HIGHLIGHT-COLOR: #EDEDED;
            SCROLLBAR-SHADOW-COLOR: #B6B6B6;
            SCROLLBAR-3DLIGHT-COLOR: #FFFFFF;
            SCROLLBAR-DARKSHADOW-COLOR: #FFFFFF;
            SCROLLBAR-ARROW-COLOR: #FFFFFF;
            SCROLLBAR-TRACK-COLOR: #F4F4F4;
        }

        .target:before {
            content: '';
        }
    </style>
  </head>
  <body>
    <!-- scrollbar-base-color / scrollbar-darkshadow-color / scrollbar-face-color
         scrollbar-highlight-color / scrollbar-shadow-color / scrollbar-track-color -->
    <button class="target"
        onclick="alert(window.getComputedStyle(this, ':before').getPropertyValue('scrollbar-3dlight-color'))">
      click to crash IE9
    </button>
  </body>
</html>

@aleen42
Copy link
Owner Author

aleen42 commented Jan 4, 2021

Wrong base64 images with texts from the clipboard under IE11 (IE11 下图文粘贴时错误的 base64 编码图片)

When I tried to copy a snippet of texts and an image from a Word document at the same time, I found that the image from the clipboard under IE11 would specify a wrong Content-Type in the image base64 code like data:image/png;xxx, but that image was converted into JPEG formats. It means that if we upload it via Blob, IE11 fails to render such an image from the server by throwing DOM7009: Unable to decode image at URL: '[some unique url]' when the server responses with a header X-Content-Type-Options: nosniff;.

当从 Word 文档同时粘贴一段图文时,就会发现 IE11 粘贴板内所拿到的图片 base64 地址含有错误的 Content-Typedata:image/png:xxx,而实际上该图片已经被转换成 JPEG 格式。这就意味着,如果我们把 base64 地址转成 Blob 上传到服务器时,IE11 就会由于服务器返回的 X-Content-Type-Options: nosniff; 头部而编码不了该图片显示裂图,并抛出异常 DOM7009: Unable to decode image at URL: '[some unique url]'

References:

引用:

@aleen42
Copy link
Owner Author

aleen42 commented Mar 23, 2021

We cannot access some attributes of DispHTMLCurrentStyle under IE8 (IE8 下无法直接访问 DispHTMLCurrentStyle 的部分属性)

In IE8, there is no Window.getComputedStyle() method for extracting styles from a DOM element. In such a case, we need to use Element.currentStyle or ElementCSSInlineStyle.style as a workaround. These attribute should be defined as a DispHTMLCurrentStyle object.

在 IE8 下, 我们无法通过 Window.getComputedStyle() 方法来获取 DOM 元素的样式。这时候,我们可以使用 Element.currentStyleElementCSSInlineStyle.style 来替代。两者属性所返回的对象,理论上命名为 DispHTMLCurrentStyle

Nevertheless, I recently found that we can directly access some attributes of it like outline or outlineWidth under IE8. It should throw an error: Unspecified error.

然而,最近我发现在 IE8 下我们如果直接访问其中的部分属性如 outlineoutlineWidth,浏览器将会抛出类似异常:Unspecified error

@aleen42
Copy link
Owner Author

aleen42 commented Dec 31, 2021

URL() Constructor in old Edge (旧版 Edge 中的 URL() 构造)

Before Edge 79, query arguments in the base URL argument are removed when calling the URL() constructor event passing empty URL as the first argument.

在 Edge 79 版之前,即便第一个参数传空 URL,base 参数所传的 URL 变量仍然会被移除。

// Chrome, Firefox, Node, New Edge
new URL('', 'http://www.test.com?sid=1').searchParams.get('sid'); // => "1"
// Old Edge
new URL('', 'http://www.test.com?sid=1').searchParams.get('sid'); // => null

References:

引用:

@aleen42
Copy link
Owner Author

aleen42 commented Dec 31, 2021

If you can't generate Blob with TypedArray under IE10 (若你在 IE10 无法生成 TypedArrayBlob)

Sometimes, if you get such a error when trying to generate Blob with a given TypedArray:

new Blob([new Uint8Array(0)]); // => "InvalidStateError"

You can workaround by using MSBlobBuilder:

var bytes = new Uint8Array(0);

try {
    new Blob([bytes]); // => "InvalidStateError"
} catch (ignore) {
    var blobBuilder = new MSBlobBuilder();
    blobBuilder.append(bytes.buffer);
    blobBuilder.getBlob();
}

References:

引用:

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

1 participant