【问题标题】:Dynamic JS/HTML elements appear out of order only in GAS? [closed]动态 JS/HTML 元素仅在 GAS 中出现乱序? [关闭]
【发布时间】:2022-01-23 21:11:00
【问题描述】:

我有一个从 Google Apps 脚本生成的动态 HTML 表单。我希望元素以与它们在输入对象中出现的顺序相同的顺序出现,这与我附加元素的顺序相同。但这些元素实际上以意想不到的顺序出现。

我关注this other post 试图解决这个问题,但是当我在 GAS 中运行我的代码时,这些元素仍然出现乱序。问题是,当我运行my code in jsfiddle 时,它按预期工作,即元素以与它们在输入对象中相同的顺序出现。 GAS 中的元素只是没有按预期排序。

为什么元素在 GAS 中出现乱序,而在 jsfiddle 中却出现有序?如何在 GAS 中使用 vanilla JS 解决这个问题?

jsfiddle代码副本:

HTML

<!DOCTYPE html>
<html>
<head>
  <base target="_top">
  </head>
      
    <div id='input_parent'></div>
  </body>  



  
    <br><input type="button" value="Submit" onClick="test()">
    
    </form>

JS

inputObj = {"first field":{"required":true,"dataType":"select","options":["first opt","second opt"]},"second field":{"required":true,"dataType":"text","options":"none"}}
        // Section  
        section = document.getElementById("input_parent");

        div = document.createElement("div");
        div.setAttribute("id", "input_child");

        section.appendChild(div); 

             

          var fields = Object.keys(inputObj);     

          Array.from(fields).forEach((arg) => { 

            // Label
            section = document.getElementById("input_parent");

            label = document.createElement("label");
            label.setAttribute("id", "label_"+arg);
            label.setAttribute("for", arg);
            label_txt = document.createTextNode(arg+":");
            label.appendChild(label_txt);

            section.appendChild(label); 
            
            
                if (inputObj[arg].dataType == "select") { 
                      // Create select element                  
                      section = document.getElementById("input_parent");
                      const select_element = document.createElement("select");
                      select_element.setAttribute("id", "select_"+arg);               
                      
                      section.appendChild(select_element);
                      
                      var options = inputObj[arg].options
                      for(let o = 0; o < options.length; o++)
                      {
                        var element = document.getElementById("select_"+arg);
                        const option = document.createElement("option");
                        var text = document.createTextNode(arg+":");
                        option.textContent = options[o];
                        option.setAttribute("value", options[o]);
                        element.appendChild(option);

                      };

                     
                                            
                  } else {

                    section = document.getElementById("input_parent");
                    input_field = document.createElement("input");
                    input_field.setAttribute("id", "input_"+arg);          
                    input_field.setAttribute("type", inputObj[arg].dataType);

                section.appendChild(input_field);

                }

              

        });

回复@Nikko J 的其他信息。以下代码应重现下图中的结果。

dynamHtmlTbrlsht.html 渲染表单。

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <div id='input_parent'></div>
  </body>
   <br><input type="button" value="Submit" onClick="test()">
</html>


<script type='text/javascript'>
window.onload = function(){   
  google.script.run.withSuccessHandler(addElements).getElementInfo();   

  }
</script>


<script type='text/javascript'> 
function addElements(inputObj) {
// Section  
        section = document.getElementById("input_parent");

        div = document.createElement("div");
        div.setAttribute("id", "input_child");

        section.appendChild(div); 

             

          var fields = Object.keys(inputObj);     

          Array.from(fields).forEach((arg) => { 

            // Label
            section = document.getElementById("input_parent");

            label = document.createElement("label");
            label.setAttribute("id", "label_"+arg);
            label.setAttribute("for", arg);
            label_txt = document.createTextNode(arg+":");
            label.appendChild(label_txt);

            section.appendChild(label); 
            
            
                if (inputObj[arg].dataType == "select") { 
                      // Create select element                  
                      section = document.getElementById("input_parent");
                      const select_element = document.createElement("select");
                      select_element.setAttribute("id", "select_"+arg);               
                      
                      section.appendChild(select_element);
                      
                      var options = inputObj[arg].options
                      for(let o = 0; o < options.length; o++)
                      {
                        var element = document.getElementById("select_"+arg);
                        const option = document.createElement("option");
                        var text = document.createTextNode(arg+":");
                        option.textContent = options[o];
                        option.setAttribute("value", options[o]);
                        element.appendChild(option);

                      };

                     
                                            
                  } else {

                    section = document.getElementById("input_parent");
                    input_field = document.createElement("input");
                    input_field.setAttribute("id", "input_"+arg);          
                    input_field.setAttribute("type", inputObj[arg].dataType);

                section.appendChild(input_field);

                }

              

        });
}

</script>        

trblsht.gs 创建输入对象。 (请注意,这是为了解决手头的问题而进行了简化。实际上,inputObj 是通过运行一些动态创建对象并从外部源获取选项的函数来生成的。)

