我正在尝试使用jQuery ui日历在点击日期时进行ajax调用, 但是几天前我遇到了一个问题。 我找到了一段代码,据说可以做到这一点,但正如我发现它使用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>
答案 0 :(得分:6)
最简短的解释是“jQuery 1.8.0中有一个错误,升级到1.8.1进行修复”,但这并不能解决所有问题。
jQuery 1.8.x有一个显着升级的“Sizzle”引擎,它用于选择器。调用自定义选择器的方式已作为此更改的一部分进行了更改,而且更改了处理许多选择器的方式。关于处理规则的顺序的各种假设不再成立,等等。在各种用例中它也明显更快。
即使升级到1.8.1,在处理您提供的示例时,您仍会看到与1.7.2(1.8.x之前的系列中的最新版本)中的内容完全不同的内容。这解释了您在选择“页面上的第一个&lt; a&gt;元素”时所看到的内容。也就是说:你对自定义选择器如何工作的期望不是它们实际工作的方式,如果你允许循环继续(而不是在第一次迭代时调用'debugger;'),你会看到它实际上正在通过所有&lt; a&gt;元件)。简而言之:Sizzle不保证将调用各种规则的顺序,只是结果将匹配所有规则。
如果您确定自定义规则的效率低于其他规则(可能是因为您确定其他规则可以严重减少匹配元素的数量),您可以通过选择它们来强制执行这些规则,然后仅在元素子集上调用.find(),例如:
$(".ui-datepicker-calendar").find("td a:test(3)");
至于未定义的“堆栈”,正如Kevin B指出的那样,虽然1.8.1更新恢复了向后兼容性,但API已经改变,看起来“堆栈”根本就不再传递给伪。实际上,这是有道理的,因为可以调用测试的顺序改变。也就是说:当你到达它时,堆栈是空的,因为“看看是否有任何&lt; a&gt;元素与这个伪选择器匹配”是第一个被处理的规则。测试应始终是自包含的,因此无论如何堆栈都不会非常有用(可能只会导致混淆)。
因此,如果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());
};
});
此版本的“完全”应该与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());
};
});
答案 1 :(得分:4)
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() ];
}
});