Category : JavaScript

フリーランスになってから半年以上が経ちました。お仕事に埋もれる毎日ですが、お陰様でなんとかやっております。

様々なお仕事を請けていて感じたのですが、JavaScriptのライブラリー利用不可なケースが割とあるように思えます。BtoB系やプラットフォームに近いサービスに多いのではないでしょうか。その際利用した、jQuery的に使えるネイティブメソッドをメモしておきます。(※ 昔使っていたものを引っ張りだして改修したものなので、どこかで公開されている可能性があります)

イベントリスナ

jQueryでいうところの、.on() に該当するものです。 “addEventListener” か “attachEvent” かを判定し、addEventメソッドを定義しています。

var addEvent;

if (window.addEventListener) {
	addEvent = function (target, name, fn) {
		if (!name) { return false; }
		target.addEventListener(name, fn, false);
	};
} else if (window.attachEvent) {
	addEvent = function (target, name, fn) {
		if (!name) { return false; }
		target.attachEvent('on' + name, fn);
	};
}

こんな感じで、イベントを追加します。

addEvent({DOMオブジェクト}, 'click', {メソッド名});

addEvent({DOMオブジェクト}, 'touchend', function() {
	/* hogehoge */
});

クラス名操作

jQueryでいうところの、addClass、removeClass、hasClass にあたります。

var addClass,
	removeClass,
	hasClass,
	rclass = /[\t\r\n\f]/g;

/* クラス名追加 */
addClass = function (tgt, name) {
	var src = ' ' + tgt.className.replace(rclass, ' ') + ' ';
	if (src.indexOf(' ' + name + ' ') >= 0) { return false; }
	tgt.className += ' ' + name;
	return true;
};

/* クラス名削除 */
removeClass = function (tgt, name) {
	var src = ' ' + tgt.className.replace(rclass, ' ') + ' ';
	var dst = src.replace(' ' + name + ' ', ' ');
	tgt.className = dst.replace(/^\s+/, '').replace(/\s+$/, '');
	return (src !== dst);
};

/* クラス名有無チェック */
hasClass = function (tgt, name) {
	var className = ' ' + name + ' ',
		l = tgt.length;
	if ((' ' + tgt.className + ' ').replace(rclass, ' ').indexOf(className) >= 0) {
		return true;
	}
	return false;
};

使い方は下記のような感じです。これもjQueryチックに使えます。

/* クラス名追加 */
addClass({DOMオブジェクト}, 'className');

/* クラス名削除 */
removeClass({DOMオブジェクト}, 'className');

/* クラス名有無チェック */
hasClass({DOMオブジェクト}, 'className');

UAチェック

以前こちらでも書きましたが再掲です。

var checkUA, uaDevice, uaBrouser;

/* UAチェック */
checkUA = function () {
    var userAgent = navigator.userAgent.toLowerCase();
    var appVersion = navigator.appVersion.toLowerCase();
    /* デバイス判定 */
    if (userAgent.indexOf('iphone') > 0 || userAgent.indexOf('ipod') > 0 || userAgent.indexOf('android') > 0) {
        uaDevice = 'device-mobile';
    }
    /* ブラウザ判定 */
    if (userAgent.indexOf('opera') !== -1) {
        uaBrouser = 'opera';
    } else if (userAgent.indexOf("msie") !== -1) {
        if (appVersion.indexOf("msie 6.") !== -1) {
            uaBrouser = 'ie6';
        } else if (appVersion.indexOf("msie 7.") !== -1) {
            uaBrouser = 'ie7';
        } else if (appVersion.indexOf("msie 8.") !== -1) {
            uaBrouser = 'ie8';
        } else if (appVersion.indexOf("msie 9.") !== -1) {
            uaBrouser = 'ie9';
        } else {
            uaBrouser = 'ie';
        }
    } else if (userAgent.indexOf('trident') !== -1) {
        uaBrouser = 'ie11';
    } else if (userAgent.indexOf('chrome') !== -1) {
        uaBrouser = 'chrome';
    } else if (userAgent.indexOf('safari') !== -1) {
        uaBrouser = 'safari';
    } else if (userAgent.indexOf('firefox') !== -1) {
        uaBrouser = 'firefox';
    } else {
        uaBrouser = false;
    }
};

