扩展原生appendChild方法

本文介绍了一个增强版的appendChild方法——appendChildPlus,该方法允许同时插入多个HTML字符串和节点对象,并确保script标签在插入后能够正常运行。所有参数将作为整体一次性插入到目标节点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Node.prototype.appendChildPlus()

写于:2017-1-2 | 更新于:2017-1-3

概述

appendChild方法的缺点有两个:

  • 不能传多个参数

  • 参数只能是节点对象

这里给Node.prototype添加一个方法,名称为:appendChildPlus,可以认为是appendChild的加强版,特点如下:

  • 参数可以是html字符串和节点对象

  • 可以传多个参数

  • script标签字符串和新建的script节点对象插入文档以后可以正常运行

  • 所有参数是一起插入指定节点的,而不是一个一个插入。

源代码

// appendChildPlus.js
/**
 * Created by jszhou on 2017/1/2.
 * updated by jszhou on 2017/1/3.
 */
(function () {
    /**
     * 把类数组对象转换为数组。
     */
    var toArray = Array.from || function (arg) {
            return Array.prototype.slice.call(arg);
        };

    /**
     * 判断参数是否为节点对象。
     * @param {?} arg
     * @return {boolean}
     */
    function isNode(arg) {
        return arg instanceof Node;
    }

    /**
     * 判断参数是否为script节点对象。
     * @param {?} arg
     * @return {boolean}
     */
    function isScript(arg) {
        return isNode(arg) && arg.nodeName.toLowerCase() === 'script';
    }

    /**
     * 复制script节点对象,复制以后script标签的内容可以运行。
     * @param {object} script script节点对象
     * @return {object} 返回新的script节点对象
     */
    function copyScript(script) {
        var newScript = document.createElement('script');

        // 复制属性
        toArray(script.attributes).forEach(function (item) {
            newScript.setAttribute(item.nodeName, item.nodeValue);
        });

        newScript.innerHTML = script.innerHTML;

        return newScript;
    }

    /**
     * 替代script节点对象,替代以后script标签的内容可以运行。
     * @param {object} node 节点对象
     * @return {object} 返回script被替代的节点对象(script节点对象被替代成可以运行的)
     */
    function replaceAllScript(node) {
        Array.prototype.forEach.call(node.childNodes, function (item) {
            // 如果是script节点对象,则替代为可运行的script节点对象
            if(isScript(item)) {
                item.parentNode.replaceChild(copyScript(item), item);
                return;
            }
            // 递归
            if(item.childNodes.length > 0){
                replaceAllScript(item);
            }
        });

        return node;
    }

    /**
     * 字符串转成文档片段节点对象。
     * @param {String} str 字符串,可以包含script标签,转换完插入文档以后可以运行。
     * @return {Object} 返回文档片段节点对象
     */
    function strToNode(str) {
        var docFrag = document.createDocumentFragment(),
            div = document.createElement('div');

        div.innerHTML = str;

        toArray(replaceAllScript(div).childNodes).forEach(function (item) {
            docFrag.appendChild(item);
        });

        return docFrag;
    }

    /**
     * 用于扩展appendChild方法。可以传入html字符串和节点对象,可以传多个参数。
     * 如果html字符串中有script标签,插入以后可以正常运行。
     * 注意,所有参数是一起插入指定节点的,而不是一个一个插入。
     */
    Node.prototype.appendChildPlus = function () {
        var docFrag = document.createDocumentFragment();

        toArray(arguments).forEach(function (arg) {
            docFrag.appendChild(isNode(arg) ? arg : strToNode(String(arg)));
        });

        this.appendChild(docFrag);
    };
})();

示例代码

// extend.js
var wrap = document.querySelector('.js-wrap'),
    deepScript = '<div><script>wrap.append("deepScript");</script></div>',
    newScriptNode = document.createElement('script'),
    outerScript = '<script src="./outerScript.js"></script>';

newScriptNode.textContent = 'wrap.append("newScriptNode");';

wrap.appendChildPlus(deepScript, newScriptNode, outerScript, '<div>nihao</div>');

// outerScript.js
wrap.append('outerScript');
<!--extend.html-->
<!--运行前-->
<div class="js-wrap"></div>
<script src="../appendChildPlus.js"></script>
<script src="./extend.js"></script>

