web-dev-qa-db-de.com

Bestimmen Sie, ob der Benutzer auf die Bildlaufleiste oder auf den Inhalt klickt (klicken Sie auf die native Bildlaufleiste).

Ich versuche, benutzerdefinierte Ereignisse in JQuery zu erstellen, die erkennen sollen, wenn auf eine Bildlaufleiste geklickt wird1.
Ich weiß, dass es viele Texte gibt, aber alle meine Fragen sind fett gedruckt und es gibt ein JSFiddle-Beispiel , an dem Sie sofort arbeiten können.

Da ich dafür keine eingebaute Funktionalität gefunden habe,
Ich musste eine hasScroll-Funktion erstellen und prüfen, ob das Element eine Bildlaufleiste hat.

$.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;
};

und eine inScrollRange-Funktion, die prüft, ob der durchgeführte Klick innerhalb des Bildlaufbereichs liegt.

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;
}

Sind alle Bildlaufleistengrößen einheitlich? Zum Beispiel in Firefox und IE ist es 18px.
Unter der Annahme, dass es keine benutzerdefinierten Bildlaufleisten gibt, gibt es in einigen Browsern zusätzliche Auffüllungen oder Größen?

Diese Funktionen werden alle wie vorgesehen ausgeführt (soweit ich das beurteilen kann).

Es war etwas schwieriger, benutzerdefinierte Ereignisse zu erstellen, aber ich brachte es dazu, etwas zu funktionieren. Das einzige Problem ist, dass das angeklickte Element auch ausgelöst wird, wenn ein Ereignis zum Herunterfahren/Hochfahren mit dem Mausklick verknüpft ist.

Ich kann anscheinend nicht verhindern, dass die anderen Ereignisse ausgelöst werden, während gleichzeitig die mousedownScroll/mouseupScroll-Ereignisse ausgelöst werden, wie ich es nenne.

$.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.");
});

Wie verhindere ich, dass die anderen "Klick" -Ereignisse ausgelöst werden?

Während ich frage, können Sie den Code so weit wie möglich optimieren.

Hier ist eine JSFiddle zum Herumspielen.

Der Grund, warum ich das mache, ist ein größeres Plugin, das ich entwickle. Es gibt ein benutzerdefiniertes Kontextmenü, das angezeigt wird, wenn ich mit der rechten Maustaste auf einen der Bildlaufleisten klicke. Ich will das nicht. Daher dachte ich, ich sollte ein Ereignis erstellen, das nach Bildlauf-Klicks sucht (Maus-Auf/Ab) und dann verhindert, dass das Kontextmenü angezeigt wird. Um dies zu tun, muss der Bildlauf vor dem normalen Klick erfolgen und wenn möglich auch das Auslösen der normalen Klicks verhindern.

Ich denke hier nur laut nach, aber vielleicht gibt es eine Möglichkeit, alle Funktionen, die an das Element gebunden sind, abzurufen und dann die Reihenfolge zu ändern, in der sie hinzugefügt wurden? Ich weiß, dass Funktionen in der Reihenfolge ausgeführt werden, in der sie hinzugefügt wurden (1st added 1st called). Wenn ich diesen Prozess ansprechen könnte, könnte möglicherweise die gesamte "Registrierung" des Ereignisses in JQuery nur vor den click-Ereignissen eingefügt werden.


1 kann nur mousedown/mouseup verwenden, da beim Klicken auf eine Bildlaufleiste kein Klick ausgelöst wird. Wenn dies falsch ist, geben Sie bitte ein funktionierendes Beispiel/Code an

49
ShadowScripter

Sie können diesen Hack wahrscheinlich verwenden.

Sie könnten versuchen, die Ereignisse mousedown und mouseup zu entführen und zu vermeiden, wenn Sie mit Ihrer benutzerdefinierten Funktion auf eine Bildlaufleiste klicken.

$.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" );
};

Und die Umkehrung für mousedownScroll- und mouseupScroll-Ereignisse.

$.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" );
};

Ich denke übrigens, dass die Bildlaufleistenbreite eine OS-Einstellung ist.

15
Alexander

Gelöst:

Eine kürzeste Scrollbar-Klickerkennung, die ich auf IE, Firefox und Chrome testen konnte.

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

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

Arbeitsbeispiel: https://jsfiddle.net/s6mho19z/

22

Ich hatte das gleiche Problem in einem früheren Projekt und empfehle diese Lösung. Es ist nicht sehr sauber, aber es funktioniert und ich bezweifle, dass wir mit HTML viel besser machen können. Hier sind die zwei Schritte meiner Lösung:

1. Messen Sie die Breite der Bildlaufleiste in Ihrer Desktop-Umgebung.

Um dies zu erreichen, führen Sie beim Start der Anwendung folgende Schritte aus:

Fügen Sie dem Körper das folgende Element hinzu: 

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

Messen Sie mit mit der Breite des Bildlauffelds die Breite der Bildlaufleiste von jQUery (. Die Breite der Bildlaufleiste beträgt 50 - Innenfeldern).

Entfernen Sie das zusätzliche Element, das zum Messen der Bildlaufleiste verwendet wird (entfernen Sie jetzt das Element, das Sie dem Hauptteil hinzugefügt haben), nachdem Sie das Ergebnis erhalten haben.

Alle diese Schritte sollten für den Benutzer nicht sichtbar sein und Sie haben die Breite der Bildlaufleiste in Ihrem Betriebssystem

Sie können beispielsweise dieses Snippet verwenden:

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. Prüfen Sie, ob sich in der Bildlaufleiste ein Klick befindet.

Nun, da Sie die Breite der Bildlaufleiste haben, können Sie mit den Koordinaten des Ereignisses die Koordinaten des Ereignisses relativ zum Positionsrechteck der Bildlaufleiste berechnen und erstaunliche Dinge ausführen ...

Wenn Sie die Klicks filtern möchten, können Sie im Handler false zurückgeben, um deren Weitergabe zu verhindern.

9
Samuel Rossille

Stellen Sie sicher, dass der Inhalt Ihrer Scollarea vollständig [über] das übergeordnete Div ausfüllt.

Anschließend können Sie zwischen Klicks auf Ihren Inhalt und Klicks auf Ihrem Container unterscheiden.

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

Verwenden Sie die folgende Lösung, um festzustellen, ob der Benutzer mit der Maus auf die Bildlaufleiste des Elements geklickt hat. Ich habe nicht getestet, wie es mit der Bildlaufleiste des Fensters funktioniert. Ich denke, die Lösung von Pete funktioniert besser mit Fensterscrollen.

window.addEventListener("mousedown", onMouseDown);

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

Ich gebe meine eigene Antwort und akzeptiere Alexanders Antwort , weil es perfekt funktioniert hat, und bestätigt Samuels Antwort , weil es die Bildlaufleistenbreite korrekt berechnet, was ich auch brauchte.


Trotzdem entschied ich mich für zwei unabhängige Ereignisse, anstatt zu versuchen, das mousedown-Ereignis von JQuery zu überschreiben/zu überschreiben.

Dies gab mir die Flexibilität, die ich brauchte, ohne mich mit den eigenen Events von JQuery zu beschäftigen, und es war ziemlich einfach.

  • mousedownScroll
  • mousedownContent

Nachfolgend sind die beiden Implementierungen mit Alexandern und meine eigenen aufgeführt. Beide funktionieren wie ich ursprünglich beabsichtigt, aber die erste ist wahrscheinlich die beste.


  • Hier ist eine JSFiddle , die Alexanders Antwort + Samuel Antwort implementiert. 

    $.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;
    };
    
  • Hier ist ein JSFiddle was ich stattdessen getan habe.

    $.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

Die einzige Lösung, die für mich funktioniert (nur gegen IE11 getestet):

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

});

1
Pete Alvin

Ich musste die Scrollbar bei Mausedown erkennen, aber nicht für das Fenster, sondern für div.... Ich hatte ein Element, das den Inhalt ausfüllt, mit dem ich die Größe ohne Scrollbar erkannte:

.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;
}

der Code für die Erkennung war der @DariuszSikorski-Antwort ähnlich , jedoch einschließlich des Versatzes und der Verwendung des Knotens, der scrollbar war:

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

Es sei darauf hingewiesen, dass es unter Mac OSX 10.7+ keine permanenten Bildlaufleisten gibt. Bildlaufleisten werden beim Bildlauf angezeigt und verschwinden, wenn Sie fertig sind. Sie sind auch viel kleiner als 18px (sie sind 7px).

Screenshot: http://i.stack.imgur.com/zdrUS.png

0
Applehat

Ermitteln Sie einfach, ob element.offsetLeft + element.scrollWidth < event.clientX unter element.onmousedown. Die scrollWidth eines Elements enthält nicht die Breite der Bildlaufleiste. Es beinhaltet nur den inneren Inhalt. Wenn Sie auch mit einer horizontalen Bildlaufleiste erkennen möchten, kann dies auf die Skala mit Höhe und Y-Position anstelle von Breite und X-Position angewendet werden.

0
Grant Gryczan