アニメーション、フェード

jQueryのanimate()、fadeIn()、fadeOut() にあたるものです。
モダンブラウザはCSSアニメーション中心なので利用することはありませんが、IE9以下のオールドブラウザではまだ必要なんですよね・・・

var repeatAction, fadeAction;

/* アニメーションのリピート処理 */
repeatAction = function (tgt, obj, countObj) {
    var csstxt = '';
    
    countObj.num += countObj.addcnt;
    if (countObj.direction === 'plus') {
        countObj.propnum += countObj.addcnt;
    } else {
        countObj.propnum -= countObj.addcnt;
    }
    
    if (countObj.num > countObj.countlimit) {
        csstxt = obj.selector + ": " + obj.end + countObj.unitname + ";";
        if (obj.selector === 'opacity') {
            csstxt += ' ' + '-ms-filter: "alpha(opacity=' + (obj.end * 100) + ')";' + ' filter: alpha( opacity=' + (obj.end * 100) + ' );';
        }
        tgt.style.cssText = csstxt;
        return true;
    } else {
        csstxt = obj.selector + ": " + countObj.propnum + countObj.unitname + ";";
        if (obj.selector === 'opacity') {
            csstxt += " " + '-ms-filter: "alpha(opacity=' + (countObj.propnum * 100) + ')";' + ' filter: alpha( opacity=' + (countObj.propnum * 100) + ' );';
        }
        tgt.style.cssText = csstxt;
        /* 繰り返し */
        setTimeout(function () {
            repeatAction(tgt, obj, countObj);
        }, countObj.repeattime);
    }
};

/* アクションスタート */
fadeAction = function (tgt, obj) {
    var countObj = {
        num: 0,  // 絶対値のスタート数値
        propnum: obj.start, // スタイルのスタート数値
        countlimit: Math.abs(obj.start - obj.end), // 最初と最後の差の絶対値
        repeattime: obj.duration / 10,   // 毎時処理を行う時間(ms)
        unitname: obj.unitname
    };
    countObj.addcnt = countObj.countlimit / 10;  // 毎時増加する値の絶対値
    
    // プラス方向かマイナス方向か
    if (obj.start - obj.end < 0) {
        countObj.direction = 'plus';
    } else {
        countObj.direction = 'minus';
    }
    
    tgt.style.cssText = obj.selector + ": " + obj.start + ";";
    repeatAction(tgt, obj, countObj);
    return false;
};

指定した最初と最後の値の絶対値を求め、その値を元に処理回数と処理時間が決定します。本コードでは、指定した処理時間に対して、10分の1の時間単位で、値を変化させています。この部分を増減させることで、処理の滑らかさが調整出来ると思います。

実行は下記のような感じです。

/* だんだん透過 */
var props = {
    selector: 'opacity',	// セレクタ名
    start: 1,				// 開始数値
    end: 0,				// 終了数値
    duration: 260,		// アニメーション時間
    unitname: ''			// 単位名("px" など)
};
fadeAction({DOMオブジェクト}, props);

/* bottom位置移動 */
var props = {
    selector: 'bottom',	// セレクタ名
    start: 0,				// 開始数値
    end: 300,				// 終了数値
    duration: 200,		// アニメーション時間
    unitname: 'px'			// 単位名("px" など)
};
fadeAction({DOMオブジェクト}, props);

// ※ backgroundColorなどの色変化や、変化の仕方(easeなどの指定)はこのままでは出来ません。処理を追加する必要アリ

まとめ

jQueryはNG!と言われても、とりあえずこれらのメソッドがあれば、ネイティブでもいつもどおりの感覚で書けるようになると思います。IE9以下でカルーセルを求められた時も、上記メソッドを利用すれば(頑張れば)実装できるかと。

たまにネイティブで書くと新鮮で面白いです。これまでどれだけフレームワークに頼っていたのか、自分の素の実力がどの程度なのかが分かりますね。「使っちゃダメ」と言われると、逆にテンションがあがってしまうのはどうかと思いますが、私は制限があると楽しくなってしまうMタイプのようです。どうでもいいですね。