【问题标题】:Svelte: Set focus to slotted element in custom web componentSvelte:将焦点设置到自定义 Web 组件中的开槽元素
【发布时间】:2020-01-22 22:32:45
【问题描述】:

我目前正在使用 Svelte 构建自定义 Web 组件库。

我已经完成了一个选择选项下拉组件,它可以接受嵌套的、开槽的内容来组成下拉选项。它对鼠标用户没有任何已知问题,并且可以这样实现:

<custom-select label="My Custom Select Element">
        <custom-option value="1">Option 1</custom-option>
        <custom-option value="2">Option 2</custom-option>
        <custom-option value="3">Option 3</custom-option>
</custom-select>

我现在正在研究组件的可访问性,并且需要实现“陷阱焦点”。在不涉及实现陷阱焦点的各种方法的情况下,我试图完成一个简单的测试,将焦点分配给父组件中的一个开槽元素。开槽的元素都有 tabindex="0" ,我可以毫无问题地通过它们。

我可以使用 Svelte 中的绑定成功地“抓取”我想要聚焦的元素。虽然您不能直接在 slot 元素上放置绑定指令(您 可以 在 slot 上放置属性),但我能够像这样绑定到父 span 标签:

<span bind:this={slotObj}>
        <slot />
</span>

然后,我可以使用一些 JavaScript 深入到绑定的父级,并将开槽的元素分配给一个名为“选项”的对象:

let slot = slotObj.children[0];
options = slot.assignedElements();

如果我要控制台记录选项对象中的第一个元素,我会看到一个预期的:

console.log(options[0]); // <custom-option value="1">Option 1</custom-option>

我还可以成功修改选项对象中元素的属性,并查看 DOM 中的变化。因此,在“抓取”开槽元素并更改其属性时没有问题。但是,以下内容将在父组件内工作以聚焦开槽元素:

options[0].focus();

这不会引发任何错误,它只是没有做任何我能够辨别的事情。如前所述,我可以通过选项卡进行选项卡,因此它们能够在常规选项卡场景中接收焦点。只有通过 JavaScript,我似乎无法分配焦点。

在处理自定义 Web 元素和插槽内容时,任何 Svelte 特定建议和/或一般建议将不胜感激。非常感谢。

【问题讨论】:

标签: focus web-component shadow-dom svelte slot


【解决方案1】:

我过去做过的一件事是我隐藏了父标签,然后我抓取了选项的所有值并使用#each 将它们放入新标签中。我将相关的事件监听器放在了#each 块的父级上。

App.svelte

<MultiSelect>
  <option value="MS">Malay</option>
  <option value="RU">Russian</option> 
  <option value="TH">Thai</option>
</MultiSelect>
MultiSelect.svelte

<script>
  let slot, options = [], selected = {};
  onMount(() => {
    slot.querySelectorAll('option').forEach(o => {
      o.selected && !value.includes(o.value) && (value = [...value, o.value]);
      options = [...options, {value: o.value, name: o.textContent}]
    });
</script>

<ul class="options" on:mousedown|preventDefault={handleOptionMousedown}>
  {#each filtered as option}
    <li class:selected={selected[option.value]} class:active={activeOption === option} data-value="{option.value}">{option.name}</li>
  {/each}
</ul>

<select bind:this={slot} type="multiple" class="hidden"><slot></slot></select>

Here's the link to the whole REPL

【讨论】:

  • 这很棒。谢谢你,本尼!
  • 如果你觉得有用,记得投上一票:)
  • 很遗憾,“声望低于 15 人的投票会被记录,但不会更改公开显示的帖子得分。”但我给了你我的投票:) 总有一天它会显示大声笑
【解决方案2】:

我认为 Benny Hinrichs 提供了一个优雅的解决方案(加上一个有效的REPL!),它涉及的变通办法比我发现的以下方法要少。

奇怪的是,聚焦 light DOM 元素(通过 options[0]),如我开篇文章中所述,并没有设置焦点,但是钻入 light DOM 元素的 shadowRoot让我专注于一个包含我的选项的 div 类。这成功地将焦点分配给了一个开槽元素,只是它是 错误 元素...

似乎存在堆栈顺序问题(可能特定于 Svelte),因为将焦点包装在 setTimeout 函数中(您可以使用 0 毫秒)然后将焦点设置到数组中的正确元素,在这种情况下是位置中的元素0 即我列表中的第一个下拉选项。

setTimeout(function() {
        options[0].shadowRoot.querySelector(".option-container").focus();
}, 0);

【讨论】:

    猜你喜欢
    • 2019-06-21
    • 2019-11-13
    • 1970-01-01
    • 2020-06-17
    • 2020-03-27
    • 2019-10-14
    • 2021-07-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多