两次加载两个相似的脚本不起作用

Loading two similar scripts twice dosn't work

我有这个 javascript,我称之为 cookiebar.js,它显示 cookie 的粘性条消息,(source code)

(function (context) {
"use strict";
var win = context,
    doc = win.document;
var global_instance_name = "cbinstance";
function contentLoaded(win, fn) {
    var done = false,
        top = true,
        doc = win.document,
        root = doc.documentElement,
        add = doc.addEventListener ? "addEventListener" : "attachEvent",
        rem = doc.addEventListener ? "removeEventListener" : "detachEvent",
        pre = doc.addEventListener ? "" : "on",
        init = function (e) {
            if (e.type == "readystatechange" && doc.readyState != "complete") return;
            (e.type == "load" ? win : doc)[rem](pre + e.type, init, false);
            if (!done && (done = true)) fn.call(win, e.type || e);
        },
        poll = function () {
            try {
                root.doScroll("left");
            } catch (e) {
                setTimeout(poll, 50);
                return;
            }
            init("poll");
        };
    if (doc.readyState == "complete") fn.call(win, "lazy");
    else {
        if (doc.createEventObject && root.doScroll) {
            try {
                top = !win.frameElement;
            } catch (e) {}
            if (top) poll();
        }
        doc[add](pre + "DOMContentLoaded", init, false);
        doc[add](pre + "readystatechange", init, false);
        win[add](pre + "load", init, false);
    }
}
var Cookies = {
    get: function (key) {
        return decodeURIComponent(doc.cookie.replace(new RegExp("(?:(?:^|.*;)\s*" + encodeURIComponent(key).replace(/[\-\.\+\*]/g, "\$&") + "\s*\=\s*([^;]*).*$)|^.*$"), "")) || null;
    },
    set: function (key, val, end, path, domain, secure) {
        if (!key || /^(?:expires|max\-age|path|domain|secure)$/i.test(key)) {
            return false;
        }
        var expires = "";
        if (end) {
            switch (end.constructor) {
                case Number:
                    expires = end === Infinity ? "; expires=Fri, 31 Dec 9999 23:59:59 GMT" : "; max-age=" + end;
                    break;
                case String:
                    expires = "; expires=" + end;
                    break;
                case Date:
                    expires = "; expires=" + end.toUTCString();
                    break;
            }
        }
        doc.cookie = encodeURIComponent(key) + "=" + encodeURIComponent(val) + expires + (domain ? "; domain=" + domain : "") + (path ? "; path=" + path : "") + (secure ? "; secure" : "");
        return true;
    },
    has: function (key) {
        return new RegExp("(?:^|;\s*)" + encodeURIComponent(key).replace(/[\-\.\+\*]/g, "\$&") + "\s*\=").test(doc.cookie);
    },
    remove: function (key, path, domain) {
        if (!key || !this.has(key)) {
            return false;
        }
        doc.cookie = encodeURIComponent(key) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT" + (domain ? "; domain=" + domain : "") + (path ? "; path=" + path : "");
        return true;
    },
};
var Utils = {
    merge: function () {
        var obj = {},
            i = 0,
            al = arguments.length,
            key;
        if (0 === al) {
            return obj;
        }
        for (; i < al; i++) {
            for (key in arguments[i]) {
                if (Object.prototype.hasOwnProperty.call(arguments[i], key)) {
                    obj[key] = arguments[i][key];
                }
            }
        }
        return obj;
    },
    str2bool: function (str) {
        str = "" + str;
        switch (str.toLowerCase()) {
            case "false":
            case "no":
            case "0":
            case "":
                return false;
            default:
                return true;
        }
    },
    fade_in: function (el) {
        if (el.style.opacity < 1) {
            el.style.opacity = (parseFloat(el.style.opacity) + 0.05).toFixed(2);
            win.setTimeout(function () {
                Utils.fade_in(el);
            }, 50);
        }
    },
    get_data_attribs: function (script) {
        var data = {};
        if (Object.prototype.hasOwnProperty.call(script, "dataset")) {
            data = script.dataset;
        } else {
            var attribs = script.attributes;
            var key;
            for (key in attribs) {
                if (Object.prototype.hasOwnProperty.call(attribs, key)) {
                    var attr = attribs[key];
                    if (/^data-/.test(attr.name)) {
                        var camelized = Utils.camelize(attr.name.substr(5));
                        data[camelized] = attr.value;
                    }
                }
            }
        }
        return data;
    },
    normalize_keys: function (options_object) {
        var camelized = {};
        for (var key in options_object) {
            if (Object.prototype.hasOwnProperty.call(options_object, key)) {
                var camelized_key = Utils.camelize(key);
                camelized[camelized_key] = options_object[camelized_key] ? options_object[camelized_key] : options_object[key];
            }
        }
        return camelized;
    },
    camelize: function (str) {
        var separator = "-",
            match = str.indexOf(separator);
        while (match != -1) {
            var last = match === str.length - 1,
                next = last ? "" : str[match + 1],
                upnext = next.toUpperCase(),
                sep_substr = last ? separator : separator + next;
            str = str.replace(sep_substr, upnext);
            match = str.indexOf(separator);
        }
        return str;
    },
    find_script_by_id: function (id) {
        var scripts = doc.getElementsByTagName("script");
        for (var i = 0, l = scripts.length; i < l; i++) {
            if (id === scripts[i].id) {
                return scripts[i];
            }
        }
        return null;
    },
};
var script_el_invoker = Utils.find_script_by_id("cookiebanner");
var Cookiebanner = (context.Cookiebanner = function (opts) {
    this.init(opts);
});
Cookiebanner.prototype = {
    cookiejar: Cookies,
    init: function (opts) {
        this.inserted = false;
        this.closed = false;
        this.test_mode = false;
        var default_text = "This site uses cookies.";
        var default_link = "Detail";
        this.default_options = {
            cookie: "cookiebanner-accepted",
            closeText: "&#10006;",
            cookiePath: "/",
            debug: false,
            expires: Infinity,
            zindex: 255,
            mask: false,
            maskOpacity: 0.5,
            maskBackground: "#000",
            height: "auto",
            minHeight: "21px",
            bg: "#000",
            fg: "#ddd",
            link: "#aaa",
            position: "bottom",
            message: default_text,
            linkmsg: default_link,
            moreinfo: "http://www.examplesite123.com/cookie-policy/",
            effect: null,
            fontSize: "14px",
            fontFamily: "arial, sans-serif",
            instance: global_instance_name,
            textAlign: "center",
            acceptOnScroll: true,
        };
        this.options = this.default_options;
        this.script_el = script_el_invoker;
        if (this.script_el) {
            var data_options = Utils.get_data_attribs(this.script_el);
            this.options = Utils.merge(this.options, data_options);
        }
        if (opts) {
            opts = Utils.normalize_keys(opts);
            this.options = Utils.merge(this.options, opts);
        }
        global_instance_name = this.options.instance;
        this.options.zindex = parseInt(this.options.zindex, 10);
        this.options.mask = Utils.str2bool(this.options.mask);
        if ("string" === typeof this.options.expires) {
            if ("function" === typeof context[this.options.expires]) {
                this.options.expires = context[this.options.expires];
            }
        }
        if ("function" === typeof this.options.expires) {
            this.options.expires = this.options.expires();
        }
        if (this.script_el) {
            this.run();
        }
    },
    log: function () {
        if ("undefined" !== typeof console) {
            console.log.apply(console, arguments);
        }
    },
    run: function () {
        if (!this.agreed()) {
            var self = this;
            contentLoaded(win, function () {
                self.insert();
            });
        }
    },
    build_viewport_mask: function () {
        var mask = null;
        if (true === this.options.mask) {
            var mask_opacity = this.options.maskOpacity;
            var bg = this.options.maskBackground;
            var mask_markup =
                '<div id="cookiebanner-mask" style="' +
                "position:fixed;top:0;left:0;width:100%;height:100%;" +
                "background:" +
                bg +
                ";zoom:1;filter:alpha(opacity=" +
                mask_opacity * 100 +
                ");opacity:" +
                mask_opacity +
                ";" +
                "z-index:" +
                this.options.zindex +
                ';"></div>';
            var el = doc.createElement("div");
            el.innerHTML = mask_markup;
            mask = el.firstChild;
        }
        return mask;
    },
    agree: function () {
        this.cookiejar.set(this.options.cookie, 1, this.options.expires, this.options.cookiePath);
        return true;
    },
    agreed: function () {
        return this.cookiejar.has(this.options.cookie);
    },
    close: function () {
        if (this.inserted) {
            if (!this.closed) {
                if (this.element) {
                    this.element.parentNode.removeChild(this.element);
                }
                if (this.element_mask) {
                    this.element_mask.parentNode.removeChild(this.element_mask);
                }
                this.closed = true;
            }
        }
        return this.closed;
    },
    agree_and_close: function () {
        this.agree();
        return this.close();
    },
    cleanup: function () {
        this.close();
        return this.unload();
    },
    unload: function () {
        if (this.script_el) {
            this.script_el.parentNode.removeChild(this.script_el);
        }
        context[global_instance_name] = undefined;
        return true;
    },
    insert: function () {
        this.element_mask = this.build_viewport_mask();
        var zidx = this.options.zindex;
        if (this.element_mask) {
            zidx += 1;
        }
        var el = doc.createElement("div");
        el.className = "cookiebanner";
        el.style.position = "fixed";
        el.style.left = 0;
        el.style.right = 0;
        el.style.height = this.options.height;
        el.style.minHeight = this.options.minHeight;
        el.style.zIndex = zidx;
        el.style.background = this.options.bg;
        el.style.color = this.options.fg;
        el.style.lineHeight = el.style.minHeight;
        el.style.padding = "5px 16px";
        el.style.fontFamily = this.options.fontFamily;
        el.style.fontSize = this.options.fontSize;
        el.style.textAlign = this.options.textAlign;
        if ("top" === this.options.position) {
            el.style.top = 0;
        } else {
            el.style.bottom = 0;
        }
        el.innerHTML = '<div class="cookiebanner-close" style="float:right;padding-left:5px;">' + this.options.closeText + "</div>" + "<span>" + this.options.message + " <a>" + this.options.linkmsg + "</a></span>";
        this.element = el;
        var el_a = el.getElementsByTagName("a")[0];
        el_a.href = this.options.moreinfo;
        el_a.target = "_blank";
        el_a.style.textDecoration = "none";
        el_a.style.color = this.options.link;
        var el_x = el.getElementsByTagName("div")[0];
        el_x.style.cursor = "pointer";
        function on(el, ev, fn) {
            var add = el.addEventListener ? "addEventListener" : "attachEvent",
                pre = el.addEventListener ? "" : "on";
            el[add](pre + ev, fn, false);
        }
        var self = this;
        on(el_x, "click", function () {
            self.agree_and_close();
        });
        if (this.element_mask) {
            on(this.element_mask, "click", function () {
                self.agree_and_close();
            });
            doc.body.appendChild(this.element_mask);
        }
        if (this.options.acceptOnScroll) {
            on(window, "scroll", function () {
                self.agree_and_close();
            });
        }
        doc.body.appendChild(this.element);
        this.inserted = true;
        if ("fade" === this.options.effect) {
            this.element.style.opacity = 0;
            Utils.fade_in(this.element);
        } else {
            this.element.style.opacity = 1;
        }
    },
};
if (script_el_invoker) {
    if (!context[global_instance_name]) {
        context[global_instance_name] = new Cookiebanner();
    }
}
})(window);

我在Wordpress的functions.php中是这样加载的:

function wpb_hook_javascript() {
    ?>
<script defer type="text/javascript" id="cookiebanner" src="https://www.examplesite123.com/cookiebar.js"></script>
    <?php
}
add_action('wp_head', 'wpb_hook_javascript');

效果很好

现在我复制相同的 javascript 代码并将其命名为 stickybar.js。我添加了一些修改,还更改了 class 名称为“stickybar”:

stickybar.js的代码是here(我把它粘贴到jsfiddle因为Whosebug的文本太多了)

然后 我仅在移动设备上显示第二个柱状图 (stickybar.js),并在 8 秒后显示 CSS:

.stickybar { display: none; }

@media only screen and (max-device-width:480px) {
.stickybar {
  display: block;
  animation: cssAnimation 0s 8s forwards;
  visibility: hidden;
}

@keyframes cssAnimation {
  to   { visibility: visible; }
}
}

我使用 functions.php:

中的代码将其加载到 Wordpress 中
function wpb_hook_javascript() {
    ?>
<script defer type="text/javascript" id="cookiebanner" src="https://www.examplesite123.com/stickybar.js"></script>
    <?php
}
add_action('wp_head', 'wpb_hook_javascript');

效果很好

如果我一个一个地加载这些代码,它们工作正常。

