Detecting a click outside of a div or its opening button and closing the element using Vanilla JS

When dealing with two buttons, one opens a form, and the other a dropdown.

The goal is to have both elements close when clicking outside. The dropdown functionality already works as expected because it closes whenever the user clicks outside the opening button.

However, the second button is not behaving as desired. It should close when the following logical formula is true:

  • (the opening button 'fulfillSetButton' isn't clicked) OR (the form 'dateForm' isn't clicked)

To achieve this, an event listener has been added to the entire HTML document with the logic inside an if statement:

html.addEventListener("click", function(e){
  if(e.target !== (dateForm || fulfillSetButton)){
    dateForm.classList.remove("active");
  }
});

The problem is that this solution is not working as expected. Any suggestions on what might be going wrong?

Answer №1

Here is an updated version:

window.addEventListener('click', function(e){
if( div.contains(e.target)){
    //click inside of element  
} else{
    //click outside of element
  }
});

I made some adjustments to the CSS and HTML as well.

// Accessing all elements
const dropdownButton = document.querySelector("#dropdownToggle"),
      fulfillSetButton = document.querySelector("#fulfillSetButton"),
      dropdownMenu = document.querySelector('.dropdown-menu'),
      html = document.querySelector("body"),
      dateForm = document.querySelector("#completion-date-id");
      
// Preventing default form submission action
function preventDefault(e) {
  e = e || window.event;
  if (e.preventDefault) {
    e.preventDefault();
  } else {
    e.returnValue = false;
  }
}

// Toggle dropdown menu visibility *working*
dropdownButton.addEventListener("click", function () {
    dropdownMenu.classList.toggle("show");
});

// Close dropdown when clicked outside *new way works everywhere on page*
window.addEventListener('click', function(e){
if( dropdownButton.contains(e.target)){
  } else{
    dropdownMenu.classList.remove("show");
  }
});

// Opening the date form
fulfillSetButton.addEventListener("click", function() {
    dateForm.classList.add("active");
});

// Closing the date form by submitting
dateForm.lastElementChild.addEventListener(
  "click", function () {
    preventDefault();
    dateForm.classList.remove("active");
  }
);

// Closing the date form by clicking outside *now working*
window.addEventListener('click', function(e){
    
if( dateForm.contains(e.target) || fulfillSetButton.contains(e.target)){
  } else{
    dateForm.classList.remove("active");
  }
});

:root {
  --trans-left:#84fab0;
  --trans-right:#8fd3f4;
  --background: #fff;
  --color: #222;
}

/* More CSS styles here */

.active{
display: block;
}
<body>
<section class="entry">
        <form autocomplete="off">
          <input type="text" id="taskText">
          <div class="second-item">
            <button type="button" id="fulfillSetButton">Date form</button>
            <div class="dropdown-wrap">
              <button class="btn dropdown" type="button" id="dropdownToggle">Dropdown button</button>
              <ul class="dropdown-menu" id="taskPriority" style="background: linear-gradient(0deg, rgba(132,250,176,1) 0%, rgba(132,250,176,1) 33%, rgba(255,255,255,1) 33%, rgba(255,255,255,1) 50%, rgba(255,255,255,1) 66%, rgba(255,160,160,1) 66%, rgba(255,160,160,1) 100%); color: #000">
                <li>High</li>
                <li>Medium</li>
                <li>Low</li>
              </ul>
            </div>
            <button type="submit">Submit</button>
          </div>
        </form>
        <form id="completion-date-id" class="completion-date active">
        
        /* More HTML code here */
        
      </form>
      </section>
</body>

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

Opening new windows in Chrome after an AJAX request behaves like a pop-up

When a user clicks a button in my application, an ajax request is triggered. Following the success of this request, I generate a URL which I intend to open in a new tab. Unfortunately, when using Chrome and calling window.open within the success handler, t ...

Emailer: Missing Salutation

