web-dev-qa-db-ja.com

ユーザーがスクロールバーまたはコンテンツ(ネイティブスクロールバーのonclick)をクリックするかどうかを決定します

私はスクロールバーがクリックされたときに検出することになっているJQueryでカスタムイベントを作成しようとしています1
テキストはたくさんありますが、質問はすべて太字で書かれており、 JSFiddleの例 すぐに作業できます。

この機能が組み込まれていないため、
要素にスクロールバーがあるかどうかを確認して、hasScroll関数を作成する必要がありました。

$.fn.hasScroll = function(axis){
    var overflow = this.css("overflow"),
        overflowAxis;

    if(typeof axis == "undefined" || axis == "y") overflowAxis = this.css("overflow-y");
    else overflowAxis = this.css("overflow-x");

    var bShouldScroll = this.get(0).scrollHeight > this.innerHeight();

    var bAllowedScroll = (overflow == "auto" || overflow == "visible") ||
                         (overflowAxis == "auto" || overflowAxis == "visible");

    var bOverrideScroll = overflow == "scroll" || overflowAxis == "scroll";

    return (bShouldScroll && bAllowedScroll) || bOverrideScroll;
};

inScrollRange関数、実行されたクリックがスクロール範囲内であったかどうかを確認します。

var scrollSize = 18;

function inScrollRange(event){
    var x = event.pageX,
        y = event.pageY,
        e = $(event.target),
        hasY = e.hasScroll(),
        hasX = e.hasScroll("x"),
        rX = null,
        rY = null,
        bInX = false,
        bInY = false

    if(hasY){
        rY = new RECT();
        rY.top = e.offset().top;
        rY.right = e.offset().left + e.width();
        rY.bottom = rY.top +e.height();
        rY.left = rY.right - scrollSize;

        //if(hasX) rY.bottom -= scrollSize;
        bInY = inRect(rY, x, y);
    }

    if(hasX){
        rX = new RECT();
        rX.bottom = e.offset().top + e.height();
        rX.left = e.offset().left;
        rX.top = rX.bottom - scrollSize;
        rX.right = rX.left + e.width();

        //if(hasY) rX.right -= scrollSize;
        bInX = inRect(rX, x, y);
    }

    return bInX || bInY;
}

すべてのスクロールバーのサイズは均一ですか?たとえば、FirefoxでIE 18ピクセルです。
カスタマイズされたスクロールバーがないと仮定すると、一部のブラウザーには余分なパディングやサイズがありますか?

これらの機能はすべて、意図したとおりに機能します(私が認識できることから)。

カスタムイベントを作成するのは少し面倒でしたが、やや機能するようになりました。唯一の問題は、クリックされた要素にmousedown/upイベントが関連付けられている場合、それもトリガーされることです。

私が呼んでいるmousedownScroll/mouseupScrollイベントを同時にトリガーしながら、他のイベントのトリガーを止めることはできないようです。

$.fn.mousedownScroll = function(fn, data){
    if(typeof fn == "undefined" && typeof data == "undefined"){
        $(this).trigger("mousedownScroll");
        return;
    }

    $(this).on("mousedownScroll", data, fn);
};

$.fn.mouseupScroll = function(fn, data){
    if(typeof fn == "undefined" && typeof data == "undefined"){
        $(this).trigger("mouseupScroll");
        return;
    }

    $(this).on("mouseupScroll", data, fn);
};

$(document).on("mousedown", function(e){
    if(inScrollRange(e)){
        $(e.target).trigger("mousedownScroll");
    }
});

$(document).on("mouseup", function(e){
    if(inScrollRange(e)){
        $(e.target).trigger("mouseupScroll");
    }
});

$("selector").mousedown(function(e){
    console.log("Clicked content."); //Fired when clicking scroller as well
});

$("selector").mousedownScroll(function(e){
    console.log("Clicked scroller.");
});

他の「クリック」イベントのトリガーを停止するにはどうすればよいですか?

お願いしている間、可能な限りコードを最適化してください。

混乱させるJSFiddleです。

私がこれを作っている理由は、私が開発しているより大きなプラグインのためです。いずれかのスクローラーを右クリックすると、カスタムコンテキストメニューが表示されます。私はそれを望んでいません。そこで、スクロールクリック(マウスアップ/ダウン)をチェックして、コンテキストメニューが表示されないようにするイベントを作成する必要があると考えました。ただし、そのためには、通常のクリックの前にスクロールクリックが必要です。また、可能であれば、通常のクリックの発生を停止します。

私はここで大声で考えていますが、要素にバインドされているすべての関数を取得し、それらの順序を切り替える方法があるかもしれません追加されましたか?関数は追加された順序で実行されることを知っています(最初に追加され、最初に呼び出されます)。


1 スクロールバーをクリックしてもクリックはトリガーされないため、mousedown/mouseupのみを使用できます。これが偽の場合、実際のサンプル/コードを提供してください

50
ShadowScripter

このハックを使用する可能性があります。

mousedownイベントとmouseupイベントをハイジャックし、カスタムパワード機能でスクロールバーをクリックするときにそれらを回避することを試みることができます。

$.fn.mousedown = function(data, fn) {
    if ( fn == null ) {
        fn = data;
        data = null;
    }    
    var o = fn;
    fn = function(e){
        if(!inScrollRange(e)) {
            return o.apply(this, arguments);
        }
        return;
    };
    if ( arguments.length > 0 ) {
        return this.bind( "mousedown", data, fn );
    } 
    return this.trigger( "mousedown" );
};

mousedownScrollおよびmouseupScrollイベントの逆。

$.fn.mousedownScroll = function(data, fn) {
    if ( fn == null ) {
        fn = data;
        data = null;
    }    
    var o = fn;
    fn = function(e){
        if(inScrollRange(e)) {
            e.type = "mousedownscroll";
            return o.apply(this, arguments);
        }
        return;
    };
    if ( arguments.length > 0 ) {
        return this.bind( "mousedown", data, fn );
    } 
    return this.trigger( "mousedown" );
};

ところで、スクロールバーの幅はOSの設定だと思います。

15
Alexander

解決済み:

IE、Firefox、Chromeでテストした、スクロールバーの最短クリック検出。

var clickedOnScrollbar = function(mouseX){
  if( $(window).outerWidth() <= mouseX ){
    return true;
  }
}

$(document).mousedown(function(e){
  if( clickedOnScrollbar(e.clientX) ){
    alert("clicked on scrollbar");
  }
});

作業例: https://jsfiddle.net/s6mho19z/

23

以前のプロジェクトでも同じ問題が発生しました。このソリューションをお勧めします。それはあまりきれいではありませんが、動作し、私たちはhtmlでもっと良くできるとは思いません。私のソリューションの2つのステップは次のとおりです。

1。デスクトップ環境でスクロールバーの幅を測定します。

これを実現するには、アプリケーションの起動時に次のことを実行します。

本体に次の要素を追加します。

<div style='width: 50px; height: 50px; overflow: scroll'><div style='height: 1px;'/></div>

JQUeryの.width()を使用して、以前に追加した要素の内部divのwithを測定し、スクロールバーの幅をどこかに保存します(scollbarの幅は50-内部divのwith)

スクロールバーの測定に使用した余分な要素を削除します(結果が得られたので、本文に追加した要素を削除します)。

これらの手順はすべてユーザーに表示されるべきではなく、OSのスクロールバーの幅があります。

たとえば、次のスニペットを使用できます。

var measureScrollBarWidth = function() {
    var scrollBarMeasure = $('<div />');
    $('body').append(scrollBarMeasure);
    scrollBarMeasure.width(50).height(50)
        .css({
            overflow: 'scroll',
            visibility: 'hidden',
            position: 'absolute'
        });

    var scrollBarMeasureContent = $('<div />').height(1);
    scrollBarMeasure.append(scrollBarMeasureContent);

    var insideWidth = scrollBarMeasureContent.width();
    var outsideWitdh = scrollBarMeasure.width();
    scrollBarMeasure.remove();

    return outsideWitdh - insideWidth;
};

2。クリックがスクロールバー上にあるかどうかを確認します。

スクロールバーの幅がわかったので、イベントの座標を使用して、スクロールバーの位置の長方形とイベントの座標を比較して、素晴らしいことを実行できます...

クリックをフィルタリングする場合は、ハンドラーでfalseを返し、伝播を防ぐことができます。

9
Samuel Rossille

Scollareaのコンテンツが親divを完全に[オーバー]満たしていることを確認してください。

次に、コンテンツのクリックとコンテナーのクリックを区別できます。

html:

<div class='test container'><div class='test content'></div></div>
<div id="results">please click</div>

css:

#results {
  position: absolute;
  top: 110px;
  left: 10px;
}

.test {
  position: absolute;
  left: 0;
  top: 0;
  width: 100px;
  height: 100px;
  background-color: green;
}

.container {
  overflow: scroll;
}

.content {
  background-color: red;
}

js:

function log( _l ) {
  $("#results").html( _l );
}

$('.content').on( 'mousedown', function( e ) {
  log( "content-click" );
  e.stopPropagation();
});

$('.container').on( 'mousedown', function( e ) {
  var pageX = e.pageX;
  var pageY = e.pageY;
  log( "scrollbar-click" );
});

http://codepen.io/jedierikb/pen/JqaCb

7
jedierikb

次のソリューションを使用して、ユーザーが要素のスクロールバー上でマウスをクリックしたかどうかを検出します。ウィンドウのスクロールバーでの動作をテストしませんでした。 Peteのソリューションはウィンドウスクロールでよりうまく機能すると思います。

window.addEventListener("mousedown", onMouseDown);

function onMouseDown(e) {
  if (e.offsetX > e.target.clientWidth || e.offsetY > e.target.clientHeight) 
  {
      // mouse down over scroll element
   }
}
5
Antti

私は自分の答えを提出して、 Alexanderの答え を受け入れて、完璧に機能させたので、賛成します Samuelの答え 、スクロールバーの幅を正しく計算します同様に必要。


そうは言っても、JQueryのmousedownイベントを上書き/オーバーライドするのではなく、2つの独立したイベントを作成することにしました。

これにより、JQueryのイベントをいじることなく必要な柔軟性が得られ、非常に簡単に実行できました。

  • mousedownScroll
  • mousedownContent

以下は、Alexandersを使用した2つの実装と、私自身の実装です。どちらも当初意図したとおりに機能しますが、おそらく前者が最適です。


  • これはJSFiddleです Alexanderの答え+ Samuelの答えを実装しています。

    $.fn.hasScroll = function(axis){
        var overflow = this.css("overflow"),
            overflowAxis;
    
        if(typeof axis == "undefined" || axis == "y") overflowAxis = this.css("overflow-y");
        else overflowAxis = this.css("overflow-x");
    
        var bShouldScroll = this.get(0).scrollHeight > this.innerHeight();
    
        var bAllowedScroll = (overflow == "auto" || overflow == "visible") ||
                             (overflowAxis == "auto" || overflowAxis == "visible");
    
        var bOverrideScroll = overflow == "scroll" || overflowAxis == "scroll";
    
        return (bShouldScroll && bAllowedScroll) || bOverrideScroll;
    };
    
    $.fn.mousedown = function(data, fn) {
        if ( fn == null ) {
            fn = data;
            data = null;
        }    
        var o = fn;
        fn = function(e){
            if(!inScrollRange(e)) {
                return o.apply(this, arguments);
            }
            return;
        };
        if ( arguments.length > 0 ) {
            return this.bind( "mousedown", data, fn );
        } 
        return this.trigger( "mousedown" );
    };
    
    $.fn.mouseup = function(data, fn) {
        if ( fn == null ) {
            fn = data;
            data = null;
        }    
        var o = fn;
        fn = function(e){
            if(!inScrollRange(e)) {
                return o.apply(this, arguments);
            }
            return;
        };
        if ( arguments.length > 0 ) {
            return this.bind( "mouseup", data, fn );
        } 
        return this.trigger( "mouseup" );
    };
    
    $.fn.mousedownScroll = function(data, fn) {
        if ( fn == null ) {
            fn = data;
            data = null;
        }    
        var o = fn;
        fn = function(e){
            if(inScrollRange(e)) {
                e.type = "mousedownscroll";
                return o.apply(this, arguments);
            }
            return;
        };
        if ( arguments.length > 0 ) {
            return this.bind( "mousedown", data, fn );
        } 
        return this.trigger( "mousedown" );
    };
    
    $.fn.mouseupScroll = function(data, fn) {
        if ( fn == null ) {
            fn = data;
            data = null;
        }    
        var o = fn;
        fn = function(e){
            if(inScrollRange(e)) {
                e.type = "mouseupscroll";
                return o.apply(this, arguments);
            }
            return;
        };
        if ( arguments.length > 0 ) {
            return this.bind( "mouseup", data, fn );
        } 
        return this.trigger( "mouseup" );
    };
    
    var RECT = function(){
        this.top = 0;
        this.left = 0;
        this.bottom = 0;
        this.right = 0;
    }
    
    function inRect(rect, x, y){
        return (y >= rect.top && y <= rect.bottom) &&
               (x >= rect.left && x <= rect.right)
    }
    
    
    var scrollSize = measureScrollWidth();
    
    function inScrollRange(event){
        var x = event.pageX,
            y = event.pageY,
            e = $(event.target),
            hasY = e.hasScroll(),
            hasX = e.hasScroll("x"),
            rX = null,
            rY = null,
            bInX = false,
            bInY = false
    
        if(hasY){ 
            rY = new RECT();
            rY.top = e.offset().top;
            rY.right = e.offset().left + e.width();
            rY.bottom = rY.top +e.height();
            rY.left = rY.right - scrollSize;
    
            //if(hasX) rY.bottom -= scrollSize;
            bInY = inRect(rY, x, y);
        }
    
        if(hasX){
            rX = new RECT();
            rX.bottom = e.offset().top + e.height();
            rX.left = e.offset().left;
            rX.top = rX.bottom - scrollSize;
            rX.right = rX.left + e.width();
    
            //if(hasY) rX.right -= scrollSize;
            bInX = inRect(rX, x, y);
        }
    
        return bInX || bInY;
    }
    
    $(document).on("mousedown", function(e){
        //Determine if has scrollbar(s)
        if(inScrollRange(e)){
            $(e.target).trigger("mousedownScroll");
        }
    });
    
    $(document).on("mouseup", function(e){
        if(inScrollRange(e)){
            $(e.target).trigger("mouseupScroll");
        }
    });
    });
    
    function measureScrollWidth() {
        var scrollBarMeasure = $('<div />');
        $('body').append(scrollBarMeasure);
        scrollBarMeasure.width(50).height(50)
            .css({
                overflow: 'scroll',
                visibility: 'hidden',
                position: 'absolute'
            });
    
        var scrollBarMeasureContent = $('<div />').height(1);
        scrollBarMeasure.append(scrollBarMeasureContent);
    
        var insideWidth = scrollBarMeasureContent.width();
        var outsideWitdh = scrollBarMeasure.width();
        scrollBarMeasure.remove();
    
        return outsideWitdh - insideWidth;
    };
    
  • これはJSFiddleです 私が代わりにやろうと決めたものです。

    $.fn.hasScroll = function(axis){
        var overflow = this.css("overflow"),
            overflowAxis,
            bShouldScroll,
            bAllowedScroll,
            bOverrideScroll;
    
        if(typeof axis == "undefined" || axis == "y") overflowAxis = this.css("overflow-y");
        else overflowAxis = this.css("overflow-x");
    
        bShouldScroll = this.get(0).scrollHeight > this.innerHeight();
    
        bAllowedScroll = (overflow == "auto" || overflow == "visible") ||
            (overflowAxis == "auto" || overflowAxis == "visible");
    
        bOverrideScroll = overflow == "scroll" || overflowAxis == "scroll";
    
        return (bShouldScroll && bAllowedScroll) || bOverrideScroll;
    };
    
    $.fn.mousedownScroll = function(fn, data){
        var ev_mds = function(e){
            if(inScrollRange(e)) fn.call(data, e);
        }
        $(this).on("mousedown", ev_mds);
        return ev_mds;
    };
    
    $.fn.mouseupScroll = function(fn, data){
        var ev_mus = function(e){
            if(inScrollRange(e)) fn.call(data, e);
        }
        $(this).on("mouseup", ev_mus);
        return ev_mus;
    };
    
    $.fn.mousedownContent = function(fn, data){
        var ev_mdc = function(e){
            if(!inScrollRange(e)) fn.call(data, e);
        }
    
        $(this).on("mousedown", ev_mdc);
    
        return ev_mdc;
    };
    
    $.fn.mouseupContent = function(fn, data){
        var ev_muc = function(e){
            if(!inScrollRange(e)) fn.call(data, e);
        }
        $(this).on("mouseup", ev_muc);
        return ev_muc;
    };
    
    var RECT = function(){
        this.top = 0;
        this.left = 0;
        this.bottom = 0;
        this.right = 0;
    }
    
    function inRect(rect, x, y){
        return (y >= rect.top && y <= rect.bottom) &&
            (x >= rect.left && x <= rect.right)
    }
    
    var scrollSize = measureScrollWidth();
    
    function inScrollRange(event){
        var x = event.pageX,
            y = event.pageY,
            e = $(event.target),
            hasY = e.hasScroll(),
            hasX = e.hasScroll("x"),
            rX = null,
            rY = null,
            bInX = false,
            bInY = false
    
        if(hasY){ 
            rY = new RECT();
            rY.top = e.offset().top;
            rY.right = e.offset().left + e.width();
            rY.bottom = rY.top +e.height();
            rY.left = rY.right - scrollSize;
    
            //if(hasX) rY.bottom -= scrollSize;
            bInY = inRect(rY, x, y);
        }
    
        if(hasX){
            rX = new RECT();
            rX.bottom = e.offset().top + e.height();
            rX.left = e.offset().left;
            rX.top = rX.bottom - scrollSize;
            rX.right = rX.left + e.width();
    
            //if(hasY) rX.right -= scrollSize;
            bInX = inRect(rX, x, y);
        }
    
        return bInX || bInY;
    }
    
    function measureScrollWidth() {
        var scrollBarMeasure = $('<div />');
        $('body').append(scrollBarMeasure);
        scrollBarMeasure.width(50).height(50)
            .css({
                overflow: 'scroll',
                visibility: 'hidden',
                position: 'absolute'
            });
    
        var scrollBarMeasureContent = $('<div />').height(1);
        scrollBarMeasure.append(scrollBarMeasureContent);
    
        var insideWidth = scrollBarMeasureContent.width();
        var outsideWitdh = scrollBarMeasure.width();
        scrollBarMeasure.remove();
    
        return outsideWitdh - insideWidth;
    };
    
1
ShadowScripter

私のために働く唯一のソリューション(IE11に対してのみテストされています):

$(document).mousedown(function(e){
    bScrollbarClicked = e.clientX > document.documentElement.clientWidth || e.clientY > document.documentElement.clientHeight;

});

1
Pete Alvin

マウスダウンではなく、ウィンドウではなくdivでスクロールバーを検出する必要があり、コンテンツを埋める要素があり、スクロールバーなしでサイズを検出するために使用していました:

.element {
    position: relative;
}
.element .fill-node {
    position: absolute;
    left: 0;
    top: -100%;
    width: 100%;
    height: 100%;
    margin: 1px 0 0;
    border: none;
    opacity: 0;
    pointer-events: none;
    box-sizing: border-box;
}

検出のコードは @ DariuszSikorski answer に似ていましたが、オフセットを含め、スクロール可能な内部にあるノードを使用していました。

function scrollbar_event(e, node) {
    var left = node.offset().left;
    return node.outerWidth() <= e.clientX - left;
}

var node = self.find('.fill-node');
self.on('mousedown', function(e) {
   if (!scrollbar_event(e, node)) {
      // click on content
   }
});
1
jcubic

Mac OSX 10.7+には、永続的なスクロールバーがないことに注意してください。スクロールバーはスクロールすると表示され、完了すると消えます。また、18ピクセルよりもはるかに小さくなります(7ピクセルです)。

スクリーンショット: http://i.stack.imgur.com/zdrUS.png

0
Applehat

element.offsetLeft + element.scrollWidth < event.clientXelement.onmousedown。要素のscrollWidthには、スクロールバーの幅は含まれません。内部コンテンツの情報のみが含まれます。水平スクロールバーでも検出したい場合は、幅とX位置の代わりに高さとY位置を使用して同じスケールをそのスケールに適用できます。

0
Grant Gryczan