问题是,当我在 functions.php 中以这种方式一起加载两个脚本时,只有第一个有效:

function wpb_hook_javascript() {
    ?>
<script defer type="text/javascript" id="cookiebanner" src="https://www.examplesite123.com/cookiebar.js"></script>
<script defer type="text/javascript" id="cookiebanner" src="https://www.examplesite123.com/stickybar.js"></script>
    <?php
}
add_action('wp_head', 'wpb_hook_javascript');

如何一起加载这两个脚本?

这个问题的评论线程在语义上是正确的,每个 html id 属性只能有一个实例,它们 必须 是唯一的,并且您的 find_script_by_id 方法都在搜索相同的东西。

但是,您正在执行通常所谓的将脚本“嵌入”到您的 header 中,这充其量是一种失礼,至少就 WordPress 而言是这样。 Properly Enqueueing Scripts (and styles) 在 WordPress 中非常简单,您未来的自己、网络客户和其他查看您代码的人都会感谢您这样做。

这与您现在“融入”脚本的方式没有什么不同:

function wpb_hook_javascript() {
    ?>
<script defer type="text/javascript" id="cookiebanner" src="https://www.examplesite123.com/cookiebar.js"></script>
<script defer type="text/javascript" id="cookiebanner" src="https://www.examplesite123.com/stickybar.js"></script>
    <?php
}
add_action('wp_head', 'wpb_hook_javascript');

