【问题标题】:Why are the form DOM object values not being updated in this code?为什么此代码中没有更新表单 DOM 对象值?
【发布时间】:2019-06-25 17:38:09
【问题描述】:

我正在使用 javascript 使用内置的 SubtleCrypto.digest() 函数生成密码校验和。此函数将结果作为promise 对象返回,然后将其传递给内联函数,该函数将结果转换为十六进制字节的文本表示。问题是内联文本转换函数digToHex() 没有更新 DOM 中的隐藏字段,然后在 POST 请求中提交给服务器。

通过附加到表单按钮的onclick() 事件调用该函数。它读取用户在表单上输入的密码,并将其传递给digestMsg() 函数以生成哈希。

请注意,我了解SHA-1 周围的安全问题。目前这只是概念验证。

我在控制台和网络选项卡上观察了 Chrome 开发人员工具中的输出。我还监控了服务器(一个物联网设备)上的传入请求。提交的值始终为空。 digToHex() 函数似乎正在生成散列,因为散列确实在控制台选项卡中显示为 chk.value 的值。从代码中可以看出,我已经在digestMsg() promise 处理程序的上下文内外尝试了赋值和表单提交。

function reboot() {
    var msg = document.querySelector('#pwd1').value;

    // Test purposes only
    var opt = document.querySelector('[name = "opt"]');
    var chk = document.querySelector('[name = "chk1"]');
    // Test purposes only

    digestMsg(msg).then(result=>{
        var opt = document.querySelector('[name = "opt"]');
        var chk = document.querySelector('[name = "chk1"]');
        opt.value = 3;
        chk.value = digToHex(result);
        // document.querySelector('form').submit();

    console.log(opt.value);
    console.log(chk.value);

    });
    // form.submit();

    alert("Result: " + opt.value + " - " + chk.value);
}

function digToHex(buf) {
    const bytes = new Uint8Array(buf);
    const hexCodes = [...bytes].map(value => {
        const hexCode = value.toString(16);
        const padHexCode = hexCode.padStart(2, '0');
        return padHexCode;
    });
    return hexCodes.join('');
}

function digestMsg(msg) {
    const enc = new TextEncoder();
    const data = enc.encode(msg);
    return window.crypto.subtle.digest('SHA-1', data);
}

当两个 sumbit() 行中的任何一个未注释时,null 值会在服务器上收到,但 Chrome 开发人员工具会在控制台输出中显示正确的值。对开发人员工具中的网络输出选项卡的进一步调查确认实际上正在传输空值。我希望看到 "opt" 的值为 3chk1 的值包含哈希。

这是调用代码的 HTML:

<form method="post" action="/admin">
<input name="chk1" value="" hidden/>
<input name="chk2" value="" hidden/>
<input name="opt" value=0 hidden/>
<table>
<tr><th>Current password:</th><td><input id="pwd1" type="password">        </input></input></td>
<tr><th>New password:</th><td><input id="pwd2" type="password"></input></input></td>
<tr><th>Confirm new password:</th><td><input id="pwd3" type="password"></input></input></td>
<tr><th></th><td align="right"><input type="submit" class="btn" value="Change" onclick="change()"/></td></tr>
<tr><th></th><td><br></td></tr>
<tr><th>Defaults</th><td align="right"><input type="submit" class="btn" value="Reset" onclick="reset()"/></td></tr>
<tr><th>Restart WiFi</th><td align="right"><input type="button" class="btn" value="Reboot" onclick="reboot()"/></td></tr>
</table>
</form>

根据 Marius 的进一步测试表明,该代码确实适用于静态 html 页面。因此,该问题似乎与我为每个表单动态加载 html 的方式有关。我在下面附上了主(父)页面的 html:

<html>
<head>
<meta charset="utf-8">
<title>AR488 WiFi Configuration</title>
<link rel="stylesheet" href="AR488wifi.css">
<script defer src="AR488wifi.js"></script> 
</head>
<body onload="getPage('seeStat.html')";>
<div class="mpage">
<div class="headr">AR488 WiFi Configuration</div>
<div>
<ul>
<li id="mStat" class="active"><a onclick="getPage('seeStat.html')">Status</a></li>
<li id="mGen"><a onclick="getPage('cfgGen.div')">General</a></li>
<li id="mWifi"><a onclick="getPage('cfgWifi.div')">WiFi</a></li>
<li id="m488"><a onclick="getPage('cfg488.div')">GPIB</a></li>
<li id="mAdm"><a onclick="getPage('cfgAdm.div')">Admin</a></li>
</ul>
</div>
<hr>
<div id="cfgPage" class="conf">
</div>
<div>
<hr>
<table class="foot"><tr>
<td>AR488 WiFi ver: 0.00.00</td>
<td>Firmware ver: 0.00.00</td>
</tr></table>
</div>
</div>
</body>
</html>