function getElementInfo() {
  
  var inputObj = {"first_fild":{"required":true,"dataType":"select","options":["option 1","option 2","option 3","option 4"]},"second_field":{"required":true,"dataType":"text","options":"none"},"third_field":{"required":true,"dataType":"text","options":"none"},"fourth_field":{"required":true,"dataType":"text","options":"none"},"fifth_field":{"required":false,"dataType":"select","options":["option 1","option 2","option 3","option 4","option 5","option 6","option 7","option 8","option 9","option10"]},"sixth_field":{"required":false,"options":"none"},"seventh_field":{"required":false,"dataType":"select","options":["option 1","option 2","option 3","option 4","option 5","option 6"]}}
  

  Logger.log("inputObj: "+JSON.stringify(inputObj))
  return inputObj;
}

为了完整起见,以下几行位于onEdit 函数中,该函数在活动单元格== "troubleshoot" 时生成表单。

function myOnEditTriggerFunc() {
    // do other stuff
    var currentRange = SpreadsheetApp.getActiveRange();
    var currentVal = 
    currentRange.getValue().toString().replace(/(^\s+|\s+$)/g,"");
    if (currentVal == "troubleshoot") {
        openHTML("dynamHtmlTbrlsht","Troubleshoot",400)
        return;
  }
  

}

openHTML()在上述函数中引用。

function openHTML(htmlFile,htmlTitle,height) {
      var html = HtmlService.createHtmlOutputFromFile(htmlFile)
      .setSandboxMode(HtmlService.SandboxMode.IFRAME)
      .setHeight(height);

    SpreadsheetApp.getUi() 
      .showModalDialog(html, htmlTitle);
      return;
 };

显示意外元素顺序的输出表单:

显示预期元素顺序的输出日志:

jsfiddle显示正常的js。

我开始怀疑问题是否出在dynamHtmlTbrlsht.html 中的Array.from(fields).forEach((arg)。我故意使用Array 而不是Object,因为Array is orderedObjectnot always ordered(取决于ES)。也许有与V8 runtime 相关的东西影响了这一点,而我没有注意到?

【问题讨论】:

  • 您是如何在应用程序脚本中对此进行测试的?您能否向我们展示您在应用程序脚本中的代码以及 GAS 与普通 JS 中输出的屏幕截图。另见minimal reproducible example

标签: javascript html google-apps-script


【解决方案1】:

当我看到您的脚本时,我认为您的问题的原因是由于数据是 JSON 对象。当看到关于 JSON 的文档时,它说如下。 Ref

在 JSON 中,它们采用以下形式:

对象是一组无序的名称/值对。一个对象以{左大括号开始,以}右大括号结束。每个名称后跟:冒号,名称/值对由逗号分隔。

而且,在您的脚本中,这些值是使用var fields = Object.keys(inputObj) 从 JSON 对象中检索的。为了使用您的脚本确认这一点,当 console.log(Object.keys(inputObj)) 放在 Javascript 端的 function addElements(inputObj) { 行之后和 Google Apps 脚本端的 return inputObj; 行之前,每个日志显示如下。

  1. 对于 Javascript 端,它是 ["fourth_field", "seventh_field", "second_field", "sixth_field", "third_field", "fifth_field", "first_fild"]

  2. 对于 Google Apps 脚本方面,它是 ["first_fild","second_field","third_field","fourth_field","fifth_field","sixth_field","seventh_field"]

发现 Javascript 端和 Google Apps 脚本端的键顺序不同。我认为这可能是您的问题的原因。

如果要使用["first_fild","second_field","third_field","fourth_field","fifth_field","sixth_field","seventh_field"]的顺序,下面的修改如何?

修改脚本一:

在此模式中,键按顺序设置为数组。

Google Apps 脚本方面:

发件人:

return inputObj;

收件人:

return [inputObj, ["first_fild","second_field","third_field","fourth_field","fifth_field","sixth_field","seventh_field"]];

Javascript 端:

发件人:

function addElements(inputObj) {
// Section  
        section = document.getElementById("input_parent");

        div = document.createElement("div");
        div.setAttribute("id", "input_child");

        section.appendChild(div); 

             

          var fields = Object.keys(inputObj);  

收件人:

function addElements([inputObj, fields]) {
// Section  
        section = document.getElementById("input_parent");

        div = document.createElement("div");
        div.setAttribute("id", "input_child");

        section.appendChild(div); 

             

          // var fields = Object.keys(inputObj);  

修改脚本2:

在此模式中,键按顺序设置为Object.keys(inputObj)。这样,可以在 Google Apps 脚本端和 Javascript 端之间使用相同的顺序。

Google Apps 脚本方面:

发件人:

return inputObj;

收件人:

return [inputObj, Object.keys(inputObj)];

本模式中,Javascript端的修改与模式1相同。

【讨论】:

  • 谢谢。模式 2 工作。您查看了 GAS 端和 JavaScript 端的输出。你提到的 JavaScript 端是指 HTML 文件中的 JS 吗?如果是这样,您是如何通过 GAS 从 JS 端访问输出的?
  • @user8121557 感谢您的回复。我很高兴你的问题得到了解决。关于Does the JavaScript-side that you mention refer to the JS in the HTML file?的附加问题1,是的。关于If so, how did you access the output from the JS-side via GAS?,我不得不为我糟糕的英语水平道歉。不幸的是,我无法理解您的第二个附加问题。可以问一下具体情况吗?
猜你喜欢
  • 2011-09-20
  • 2018-08-13
  • 1970-01-01
  • 2019-12-26
  • 1970-01-01
  • 2014-03-04
  • 1970-01-01
  • 2012-07-05
  • 1970-01-01
相关资源
最近更新 更多