单击事件未附加到跨多个选项卡的按钮

问题描述 投票:0回答:1

我有一个 Chrome 扩展(清单版本 3),它将自定义按钮添加到几个不同的 ServiceTitan 网页,然后将单击事件处理程序附加到该按钮。当仅在单个浏览器页面中完成时,这一切都可以完美地工作。但是,当我在不同的选项卡中打开多个网页,并且该按钮出现在每个页面上时,我的单击事件有时似乎没有附加到其中一个选项卡/页面上的按钮。在其他选项卡中仍然可以正常工作。所以它有点零星,但也相当可重复。当我导航到不同的页面并观察我的自定义按钮并单击它时,很快我就会遇到一个选项卡,其中单击事件未附加到我的按钮。

这是我附加点击事件的方法:

$("#btnEZsaltGo").on("click", function(){ezsaltButtonClicked()});

同样,这在大多数情况下都有效,但在使用多个选项卡时,有时却行不通。但在所有情况下,该按钮始终会出现。

注意:我没有使用后台脚本,只是使用内容脚本。

在所有情况下,如何使我的点击事件附加到每个选项卡中的自定义按钮?

这是完整来源:

var interval = 0;
var rootEZsaltURL;

console.log("EZsaltGo extension loaded...");

if(location.href.indexOf("next.servicetitan.com") > -1 || location.href.indexOf("integration.servicetitan.com") > -1){ // Sandbox ServiceTitan
    rootEZsaltURL = "https://test.ezsaltgo.com/ServiceTitanLanding.php?CustomerID="; // TEST EzsaltGo
}else if(location.href.indexOf("go.servicetitan.com") > -1){ // LIVE ServiceTitan
    rootEZsaltURL = "https://ezsaltgo.com/ServiceTitanLanding.php?CustomerID="; // LIVE EZsaltGo
}

(function() { // initial page load (static page content)
    interval = setInterval(function(){ checkForEZsaltButton(); }, 1000);
})();

function checkForEZsaltButton() {
    if($("#btnEZsaltGo").length == 0){ // only create the ezsalt button if it's not already there
        var insertBeforeAnchorElement; // insert the EZsaltGo button BEFORE this element
        var insertAfterAnchorElement; // insert the EZsaltGo button AFTER this element
        var pageType;
        var buttonMarginLeft = 0;
        var buttonMarginRight = 0;
        var hasBillingAddress = ($("span:contains('Billing Address')").length > 0);
        var hasServiceAddress = ($("span:contains('Service Address')").length > 0);
        var hasMoreActionsMenu = ($('div[data-testid="more-actions-menu"]').length > 0);
        var hasCustomerIDSpan = ($("span:contains('Customer ID')").length > 0);
        var hasCustomerIDLink = ($('a[role="link"][href*="/Customer/"]').length > 0);
        var isModal = ($("h2.Modal__header-title").length > 0);
        
        if((hasBillingAddress || hasServiceAddress) && hasMoreActionsMenu && (hasCustomerIDSpan || (!hasCustomerIDSpan && hasCustomerIDLink))){
            // clearInterval(interval);
            // console.log("Customer DIV loaded!! Interval stopped.");
            
            if(isModal){
                // we're inside the modal customer window
                pageType = "modal";
                insertAfterAnchorElement = $("h2.Modal__header-title"); // 
            }else{
                // we're inside a normal ST web page - let's figure out which page exactly

                if(location.href.indexOf("/customer/") > 0) pageType = "customer";
                if(location.href.indexOf("/location/") > 0) pageType = "location";

                if(pageType == "customer"){ 
                    insertBeforeAnchorElement = $("div.ButtonGroup").parent();
                    // no margin padding needed
                }
                
                if(pageType == "location"){ 
                    insertBeforeAnchorElement = $("div.ButtonGroup").parent().parent();
                    buttonMarginRight = 19;
                }
            }
            
            var ezsaltButton = $('<button id="btnEZsaltGo" class="Button Button--blue Button--solid Button--focus-visible" style="' + (buttonMarginRight > 0 ? "margin-right: " + buttonMarginRight + "px" : "") + (buttonMarginLeft > 0 ? "margin-left: " + buttonMarginLeft + "px" : "") + '">EZsaltGo</button>');
            
            if(insertBeforeAnchorElement){
                ezsaltButton.insertBefore(insertBeforeAnchorElement);
            }else if(insertAfterAnchorElement){
                ezsaltButton.insertAfter(insertAfterAnchorElement);
            }else{
                return; // we don't have anything to anchor against, so just return
            }
            
            $("#btnEZsaltGo").on("click", function(){ezsaltButtonClicked()});
            
            // attach click event to the little X at the top right of the modal - if that is clicked, the modal will close, and we need to resume checking
            if($(".Modal__header-close").length > 0){
                $(".Modal__header-close").on("click", function(){
                    //interval = setInterval(function(){ checkForEZsaltButton(); }, 1000);
                    console.log("Modal closed. Interval check resuming...");
                });
            }
        }
    }else{
        // check whether click event handler has been attached - doesn't work

        // var clickHandlers = $("#btnEZsaltGo").data('events')?.click; // clickHandlers always null even when button is there and click handler is attached
        // if(clickHandlers && clickHandlers.length > 0) doSomething(); //  clickHandlers always null even when button is there and click handler is attached
    }
}

