B-Teck!

お仕事からゲームまで幅広く

【JavaScript/jQuery】要素を切り替えるスライダー

しくみ

f:id:beatdjam:20180226022435p:plain

さんぷる

See the Pen VQEZwM by baetdjam (@beatdjam) on CodePen.

こーど

  • js
$(window).on('load',function(){
    class slider{
        constructor(time, slideId, innerArea, prevBtn, nextBtn){
            this.slideId = slideId;
            this.innerArea = innerArea;
            this.defaultLeftVal = $(this.slideId).css("left");
            this.time = time;
            //ボタン制御
            $(prevBtn).click(()=>{this.move(false)});
            $(nextBtn).click(()=>{this.move(true)});
        }
        move(isNext){
            // 実行中は再度実行させない
            if($(this.slideId).css("left") !== this.defaultLeftVal) return;
            // スクロール領域をli要素数+1の大きさで用意する
            $(this.slideId).css("width", parseInt($(this.slideId).css("width")) * ($(this.innerArea+ " li").length + 1));

            // 押されたボタンによって要素を切り替える
            if(isNext){
                var clone = this.slideId + " li:first-child";
                var insertTo = this.slideId + " li:last-child";
                var left = "-=" + $(this.innerArea).css("width");
                // 次へボタンのときはアニメーション前に要素を追加する
                if(isNext) $(insertTo).after($(clone).clone());
            }else{
                var clone = this.slideId + " li:last-child";
                var insertTo = this.slideId + " li:first-child";
                var left = "+=" + $(this.innerArea).css("width");
            }
            $(this.slideId).one("transitionend webkitTransitionEnd oTransitionEnd mozTransitionEnd",()=>{
              if(!isNext) $(insertTo).before($(clone).clone());
              $(clone).remove();  
              $(this.slideId).css({
                left: this.defaultLeftVal,
                WebkitTransition: "",
                MozTransition: "",
                MsTransition: "",
                OTransition: "",
                transition: ""
              });
            });
            $(this.slideId).css({
              left: left,
              WebkitTransition: "left "+ this.time + "ms linear",
              MozTransition: "left "+ this.time + "ms linear",
              MsTransition: "left "+ this.time + "ms linear",
              OTransition: "left "+ this.time + "ms linear",
              transition: "left "+ this.time + "ms linear"
            });
        }        
    }
    var slide = new slider(600, "#slideArea", "#viewableArea", "#prev", "#next");
});
  • css
/* コメントのある行は変えても問題ない箇所 */
#viewableArea {
    overflow: hidden;
    width: 102px; /* itemのwidth+border(100px + 2px) */
}
#slideArea {
    position: relative;
    left: -102px; /* item一つ分左にずらす(102px) */
    width: 306px; /* li要素の個数分(102px * 3) */
}

#slideArea > ul{
    padding-left: 0; /* デフォルトのスタイルシート削除用 */
    list-style: none;
}
.item {
    background-color: #e7e7e7; /* list領域をわかりやすくするため */
    border:1px solid #000; /* list領域をわかりやすくするため */
    float: left;
    width: 100px; /* 変更可 */
}
  • html
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<div id="viewableArea">
  <div id="slideArea">
    <ul>
      <li class="item">
        hoge
      </li>
      <li class="item">
        fuga
      </li>
      <li class="item">
        hoge2
      </li>
    </ul>     
  </div>  
</div>    
<button id="prev">前へ</button>
<button id="next">次へ</button>

【JavaScript】JavaScriptの変数・関数の巻き上げ(ホイスティング)

ホイスティングとは

JavaScriptは、関数内でvarで宣言した全ての変数が先頭で宣言したことになる。
この、宣言が先頭に移動する仕様をホイスティング、または宣言の巻き上げと呼ぶ。
宣言のみが先頭に移動し、代入された変数は移動しないため、
直感的ではない動作をするケースが有る。

変数の巻き上げ

var scope = 'global';

