アナログCPU:5108843109

ゲームと音楽とプログラミング(酒と女とロックンロールのノリで)

('ω') < イザユケエンジニャー

ドラッグ&ドロップで項目の並べ替えを可能にする

まずはサンプルから。

hoge」「fuga」「piyo」の文字列をドラッグ&ドロップで並べ替えることができます。

↓実装サンプルここから
================================


================================
↑実装サンプルここまで

これ、どんだけめんどくさい実装なのかと思ったらめちゃくちゃ簡単でした。

jQuery使用してます。jquery-uiもいります。

<script src=" ... /jquery.min.js"></script>
<script src=" ... /jquery-ui.js"></script>

<HTML>

<ul class="sortable">
  <li id="1">hoge</li>
  <li id="2">fuga</li>
  <li id="3">piyo</li>
</ul>

<JS>

$(function() {
    $(".sortable").sortable();
});

以上。まじかよ。すごい。

ちなみにスマホ対応したいときは、↓をDLしてincludeするだけで済むっぽい。まじかよすごい。
jQuery UI Touch Punch - Touch Event Support for jQuery UI

<script src=" ... /jquery.ui.touch-punch.min.js"></script>

おまけ:ドロップしたときの処理を作る

正確にはドロップによって並びを変更したときの処理ですが。
JS側を以下のような形に変更すればOK。

<JS>

$(function() {
    $(".sortable").sortable({
        update: function(){
            // ここにドロップ時処理
        }
    });
});

ここで随時ajaxで保存するも良し、submit用の配列なりテキストなりをこの時点で作っておくも良し。
パズルゲームとか作れそうだな。

JSとPHPでJSONデータを扱う

WEBアプリを作っていると、JSとPHPの間のデータのやり取りなどでJSON形式を使うことが多いので、
JSONを配列にしたり、配列をJSONにしたりする方法をメモ。

PHPで配列をJSONにする

json_encodeを使います。
PHP: json_encode - Manual
変換したい配列以外にもオプションがありますが、基本的に無指定でOK。

<?
$data_array = array(
    'hoge' => 1,
    'fuga' => 2,
);
$data_json = json_encode($data_array);

PHPJSONを配列にする

json_decodeを使います。
PHP: json_decode - Manual
第二引数を無指定(またはfalse)にするとオブジェクト形式になり、
trueを渡すと連想配列になります。
個人的には配列が良いので常にtrueを渡しています。

<?
$data_json = '{"hoge":1,"fuga":2}';
$data_array = json_decode($data_json, true);

JSで配列をJSONにする

JSON.stringifyを使います。

var data_array = { hoge: 1, fuga: 2 };
var data_json = JSON.stringify(data_array);

JSでJSONを配列にする

jQueryなら$.parseJSONを、生JSなら…なんやこれ。

jQuery

var data_json = '{"hoge":1,"fuga":2}';
var data_array = $.parseJSON(data_json);

<JS>

var data_json = '{"hoge":1,"fuga":2}';
var data_array = (new Function("return " + data_json))();

非同期処理を同期処理っぽく扱う

まず、以下2つの関数を用意しました。

// funcBの戻り値と"funcA"を吐き出す関数
function funcA(){
    console.log(funcB());
    console.log("funcA");
}

// 開始から5秒後に「funcB」とreturnする関数
function funcB(){
    setTimeout(function(){
        return "funcB";
    },5000);
}

期待としては、実行から5秒後に「funcB」と「funcA」が
(ほぼ)同時に表示されてほしいわけですが、まあそういうわけにはいきません。
setTimeoutは「5秒後に○○という処理をする」というタイマー的なものをセットしているだけなので。
具体的には、実行と(ほぼ)同時に

undefined
funcA

と表示されます。
その5秒後くらいにfuncBが返している値は闇に消えます。

ここまで簡単な例ならsetTimeout内で「funcB」「funcA」を表示すりゃいいじゃん、で済みますが
複雑になってくると困る場合もあったので、いろいろ調べて以下のように加筆修正してみました。

