シンプルで効率的なタスク管理ができるライフハックの一つに「アイビー・リー・メソッド」があります。20世紀に発明されたこのタスク管理の手法は現代でも業務効率化に十分役立ちます。そこで、これを実践する便利なツールをJavaScriptで作ってみましょう。

  • アイビー・リー・メソッドの実践に便利なタスク管理ツールを作ってみよう

    アイビー・リー・メソッドの実践に便利なタスク管理ツールを作ってみよう

アイビー・リー・メソッドとは?

そもそも「アイビー・リー・メソッド」とは、20世紀前半に活躍したアメリカの広報コンサルタントのアイビー・リーが提唱したライフハックです。実業家で鉄鋼王のチャールズ・M・シュワブは、このメソッドを実践した結果、業務効率が大きく向上しました。彼は、このメソッドを「これまで受け入れたアドバイスの中で最も有益だった」と評価し、高額な報酬をリーに払ったと伝えられています。

これは、「前日夜に達成したい事項を6個書き出し重要度順に並び替えて、翌日そのタスクを順番に一つずつ取り組んでいく」というシンプルなものです。具体的には、次の手順で行います。

(1) 毎晩、次の日にやるべきことを6つまで書き出す
(2) 6つのタスクを重要度や優先度の高いものから並べ替える
(3) 翌日はリストの一番上のタスクから順に始める
(4) タスクが完了しなかったものは、翌日のリストに繰り越す
(5) その日の夜、再びタスクリストを作る(そして、1に戻る)

このように、とても単純です。なお、前日に翌日のタスクを決めることや、6つまでに絞ること、順番通りにやること、複数のタスクを同時にやらないことがポイントです。

JavaScriptで作ってみよう

それでは、上記の点を実践できるタスクリストを作ってみましょう。せっかく、アイビー・リー・メソッドの実践を視野に入れて設計するのですから、次の機能を重点的に作成しましょう。

  • リストの順番を簡単に入れ替え可能であること
  • 6個を超えたら警告のため赤字になること
  • ブラウザだけで手軽に使えること

特に、リストの順番を手軽に入れ替えができるという点に注力しましょう。単に上下を変えるだけでなく、マウスのドラッグ&ドロップを利用して、順番を自由に差し替えられるように工夫してみます。

なお、今回作成したプログラムは、こちらのGistにアップロードしています。実際にプログラムを実行する際にご利用ください。

基本的なHTMLを書いてみよう

最初に、必要となる部品をHTMLに配置してみましょう。以下のHTMLコードを「tasklist.html」という名前で保存しましょう。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>アイビーリーメソッド</title>
    <link rel="stylesheet" href="tasklist.css">
</head>
<body>
    <h1>アイビーリーメソッド</h1>
    <div class="input-row">
      <input type="text" id="taskInput" class="task-input" placeholder="タスクを入力(最大6個)" maxlength="100">
      <button id="addTaskBtn" class="btn">追加</button>
    </div>
    <ul class="task-list" id="taskList"></ul>
    <div>タスクはドラッグ&ドロップで並べ替えできます</div>
    <script src="tasklist.js"></script>
</body>
</html>

このHTMLは、必要最低限の基本パーツを配置したシンプルな構成です。タスクの入力ボックス(taskInput)やリストの表示欄(taskList)を記述しました。HTMLはすぐに複雑になりがちなので、常に簡潔さを意識しましょう。

JavaScriptを記述しよう

次に、HTMLに動きを付けるため、JavaScript「tasklist.js」を記述しましょう。以下のプログラムは「tasklist.js」からの抜粋です。全体は、こちらで確認してください。

// タスクを管理するクラスを定義
class SimpleTaskManager {
    constructor() { // コンストラクタ --- (*1)
        this.tasks = this.loadTasks();
        this.initEvents();
        this.update();
    }
    loadTasks() { // localStorageからタスクを読み込む --- (*2)
        const saved = localStorage.getItem('ivy_tasks');
        return saved ? JSON.parse(saved) : [];
    }
    initEvents() { // イベントリスナーを初期化 --- (*3)
        document.getElementById('addTaskBtn').onclick = () => this.addTask();
        document.getElementById('taskInput').onkeypress = (e) => {
            if (e.key === 'Enter') this.addTask();
        };
    }
    saveTasks() { // タスクをlocalStorageに保存 --- (*4)
        localStorage.setItem('ivy_tasks', JSON.stringify(this.tasks));
    }
    addTask() { // タスクの追加 --- (*5)
        const input = document.getElementById('taskInput');
        const text = input.value.trim();
        if (!text) return;
        this.tasks.push({
            id: Date.now(),
            text: text,
            completed: false
        });
        input.value = '';
        this.saveTasks();
        this.update();
    }
    …
    const tm = new SimpleTaskManager();
}

プログラムを確認してみましょう。このプログラムは、タスクを管理するクラスSimpleTaskManagerを定義したものです。

(*1)では、クラスのコンストラクタを定義するのですが、初期化処理をまとめて行います。loadTaskメソッドで保存済みのタスクを読み込み、initEventsメソッドでボタンやキーボード入力のイベントリスナーを登録し、updateメソッドで画面を更新します。

(*2)では、ブラウザのlocalStorageに保存されているタスクデータ(JSON文字列)を読み取り、オブジェクトに変換して返します。

(*3)では、タスク追加用のボタンの処理と、入力ボックスでEnterキーを押した場合にタスクを追加するaddTaskメソッドを登録します。