function test() {
  // 関数内のscopeの宣言のみが巻き上がるので
  // undefinedになる
  console.log(scope); 

  // 宣言は巻き上がっているので代入のみ行われる
  var scope = 'local';
  // 代入されたlocalが出力される
  console.log(scope);
}

test();

見た目上は最初のconsole.log(scope)ではglobalが出力されそうだが、
undefinedが出力される。

その原因は、test()が実際には下記のコードと等価になるせいだ。

function test() {
  // 宣言のみ先頭に移動
  var scope;
  // 値は代入されていないのでundefined
  console.log(scope); 

  scope = 'local';
  // 代入されたlocalが出力される
  console.log(scope);
}

ES2015の場合

実は、ES2015以降ではホイスティングを考慮せず記述することができる。
varではなくletまたはconstで宣言を行うと、宣言より前に使用された変数は
ReferenceErrorが出力されるようになった。

var scope = 'global';

function test() {
  // letで変数が宣言された場合、
  // 宣言は巻き上がらずReferenceErrorとなる
  console.log(scope); 

  let scope = 'local';
  console.log(scope);
}

test();

関数の巻き上げ

関数については、function文で宣言したものについては、その定義ごと巻き上がる。
変数に保持した場合は、変数のときと同様の動作となる。

function文で宣言した場合

定義ごと宣言が巻き上がるため、実行することができる。

function hoge() {
  // fuga()の定義も巻き上がっているので、  
  // 使用することができる。
  fuga();
  function fuga() {
    console.log('fuga');
  }
}

hoge();

varで宣言した場合

宣言のみ巻き上がるが、実行不可能。

function hoge() {
  // fugaの宣言のみ巻き上がるが、
  // undefinedのため関数として実行することができず
  // TypeErrorが発生する
  fuga();

  var fuga = function () {
    console.log('fuga');
  }
}
hoge();

letconstで宣言した場合

巻き上がらないため、実行不可能。

function hoge() {
  // letで宣言しているため定義が巻き上がらず、
  // ReferenceErrorが発生する
  fuga();

  let fuga = () => {
    console.log('fuga');
  }
}

hoge();

【JavaScript】最大値と最小値を指定して範囲内の値を持った配列を作成する(ES2015対応版)

この記事は下記の記事をES2015対応機能でリライトしたものです。 beatdjam.hatenablog.com

/** 
* range
* 範囲内の整数値を持った配列を作成する
* @param {number} max 範囲の最大値
* @param {number} min 範囲の最小値(デフォルト値:0)
* @param {function} filer フィルタ関数(デフォルト値:全てtrue)
* @return {array} 作成した配列
*/
function range(max,min = 0,filter=(e)=>{return true}){
    return Array(max-min+1).fill()
                           .map((v,i)=>{return min+i})
                           .filter(filter);
}
console.log(range(5));
// 第一引数までの配列を作成する
// Array [ 0, 1, 2, 3, 4, 5 ]
console.log(range(5,1));
// 第二引数から第一引数までの配列を作成する
// Array [ 1, 2, 3, 4, 5 ]
console.log(range(5,1,e=>{return e%2 === 0}));
// 第二引数から第一引数までのうち、第三引数の条件に合致する配列を作成する
// Array [ 2, 4 ]
console.log(range(-5,-11));
// 負の値も生成可能
// Array [ -11, -10, -9, -8, -7, -6, -5 ]

前回は(最小値、最大値、フィルタ関数)という引数の並びだったけど、
最大値までの連番を作る方が用途として多かったので、
(最大値、最小値、フィルタ関数)という並びに変更した。

処理の概要は、

  • Array(max-min+1)で必要な長さの配列を生成
  • 配列の要素をfill()undefinedにする ※空要素の場合map()で処理がうまく走らないので必要
  • 最小値+indexの値を各要素に格納
  • フィルタ関数が指定されていなければ全てtrue、
    指定されていればフィルタ関数でfilter()する