<!--运行后-->
<div class="js-wrap">
    <div><script>wrap.append("deepScript");</script></div>
    <script>wrap.append("newScriptNode");</script>
    <script src="./outerScript.js"></script>
    <div>nihao</div>
    deepScript
    newScriptNode
    outerScript
</div>
<script src="../appendChildPlus.js"></script>
<script src="./extend.js"></script>

更多

本扩展的markdown和demo源文件在github上,点击这里查看

<think>我们正在讨论如何重写JavaScript的原生alert()函数。根据引用[1],我们可以通过直接给window.alert赋值一个新的函数来实现覆盖。引用[1]中的示例代码展示了如何重写alert函数,使其显示一个自定义的模态框(在这里是使用jQuery来显示一个类为'can'的元素,并将消息内容放入id为'error_info'的元素中)。 引用[2]提到了JavaScript中函数同名覆盖的问题,并指出由于JavaScript没有函数重载,所以我们可以直接覆盖。但是,在重写alert时,我们通常不需要考虑重载,因为原生的alert只有一个参数(要显示的字符串)。 引用[5]提到可以用Promise来重写confirm和alert,但这里我们只关注alert。alert通常不需要用户交互后的回调(它只有一个确定按钮),所以用Promise重写alert可能不是必须的,但我们可以考虑扩展功能,比如让alert返回一个Promise,这样我们可以知道用户什么时候关闭了提示框。 然而,用户的问题只是如何覆盖或重写原生的alert()函数,所以我们可以先给出一个基本的覆盖方法,然后再考虑一些扩展。 基本步骤: 1. 将自定义函数赋值给window.alert。 2. 在自定义函数中,实现我们想要的效果,比如显示一个自定义的模态框,并将消息显示在其中。 注意:重写alert后,原来所有调用alert的地方都会调用我们的自定义函数。 示例代码(使用纯JavaScript,不依赖jQuery): ```html <!DOCTYPE html> <html> <head> <title>重写alert示例</title> <style> #customAlert { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); z-index: 9999; } .alert-content { background-color: #fff; margin: 15% auto; padding: 20px; width: 50%; text-align: center; } </style> </head> <body> <button onclick="testAlert()">测试alert</button> <div id="customAlert"> <div class="alert-content"> <p id="alertMessage"></p> <button onclick="closeAlert()">确定</button> </div> </div> <script> // 重写alert window.alert = function(message) { const customAlert = document.getElementById('customAlert'); const alertMessage = document.getElementById('alertMessage'); alertMessage.textContent = message; customAlert.style.display = 'block'; }; function closeAlert() { document.getElementById('customAlert').style.display = 'none'; } function testAlert() { alert('这是一个自定义的alert窗口!'); } </script> </body> </html> ``` 在这个例子中,我们创建了一个自定义的模态框(id为customAlert),当调用alert时,我们显示这个模态框并设置消息内容。点击确定按钮后,调用closeAlert函数隐藏模态框。 需要注意的是,原生的alert是阻塞的(会暂停JavaScript的执行直到用户点击确定),而我们自定义的alert是非阻塞的(异步的)。这意味着,如果我们希望自定义的alert也是阻塞的,那么我们需要使用更复杂的方法,比如使用Promise和async/await。但是,由于alert的阻塞特性对用户体验有负面影响,通常我们不建议模拟阻塞行为,而是采用异步回调的方式。 然而,如果确实需要模拟阻塞行为,我们可以使用类似同步的对话框(例如,使用模态框并配合Promise,然后使用async函数来等待用户操作)。但是请注意,这并不能真正阻塞整个页面的JavaScript执行(因为JavaScript是单线程的,阻塞会导致页面无响应),所以通常我们只模拟阻塞的效果,即后续代码要等到用户点击确定后再执行,但整个页面不会卡死。这可以通过将后续代码放入回调或者使用Promise来实现。 例如,我们可以这样设计(但注意,这改变了alert的使用方式,因为调用alert后不会立即阻塞,而是返回一个Promise): ```javascript window.alert = function(message) { return new Promise((resolve) => { // 显示自定义alert const customAlert = document.getElementById('customAlert'); const alertMessage = document.getElementById('alertMessage'); alertMessage.textContent = message; customAlert.style.display = 'block'; // 当用户点击确定时,隐藏并resolve const confirmBtn = document.getElementById('alertConfirm'); confirmBtn.onclick = () => { customAlert.style.display = 'none'; resolve(); }; }); }; // 使用的时候,我们需要用async函数和await async function test() { await alert('这是一个自定义alert'); console.log('用户点击了确定'); } ``` 但是,请注意,原生的alert调用是同步的,而我们的Promise版本是异步的。因此,如果我们希望覆盖后的alert在调用时仍然保持同步的代码风格(即后续代码等待alert关闭),这是不可能完全模拟的,因为JavaScript中无法用自定义函数实现真正的同步阻塞(除了像alert、confirm这样的原生阻塞函数)。所以,通常我们只能改变代码的写法,使用异步的方式。 因此,在大多数情况下,我们只是覆盖alert以改变其显示方式,而不改变其异步的本质。如果原有代码依赖于alert的阻塞特性,那么重写为非阻塞的alert可能会导致原有代码逻辑错误。所以,在重写alert时,需要谨慎考虑这一点。 综上所述,我们通常只重写alert的显示方式,而不改变其异步特性。如果原有代码需要阻塞,那么可能需要重构代码以适应异步。 §§相关问题§§ 1. 如何重写JavaScript的confirm函数? 2. 如何创建一个自定义的模态对话框? 3. 为什么原生alert函数会阻塞JavaScript执行? 4. 如何用Promise重写confirm函数?</think>### 如何重写 JavaScript 的原生 alert() 函数 在 JavaScript 中,可以通过直接覆盖 `window.alert` 属性来重写原生的 `alert()` 函数。以下是具体实现方法和注意事项: #### 基本重写方法 ```html <script> // 保存原始 alert 函数的引用(可选) const nativeAlert = window.alert; // 重写 alert 函数 window.alert = function(message) { // 1. 创建自定义弹窗元素 const modal = document.createElement('div'); modal.style = `position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.5); display:flex; justify-content:center; align-items:center; z-index:9999;`; const content = document.createElement('div'); content.style = "background:white; padding:20px; border-radius:5px; text-align:center;"; // 2. 添加消息内容 const text = document.createElement('p'); text.textContent = message; content.appendChild(text); // 3. 添加确定按钮 const button = document.createElement('button'); button.textContent = '确定'; button.onclick = () => { document.body.removeChild(modal); // 可在此处添加回调逻辑 }; content.appendChild(button); // 4. 组合元素并显示 modal.appendChild(content); document.body.appendChild(modal); }; // 测试 alert("这是自定义弹窗!"); </script> ``` #### 关键特性说明 1. **覆盖机制** JavaScript 允许直接覆盖 `window.alert` 属性,所有后续 `alert()` 调用都会使用新函数[^1][^2]。 2. **自定义UI** 完全控制弹窗的样式和布局,可使用 CSS 自由设计[^1]。 3. **阻塞行为模拟** 原生 `alert()` 是同步阻塞的,但自定义实现通常是异步的。如需模拟阻塞效果,可使用 Promise: ```javascript window.alert = async function(message) { // 显示自定义弹窗 return new Promise(resolve => { button.onclick = () => { document.body.removeChild(modal); resolve(); }; }); }; // 使用 await 调用 await alert("请确认"); console.log("弹窗已关闭"); ``` 4. **保留原始功能(可选)** ```javascript window.alert = function(message) { if (message.includes("紧急")) { nativeAlert("❗" + message); // 调用原始alert } else { // 自定义实现 } }; ``` #### 注意事项 1. **浏览器兼容性** 所有现代浏览器均支持此覆盖方式,但需确保在页面加载早期执行(如 `<head>` 中)。 2. **框架集成** 在 React/Vue 中,建议使用组件库的模态框,直接覆盖 `alert` 可能干扰框架状态管理。 3. **功能完整性** 原生 `alert()` 会暂停 JS 执行和页面渲染,自定义实现无法完全复制此行为[^5]。 完整示例见:[JSFiddle 示例](https://jsfiddle.net/6Lk5m2zo/) ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值