// funcBの戻り値と"funcA"を吐き出す関数
function funcA(){
    var def = funcB();
    def.done(function(){
        console.log(def.return_value);
        console.log("funcA");
    });
}

// 開始から5秒後に「funcB」とreturnする関数
function funcB(){
    var deferred = new $.Deferred();
    setTimeout(function(){
        deferred.return_value = "funcB";
        deferred.resolve();
    },5000);
    return deferred;
}

わーいらくちーん
deferredというのを使っています。
処理を監視して、「終了したら○○する」ということができます。

無事に、実行後5秒ほど経ってから

funcB
funcA

と表示されました。

ポイントは以下のような感じでしょうか。

  • funcA
    • funcBの戻り値を変数で受けて、「.done」で「funcB終了時の処理」を記述
  • funcB
    • Deferredオブジェクトを生成し、resolve()で処理終了したことを通知
    • Deferredオブジェクトのreturnは、時間のかかる処理の外でOK
    • Deferredオブジェクトには戻り値用の要素を勝手に追加できる
      • 上記の例では「return_value」が勝手に追加したやつ

これなら大元のロジックを大きく変えることなく実装できそう。

参考文献
jQueryのDeferredが便利過ぎた

連想配列に含まれるIDを使ってユニーク化する関数

を作ったのでメモ。

function uniqAry(ary){
    // idの値で昇順ソートする
    ary.sort(function(a, b){ return a.id > b.id; });

    // 重複しているIDがあれば後に出てきた方を削除(undefinedにする)
    var tmpid = 0;
    $(ary).each(function(i, e){
        if (e.id == tmpid){
            delete ary[i];
        }
        else{
            tmpid = e.id;
        }
    });

    // undefined(削除した項目)を除いたものを返す
    return ary.filter(function(v){ return v != undefined; });
}

配列に必ず0でない数値の「id」が含まれているという前提。

配列に含まれる「id」でソートして、
ソート後配列を順番に見て前レコードと同じIDだったら削除しています。
spliceで要素を削除するとループがめんどくさいことになるので、deleteでundefined化してからfilterしています。


例えば以下のようなのを実行すると

var ary = [
    {id: 1, name:"name1_1"},
    {id: 3, name:"name3_1"},
    {id: 1, name:"name1_2"},
    {id: 2, name:"name2_1"},
    {id: 3, name:"name3_2"},
];
console.log( uniqAry(ary) );

こうなります。

{id: 1, name:"name1_1"},
{id: 2, name:"name2_1"},
{id: 3, name:"name3_1"},

特定のクラスを持ったチェックボックスについて、チェック有無で絞り込む

jQueryセレクタ便利…。
いや、プレーンJSの書き方知らないので比較できないんですけどこれはたぶん便利なやつ…。

必要になるたびググってる気がするのでメモ。

↓実装サンプルここから
================================



チェックしているものとしていないものを分ける

(結果はコンソールで見てね)
================================
↑実装サンプルここまで

<HTML>

<input type="checkbox" class="hoge-cb" value=1>
<input type="checkbox" class="hoge-cb" value=2>
<input type="checkbox" class="hoge-cb" value=3>
<a href="#" onclick="testCheckbox()">チェックしているものとしていないものを分ける</a>

<JS>

function testCheckbox(){
    console.log( $(".hoge-cb:checked") );
    console.log( $(".hoge-cb:not(:checked)") );
}

ドラッグ&ドロップでファイルをアップロードする(ちょっとリッチ版)

こちらの記事が前提となります。

IEだと動かないはず。対応した版はこちら。
ドラッグ&ドロップでファイルをアップロードする(IE対応版) - アナログCPU:5108843109

簡単に書くと
「ファイルのアップロードフォーム」と
「アップロードしたいファイルをドロップできるエリア」を用意して
エリアにドロップされたファイルをフォームに渡す処理が既にあります。

