225

私は小さな「フローティング ツール ボックス」を持っていposition:fixed; overflow:autoます。うまく動作します。

ただし、そのボックス内を (マウス ホイールで) スクロールし、下部または上部に到達すると、親要素が「スクロール リクエスト」を「引き継ぐ」: ツール ボックスの背後にあるドキュメントがスクロールします。
- これは迷惑であり、ユーザーが「求めた」ものではありません。

私はjQueryを使用しており、event.stoppropagation()でこの動作を停止できると考えました:
$("#toolBox").scroll( function(event){ event.stoppropagation() });

それは機能に入りますが、とにかく伝播が発生します(ドキュメントがスクロール
します)-SO(およびGoogle)でこのトピックを検索するのは驚くほど難しいので、質問する必要があります:
スクロールイベントの伝播/バブリングを防ぐ方法?

編集:
amustill (およびここの mousewheel-plugin の Brandon Aaron のおかげで解決策を実行:
https://github.com/brandonaaron/jquery-mousewheel/raw/master/jquery.mousewheel.js

$(".ToolPage").bind('mousewheel', function(e, d)  
    var t = $(this);
    if (d > 0 && t.scrollTop() === 0) {
        e.preventDefault();
    }
    else {
        if (d < 0 && (t.scrollTop() == t.get(0).scrollHeight - t.innerHeight())) {
            e.preventDefault();
        }
    }
});
4

31 に答える 31

174

@amustill が受け入れた回答では Internet Explorer の問題が正しく解決されないため、完全を期すためにこの回答を追加しています。詳細については、私の元の投稿のコメントを参照してください。さらに、このソリューションにはプラグインは必要ありません。jQuery のみです。

本質的に、コードはmousewheelイベントを処理することによって機能します。このような各イベントには、スクロール可能領域を移動する先wheelDeltaの数に等しい が含まれます。pxこの値が>0の場合、スクロールしてupいます。の場合wheelDelta<0、スクロールしてdownいます。

FireFox : FireFox はをDOMMouseScrollイベントとして使用し、上記とは逆に入力します。通常、 の間隔を返しますが、他のブラウザは の間隔でスクロールを返します(少なくとも私のマシンでは)。修正するには、単純にそれを検出し、乗算して正規化します。originalEvent.detail+/-3120-40

@amustillの回答<div>は、スクロール可能な領域がすでに上部または下部の最大位置にある場合、イベントをキャンセルすることで機能します。ただし、残りのスクロール可能なスペースよりも大きい場合、 Internet Explorerはキャンセルされたイベントを無視します。delta

言い換えると、スクロール可能なコンテンツを含む200px高さがあり、現在がである場合、ブラウザにさらにスクロールするように指示するイベントは、 + >であるため、と の両方でスクロールします。<div>500pxscrollTop400mousewheel120px<div><body>400120500

そのため、問題を解決するには、以下に示すように、少し異なることを行う必要があります。

必要なjQueryコードは次のとおりです。

$(document).on('DOMMouseScroll mousewheel', '.Scrollable', function(ev) {
    var $this = $(this),
        scrollTop = this.scrollTop,
        scrollHeight = this.scrollHeight,
        height = $this.innerHeight(),
        delta = (ev.type == 'DOMMouseScroll' ?
            ev.originalEvent.detail * -40 :
            ev.originalEvent.wheelDelta),
        up = delta > 0;

    var prevent = function() {
        ev.stopPropagation();
        ev.preventDefault();
        ev.returnValue = false;
        return false;
    }

    if (!up && -delta > scrollHeight - height - scrollTop) {
        // Scrolling down, but this will take us past the bottom.
        $this.scrollTop(scrollHeight);
        return prevent();
    } else if (up && delta > scrollTop) {
        // Scrolling up, but this will take us past the top.
        $this.scrollTop(0);
        return prevent();
    }
});

本質的に、このコードは、不要なエッジ条件を作成するスクロール イベントをキャンセルし、jQuery を使用して、イベントが要求した方向に応じて、最大値または最小値のいずれかscrollTopに設定します。<div>mousewheel

いずれの場合もイベントは完全にキャンセルされるため、イベントはまったく伝播さbodyれず、したがって IE および他のすべてのブラウザの問題が解決されます。

jsFiddle にも実際の例を掲載しました。

于 2013-05-01T19:12:45.553 に答える
45

このスレッドで提供されているすべての解決策は、DOM を並べ替えたり、イベントを防止するトリックを使用したりせずに、この問題を解決する既存の (そしてネイティブな) 方法について言及していません。しかし、正当な理由があります。この方法は独自のものであり、MS Web プラットフォームでのみ使用できます。MSDNを引用:

