【问题标题】:Custom buttons in new Google Sites新版 Google 协作平台中的自定义按钮
【发布时间】:2023-02-01 10:13:43
【问题描述】:

我最近尝试使用 Google 协作平台在我的网站上实现自定义按钮,只是为了执行一个基本功能:滚动到某个部分或顶部。没想到会这么难...

我必须使用嵌入代码来添加我的按钮(否则无法自定义它们)但这会使它们显示在 iframe 中。

onclickwindow.top 似乎根本不起作用(或者我做错了)。

我的临时解决方案如下,但它会在滚动前打开一个新选项卡,所以效果不是很好:

.button-test {
  background-color: #0078d0;
  border: 0;
  border-radius: 56px;
  color: #fff;
  cursor: pointer;
  display: inline-block;
  font-family: system-ui, -apple-system, system-ui, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", sans-serif;
  font-size: 18px;
  font-weight: 600;
  outline: 0;
  padding: 16px 21px;
  position: relative;
  text-align: center;
  text-decoration: none;
  transition: all .3s;
  user-select: none;
  -webkit-user-select: none;
  touch-action: manipulation;
}

.button-test:before {
  background-color: initial;
  background-image: linear-gradient(#fff 0, rgba(255, 255, 255, 0) 100%);
  border-radius: 125px;
  content: "";
  height: 50%;
  left: 4%;
  opacity: .5;
  position: absolute;
  top: 0;
  transition: all .3s;
  width: 92%;
}

.button-test:hover {
  box-shadow: rgba(255, 255, 255, .2) 0 3px 15px inset, rgba(0, 0, 0, .1) 0 3px 5px, rgba(0, 0, 0, .1) 0 10px 13px;
  transform: scale(1.05);
}

@media (min-width: 768px) {
  .button-test {
    padding: 16px 48px;
  }
}
<a href="http://test.com/en#h.2f00a9eddc84963_56">
  <button class="button-test" role="button">Test Button</button>
</a>

无法找到使主/顶部窗口刷新而不打开新选项卡以滚动到该部分的函数或脚本。 有没有人找到解决方案? 我真的希望如此……我不明白为什么 Google 协作平台如此有限。

这是一个例子:sites.google.com/view/testcustombutton

据我了解,这与自动生成的沙箱 iframe 没有允许顶部导航选项有关。 有没有办法改变或绕过它?

【问题讨论】:

  • 您想要超链接 (&lt;a&gt;) 标签内的按钮有什么特别的原因吗?为什么不简单地将超链接标记样式设置为看起来像一个按钮呢?
  • 嗨 blurfus,没有特别的原因。我从一个按钮开始,但无法使 onclick 在其 iframe 之外工作,因此转移到超链接标签解决方案。但无论哪种方式,它仍然会在 Google 协作平台中打开一个新标签
  • 没有足够的信息可以提供帮助。默认情况下,超链接(或按钮)不会在新选项卡中打开。请在您的问题中包含 minimal reproducible example,以便更快地进行故障排除。另请阅读我们的How to Ask 页面以获取有关如何改进此问题的更多提示
  • 我在这里发布了一个示例:sites.google.com/view/testcustombutton 我无法提供确切的代码,因为它是由 Google 协作平台自动生成的
  • 无法解决这个问题——这是一个位于多个其他 iframe 内的 iframe 内的 iframe……首先想到的是:为什么有这么多 iframe?

标签: javascript jquery button embed google-sites-2016


【解决方案1】:

建议 #1 - iframe 和消息传递

由于您的按钮位于 iframe 中,因此您需要将数据传递给父级,并且需要以特定方式进行,以避免 CORS 问题(稍后会详细介绍)。现在,取决于你的 iframe-inception 有多深,这可能很快就会变得笨拙(一个 iframe 有一个父 iframe,它本身是另一个 iframe 的孩子,依此类推,直到我们到达父文档)。

在最简单的情况下(iframe 只有一个父级),这可能会。但是请注意,出于安全原因,这不是最明智的做法。

在 iframe 中:

const myBtn = document.getElementById("myBtn");
myBtn.on("click",handleScroll);

// This is the part which communicates with the main / parent window
function handleScroll() {
    window.parent.postMessage({message: <your_message>, secret: "trustMeBro"}, "*");
}

对到目前为止所做的事情的一些解释:

  • 我们使用window.parent.postMessage 来避免跨源问题,并让信息从其 iframe 流回父文档。我们不能允许从 iframe 中单击按钮对父文档有任何影响,否则
  • window.parent.postMessage(...) 的第一个参数 (JSON) 是一个 JSON,其中包含消息(message 键及其值)和秘密(secret 键及其值)。该消息顾名思义 - 我们父文档的实际消息。在您的情况下,这将是您希望文档滚动到的锚定链接。秘密是你向父母表明自己身份的方式。这是不是最好的办法,因为有人可以向您的父页面注入各种废话。更好的方法,参考MDN documentation
  • 前一个与最后一个参数 - "*" 相关联。这是消息的来源。正如 MDN 文档所述:

如果您知道其他窗口的文档应该位于何处,请始终提供特定的 targetOrigin,而不是 *。未能提供特定目标会泄露您发送到任何感兴趣的恶意站点的数据。

那为什么我把它设置为“*”呢?因为我的测试用例在 localhost 中。对于您的具体情况,您可以将原点设置为 iframe 文件实际位置的 URL。

继续看父母。您的主页/父页面应该有这个,以便能够使用我们在您的 iframe *.html 文件中已有的内容。

window.addEventListener("message", function(event) {
    // let's see what we're getting
    console.log(event);
    console.log(event.origin);
    console.log(event.originalTarget);
    console.log(event.explicitOriginalTarget);
    if(event.data.secret === "trustMeBro") {
        // do stuff, scroll, change HTML in the parent document, etc
        console.log(event.data.message);
       // do more stuff
    }
});

如果我们期待来自外部的消息,我们需要为此附加一个事件监听器。在根据接收到的数据执行任何操作之前,您应该首先只对在触发 message 事件时收到的信息进行控制台记录。在此基础上(和实验),您将构建以后的逻辑。

如果您有 iframe 的中继,所有这些都在彼此之间传递数据,直到消息到达父窗口,那么您做的事情就不对。


建议 #2 - cookies 和 setInterval

处理您遇到的问题的另一种方法是使用 cookie。这有其自身的问题 - 根据您所在的位置,您可能必须在使用 cookie 时让用户选择退出,然后此解决方案会破坏您网站的功能。

话虽这么说,这里是 iframe 的代码。整个建议假定您所有的 *.html 文件都在您网站上的同一目录中。

const myBtn = document.getElementById("myBtn");
myBtn.on("click",setCookie);

// This is the part which communicates with the main / parent window
function setCookie() {
    var cookieName = <cookie_name>;
    var cookieValue = <your_message_to_parent>;
    var expiresSeconds = <cookie_expiration_in_seconds>;
    var d = new Date();
    d.setTime(d.getTime() + (expiresSeconds*24*60*60*1000));
    var expires = "expires="+ d.toUTCString();
    document.cookie = 
        cookieName + "=" 
        + cookieValue + ";" 
        + expires 
        + ";path=/;secure;httponly;domain=<your_domain>;SameSite=Strict";
}

说明:

  • 当您的 iframe 按钮被点击时,这将设置一个具有特定名称的 cookie - 例如,btnscroll(或任何其他名称,您可以随意命名)
  • cookie 的值应该是您向父窗口发送的消息 - 它应该做什么。在您的情况下,如果单击了该特定按钮,那将是滚动的
  • cookie 还需要有一个过期日期 - 如果您只是处理简单的事情,比如滚动父级,那么 cookie 不应设置为从现在起 10 年过期
  • document.cookie部分的其他内容(路径、安全等),请参考MDN documentation

让我们看看我们需要在父级中更改什么。

var scrollInterval = setInterval(function() {
    let cookieVal = checkForCookie(<cookie_name>); // the name that was set in your iframe
    // do stuff based on what the cookieVal is
},500);

function checkForCookie(cookie) {
    // let's get all of the cookies
    let cookies = document.cookie;
    // let's see what we have
    console.log(cookies);
    // ah, so the semicolon can be used
    // to split this long string into an array
    let cookieArray = cookies.split(';');
    // let's check the array
    console.log(cookieArray);
    // good, now we just have to loop through
    // but let's eliminate the empty spaces around
    // cookie=value elements of our array
    cookieArray.map(function(a) {
        return a.trim();
    });
    
    for(let i = 0; i < cookieArray.length; i++) {
        if(cookieArray[i].indexOf(cookie + '=') > -1) {
            let ourCookieArr = cookieArray[i].split(cookie + '=');
            // let's see what we have
            console.log(ourCookieArr);
            // ok, let's get the second element, and clear the cookie
            deleteCookie(cookie);
            return ourCookieArr[1];
        }
    }

    // no cookie found
    return false;
}

function deleteCookie(cookie) {
    // we're setting it to an empty string
    // which expires in the past
    document.cookie = 
        cookie + "=" 
        + ";" 
        + "Expires=Thu, 01 Jan 1970 00:00:01 GMT;"
        + "path=/;secure;httponly;domain=<your_domain>;SameSite=Strict";
}

这个解决方案也不是最好的,因为如果设置了 cookie,你会不断运行后台检查,并且通过这样做你会缠着客户端的浏览器检查偶尔发生的事情(单击按钮滚动).


要点 - 有多种方法可以满足您的需求,但是,如果可能,您应该避免使用 iframe。以不同的方式组织您的内容,或者更改主机/平台,如果那不可能的话。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-19
    • 2016-03-04
    • 1970-01-01
    相关资源
    最近更新 更多