prototype.jsの中身を削る

[新着] TAG indexオフライン版 3.0 を準備中です



0   名前: あまるた : 2007/03/09(金) 10:12  ID:FUlO79v0 sub-tF
タイトルの通りなんですがprototype.jsを使ったタブ切り替えscriptを使っているのですが、

http://a-h.parfe.jp/einfach/archives/2006/1012175043.html

prototype.js自体で70kもあります。このタブ切り替えだけ使えれば良いと思ったのでprototype.jsの中身を削除しまくり現在3800bytes程まで減らしました。特に知識がないので「削除→アップ→削除→アップ・・・」

この繰り返しでタブ切り替えが動かなければ戻す。動いたら又削除の繰り返しです。これは問題ないのでしょうか?どうも画面がカクカク重いので始めたんですが。

以前のversionのものや新しいversionのものでも随分容量が変わっているんですが、容量が重いから処理も重いという事はないんでしょうか?知識LVはhtmlとcssしかなありません。

1   名前: 匿名 : 2007/03/09(金) 10:12  ID:9jO6XtAy sub-kJ
何を削ったか覚えてるなら教えてほしい。差分を取るのが面倒なので。

> どうも画面がカクカク重いので始めたんですが

イベントリスナを除去する処理(Event.unloadCache、Event.stopObserving、その他)を消したのなら、リロードの繰り返しでメモリリークでもしたんじゃなかろうか。

> 容量が重いから処理も重いという事はないんでしょうか

あることはある。だがそれ以上に、文字列処理やループの書き方の影響の方が大きい。

2   名前: 匿名 : 2007/03/09(金) 10:12  ID:9jO6XtAy sub-kJ
あ、

> どうも画面がカクカク重いので始めたんですが

これは削るのを始めた動機で、削った結果ではないのか。誤読ごめん。

3   名前: あまるた : 2007/03/09(金) 10:12  ID:FUlO79v0 sub-tF
コメントありがとう御座います。少し情報が足らなさ過ぎたかもですね。

現状3枚のタブがある状態で完全に読み込みが終わるまで3〜10秒ほどかかります。削除前は画面が真っ白になる事が多々ありましたが現状は真っ白になることはなくなりました。しかし依然画面が重いです。

以下が現在のprototype.jsの状態です。削除し過ぎて不具合が起こっているのかどうかも不明です。これ以上削除する部分があるのか削除し過ぎたのかすら分からない状態ですが一応タブ切り替えは動いてます(汗)
var Class = {
  create: function() {
    return function() { 
      this.initialize.apply(this, arguments);
    }
  }
}

var Abstract = new Object();

Object.extend = function(destination, source) {
  for (property in source) {
    destination[property] = source[property];
  }
  return destination;
}

Function.prototype.bind = function(object) {
  var __method = this;
  return function() {
    __method.apply(object, arguments);
  }
}

function $() {
  var elements = new Array();

  for (var i = 0; i < arguments.length; i++) {
    var element = arguments[i];
    if (typeof element == 'string')
      element = document.getElementById(element);

    if (arguments.length == 1) 
      return element;

    elements.push(element);
  }

  return elements;
}

document.getElementsByClassName = function(className) {
  var children = document.getElementsByTagName('*') || document.all;
  var elements = new Array();
  
  for (var i = 0; i < children.length; i++) {
    var child = children[i];
    var classNames = child.className.split(' ');
    for (var j = 0; j < classNames.length; j++) {
      if (classNames[j] == className) {
        elements.push(child);
        break;
      }
    }
  }
  
  return elements;
}

var Form = {
  serialize: function(form) {
    var elements = Form.getElements($(form));
    var queryComponents = new Array();
    
    for (var i = 0; i < elements.length; i++) {
      var queryComponent = Form.Element.serialize(elements[i]);
      if (queryComponent)
        queryComponents.push(queryComponent);
    }
    
    return queryComponents.join('&');
  },

  reset: function(form) {
    $(form).reset();
  }
}

if (!window.Event) {
  var Event = new Object();
}

