アンチパターンから学ぶ ダイアログや通知バナーの実装

目次

はじめに

kintoneのカスタマイズでは、ユーザーに確認を求めるダイアログや、処理結果を知らせる通知バナーを実装するケースがあります。
こうしたダイアログや通知バナーの実装には、一見動作しているように見えても、リスクを抱える実装パターンが存在します。

この記事では、ダイアログや通知バナーの実装について、以下の2つを解説します。

  • アンチパターンとそのリスク
    たとえば、ブラウザー標準のダイアログやDOM操作で自作のUIを構築するカスタマイズは、kintoneのデザインとの不統一や保守性の低下、クロスブラウザー対応の困難さといったリスクがあります。
  • 推奨される実装方法とその利点
    ダイアログ・通知バナーのAPIを使用することで、kintone標準のデザインに統一されたフィードバックUIを実装したり、クロスブラウザー対応がしやすくなります。

アンチパターンと推奨される実装方法を、サンプルコードを交えて比較しながら紹介します。
既存のカスタマイズのリスク確認や、新たに実装する際の参考にしてください。

今回紹介するAPI

API 概要
kintone.showConfirmDialog() kintone標準デザインの確認ダイアログを表示
ユーザーにOK/キャンセル等の選択を求める用途向け
kintone.createDialog() kintone標準デザインのダイアログを作成
入力フィールドなど独自のHTML要素を配置できる
kintone.showNotification() kintone標準デザインの通知バナーを表示

1. ダイアログを表示する

kintoneのカスタマイズで、ユーザーに確認のダイアログを表示することがあります。
たとえば、レコードの削除や一括更新の前に「本当に実行しますか?」と確認を求めるケースです。

ここでは、「選択したレコードを更新しますか?」という確認ダイアログを表示する方法を比較します。

アンチパターン:window.confirm()によるブラウザー標準ダイアログの表示

window.confirm()を使用する方法は手軽ですが、いくつかの制約があります。

warning
注意

以下のコードはAPIを使った実装に置き換えることが推奨される書き方です。
新規のカスタマイズでは、後述するshowConfirmDialog()を使用してください。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 非推奨: この書き方は showConfirmDialog() を使った実装への置き換えを推奨します
(() => {
  'use strict';

  kintone.events.on('app.record.index.show', (event) => {
    const header = kintone.app.getHeaderMenuSpaceElement();
    if (!header) {
      return event;
    }

    const button = document.createElement('button');
    button.textContent = '一括更新';
    button.onclick = () => {
      const result = window.confirm('選択したレコードを更新しますか?');
      if (result) {
        // 更新処理
      }
    };
    header.appendChild(button);

    return event;
  });
})();

実装すると以下のようなブラウザー標準の確認ダイアログが表示されます。

このアプローチには以下のリスクがあります。

リスク 説明
kintone UIとの不統一 ブラウザー標準のダイアログはkintoneのデザインと一致せず、ユーザーに違和感を与えます。
カスタマイズ性の不足 タイトルの追加やボタンのラベル変更ができず、表示できる情報が限られます。
その結果、ユーザーに十分な情報を提示できないまま操作を求めるリスクがあります。
非同期フローとの不整合 window.confirm()は同期処理です。
async/awaitを使った非同期フローの中で呼び出すと、処理がブロックされた状態でダイアログが表示されます。
その間、他の非同期処理が意図しないタイミングで割り込む可能性があり、処理の順序が保証されないリスクがあります。
環境依存の表示差異 ダイアログの見た目や挙動はOSやブラウザーに依存するため、環境ごとに表示が異なります。
また、ブラウザーのセキュリティ対応を理由とした仕様変更により、将来的に挙動が変わるリスクがあります。

推奨される実装方法:showConfirmDialog()による確認ダイアログの表示

kintone.showConfirmDialog()を使用することで、kintone標準デザインの確認ダイアログを表示できます。
戻り値がPromiseのため、async/awaitで自然に非同期フローへ組み込めます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
(() => {
  'use strict';

  kintone.events.on('app.record.index.show', (event) => {
    const header = kintone.app.getHeaderMenuSpaceElement();
    if (!header) {
      return event;
    }

    const button = document.createElement('button');
    button.textContent = '一括更新';
    button.onclick = async () => {
      const result = await kintone.showConfirmDialog({
        title: 'レコードの一括更新',
        body: '選択したレコードを更新しますか?',
        okButtonText: '更新する',
        showCancelButton: true,
        cancelButtonText: 'キャンセル'
      });
      if (result === 'OK') {
        // 更新処理
      }
    };
    header.appendChild(button);

    return event;
  });
})();