(*4)では、タスク一覧をsaveTasksメソッドでブラウザのlocalStorageに保存します。その際、オブジェクトをJSON形式に変換します。

(*5)では、ユーザーがタスクを入力して追加ボタンを押した時に実行するメソッドaddTaskを定義します。入力ボックス(taskInput)から値を取り出して、Array型のtasksにタスクを追加します。その後、保存と画面更新を行います。

なお、タスク一覧を更新するupdateメソッドと、タスクをドラッグ&ドロップで並び替える処理は、次のようになっています。(*5)の下で定義しています。

class SimpleTaskManager {
    …
    update() { // 画面を更新する --- (*6)
        // タスク一覧を一度空にする
        const list = document.getElementById('taskList');
        list.innerHTML = '';
        …
        // タスクをliに変換して#taskListに追加
        this.tasks.forEach((task, index) => {
            const li = document.createElement('li');
            …
            li.setAttribute('draggable', 'true');
            li.dataset.index = index;
            // ドラッグ&ドロップのイベントを追加 --- (*7)
            li.addEventListener('dragstart', this.handleDragStart.bind(this));
            li.addEventListener('dragover', this.handleDragOver.bind(this));
            li.addEventListener('dragleave', this.handleDragLeave.bind(this));
            li.addEventListener('drop', this.handleDrop.bind(this));
            li.addEventListener('dragend', this.handleDragEnd.bind(this));
            …
            list.appendChild(li);
        });
    }
    // ドラッグを開始したときの処理 --- (*8)
    handleDragStart(e) {
        e.dataTransfer.effectAllowed = 'move';
        e.dataTransfer.setData('text/plain', e.target.dataset.index);
        e.target.classList.add('dragging');
    }
    // ドラッグ中の要素を挿入位置に合わせて移動する処理 --- (*9)
    handleDragOver(e) { … }
    …
    // ドロップ時の処理 --- (*10)
    handleDrop(e) {
        e.preventDefault();
        const li = e.target.closest('li');
        if (!li) return;
        const fromIndex = Number(e.dataTransfer.getData('text/plain'));
        const toIndex = Number(li.dataset.index);
        // 挿入位置判定
        const rect = li.getBoundingClientRect();
        const offset = e.clientY - rect.top;
        let insertAt = toIndex;
        if (offset >= rect.height / 2) insertAt++;
        if (insertAt > fromIndex) insertAt--;
        if (fromIndex === insertAt) {
            this.handleDragEnd();
            return;
        }
        // 位置を入れ替えて保存して画面更新
        const moved = this.tasks.splice(fromIndex, 1)[0];
        this.tasks.splice(insertAt, 0, moved);
        this.saveTasks();
        this.update();
    }
    // ドラッグの終了処理 --- (*10)
    handleDragEnd(e) { … }
}

プログラムを確認してみましょう。

(*6)ですが、これは、タスクリストを再描画する関数です。既存のDOMをすべてクリアし、現在のタスク一覧(this.tasks)に基づいて、li要素を再構築します。各タスクごとに、li要素を作成します。

その際、(*7)でドラッグ&ドロップ操作のイベントを登録します。イベントの登録時に「.bind(this)」を指定することによってクラスのインスタンスを正しく参照するようにできます。

(*8)以降では、ドラッグ&ドロップの処理を記述します。(*8)では、ドラッグ開始時の処理を記述します。(*9)では、マウス位置に応じて、挿入位置の候補のガイドを表示します。視覚的に「どこにドロップされるか」が分かるように drop-before や drop-after クラスを付け外しします。

(*10)では、タスクをドラッグしたあと、ドロップされた位置に応じてリスト内の並びを変更します。要素の移動は、spliceメソッドを使います。その後、変更を保存して、画面を再描画します。

アプリを実行しよう

それでは、アプリを実行してみましょう。CSSファイルも必要となりますが、ここでは省略します。CSSファイルは、こちらからダウンロードして「tasklist.css」という名前で保存しましょう。

そして、「tasklist.html」をブラウザにドラッグ&ドロップします。すると、プログラムが実行できます。画面上部にタスクを入力して「追加」ボタンを押すと、一覧にタスクが追加されます。

  • ブラウザにドラッグ&ドロップして実行したとこ

    ブラウザにドラッグ&ドロップして実行したところ

まとめ

以上、今回は、アイビー・リー・メソッドを実践するのにぴったりの自作ツールを作ってみました。今回は、重要度順に並び替えるという機能を中心にしたツールに仕上げてみました。このように、コンセプトがはっきりした自作ツールを作る際には、どんな機能を中心にしたいのかを、具体的にイメージしてから開発に取りかかると良いでしょう。

また、アイビー・リー・メソッドは、シンプルで実践も容易なので挑戦してみると良いでしょう。

自由型プログラマー。くじらはんどにて、プログラミングの楽しさを伝える活動をしている。代表作に、日本語プログラミング言語「なでしこ」 、テキスト音楽「サクラ」など。2001年オンラインソフト大賞入賞、2004年度未踏ユース スーパークリエータ認定、2010年 OSS貢献者章受賞。技術書も多く執筆している。直近では、「実践力をアップする Pythonによるアルゴリズムの教科書(マイナビ出版)」「シゴトがはかどる Python自動処理の教科書(マイナビ出版)」「すぐに使える!業務で実践できる! PythonによるAI・機械学習・深層学習アプリのつくり方 TensorFlow2対応(ソシム)」「マンガでざっくり学ぶPython(マイナビ出版)」など。