HTML 5.1 のmenuitem要素を使ってコンテキストメニューをカスタマイズする

HTML 5.1(W3C)がアップデートされましたね。

変更点はDifferences from HTML4(W3C)にまとまっていますが、main要素の追加、hgroup要素の削除などとともに個人的に注目しているのは menu API が刷新されたことです。

HTML4 以前の話も含め、改めておさらいしてみたいと思います。

menu要素の生い立ち

§

menu要素の歴史は古く、1993年のいわゆるHTML 1.0(W3C)の Internet Draft 文書には uldir などとともに定義されています。当時の意味づけは ul と似た順不同リストのようで、 "smaller paragraphs" を表す点などが異なりました。ところがブラウザはそうは描画しなかったようで、HTML 3.2ではIn practice, Mosaic and most other user agents have ignored this advice and instead render DIR and MENU in an identical way to UL elements.(W3C)と書かれてしまっています。

HTML 4ではこの要素は Deprecated(推奨しない)とされ、代わりに ul 要素を使うよう推奨されています。

HTML5 のmenu要素とcommand要素

§

HTML5では従来非推奨とされ、また実際もほとんど使われていなかった [要出典] menu要素がコマンドリストを示すものとして復活しました。さらにcommand要素が登場し、menu要素と組み合わせることでツールバーやコンテキストメニューを作ることが可能、とされました。

ただし、私の知る限りcommand要素をサポートしたブラウザは存在しません。

2013年8月8日追記2013年8月6日版(CR)(W3C)command要素とmenu要素は削除されました。

HTML 5.1 のmenu要素とmenuitem要素

§

去年12月に HTML5 が勧告候補(CR)になるとともに最初の草案が発表された HTML 5.1 ですが、先日1回目のアップデート(W3C)が行われました。menu API 関連の主な変更点はこんなところです。

  • command要素を止めて新たにmenuitem要素が追加
  • menu要素のtypeが変更、属性を省略した場合のデフォルトも変わっている
  • button要素のtypemenuが追加

HTML5 では、menu要素のtypeは「context(コンテキストメニュー)」「toolbar(ツールバー)」、および省略時の「リスト」の3種類とされていました。HTML 5.1 ではこれが「popup(ポップアップメニュー)」「toolbar(ツールバー)」に変更されています。

コンテキストメニューはポップアップメニューに含まれていますが、現状は唯一(?)対応しているFirefoxが popup を認識しないため、コンテキストメニューを出したい場合は従来どおり context を指定する必要があります。

menuitem要素は実例を見た方が早いでしょう。コンテキストメニューをカスタマイズするにはこんなマークアップを行います。

<p contextmenu="my_context">Firefox 8 以降でこの文章上を右クリックすると独自のコンテキストメニューを表示します。</p>
<menu type="context" id="my_context">
  <menuitem label="独自のコンテキストメニューです" icon="context_icon.png"/>
</menu>
  • コンテキストメニューの場合、label属性は必須です。

これだけではあくまで右クリックしたときなどに表示を行うだけで、それを選択しても何も起こりません。実際の動作はスクリプトで定義することになります。

実例: 引用文のcite属性へ遷移する機能を作ってみる

§

HTML4では引用を表す blockquote, q要素にcite属性が定義され、機械可読可能な形で出典の提示が行えるようになりました。一方、cite属性値を画面に表示したり、自動リンクするようなブラウザは存在しないため、閲覧者に対して出典を明示する場合はa要素でリンクを併記するなどの対応が必要です。

一応、Operaは次のような拡張スタイルシートを使えばリンクすることができるのですが、次バージョンの Opera 15 ではレンダリングエンジンの変更に伴って使えなくなりますし、そもそも現状誰も使っていない [要出典] でしょうし…。

blockquote[cite],
q[cite] {
  -o-link: attr(cite);
  -o-link-source: current;
}

ともかくそんな状況ですので、menuitem要素を使って「引用元ページを見る」機能を付けてみます。

