あと味

たくさん情報を食べて、たくさん発信すると、あとになって味わい深い。

a要素のclass名にpagetopを指定するとスムーススクロールするjsを書いた

しばらくプログラミングしてなかったのですが、最近またJavaScriptの熱が再沸してきました。

AS3を勉強しようとオライリー詳説 ActionScript 3.0を買うも、アクセス制御指定子が序盤から出てきてめんどくせーってなっちゃいました。ECMAScriptやるなら、やっぱJavaScriptがよかです。

一応、制作会社の人間なので、js書くときは以下のことについて気をつける方針です。

  • JavaScript切ってても、コンテンツ閲覧が可能であること
  • onclick等の属性は使わないこと
  • 他のライブラリと名前の干渉をしないこと
  • 極力多くのブラウザで動くこと

言うなれば、閲覧者に迷惑をかけず、デザイナの手間を極力省くって感じです。

で、今回はyoumosにあった、スムーススクロールの実装方法についての記事を参考に、一部リライトして作りました。

さらに、最近ホッテントリにあがっていた、id:nazokingさんの記事を参考にクロージャで処理を書いています。

そのエントリに対するid:Akazaさんのブコメにあるとおり、YUI風ってやつでしょうか?使ってみていろいろと発見があったので、それについては後日記事を書きます。簡単に言うなれば、YUI風の書き方をすると、スコープの使い分けが明確になるし、一目瞭然でした。アマグラマーとしては、このわかりやすさってのがすごく重要です。

仕様とか注意事項とか

  • 下記のソースをコピペして保存したら、head要素内で読み込む。コメントは消してください。
  • a要素のclass名に、pagetopを指定すると、そのa要素がスムーススクロールになる。他のクラス名が入っていてもいい。
  • a要素のhref属性の中にはJavaScriptを切ってるユーザーのために、hoge#とか#を入れておいて欲しい。
  • ie6,7、Firefox3、Safari4Chromeで動作確認。

ソースの中身

// 名前空間を定義。NSはnamespaceの略で書いてあるだけ。
// 任意の名前にすればいいと思います。
if (typeof NS == 'undefined' || !NS) {
  window.NS = {};
}

// NS.utilは他のクラスからもよく使いそうな汎用的な入れ子クラスのつもり。
// 今回はpagetopしか貼ってないけど、実際は他のクラスもあって、それらのクラスもここを使ってる。
// 便利そうな処理はreturnの中にどんどん追加する。
NS.util = function() {
  return {
    // 今回は使ってないけど、このうんこ検知システムはよく使う。
    // うんこは水に流して、なかったことにしたいけど、そうはいかないからなー。
    isMSIE : /*@cc_on!@*/false, 
    
    // 見本のyoumosにもあった、ieとその他ブラウザの仕様差異を吸収する処理。
    addListener : function (el, type, fn, flg) {
      if(!el) { return false; }
      if(el.addEventListener) {
        el.addEventListener(type, fn, flg);
      }
      else if(el.attachEvent) {
        el.attachEvent('on' + type, fn);
      }
      else {
        return false;
      }
    }
  }
}(); // 最後に括弧を入れないと、オブジェクトとして有効にならない。

// ここがスムーススクロールの本体。
NS.pageTop = function() {

  // アニメーションの処理。
  // これを独力で発想するのは、私の頭の仕様上厳しい。
  var scrollUp = function() {
    var position = getPosition();
    // アニメーション動作の微調整(早さとかスクロール量)についてはyoumosの記事を参考にしてください。
    window.scrollTo(Math.max(Math.floor(position.x / 2), 0), Math.max(Math.floor(position.y - (position.y / 5)), 0));
    if (position.x > 0 || position.y > 0) {
      window.setTimeout(scrollUp, 30);
    }
  }
  var getPosition = function() {
    var position = {};
    position.x = document.body.scrollLeft || document.documentElement.scrollLeft;
    position.y = document.body.scrollTop  || document.documentElement.scrollTop;
    return position;
  }
  var getAnchors = function() {
    var anchors = document.getElementsByTagName('a');
    return anchors;
  }
  // mainとか冗長で無駄だとは思うけど、スコープを意識する上では役に立ってる気がしてる。
  var main = function() {
    var anchors = getAnchors();
    for (var i = 0; i < anchors.length; i++) {
      if (anchors[i].className.match(/ *pagetop */)) {
        NS.util.addListener(anchors[i], 'click', scrollUp, false);
        if (anchors[i].getAttribute('href')) {
          anchors[i].setAttribute('href', 'javascript:void(0);');
        }
      }
    }
  }();
}

// 最後にロードイベントに追加。
NS.util.addListener(window, 'load', NS.pageTop , false);

パフォーマンスの問題があるし、いずれにせよprototypeプロパティとかを使った書き方の勉強も必要になると思うけど、YUI風の書き方はいい!わかりやすい!しばらくはこの書き方でいろいろ作ってみようと思います。JavaScriptはある程度までのレベルに達せるよう、今まで以上にJavaScriptに関するエントリを書いていけるといいなと思ってます。

追伸

今さらプログラミングのカテゴリを追加しました。今後はプログラミングに関する記事を書いたらここに保存するようにします。