← All articles

Ionic's IonItem Can Become a div, a, or button—a Versatile Component

How ion-item picks div, anchor, or button from href and button attributes for accessible, standards-friendly DOM.

Published
Ionic's IonItem Can Become a div, a, or button—a Versatile Component cover image

When you use Ionic, IonItem (ion-item) is a component you reach for often. Let's look at its structure in detail.

Basic Usage

The basic usage is as follows:

<ion-item>
  <ion-label>Item</ion-label>
</ion-item>

In this case, IonItem expands as a Custom Element into the following tags:

<ion-item>
  <div>
    <slot name="start"></slot>
    <div class="item-inner">
    <div class="input-wrapper">
      <slot><ion-label>Item</ion-label></slot>  // ion-label is placed here because no slot is specified
    </div>
    <slot name="end"></slot>
  </div>
</ion-item>

(Strictly speaking it is shadow-root, so nested ion-label ends up outside shadow-root under host, but I omit that here.)

What I want you to notice here is ion-item > div. With no attributes specified, a generic container div is used, but IonItem changes this value based on its attributes.

Turning It into an a Tag

If you look at https://github.com/ionic-team/ionic-framework/blob/master/core/src/components/item/item.tsx#L258, the div above is determined by this conditional:

const TagType = clickable ? (href === undefined ? 'button' : 'a') : 'div' as any;

clickable is defined by the following code:

private isClickable(): boolean {
  return (this.href !== undefined || this.button);
}

this.href is the value passed when an href attribute is present, so for a component like ion-item[href=http://zenn.div], isClickable returns true and TagType becomes a. Attributes are passed through to TagType as-is.

So when you write:

<ion-item href="http://zenn.div">
  <ion-label>Item</ion-label>
</ion-item>

it expands like this:

<ion-item>
  <a href="http://zenn.div">
    <slot name="start"></slot>
    <div class="item-inner">
    <div class="input-wrapper">
      <slot><ion-label>Item</ion-label></slot>
    </div>
    <slot name="end"></slot>
  </a>
</ion-item>

Ionic is built with Web Components, a web standard, and you can see it expands the DOM while honoring HTML documentation practices—not only handling events in JavaScript. Angular's RouterLink works fine too, by the way.

Turning It into a button Tag

There are several ways to get a button tag instead of an a tag; the direct one is the ion-item[button=true] attribute. That makes it a button.

What matters here is that on mobile devices, except for a tags, button tags, and tags with a tappable attribute, there is a 300 ms delay before events fire while the device decides single vs double tap. In principle, "anything you click to fire an event must not be a div tag."

So anything that is not an a tag (link) needs to be a button tag.

When you write:

<ion-item button="true" (click)="launchZenn()">
  <ion-label>Item</ion-label>
</ion-item>

you get:

<ion-item>
  <button (click)="launchZenn()">
    <slot name="start"></slot>
    <div class="item-inner">
    <div class="input-wrapper">
      <slot><ion-label>Item</ion-label></slot>
    </div>
    <slot name="end"></slot>
  </button>
</ion-item>

The tappable attribute is not a web standard—it is vendor-specific on mobile devices—so you want to support web standards when you can.

Summary

Ionic samples look decent if you copy them as-is, but each component has many attributes.
Using them lets you build more attractive components closer to web standards, so read the documentation carefully and, if you can, look at component source too for deeper understanding.

https://github.com/ionic-team/ionic-framework/blob/master/core/src/components/item/item.tsx

See you again.