【问题标题】:Setting up EventListeners across singular CSS class comes up null when used with multiple element types当与多种元素类型一起使用时,跨单个 CSS 类设置 EventListeners 为 null
【发布时间】:2022-01-20 13:13:51
【问题描述】:

我正在尝试使浮动标签在您单击输入时动态调整自身大小,就像今天的更多表单一样。我正在使用 vanilla JS 并专门使用 vanilla JS 来解决这个问题。

我目前已对其进行了设置,以便它可以与标签一起使用。但是,对于我的标签,它没有。出于某种原因,每次我在一两天内研究这个问题或寻找然后走开以重置自己时,当我尝试将 .active 类添加到它时,我看不到为什么我的元素出现 null ,所以以使字体和动画正常播放。这是 JS 脚本,因为它现在可以运行

  
    // add active class
    const handleFocus = (e) => {
      const target = e.target;
      target.parentNode.classList.add('active');
      target.setAttribute('placeholder', target.getAttribute('data-placeholder'));
    };
    
    // remove active class
    const handleBlur = (e) => {
      const target = e.target;
      if(!target.value) {
        target.parentNode.classList.remove('active');
      }
      target.removeAttribute('placeholder');
    };
    
    // register events
    const bindEvents = (element) => {
      const floatField = element.querySelector('input');
      floatField.addEventListener('focus', handleFocus);
      floatField.addEventListener('blur', handleBlur);   
    };
    
    // get DOM elements
    const init = () => {
      const floatContainers = document.querySelectorAll('.float-container');
      //I can see the textarea element in this list.
      console.log(document.querySelectorAll('.float-container'));
      floatContainers.forEach((element) => {
        
        if (element.querySelector('input').value) {
          element.classList.add('active');
        }
        //The commented section below causes a "TypeError querySeclector is null"
        /*
        if (element.querySelector('textarea').value) {
          element.classList.add('active');
        }
        */
        bindEvents(element);
      });
    };
    
    return {
      init: init
    };
  })();
  
   window.onload=FloatLabel.init();

正如脚本的注释部分所述,当我检查 console.log 以获取它抓取的元素列表时,我可以在该列表中看到 。但是,当我尝试添加活动类以检查文本是否在文本区域内时,它会出现 null 并破坏脚本。

我不确定是否有什么我没有看到,或者我是否根本不知道/不了解正在发生的事情的一部分。我还检查以确保我得到的 TypeError 与 DOM 未准备好无关。我还确保脚本位于 php 文件的底部,并且为了更好地添加了 defer 关键字。虽然,根据我对 JS 的了解,将 defer 放在文档底部的脚本上并没有做任何事情,但我离题了。这是涉及的 CSS 类。

{
    width: 66%;
    position: relative;
}

.float-container.small input
{
    width: 50%;
    outline-offset: 1px;
    font-size: 16px;
    padding: 16px 4px 10px 4px;
    border-radius: 4px;
}

.float-container.small textarea
{
    width: 100%;
    outline-offset: 1px;
    font-size: 16px;
    padding: 16px 4px 10px 4px;
    border-radius: 4px;
    color: red;
}

.float-container.large input 
{
    width: 100%;
    outline-offset: 1px;
    font-size: 16px;
    padding: 16px 4px 10px 4px;
    border-radius: 4px;
}

.float-container.active
{
    border: 2px solid #1A57FF;
    outline: 2px solid rgba(26, 87, 255, 0.3);
    border-radius: 4px;
}

.float-container.active label
{
    transform: translate(0, 4px) scale(.75);
}



.floating-text
{
    position: absolute;
    font-size: 16px;
    text-decoration: underline;
    color: rgb(100, 100, 100);
    transform: translate(0, 16px) scale(1);
    transform-origin: top left;
    transition: all .1s ease-in-out;
}

最后,这里是来自文档的表单部分

<div id="floatContainer" class="float-container small">
                    <label for="fName" class="floating-text">First Name</label>
                    <input type="text" id="fName" name="fName" data-placeholder="First name"><br>
                </div>
                <p id="fName-validate" class="form-validate-text"><p>
                <div id="floatContainer" class="float-container small">
                    <label for="lName" class="floating-text">Last Name</label>
                    <input type="text" id="lName" name="lName" data-placeholder="Last name"><br>    
                </div>
                <p id="lName-validate" class="form-validate-text"><p>
                <div id="floatContainer" class="float-container small">
                    <label for="email" class="floating-text">Email</label>
                    <input type="text" id="email" name="email" data-placeholder="Email"><br>
                </div>
                <p id="email-validate" class="form-validate-text"><p>
                <div id="floatContainer" class="float-container small active">
                    <label for="phoneNumber" class="floating-text">Phone Number</label>
                    <input type="tel" id="phoneNumber" name="phoneNumber" data-placeholder="Phone"><br>
                </div>
                <p id="phoneNumber-validate" class="form-validate-text"><p>
                <div id="floatContainer" class="float-container small">
                    <label for="problemDesc" class="floating-text">Description of problem</label>
                    <textarea id="problemDesc" name="problemDesc"></textarea><br>
                </div>

【问题讨论】:

  • 大多数.float-container 元素中没有任何textarea(只有最后一个有),所以element.querySelector('textarea') 在这些返回null 上并尝试阅读.value会引发错误。
  • @T.J.Crowder 对,但是当其中有文本时,我如何让脚本检查/更新 textarea 的类?
  • 只需添加一个守卫:const textArea = element.querySelector("textarea"); if (textArea) { /*...code to handle text area here...*/ }.
  • 它不再抛出错误,脚本也没​​有崩溃,但是不幸的是文本区域仍然没有响应。

标签: javascript html css forms


【解决方案1】:

我宁愿推荐给你的输入和文本区域一个像input这样的类。
这样您就不必检查字段的类型及其当前值。

// add active class
const handleFocus = (e) => {
  const target = e.target;
  target.parentNode.classList.add("active");
  target.setAttribute("placeholder", target.getAttribute("data-placeholder"));
};

// remove active class
const handleBlur = (e) => {
  const target = e.target;
  if (!target.value) {
    target.parentNode.classList.remove("active");
  }
  target.removeAttribute("placeholder");
};


// register events
const bindEvents = (element) => {
  const floatField = element.querySelector(".input");
  if (floatField) {
    floatField.addEventListener("focus", handleFocus);
    floatField.addEventListener("blur", handleBlur);
  }
};

// get DOM elements
const init = () => {
  const floatContainers = document.querySelectorAll(".float-container");
  floatContainers.forEach((element) => {
    let input = element.querySelector(".input");
    let inputVal = input ? input.value : "";
    if (inputVal) {
      input.classList.add("active");
    }
    bindEvents(element);
  });
};


init();
/*{
    width: 66%;
    position: relative;
}
*/
* {
  box-sizing: border-box
}


.input{
  border: 2px dotted #000;
  width: 100%!important;
}

.float-container.small input
{
    width: 50%;
    outline-offset: 1px;
    font-size: 16px;
    padding: 16px 4px 10px 4px;
    border-radius: 4px;
}

.float-container.small textarea
{
    width: 100%;
    outline-offset: 1px;
    font-size: 16px;
    padding: 16px 4px 10px 4px;
    border-radius: 4px;
    color: red;
}

.float-container.large input 
{
    width: 100%;
    outline-offset: 1px;
    font-size: 16px;
    padding: 16px 4px 10px 4px;
    border-radius: 4px;
}

.float-container.active
{
    border: 2px solid #1A57FF;
    outline: 2px solid rgba(26, 87, 255, 0.3);
    border-radius: 4px;
}

.float-container.active label
{
    transform: translate(0, 4px) scale(.75);
}



.floating-text
{
    position: absolute;
    font-size: 16px;
    text-decoration: underline;
    color: rgb(100, 100, 100);
    transform: translate(0, 16px) scale(1);
    transform-origin: top left;
    transition: all .1s ease-in-out;
}
<div id="floatContainer" class="float-container small">
  <label for="fName" class="floating-text">First Name</label>
  <input type="text" id="fName" class="input" name="fName" data-placeholder="First name"><br>
</div>
<p id="fName-validate" class="form-validate-text">
<p>
<div id="floatContainer" class="float-container small">
  <label for="lName" class="floating-text">Last Name</label>
  <input type="text" id="lName" class="input" input name="lName" data-placeholder="Last name"><br>
</div>
<p id="lName-validate" class="form-validate-text">
<p>
<div id="floatContainer" class="float-container small">
  <label for="email" class="floating-text">Email</label>
  <input type="text"  class="input" id="email" name="email" data-placeholder="Email"><br>
</div>
<p id="email-validate" class="form-validate-text">
<p>
<div id="floatContainer" class="float-container small --active">
  <label for="phoneNumber" class="floating-text">Phone Number oi</label>
  <input type="tel" id="phoneNumber"  class="input" name="phoneNumber" data-placeholder="Phone"><br>
</div>
<p id="phoneNumber-validate" class="form-validate-text">
<p>
  
<div id="floatContainer" class="float-container small">
  <label for="problemDesc" class="floating-text">Description of problem</label>
  <textarea id="problemDesc" class="input" name="problemDesc"></textarea><br>
</div>

init 函数可能如下所示(使用三元运算符检查当前值):

const init = () => {
  const floatContainers = document.querySelectorAll(".float-container");
  floatContainers.forEach((element) => {
    let input = element.querySelector(".input");
    let inputVal = input ? input.value : "";
    if (inputVal) {
      input.classList.add("active");
    }
    bindEvents(element);
  });
};

【讨论】:

  • 嘿,只是退后一步重新开始工作。是的,一切正常,只是一个简单的改变。不知道为什么我自己不考虑这个。不管怎样,谢谢。被选为答案。
  • @NorthernDev:谢谢,但我也想知道,如果您真的需要通过 js 更改显示。您还可以使用 .float-container:focus-within label 通过 css 切换您的输入样式 - 只是一个想法(不要误会我的意思:我不是 css-only-at-any-price-fan)
  • 你提出了一个很好的观点。我喜欢根据个人需要使用尽可能少的 JS。老实说,我不确定我是否知道 :focus-within 伪类。我的印象是我无法仅使用 CSS 来实现这一点。我的前端技术还比较薄弱,所以我很欣赏 CSS 独有的方法。我现在就实施。非常感谢。
【解决方案2】:

对于您的注释代码:您可以使用 optional chaining operator 在访问值之前检查 querySelector('textarea') 返回对象是否存在。

您的 bindEvents 函数似乎将事件侦听器分配给输入元素,而不是输入和 textarea 元素。

编辑:更多信息。

【讨论】:

  • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
  • 不将元素传递给 bindEvents 做这项工作吗?如果我打印元素的日志,我可以在那里看到文本区域。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-09-20
  • 2019-12-04
  • 2012-07-28
  • 2011-11-01
  • 2018-01-30
  • 1970-01-01
  • 2021-11-26
相关资源
最近更新 更多