<!DOCTYPE html>
<html lang="ja">
<head>
<title>menuitem要素を使って引用元ページ(cite属性)へ遷移するメニューを作る</title>
<script>
window.addEventListener("load", function() {
  /**
   * menuitem要素を使って引用元ページ(cite属性)へ遷移するメニューを作る
   */
  (function() {
    if (!("contextMenu" in document.body) || !("HTMLMenuItemElement" in window)) {
      // menuitem要素によるコンテキストメニューのカスタマイズに対応していない場合
      return;
    }

    var target;
    var quotes = document.querySelectorAll('blockquote[contextmenu], q[contextmenu]');
    for (var i = 0, quoteLen = quotes.length; i < quoteLen; i++) {
      quotes[i].addEventListener("mousemove", function(ev) {
        // contextmenu属性が設定された blockquote, q 要素にマウスが乗ったときは当該要素を記録する
        target = ev.currentTarget;
      }, false);
    }

    var menuitem = document.createElement("menuitem");
    menuitem.label = "引用元ページを見る";
    menuitem.icon = "context_icon.png";
    menuitem.addEventListener("click", function() {
      // 対象コンテキストを押下した場合、当該引用文のcite属性値のリソースへ遷移する
      location.assign(target.cite);
    }, false);

    // 生成したmenuitem要素をmenu要素に挿入する
    document.getElementById("quote_cite").appendChild(menuitem);
  })();
}, false);
</script>
</head>
<body>

<p>menu要素について、HTML 2.0(RFC 1866)では以下のように定義されていました。</p>
<blockquote lang="en" cite="http://tools.ietf.org/html/rfc1866#section-5.6.4" contextmenu="quote_cite">
<p>The &lt;MENU&gt; element is a list of items with typically one line per item. The menu list style is typically more compact than the style of an unordered list.</p>
</blockquote>
<p>ところが、HTML 3.2では
  <q cite="http://www.w3.org/TR/REC-html32#menu" contextmenu="quote_cite">In practice, Mosaic and most other user agents have ignored this advice and instead render DIR and MENU in an identical way to UL elements.</q>
  と書かれてしまっています。</p>

<menu type="context" id="quote_cite"></menu>

</body>
</html>

Firefoxで引用文上で右クリックすると、コンテキストメニューの最上段に「引用元ページを見る」が出現し、文字の左側にはアイコン(context_icon.png)も表示されます。

サムネイル画像
menuitem要素を使ってコンテキストメニューをカスタマイズした例(Firefox 21)オリジナル画像

このコードでは、HTMLにはmenu要素のみを書いておき、menuitem要素はJavaScriptで挿入しています。スクリプト無効環境では独自のコンテキストメニューを表示する意味が無い(押しても動作しない)ので、このような実装をしています。

また、blockquoteq要素のcontextmenu属性はcite属性が存在し、かつその値がURLな場合のみを付与することになります。もし条件にかかわらず属性を一律付与しなければならない場合は、JavaScript側でcite属性が存在しない場合やURNだった場合の除外判定を入れる必要があるでしょう。

ところでこのコードには「キーボード操作では独自コンテキストメニューが表示されない」という問題があります。Firefoxでは、F7キーを押すことによりキャレットブラウズモード(support.mozilla.org)になりますが、その状態で Shift + F10 キーなどでコンテキストメニューを表示しても、menuitem要素で定義した項目は現れません。この問題はBug 699709(bugzilla.mozilla.org)に上がっていますが、今のところ解決されていないようです。

Webサイト側でできる解決方法として、当該要素に tabindex="0" を付けることでキー操作においても独自コンテキストメニューを表示することができます。ただ、本来フォーカスさせる必要のない要素にtabindexを設定するのはちょっと不毛な感じもしますが…。

まあどちらにしても現状は Firefox でしか使えない機能ですし、JavaScriptを無効にしているユーザーや、コンテキストメニューを出せない・出しにくい環境のこと考えると、(menu APIのブラウザ実装が普及したとしても)従来どおりa要素によるリンクは残しておいた方が良いのかな、と思います。