Editor.jsでページネーションを付ける方法
この記事は2023/03/31に作成されました。
環境
PHP 8.1
editor.js 2.26.2
Editor.jsで投稿する機能を作成する中で、Wordpressのページ区切りのようなコンテンツを複数のページに分けて表示するプラグインを自作したので、ご紹介します。
はじめに
このページでは
- header(@editorjs/header)
- paragraph
- separate(自作)
のみのシンプルなエディターを作成&表示します。
プラグイン作成
プラグイン部分はシンプルに [separate] と表示するのみです。
class Separate{
static get toolbox() {
return {
title: 'Separate',
icon: '<i>S</i>'
}
}
render() {
const p = document.createElement('p');
p.innerText = "[separate]";
return p;
}
save(data) {
return {
text: "[separate]"
}
}
}
わかりやすくするために、wordpressの区切り線のようなスタイルを当ててもよさそうです。
Editor生成
次に、DOMContentLoaded内等でエディターを作成します。
先ほど作成した Separate をプラグインとして追加します。
editor = new EditorJS({
holder: 'editorjs',
placeholder: 'No content',
data: '',
tools:{
header: Header,
image: ImageTool,
separate: Separate,
},
});
作成時の設定は以上です。
エディター内でコンテンツ作成時に、ページを区切りたい部分で separate を追加してください。
保存
今回はDBに、save()で保存した際に返ってくるデータをJSON化したものを全て保存しました。
this.editor.save()
.then((savedData) => {
this.contents = JSON.stringify(savedData);
...
XMLHttpRequest等でpostをDBに保存
...
PHPで取得
次に、PHPでDBからデータを取り出し、表示用に整形します。
//最終的に表示する用の配列
$toShowContents = [''];
//ページ分け用のindex
$nowIndex = 0;
//ブロックごとにループ
foreach ($contents as $block){
switch ($block->type){
// paragraph 普通の文
case 'paragraph':
$toShowContents[$nowIndex] .= '<p>' . $block->data->text . "</p>\n";
break;
// header 見出し
case 'header':
$toShowContents[$nowIndex] .= '<h2>' . $block->data->text . "</h2>\n";
break;
// separate ページ区切り
case 'separate':
// 表示は何もせず、indexだけプラスする
$nowIndex++;
$toShowContents[$nowIndex] = '';
break;
default:
break;
}
}
表示するための配列を、このようにして整形します。
PHPで表示
次に、表示部分です。
全ページ下部にページャーを表示させ、各ページを css で表示:非表示する方法で実装していきます。
<script>
let pageIndex = 0;
const pageIndex_max = <?php echo count($contents) - 1; ?>;
</script>
<?php foreach($contents as $pageIndex => $content): ?>
<div id="js-content-<?php echo $pageIndex ?>"
<?php if($pageIndex !== 0): ?>
style="display: none"
<?php endif; ?>
>
<?php echo $content; ?>
<ol>
<li class="js-prevBtn" style="display: none">
<a href="#" class="js-prevPageLink">PREV</a>
</li>
<?php foreach($contents as $index_pager => $content_pager)
<li
<?php if($index_pager === $pageIndex): ?>
class="isCurrent"
<?php endif; ?>
>
<a href="#" id="js-pageLink-<?php echo $index_pager; ?>"><?php echo $index_pager + 1; ?></a>
</li>
<?php endforeach; ?>
<li class="js-nextBtn"
<?php if(isset($column->contents[1]): ?>
style="display: none"
<?php endif; ?>
>
<a href="#" class="js-nextPageLink">NEXT</a>
</li>
</ol>
</div>
<?php endforeach; ?>
JSで表示制御
これで1ページ目は表示できたので、次にページャーで表示を制御するためのJSを書いていきます。
document.addEventListener('DOMContentLoaded', () => {
// ページャー
const nextPageLinkList = document.getElementsByClassName('js-nextPageLink') ?? [];
const prevPageLinkList = document.getElementsByClassName('js-prevPageLink') ?? [];
const pageLinkList = document.querySelectorAll('[id^="js-pageLink-"]') ?? [];
const pageTargetList = document.querySelectorAll('[id^="js-content-"]') ?? [];
const nextPageBtnList = document.getElementsByClassName('js-nextBtn') ?? [];
const prevPageBtnList = document.getElementsByClassName('js-prevBtn') ?? [];
//次のページへボタンクリック時
for (const next of nextPageLinkList) {
next.addEventListener('click', () => {
pageIndex++;
//prevボタンを表示
for (const btn of prevPageBtnList) {
btn.style.display = 'list-item';
}
//最後のページだったらnextボタン非表示
if (pageIndex === pageIndex_max) {
for (const btn of nextPageBtnList) {
btn.style.display = 'none';
}
}
//対応するページを表示
pageTargetList.forEach((page, index) => {
page.style.display = index === pageIndex ? 'block' : 'none';
});
});
}
// 前のページへボタンクリック時
for (const prev of prevPageLinkList) {
prev.addEventListener('click', () => {
pageIndex--;
//nextボタンを表示
for (const btn of nextPageBtnList) {
btn.style.display = 'list-item';
}
//最初のページだったらprevボタン非表示
if (pageIndex === 0) {
for (const btn of prevPageBtnList) {
btn.style.display = 'none';
}
}
//対応するページを表示
pageTargetList.forEach((page, index) => {
page.style.display = index === pageIndex ? 'block' : 'none';
});
});
}
// 各ページボタンクリック時
for (const pageLink of pageLinkList) {
pageLink.addEventListener('click', (e) => {
const targetId = e.currentTarget.id.slice(e.currentTarget.id.lastIndexOf('-') + 1);
pageIndex = parseInt(targetId);
//最初のページじゃなかったらprevボタンを表示
const nextDisplay = (pageIndex !== 0 ? 'list-item' : 'none');
for (const btn of prevPageBtnList) {
btn.style.display = nextDisplay;
}
//最後のページじゃなかったらnextボタンを表示
const prevDisplay = (pageIndex !== pageIndex_max ? 'list-item' : 'none');
for (const btn of nextPageBtnList) {
btn.style.display = prevDisplay;
}
//対応するページを表示
pageTargetList.forEach((page, index) => {
page.style.display = (index === pageIndex ? 'block' : 'none');
});
});
}
});
各ページャークリック時に、対応するページを表示、しないページを非表示にするというイベントをaddEventListenerしていきます。
これでページャー付きのコンテンツが表示できました。
最後に
ページネーション機能は想像より簡単に作成できました。
簡単な記事作成機能でしたら、これで十分かなと感じました。
プログラマー/A.A