我有一个 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,即使单击事件明显附加到按钮,因为我可以单击它并且它可以工作(新页面启动)。
可能的原因
在
ezsaltButton
插入之后,当您执行 $("#btnEZsaltGo").on("click", function(){ezsaltButtonClicked()});
时,您的 DOM 尚未准备好,因此,$("#btnEZsaltGo")
返回一个空的 jQuery 对象。这可能不会每次都会发生,但有可能发生,
上述情况可能会导致执行缓慢和 DOM 操作缓慢。
可能的解决方案
$(document).on("click", "#btnEZsaltGo", ezsaltButtonClicked);
document
对象,如果事件目标与 #btnEZsaltGo
选择器匹配,则调用 ezsaltButtonClicked
处理程序。#btnEZsaltGo
元素不需要存在于 DOM 上,但在单击按钮以到达 ezsaltButtonClicked
处理程序时必须存在。按照
@Brahma Dev
的建议,尝试ezsaltButton.on("click", ezsaltButtonClicked);
并在ezsaltButton
插入之前使用它。
ezsaltButton
jQuery 对象始终可用,因为您之前只需几条语句就对其进行了初始化,而 $("#btnEZsaltGo")
将从 DOM 中搜索元素并在找到时准备一个 jQuery 对象。ezsaltButton.on("click", ...
,您可以确保单击事件处理程序始终附加到预期的按钮,甚至在将其附加到 DOM 之前也是如此。将其附加到 DOM 后,按钮及其单击事件就可以进行交互。