jQueryはNGと言われた時の便利なメソッド


とあるお仕事で、jQueryなどのライブラリー利用不可というものがありました。その際利用した、jQuery的に使える便利なメソッドをメモしておきます。（※ 昔使っていたものを引っ張りだして改善したものなので、どこかで公開されている可能性があります）

##更新
addClass,removeClassを修正致しました。

##元記事
下記エントリの転載になります。
[jQueryがNGな時に備えて、用意しておきたい便利なメソッド](http://www.rokurofire.info/2015/02/28/nativejs/)

##イベントリスナ

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

```js: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);
	};
}
```

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

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

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

##クラス名操作

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

```js:manageClassname
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チックに使えます。

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

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

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

##UAチェック

以前<a href="http://qiita.com/hiroki_y/items/e7b8d9a4260dbdf21bd7">こちら</a>でも書きましたが再掲です。

```js
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以下のオールドブラウザではまだ必要なんですよね・・・

```js
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の時間単位で、値を変化させています。この部分を増減させることで、処理の滑らかさが調整出来ると思います。

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

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

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

fadeAction({DOMオブジェクト}, props);
```

とりあえず、最も利用頻度の高い、pxと透過度の遷移には対応できています。それ以外のプロパティ（backgroundColorの色変化や、easeなどの指定）はこのままでは出来ません。処理を追加する必要アリ。

##まとめ

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