While attempting to send emails using Node with Nodemailer (https://github.com/nodemailer/nodemailer), the sendMail call from the Nodemailer transporter is throwing an error message of Greeting never received when connected to an Ethereal test email accoun ...

Error message in JS/Ajax alert box

I keep receiving an alert box saying "Image uploaded" even when the variable $imagename is empty. Below is the script in question: <script> function ajax_post1(ca){ var cat = ca; var name = document.getElementById("name").value; var ...

Prevent clicking on a div using Jquery

Imagine I have a simple click event set up for an HTML div element. When the div is clicked, it should trigger a fadeToggle effect. Inside that div, there is another nested div with its own click event. Upon clicking this inner div, it is supposed to do s ...

What is the best way to personalize the Window.Confirm() dialog in JavaScript?

var val= confirm("Are you sure to cancel?"); The code snippet above will display a popup with two choices - Ok and Cancel, with Ok being the default choice. Is there a way to make Cancel the default choice instead and switch the positions of the ...

Attempting to retrieve nested data, only to be met with an undefined response

I've been attempting to retrieve specific information by iterating through the SearchResult object like so: for (let productKey in SearchResult) { if (SearchResult.hasOwnProperty(productKey)) { products.push({ name ...

Is there a way to retrieve the number of notifications that have not been seen or read from the API?

Is there a way to retrieve the unread or unseen count in PHP without relying on a real-time notifications JavaScript library? Typically, using a JavaScript library would provide the following information: "data": { "deleted": "array of activities or ...

Utilizing jQuery to correspond with CSS media queries

Continuing from my previous question about an automatic jQuery image slider, you can refer to it here. I have made some changes in my CSS using media queries and I am trying to incorporate them into my JavaScript code using an 'if / else if' sta ...

Prevent floating labels from reverting to their initial position

Issue with Form Labels I am currently in the process of creating a login form that utilizes labels as placeholders. The reason for this choice is because the labels will need to be translated and our JavaScript cannot target the placeholder text or our de ...

Can you explain the role of the faceVertexUV array within the three.js Geometry class?

Currently, I am utilizing three.js to create curved shapes using parametric functions. Within the THREE.js javascript file, there is a function called THREE.ParametricGeometry that continuously adds 2D vectors to the faceVertexUvs array. I am curious abo ...

Grab a parameter from the URL and insert it into an element before smoothly scrolling down to that

On a button, I have a URL that looks like this: www.mywebsite.com/infopage?scrollTo=section-header&#tab3 After clicking the button, it takes me to the URL above and opens up the tab labeled tab3, just as expected. However, I would like it to direct m ...

Retrieving string-based JSON information

Within my text box, the user inputs strings separated by commas. These strings are split on the front end, then sent to the backend to retrieve data in JSON format. The interesting part is that when I directly entered the key of the JSON, like this, it wo ...

What is the proper way to incorporate a standalone Vue library into a Vue application?

I'm currently facing an issue with integrating a vue component library into my vue application. This component library is loaded by a script tag and is set up with webpack using the externals setting to exclude vue dependencies. Therefore, the host bu ...

Load the ajax content only if the specific class matches the given class name "x

I've created the code below with the expectation that it will only load AJAX if the content hasn't already been loaded into the div. Essentially, when I use AJAX to load the content and assign a class name based on which content is currently disp ...

What is the best way to extract a specific value from a line of data using JavaScript (JSON)?

My current task involves extracting the "correctAnswers" from a specific number. Let's take a look at this JSON example: { "questions": [ { "number": 3, "question": "☀️ ➕ ...

Utilizing a different file to arrange and establish state

In a separate file called Origin.js, I have some data (objects) stored. This data is exported using the spread operator within an object named OriginState: Origin.js //info const info = { title: '', year: '', }; //images const ...

Unleashing the Power of Resetting CSS Class Attributes in Twitter Bootstrap

Last year, @Andres Ilich shared a remarkably elegant solution for centering the navigation bar in Bootstrap. Here is an excerpt from the answer he posted: Visit here for more details. <div class="navbar navbar-fixed-top center"> <div class=" ...

Angular 5 - Creating a dynamic function that generates a different dynamic function

One of my latest projects involved creating a versatile function that generates switch-case statements dynamically. export function generateReducer(initialState, reducerName: ReducerName, adapter: EntityAdapter<any>): (state, initialState) => ISt ...

The inputmask is triggering an unhandled RangeError due to surpassing the maximum call stack size

I need to dynamically set a regex pattern on the idnumber field by selecting a different value from the idtype dropdown. Everything works smoothly until I choose the last option which contains a "?" character, causing the page to become unresponsive and di ...

The creation of the ESLint CLIEngine encountered some issues

Encountered an issue while setting up the ESLint CLIEngine - 'basePath' must be an absolute path Attempting to utilize eslint $ npx prettier-eslint **/*.js However, receiving the following error message: prettier-eslint [ERROR]: Encountered a ...