tmanaの開発メモ

開発時の記録など書いていく予定です。

【JavaScript】 文字と画像を一緒に入力する

お知らせの内容を作成する場合とかに画像と文字を一緒に入力したかったので、簡易の入力フォーム作成メモ。
Chromeで実行。IEでは動かない。
デモページ作成予定

今回作ったもの

文字と画像を一緒に入力できるフォーム。

準備するもの

今回AngulerJSを使用しましたが、AngularJSがなくてもいけると思います。なので特になし。

ソース

JavaScript

(function() {
    'use strict';

    var app = angular.module('myApp', []);
    app.directive('fileModel',function($parse){
        return{
            restrict: 'A',
            link: function(scope,element,attrs){
                var model = $parse(attrs.fileModel);
                element.bind('change',function(){
                    scope.$apply(function(){
                        model.assign(scope,element[0].files[0]);
                    });
                });
            }
        };
    });

    app.controller('FileController', ['$scope', function($scope) {
        var files = [];

        // imgタグにして返す
        var toImgHtml = function(src) {
            return '<img src="' + src + '" style="max-width: 100%; height: auto;">';
        }

        // imgタグを[!index]に変換する
        var convartImgIntoSymbol = function(htmlStr) {
            var str = htmlStr;
            str = str.replace(/\[/g,'[[').replace(/\]/g,']]');
            var symbolPrefix = '[!';
            var symbolSuffix = ']';
            for (var i in files) {
                while (str.indexOf(toImgHtml(files[i])) != -1) {
                    str = str.replace(toImgHtml(files[i]), symbolPrefix + i + symbolSuffix);
                }
            }
            return str;
        }

        // jqLite用element取得
        var getJqLiteElemById = function(id) {
            var elem = document.getElementById(id);
            return angular.element(elem);
        };

        // htmlタグを除いたテキストを取得
        var getText = function() {
            var str = convartImgIntoSymbol('' + getJqLiteElemById('userText').html());
            var strArray = str.split(/\r\n|\r|\n/);
            str = "";
            for (var i in strArray) {
                str += strArray[i].trim();
            }
            str = str.replace(/<div>/g, '\n');
            var div = document.createElement('div');
            return angular.element(div).html(str).text();
        };

        $scope.log = function() {
            console.log(getText());
        };

        //変化を監視して画像読み込み+表示を実行
        $scope.$watch("file",function(file){
            // 選択画像のリセット
            getJqLiteElemById('inputFile').val('');
            
            $scope.srcUrl = undefined;
            
            //画像ファイルじゃなければ何もしない
            if(!file || !file.type.match("image.*")){
                return;
            }

            var reader = new FileReader();

            //callback
            reader.onload = function(){
                $scope.$apply(function(){
                    $scope.srcUrl = reader.result;
                });
                files.push(reader.result);
                getJqLiteElemById('userText').append('<div>' + toImgHtml(reader.result) + '</div>');
            };

            //read as url(reader.result = url)
            reader.readAsDataURL(file)
        });
    }]);
}());

HTML

<div ng-app="myApp" ng-controller="FileController">
	<div class="input-text" ng-model="hoge" id="userText" contenteditable="true" style="width: 300px; height: 200px; background-color: #eef; border: 1px solid;margin-bottom: 10px;">
		
	</div>
	<label for="inputFile" style="background-color: #ccc;padding: 6px;border-radius: 12px;">
		画像を選択
		<input type="file" id="inputFile" file-model="file" style="display: none;">	
	</label>
	
	<label ng-click="log()" style="background-color: #ccc;padding: 6px;border-radius: 12px;margin-left: 10px;">
		Log
	</label>
</div>

解説

画像ファイルの取得に関してはこちらのサイト様のソースを利用しています。
http://qiita.com/zaburo/items/f03433caa710902d599f

コンテンツの編集はdivタグにcontenteditable属性を付けることで可能にしています(HTML5)。
取得したテキストは、(なぜか)改行にdivタグが使われているのでテキストを取得する際はdivタグを改行文字に変換しています。

str = str.replace(/<div>/g, '\n');

また、取得したテキストに謎の空白が入ったりbrタグが入ったりするので以下の処理を加えています。

謎の空白の削除

var strArray = str.split(/\r\n|\r|\n/);
str = "";
for (var i in strArray) {
	str += strArray[i].trim();
}

htmlタグの削除

angular.element(div).html(str).text();

今回はhtmlタグを除きたかったので上記を使用しましたが、こちらにエスケープとしての機能はないので気を付けて下さい。
エスケープについてはこちらのサイト様が詳しく書いてました。
http://iwb.jp/jquery-javascript-html-escape/

imgタグの変換はサーバの方で文章と画像を別々に取り扱いたかったので書きました。
文章を表示する際に記号と画像を戻してください。

参考サイト

https://blogs.msdn.microsoft.com/osamum/2014/02/13/html5api-contenteditable/

http://curtaincall.weblike.jp/blog/?p=190

http://qiita.com/zaburo/items/f03433caa710902d599f

http://www.todesking.com/blog/2015-02-05-html-escape-with-innertext-and-innerhtml-is-not-enough/