-ms-scroll-chainingプロパティ - ユーザーが操作中にスクロール制限に達したときに発生するスクロール動作を指定します。プロパティ値:

chained - 初期値。ユーザーが操作中にスクロール制限に達すると、最も近いスクロール可能な親要素がスクロールを開始します。バウンス効果は表示されません。

none - ユーザーが操作中にスクロール制限に達すると、バウンス効果が表示されます。

確かに、このプロパティは IE10+/Edge でのみサポートされています。それでも、ここに有力な引用があります:

スクロール チェーンの防止がどれほど人気が​​あるかを理解するために、http アーカイブの簡単な検索によると、「-ms-scroll-chaining: none」は、機能が制限されており、 IE/エッジ。

そして、皆さん、朗報です!Chrome 63 以降、ついに Blink ベースのプラットフォームのネイティブ キュアも用意されました。これは、Chrome (明らかに) と Android WebView (間もなく) の両方です。

紹介記事引用:

overscroll -behaviorプロパティは、コンテナー (ページ自体を含む) をオーバースクロールしたときの動作を制御する新しい CSS 機能です。これを使用して、スクロール チェーンをキャンセルしたり、プルして更新するアクションを無効化/カスタマイズしたり、iOS でラバーバンド効果を無効にしたり (Safari がオーバースクロール動作を実装している場合) したりできます。[...]

このプロパティは、次の 3 つの値を取ります。

auto - デフォルト。要素で発生したスクロールは、祖先要素に伝播する場合があります。

含む- スクロールの連鎖を防ぎます。スクロールは祖先に伝播しませんが、ノード内のローカル効果が表示されます。たとえば、Android のオーバースクロール グロー エフェクトや iOS のラバーバンド エフェクトは、ユーザーがスクロール境界に達したときに通知します。注: html 要素で overscroll-behavior: contain を使用すると、オーバースクロール ナビゲーション アクションが防止されます。

none - contain と同じですが、ノード自体のオーバースクロール効果も防ぎます (例: Android のオーバースクロール グローや iOS のラバーバンド)。

[...]最良の部分は、オーバースクロール動作を使用しても、イントロで言及されたハックのようにページのパフォーマンスに悪影響を及ぼさないことです!

この機能の実際の動作は次のとおりです。対応するCSS モジュールのドキュメントは次のとおりです。

更新:バージョン 59 以降の Firefox がクラブに参加し、MS Edge はバージョン 18 でこの機能を実装する予定です。対応するcaniusageは次のとおりです。

于 2018-01-03T19:40:13.080 に答える
44

Brandon Aaron のMousewheel プラグインを使用すると可能です。

ここにデモがあります: http://jsbin.com/jivutakama/edit?html,js,output

$(function() {

  var toolbox = $('#toolbox'),
      height = toolbox.height(),
      scrollHeight = toolbox.get(0).scrollHeight;

  toolbox.bind('mousewheel', function(e, d) {
    if((this.scrollTop === (scrollHeight - height) && d < 0) || (this.scrollTop === 0 && d > 0)) {
      e.preventDefault();
    }
  });

});
于 2011-04-27T10:53:05.953 に答える
27

私はそれがかなり古い質問であることを知っていますが、これはGoogleでトップの結果の1つであるため... jQueryを使用せずにスクロールバブリングを何らかの形でキャンセルする必要があり、このコードは私のために機能します:

function preventDefault(e) {
  e = e || window.event;
  if (e.preventDefault)
    e.preventDefault();
  e.returnValue = false;  
}

document.getElementById('a').onmousewheel = function(e) { 
  document.getElementById('a').scrollTop -= e. wheelDeltaY; 
  preventDefault(e);
}
于 2012-03-20T11:58:05.317 に答える
13

このような質問はたくさんあり、多くの答えがありますが、イベント、スクリプト、プラグインなどを含まない満足のいく解決策を見つけることができませんでした.HTML と CSS でそれをまっすぐにしたかった. イベント チェーンを壊すためにマークアップを再構築する必要がありましたが、ようやく機能する解決策を見つけました。


1. 基本問題

モーダル要素に適用されたスクロール入力 (例: マウスホイール) は、祖先要素にスピルオーバーし、そのような要素がスクロール可能である場合、同じ方向にスクロールします。

(すべての例は、デスクトップの解像度で表示することを意図しています)

https://jsfiddle.net/ybkbg26c/5/

HTML:

<div id="parent">
  <div id="modal">
    This text is pretty long here.  Hope fully, we will get some scroll bars.
  </div>
</div>

CSS:

