In a Web Component How to show tooltip & hide tooltip when clicking outside or when another tooltip clicked

huangapple go评论72阅读模式
英文:

In a Web Component How to show tooltip & hide tooltip when clicking outside or when another tooltip clicked

问题

我有这个带有工具提示的 Web 组件。

我正在尝试完成三件事情。我尝试使用纯 JavaScript 而不是 jQuery 或其他库。

  1. 当单击按钮时显示,再次单击按钮时隐藏。这部分目前已经实现。

  2. 单击按钮外部时隐藏。我尝试选择文档,然后将单击的元素与我要目标的元素进行比较,但在目标元素的选择上遇到问题。

  3. 同一时间只显示一个工具提示,因此如果有一个工具提示处于打开状态,然后单击另一个按钮,只显示第二个工具提示。不确定如何处理这个。

这是我目前的代码。

class BuildABox extends HTMLElement {
  constructor() {
    super();
    this.querySelectorAll('[data-tooltip]').forEach((button) =>
      button.addEventListener('click', this.toggleTooltip.bind(this))
    );
  }

  toggleTooltip(e) {
    e.preventDefault();
    const toolTip = e.currentTarget.lastChild.previousSibling;
    toolTip.classList.toggle('tw-invisible');
  }
}

customElements.define('build-a-box', BuildABox);
<button id="info-btn" aria-label="Information" type="button" data-info="{{ block.settings.product_description }}">
  <div data-tooltip="tooltip" class="tw-w-12">
    <svg xmlns="http://www.w3.org/2000/svg"
          viewBox="0 0 24 24"
          fill="currentColor"
          class="w-12 h-12"
    >
      <path fill-rule="evenodd" d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12zm8.706-1.442c1.146-.573 2.437.463 2.126 1.706l-.709 2.836.042-.02a.75.75 0 01.67 1.34l-.04.022c-1.147.573-2.438-.463-2.127-1.706l.71-2.836-.042.02a.75.75 0 11-.671-1.34l.041-.022zM12 9a.75.75 0 100-1.5.75.75 0 000 1.5z" clip-rule="evenodd" />
    </svg>
    {% comment %} This is tooltip to hide/show {% endcomment %}
    <div
      id="tooltip"
      class="tw-invisible z-50 tw-absolute tw-top-24 tw-max-w-[300px] tw-bg-blue-400 tw-text-white tw-border-graphite tw-border-2 tw-overflow-auto tw-rounded-2xl tw-p-4 tw-mt-1"
    >
      <p>{{ block.settings.product_description }}</p>
    </div>
  </div>
</button>

希望这些代码对你有所帮助。

英文:

I have this Web Component that has cards with tooltips.

I am trying to accomplish 3 things. I am trying use Vanilla Javascript vs jQuery or other libraries.

  1. Show when a button is clicked then hide if button is clicked again. This is currently working.

  2. Hide when clicked outside of button. I tried selecting the document then comparing clicked element with the element I want to target, having trouble with targeting.

  3. Only show 1 tooltip at a time so if 1 is open then you click another only the second one shows. Not sure how to approach this.

Here is what I have so far.

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