Object.extend(Event, {
  KEY_BACKSPACE: 8,
  KEY_TAB:       9,
  KEY_RETURN:   13,
  KEY_ESC:      27,
  KEY_LEFT:     37,
  KEY_UP:       38,
  KEY_RIGHT:    39,
  KEY_DOWN:     40,
  KEY_DELETE:   46,

  observers: false,
  
  _observeAndCache: function(element, name, observer, useCapture) {
    if (!this.observers) this.observers = [];
    if (element.addEventListener) {
      this.observers.push([element, name, observer, useCapture]);
      element.addEventListener(name, observer, useCapture);
    } else if (element.attachEvent) {
      this.observers.push([element, name, observer, useCapture]);
      element.attachEvent('on' + name, observer);
    }
  },  

  observe: function(element, name, observer, useCapture) {
    var element = $(element);
    useCapture = useCapture || false;
    
    if (name == 'keypress' &&
        ((navigator.appVersion.indexOf('AppleWebKit') > 0) 
        || element.attachEvent))
      name = 'keydown';
    
    this._observeAndCache(element, name, observer, useCapture);
  },

  stopObserving: function(element, name, observer, useCapture) {
    var element = $(element);
    useCapture = useCapture || false;
    
    if (name == 'keypress' &&
        ((navigator.appVersion.indexOf('AppleWebKit') > 0) 
        || element.detachEvent))
      name = 'keydown';
    
    if (element.removeEventListener) {
      element.removeEventListener(name, observer, useCapture);
    } else if (element.detachEvent) {
      element.detachEvent('on' + name, observer);
    }
  }
});

4   名前: 匿名 : 2007/03/09(金) 10:12  ID:9jO6XtAy sub-kJ
確認したいのだけど、重いというのは読み込み時?タブ切り替え時?それとも両方?

