コムセント 技術情報

  1. TOP
  2. コムセント 技術情報
  3. Editor.jsでページネーションを付ける方法

Editor.jsでページネーションを付ける方法

環境

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

このメンバーの記事一覧へ

おすすめ記事