#modal {
  position: absolute;
  height: 100px;
  width: 100px;
  top: 20%;
  left: 20%;
  overflow-y: scroll;
}
#parent {
  height: 4000px;
}

2.モーダルスクロールに親スクロールがない

祖先がスクロールしてしまう理由は、スクロール イベントがバブルし、チェーンの一部の要素がそれを処理できるためです。これを止める方法は、チェーン上のどの要素もスクロールの処理方法を認識していないことを確認することです。この例では、ツリーをリファクタリングしてモーダルを親要素の外に移動できます。あいまいな理由で、親とモーダル DOM 兄弟を保持するだけでは十分ではありません。親は、新しいスタッキング コンテキストを確立する別の要素でラップする必要があります。親の周りに絶対配置されたラッパーは、そのトリックを行うことができます。

得られる結果は、モーダルがスクロール イベントを受け取る限り、イベントは「親」要素にバブリングしないということです。

通常、エンド ユーザーの表示に影響を与えることなく、この動作をサポートするように DOM ツリーを再設計できるはずです。

https://jsfiddle.net/0bqq31Lv/3/

HTML:

<div id="context">
  <div id="parent">
  </div>
</div>
<div id="modal">
  This text is pretty long here.  Hope fully, we will get some scroll bars.
</div>

CSS (新規のみ):

#context {
  position: absolute;
  overflow-y: scroll;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
}

3.アップ中はモーダル以外ではスクロールしない

上記の解決策では、スクロール イベントがモーダル ウィンドウによって傍受されない限り (つまり、カーソルがモーダル上にないときにマウスホイールによってトリガーされた場合)、親はスクロール イベントを受け取ることができます。これは望ましくない場合があり、モーダルが起動している間はすべてのバックグラウンド スクロールを禁止したい場合があります。これを行うには、モーダルの背後にあるビューポート全体にまたがる追加のスタック コンテキストを挿入する必要があります。必要に応じて完全に透明にすることができる絶対配置オーバーレイを表示することでこれを行うことができます (しかし ではありませんvisibility:hidden)。

https://jsfiddle.net/0bqq31Lv/2/

HTML:

<div id="context">
  <div id="parent">
  </div>
</div>
<div id="overlay">  
</div>
<div id="modal">
  This text is pretty long here.  Hope fully, we will get some scroll bars.
</div>

CSS (#2 の上に追加):

#overlay {
  background-color: transparent;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
}
于 2015-12-24T10:12:17.627 に答える
11

プレーンな JavaScript バージョンを次に示します。

function scroll(e) {
  var delta = (e.type === "mousewheel") ? e.wheelDelta : e.detail * -40;
  if (delta < 0 && (this.scrollHeight - this.offsetHeight - this.scrollTop) <= 0) {
    this.scrollTop = this.scrollHeight;
    e.preventDefault();
  } else if (delta > 0 && delta > this.scrollTop) {
    this.scrollTop = 0;
    e.preventDefault();
  }
}
document.querySelectorAll(".scroller").addEventListener("mousewheel", scroll);
document.querySelectorAll(".scroller").addEventListener("DOMMouseScroll", scroll);
于 2016-03-18T23:53:57.263 に答える
9

バリアントとして、scrollまたはmousewheel処理のパフォーマンスの問題を回避するために、以下のようなコードを使用できます。

CSS:

body.noscroll {
    overflow: hidden;
}
.scrollable {
    max-height: 200px;
    overflow-y: scroll;
    border: 1px solid #ccc;
}

html:

<div class="scrollable">
...A bunch of items to make the div scroll...
</div>
...A bunch of text to make the body scroll...

js:

var $document = $(document),
    $body = $('body'),
    $scrolable = $('.scrollable');

$scrolable.on({
          'mouseenter': function () {
            // add hack class to prevent workspace scroll when scroll outside
            $body.addClass('noscroll');
          },
          'mouseleave': function () {
            // remove hack class to allow scroll
            $body.removeClass('noscroll');
          }
        });

作品例:http: //jsbin.com/damuwinarata/4

于 2014-09-10T14:33:52.513 に答える
6

誰かがまだこれに対する解決策を探している場合、次のプラグインが仕事をしますhttp://mohammadyounes.github.io/jquery-scrollLock/

特定のコンテナー内でマウス ホイール スクロールをロックする問題に完全に対処し、それが親要素に伝播されないようにします。

ホイールのスクロール速度は変わらず、ユーザー エクスペリエンスには影響しません。OS のマウス ホイールの垂直スクロール速度に関係なく、同じ動作が得られます (Windows では、1 ノッチあたり最大 100 行まで 1 画面または 1 行に設定できます)。

デモ: http://mohammadyounes.github.io/jquery-scrollLock/example/

ソース: https://github.com/MohammadYounes/jquery-scrollLock

于 2014-10-24T12:47:17.747 に答える
4

ノックアウトハンドラーとしてのamustillの答え:

ko.bindingHandlers.preventParentScroll = {
    init: function (element, valueAccessor, allBindingsAccessor, context) {
        $(element).mousewheel(function (e, d) {
            var t = $(this);
            if (d > 0 && t.scrollTop() === 0) {
                e.preventDefault();
            }
            else {
                if (d < 0 && (t.scrollTop() == t.get(0).scrollHeight - t.innerHeight())) {
                    e.preventDefault();
                }
            }
        });
    }
};
于 2013-03-18T14:45:23.240 に答える
4

これは実際に AngularJS で機能します。Chrome と Firefox でテスト済み。

.directive('stopScroll', function () {
    return {
        restrict: 'A',
        link: function (scope, element, attr) {
            element.bind('mousewheel', function (e) {
                var $this = $(this),
                    scrollTop = this.scrollTop,
                    scrollHeight = this.scrollHeight,
                    height = $this.height(),
                    delta = (e.type == 'DOMMouseScroll' ?
                    e.originalEvent.detail * -40 :
                        e.originalEvent.wheelDelta),
                    up = delta > 0;

                var prevent = function() {
                    e.stopPropagation();
                    e.preventDefault();
                    e.returnValue = false;
                    return false;
                };

                if (!up && -delta > scrollHeight - height - scrollTop) {
                    // Scrolling down, but this will take us past the bottom.
                    $this.scrollTop(scrollHeight);
                    return prevent();
                } else if (up && delta > scrollTop) {
                    // Scrolling up, but this will take us past the top.
                    $this.scrollTop(0);
                    return prevent();
                }
            });
        }
    };
})
于 2015-06-24T19:32:59.797 に答える
2

同様の状況があり、これを解決した方法は次のとおりです。
スクロール可能なすべての要素がクラスscrollableを取得します。

$(document).on('wheel', '.scrollable', function(evt) {
  var offsetTop = this.scrollTop + parseInt(evt.originalEvent.deltaY, 10);
  var offsetBottom = this.scrollHeight - this.getBoundingClientRect().height - offsetTop;

  if (offsetTop < 0 || offsetBottom < 0) {
    evt.preventDefault();
  } else {
    evt.stopImmediatePropagation();
  }
});

stopImmediatePropagation()は、スクロール可能な子領域から親のスクロール可能な領域をスクロールしないようにします。

これは、バニラの JS 実装です: http://jsbin.com/lugim/2/edit?js,output

于 2014-08-21T15:06:49.483 に答える
2

ここに新しいウェブ開発者。これは、IE と Chrome の両方で魅力的に機能しました。

static preventScrollPropagation(e: HTMLElement) {
    e.onmousewheel = (ev) => {
        var preventScroll = false;
        var isScrollingDown = ev.wheelDelta < 0;
        if (isScrollingDown) {
            var isAtBottom = e.scrollTop + e.clientHeight == e.scrollHeight;
            if (isAtBottom) {
                preventScroll = true;
            }
        } else {
            var isAtTop = e.scrollTop == 0;
            if (isAtTop) {
                preventScroll = true;
            }
        }
        if (preventScroll) {
            ev.preventDefault();
        }
    }
}

行数に惑わされないでください。非常に単純です。読みやすくするために少し冗長です (自己文書化コードですよね?)。

また、ここで使用する言語はTypeScriptですが、いつものように JS に変換するのは簡単です。

于 2015-03-19T21:18:30.137 に答える
2

私のjQueryプラグイン:

$('.child').dontScrollParent();

$.fn.dontScrollParent = function()
{
    this.bind('mousewheel DOMMouseScroll',function(e)
    {
        var delta = e.originalEvent.wheelDelta || -e.originalEvent.detail;

        if (delta > 0 && $(this).scrollTop() <= 0)
            return false;
        if (delta < 0 && $(this).scrollTop() >= this.scrollHeight - $(this).height())
            return false;

        return true;
    });
}
于 2012-09-14T14:28:28.723 に答える
1

