30740

[code]

    var f = $.fn.scrollTop;
    $.fn.scrollTop = function()
    {
      if (this[0] === window)
      {
        return f.apply($('.b'));
      }
    
      return f.apply(this);
    };
    
[/code]

`$(window).scrollTop()`というようにターゲットが `$(window)` の場合、`$('.b')`
に変更するよう上書きしたい場合、上記のような書き方でいいのでしょうか？


CC BY-SA 3.0
2016-11-27T19:56:59.910
30733
2016-11-28T06:07:25.270
19687
question
javascript jquery
jQueryの関数を一部変更したいとき


`$()`関数には、第二引数（コンテキストの指定）もありえるので、`f.apply(this, arguments)`などとしておいたほうが良いと思います。  
それ以外は問題なさそうかな？


CC BY-SA 3.0
2016-11-28T03:48:46.963
30739
2016-11-28T03:48:46.963
8078
30733
answer


引数なしの `scrollTop()` で値の取得しかしないのなら、大丈夫だと思います。

引数を渡す（スクロール位置の設定を行う）場合には次の問題があります。

  * 対象が window かどうかに関わらず、元の `scrollTop()` に引数を渡していません  
→ `f.apply(..., arguments)` などとしましょう

  * 引数を渡した `scrollTop()` は元のjQueryオブジェクトを返すべきですが、置き換え後の jQuery オブジェクト、例えば `$(window)` ではなく `$('.b')` を返してしまいます  
→ この場合は `scrollTop()` の戻り値を無視して、自身のthisを返した方がいいでしょう

  * jQueryオブジェクトが複数の要素を選択している場合が考慮されていません  
→ `this.map(...)` などで、各要素に対して個別に検査・置き換えをしましょう

最後の点について具体例を挙げてみます。次のコードは説明のために質問とは少し設定を変えていますが、他2つの問題については修正しています。

[code]

    var f = $.fn.scrollLeft;
    $.fn.scrollLeft = function()
    {
      if (this[0] === $('.a')[0])
      {
        if (arguments.length === 0) {
          return f.apply($('.b'));
        } else {
          f.apply($('.b'), arguments);
          return this;
        }
      }
    
      return f.apply(this, arguments);
    };
    
    $('.a, .c').scrollLeft(20);
    
    // .a と .c に対して設定しようとしているので、.a -> .b と置き換えて
    // .b と .c が対象になってほしい
    // ...だが実際には .b にしか scrollLeft が設定されない
[/code]

[code]

    div{
      width: 30px;
      height: 50px;
      overflow-x: scroll;
    }
[/code]

[code]

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <div class="a">abcdefg</div>
    <div class="b">abcdefg</div>
    <div class="c">abcdefg</div>
[/code]

まあ今回の場合は window と何かを組み合わせることはなさそうなので、実際に問題になることはないかもしれませんが、一応修正例も挙げておきます。

[code]

    var f = $.fn.scrollLeft;
    $.fn.scrollLeft = function()
    {
      var target = this.map(function() {
        return (this === $('.a')[0]) ? $('.b').toArray() : this;
      });
      
      if (arguments.length === 0) {
        return f.apply(target);
      } else {
        f.apply(target, arguments);
        return this;
      }
    };
    
    $('.a, .c').scrollLeft(20);
[/code]

[code]

    div{
      width: 30px;
      height: 50px;
      overflow-x: scroll;
    }
[/code]

[code]

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <div class="a">abcdefg</div>
    <div class="b">abcdefg</div>
    <div class="c">abcdefg</div>
[/code]


CC BY-SA 3.0
2016-11-28T06:07:25.270
30740
2016-11-28T06:07:25.270
8000
30733
answer