とりあえず、もし読み込み時の重さの問題なら、getElementsByClassName が修正候補の筆頭。1 回呼び出すたびに、ページ内の全要素を取得し、かつループのたびに(length を確認するために)全要素にアクセスし、さらにその中で同じループを繰り返すのだから。せめて、
for (var i = 0, len = children.length; i < len; i++) {
のようにループ前にスナップショットをとっておこう。ループ内も何とか改善できないものかな。

# Array.forEach か TreeWalker か XPath が使えるなら、そちらで取得した方がはるかに速い。あるいは、DOMNodeInserted イベントを使用して、ノードが読み込まれた時点で、class を確認してキャッシュしてしまうのが一番速いだろう。
# IE は上記のいずれにも対応していない。IE に対しては HTC 化して .tab { behavior: url("tab.htc"); } のようにバインドした方が良いかもしれない。


なお、以下を消してはいけない。IE および Fx 1.5 以前でメモリリークする。
/* prevent memory leaks in IE */
Event.observe(window, 'unload', Event.unloadCache, false);
それから、キーコード指定と Form は不要な気がするが(使ってたっけ?)。

5   名前: 匿名 : 2007/03/09(金) 10:12  ID:9jO6XtAy sub-kJ
書き忘れ。

>>3
> 削除前は画面が真っ白になる事が多々ありましたが

外部スクリプトの読み込みと実行が終わるまで、レンダリングは一時中断される。だから、ファイルサイズを減らす選択には一理ある。
# ちなみに、アクセス解析用の script 要素がファイル先頭に仕込まれている場合、サーバが止まっていると泣きたくなる。

だから、

(a). script 要素をファイル末尾に移動させる。
(b). script 要素に defer 属性をつける(遅延実行)。
(c). script 要素を直接書かず、load イベントで prototype.js と tabMaker.js を読み込ませる(遅延実行)。

など、レンダリングに干渉しないよう工夫する必要もあるかもしれない。

6   名前: あまるた : 2007/03/09(金) 10:12  ID:FUlO79v0 sub-tF
丁寧なご指導ありがとう御座います。もしかしたら「お前全然分かってね〜な」とおっしゃられるかもですが又、削り若干書き換えました。#部分の方はHTC化と言う部分が学んで取り込めそうでしたのでこれから活動してみます。

アクセス解析のスクリプトも最下部に移動させました。loadイベントはonloadなんでしょうか?しかし現状夕方に比べ物凄い軽くなりました。タブ切り替えは元々支障はなかったので、ご指摘の通り読み込み時でしたが早くなり何より読み込み後のカクカクがなくなりました!
var Class = {
  create: function() {
    return function() { 
      this.initialize.apply(this, arguments);
    }
  }
}

var Abstract = new Object();

Object.extend = function(destination, source) {
  for (property in source) {
    destination[property] = source[property];
  }
  return destination;
}

Function.prototype.bind = function(object) {
  var __method = this;
  return function() {
    __method.apply(object, arguments);
  }
}

function $() {
  var elements = new Array();

    for (var i = 0; i < arguments.length; i++) {
    var element = arguments[i];
    if (typeof element == 'string')
      element = document.getElementById(element);

    if (arguments.length == 1) 
      return element;

    elements.push(element);
  }

  return elements;
}

document.getElementsByClassName = function(className) {
  var children = document.getElementsByTagName('*') || document.all;
  var elements = new Array();
  
  for (var i = 0, len = children.length; i < len; i++) {
    var child = children[i];
    var classNames = child.className.split(' ');
    for (var j = 0; j < classNames.length; j++) {
      if (classNames[j] == className) {
        elements.push(child);
        break;
      }
    }
  }
  
  return elements;
}

if (!window.Event) {
  var Event = new Object();
}

Object.extend(Event, {
  observers: false,
  
  _observeAndCache: function(element, name, observer, useCapture) {
    if (!this.observers) this.observers = [];
    if (element.addEventListener) {
      this.observers.push([element, name, observer, useCapture]);
      element.addEventListener(name, observer, useCapture);
    } else if (element.attachEvent) {
      this.observers.push([element, name, observer, useCapture]);
      element.attachEvent('on' + name, observer);
    }
  },  

  observe: function(element, name, observer, useCapture) {
    var element = $(element);
    useCapture = useCapture || false;
    
    if (name == 'keypress' &&
        ((navigator.appVersion.indexOf('AppleWebKit') > 0) 
        || element.attachEvent))
      name = 'keydown';
    
    this._observeAndCache(element, name, observer, useCapture);
  },

  stopObserving: function(element, name, observer, useCapture) {
    var element = $(element);
    useCapture = useCapture || false;
    
    if (name == 'keypress' &&
        ((navigator.appVersion.indexOf('AppleWebKit') > 0) 
        || element.detachEvent))
      name = 'keydown';
    
    if (element.removeEventListener) {
      element.removeEventListener(name, observer, useCapture);
    } else if (element.detachEvent) {
      element.detachEvent('on' + name, observer);
    }
  }
});

/* prevent memory leaks in IE */
Event.observe(window, 'unload', Event.unloadCache, false);

現在は上記の通りです。(大して変わってないかもです・・・。)現在head部分で読み込んでいるjavascriptはtabMaker.jsとprototype.jsだけにして様子見てます。javascriptは難しいですね(汗)

7   名前: 匿名 : 2007/03/09(金) 10:12  ID:9jO6XtAy sub-kJ
> 読み込み後のカクカクがなくなりました!

あ、やっと分かった。カクカクというのは、いったん全ページが素の状態で表示されてから、タブ状態に切り替わることかな。

これは load イベント(レガシー HTML 風に言えば onload)を使う以上、避けられない部分がある。遅延実行の場合はむしろ顕著になるかもしれない。

どうしても嫌なら、ページ読み込み開始と同時にタブ関連部分を { visibility: hidden; } にでもしておいて、タブ準備完了後に元に戻す、のようにするしかないと思う。

Firefox(Opera も? Safari はまだない)ならば、>>4 でも触れたが、DOMNodeInserted イベントで
document.addEventListener (
    'DOMNodeInserted',
    function (event) { event.target; /* 読み込まれたノード */ },
    false
);
読み込まれたノードが次々と通知されてくる。ページ読み込み完了を待たずにノードチェック&キャッシュができる(SAX 風?)ので、getElementsByClassName を使う必要がないし、「カクカク」もたぶん生じない。

IE の場合は HTC 化で回避できるんじゃないかな(だったら、Firefox その他には XBL 化しろということになったりして)。

8   名前: あまるた : 2007/03/09(金) 10:12  ID:FUlO79v0 sub-tF
ご返事大変遅れました(汗)
上記の件改善されましたがPC買い換えてあれこれ検証中です!
HTC化頑張ります!お礼遅れました本当に申し訳ありませんでした!

一覧へ戻る