覆盖 JQuery 选择器以支持元素 id 中的美元符号 ($)

Override JQuery Selector to support Dollar Sign ($) in element id

我正在尝试编写一个 JQuery 插件来支持元素 id 中的 $,我的代码是:

DollarSignPlugin.js:

function escapeJQueryElementName(elementName) {
    elementName = elementName.replace(/\$/g, "$");

    return elementName.replace(/$/g, "\$");
}

var jQueryInit = $.fn.init;

$.fn.init = function(arg1, arg2, rootjQuery){
    arg2 = arg2 || window.document;
    if (arg1 && arg1.replace) {
        var newArg1 = escapeJQueryElementName(arg1);
        return new jQueryInit(newArg1, arg2, rootjQuery);
    }

    return new jQueryInit(arg1, arg2, rootjQuery);
};

它运行良好,但我遇到了当前行的一个问题:

var $anyselector = $("#");

JQuery 抛出错误 "Syntax error, unrecognized expression: #".

此行来自 bootstrap 单击带有 href="#" 的任何选项卡时。

删除我的插件时不会出现此错误,如果我将替换功能直接复制粘贴到 jquery 文件,它也可以正常工作。

那么有没有更好的方法来覆盖选择器或者我的代码有问题,请帮忙?

与其尝试通过覆盖实际上只应在内部使用的内容来解决此问题,更好的解决方案是创建一个支持转义选择器的新别名 jQuery 的函数。

顺便说一句,请记住,不应该对 HTML 的内容进行转义,例如$('<span>give me $$$</span>'),所以我粗略地检查了一下。

(function(jQuery) {
  jQuery.withSelectorEscaping = function() {
    function escapeJQueryElementName(elementName) {
      elementName = elementName.replace(/\$/g, "$");
      return elementName.replace(/$/g, "\$");
    }

    return function(selector, context) {
      // avoid doing this for HTML
      if (selector && selector.replace && selector[0] !== '<') {
        selector = escapeJQueryElementName(selector);
      }
      return jQuery(selector, context);
    };
  }
}(jQuery));

// create new alias here
$ = jQuery.withSelectorEscaping();

// use new alias
console.log($('#'));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

此代码片段向 jQuery 本身添加了一个名为 jQuery.withEscaping 的新函数;它 returns 一个你可以分配给你选择的别名的函数,我在这里选择了 $ 但你也可以这样做:

$foo = jQuery.withSelectorEscaping();

它不影响 jQuery 本身的行为,只为需要它的东西公开您的转义功能。

我强烈建议您不要这样做(但如果您愿意,请继续阅读,我在下面有一个修复程序),因为:

  1. 这意味着您在代码中使用了无效的选择器,这是一个维护问题——在某些时候,进行维护的人不会理解发生了什么,或者您会遇到困难条件等

  2. 您当前的实施会搞乱

    $("<div></div>").appendTo(document.body);
    

    (通过在美元符号前加上反斜杠)。谁知道它还搞砸了什么?

相反,我只需要一个简单的:

function $gid(id) {
    var e = document.getElementById(id);
    if (!e) {
        return $();
    }
    if (e.id === id) {
        return $(e);
    }
    // Fallback processing for old IE that matches name attributes
    return $('[id="' + id.replace(/"/g, '\"') + '"]').filter(function() {
        return this.id === id;
    }).first();
}

示例:

// Plugin
function $gid(id) {
    var e = document.getElementById(id);
    if (!e) {
        return $();
    }
    if (e.id === id) {
        return $(e);
    }
    // Fallback processing for old IE that matches name attributes
    return $('[id="' + id.replace(/"/g, '\"') + '"]').filter(function() {
        return this.id === id;
    }).first();
}

// Use
$gid("a$b").css("color", "blue");
$gid("$c").css("color", "green");
<div id="a$b">a$b</div>
<div id="$c">$c</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

但是:如果你打算这样做,你必须小心,尽可能轻柔地踩踏。这意味着使用完全相同数量的参数、相同的 this 值等调用原件;并确保 $.fn.init.prototype 在替换 $.fn.init 后具有与之前相同的值。

做这两件事似乎可以解决问题:

// Plugin
(function () {
    function escapeJQueryElementName(elementName) {
        elementName = elementName.replace(/\$/g, "$");
        return elementName.replace(/$/g, "\$");
    }

    var slice = Array.prototype.slice;
    var jQueryInit = $.fn.init;

    $.fn.init = function (arg1) {
        var args = slice.call(arguments, 0);
        if (arg1 && arg1.replace) {
            args[0] = escapeJQueryElementName(arg1);
        }
        return jQueryInit.apply(this, args);
    };
    $.fn.init.prototype = $.fn;
})();

// Use
$("#a$b").css("color", "blue"); // Using special version that handles $ without \
$("#$c").css("color", "green"); // Using special version that handles $ without \
$("<div>Checking '#' selector, should get 0: " + $("#").length + "</div>").appendTo(document.body);
<div id="a$b">a$b</div>
<div id="$c">$c</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>