実装すると以下のような確認ダイアログが表示されます。

window.confirm()と比較して、以下の点が改善されます。

改善点 説明
kintone UIとの統一 kintone標準のダイアログデザインが適用され、一貫した操作体験を提供できます。
カスタマイズ性の向上 タイトル、メッセージ、ボタンラベルを自由に設定できます。
非同期フローとの親和性 戻り値がPromiseのため、async/awaitで非同期処理の中に組み込めます。
window.confirm()のように処理をブロックすることなく、他の非同期処理との順序を保ちながらユーザーの選択結果を受け取れます。
環境に依存しない表示 kintoneが描画を管理するため、OSやブラウザーによる見た目の違いが生じません。
モバイル版ではkintone.mobile.showConfirmBottomSheet()で同様の確認ダイアログを表示できます。

showConfirmDialog()のAPIドキュメントは、以下を参照してください。

kintone.showConfirmDialog()

2. 入力フォーム付きのダイアログを作成する

kintoneのカスタマイズで、入力フォーム付きのダイアログを表示することがあります。
たとえば、レコード保存前に追加情報を入力させたり、一括操作の確認画面を表示したりするケースです。

ここでは、タイトルとメモの2つの入力欄をもつタスク登録ダイアログを表示する実装を紹介します。

アンチパターン:document.body操作による入力フォーム付きダイアログの作成

自前のHTMLダイアログを構築してdocument.body.appendChild()などでDOMに付与する方法では、オーバーレイの<div>作成やz-indexの調整など、ダイアログの見た目や動作を制御するためだけのコードが必要です。

warning
注意

以下のコードはAPIを使った実装に置き換えることが推奨される書き方です。
新規のカスタマイズでは、後述するcreateDialog()を使用してください。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// 非推奨: この書き方は createDialog() を使った実装への置き換えを推奨します
(() => {
  'use strict';

  kintone.events.on('app.record.index.show', (event) => {
    const header = kintone.app.getHeaderMenuSpaceElement();
    if (!header) {
      return event;
    }

    const button = document.createElement('button');
    button.textContent = 'タスクの簡易登録';
    button.onclick = () => {
      // オーバーレイの作成
      const overlay = document.createElement('div');
      overlay.style.cssText =
        'position:fixed;top:0;left:0;width:100%;height:100%;' +
        'background:rgba(0,0,0,0.5);z-index:10000;';

      // ダイアログ本体の作成
      const modal = document.createElement('div');
      modal.style.cssText =
        'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);' +
        'background:#fff;padding:24px;border-radius:8px;z-index:10001;';

      const label1 = document.createElement('div');
      label1.textContent = 'タイトル';
      label1.style.cssText = 'margin-bottom:4px;';
      const input = document.createElement('input');
      input.type = 'text';
      input.style.cssText = 'width:300px;margin-bottom:12px;';

      const label2 = document.createElement('div');
      label2.textContent = 'メモ';
      label2.style.cssText = 'margin-bottom:4px;';
      const textarea = document.createElement('textarea');
      textarea.style.cssText = 'width:300px;height:80px;';

      const buttonContainer = document.createElement('div');
      buttonContainer.style.cssText = 'margin-top:12px;text-align:right;';

      const cancelButton = document.createElement('button');
      cancelButton.textContent = 'キャンセル';
      cancelButton.onclick = () => document.body.removeChild(overlay);

      const okButton = document.createElement('button');
      okButton.textContent = 'OK';
      okButton.style.cssText = 'margin-left:8px;';
      okButton.onclick = () => document.body.removeChild(overlay);

      buttonContainer.append(cancelButton, okButton);
      modal.append(label1, input, label2, textarea, buttonContainer);
      overlay.appendChild(modal);
      document.body.appendChild(overlay);

      // Escキーで閉じる処理
      const onKeyDown = (e) => {
        if (e.key === 'Escape') {
          document.body.removeChild(overlay);
          document.removeEventListener('keydown', onKeyDown);
        }
      };
      document.addEventListener('keydown', onKeyDown);
    };
    header.appendChild(button);

    return event;
  });
})();

実装すると以下のようなダイアログが表示されます。

このアプローチには以下のリスクがあります。