これに対して、この記事では以下の処理を追加していきます。

  • ドロップしたら送信ボタンを押さなくてもアップロード
  • ファイル選択から選択したときもボタンを押さずにアップロード
  • ファイル選択のボタンとかかっこわるいから出さない。ドロップ用のエリアをクリックしたら選べるようにする
  • ドロップエリアにマウスが乗ってる間は色を変えるとか分かりやすくする
  • ドロップエリアの外でドロップしてもファイルが開かないようにする

これを実現するためにもうちょっと切り分けて、具体的には以下のような実装をします。

  • formを非表示にする
  • ドロップエリアをクリックしたら、formのファイル選択ボタンをクリックしたことにする
  • formのファイルinputタグが変更されたら、formの送信ボタンをクリックしたことにする
  • ドロップエリアの上にファイルを掴んで乗っかったときにエリアの色を薄くする
  • ドロップエリアでドロップされたときにエリアの色をデフォルト状態に戻す
  • ドロップエリアから離れたときにエリアの色をデフォルト状態に戻す
  • ドロップエリアの外でドロップしてもファイルが開かないようにする

こんな感じでしょうか。
順番にやっつけていきます。

続きを読む

ドラッグ&ドロップでファイルをアップロードする(最低限版)

タイトル通りのやつにちょっと苦戦したのでメモ。
つーか業務アプリなんだからドラッグ&ドロップじゃなくてよくね??

今回は必要最低限の処理だけ書いた版です。ちょっとリッチ版はこちら。
ドラッグ&ドロップでファイルをアップロードする(ちょっとリッチ版) - アナログCPU:5108843109

IEだと動かないはず。対応した版はこちら。
ドラッグ&ドロップでファイルをアップロードする(IE対応版) - アナログCPU:5108843109

ほぼ完成サンプル

<HTML>

<div id="upload-area" style="height: 100px; width: 100px; background: #DDD;">うp</div>

<form enctype="multipart/form-data" action="【※】" method="post">
  <input id="upload-form-fileselect" type="file" name="files[]" multiple="multiple">
  <input type="submit" value="送信">
</form>

【※】の部分はファイルの送信先
例えば「/upload.php」に受け取る処理を作るなら「/upload.php」と指定すればOK。
今回はアップロード後の処理まで書きませんが、
とりあえず確認する分には

<?php 
var_dump($_FILES);

とだけ書いたupload.phpでも用意しときゃいいんじゃないかな。

<JS>

$(document).ready(function(){
    $("#upload-area").on("dragover", function(e){
        e.preventDefault();
    });
    $("#upload-area").on("drop", function(e){
        e.preventDefault();
        document.getElementById("upload-form-fileselect").files = e.originalEvent.dataTransfer.files;
    });
});

わーい意外とシンプル。


以下メイキングです。

続きを読む

アイテムをドラッグ&ドロップできるようにする

並べ替えとか、ファイルとフォルダの移動とか、そういうものの実装に使えそうなやつ。
意外と簡単に実装できました。

jquery使ってます。

<HTML>

<span class="drag-item" data-iid="1">アイテム1</span>
<span class="drag-item" data-iid="2">アイテム2</span>

<div class="drop-area" data-aid="1">エリア1</div>
<div class="drop-area" data-aid="2">エリア2</div>

<JS>

setDraggable();
setDroppable();

function setDraggable(){
    $(".drag-item").draggable({
        helper: "clone",
    });
}
function setDroppable(){
    $(".drop-area").droppable({
        accept: ".drag-item",
        tolerance: "pointer",
        drop: function(event, ui) {

            // >>> ドロップしたときの処理 >>>
            var area_id = $(this).data("aid");
            var item_id = $(ui.draggable).data("iid");
            alert("アイテム" + item_id + "を、エリア" + area_id + "にドラッグ&ドロップしました。");
            // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

        }
    });
}

わざわざ関数化しているのは、
「drag-item」「drop-item」をJSの他の処理で後から増やしたとき、
それらを改めてdraggable・droppableにしないと動かないからです。
増やす処理の後でこれらの関数を呼べばOK。