function ezsaltButtonClicked(){
    console.log("EZsalt button clicked!");
    
    if(rootEZsaltURL && rootEZsaltURL != ""){
        var customerID = getCustomerIDForEZsalt();
        
        if(customerID && customerID != ""){
            window.open(rootEZsaltURL + customerID);    
        }else{
            alert("Unable to locate Customer ID. Contact EZsalt."); 
        }
    }else{
        alert("EZsalt URL could not be obtained. Contact EZsalt."); 
    }
}

function getCustomerIDForEZsalt(){
    var hasCustomerIDSpan = ($("span:contains('Customer ID')").length > 0);
    
    if(hasCustomerIDSpan){
        var customerIDSpan = $("span:contains('Customer ID')");
        var customerIDChunk = customerIDSpan.next('p');
        return $.trim(customerIDChunk.html());
    }else{
        var hasCustomerIDLink = ($('a[role="link"][href*="/Customer/"]').length > 0);
        
        if(hasCustomerIDLink){
            var hrefChunk = $('a[role="link"][href*="/Customer/"]').attr('href');
            var customerID = $.trim(hrefChunk.match(/\d+/)[0]);
            return customerID;
        }
    }
    
    return ""; // something went wrong - should never get this far
}

我已经尝试过这个函数作为解决它的可能方法:

function isClickHandlerAttachedToButton(){
    // check whether click event handler has been attached
    var clickHandlers = $("#btnEZsaltGo").data('events')?.click;
    var clickHandlerIsAttached = (clickHandlers && clickHandlers.length > 0);
    return clickHandlerIsAttached;
}

但它 100% 的时间返回 null,即使单击事件明显附加到按钮,因为我可以单击它并且它可以工作(新页面启动)。

jquery google-chrome-extension
1个回答
0
投票

可能的原因

ezsaltButton
插入之后,当您执行
$("#btnEZsaltGo").on("click", function(){ezsaltButtonClicked()});
时,您的 DOM 尚未准备好,因此,
$("#btnEZsaltGo")
返回一个空的 jQuery 对象。这可能不会每次都会发生,但有可能发生,

  • 如果页面充满脚本并且有太多 DOM 元素,
  • 如果页面执行复杂的执行,使主线程非常繁忙,
  • 如果添加太多选项卡,导致为每个选项卡分配的CPU、内存资源较少。

上述情况可能会导致执行缓慢和 DOM 操作缓慢。


可能的解决方案

  1. $(document).on("click", "#btnEZsaltGo", ezsaltButtonClicked);

    • 单击事件附加到始终存在的
      document
      对象,如果事件目标与
      #btnEZsaltGo
      选择器匹配,则调用
      ezsaltButtonClicked
      处理程序。
    • 执行上述代码时,
    • #btnEZsaltGo
      元素不需要存在于 DOM 上,但在单击按钮以到达
      ezsaltButtonClicked
      处理程序时必须存在。
    • 此格式可能比常规 jQuery 事件侦听器附件慢一点。
  2. 按照

    @Brahma Dev
    的建议,尝试
    ezsaltButton.on("click", ezsaltButtonClicked);
    并在
    ezsaltButton
    插入之前使用它。

    • ezsaltButton
      jQuery 对象始终可用,因为您之前只需几条语句就对其进行了初始化,而
      $("#btnEZsaltGo")
      将从 DOM 中搜索元素并在找到时准备一个 jQuery 对象。
    • 通过使用
      ezsaltButton.on("click", ...
      ,您可以确保单击事件处理程序始终附加到预期的按钮,甚至在将其附加到 DOM 之前也是如此。将其附加到 DOM 后,按钮及其单击事件就可以进行交互。
    • 简单有效。
© www.soinside.com 2019 - 2024. All rights reserved.