但有一些变化:

function enqueue_my_scripts(){
    wp_enqueue_script( 'cookie-bar', 'https://www.examplesite123.com/cookiebar.js', array(), '1.0', true );
    wp_enqueue_script( 'sticky-bar', 'https://www.examplesite123.com/stickybar.js', array(), '1.0', true );
}
add_action( 'wp_enqueue_scripts', 'enqueue_my_scripts' );

即,它使用了 wp_enqueue_script() function on the wp_enqueue_scripts 钩子。这使您可以将脚本推迟到页脚,加载 header,添加版本号以防止缓存问题,添加依赖项,允许您以编程方式动态 add/remove 它们,根据句柄为它们提供唯一 ID , 以及更多。 (您仍然需要更新 find_script_by_id 函数以使用这些新句柄来代替 cookie-barsticky-bar、更改 global_instance_name 等(稍后会详细介绍)

话虽如此,如果 .js 文件在您的服务器上,您需要使用 site_url(), plugins_url(), get_stylesheet_directory_uri() 或类似函数来获取文件的 URL 而不是打字出来。如果您使用的是远程资源,请不要担心,但如果它们在您的网站上,您应该换掉内置版本,这样您在移动网站时就不会遇到问题, 它允许您更简单的方法来编辑 version 以防止在更改它们时出现缓存问题。

回到你的变量,你可能还想用 document.currentScript 代替你的 find_script_by_id 类型函数,让它们更抽象而不依赖于 typo/duplicate 容易出现的元素ID,而是引用当前的 运行 <script> 标签。