Webページに参考サイト管理ツールを作る
Webサイトやブログを作成するにあたり、参考サイトはちゃんと紹介したいものです。しかし記述が面倒だったり、ブラウザを閉じてしまってどのページがわからなくなってしまったりして紹介できないこともあるのが現状です。
そこで、そのような参考サイトの管理の手助けになるようなWebツールを作ってみようというのが今回の話です。
ここではコード等の説明をしています。ページの最後に稼働サンプルも用意しました。
利用するライブラリ
利用するライブラリは、Vue.js(Version3系)と、clipboard.jsです。Vue.jsは多少勉強する必要はありますがその分JavaScriptのコーディングが楽になるフレームワークです。ここではHTMLファイルに直接記述する方法(インライン式)を用います。Vue.jsでの開発の方法は過去の記事でも触れていますので、よければ参考にしてください。
clipboard.js
clipboard.jsはこのブログでは初めて登場したので、簡単に使い方を説明します。クリックでクリップボードにコピーをするためのライブラリです。
ここではunpkg.comのCDNで利用しています。読み込むには次のタグを挿入します。
<script src="https://unpkg.com/clipboard@2.0.6/dist/clipboard.js"></script>
基本的な使い方に移ります。まず、スクリプト内でClipbordJSオブジェクトを作成します。ここではコンストラクタにクラス名を指定しています。クラス名を指定する時は先頭に.を入れます。該当のクラスのついたボタンがすべてclipbord.jsと連携できるようになります。スクリプト部の設定はこれだけです。
ちなみにidで指定したい場合は先頭の.を#に変更します。
<script>
var clipbordjs =new ClipboardJS('.class-name');
</script>
次に、HTML部でコピー対象の要素を作成します。これはinputやtextareaでなくても利用することができます。ここではiput要素にします。これに好きなIDを付けます。
<input type="text" id="copytarget" />
最後にコピー用のボタンを作成します。コピー用のボタンにはClipbordJSのコンストラクタに渡したクラス名をつけます。また、data-clipboard-targetという属性を作りその値に、コピー対象の要素のidを設定します。この時先頭に#をつけてください。
<button class="class-name" data-clipboard-target="#copytarget" >コピー</button>
サンプル
ボタンの属性値に「data-clipboard-action="cut"」を加えると、カット(切り取り)モードになりますが、これはinputやtextareaでしか使えません。
ボタン自身にコピー対象の文字列を設定したい場合はdata-clipboard-targetの代わりにdata-clipboard-text属性を使い、クリップボードに入れたいデータを属性値にセットします。
コピーイベントを設定したい場合はスクリプトのインスタンスに記述を加えます。イベント関数の引数には、成功時はaction、text、triggerプロパティが存在し、エラー時にはaction、triggerプロパティが存在します。actionには"copy"や"cut"の文字が入ります。textにはコピーしたテキスト、triggerには「button.class-name」等が入ります。 cu
ちなみにこのイベント設定をしないなら、インスタンスをclipboadjs変数に受ける必要はありません。(new ClipboardJS('.class-name');という記述だけでいいです。)
<script>
var clipbordjs =new ClipboardJS('.class-name');
//成功時のイベント
clipbordjs.on('success',function(e){ console.log(e) });
//エラー時のイベント
clipbordjs.on('error',function(e){ console.log(e) });
</script>
参考リンク管理ツールのソースコード
Vue.jsで書いたソースコードは次のようになります。間違えてページを閉じてもいいようにLocalStorageを使ってデータを保存しています。
また、手動でリンクを追加するのとは別のインターフェースとしてa要素の配列でデータを流し込めるようにしています。その際にa要素の配列(HTMLCollection)を取得するのがgetLinksです。ここでは、等ブログのサイトマップからリンクを取ってきています。
HTML部
<div id="app">
<p>アドレス</p><p><input type="text" name="siteaddress" v-model="url"/></p>
<p>リンクテキスト</p><p><input type="text" name="sitename" v-model="txt"/></p>
<p><label><input type="checkbox" name="norefferer" v-model="ref"/>rel no<span>R</span>eferrer(リファラを参照元へ渡しません)</label></p>
<p><label><input type="checkbox" name="nofollow" v-model="fol"/>rel no<span>F</span>ollow(ページ評価を参照元へ渡しません)</label></p>
<p><label><input type="checkbox" name="noopener" v-model="ope"/>rel no<span>O</span>pener(利用者向けのセキュリティ対策)</label></p>
<p><label><input type="checkbox" name="blank" v-model="bla" /><span>T</span>arget _blank(新しいページとして開きます)</label></p>
<p><button v-on:click="addLinks">入力したサイトを追加</button> <button v-on:click="getLinks()">当ブログの全てのリンクを取得</button></p>
<div>
<table>
<tr><th>アドレス<th>リンクテキスト<th>R<th>F<th>O<th>T<th>採用<th>コピー<th>削除</tr>
<tr v-for="l in links">
<a :href="l.url" target="_blank" rel="noopener">{{l.url}}</a>
</td>
<td><textarea rows="5" v-model="l.txt" style="vertical-align: top;
width: 95%;
"></textarea></td>
<td><input type="checkbox" v-model="l.ref" /></td>
<td><input type="checkbox" v-model="l.fol" /></td>
<td><input type="checkbox" v-model="l.ope" /></td>
<td><input type="checkbox" v-model="l.bla" /></td>
<td><input type="checkbox" v-model="l.sel" /></td>
<td><button v-on:click="copyRow(l.id)">c</button></td>
<td><button v-on:click="deleteRow(l.id)">d</button></td>
</tr>
</table>
<div>
<p>メッセージ</p>
<p id="linkresult">{{result}}</p>
<button id="btnclipboard" class="btn" data-clipboard-target="#linkresult">結果をクリップボードへコピー</button>
</div>
<p><button v-on:click="saveData()">リストの編集を保存</button><button v-on:click="clear()">リストを全削除</button></p>
</div>
</div>
スクリプト部
<script src="https://unpkg.com/vue@next"></script>
<script src="https://unpkg.com/clipboard@2.0.6/dist/clipboard.js"></script>
<script>
//localstorage用のキー
const LOCAL_STORAGE_KEY='links';
//clipbord.jsのインスタンス
new ClipboardJS('#btnclipboard');
var vueparams = {
data () {
return {
result:"",
id: 0,
ref:false,
fol:false,
ope:true,
bla:true,
txt:"",
url:"",
links: [],
}
},
mounted: function(){
//ページ表示時ローカルストレージにデータがあれば復元します
let jsonstr = localStorage.getItem(LOCAL_STORAGE_KEY);
if (jsonstr != null) {
let parse = JSON.parse(jsonstr);
for (let i =0; i < parse.length;i++) {
parse[i].id=i+1;
this.links.push(parse[i]);
}
this.id=parse.length;
}
},
methods: {
addLinks () {
this.id++;
const obj = {
id: this.id,
ref: this.ref,
fol: this.fol,
ope: this.ope,
bla: this.bla,
txt: this.txt,
url: this.url,
sel: false,
};
this.links.push(obj);
//テキストとアドレスはクリア
this.txt="";
this.url="";
localStorage.setItem(LOCAL_STORAGE_KEY,JSON.stringify(this.links));
this.result="";
},
deleteRow(intId) {
for(let i = 0; i < this.links.length;i++) {
if (this.links[i].id === intId) {
this.links.splice(i,1);
localStorage.setItem(LOCAL_STORAGE_KEY,JSON.stringify(this.links));
break;
}
}
},
getLinks() {
//リンクを取得するメソッド、<a href="">リンクテキスト</a>の配列になるように取得します。
//各自で実装してください。
let req = new XMLHttpRequest();
const strTargetUrl="https://nanbu.marune205.net/p/sitemaps.html?m=1";
req.onreadystatechange=() => {
if (req.readyState == 4 && req.status == 200) {
let elDiv = document.createElement('div');
elDiv.innerHTML=req.responseText;
let elDiv2 = elDiv.getElementsByClassName('divwrapper')[0];
let aElements = elDiv2.getElementsByTagName("a");
this.reflectLinks(aElements);
}
}
//trueをfalseにすると同期通信になります。
req.open('GET',strTargetUrl,true);
req.send(null);
},
reflectLinks(aElements) {
//aElementsにa要素の配列が入っている前提です
for (let i=0; i < aElements.length; i++) {
let strHref = aElements[i].getAttribute("href");
let strInner = aElements[i].innerHTML;
this.id++;
const obj = {
id: this.id,
ref: this.ref,
fol: this.fol,
ope: this.ope,
bla: this.bla,
txt: strInner,
url: strHref,
sel: false,
};
this.links.push(obj);
}
},
saveData(intId) {
localStorage.setItem(LOCAL_STORAGE_KEY,JSON.stringify(this.links));
this.result="保存しました";
},
clear() {
if(confirm("表示中のリストを完全に削除します.よろしいですか?")) {
this.links=[];
localStorage.removeItem(LOCAL_STORAGE_KEY);
this.result="";
}
},
copyRow(intId) {
let intTarget = -1;
let strWk = "";
let blnHit = false;
for(let i = 0; i < this.links.length;i++) {
if (this.links[i].id === intId) {
intTarget = i;
break;
}
}
if (0 <= intTarget) {
strWk='<a href="'+this.links[intTarget].url+'"';
if (this.links[intTarget].bla) {
strWk+=' target="_blank"';
}
if (this.links[intTarget].fol || this.links[intTarget].ref || this.links[intTarget].ope) {
strWk+=' rel="';
if (this.links[intTarget].fol) {
strWk+='nofollow ';
}
if (this.links[intTarget].ref) {
strWk+='noreferrer ';
}
if (this.links[intTarget].ope) {
strWk+='noopener ';
}
//最後の空白をカット
strWk = strWk.slice(0, -1);
strWk +='">'+this.links[intTarget].txt+'</a>'
}
this.result=strWk;
setTimeout(()=>{document.getElementById("btnclipboard").click();},1);
} else {
this.result="";
}
},
}
}
const app = Vue.createApp(vueparams);
app.mount("#app");
</script>
サンプルの使い方
- アドレスにサイトのアドレスを入力してください。
- リンクテキストと、target属性、rel属性を入力または選択してください。
この項目は後から修正できます。
- 追加ボタンを押すとリストに追加されます。
- 採用チェックボックスは特に機能しません。
実際に記事に利用したか否かをメモするのに使ってください。
- c(copy)ボタンを押すとクリップボードにコピーされます。
また、下段のメッセージにもHTMLが出力されますので、クリップボードに入らない場合はそちらからコピーして下さい。
- d(delete)ボタンを押すと行が削除されます。
- 行追加、削除のタイミングで、LocalStorageに保存されます。
- 修正の保存は手動です
テーブル内のテキストやチェックボックスを修正した場合は保存ボタンで内容をLocalStorageに保存します。
- 全削除
すべてのデータを削除したい場合は、全削除ボタンを押してください。LocalStorageのデータも削除されます。
ライセンス情報
- Vue.js
Released under the MIT License Copyright © 2014-2020 Evan You
- clipboard.js v2.0.6
Released under the MIT License Copyright © Zeno Rocha
- 参考サイト管理ツール
Released under the MIT License Copyright © なんぶ電子
稼働サンプル
参考サイト管理ツール