Help us understand the problem. What is going on with this article?

chrome拡張機能で抽選機を作った

突然ですが私は極度の優柔不断です。
今日どのゲームするかを決めるのにも迷ってしまいます。
ということで前々から触ってみたかったchrome拡張機能で抽選王的なものを作りました。
と言っても今回は拡張機能APIを使う部分が無くてほぼ生のhtmlとjsだけ

出来たもの

https://github.com/engabesi/RandomPicker
tyusenk.gif

やること

  • chrome拡張機能セットアップ
  • popup作成

セットアップ

構成予定
RandomPicker/
├─ css/
│   └─ bulma.min.css
├─ html/
│   └─ popup.html
├─ icons/
│   ├─ icon16.png
│   ├─ icon48.png
│   └─ icon128.png
├─ js/
│   └─ popup.js
└─ manifest.json

セットアップと仰々しく書きましたが実際はjsonを一つ作るだけです。
chrome拡張機能のsettingsとなるmanifest.jsonを作ります。

manifest.json
{
  "manifest_version": 2,
  "version": "1.0",
  "name": "Random Picker",
  "description": "Pick a character string at random",
  "icons": {
    "16": "icons/icon16.png",
    "48": "icons/icon48.png",
    "128": "icons/icon128.png"
  },
  "browser_action": {
    "default_popup": "html/popup.html"
  }
}

manifest_version, version, nameは必須です。
また、今回は拡張機能のアイコンをクリックした際に出てくるポップアップ内で抽選ページを作るのでbrowser_actiondefault_popupにhtmlファイルを指定します。

詳細は公式ドキュメントや以下の方の記事が参考になります。
https://developer.chrome.com/extensions/manifest
https://qiita.com/mdstoy/items/9866544e37987337dc79

次にhtmlディレクトリを作成し、その中にpopup.htmlを作ります。

html\popup.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
  </head>
  <body>
    <h1>Hello World!</h1>
    <script src="../js/popup.js"></script>
  </body>
</html>

ここまでだけでもうポップアップだけですが拡張機能ができました。
次にchromeに導入します。
chrome://extensions/をURL欄に入力して拡張機能設定画面に飛びます。
右上のスイッチを切り替えてデベロッパーモードをONにします。
「パッケージ化されていない拡張機能を読み込む」をクリックしてRandomPickerディレクトリを指定します。
するとアドレスバーの右側にRのアイコンが出現します。(iconを設定済みであればそのicon)
それをクリックして以下のようにポップアップが出現したら無事拡張機能インストール完了です。
image.png

作る

ここからhtmlとjsを書いていきます。
拡張機能要素はほぼ無いです。
解説するようなものが無いのでコードを貼るだけにしておきます。

CSSフレームワーク導入

htmlを書く前に、cssを極力書きたくないのでフレームワークを導入します。
今回はbulmaを使用します。
npmやCDN等がありますが、オフライン且つなるべく容量を少なくしたかったのでminだけ引っ張ります。
以下からbulmaをDLし、bulma.min.cssをcssフォルダ内に入れてインポートします。
https://bulma.io/

html + js

popup.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <link rel="stylesheet" href="../css/bulma.min.css" />
    <style>
      body {
        width: 500px;
      }
    </style>
  </head>
  <body>
    <section class="section">
      <div class="container">
        <!-- header -->
        <h1 id="title" class="title">Random Picker</h1>
        <!-- add area -->
        <div class="columns is-mobile is-vcentered">
          <div class="column">
            <form name="addForm" class="field has-addons">
              <div class="control">
                <input
                  name="addInput"
                  class="input"
                  type="text"
                  placeholder="New Text"
                />
              </div>
              <div class="control">
                <button type="submit" class="button is-info" tabindex="-1">
                  ADD
                </button>
              </div>
            </form>
          </div>
        </div>
        <!-- pick area -->
        <div class="columns is-mobile is-vcentered">
          <div class="column is-7">
            <form name="pickForm" class="field has-addons">
              <div class="control">
                <input
                  name="pickInput"
                  class="input"
                  type="number"
                  placeholder="Count of Pick"
                  value="1"
                />
              </div>
              <div class="control">
                <button type="submit" class="button is-success" tabindex="-1">
                  Pick
                </button>
              </div>
            </form>
          </div>
          <div class="column">
            <button id="clearBtn" class="button is-danger" tabindex="-1">
              Clear
            </button>
          </div>
        </div>
        <div class="columns is-mobile is-multiline">
          <!-- pick list -->
          <div class="column">
            <div
              id="pickList"
              class="column field is-grouped is-grouped-multiline"
            ></div>
          </div>
          <!-- picked list -->
          <div class="column">
            <p class="subtitle is-6">抽選結果</p>
            <div id="pickedList"></div>
          </div>
        </div>
      </div>
    </section>
    <script src="../js/popup.js"></script>
  </body>