リスク 説明
kintone UIとの不統一 自作のダイアログはkintoneのデザインと異なる見た目になり、ユーザーに違和感を与えます。
z-indexの競合 kintoneのUIコンポーネントとz-indexが競合し、ダイアログが隠れたり操作を妨げたりするリスクがあります。
保守コストの増大 オーバーレイ、位置調整、キーボード操作、フォーカス制御など、ダイアログの基本機能をすべて自前で実装・維持する必要があります。
コードが複雑化するにつれて、バグが混入するリスクが高まります。
環境依存の表示差異 自作ダイアログの見た目や挙動はOSやブラウザーに依存するため、環境ごとに表示が異なります。
また、モバイル版ではレイアウトが崩れるリスクがあり、画面ごとにコードを書き分ける必要が生じます。

推奨される実装方法:createDialog()による入力フォーム付きダイアログの作成

kintone.createDialog()を使用することで、kintone標準デザインのダイアログを作成できます。
ヘッダー・ボディ・フッターの構成はAPIで管理され、HTML要素をボディに挿入するリッチなダイアログも構築できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
(() => {
  'use strict';

  kintone.events.on('app.record.index.show', (event) => {
    const header = kintone.app.getHeaderMenuSpaceElement();
    if (!header) {
      return event;
    }

    const button = document.createElement('button');
    button.textContent = 'タスクの簡易登録';
    button.onclick = async () => {
      const container = document.createElement('div');
      container.style.cssText = 'padding:16px;';

      const label1 = document.createElement('div');
      label1.textContent = 'タイトル';
      const input = document.createElement('input');
      input.type = 'text';
      input.style.cssText = 'width:100%;margin-bottom:8px;';

      const label2 = document.createElement('div');
      label2.textContent = 'メモ';
      const textarea = document.createElement('textarea');
      textarea.style.cssText = 'width:100%;height:80px;';

      container.append(label1, input, label2, textarea);

      const dialog = await kintone.createDialog({
        title: 'タスクの簡易登録',
        body: container,
        showCancelButton: true
      });
      await dialog.show();
    };
    header.appendChild(button);

    return event;
  });
})();

実装すると以下のようなダイアログが表示されます。

DOM操作による自作ダイアログと比較して、以下の点が改善されます。

改善点 説明
kintone UIとの統一 kintone標準のダイアログデザインが適用されるため、ユーザーに一貫した操作体験を提供できます。
z-indexの競合を回避 kintoneがダイアログの表示レイヤーを適切に管理するため、UIコンポーネントとの競合が起きません。
保守コストの削減 オーバーレイやキーボード操作の制御はAPIが提供するため、ダイアログの内容に集中できます。
環境に依存しない表示 kintoneが描画を管理するため、OSやブラウザーによる見た目の違いが生じません。
モバイル版ではkintone.mobile.createBottomSheet()で同じ構造のダイアログを作成できます。

createDialog()のAPIドキュメントは、以下を参照してください。

kintone.createDialog()

3. 通知バナーを表示する

kintoneのカスタマイズで、通知バナーを表示することがあります。
たとえば、処理の完了メッセージや入力エラーの警告がそれに該当します。

ここでは、「レコードを更新しました」という通知バナーを表示する方法を、2つの実装パターンで比較します。

アンチパターン:DOM操作による通知バナーの表示

通知バナーを表示する方法として、DOMに自作のバナー要素を追加するパターンがあります。
以下のコードは、DOMで自作のバナーを作成し、画面上部に表示する例です。

warning
注意

以下のコードはAPIを使った実装に置き換えることが推奨される書き方です。
新規のカスタマイズでは、後述するshowNotification()を使用してください。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 非推奨: この書き方は showNotification() を使った実装への置き換えを推奨します
(() => {
  'use strict';

  function showCustomBanner(message, type) {
    const banner = document.createElement('div');
    banner.textContent = message;
    banner.style.cssText =
      'position:fixed;top:0;left:0;width:100%;padding:12px;' +
      'text-align:center;z-index:10000;color:#fff;' +
      `background:${type === 'error' ? '#d32f2f' : '#388e3c'};`;

    document.body.appendChild(banner);

    // 自動消去の実装
    setTimeout(() => {
      if (banner.parentNode) {
        banner.parentNode.removeChild(banner);
      }
    }, 3000);
  }

  kintone.events.on('app.record.edit.submit.success', (event) => {
    showCustomBanner('レコードを更新しました', 'success');
    return event;
  });
})();

実装すると以下のようなバナーが画面上部に表示されます。