class BuildABox extends HTMLElement {
  constructor() {
    super();
    this.querySelectorAll(&#39;[data-tooltip]&#39;).forEach((button) =&gt;
      button.addEventListener(&#39;click&#39;, this.toggleTooltip.bind(this))
    );
  }

  toggleTooltip(e) {
    e.preventDefault();
    const toolTip = e.currentTarget.lastChild.previousSibling;
    toolTip.classList.toggle(&#39;tw-invisible&#39;);
  }
}

customElements.define(&#39;build-a-box&#39;, BuildABox);

<!-- language: lang-html -->

&lt;button id=&quot;info-btn&quot; aria-label=&quot;Information&quot; type=&quot;button&quot; data-info=&quot;{{ block.settings.product_description }}&quot;&gt;
  &lt;div data-tooltip=&quot;tooltip&quot; class=&quot;tw-w-12&quot;&gt;
    &lt;svg  xmlns=&quot;http://www.w3.org/2000/svg&quot;
          viewBox=&quot;0 0 24 24&quot;
          fill=&quot;currentColor&quot;
          class=&quot;w-12 h-12&quot;
    &gt;
      &lt;path fill-rule=&quot;evenodd&quot; d=&quot;M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12zm8.706-1.442c1.146-.573 2.437.463 2.126 1.706l-.709 2.836.042-.02a.75.75 0 01.67 1.34l-.04.022c-1.147.573-2.438-.463-2.127-1.706l.71-2.836-.042.02a.75.75 0 11-.671-1.34l.041-.022zM12 9a.75.75 0 100-1.5.75.75 0 000 1.5z&quot; clip-rule=&quot;evenodd&quot; /&gt;
      &lt;/svg&gt;
    {% comment %} This is tooltip to hide/show {% endcomment %}
    &lt;div
      id=&quot;tooltip&quot;
      class=&quot;tw-invisible z-50 tw-absolute tw-top-24  tw-max-w-[300px] tw-bg-blue-400 tw-text-white tw-border-graphite tw-border-2 tw-overflow-auto tw-rounded-2xl tw-p-4 tw-mt-1&quot;
                              &gt;
      &lt;p&gt;{{ block.settings.product_description }}&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/button&gt;

<!-- end snippet -->

答案1

得分: 1

不创建依赖,因此尽量将大部分逻辑_内部_保留在 Web 组件中。

在这里使用 shadowDOM,因为它可以使 Web 组件更小,并且可以使用全局 CSS 来进行样式设置::part

关键概念: &lt;my-button tooltip&gt;
tooltip 属性决定它是否可见。

我添加了额外的样式以显示更多的样式选项。
要了解有关 &lt;slot&gt;::slotted 的详细信息,请阅读:https://stackoverflow.com/questions/61626493/slotted-css-selector-for-nested-children-in-shadowdom-slot/61631668#61631668

相同的代码在:https://jsfiddle.net/WebComponents/xg3ns6od/

customElements.define('my-button', class extends HTMLElement {
  constructor() {
    super().attachShadow({mode:"open"}).innerHTML =
    `<style>`+
      `button{ width:120px; cursor:pointer } ::slotted(div){ pointer-events:none }` + 
      `[name="tooltip"]::slotted(div) { color:blue; font-size: 1.2rem }`+
      `[name="label"]::slotted(div) { font-size:1em;font-weight:bold }`+
      `:host([tooltip]) svg { fill:green!important } `+
      `:host(:hover:not([tooltip])) path{ scale:1.2; transform-origin: center }` +
      `:host(:not([tooltip])) [name="tooltip"]::slotted(div) { visibility:hidden } `+
    `</style>`+
    `<button part="button"><svg part="icon" viewBox="0 0 2400 2400">`+
      `<path fill-rule="evenodd" d="m225 1200c0-539 437-975 975-975s975 437 975 975-437 975-975 975-975-436-975-975zm871-144c115-57 244 46 213 171l-71 284 4-2a75 75 90 0167 134l-4 2c-115 57-244-46-213-171l71-284-4 2a75 75 90 11-67-134l4-2zm104-156a75 75 90 100-150 75 75 90 000 150z"/></svg><slot name="tooltip"></slot>`+
      `<slot name="label"></slot></button>`;
  }
  connectedCallback(){
  	this.onclick = () => this.tooltip = !this.tooltip;
    this.globalListener = document.addEventListener("click",
                                  (evt) => this.tooltip = evt.target == this )
  }
  get tooltip()   { return this.hasAttribute("tooltip") }
  set tooltip(val){	this.toggleAttribute("tooltip",val) }

  disconnectedCallback(){
  	document.removeEventListener("click", this.globalListener);
  }
});
/* 在 shadowDOM 中样式部分 */
my-button::part(icon){ fill:red }

/* 在 lightDOM 中样式! */
my-button[tooltip] [slot="tooltip"] { background:lightgreen }
<my-button>
  <div slot="tooltip">Tooltip 1</div>
  <div slot="label">Product Description</div>
</my-button>
<my-button tooltip>
  <div slot="tooltip">Tooltip 2</div>
  <div slot="label">Product Description</div>
</my-button>
<my-button>
  <div slot="tooltip">Tooltip 3</div>
  <div slot="label">Product Description</div>
</my-button>
英文:

You don't want to create dependencies, thus keep as much of the logic inside the Web Component

Using shadowDOM here because it keeps the Web Component small
and you can style with global CSS using ::part

Key concept: &lt;my-button tooltip&gt;
The tooltip attribute determines of it is visible, or not

I whacked in additional styling to show more styling options.
For deep-deep-details on &lt;slot&gt; and ::slotted read: https://stackoverflow.com/questions/61626493/slotted-css-selector-for-nested-children-in-shadowdom-slot/61631668#61631668

same code in: https://jsfiddle.net/WebComponents/xg3ns6od/

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

customElements.define(&#39;my-button&#39;, class extends HTMLElement {
  constructor() {
    super().attachShadow({mode:&quot;open&quot;}).innerHTML =
    `&lt;style&gt;`+
      `button{ width:120px; cursor:pointer } ::slotted(div){ pointer-events:none }` + 
      `[name=&quot;tooltip&quot;]::slotted(div) { color:blue; font-size: 1.2rem }`+
      `[name=&quot;label&quot;]::slotted(div) { font-size:1em;font-weight:bold }`+
      `:host([tooltip]) svg { fill:green!important } `+
      `:host(:hover:not([tooltip])) path{ scale:1.2; transform-origin: center }` +
      `:host(:not([tooltip])) [name=&quot;tooltip&quot;]::slotted(div) { visibility:hidden } `+
    `&lt;/style&gt;`+
    `&lt;button part=&quot;button&quot;&gt;&lt;svg part=&quot;icon&quot; viewBox=&quot;0 0 2400 2400&quot;&gt;`+
      `&lt;path fill-rule=&quot;evenodd&quot; d=&quot;m225 1200c0-539 437-975 975-975s975 437 975 975-437 975-975 975-975-436-975-975zm871-144c115-57 244 46 213 171l-71 284 4-2a75 75 90 0167 134l-4 2c-115 57-244-46-213-171l71-284-4 2a75 75 90 11-67-134l4-2zm104-156a75 75 90 100-150 75 75 90 000 150z&quot;/&gt;`+
      `&lt;/svg&gt;&lt;slot name=&quot;tooltip&quot;&gt;&lt;/slot&gt;`+
            `&lt;slot name=&quot;label&quot;&gt;&lt;/slot&gt;&lt;/button&gt;`;
  }
  connectedCallback(){
  	this.onclick = () =&gt; this.tooltip = !this.tooltip;
    this.globalListener = document.addEventListener(&quot;click&quot;,
                                  (evt) =&gt; this.tooltip = evt.target == this )
  }
  get tooltip()   { return this.hasAttribute(&quot;tooltip&quot;) }
  set tooltip(val){	this.toggleAttribute(&quot;tooltip&quot;,val) }

  disconnectedCallback(){
  	document.removeEventListener(&quot;click&quot;, this.globalListener);
  }
});

<!-- language: lang-css -->

/* style parts IN shadowDOM */
my-button::part(icon){ fill:red }

/* style lightDOM! */
my-button[tooltip] [slot=&quot;tooltip&quot;] { background:lightgreen }

<!-- language: lang-html -->

&lt;my-button&gt;
  &lt;div slot=&quot;tooltip&quot;&gt;Tooltip 1&lt;/div&gt;
  &lt;div slot=&quot;label&quot;&gt;Product Description&lt;/div&gt;
&lt;/my-button&gt;
&lt;my-button tooltip&gt;
  &lt;div slot=&quot;tooltip&quot;&gt;Tooltip 2&lt;/div&gt;
  &lt;div slot=&quot;label&quot;&gt;Product Description&lt;/div&gt;
&lt;/my-button&gt;
&lt;my-button&gt;
  &lt;div slot=&quot;tooltip&quot;&gt;Tooltip 3&lt;/div&gt;
  &lt;div slot=&quot;label&quot;&gt;Product Description&lt;/div&gt;
&lt;/my-button&gt;

<!-- end snippet -->

答案2

得分: 0

以下是翻译好的部分:

<build-a-box>
  <button id="info-btn" aria-label="Information" type="button" data-info="{{ block.settings.product_description }}" data-tooltip-target="mine">
    <div class="tw-w-12">
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-12 h-12">
        <path fill-rule="evenodd" d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12zm8.706-1.442c1.146-.573 2.437.463 2.126 1.706l-.709 2.836.042-.020a.75.75 0 01.670 1.34l-.040.022c-1.147.573-2.438-.463-2.127-1.706l.71-2.836-.042.020a.75.75 0 11-.671-1.34l.041-.022zM12 9a.75.75 0 100-1.5.75.75 0 000 1.5z"></path>
      </svg>
      <tool-tip data-tooltip="mine" class="tw-invisible">
        <span class="tooltip-text">This is tooltip to hide/show</span>
      </tool-tip>
      <div class="z-50 tw-absolute tw-top-24 tw-max-w-[300px] tw-bg-blue-400 tw-text-white tw-border-graphite tw-border-2 tw-overflow-auto tw-rounded-2xl tw-p-4 tw-mt-1">
        <p>{{ block.settings.product_description }}</p>
      </div>
    </div>
  </button>
</build-a-box>

请注意,代码部分未被翻译。如果您需要进一步的翻译,请提供相应的指示。

英文:

To expound upon my prior comments which I deleted; I updated your code slightly and added some CSS to show it clearly here. I have a data element to choose a target and then use that in the click handler.

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

class BuildABox extends HTMLElement {
  constructor() {
    super();
    this.querySelectorAll(&#39;[data-tooltip-target]&#39;).forEach((button) =&gt; {
      button.addEventListener(&#39;click&#39;, this.toggleTooltip.bind(this))
    });
  }

  toggleTooltip(e) {
    e.preventDefault();
    const mytarget = e.currentTarget.dataset.tooltipTarget;
    const toolTip = e.currentTarget.querySelector(`[data-tooltip=&quot;${mytarget}&quot;]`);
    toolTip.classList.toggle(&#39;tw-invisible&#39;);
  }
}

customElements.define(&#39;build-a-box&#39;, BuildABox);

<!-- language: lang-css -->

.tw-invisible {
  display: none;
}

.tooltip-text {
  color: blue;
  font-size: 1.5rem;
}

<!-- language: lang-html -->

&lt;build-a-box&gt;
  &lt;button id=&quot;info-btn&quot; aria-label=&quot;Information&quot; type=&quot;button&quot; data-info=&quot;{{ block.settings.product_description }}&quot; data-tooltip-target=&quot;mine&quot;&gt;
  &lt;div class=&quot;tw-w-12&quot;&gt;
    &lt;svg  xmlns=&quot;http://www.w3.org/2000/svg&quot;
          viewBox=&quot;0 0 24 24&quot;
          fill=&quot;currentColor&quot;
          class=&quot;w-12 h-12&quot;
    &gt;
      &lt;path fill-rule=&quot;evenodd&quot; d=&quot;M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12zm8.706-1.442c1.146-.573 2.437.463 2.126 1.706l-.709 2.836.042-.02a.75.75 0 01.67 1.34l-.04.022c-1.147.573-2.438-.463-2.127-1.706l.71-2.836-.042.02a.75.75 0 11-.671-1.34l.041-.022zM12 9a.75.75 0 100-1.5.75.75 0 000 1.5z&quot; clip-rule=&quot;evenodd&quot; /&gt;
      &lt;/svg&gt;
    &lt;tool-tip data-tooltip=&quot;mine&quot; class=&quot;tw-invisible&quot;&gt;
    &lt;span class=&quot;tooltip-text&quot;&gt;This is tooltip to hide/show&lt;/span&gt;
    &lt;/tool-tip&gt;
    &lt;div class=&quot;z-50 tw-absolute tw-top-24  tw-max-w-[300px] tw-bg-blue-400 tw-text-white tw-border-graphite tw-border-2 tw-overflow-auto tw-rounded-2xl tw-p-4 tw-mt-1&quot;&gt;
      &lt;p&gt;{{ block.settings.product_description }}&lt;/p&gt;
    &lt;/div&gt; 
  &lt;/div&gt;
&lt;/button&gt;
&lt;/build-a-box&gt;

<!-- end snippet -->

huangapple
  • 本文由 发表于 2023年2月10日 10:58:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/75406514.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定