PostgreSQLで配列を使いこなす

配列型、テーブル定義にはあんま使いたくないけど
複雑なクエリ書くときは便利~。

以下のようなテーブル「hoge」について考えます。

type code
1 a
1 b
2 c
2 a
3 b
3 c

実テーブル作るのめんどいからとりあえず一時テーブル。

CREATE TEMPORARY TABLE "hoge" AS
SELECT 1 AS "type", 'a' AS "code" UNION
SELECT 1 AS "type", 'b' AS "code" UNION
SELECT 2 AS "type", 'c' AS "code" UNION
SELECT 2 AS "type", 'a' AS "code" UNION
SELECT 3 AS "type", 'b' AS "code" UNION
SELECT 3 AS "type", 'c' AS "code";

GROUP BYを利用して、特定カラムを配列にまとめる

typeごとにcodeを配列にまとめたもの、を表示するにはこう。

SELECT
  "type"
 ,ARRAY_AGG("code") AS "code_list"
FROM
  "hoge"
GROUP BY
  "type"

結果はこんな形。code_listの部分が文字列配列。

type code_list
1 {a,b}
2 {c,a}
3 {b,c}

配列に対する条件で絞り込みを行う

「codeに○○を含む(or含まない)typeの一覧」
を求めるクエリを何パターンか作ってみました。

SELECT "type"
FROM (
  SELECT   "type", ARRAY_AGG("code") AS "code_list"
  FROM     "hoge"
  GROUP BY "type"
) AS "tmp_hoge"
WHERE 【ここに条件文】

以下、↑のWHEREの部分のみ抜粋。
テストデータが少なかったのでサンプルはちょっと微妙ですが。

「a」を含む
WHERE "code_list" && ARRAY['a'] -- 1,2
WHERE 'a' = ANY("code_list") -- 1,2

どちらの書き方でもOK。
これはもちろん「SELECT DISTINCT "type" FROM "hoge" WHERE "code" = 'a'」でもOK。

「a」または「b」を含む
WHERE "code_list" && ARRAY['a', 'b'] -- 1,2,3

これも「SELECT DISTINCT "type" FROM "hoge" WHERE "code" IN ('a', 'b')」でもOK。

「a」と「b」の両方を含む
WHERE "code_list" @> ARRAY['a', 'b'] -- 1

この辺から、配列を使わない代替SQLが地味にめんどくさくなるやつ。めんどくさいから書かない。

「a」と「b」の両方のみを含む
WHERE "code_list" @> ARRAY['a', 'b']
  AND "code_list" <@ ARRAY['a', 'b'] -- 1

もしかしたらもっとスマートな方法もあるかもしれない。
順序も含めて完全一致かどうかは単純にイコールによる比較でよいので、
並べ替えてから配列化しておくとか。

「a」を含まない
WHERE NOT("code_list" && ARRAY['a']) -- 3

「a」を含む、の逆なのでNOTするだけ。

「a」も「b」も含まない
WHERE NOT("code_list" && ARRAY['a', 'b']) -- (0件)

「a」または「b」を含む、の逆なので(以下略)

「a」と「b」の両方を含むものを除く
WHERE NOT("code_list" @> ARRAY['a', 'b']) -- 2,3

「a」と「b」の両方を含む、の逆(ry

文字列をクリップボードにコピーする関数

…を作ったのでメモ。
テキストエリアを作らないとコピーできないようなのでそのようにしています。

function copyText(text){
    // コピーする文字列を入れるフォーム
    var form = document.createElement("textarea");
    form.textContent = text;

    // bodyタグ内に追加
    $("body")[0].appendChild(form);

    // フォーム内の文字列を選択状態にし、コピー
    form.select();
    var result = document.execCommand('copy');

    // bodyタグ内から削除
    $("body")[0].removeChild(form);

    // コピーに成功した場合はtrue, 失敗した場合はfalse
    return result;
}