简体   繁体   中英

HTML - Click on button causes list to scroll on top for unknown reasons

I am using tampermonkey to add some custom buttons to the Unity Documentation .

I noticed a strange problem If I am using a html <button> . Everytime when I click on the button, then the scroll list is scrolling to the top and the code is only executed on the second click.


However, If I replace <button> with <div> then everything is working perfectly fine.


Why is <button> behaving so weird?

Below is the tampermonkey script for reproduction.

// ==UserScript==
// @name         Unity Documentation (bugtest)
// @namespace    https://docs.unity3d.com
// @version      1.0
// @description  test
// @author       Edward Black
// @match        *://docs.unity3d.com/*
// @grant        GM_addStyle
// ==/UserScript==

GM_addStyle('.confirmBox { z-index: 100; width: 275px; background-color: greenyellow; border: 1px solid black; }');
GM_addStyle('.confirmBoxButtons { margin-top: 5px; }');
GM_addStyle('.confirmBoxClose { position: absolute; height: 20px; width: 20px; background-color: white; color:black; border: 0.5px solid black; text-align: center; right: 0px; }');
GM_addStyle('.confirmBoxClose:hover { background-color: black; color:white; cursor: pointer; }');
GM_addStyle('.confirmBoxBtn { background-color: white; color:black; width: 100px; border: 1px solid black; }');
GM_addStyle('.confirmBoxBtn:hover { background-color: black; color:white; cursor: pointer; }');

$(document).ready(function() {

    setTimeout(function() {
        prepareCustomContextMenue();
        $("div.mCSB_container").delegate("#theButton", "click", function() {
            alert("Hello from Button");
        });
        $("div.mCSB_container").delegate("#theDiv", "click", function() {
            alert("Hello from Div");
        });
        $("div.mCSB_container").delegate(".confirmBoxClose", "click", function() {
            $(".confirmBox").remove();
        });

    }, 4000);


});

function prepareCustomContextMenue()
{
    $("div.mCSB_container").find("a").each(function(j, obj) {

        $(obj).on("contextmenu", function(){
            return false;
        });
        $(obj).on("mousedown", function(e){
            if( e.button == 2 ) {

                console.log('Right mouse button!');
                showConfirmBox(this);

                return false;
            }
            return true;
        });
    });
}

function showConfirmBox(container)
{
    $(".confirmBox").remove();

    var elm = '<li><div class="confirmBox">'+
                  '<div class="confirmBoxClose">x</div>' +

                  '<div class="confirmBoxButtons">' +
                      '<button id="theButton" class="confirmBoxBtn"> This is a button </button>' +
                      '<div id="theDiv" class="confirmBoxBtn"> This is a div </div>' +
                  '</div>' +
              '</div></li>';

    $parent = $(container).parent();
    $(elm).appendTo($parent);
}

Instructions


Wait until the site loaded completly (about 4 seconds until the site load indicator disapeared) then right click on a link in the scroll list at left, which should be as far as possible down (so you can see that the list is actually scrolling up after clicking on the button). Now a container should appear:

在此处输入图片说明

Now click on the <button> named This is a button and notice that the scroll list scrolls to the top and the code from the button is not executed (alert should show). Press again on the button and notice that the code is now executed and the scroll list scrolls to top again after pressing Ok on the alert.

Next click on the <div> named This is a div and note that everything works as expected.

There are several issues with that script and, as Jim-miraidev pointed out, you need to use jQuery's .on() , .stopPropagation() , and .preventDefault() .

But the priority problem here is that the page has several other events at play (especially click and mouseup ). The userscript code is causing the page's flexbox scroll state to get confused.
(Exhibit A: the scroll only happens on first click immediately after the mousedown event that fires showConfirmBox() .)

So, an easy way to patch this is to capture all 3 of the (potentially) conflicting events :

$("div.mCSB_container").on ("click mousedown mouseup", "#theButton", function (zEvent) {
    zEvent.preventDefault ();
    zEvent.stopPropagation ();

    if (zEvent.type == "mousedown") {
        console.log ("Hello from Button");
    }
} );

Note that browsers fire the events in the order: mousedown, mouseup, click.

Buttons default will submit the page, causing a refresh.

<button id="theButton" class="confirmBoxBtn"> This is a button </button>

The default "type" for a button is "submit", which self posts. Update the button "type" to "button".

<button type="button" id="theButton" class="confirmBoxBtn"> This is a button </button>

Also delagate() as of jQuery 3.0, has been deprecated. Update for

 $("#theButton").on("click", function(e) {
      // remove default of the click
      e.preventDefault();
      // stop propagation
      e.stopPropagation();
     alert("Hello from Button");
 });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM