当从 RxJS 订阅回调更改时,选中的复选框 属性 不会更新元素

Checkbox checked property does not update element when changed from RxJS subscribe callback

我发现了一个我无法解释的非常奇怪的行为。我正在使用 RxJS 根据 BehaviorSubject 的值更新一组复选框。我不想更改事件处理程序中的 checked 属性,也不想让浏览器处理它,所以我使用 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.namename 在记录的值中是相同的,这意味着 el 应该是与之前相同的元素。

将对 el.checked 的赋值包装在 setTimeout 中似乎可以解决问题,但这样做几乎从来都不是正确的解决方案。我想知道是什么导致了这种行为,以及如何在没有黑客的情况下使这种设置正常工作。

preventDefault()会在点击事件监听器执行后恢复值。请参见

您可以使用 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>