複数のブックマークレットを合体（結合）させる


## TL;DR

手軽にブックマークレットを結合するブックマークレットを用意しました。
世の中の不毛不条理なタスクに追われる非開発者の方々のお役に立てば幸いです🙆‍♀️

```javascript
javascript:((e,t)=>{"use strict";e.CONCAT_BOOKMARKLETS?t.body.append(e.CONCAT_BOOKMARKLETS):(console.group("Concat bookmarklets 1.0.0 2018/12/27: How to use."),console.log("表示されるパネルの中に結合したいブックマークレットを追加していきます。"),console.log("結合ボタンを押下することで、対象のブックマークレットを結合することができます。"),console.log("結合したブックマークレットの実行内容は、結合したブックマークレットの実行後に`CONCAT_BOOKMARKLETS.executed`から確認することができます。"),console.log("GitHub： https://github.com/Soten-Bluesky/bookmarklets/tree/master/複数のブックマークレットを結合する.js"),e.CONCAT_BOOKMARKLETS=t.createElement("concat-bookmarklets"),(()=>{const o=e.CONCAT_BOOKMARKLETS,a=o.attachShadow({mode:"closed"}),n=(()=>{const e=t.createDocumentFragment(),n=t.createElement("style"),r=t.createElement("button"),l=t.createElement("button"),i=t.createElement("div"),s=t.createElement("input"),c=t.createElement("span"),p={close:()=>{o.remove()},getFormSet:function(){const e=t.createDocumentFragment(),o=t.createElement("div"),a=t.createElement("input"),n=t.createElement("input"),r=t.createElement("button"),l=t.createElement("button");return a.placeholder="タスク名",a.setAttribute("aria-label","タスク名"),n.placeholder="javascript:",n.setAttribute("aria-label","結合したいブックマークレットのJavaScriptコード"),o.className="item",r.type="button",r.className="plus",r.innerHTML="<span>追加</span>",r.addEventListener("click",this.add),l.type="button",l.className="delete",l.innerHTML="<span>削除</span>",l.addEventListener("click",this.remove),o.append(a),o.append(n),o.append(r),o.append(l),e.append(o),e},add:function(){const e=p.getFormSet();this.parentNode.after(e)},remove:function(){const{parentNode:e}=this,{previousElementSibling:t,nextElementSibling:o}=e;(t&&"item"===t.className||o&&"item"===o.className)&&e.remove()},concat:(()=>{let e=null,t=()=>{c.setAttribute("aria-hidden","true")};return()=>{const o=[];a.querySelectorAll(".item").forEach(e=>{const t=(()=>{let{value:t}=e.children[1];t=t.replace(/^javascript:/,"");try{return decodeURIComponent(t)}catch(e){return t}})(),a=e.firstElementChild.value||t;o.push({title:a,code:t})}),s.value=`javascript:(() => {const srcSet = ${JSON.stringify(o)}; srcSet.forEach(({title, code}) => {console.log(\`\${title} を実行します\`);try {eval(code);} catch (e) {console.error(e);}});console.log('Done: 次の内容を実行しました。');console.table(srcSet);window.CONCAT_BOOKMARKLETS = window.CONCAT_BOOKMARKLETS || Object.create(null); CONCAT_BOOKMARKLETS.executed = srcSet;})();`,c.setAttribute("aria-hidden","false"),clearTimeout(e),e=setTimeout(t,1e3)}})()};return r.type="button",r.className="close",r.innerHTML="<span>閉じる</span>",r.addEventListener("click",p.close),e.append(r),e.append(n),n.textContent=':host {overflow: auto;max-width: 500px;width: 90vw;margin: auto;line-height: 1.8;font-size: 14px;display: block;position: fixed;left: 0;right: 0;top: 0;bottom: 0;z-index: 999999;background: #fff;box-shadow: 0 0 2px rgba(0, 0, 0, .4);max-height: 90vh;padding: 10px;} * {box-sizing: border-box;}.close::before,.close::after,.plus::before,.plus::after,.delete::before,.delete::after {content: ""; width: 3px;background: #aaa;display: block;left: 0;right: 0;top: 0;bottom: 0;margin: auto;z-index: 0;position: absolute;}.plus::before,.plus::after,.delete::before,.delete::after {height: 11px;}.close::before, .close::after {height: 21px;}.close::before, .delete::before {transform: rotate(-45deg);}.close::after, .delete::after {transform: rotate(45deg);} .close {height: 30px;width: 30px;margin: 0 0 10px;} input, button {display: block;} button {position: relative; border: 0;background: transparent;} button span {position: absolute; width: 0; height: 0; overflow: hidden; display: block; z-index: -1;} input {border: 0;background: transparent;width: 100%; padding: 10px 40px 10px 10px; margin: 0 0 8px;font-weight: bold;} input + input {background:#fff;box-shadow: 1px 1px 1px rgba(0, 0, 0, .2);font-weight: normal;} .item {background: #efefef;padding: 10px;position: relative; margin: 0 0 70px;} .plus,.delete {position: absolute;} .plus {border: 1px solid #aaa; border-radius: 80px; height: 39px; width: 39px; background: #fff;margin: 10px auto 0; left: 0; right: 0; top: 100%;}.plus::after {transform: rotate(90deg);} .delete {border: 0;background: transparent;right: 10px;top: 10px; width: 30px; height: 30px;} .item:nth-child(3):nth-last-child(3) .delete {display: none;} .resultWrap {margin: 10px 0 0;}.result {border: 1px solid #ccc; background: #333; color: #fff;}.concat {background: #04b8c3;color: #fff;padding: 4px 12px;border-radius: 5px;}.resultWrap span {display: block;color: #398700;font-weight: bold;text-align: center;} .resultWrap  [aria-hidden="true"] {opacity: 0;transition: .2s opacity ease-out;}',e.append(p.getFormSet()),l.textContent="結合する",l.type="button",l.className="concat",l.addEventListener("click",p.concat),e.append(l),c.textContent="結合しました！",c.setAttribute("aria-hidden","true"),i.className="resultWrap",s.className="result",s.placeholder="結合結果",s.setAttribute("aria-label","結合結果"),s.readOnly=!0,i.append(s),i.append(c),e.append(i),e})();a.append(n),t.body.append(o)})())})(window,document);
```
GitHub：[複数のブックマークレットを結合する.js](https://github.com/Soten-Bluesky/bookmarklets/tree/master/%E8%A4%87%E6%95%B0%E3%81%AE%E3%83%96%E3%83%83%E3%82%AF%E3%83%9E%E3%83%BC%E3%82%AF%E3%83%AC%E3%83%83%E3%83%88%E3%82%92%E7%B5%90%E5%90%88%E3%81%99%E3%82%8B.js)


## 複数のブックマークレットを結合させるメリット

ブックマークレットは便利な場面では大変便利なのですが、複数のブックマークレットを同時に（1度のクリックで）実行することはできません。

案件ごとや、よく見るWebサイトごとに利用するブックマークレットが異なっている場合、その都度必要なブックマークレットの数だけブックマークをクリックしなければならないでしょう。

しかし、あらかじめお気に入りのブックマークレットをそもそも1つに結合してしまえば、1回のクリックで実行したいブックマークレットをまとめて走らせることができます。

※ ブラウザ拡張ならばクリックすら必要ないですがそれはまたの機会に

### そもそもブックマークレットとは

Webブラウザに搭載されている、ページのブックマークにJavaScriptコードを登録しておくことができる古くからある技術です。

ブックマークには、URLの代わりに`javascript:`から始まるJavaScriptコードを登録します。

そうしておくと、お気に入りに入れているWebページを開く感覚で登録したJavaScriptコードを実行することができます。

参考：[Bookmarklet という一番身近な自動化技術 | blog.jxck.io](https://blog.jxck.io/entries/2018-01-12/let-it-bookmarklet.html)
参考：[ブックマークレットの登録方法 - Qiita](https://qiita.com/aqril_1132/items/b5f9040ccb8cbc705d04)