【问题讨论】:

  • 请显示相应的 HTML。请注意,如果该字段被禁用,则在表单提交期间将不会传递该值。
  • 隐藏字段也必须在提交的表单元素中。
  • 你是怎么打电话给reboot的?您是否阻止默认操作 - 在您的承诺填写之前提交表单?
  • 这不是您创建隐藏字段的方式。它是&lt;input type="hidden" ... /&gt; 我猜你有一些 CSS 使用“隐藏”属性并隐藏 DOM 中的字段。
  • 我只是从使用 onclick() 事件的按钮控件调用 reboot()。我现在添加了 HTML 来显示这一点。我很欣赏可以将事件处理程序添加到 javascript。

标签: javascript promise


【解决方案1】:

您的代码看起来不错。我已经复制了你所拥有的并添加了一些 HTML 来模拟这一点。确保这些字段没有被禁用。

reboot();

function reboot() {
    var msg = document.querySelector('#pwd1').value;

    // Test purposes only
    var opt = document.querySelector('[name = "opt"]');
    var chk = document.querySelector('[name = "chk1"]');
    // Test purposes only

    digestMsg(msg).then(result=>{
        var opt = document.querySelector('[name = "opt"]');
        var chk = document.querySelector('[name = "chk1"]');
        opt.value = 3;
        chk.value = digToHex(result);

    console.log(opt.value);
    console.log(chk.value);

    });

}

function digToHex(buf) {
    const bytes = new Uint8Array(buf);
    const hexCodes = [...bytes].map(value => {
        const hexCode = value.toString(16);
        const padHexCode = hexCode.padStart(2, '0');
        return padHexCode;
    });
    return hexCodes.join('');
}

function digestMsg(msg) {
    const enc = new TextEncoder();
    const data = enc.encode(msg);
    return window.crypto.subtle.digest('SHA-1', data);
}
<input type="password" id="pwd1" value="test" />
<input type="text" name="opt" />
<input type="text" name="chk1" />

【讨论】:

  • 我很欣赏关于禁用字段的观点,但我可以确认这些字段只是隐藏的,而不是禁用的。我现在已将 HTML 添加到原始帖子中。需要注意的一点是,当父页面上的菜单链接被选中时,HTML 表单会被提取并加载到 div.innerHTML 中。这样可以选择4个不同的配置页面。
  • 我刚刚复制了你的测试(顺便说一句,感谢你不厌其烦地这样做),方法是用 标签包装我在上面发布的 HTML 代码并添加一个带有链接的标题部分相同的 javascript 和 CSS。我刚刚测试了它,它的工作原理!显然,这个问题一定与我使用 innerHTML 动态加载 HTML 代码的方式有关。我已将父页面的 HTML 添加到原始帖子中。现在的问题是,当我以这种方式动态加载 HTML 时,为什么我的代码会出现错误行为?会不会像贝尔吉所说的那样?
  • 我看到了更新的 HTML。您的 html 中有一些格式错误的元素标签。您的输入字段应该是自动关闭 &lt;input /&gt; 而不是 &lt;input&gt;&lt;/input&gt;> 我还看到每个输入都有 2 个关闭标签。这可能会破坏您的表格。您使用什么方法来创建这些 HTML 元素?
  • 感谢您发现这一点。我会做出必要的更正。
【解决方案2】:

我相信我已经找到了解决办法!

在我的原始代码中,我使用 querySelector() 来获取对表单对象的引用,该引用立即传递给提交,但处理完成后:

document.querySelector('form').submit();

提交表单时“opt”设置为“0”——我专门设置的一个数值——不是 null 或未定义,所以这个命令似乎是在创建对原始未修改表单的引用,然后立即已提交。

我通过在 处理发生之前在 digestMsg() 处理程序的上下文中创建对表单的引用进行了实验:

var formObj = document.querySelector('form');

在处理表单并更新值之后,它被提交:

formObj.submit();

这一次,表单提交了正确的更新值。调整后的代码(减去各种测试警报)现在如下所示:

function reboot() {
    var msg = document.querySelector('#pwd1').value;
    digestMsg(msg).then(result=>{
        var formObj = document.querySelector('form');
        var opt = document.querySelector('[name = "opt"]');
        var chk = document.querySelector('[name = "chk1"]');
        opt.value = 3;
        chk.value = digToHex(result);
        formObj.submit();
    });
}

我很想知道为什么第一个命令创建了对原始修改表单的引用并且没有考虑更新的值?在这两种情况下,事情似乎都以相同的顺序发生——表单被加载到浏览器中,值被更新,表单被提交。另外,为什么原始版本可以处理静态 HTML 页面,但不能处理动态加载的表单?如果有人有解释,我很想听。

同时,感谢帮助我集中思考并找到解决方案的 cmets。

以上只是概念证明,需要进一步验证和调整才能实现实际工作形式。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-02-07
    • 2018-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-06
    • 2021-01-28
    • 1970-01-01
    相关资源
    最近更新 更多