Having EventListeners set up across a single CSS class returns null when applied to different types of elements simultaneously

I want to create floating labels that resize dynamically when an input is clicked, similar to modern forms. I am using vanilla JS exclusively for this task.

Currently, the setup works with the <input> tag. However, it does not work with the <textarea> tag. Despite spending a significant amount of time trying to troubleshoot this issue, I cannot figure out why the <textarea> element is returning null when I attempt to add the .active class to it. This class is essential for the font and animation to function correctly. Below is the current JS script:

  
    // 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');
      console.log(document.querySelectorAll('.float-container'));
      floatContainers.forEach((element) => {
        
        if (element.querySelector('input').value) {
          element.classList.add('active');
        }
        
        bindEvents(element);
      });
    };
    
    return {
      init: init
    };
  })();
  
   window.onload=FloatLabel.init();

Despite confirming that the <textarea> element is present in the list of elements retrieved, attempting to add the active class when checking for text inside the textarea results in a null value and breaks the script.

I'm uncertain if there is something I'm overlooking or if there is a fundamental aspect of the problem that I do not understand. I have verified that the TypeError is not related to the DOM not being ready. Additionally, the script is positioned at the bottom of the PHP file and the defer keyword has been added as a precaution. While I understand that using defer on a script at the bottom of the document may not have any impact, I have included it nonetheless. Below are the CSS classes used:


.float-container {
    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;
}

Lastly, here is a snippet of the form from the document:


<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>

Answer №1

I suggest assigning a class like input to your inputs and textareas.
This way, there's no need to check the field type and current value.

// 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>

The init function can be simplified using a ternary operator to check the current value:

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);
  });
};

Answer №2

When reviewing your code, consider implementing the optional chaining operator to verify the existence of the querySelector('textarea') return object before accessing its value.

It appears that the bindEvents function is only adding the event listener to input elements, neglecting textarea elements.

Additional note: more details have been added.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to include a form within every row of an HTML datatables structure?

I have a regular table that is populated with data fetched via Ajax, and it appears like this: Ajax <script> $(document).ready(function() { $('#mytable').DataTable( { "ajax": "myurl", "dataType": 'json', ...

The registration feature powered by JQuery is experiencing technical difficulties and not functioning

Having trouble with a registration system on my website at *. When someone registers, it should either show an error message or display "true" if the registration is successful. I have a javascript file (http://pastebin.com/mv9CWZcT) set up to redirect the ...

jQuery can't capture form submission

I've been trying to implement this seemingly simple piece of code, but no matter what I do, it just won't work. I've followed several examples online and even tested it on both IE and Chrome without success. One thing that did work was testi ...

Problem with Onsen UI navigation: It is not possible to provide a "ons-page" element to "ons-navigator" when attempting to navigate back to the initial page

Hi, I am having trouble with navigation using Onsen UI. Here is the structure of my app: start.html: This is the first page that appears and it contains a navigator. Clicking on the start button will open page1.html page1.html: Performs an action that op ...

Angular Recursive Bootstrap Breadcrumb Guide

I am looking to implement the bootstrap breadcrumb component (https://getbootstrap.com/docs/4.0/components/breadcrumb/) in my project. My goal is to use the breadcrumb to show the entire path to the current directory within a component that resembles a di ...

When you hit a certain point on the website, the scrolling momentarily pauses

Upon refreshing the page and scrolling down, I notice that the website experiences a brief lag for a few milliseconds before continuing as normal. Oddly enough, this issue only occurs after refreshing the page. Any suggestions on how to resolve this? Th ...

What is the best way to send information from child components to their parent in React

I'm facing a scenario where I need to increase the parent value based on actions taken in the children components. Parent Component: getInitialState :function(){ return {counter:0} }, render(){ <CallChild value={this.state.counter}/> ...

Content not aligned in the center of the page on Internet Explorer

Recently, I've taken on the responsibility of managing the content for this website after it was passed down from the previous developer. Each page's content is contained within a div element with the class "ct", which has auto margins applied t ...

Posting forms in NextJS can be optimized by utilizing onChange and keypress events for input fields

I am currently working on my first Edit/Update form within a newly created NextJs application as I am in the process of learning the framework. I seem to be facing an issue where the form constantly posts back to the server and causes the page to refresh ...

Animate the CSS when the content is within the viewport using pagepiling.js integration

I'm currently working on animating content as it enters the viewport. However, I've encountered an issue where jQuery (used to check if the content is in the viewport) isn't functioning properly alongside pagepiling.js (). I suspect this mig ...

POST request body is not defined

Client Interface: procedure OnButtonClick(Sender: TObject); begin gcm := GetGCMInstance; p := TJavaObjectArray<JString>.Create(1); p.Items[0] := StringToJString('460004329921'); FRegistrationID := JStringToString(gcm.register(p)); ...

Variations in jQuery's append method when dealing with a string, a reference to a jQuery object

Here are multiple ways to add a div element to the body of an HTML document. But what distinctions exist between them and in what scenarios should each be utilized for optimal performance? var newDiv = '<div id="divid"></div>'; $(&ap ...

What is the best way to split an AJAX response into different variables and effectively retrieve each one of them?

When using AJAX to fetch data from a database and display it in a text box, most examples found online only show how to display the AJAX response in one text box. But what if we need to separate multiple PHP variables retrieved from the AJAX response and d ...

What is the best way to translate these CSS properties into Mui?

I am currently in the process of transitioning my CSS code to utilize MUI's sx prop and styled() function. However, I have not been able to find any documentation on how to properly convert the following code to MUI syntax. Can someone assist me with ...

Achieving Flexbox alignment in a responsive layout

Struggling with achieving a responsive layout using Flexbox. Picture a page filled with panels. The main objective is to display a certain number of panels per row, aligned both horizontally and vertically like the rest. The main issue I am encountering ...

"The jQuery colorpicker function is not functioning properly when applied to newly added elements

I've got these amazing gadgets with a cool sliding box feature inside. Initially, there are two widgets visible on the page, but you have the option to add or delete a widget as needed. Within the sliding box, there is a color picker tool. Interestin ...

Implement Acrobat JavaScript to enforce a mandatory separate field when a checkbox is selected

As a designer with limited coding skills, I have developed an acrobat form that includes several due date fields. These fields are only mandatory if a specific checkbox is selected. I am looking for the javascript code that will validate the requirement: ...

What is the best way to incorporate a particular locale from AngularJS I18n files with bower?

After successfully downloading the angular I18n repo by using bower install angular-i18n, it is now located in my bower_components directory and has updated the bower.json file with angular-i18n : 1.5.3 as expected. However, I am facing an issue where a s ...

Issue with submitting form on PHP page

I need some help with my signup form: <h2>Signup</h2> <form action="actions.php"> Email:<br> <input type="text" name="email"> <br> Password:<br> &l ...

Trouble with hide/show loop in setTimeout function

I have a special animation with 3 text items that are initially invisible. The goal is to make these items appear one by one with a delay of 2 seconds after clicking a button. Each item should be visible for 1 second before fading out and making way for th ...