このページの内容

4.6HTMLブロック

HTMLブロックとは、生のHTMLとして扱われる行のまとまりです。HTML出力ではエスケープされません。

HTMLブロックには7種類あり、それぞれの開始条件と終了条件で定義できます。ブロックは、任意の最大3個のスペースによる字下げの後に、開始条件を満たす行で始まります。その種類に対応する終了条件を満たす最初の後続行で終わります。該当する行がなければ、文書の最終行、または現在のHTMLブロックを含むコンテナブロックの最終行で終わります。これは、終了条件を満たす行に出会わない場合の扱いです。最初の行が開始条件と終了条件の両方を満たす場合、そのブロックにはその1行だけが含まれます。

  1. 開始条件: 行が文字列<pre、<script、<style、または<textareaで始まり、大文字と小文字は区別しません。その後にスペース、タブ、文字列>、または行末が続きます。
    終了条件: 行に終了タグ</pre>、</script>、</style>、または</textarea>が含まれます。大文字と小文字は区別せず、開始タグと一致する必要もありません。

  2. 開始条件: 行が文字列<!--で始まります。
    終了条件: 行に文字列-->が含まれます。

  3. 開始条件: 行が文字列<?で始まります。
    終了条件: 行に文字列?>が含まれます。

  4. 開始条件: 行が文字列<!で始まり、その後にASCIIの英字が続きます。
    終了条件: 行に文字>が含まれます。

  5. 開始条件: 行が文字列<![CDATA[で始まります。
    終了条件: 行に文字列]]>が含まれます。

  6. 開始条件: 行が文字列<または</で始まり、その後に次の文字列のいずれかが続きます。大文字と小文字は区別しません。address、article、aside、base、basefont、blockquote、body、caption、center、col、colgroup、dd、details、dialog、dir、div、dl、dt、fieldset、figcaption、figure、footer、form、frame、frameset、h1、h2、h3、h4、h5、h6、head、header、hr、html、iframe、legend、li、link、main、menu、menuitem、nav、noframes、ol、optgroup、option、p、param、search、section、summary、table、tbody、td、tfoot、th、thead、title、tr、track、ul。さらに、スペース、タブ、行末、文字列>、または文字列/>が続きます。
    終了条件: 行の後に空行が続きます。

  7. 開始条件: 行が完全な開始タグ(タグ名はpre、script、style、textarea以外の任意のもの)または完全な終了タグで始まり、その後に0個以上のスペースやタブ、さらに行末が続きます。
    終了条件: 行の後に空行が続きます。

HTMLブロックは、対応する終了条件、文書の最終行、またはそれを含む別のコンテナブロックの最終行によって閉じられるまで続きます。したがって、通常なら開始条件として認識されうるHTMLでも、HTMLブロックの内部では、パーサーの状態を変えることなく無視され、そのまま出力に渡されます。

たとえば、<pre>が<table>で始まったHTMLブロックの内部にあっても、パーサーの状態には影響しません。このHTMLブロックは開始条件6で始まっているため、空行で終わります。意外に感じられることもあります。

<table><tr><td>
<pre>
**Hello**,

_world_.
</pre>
</td></tr></table>
<table><tr><td>
<pre>
**Hello**,
<p><em>world</em>.
</pre></p>
</td></tr></table>

この場合、HTMLブロックは空行で終わります。**Hello**のテキストはそのまま残り、通常の解析が再開されます。その結果、後ろには段落、強調されたworld、インラインとブロックのHTMLが続きます。

種類7以外のすべてのHTMLブロックは、段落を中断できます。種類7のブロックは段落を中断できません。この制限は、行を折り返した段落の内部にある長いタグが、意図せずHTMLブロックの開始と解釈されることを防ぐためです。

以下に単純な例を示します。まず、種類6の基本的なHTMLブロックです。

<table>
  <tr>
    <td>
           hi
    </td>
  </tr>
</table>

okay.
<table>
  <tr>
    <td>
           hi
    </td>
  </tr>
</table>
<p>okay.</p>
 <div>
  *hello*
         <foo><a>
 <div>
  *hello*
         <foo><a>

ブロックは終了タグで始めることもできます。

</div>
*foo*
</div>
*foo*

次の例には、Markdownの段落を間にはさんだ2つのHTMLブロックがあります。

<DIV CLASS="foo">

*Markdown*

</DIV>
<DIV CLASS="foo">
<p><em>Markdown</em></p>
</DIV>

最初の行のタグは、本来空白が入る位置で分割されていれば、途中まででもかまいません。

<div id="foo"
  class="bar">
</div>
<div id="foo"
  class="bar">
</div>
<div id="foo" class="bar
  baz">
</div>
<div id="foo" class="bar
  baz">
</div>

開始タグを閉じる必要はありません。

<div>
*foo*

*bar*
<div>
*foo*
<p><em>bar</em></p>

途中までのタグは、最後まで完成させる必要すらありません。不正な入力は、不正なまま出力されます。

<div id="foo"
*hi*
<div id="foo"
*hi*
<div class
foo
<div class
foo

最初のタグは、タグらしく始まっていれば、有効なタグである必要すらありません。

<div *???-&&&-<---
*foo*
<div *???-&&&-<---
*foo*

種類6のブロックでは、最初のタグだけで1行を占める必要はありません。

<div><a href="bar">*foo*</a></div>
<div><a href="bar">*foo*</a></div>
<table><tr><td>
foo
</td></tr></table>
<table><tr><td>
foo
</td></tr></table>

次の空行または文書の末尾までのすべてが、HTMLブロックに含まれます。そのため、次の例でMarkdownのコードブロックに見えるものは、実際にはHTMLブロックの一部です。HTMLブロックは、空行または文書の末尾に達するまで続きます。

<div></div>
``` c
int x = 33;
```
<div></div>
``` c
int x = 33;
```

(6)のブロックレベルタグ一覧にないタグでHTMLブロックを始めるには、最初の行にそのタグだけを置かなければなりません。また、タグは完全でなければなりません。

<a href="foo">
*bar*
</a>
<a href="foo">
*bar*
</a>

種類7のブロックでは、タグ名は任意です。

<Warning>
*bar*
</Warning>
<Warning>
*bar*
</Warning>
<i class="foo">
*bar*
</i>
<i class="foo">
*bar*
</i>
</ins>
*bar*
</ins>
*bar*

これらの規則は、ブロックレベルにもインラインレベルにもなれるタグを扱えるように設計されています。<del>タグはよい例です。内容を<del>タグで囲む方法は3通りあります。この場合、<del>タグだけで1行を占めているため、生のHTMLブロックになります。

<del>
*foo*
</del>
<del>
*foo*
</del>

この場合、直後の空行で終わるため、<del>タグだけを含む生のHTMLブロックになります。そのため、内容はCommonMarkとして解釈されます。

<del>

*foo*

</del>
<del>
<p><em>foo</em></p>
</del>

最後に、この場合は、<del>タグがCommonMarkの段落の内部の生のHTMLとして解釈されます。タグだけで1行を占めていないため、HTMLブロックではなくインラインHTMLになります。

<del>*foo*</del>
<p><del><em>foo</em></del></p>

文字どおりの内容を含めるためのHTMLタグ(pre、script、style、textarea)、コメント、処理命令、宣言は、少し異なる方法で扱います。最初の空行で終わるのではなく、対応する終了タグを含む最初の行で終わります。このため、これらのブロックには空行を含められます。

preタグ(種類1)の例です。

<pre language="haskell"><code>
import Text.HTML.TagSoup

main :: IO ()
main = print $ parseTags tags
</code></pre>
okay
<pre language="haskell"><code>
import Text.HTML.TagSoup

main :: IO ()
main = print $ parseTags tags
</code></pre>
<p>okay</p>

scriptタグ(種類1)の例です。

<script type="text/javascript">
// JavaScript example

document.getElementById("demo").innerHTML = "Hello JavaScript!";
</script>
okay
<script type="text/javascript">
// JavaScript example

document.getElementById("demo").innerHTML = "Hello JavaScript!";
</script>
<p>okay</p>

textareaタグ(種類1)の例です。

<textarea>

*foo*

_bar_

</textarea>
<textarea>

*foo*

_bar_

</textarea>

styleタグ(種類1)の例です。

<style
  type="text/css">
h1 {color:red;}

p {color:blue;}
</style>
okay
<style
  type="text/css">
h1 {color:red;}

p {color:blue;}
</style>
<p>okay</p>

対応する終了タグがなければ、ブロックは文書の末尾、またはそれを含むブロック引用やリスト項目の末尾で終わります。

<style
  type="text/css">

foo
<style
  type="text/css">

foo
> <div>
> foo

bar
<blockquote>
<div>
foo
</blockquote>
<p>bar</p>
- <div>
- foo
<ul>
<li>
<div>
</li>
<li>foo</li>
</ul>

終了タグは、開始タグと同じ行にあってもかまいません。

<style>p{color:red;}</style>
*foo*
<style>p{color:red;}</style>
<p><em>foo</em></p>
<!-- foo -->*bar*
*baz*
<!-- foo -->*bar*
<p><em>baz</em></p>

