jQuery 自定义选择器,“未定义”
Posted
技术标签:
【中文标题】jQuery 自定义选择器,“未定义”【英文标题】:jQuery custom selector, "undefined" 【发布时间】:2012-08-28 00:25:46 【问题描述】:我正在尝试制作一个 jQuery ui 日历,以便在单击日期时进行 ajax 调用, 但是几天前我遇到了一个问题。 我发现了一个据说可以执行此操作的代码 sn-p,但我发现它使用 jQuery 自定义选择器。该代码给了我一个错误,所以我开始深入研究自定义选择器以了解有关它们的更多信息。到目前为止,我还无法找出为什么会出现这种奇怪的行为。
这是一张希望澄清问题的图片,我会在它之后解释更多
我已经在控制台输入了
$('.ui-datepicker-calendar td a:test(3)')
正如你所见,我 meta2 和 stack2 是未定义的,还有一件更奇怪的事情,为什么 index2 返回一个 #document ,它应该包含元素数组的索引。
此外,元素 (el2) 甚至都不是正确的元素。 看一下,我叫
$('.ui-datepicker-calendar td a:test(3)')
这应该选择日历中的所有日期,并且在第一个循环中,console.log 应该打印出这个
<td class=" ui-datepicker-week-end " data-handler="selectDay" data-event="click" data-month="8" data-year="2012"><a class="ui-state-default" href="#">1</a></td>
但是我得到了整个文档中的第一个“a”标签,在这种情况下它是上个月的标签(如图所示)。
如果有人能对这种情况有所了解,请做。 哦,还有一件事我忘记了
meta2 ,它应该包含这个
[
':test(argument)', // full selector
'test', // only selector
'', // quotes used
'argument' // parameters
]
在我的情况下,它又是未定义的......
我将分享我的 javascript 代码,希望对您有所帮助
<script>
$(function()
$.expr[":"].test = function(el2,index2,meta2,stack2)
debugger;
console.log(el2);
console.log(index2);
console.log(meta2);
console.log(stack2);
)
$(function()
function getJsonDate(year, month)
$.getJSON('dates.php?year='+year+'&month='+month, function(data)
var i = 0;
for (i = 0; i < data.data.length; i++)
debugger;
var myDay = data.data[i]['d'];
$('.ui-datepicker-calendar td a:exactly('+data.data[i]['d']+')')
.css(color: '#f00')
.attr('href',data.data[i]['link'])
.parent().attr('onclick','');
);
$.expr[":"].exactly = function(el, index, meta, stack)
debugger;
console.log(el);
console.log(index);
console.log(meta);
console.log(stack);
var s = meta[3];
if (!s) return false;
return eval("/^" + s + "$/i").test($(el).text());
;
$('#datepicker').datepicker(
inline: true,
onSelect: function(dateText, inst)
Date.prototype.toString = function () return isNaN (this) ? 'NaN' : [this.getDate(), this.getMonth(), this.getFullYear()].join('/')
d = new Date(dateText);
getJsonDate(d.getFullYear(), d.getMonth()+1);
,
onChangeMonthYear: function(year, month, inst)
//alert(year);
//alert(month);
getJsonDate(year, month);
);
);
</script>
【问题讨论】:
我今天早上遇到了同样的问题,花了 2 小时,对我来说 meta 仍然是未定义的。以前工作得很好,如果你发现了什么,请让我更新。谢谢。 你好@JakubKuchar,我没能让它工作,所以我做了一个解决方法并选择了我需要其他方式的元素。但我仍然很想知道答案。 Sizzle 在 jQuery 1.8 中进行了更新,导致自定义选择器创建的语法发生重大变化。请查看 sizzle 文档以获取更多信息。 github.com/jquery/sizzle/wiki/Sizzle-Documentation 看来如果你升级到 jQuery 1.8.1,旧的选择器将再次工作。 请同时发布您的 html。 【参考方案1】:最短的解释是“jQuery 1.8.0 有一个错误,升级到 1.8.1 进行修复”,但这并不能完全回答所有问题。
jQuery 1.8.x 有一个显着升级的“Sizzle”引擎,它用于选择器。作为此更改的一部分,调用自定义选择器的方式已更改,而且许多选择器的处理方式已更改。关于规则处理顺序的各种假设不再成立,等等。在各种用例中也明显更快。
即使升级到 1.8.1,在处理您提供的示例时,您仍然会看到与 1.7.2(1.8.x 之前的系列中的最新版本)中的情况有很大不同。这解释了您在选择“页面上的第一个 元素”时看到的内容。也就是说:您对自定义选择器如何工作的期望不是它们实际如何工作,如果您允许循环继续(而不是在第一次迭代时调用“调试器”),您会看到它实际上正在通过所有 元素)。简而言之:Sizzle 不保证将调用各种规则的顺序,只保证结果将匹配所有规则。
如果您确定您的自定义规则的效率会低于其他规则(可能是因为您确信其他规则会严重减少匹配元素的数量),您可以通过选择它们来强制这些规则首先运行,然后仅在该元素子集上调用 .find(),例如:
$(".ui-datepicker-calendar").find("td a:test(3)");
至于“堆栈”未定义,正如 Kevin B 指出的那样,虽然 1.8.1 更新恢复了向后兼容性,但 API 发生了变化,看起来“堆栈”不再传递给伪.这确实是有道理的,因为调用测试的顺序发生了变化。即:当您到达堆栈时,堆栈是空的,因为“查看是否有任何 元素与此伪选择器匹配”是第一个被处理的规则。测试应该始终是自包含的,因此堆栈无论如何都不会真的很有用(可能只会导致混乱)。
那么如果 1.8.1 恢复了向后兼容,那么创建伪选择器的前向兼容方法是什么?正如你在the documentation for Sizzle's pseudo-selectors 中看到的,从 jQuery 1.8 开始,创建伪选择器的首选方法是“createPseudo”方法($.expr.createPseudo),它更喜欢使用闭包而不是“meta”参数。因此,对于您的特定示例,执行它们的“新”方法将是:
$.expr[":"].test = $.expr.createPseudo(function( tomatch )
return function( el2 )
debugger;
console.log(el2); // not much else to see here
;
)
其中“tomatch”是 :test(...) 选择器的参数。如您所见,您正在寻找的额外参数在这种新语法中不再需要。至于更有用的东西:
$.expr[":"].exactly = $.expr.createPseudo(function( s )
return function(el)
if(!s) return false;
return eval("/^" + s + "$/i").test($(el).text());
;
);
这个版本的“exactly”应该兼容1.8+,是首选的*做事方式。
我认为,即使 jQuery 版本/api 出现了问题,您提供的代码仍然不能完全按照您的意愿执行,因为日期选择器可能会根据插件的突发奇想进行重建。仍有一小段时间您可以判断出所需元素确实按预期突出显示,因此选择器本身似乎确实有效。
以下是基于您提供的示例的完整示例。您可以通过更改 1.7.2、1.8.0 和 1.8.1 之间使用的 jQuery 版本来查看行为差异。为了跨版本的兼容性,对 $.expr.createPseudo 的测试已添加到伪选择器函数分配中。注意所有的“调试器;”语句已被注释掉,因为在 datepicker 中的所有日期链接的每次迭代中都有一个断点变得相当乏味,并且 getJSON 调用已被模拟以允许测试是独立的。
<html>
<head>
<title>jQuery custom selector, "undefined"</title>
<!-- <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.js"></script> -->
<!-- <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.js"></script> -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.23/jquery-ui.js"></script>
<link rel="stylesheet"
href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.23/themes/base/jquery-ui.css" />
<script>
$(function()
$.expr[":"].test = $.expr.createPseudo ?
$.expr.createPseudo(function( tomatch )
return function( el2 )
// debugger;
console.log(el2);
;
) :
function(el2,index2,meta2,stack2)
// debugger;
console.log(el2);
console.log(index2);
console.log(meta2);
console.log(stack2);
;
)
$(function()
function getJsonDate(year, month)
//$.getJSON('dates.php?year='+year+'&month='+month, function(data)
//
var data = data:[
d:1,link:"a",
d:15,link:"b",
d:25,link:"c"
];
var i = 0;
for (i = 0; i < data.data.length; i++)
// debugger;
var myDay = data.data[i]['d'];
$('.ui-datepicker-calendar td a:exactly('+data.data[i]['d']+')').
css(color: '#f00').
attr('href',data.data[i]['link']).
parent().attr('onclick','');
//);
$.expr[":"].exactly = $.expr.createPseudo ?
$.expr.createPseudo(function( s )
return function(el)
if(!s) return false;
return eval("/^" + s + "$/i").test($(el).text());
;
) :
function(el, index, meta, stack)
// debugger;
console.log(el);
console.log(index);
console.log(meta);
console.log(stack);
var s = meta[3];
if (!s) return false;
return eval("/^" + s + "$/i").test($(el).text());
;
$('#datepicker').datepicker(
inline: true,
onSelect: function(dateText, inst)
Date.prototype.toString = function ()
return isNaN (this) ?
'NaN' :
[this.getDate(), this.getMonth(), this.getFullYear()].join('/')
d = new Date(dateText);
getJsonDate(d.getFullYear(), d.getMonth()+1);
,
onChangeMonthYear: function(year, month, inst)
//alert(year);
//alert(month);
getJsonDate(year, month);
return false;
);
);
</script>
<script>
(function($)$(function()
$("<input />").
attr(type:"button", value: "run test selector").
click(function()
$(".ui-datepicker-calendar td:test(3) a");
// Or, if you are certain that your test will be less-efficient than an exclusion based
// on parents, you could do:
// $(".ui-datepicker-calendar").find("td a:test(3)");
).
appendTo("body");
)(window.jQuery));
</script>
</head>
<body>
<a href="#ignoreThisLink">.</a>
<a href="#ignoreThisToo">.</a>
<p>
(first, click the input box to cause the datepicker to initialise)
</p>
<input type="text" id="datepicker" />
</body>
</html>
我希望这有助于阐明一些事情。
*我说这是“首选”方法,但您仍在使用“eval”。这是非常不鼓励的,因为它很慢并且可能导致意外/令人惊讶/危险的结果。基于字符串构建正则表达式的更好方法是
return (new RegExp("^" + s + "$", "i")).test($(el).text());
尽管这样也有问题,因为“s”可能包含 RegExp 特殊字符。但是,在这种特殊情况下,甚至不需要正则表达式,并且可以通过以下方式更有效地测试事物:
return String(s).toUpperCase() === $(el).text().toUpperCase();
您甚至可以通过将转换滚动到闭包中来节省更多,为您提供以下全部功能:
$.expr[":"].exactly = $.expr.createPseudo(function( s )
if(!s) return function() return false; ;
s = String(s).toUpperCase();
return function(el)
return (s === $(el).text().toUpperCase());
;
);
【讨论】:
【参考方案2】:jquery ui datepicker 具有这种功能的钩子。与其尝试定位构成日期的 DOM 元素,不如绑定到选择一个的行为。 jsFiddle
$('#datepicker').datepicker(
onSelect: function(dateText, inst)
//awesome ajax stuff based on dateText
);
编辑评论:如果您需要设置特定日期的样式,那么您应该通过在绘制之前应用自定义类来定位它。 jsFiddle
$('#datepicker').datepicker(
beforeShowDay: function(date)
return [true, 'date-' + date.getDate() ];
);
【讨论】:
是的,没错。但我需要并且我正在使用自定义 jquery 选择器来突出显示日期..例如。 当然,那么您可以在使用beforeShowDay
回调绘制日期之前将自定义类应用于日期,然后只定位该类。底线,总是首先使用 jquery UI api,它们很广泛。以上是关于jQuery 自定义选择器,“未定义”的主要内容,如果未能解决你的问题,请参考以下文章