Don*_*own 8 html javascript jquery
我刚刚在选择元素中删除和重新附加选项时意识到了一种奇怪的行为.如果选择了其中一个选项,则在附加后,将选择下一个项目而不是原始项目.考虑以下html:
var $opts = $("#sel option").remove();
console.log($opts);
$("#sel").append($opts);Run Code Online (Sandbox Code Playgroud)
<select id="sel">
<option>A</option>
<option>B</option>
<option selected="selected">C</option>
<option>D</option>
</select>
(Look in the console.)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>Run Code Online (Sandbox Code Playgroud)
(或作为一个小提琴)
它使得选项具有值"D",而不是具有值的选项"C",如最初定义的那样.注意控制台中打印的选项,selected该remove()方法后属性会发生变化.
为什么会这样?
注意:我知道如何修复它或解决它,这不是问题.问题是为什么会发生?
有趣的问题。
我认为,这取决于浏览器在删除唯一选择的选项时自动选择一个选项,然后将选项添加到selected设置了标志的框中会发生什么。它也似乎是特定于浏览器的,这也许不足为奇。但更多的是,Chrome会根据我们的观看程度来改变其行为。这确实让我感到惊讶。详细信息如下。
首先,让我们使用诸如更新后的示例之类的内容,但首先选择的内容既不是第一个也不是最后一个,而是看到了最终的结果。因此,我们将得到A,B,C,D和E,然后从选择的B开始:
$("input[type=button]").on("click", function() {
var $opts = $("#sel option").remove();
$("#sel").append($opts);
});Run Code Online (Sandbox Code Playgroud)
<select id="sel">
<option>A</option>
<option selected="selected">B</option>
<option>C</option>
<option>D</option>
<option>E</option>
</select>
<input type="button" value="Go">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>Run Code Online (Sandbox Code Playgroud)
我们观察到的结果因浏览器而异:
更糟的是,Chrome浏览器的行为会根据我们是否观察到元素的属性而有所不同selected。Heisenbug!如果我们观察到它,它最终将执行Firefox(E)而不是C的操作。
这为我们提供了有关Chrome中发生的情况的重要线索。但是,让我们从简单的开始:
似乎IE11只是留下了这些标志,可能是因为它不担心在JavaScript代码运行时仅在它退回并且IE更新其UI时更新它们的担心。
如果selected在框中未选择任何选项时主动设置选项上的标志,则Firefox有意义(因为我们删除了选中的选项):
selected设置其标志。selected设置了B的标志,因此它成为选定的属性,而A被取消选择。我们可以观察IE11和Firefox的发生,因为它们不会根据我们是否观察到它们而改变其行为:
var $opts = $("#sel option");
showOptions("Before", $opts);
$opts.each(function() {
var opt = $(this);
opt.remove();
showOptions("After removing " + opt.text(), $opts);
});
$opts.each(function() {
var opt = $(this);
$("#sel").append(opt);
showOptions("After appending " + opt.text(), $opts);
});
function showOptions(label, $opts) {
var row = $("<tr>");
row.append($("<td>").text(label).addClass("label"));
$opts.each(function() {
row.append($("<td>").text(this.selected ? "*" : "o"));
});
row.appendTo("#results");
}Run Code Online (Sandbox Code Playgroud)
th, td {
min-width: 3em;
text-align: center;
padding: 4px;
}
table, th, td {
border-collapse: collapse;
border: 1px solid #eee;
}
.label {
text-align: left;
}Run Code Online (Sandbox Code Playgroud)
<select id="sel">
<option>A</option>
<option selected="selected">B</option>
<option>C</option>
<option>D</option>
<option>E</option>
</select>
<table>
<thead>
<tr>
<th></th>
<th>A</th>
<th>B</th>
<th>C</th>
<th>D</th>
<th>E</th>
</tr>
</thead>
<tbody id="results">
</tbody>
</table>
* = selected, o = not selected
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>Run Code Online (Sandbox Code Playgroud)
这使我们陷入困境:Chrome。Chrome会根据我们是否selected随便观察该属性来更改其行为。具体来说,如果我们观察selected到删除它们时仍在选择框中的选项,它将改变其行为。它不关心,如果我们看一下selected在去除的,它不会不管我们看一下selected,我们要追加。就像我们要删除的一样,还有我们还没有删除的那些。
首先,这是证明:
$("#btn").on("click", function() {
var observeLTE = true;
var observeGT = true;
var index;
var $opts = $("#sel option");
showOptions("Before", $opts);
observeLTE = $("#cbremove")[0].checked;
observeGT = $("#cbremoveun")[0].checked;
$opts.each(function(i) {
var opt = $(this);
index = i;
opt.remove();
showOptions("After removing " + opt.text(), $opts);
});
observeLTE = observeGT = $("#cbappend")[0].checked;
$opts.each(function(i) {
var opt = $(this);
index = i;
$("#sel").append(opt);
showOptions("After appending " + opt.text(), $opts);
});
function showOptions(label, $opts) {
var row = $("<tr>");
row.append($("<td>").text(label).addClass("label"));
$opts.each(function(i) {
var selected;
if ((observeLTE && i <= index) || (observeGT && i > index)) {
selected = this.selected ? "*" : "o";
} else {
selected = "-";
}
row.append($("<td>").text(selected));
});
row.appendTo("#results");
}
});Run Code Online (Sandbox Code Playgroud)
th, td {
min-width: 3em;
text-align: center;
padding: 4px;
}
table, th, td {
border-collapse: collapse;
border: 1px solid #eee;
}
.label {
text-align: left;
}Run Code Online (Sandbox Code Playgroud)
<div>
<label>
<input id="cbremove" type="checkbox">Observe <code>selected</code> on removed options while removing
</label>
</div>
<div>
<label>
<input id="cbremoveun" type="checkbox">Observe <code>selected</code> on UNremoved options while removing
</label>
</div>
<div>
<label>
<input id="cbappend" type="checkbox">Observe <code>selected</code> while appending
</label>
</div>
<select id="sel">
<option>A</option>
<option selected="selected">B</option>
<option>C</option>
<option>D</option>
<option>E</option>
</select>
<input id="btn" type="button" value="Go">
<table>
<thead>
<tr>
<th></th>
<th>A</th>
<th>B</th>
<th>C</th>
<th>D</th>
<th>E</th>
</tr>
</thead>
<tbody id="results">
</tbody>
</table>
* = selected, o = not selected, - = not observed
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>Run Code Online (Sandbox Code Playgroud)
如果您旋转一下(显然是在Chrome上),则无论您对第一个或最后一个复选框进行何种操作,都将最终以C结束,但是选中中间的复选框将使您最终以E结束。
这给了我们足够的数据来推断的解释。我不能说这实际上是解释,只是它适合可用数据。如果有人想更全面,可以在调试版本中逐步浏览Chrome的内部代码。我认为那太过分了。
因此,这里是我的回答,我再次强调,被推断:
selected属性。也就是说,如果您不读取属性的值,Chrome不会检查以确保该属性反映事物的当前状态。HTMLOptionElementselected排队change事件时,它读取属性。change如果该选择框已经挂起,则它不会将事件排队。基于此,我认为这是在Chrome中发生的情况:
change事件,因为值没有更改。change事件排队,该事件观察selected并因此将属性设置为C。change待处理的事件,因此没有新change事件排队,因此selected不会观察到D,并且其值也不会更新。它仍然false即使当我们读它,它会变成true。selected作为true对B和C,但没有其他人,因为d和E,而他们所选择的选项,并没有更新他们的价值并没有观察到。我们知道这些是它们的价值,因为那时我们可以观察它们。selected = true元素在已经选择了另一个元素时取消选择上一个元素。因此,在添加C时取消选择B。当我们添加D和E时,由于C的selected属性为,所以C会被保留false。哇,找出来很有趣。