</html>
popup.js
const addForm = document.addForm;
const pickForm = document.pickForm;
const pickListItems = [];

/** LocalStorage */
const saveToLocalStorage = str => {
  if (!str) return;
  localStorage.setItem(str, str);
};

const deleteFromLocalStorage = str => {
  localStorage.removeItem(str);
};

/** pick list */
const pickList = document.getElementById("pickList");

const addString = event => {
  event.preventDefault();
  const inputStr = addForm.addInput.value.trim();
  if (inputStr === "") return;
  appendAddHtml(inputStr);
  addForm.reset();
};

const appendAddHtml = str => {
  const html = createAddHtml(str);
  pickList.innerHTML += html;
  pickListItems.push(str);
  saveToLocalStorage(str);
};

const createAddHtml = str => {
  return `
  <div class="control">
    <div class="tags has-addons">
      <a class="tag is-success del">${str}</a>
      <a class="tag is-delete del"></a>
    </div>
  </div>`;
};

const deleteString = event => {
  if (!event.target.classList.contains("del")) return;
  const parent = event.target.parentElement.parentElement;
  parent.remove();
  const str = parent.textContent.trim();
  pickListItems.forEach((item, index) => {
    if (item === str) pickListItems.splice(index, 1);
  });
  deleteFromLocalStorage(str);
};

const clearStr = () => {
  pickListItems.length = 0;
  pickList.innerHTML = "";
  localStorage.clear();
};

/** picked list */
const pickedList = document.getElementById("pickedList");

const createPickedListHtml = strs => {
  let pickedListHtml = `<div class="list">`;
  strs.forEach(str => (pickedListHtml += `<a class="list-item">${str}</a>`));
  pickedListHtml += "</div>";
  return pickedListHtml;
};

const randomPick = event => {
  event.preventDefault();
  let length = pickListItems.length;
  if (length <= 0) return;
  const pickedStrs = [];
  const pickListItemsCpy = pickListItems.concat();
  const pickInputVal = pickForm.pickInput.value;
  const pickCount = pickInputVal > length ? length : pickInputVal;
  for (let i = pickCount; i > 0; i--) {
    const randomIndex = Math.floor(Math.random() * length);
    pickedStrs.push(pickListItemsCpy[randomIndex]);
    pickListItemsCpy.splice(randomIndex, 1);
    length--;
  }
  pickedList.innerHTML = createPickedListHtml(pickedStrs);
};

addForm.addEventListener("submit", e => addString(e));
pickList.addEventListener("click", e => deleteString(e));
pickForm.addEventListener("submit", e => randomPick(e));
document.getElementById("clearBtn").addEventListener("click", _ => clearStr());

// init
(() => {
  addForm.addInput.focus();
  Object.keys(localStorage).forEach(key => {
    const str = localStorage.getItem(key);
    if (!str) return;
    appendAddHtml(str);
  });
})();

これで冒頭に貼った画像のものが完成します。

まとめ

久しぶりにhtmlとjsを生で書きましたが中々辛い。
各種フレームワーク、ライブラリの有難みを感じる開発でした。
また今回拡張機能API等、拡張機能らしいことをほぼしていないので今度はその辺を絡めたものを作りたい。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした