JS 代码不适用于从 Django 模型中检索到的所有数据

Posted

技术标签:

【中文标题】JS 代码不适用于从 Django 模型中检索到的所有数据【英文标题】:JS code not working on all data retrieved from Django Model 【发布时间】:2020-03-22 08:51:23 【问题描述】:

我正在使用 html 页面上的 forloop 从 Django 模型中检索 endDate 字段。 我想检查所有即将到来的 endDate 是否都小于今天的日期。为此,我使用的是 JS 代码。

我的代码只能在检索到的第一个日期执行检查,但它不适用于其他日期。

这是我用于检索数据的 HTML 代码

% for p in object_list %
    <h4> p.jobName </h4>
    <p><b style="color: red">Expires On: </b><b> p.endDate </b></p>

    <a href=" p.get_absolute_url " target="_blank">View info</a>    

    <input type="hidden" id="endDate" name="variable" value=" p.endDate ">
    <span id="check"></span>            

% endfor %

JavaScript 代码检查日期:

<script type="text/javascript">       
    var endDate = document.getElementById("endDate").value;
    var ToDate = new Date();
    if(new Date(endDate).getTime()<=ToDate.getTime())
        document.getElementById("check").innerHTML = "Expired";
        document.getElementById("check").className = "label danger";
    
    else
        document.getElementById("check").innerHTML = "Active";
        document.getElementById("check").className = "label success";
    
</script>

我现在有 2 个 p.jobName 。第一个应该显示Expired,而第二个应该显示Active。 我的代码仅适用于第一次约会,即仅适用于计算机管理员。

这是我得到的输出:

谁能告诉我这是什么问题?

谢谢

【问题讨论】:

span#check 是否填充了“活动”文本? @JaredASutton 是的。它也适用于 Active。问题是它只适用于第一次约会,而不适用于其他人。 您的 JS 代码将只适用于 ID 为 endDate 的单个元素。您的 HTML 是否包含具有相同 ID 的多个元素?如果是这样,那是无效的 HTML,这就是您的 JS 无法正常工作的原因 - getElementById 返回一个 single 元素。如果您希望它适用于多个,请改用一个类,例如getElementsByClassName 事实上,我刚刚注意到你也分享了你的 Django 模板代码,所以我可以确认这正是问题所在。我会输入一个如何解决它的答案。 虽然顺便说一句,我不能从这段代码中看出为什么你不能只在 Python 的服务器端做这个日期处理,并在模板中包含适当的逻辑,而不是做它在客户端 JS 中。 【参考方案1】:

扩展我在 cmets 中已经说过的内容:

你的问题的本质在这里:

% for p in object_list %
...   
    <input type="hidden" id="endDate" name="variable" value=" p.endDate ">
    <span id="check"></span> 
...
% endfor %

一个 HTML 文档只能有一个特定 ID 的 一个 元素(否则它不能很好地“识别”该元素 - 这是 HTML 规范的一部分)。在您的情况下,每当循环包含多个元素时,您最终都会得到多个元素,所有元素都具有endDate ID,以及多个具有check ID 的元素。

像这样重复 ID 以及本身是错误的,可能会产生重大的功能影响 - 正如您在代码中看到的那样。也就是说,由于给定 ID 只应该(至多)一个元素,因此在页面上运行的任何 Javascript 代码都有权假设是这种情况。特别是,document.getElementById 返回一个表示 single HTML 元素的对象,而不像 document.getElementsByClassName 这样的类似方法返回 DOM 节点的“集合”。 (因为类与 ID 不同,可以应用于同一文档中的多个元素。)

简而言之,就是如何解决您遇到的问题。只需将上面的id="endDate" 替换为class="endDate",与check 类似,并更改您的javascript 以考虑到这一点。您不仅必须将getElementById 方法更改为getElementsByClassName,还必须考虑到这会返回一个集合(您可以使用.forEach 循环),并且您需要定位相邻的@ 987654332@ span 而不是第一个,或者任何旧的随机。以下代码是使其工作的一种方法(注意使用 ES6 Array.from 将集合转换为真正的 Array,以便您可以使用 .forEach):

var endDateElts = Array.from(document.getElementsByClassName("endDate"));
var ToDate = new Date();

endDateElts.forEach(function(dateElt) 
    var endDate = dateElt.value;
    var check = dateElt.nextSibling;
    if(new Date(endDate).getTime()<=ToDate.getTime())
        check.innerHTML = "Expired";
        check.className = "label danger";
    
    else
        check.innerHTML = "Active";
        check.className = "label success";
    
);

【讨论】:

好的,我有问题了。您的代码运行良好,但 innerHTML 在这种情况下不起作用。我试过console.log(check.innerHTML),它给出了正确的结果(两个日期),但它在 HTML 模板上不可见。 这听起来很奇怪。如果它正确地记录到控制台,那么我认为这是一个 CSS 问题,为什么你看不到它。【参考方案2】:

基于

% for p in object_list %
    <h4> p.jobName </h4>
    <p><b style="color: red">Expires On: </b><b> p.endDate </b></p>

    <a href=" p.get_absolute_url " target="_blank">View info</a>    

    <input type="hidden" id="endDate" name="variable" value=" p.endDate ">
    <span id="check"></span>            

% endfor %

我假设您有多个具有相同 ID 的对象,因此 JS 只处理第一个 endDatecheck 对象。

<input type="hidden" id="endDate" name="variable" value=" p.endDate ">
<span id="check"></span> 

你应该做什么:

    使用document.querySelectorAll()获取多个DOM元素 具有相同的类名。 遍历NodeList 并执行您希望执行的操作。

const endDates = document.querySelectorAll('.endDate');
const checks = document.querySelectorAll('.check');
let ToDate = new Date();
let i = 0;
for (const endDate of endDates)
    if(new Date(endDate.value).getTime()<=ToDate)
      checks[i].innerHTML = "Expired";
      checks[i].className = "label danger";
    
    else
      checks[i].innerHTML = "Active";
      checks[i].className = "label";
    
    i++;
<div class="container-1">
<input type="text" class="endDate" name="variable" value="2019-11-29">
<span class="check"></span>
<br>
</div>
<div class="container-2">
<input type="text" class="endDate" name="variable" value="2019-11-24">
<span class="check"></span>
<br>
</div>
<div class="container-3">
<input type="text" class="endDate" name="variable" value="2019-11-27">
<span class="check"></span>
</div>

注意事项 如果可以在 views.py 上进行验证,则不太确定为什么要在前端进行验证。 @Robin Zigmond 给出了一个非常好的建议,即只在后端执行此操作并使用结果填充模板。

【讨论】:

只有当我提供静态不同的输入标签时,您的代码才有效。但就我而言,日期是动态的,所以我不知道它的计数,因此不能使用多个输入标签。 是的,我可以在后端使用它来工作。我只是没有想到它,但感谢@Robin ZIgmond。 太棒了,他的建议确实可行。编码愉快!

以上是关于JS 代码不适用于从 Django 模型中检索到的所有数据的主要内容,如果未能解决你的问题,请参考以下文章

未检索到特定集合 Firebase 的数据

Django覆盖模型get()方法不适用于外键

django-rest-swagger 是不是不适用于模型序列化器?

Django dumpdata 和 loaddata 不适用于多对多中间模型

Python Django - 模板标签中的 % request.user.is_authenticated % 不适用于 JS

@ModelAttribute 不适用于 Spring Security