Skip to content
Open UI

Interest Invokers (Explainer)

Interest Invokers

Pitch in Code

<button interesttarget="my-tooltip">Hover/Focus to show the tooltip</button>

<span popover=hint id="my-toolip">This is the tooltip</span>

Introduction

Following invokers, adding an interesttarget and interestaction attributes to interactive elements: starting with <button>, <input type="button">/<input type="reset">/<input type="submit">, <a>, and <area> (perhaps expanding to <input>, <textarea>, <select>, <summary> or maybe more, see #14). This would allow disclosure of high fidelity tooltips in a more accessible and declarative way. Elements with interesttarget will - when hovered, or focussed (or an equivalent user-agent determined action) - dispatch an InterestEvent on the element referenced by interesttarget, with some default behaviours.

Terms

  • Interest/Shows Interest: The action of Interest refers to the user “landing” on an element without invoking it, using a Human Input Device (HID). See below for how various HIDs can show interest.
  • Loses Interest/Lost Interest: The action of Loses Interest refers to the user “moving away” from an element, or its interestee, using a HID; in other words interest must be on a different element that is neither. Elements can only Lose Interest if they are in the state of Showing Interest. See below for how various HIDs can lose interest.
  • Interestee: An element which is referenced to by an Interest element, via the interesttarget attribute.

HIDs and interest

Interest is shown and lost based on a user gesture, which will be dependent on the users Human Input Device (HID). A keyboard will use focus to determine interest equivalent to the focusin/focusout events available today. A pointer device, such as a mouse, will use the pointerenter/pointerleave.

“Interest” is an intentional abstraction from traditional keyboard and pointer events to allow other HIDs to map an appropriate (privacy preserving) user gesture, without emulating or otherwise conflicting with keyboard or pointer events. For example a user agent with a touch screen HID might map showing interest to a menu item in the devices long-press context-menu, while a device with hand tracking (such as mixed reality headsets) might offer an explicit hand gesture.

While the proposal aims to encompass alternate HIDs and decvices, the specifics precisely how these HIDs will show Interest is not part of the initial proposal, and is an area of active discussion. It also stands to reason that to-be-invented devices may introduce new and novel concepts of showing/losing interest.

Proposed Plan

In the style of invoketarget, this document proposes we add interesttarget and interestaction as available attributes to <button>, <a>, <area> and <input> elements.

interface mixin InterestInvokerElement {
  [CEReactions] attribute Element? interestTargetElement;
  [CEReactions] attribute DOMString interestAction;
};
HTMLButtonElement includes InterestInvokerElement
HTMLInputElement includes InterestInvokerElement
HTMLAnchorElement includes InterestInvokerElement
HTMLAreaElement includes InterestInvokerElement
SVGAElement includes InterestInvokerElement

The interesttarget value should be an IDREF pointing to an element within the document. .interestTargetElement also exists on the element to imperatively assign a node to be the invoker target, allowing for cross-root invokers (in some cases, see attr-asociated element steps for more).

The interestaction (and the interestAction reflected property) is a freeform hint to the interestee. InterestAction can be a “built-in” action or a “custom” action. If interestaction is a false value (''``,null, etc), then it will default to anauto` state.

Built-in interactive elements have built-in behaviours (detailed below) which are determined by the interestaction. The built-in names must not contain a -. An interestaction without a dash that is not a built-in is considered invalid, and will not dispatch an InterestEvent.

Valid interestactions (that is: custom interestaction or a valid built-in) will dispatch InterestEvent, allowing custom code to take control of interest invocations (for example calling .preventDefault() or executing custom side-effects).

Elements with interesttarget set will dispatch an InterestEvent on the Interestee (the element referenced by interesttarget) when the element Shows Interest or Loses Interest. When the element Shows Interest the event type will be 'interest'. If the element has Shown Interest, and interest moves away from both the Interest Element and the Interestee, then the element Loses Interest and an InterestEvent with the type of 'loseinterest' will be dispatched on the Interestee. The event also contains a invoker property that will reference the Interest element. InterestEvent objects are always non-bubbling, composed, cancellable events.

[Exposed=Window]
interface InterestEvent : Event {
  constructor(DOMString type, InterestEventInit interestEventInit);
  readonly attribute Element invoker;
  readonly attribute DOMString type = "interest";
  readonly attribute DOMString action;
};
dictionary InterestEventInit : EventInit {
  DOMString action = "";
  Element invoker;
};

Both interesttarget and invoketarget can exist on the same element at the same time, and both should be respected.

If a <button> is a form participant, or has type=submit, then invoketarget must be ignored. interesttarget is still valid in these scenarios.

If an <input> is a form participant, or has a type other than reset or button, then invoketarget and interesttarget must be ignored. interesttarget additionally works with type of submit.

Example Code

Popovers

An interesttarget allows for tooltips using popover:

<button interesttarget="my-popover">Open Popover</button>

<div id="my-popover" popover="hint">Hello world</div>

Custom behaviour

Elements with Interest will dispatch events on the Interestee element, allowing for custom JavaScript to be triggered without having to wire up manual event handlers to the Interest elements.

<button interesttarget="my-custom" interestaction="custom-action">
  When this button shows interest, the below div will display.
</button>

<div id="my-custom">Supplementary information</div>

<script>
  const custom = document.getElementById("my-custom");
  custom.addEventListener("interest", (e) => {
    if (e.action !== "custom-action") return;
    custom.classList.add('active')
  });
  custom.addEventListener("loseinterest", (e) => {
    if (e.action !== "custom-action") return;
    custom.classList.remove('active')
  });
</script>

Usability

The events interest and loseinterest are intentionally abstract to allow more complex usability concepts to unfold. One such area being explored is the ability to add “safe areas” or “hit triangles”, which allow the user to move the pointer between the Interest Invoker (e.g. the button) and Interestee (e.g. the tooltip). See #963 for more.

Accessibility

Warning: This section is TBD. PRs and discussions welcome.

See the popover=hint explainer’s accessibility section for additional comments. In particular, many of the relationships that are provided for “regular” invokers such as popovertarget apply to interest-based invokers also. For example, an aria-details relationship should be established between an interest invoker and its target.

In particular, for interest-based triggering of popovers, there is a problem with hidden objects. In general, the details relation does not work for hidden objects, and we can’t necessarily expect the popover to be complete before it becomes visible, as that may happen on-demand. So what happens if the user navigates to a popover’s trigger, and it’s not open? If they tabbed there, then we can make sure the popover is shown on focus. But, if they are navigating with a virtual buffer as is likely, how will they trigger the hint popover to open (the user would need to know to focus the trigger, or JAWS/NVDA would need to start focusing objects during navigation as other screen readers do), and then and how will the user know it’s there (should we use a new value of the haspopup object attribute for these, e.g. haspopup=hint)? See https://issues.chromium.org/issues/377923492 for more discussion.

Defaults

Depending on the target set by interesttarget, showing interest or losing interest can trigger additional behaviours alongside the event dispatch. Showing/losing interest on an interesttarget will always dispatch a trusted InterestEvent, but in addition the following table represents how interest on specific element types are handled. Note that this list is ordered and higher rules take precedence:

When the interestaction attribute is missing it will default to an auto state.

Interestee Elementaction hintEvent TypeBehaviour
<* popover=*>(auto)'interest'Call .togglePopover() on the invokee
<* popover=*>togglePopover'interest'Call .togglePopover() on the invokee
<* popover=*>(auto)'loseinterest'Call .togglePopover() on the invokee
<* popover=*>togglePopover'loseinterest'Call .togglePopover() on the invokee

Note: The above table is an attempt at wide coverage, but ideas are welcome. Please submit a PR if you have one!

PAQ (Potentially Asked Questions)

Why the name interest? Why not hover or focus?

Much like click, hover or focus are specific to certain types of HID, and are not terms which encompass all viable methods of interaction. Lots of alternatives were discussed and it was deemed that interest is the best name to explain the concept of a “hover or focus or equivalent”.

Why is interesttarget on a lot more elements than invoketarget?

While invocation should only be limited to buttons, disclosure of supplementary information can be expanded to all interactive elements. There are many useful use cases for offering a tooltip on anchors, such as signalling that they are external, or that they will open in a new window, or to show preview information (think: preview windows on iOS Safari or the hovercards that display on GitHub over a user’s handle).

Why is interesttarget not unlimited, like title is?

It could be considered a mistake to allow title on all elements; as adding interactivity to non-interactive elements creates many problems. Limiting where interesttarget is allowed aims to create a “pit of success”, guiding developers to use it only on interactive elements, where it makes sense.