67 votes

Déterminer si l'utilisateur clique sur la barre de défilement ou sur le contenu (onclick pour la barre de défilement native)

J'essaie de créer des événements personnalisés dans JQuery qui sont censés détecter le clic sur une barre de défilement. 1 .
Je sais qu'il y a beaucoup de texte, mais toutes mes questions sont en caractères gras et il y a un Exemple de JSFiddle sur lesquels vous pouvez travailler immédiatement.

Parce que je n'ai pas trouvé de fonctionnalité intégrée pour cela,
J'ai dû créer un hasScroll qui vérifie si l'élément a une barre de défilement,

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

et un inScrollRange en vérifiant si le clic effectué se situe dans la plage de défilement.

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

Les tailles des barres de défilement sont-elles toutes uniformes ? Par exemple, dans Firefox et IE, elle est de 18px.
En supposant qu'il n'y a pas de barres de défilement personnalisées, y a-t-il un rembourrage ou des tailles supplémentaires dans certains navigateurs ?

Ces fonctions fonctionnent toutes comme prévu (d'après ce que je peux discerner).

La création d'événements personnalisés était un peu plus délicate, mais j'ai réussi à la faire fonctionner quelque peu. Le seul problème est que si l'élément cliqué a un événement mousedown/up attaché à lui, il sera également déclenché.

Je ne parviens pas à empêcher les autres événements de se déclencher tout en déclenchant simultanément ce que j'appelle les événements 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.");
});

Comment puis-je empêcher les autres événements "clic" de se déclencher ?

Pendant que je vous le demande, n'hésitez pas à optimiser le code autant que possible.

Voici un JSFiddle avec lequel vous pouvez vous amuser.

La raison pour laquelle je fais ceci est à cause d'un plus gros plugin que je suis en train de développer. Il possède un menu contextuel personnalisé qui s'affiche lorsque je fais un clic droit sur l'un des dérouleurs. Je ne veux pas de ça. J'ai donc pensé que je devais créer un événement qui vérifie les clics de défilement (mouseup/downs) et qui empêche l'affichage du menu contextuel. Pour cela, il faut que le clic de défilement précède le clic normal et, si possible, qu'il empêche les clics normaux de se déclencher.

Je pense juste à voix haute ici mais Il y a peut-être un moyen de récupérer toutes les fonctions liées à l'élément et de changer l'ordre dans lequel elles ont été ajoutées ? Je sais que les fonctions sont exécutées dans l'ordre dans lequel elles ont été ajoutées (1ère ajoutée 1ère appelée), donc, si je pouvais exploiter ce processus, peut-être que l'ensemble de "l'enregistrement" de l'événement à JQuery pourrait simplement être inséré avant les événements de clic.


1 ne peut utiliser que le mousedown/mouseup parce que le click ne se déclenche pas en cliquant sur une barre de défilement. Si c'est faux, veuillez fournir un exemple de fonctionnement/code.

2 votes

Les tailles des barres de défilement ne sont pas uniformes. Dans webkit, vous pouvez les personnaliser assez fortement. css-tricks.com/examples/WebKitScrollbars

0 votes

Quel est votre objectif avec ce plugin ?

0 votes

@mrtsherman C'est juste une partie mineure d'un plus gros plugin, mais l'idée est de désélectionner un élément dans une liste lorsque l'utilisateur fait un clic de souris n'importe où dans le contenu, mais pas lorsque l'utilisateur clique sur le scroller. L'élément de la liste a déjà beaucoup d'événements de clics, mais je ne veux pas qu'ils se déclenchent lorsque le scroller est cliqué.

19voto

Alexander Points 13227

Vous pouvez probablement utiliser ce hack.

Vous pourriez essayer de détourner le mousedown y mouseup et les éviter lorsque vous cliquez sur une barre de défilement avec votre fonction personnalisée.

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

Et l'inverse pour mousedownScroll y mouseupScroll événements.

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

Au fait, je pense que la largeur de la barre de défilement est un paramètre du système d'exploitation.

0 votes

Merci Alexander, c'est a fonctionné parfaitement . Désolé pour l'acceptation tardive, vous devriez vraiment obtenir plus de upvotes pour cela, c'est brillant ! 25 points en votre faveur ;)

0 votes

@ShadowScripter, je suis heureux que cela ait été utile ;)

4 votes

La solution de jedierikb (défilement vers le bas) est bien meilleure car elle ne repose pas sur des calculs ou des bidouillages mais sur l'élément qui a déclenché l'événement. Et jQuery n'est pas vraiment nécessaire.

12voto

jedierikb Points 4066

Assurez-vous que le contenu de votre scollarea remplit complètement la div parent.

Ensuite, vous pouvez faire la différence entre les clics sur votre contenu et les clics sur votre contenant.

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

2 votes

Bonne solution. Vous pourriez aussi faire cela avec un seul événement sur .container à travers une condition comme : if($(e.target).is($(this))) //scrollbar-click . jsfiddle.net/3tzj0bps

7voto

Samuel Rossille Points 4590

J'ai eu le même problème dans un projet précédent, et je recommande cette solution. Elle n'est pas très propre mais elle fonctionne et je doute qu'on puisse faire beaucoup mieux avec le html. Voici les deux étapes de ma solution :

1. Mesurez la largeur de la barre de défilement de votre environnement de bureau.

Pour ce faire, au démarrage de l'application, vous effectuez les opérations suivantes :

Ajoutez l'élément suivant au corps :

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

Mesurez la largeur de la div interne de l'élément précédemment ajouté avec .width() de jQUery, et stockez la largeur de la barre de défilement quelque part (la largeur de la barre de défilement est de 50 - la div interne avec).

Supprimez l'élément supplémentaire utilisé pour mesurer la barre de défilement (maintenant que vous avez le résultat, supprimez l'élément que vous avez ajouté au corps).

Toutes ces étapes ne doivent pas être visibles par l'utilisateur et vous disposez de la largeur de la barre de défilement sur votre système d'exploitation.

Par exemple, vous pouvez utiliser cet extrait :

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. Vérifier si un clic se trouve sur la barre de défilement.

Maintenant que vous connaissez la largeur de la barre de défilement, vous pouvez, avec les coordonnées de l'événement, calculer les coordonnées de l'événement par rapport au rectangle d'emplacement de la barre de défilement et réaliser des choses impressionnantes...

Si vous souhaitez filtrer les clics, vous pouvez renvoyer false dans le gestionnaire pour empêcher leur propagation.

5 votes

La prise de mesures pour déterminer la largeur/hauteur de défilement est sujette à des erreurs - sur le Mac, l'ajout et le retrait d'une souris d'un ordinateur portable avec un pavé tactile fera apparaître et disparaître dynamiquement les barres de défilement overflow:scroll.

0voto

robrich Points 5284

Mon nouvel Asus a été livré avec une barre de Windows plus large que la normale, ce qui fait que la barre de défilement est également plus large que la normale. Il s'agit d'un écran tactile, donc c'est pour éviter que mon doigt soit plus gros que le pointeur de la souris. Je dirais donc qu'il ne faut pas présumer de la largeur de la barre de défilement.

0voto

Applehat Points 437

Il convient de préciser que sur Mac OSX 10.7+, il n'y a pas de barres de défilement persistantes. Les barres de défilement apparaissent lorsque vous faites défiler la page, et disparaissent lorsque vous avez terminé. Elles sont également beaucoup plus petites que 18px (elles sont de 7px).

Capture d'écran : http://i.stack.imgur.com/zdrUS.png

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X