1
2

More than 5 years have passed since last update.

職人的なDOM生成コードをテンプレートエンジンに置き換える

Last updated at Posted at 2019-06-17

背景

稼働中のアプリケーションを変更するにあたって、職人的なDOM生成コードを読み取ることが困難なことがあります。
例えば、次のように職人的にHTML Document Objectを作り

function createElement(id, data) {
  const element = document.createElement('div')
  element.setAttribute('id', id)
  element.setAttribute('title', 'id: ' + data.id + ', pred: ' + data.pred + ', value: ' + data.value)
  element.setAttribute('origin-id', data.id)
  element.setAttribute('type', data.value)
  element.setAttribute('pred', data.pred)
  element.classList.add('textae-editor__attribute')
  element.innerText = data.value

  const popUpEditorElement = document.createElement('div')
  popUpEditorElement.classList.add('textae-editor__attribute-buttons')

  const editButtonElement = document.createElement('div')
  editButtonElement.classList.add('textae-editor__attribute-button')
  editButtonElement.classList.add('textae-editor__attribute-button--edit')
  editButtonElement.setAttribute('title', 'Edit this attribute.')

  const deleteButtonElement = document.createElement('div')
  deleteButtonElement.classList.add('textae-editor__attribute-button')
  deleteButtonElement.classList.add('textae-editor__attribute-button--delete')
  deleteButtonElement.setAttribute('title', 'Delete this attribute.')

  popUpEditorElement.appendChild(editButtonElement)
  popUpEditorElement.appendChild(deleteButtonElement)

  element.appendChild(popUpEditorElement)
}

jQuery.append()を使ってDOMを追加するソースコードがあるとします。

$(parent).append(createElement(id, data))

課題

このソースコードからレンダリングされるHTMLを直感的に読み取るのは難しいです。
DOMの構造を修正したいときに変更箇所の特定が困難です。
よくやる対応では、ソースコードを少し変えてレンダリングされる内容の変化を確認しながら、変更箇所を特定していきます。

対応できますが、変更頻度が高い時は手間が馬鹿になりません。

リファクタリング手法

テンプレートエンジンを導入し、ソースコード上にレンダリング後のHTMLのイメージをそのまま記述します。
ここではHandlebars.jsを使います。

手順1. Google Chromeの開発コンソールを使ってレンダリング後のHTMLを取得します。

Google Chromeで対象となるDOMを選択肢、コンテキストメニューから Copy > Copy outerHTML を選択します。

スクリーンショット 2019-06-17 12.26.13.png

次のHTMLが取得できます。

<div id="editor1__AA1" title="id: A1, pred: example_predicate_1, value: attr1" origin-id="A1" type="attr1" pred="example_predicate_1" class="textae-editor__attribute">attr1
  <div class="textae-editor__attribute-buttons">
    <div class="textae-editor__attribute-button textae-editor__attribute-button--edit" title="Edit this attribute."></div>
    <div class="textae-editor__attribute-button textae-editor__attribute-button--delete" title="Delete this attribute."></div>
  </div>
</div>

手順2. テンプレートで置き換える場所を設定する

Handlebars.jsでは{{}}で括られた文字列を指定の値で置き換えます。
JavaScriptではテンプレート文字列をつかうと、改行を含んだ文字列をソースコード中に記述できます。
次のようなソースコードになります。

const source = `
<div id="{{id}}" title="{{title}}" origin-id="{{originId}}" type="{{value}}" pred="{{pred}}" class="textae-editor__attribute">{{value}}
  <div class="textae-editor__attribute-buttons">
    <div class="textae-editor__attribute-button textae-editor__attribute-button--edit" title="Edit this attribute."></div>
    <div class="textae-editor__attribute-button textae-editor__attribute-button--delete" title="Delete this attribute."></div>
  </div>
</div>
`

手順3. HTMLを生成する

これらを組み合わせてHTMLを生成するコードを作成します。
例えば次のようなソースコードになります。

import Handlebars from 'handlebars'

const source = `
<div id="{{id}}" title="{{title}}" origin-id="{{originId}}" type="{{value}}" pred="{{pred}}" class="textae-editor__attribute">{{value}}
  <div class="textae-editor__attribute-buttons">
    <div class="textae-editor__attribute-button textae-editor__attribute-button--edit" title="Edit this attribute."></div>
    <div class="textae-editor__attribute-button textae-editor__attribute-button--delete" title="Delete this attribute."></div>
  </div>
</div>
`
const template = Handlebars.compile(source)

function createHtml(id, attribute) {
  return template({
    id,
    title: `id: ${attribute.id}, pred: ${attribute.pred}, value: ${attribute.value}`,
    originId: attribute.id,
    value: attribute.value,
    pred: attribute.pred
  })
}

この関数はcreateElement関数と、同等の内容を生成します。
違いは生成するものがDOM ElementかHTML文字列かの違いです。

手順4. createElementをcreateHtmlに置き換える

jQuery.appendは引数にHTML文字列を受け取れます。
呼び出す関数を置き換えるだけで対応が完了します。

$(parent).append(createHtml(id, data))

おまけ

jQueryを使わずにNode.appendChild()を使っている場合、例えば、次のソースコードの場合

parent.appendChild(createElement(id, data))

Node.appendChild()は引数にDOM要素しか受け付けません。
DOMの任意の場所にHTML文字列を挿入するにはelement.insertAdjacentHTMLを使います。

parent.insertAdjacentHTML('beforeend', createHtml(id, data))

参考

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2