MooTools を使用している場合は、同等のコードを次に示します。

            'mousewheel': function(event){
            var height = this.getSize().y;
            height -= 2;    // Not sure why I need this bodge
            if ((this.scrollTop === (this.scrollHeight - height) && event.wheel < 0) || 
                (this.scrollTop === 0 && event.wheel > 0)) {
                event.preventDefault();
            }

私は、他の人たちと同じように、値を数ピクセル単位で微調整しなければならなかったことに注意してください。これが、高さ -= 2 の目的です。

基本的に主な違いは、MooTools では、イベントに渡される追加のパラメーターではなく、event.wheel からデルタ情報が取得されることです。

また、このコードを何かにバインドすると問題が発生しました (バインドされた関数の event.target.scrollHeight は、バインドされていない関数の this.scrollHeight と等しくありません)。

この投稿が私を助けたのと同じくらい、これが誰かを助けることを願っています;)

于 2011-12-11T12:14:01.953 に答える
0

Internet Explorer の自然なスクロールをエミュレートする jQuery プラグイン

  $.fn.mousewheelStopPropagation = function(options) {
    options = $.extend({
        // defaults
        wheelstop: null // Function
        }, options);

    // Compatibilities
    var isMsIE = ('Microsoft Internet Explorer' === navigator.appName);
    var docElt = document.documentElement,
        mousewheelEventName = 'mousewheel';
    if('onmousewheel' in docElt) {
        mousewheelEventName = 'mousewheel';
    } else if('onwheel' in docElt) {
        mousewheelEventName = 'wheel';
    } else if('DOMMouseScroll' in docElt) {
        mousewheelEventName = 'DOMMouseScroll';
    }
    if(!mousewheelEventName) { return this; }

    function mousewheelPrevent(event) {
        event.preventDefault();
        event.stopPropagation();
        if('function' === typeof options.wheelstop) {
            options.wheelstop(event);
        }
    }

    return this.each(function() {
        var _this = this,
            $this = $(_this);
        $this.on(mousewheelEventName, function(event) {
            var origiEvent = event.originalEvent;
            var scrollTop = _this.scrollTop,
                scrollMax = _this.scrollHeight - $this.outerHeight(),
                delta = -origiEvent.wheelDelta;
            if(isNaN(delta)) {
                delta = origiEvent.deltaY;
            }
            var scrollUp = delta < 0;
            if((scrollUp && scrollTop <= 0) || (!scrollUp && scrollTop >= scrollMax)) {
                mousewheelPrevent(event);
            } else if(isMsIE) {
                // Fix Internet Explorer and emulate natural scrolling
                var animOpt = { duration:200, easing:'linear' };
                if(scrollUp && -delta > scrollTop) {
                    $this.stop(true).animate({ scrollTop:0 }, animOpt);
                    mousewheelPrevent(event);
                } else if(!scrollUp && delta > scrollMax - scrollTop) {
                    $this.stop(true).animate({ scrollTop:scrollMax }, animOpt);
                    mousewheelPrevent(event);
                }
            }
        });
    });
};

https://github.com/basselin/jquery-mousewheel-stop-propagation/blob/master/mousewheelStopPropagation.js

于 2014-02-02T08:39:13.257 に答える
0

scrollTopスクロール可能な要素の上にマウスを置いたときに親をロックする面白いトリックもあります。この方法では、独自のホイール スクロールを実装する必要はありません。

ドキュメントのスクロールを防止する例を次に示しますが、任意の要素に対して調整できます。

scrollable.mouseenter(function ()
{
  var scroll = $(document).scrollTop();
  $(document).on('scroll.trap', function ()
  {
    if ($(document).scrollTop() != scroll) $(document).scrollTop(scroll);
  });
});

scrollable.mouseleave(function ()
{
  $(document).off('scroll.trap');
});
于 2016-02-15T16:34:15.033 に答える
0

overflow: hidden;で使用しないでくださいbody。すべてを自動的に一番上にスクロールします。JavaScriptも必要ありません。利用するoverflow: auto;

HTML 構造

<div class="overlay">
    <div class="overlay-content"></div>
</div>

<div class="background-content">
    lengthy content here
</div>

スタイリング

.overlay{
    position: fixed;
    top: 0px;
    left: 0px;
    right: 0px;
    bottom: 0px;
    background-color: rgba(0, 0, 0, 0.8);

    .overlay-content {
        height: 100%;
        overflow: scroll;
    }
}

.background-content{
    height: 100%;
    overflow: auto;
}

こちらのデモで遊んでください。

于 2014-11-11T09:19:20.560 に答える
-2

この方法で試すことができます:

$('#element').on('shown', function(){ 
   $('body').css('overflow-y', 'hidden');
   $('body').css('margin-left', '-17px');
});

$('#element').on('hide', function(){ 
   $('body').css('overflow-y', 'scroll');
   $('body').css('margin-left', '0px');
});
于 2012-07-23T13:40:42.643 に答える