最終行で終了タグの後にあるものは、すべてHTMLブロックに含まれる点に注意してください。

<script>
foo
</script>1. *bar*
<script>
foo
</script>1. *bar*

コメント(種類2)の例です。

<!-- Foo

bar
   baz -->
okay
<!-- Foo

bar
   baz -->
<p>okay</p>

処理命令(種類3)の例です。

<?php

  echo '>';

?>
okay
<?php

  echo '>';

?>
<p>okay</p>

宣言(種類4)の例です。

<!DOCTYPE html>
<!DOCTYPE html>

CDATA(種類5)の例です。

<![CDATA[
function matchwo(a,b)
{
  if (a < b && a < 0) then {
    return 1;

  } else {

    return 0;
  }
}
]]>
okay
<![CDATA[
function matchwo(a,b)
{
  if (a < b && a < 0) then {
    return 1;

  } else {

    return 0;
  }
}
]]>
<p>okay</p>

開始タグの前には、最大3個のスペースによる字下げを置けますが、4個は許されません。

  <!-- foo -->

    <!-- foo -->
  <!-- foo -->
<pre><code>&lt;!-- foo --&gt;
</code></pre>
  <div>

    <div>
  <div>
<pre><code>&lt;div&gt;
</code></pre>

種類1〜6のHTMLブロックは段落を中断でき、前に空行を置く必要はありません。

Foo
<div>
bar
</div>
<p>Foo</p>
<div>
bar
</div>

ただし、文書の末尾の場合と、上で示した種類1〜5のブロックの場合を除き、後ろには空行が必要です。

<div>
bar
</div>
*foo*
<div>
bar
</div>
*foo*

種類7のHTMLブロックは、段落を中断できません。

Foo
<a href="bar">
baz
<p>Foo
<a href="bar">
baz</p>

この規則は、John Gruberの元のMarkdown構文仕様とは異なります。そこでは次のように述べられています。

制限は、<div>、<table>、<pre>、<p>などのブロックレベルのHTML要素を、周囲の内容と空行で区切ることと、ブロックの開始タグと終了タグをスペースやタブで字下げしないことだけです。

Gruberの規則は、いくつかの点で、ここに示した規則より厳しくなっています。

  • HTMLブロックの前に空行を要求します。
  • 開始タグの字下げを許しません。
  • 対応する終了タグを要求し、その終了タグの字下げも許しません。

ほとんどのMarkdown実装は、Gruber自身による実装の一部も含めて、これらの制限をすべて守っているわけではありません。

ただし、Gruberの規則は、HTMLブロックの内部に空行を置けるという点で、ここに示した規則より寛容です。ここでそれを許さない理由は2つあります。まず、対応の取れたタグを解析する必要がなくなります。この解析は高コストで、対応する終了タグが見つからない場合は、文書の末尾から後戻りして解析しなければならないことがあります。次に、HTMLタグの内部にMarkdown内容を入れる、とても単純で柔軟な方法を提供できます。MarkdownとHTMLを空行で区切るだけです。

次の例と比較してください。

<div>

*Emphasized* text.

</div>
<div>
<p><em>Emphasized</em> text.</p>
</div>
<div>
*Emphasized* text.
</div>
<div>
*Emphasized* text.
</div>

Markdown実装の中には、開始タグにmarkdown=1属性がある場合、タグの内部の内容をテキストとして解釈する慣習を採用したものがあります。上の規則は、同じ表現力を得るための、より単純で洗練された方法に思われます。解析もはるかに簡単です。

考えられる主な欠点は、HTMLブロックをMarkdown文書に貼り付けても、100%確実には処理できなくなることです。しかし、ほとんどの場合は問題なく動きます。HTMLの空行の後には通常、HTMLブロックのタグが続くためです。たとえば次のようになります。

<table>

<tr>

<td>
Hi
</td>

</tr>

</table>
<table>
<tr>
<td>
Hi
</td>
</tr>
</table>

ただし、内部のタグが字下げされ、さらに空白で区切られている場合は問題が生じます。その場合、字下げによるコードブロックとして解釈されるためです。

<table>

  <tr>

    <td>
      Hi
    </td>

  </tr>

</table>
<table>
  <tr>
<pre><code>&lt;td&gt;
  Hi
&lt;/td&gt;
</code></pre>
  </tr>
</table>

幸い、空行は通常は不要で、削除できます。例外は<pre>タグの内部ですが、上で説明したとおり、<pre>で始まる生のHTMLブロックには空行を含められます。