このアプローチには以下のリスクがあります。

リスク 説明
kintone UIとの不統一 自作のバナーはkintoneの通知バナーデザインと異なり、ユーザーに違和感を与えます。
複数通知バナーの管理が煩雑 複数のカスタマイズが同時に通知バナーを表示する場合、バナーの重なりや表示位置の調整を自前で制御する必要があります。
管理が煩雑になるにつれて、通知バナーが正しく表示されないリスクが高まります。
自動消去のタイミング管理 setTimeoutによる消去タイミングの制御や、ユーザーが通知バナーを読む前に消えてしまうリスクの対応が必要です。
環境依存の表示差異 自作バナーの見た目はOSやブラウザーに依存するため、環境ごとに表示が異なります。
モバイル版ではPC版と表示位置やサイズが異なるため、モバイル版用のバナーを別途用意する必要があります。

推奨される実装方法:showNotification()による通知バナー表示

kintone.showNotification()を使用することで、kintone標準デザインの通知バナーを表示できます。
成功・エラー・情報などのタイプに応じて色分けされたバナーが表示されます。

1
2
3
4
5
6
7
8
(() => {
  'use strict';

  kintone.events.on('app.record.edit.submit.success', (event) => {
    kintone.showNotification('SUCCESS', 'レコードを更新しました');
    return event;
  });
})();

実装すると以下のような通知バナーが表示されます。

エラー通知バナーの場合は、type'ERROR'を指定します。

1
kintone.showNotification('ERROR', '更新に失敗しました。入力内容を確認してください。');

実装すると以下のように表示されます。

DOM操作による自作バナーと比較して、以下の点が改善されます。

改善点 説明
kintone UIとの統一 kintone標準の通知バナーデザインが適用され、一貫した操作体験を提供できます。
複数通知バナーの適切な管理 kintoneが通知バナーの表示位置や重なりを自動的に管理します。
消去処理が不要 通知バナーには閉じるボタンが付いており、閉じるまで消えない仕様です。
自前でのsetTimeout管理や閉じるボタンの付与が不要です。
環境に依存しない表示 kintoneが描画を管理するため、OSやブラウザーによる見た目の違いが生じません。
モバイル版ではkintone.mobile.showNotification()で同様の通知バナーを表示できます。

showNotification()のAPIドキュメントは、以下を参照してください。

kintone.showNotification()

実装時に押さえておきたいこと

各APIの非同期処理の扱い、使い分け、モバイル対応について案内します。
事前に把握しておくことで、実装後のトラブルを未然に防げます。

非同期処理の活用について

showConfirmDialog()createDialog()は非同期なAPIです。
async/awaitを使用して処理してください。

window.confirm()は同期処理ですが、showConfirmDialog()は非同期処理です。
呼び出し元をasync関数に変更し、awaitで戻り値を受け取る必要があります。

詳しくは次のページを参照してください。

APIの使い分けについて

通知バナー目的のDOM操作はshowNotification()に置き換えられます。
ユーザーの確認を待って処理を分岐する用途には、showConfirmDialog()の利用を推奨します。

入力フィールドなど独自のHTML要素を配置したダイアログが必要な場合は、createDialog()を使用します。
ただし、業務要件としてカスタムデザインが必要な場合は、リスクを理解したうえで自作ダイアログを維持する判断も考えられます。

モバイル版での利用について

この記事で紹介したAPIは、モバイル版でも利用できます。
モバイル版ではkintone.mobile名前空間のAPIを使用してください。

PC版 モバイル版
kintone.showConfirmDialog() kintone.mobile.showConfirmBottomSheet()
kintone.createDialog() kintone.mobile.createBottomSheet()
kintone.showNotification() kintone.mobile.showNotification()

まとめ

  • showConfirmDialog()で、window.confirm()に代わる確認ダイアログを非同期処理として組み込めます。
  • createDialog()で、DOM操作に頼らずkintone標準デザインのダイアログを作成できます。
  • showNotification()で、処理結果の通知バナーをkintone標準のデザインで表示できます。

ダイアログ・通知バナーのAPIを使用することで、kintoneカスタマイズにおけるユーザーフィードバックの信頼性が向上します。
新規のカスタマイズでは積極的にこれらのAPIを活用してください。

showConfirmDialog()createDialog()の使い分けについては、以下の記事も参考にしてください。

createDialogとshowConfirmDialogの使い分け

information

このTipsは、2026年4月版kintoneで動作を確認しています。