动态 JS/HTML 元素仅在 GAS 中出现乱序? [关闭]

Posted

技术标签:

【中文标题】动态 JS/HTML 元素仅在 GAS 中出现乱序? [关闭]【英文标题】:Dynamic JS/HTML elements appear out of order only in GAS? [closed] 【发布时间】: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 ordered 而Object 是not always ordered(取决于ES)。也许有与V8 runtime 相关的东西影响了这一点,而我没有注意到?

【问题讨论】:

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

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

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

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

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

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

    对于 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?,我不得不为我糟糕的英语水平道歉。不幸的是,我无法理解您的第二个附加问题。可以问一下具体情况吗?

以上是关于动态 JS/HTML 元素仅在 GAS 中出现乱序? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

怎样使数组元素乱序

JS代码乱序执行? JS新手[重复]

仅在特定 div 中使用 javascript 更改部分链接

[SCOI2008]配对 (贪心,动态规划)

带头结点且递增有序的单链表AB(AB中元素个数分别为mn)分别存储了一个集合。设计算法,求AB的差集 (仅在A中出现不在B中出现),并存在A中,要求保持递增有序性

Django模板仅在for循环中第一次出现匹配时执行