ほんじゃらねっと

ダイエット中プログラマのブログ

jQueryUIのsortableウィジェットを使ってアイテムの並び順を管理するUIを作成する方法

f:id:piro_suke:20161207003918j:plain

例えば商品のランキングを手動で管理する場合、

商品データに並び順のようなものをつけてその順序で表示すると思うのだけど、

並び順を設定するUIをどのように作るのが良いか。

近いうちに作る機会がありそうなので、考えてみる。

ReactやVue.jsを使う機会はなさそうなので、jQueryベースで。

やりたいこと

既に商品マスタはあり、ソート順を設定できるカラムもあるとする。

できるだけ楽にランキングを変更できて、

データの更新数が最小限になる方法を見つけたい。

色々やり方はありそうだけど、

まずは一番簡単に実装できそうな方法で作ってみよう。

簡単に実装できそうな方法

商品がリスト状に並んでいるとして、

並び順を楽に変更する方法としては、

ドラッグしてリストの順序を変更するUIがオーソドックスで良さそう。

これはjQueryUIのsortableウィジェットで実現できる。

jqueryui.com

データの更新数を少なくする方法としては、

商品毎の並び替え前ソートNoを保持しておき、

並び替え後のソートNoと比較して変更があったものだけを更新する、

という方法を取ればとりあえず全件更新するよりは更新数は少なくなりそう。

実装してみる

実際にデータベースを作成するのもアレなので、

下記のようなリストをソートする画面を作成する。

var items = [ 
    {code: 'cd01', name: 'item1', sort_no: 1}, 
    {code: 'cd02', name: 'item2', sort_no: 2}, 
    {code: 'cd03', name: 'item3', sort_no: 3}, 
    {code: 'cd04', name: 'item4', sort_no: 4}, 
    {code: 'cd05', name: 'item5', sort_no: 5}, 
    {code: 'cd06', name: 'item6', sort_no: 6}, 
    {code: 'cd07', name: 'item7', sort_no: 7}, 
    {code: 'cd08', name: 'item8', sort_no: 8}, 
    {code: 'cd09', name: 'item9', sort_no: 9}, 
    {code: 'cd10', name: 'item10', sort_no: 12} 
];  

完成版をCodePenに置いたので、先に載せておこう。

See the Pen sorting example by Pirosuke (@pirosuke) on CodePen.

商品ごとに新旧ソートNoを確認できるように表示してある。

右側のハンドラで並び順を変更すると新ソートNoが再設定される。

実際にアプリを作成する際は、新旧ソートNoが異なる商品の情報を

サーバに送信するイメージ。

コード

bowerでjQuery関連ライブラリを取得しておく。

...
"dependencies": {
  "jquery": "^3.1.1",
  "jquery-ui": "^1.12.1"
}
...

index.html

<!DOCTYPE html>
<!--[if IE 9 ]><html lang="ja" class="lt-ie9"><![endif]-->
<!--[if (gte IE 10)|!(IE)]><!--><html lang="ja"><!--<![endif]-->
  <head>
    <meta charset="UTF-8" />

    <link rel="stylesheet" href="js/bower_components/jquery-ui/themes/base/jquery-ui.min.css"/>
    <link rel="stylesheet" href="css/main.css"/>

    <title>ソート順管理</title>

  </head>

  <body>

    <ul id="js-item-list" class="item-list">
    </ul>

    <textarea id="js-console"></textarea>

    <ul id="js-item-template" class="ui-helper-hidden">
      <li data-item-cd="" class="item">
        <div class="ui-helper-clearfix">
          <div class="item-title">
            <span class="js-item-name">アイテム名</span>
            <input type="text" class="js-item-old-sort-no" value="1" />
            <input type="text" class="js-item-sort-no" value="1" />
          </div>
          <a href="#" class="js-sort-handle sort-handle"><span class="ui-icon ui-icon-arrowthick-2-n-s" aria-hidden="true"></span><span></span></a>
        </div>
      </li>
    </ul>

    <script src="js/bower_components/jquery/dist/jquery.min.js"></script>
    <script src="js/bower_components/jquery-ui/jquery-ui.min.js"></script>
    <script src="js/main.js"></script>
  </body>
</html>

main.css

.item-list {
  width: 500px;                                                                                                                                                                        
  list-style-type: none;
  margin: 0;
  padding: 0;
}

.item-list .item {
  border: 1px solid #eee;
  margin: 0 0 3px 0;
  padding: 5px;
  background-color: #efe;
}

.item-list .item .item-title {
  float: left;
}

.item-list .item .sort-handle {
  float: right;
}

#js-console {
  position: fixed;
  bottom: 0px;
  left: 0px;
  width: 100%;
  height: 100px;
}

main.js

$(function() {
    var itemListBlock = $('#js-item-list');
    var itemTemplateBlock = $('#js-item-template');                                                                                                                                    
    var consoleBlock = $('#js-console');

    var items = [
        {code: 'cd01', name: 'item1', sort_no: 1},
        {code: 'cd02', name: 'item2', sort_no: 2},
        {code: 'cd03', name: 'item3', sort_no: 3},
        {code: 'cd04', name: 'item4', sort_no: 4},
        {code: 'cd05', name: 'item5', sort_no: 5},
        {code: 'cd06', name: 'item6', sort_no: 6},
        {code: 'cd07', name: 'item7', sort_no: 7},
        {code: 'cd08', name: 'item8', sort_no: 8},
        {code: 'cd09', name: 'item9', sort_no: 9},
        {code: 'cd10', name: 'item10', sort_no: 12}
    ];

    var addLog = function (message) {
        consoleBlock.val(message + '\n' + consoleBlock.val());
    };

    var reOrderSortNo = function () {
        var sortNoList = $.map(items, function (item, i) {
            return item.sort_no;
        });
        sortNoList.sort(function (a, b) {
            var numA = a - 0;
            var numB = b - 0;
            if (numA < numB) return -1;
            if (numA > numB) return 1;
            return 0;
        });

        $('li', itemListBlock).each(function (i) {
            var itemBlock = $(this);
            $('.js-item-sort-no', itemBlock).val(sortNoList[i]);
        });
    };

    $.each(items, function (i, item) {
        var itemBlock = $('li', itemTemplateBlock).clone();
        itemBlock.attr('data-item-cd', item.code);
        $('.js-item-name', itemBlock).text(item.name);
        $('.js-item-old-sort-no', itemBlock).val(item.sort_no);
        $('.js-item-sort-no', itemBlock).val(item.sort_no);
        itemBlock.appendTo(itemListBlock);
    });

    itemListBlock.sortable({
        handle: '.js-sort-handle',
        update: function (e, ui) {
            var targetItemCd = ui.item.attr('data-item-cd');
            addLog('item ' + targetItemCd + ' position updated');
            reOrderSortNo();
        }
    });

    addLog('started');
});

おわり

これで十分な気がしてきた。

CodePen便利。

コアjQuery+プラグイン/jQuery UI 開発実践技法 (Programmer's SELECTION)

コアjQuery+プラグイン/jQuery UI 開発実践技法 (Programmer's SELECTION)