我发现了一个无法解释的非常特殊的行为。我正在使用RxJS根据BehaviorSubject的值更新一组复选框。我既不想在事件处理程序中更改选中的属性,也不想让浏览器处理它,所以我使用preventDefault
并调用next
来设置新值。然后,在我的subscribe
回调中,尝试设置输入的checked
属性,但是它似乎没有任何作用。
运行下面的代码段,然后尝试更改任何复选框。请注意,每次记录元素的名称和假定的checked
值时,这似乎都表明一切正常,除了输入本身不会更改状态。我添加了其他样式,以使选中的项目更加可见。
const { BehaviorSubject } = rxjs;
const { distinctUntilChanged } = rxjs.operators;
const $checkboxes = Array.from(document.querySelectorAll('input[type="checkbox"]'));
const settings = {
a: false,
b: true,
c: false,
d: false,
e: true,
f: true,
}
const sources = {};
const observers = {};
$checkboxes.forEach(el => {
const { name } = el;
el.checked = settings[name];
sources[name] = new BehaviorSubject(el.checked);
observers[name] = sources[name].asObservable().pipe(distinctUntilChanged());
el.addEventListener('click', e => {
e.preventDefault();
const { name } = e.target;
sources[name].next(!settings[name]);
});
observers[name].subscribe(value => {
settings[name] = value;
el.checked = settings[name];
console.log(name, el.name, el.checked, value);
});
})
console.clear();
label {
display: inline-block;
margin: 3px;
}
input:checked + span {
font-weight: bold;
text-decoration: underline;
}
<label><input type="checkbox" name="a"> <span>Option A</span></label>
<label><input type="checkbox" name="b"> <span>Option B</span></label>
<label><input type="checkbox" name="c"> <span>Option C</span></label>
<label><input type="checkbox" name="d"> <span>Option D</span></label>
<label><input type="checkbox" name="e"> <span>Option E</span></label>
<label><input type="checkbox" name="f"> <span>Option F</span></label>
<script src="https://unpkg.com/rxjs@6.2.2/bundles/rxjs.umd.min.js"></script>
当然,这只是一个MCVE,我的实际代码分为多个文件和类。我当时在想,这可能与变量传递的方式有关,但是请注意,el.name
和name
的记录值相同,这意味着el
应该是和以前一样。
在el.checked
中将对setTimeout
的分配包装起来似乎可以解决该问题,但是这样做绝不是正确的解决方案。我想知道是什么原因导致这种行为,以及如何在没有黑客的情况下使这种设置正常进行。
答案 0 :(得分:1)
preventDefault()
将恢复值。请参阅
Why does preventDefault on checkbox click event returns true for the checked attribute?
您可以使用setTimeout(()=> xxx,0)推迟在preventDefault
运行之后发生的下一个事件循环中的执行。
const {
BehaviorSubject,
Observable,
defer
} = rxjs;
const {
distinctUntilChanged,
map,
mergeMap
} = rxjs.operators;
const $checkboxes = Array.from(document.querySelectorAll('input[type="checkbox"]'));
const settings = {
a: false,
b: true,
c: false,
d: false,
e: true,
f: true,
}
const sources = {};
const observers = {};
$checkboxes.forEach(el => {
const {
name
} = el;
el.checked = settings[name];
sources[name] = new BehaviorSubject(el.checked);
observers[name] = sources[name].asObservable().pipe(distinctUntilChanged());
el.addEventListener('click', e => {
const {
name
} = e.target;
sources[name].next(!settings[name]);
e.preventDefault();
});
console.log(Observable)
observers[name].pipe(map(value => {
return setTimeout(() => {
settings[name] = value;
el.checked = settings[name];
console.log(name, el.name, el.checked, value);
}, 0)
})).subscribe()
})
console.clear();
label {
display: inline-block;
margin: 3px;
}
input:checked+span {
font-weight: bold;
text-decoration: underline;
}
<label><input type="checkbox" name="a"> <span>Option A</span></label>
<label><input type="checkbox" name="b"> <span>Option B</span></label>
<label><input type="checkbox" name="c"> <span>Option C</span></label>
<label><input type="checkbox" name="d"> <span>Option D</span></label>
<label><input type="checkbox" name="e"> <span>Option E</span></label>
<label><input type="checkbox" name="f"> <span>Option F</span></label>
<script src="https://unpkg.com/rxjs@6.2.2/bundles/rxjs.umd.min.js"></script>