【问题标题】:Loading two similar scripts twice dosn't work两次加载两个类似的脚本不起作用
【发布时间】:2020-12-07 18:50:57
【问题描述】:

我有这个我称之为 cookiebar.js 的 javascript,它显示了一个 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*([^;]*).*$)|^.*$"), "$1")) || 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因为stackoverflow的文本太多了)

然后我只在移动设备上显示第二个栏 (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 在这两个函数中寻找相同的ID。但是,您不能在同一页面上重复使用 ID。因此,将stickybar 中的那个更改为stickybar 然后在wpb_hook_javascript 中将stickybar 文件的ID 更改为stickybar。同样在stickbar文件中将global_instance_name中的实例名称更改为独特的名称,可能是sbinstance
  • 谢谢!有用!我遵循@imvain2 的说明。我也尝试将两个脚本放在一个脚本中,但是我应该如何更改functions.php中的代码?在这种情况下,我“需要调用” id="cookiebanner" 和 id="stickybar" 从一个 js 文件中。我该怎么做?

标签: javascript wordpress sticky


【解决方案1】:

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

但是,您正在执行通常所说的将脚本“烘焙”到您的标题中,这充其量是一种失礼,至少就 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_scripts 钩子上的wp_enqueue_script() 函数。这使您可以将脚本推迟到页脚,加载到标题中,添加版本号以防止缓存问题,添加依赖项,允许您以编程方式动态添加/删除它们,根据句柄为它们提供唯一 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 类型函数,以使它们更加抽象,而不是依赖于拼写错误/重复的元素 ID,而是引用当前正在运行的&lt;script&gt; 标签。

【讨论】:

  • 感谢@Xhynk 的时间和信息。我也尝试将两个脚本放在一个脚本中,但是我应该如何更改functions.php中的代码?在这种情况下,我“需要调用” id="cookiebanner" 和 id="stickybar" 从一个 js 文件中。我该怎么做?
  • 这比我在这里真正能介绍的要深入一些——但是为什么要将它们组合成一个脚本……?通常,将类文件按名称分开是一件好事,它使维护显着更容易。如果您需要将组合用于其他目的,请查看像 Autoptimize 这样的插件,它将为您组合和缩小您的 JS/CSS(前提是它已正确排队!)。这允许您维护单独的文件,但向您的用户提供单个缓存文件。
  • 用于减少请求,仅用于优化。我尝试使用 w3 Total cache,但是在组合它们之后,脚本不起作用。它什么都不做,我把它们分开,但只是缩小了。再次感谢!你太棒了!
  • 很好奇。我用 wp_enqueue_scripts 尝试你的指示,只用 'cookiebanner' 和 'stickybar' 修改句柄,但脚本不起作用。使用以下代码,它们可以工作。无论如何,谢谢你 :): function wpb_hook_javascript() { ?>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-11-26
  • 1970-01-01
  • 2020-02-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多