What is the best way to utilize the same reasoning for numerous recurring components?

Is there a way to implement dropdown functionality for multiple "cards" without duplicating code for each card in Javascript?

Here is an example of the current code that only works for the first card:

HTML:

<div class="ideanode">
    <div class="ideanodeheader">Need</div>
    <div class="content">

        <div class="title">
            <h3 contenteditable="True" onclick='this.focus();'>Title</h3>
        </div>
        <i class="fas fa-sort-down title-arrow"></i>
        <div class="maintext">
            <textarea placeholder="Text" class="maintextinput"></textarea>
        </div>
        <i class="fas fa-sort-down maintxt-arrow"></i>
        <div class="comments">
            <textarea placeholder="Comments" class="commentsinput"></textarea>
        </div>
    
    </div>
</div>

Javascript:

var maintxt = document.querySelector(".maintext");
var title = document.querySelector(".title");
var titleArrow = document.querySelector(".title-arrow");
var mainArrow = document.querySelector(".maintxt-arrow");
var comments = document.querySelector(".comments");
var mainArrows = document.querySelectorAll(".maintxt-arrow")

titleArrow.addEventListener('click', function() {
    maintxt.classList.toggle("hidden");
    mainArrow.classList.toggle("hidden");
    if (comments.classList.contains("hidden")){
        ;
    } else {
        comments.classList.toggle("hidden");    
    };
});

mainArrow.addEventListener("click", function() {
    comments.classList.toggle("hidden");
});

Code Example: https://codepen.io/ricodon1000/pen/Baoxwed The goal is to have the dropdown arrows trigger the boxes below them. The upper arrow should toggle the box directly below it, and if both are open, they should close when one is clicked. The lower arrow should only toggle the box directly beneath it.

Answer №1

Apply the same process to each individual node and restrict your usage of querySelector to the specific group.

const nodesArray = [...document.querySelectorAll('.node')];

nodesArray.forEach(node => {

  const textContent = node.querySelector(".text-content");
  const heading = node.querySelector(".heading");
  const headingArrow = node.querySelector(".heading-arrow");
  const contentArrow = node.querySelector(".text-content-arrow");
  const userComments = node.querySelector(".user-comments");

  headingArrow.addEventListener('click', function() {
    textContent.classList.toggle("hidden");
    contentArrow.classList.toggle("hidden");
    if (userComments.classList.contains("hidden")) {;
    } else {
      userComments.classList.toggle("hidden");
    };
  });

  contentArrow.addEventListener("click", function() {
    userComments.classList.toggle("hidden");
  });
});

Answer №2

In this scenario, you have several options at your disposal. However, the commonality among them is that upon clicking on a .title-arrow element, you are able to identify which specific .title-arrow was clicked by accessing its value through this (and event.currentTarget) within the event callback. Subsequently, using various DOM properties and methods like closest or the element version of querySelector, you can locate the other related elements.

One approach could involve iterating through all the .title-arrow elements and attaching a click event listener to each, followed by determining the necessary actions from there. Alternatively, utilizing event delegation might be more suitable: Attaching click event handling only once to the container housing the various .ideanode elements, and then discerning the required action based on event.target.

Assuming that all the .ideanode elements reside within a container structure similar to:

<div class="container">
    <div class="ideanode">
            <div class="ideanodeheader">Need</div>
            <div class="content">
                <div class="title">
                    <h3 contenteditable="True" onclick='this.focus();'>Title</h3>
                </div>
                <i class="fas fa-sort-down title-arrow">v</i>
                <div class="maintext">
                    <textarea placeholder="Text" class="maintextinput"></textarea>
                </div>
                <i class="fas fa-sort-down maintxt-arrow">v</i>
                <div class="comments">
                    <textarea placeholder="Comments" class="commentsinput"></textarea>
                </div>
            </div>
    </div>
    <div class="ideanode">
            <div class="ideanodeheader">Need</div>
            <div class="content">
                <div class="title">
                    <h3 contenteditable="True" onclick='this.focus();'>Title</h3>
                </div>
                <i class="fas fa-sort-down title-arrow">v</i>
                <div class="maintext">
                    <textarea placeholder="Text" class="maintextinput"></textarea>
                </div>
                <i class="fas fa-sort-down maintxt-arrow">v</i>
                <div class="comments">
                    <textarea placeholder="Comments" class="commentsinput"></textarea>
                </div>
            </div>
    </div>
    <!-- ...and so forth... -->
</div>

Therefore, the following implementation is feasible (refer to comments):

// Single handler attached to the container
document.querySelector(".container").addEventListener("click", function(event) {
    // Identifying the clicked arrow and its corresponding .ideanode, if any
    const arrow = event.target.closest(".title-arrow, .maintxt-arrow");
    const ideanode = arrow && arrow.closest(".ideanode");
    if (!ideanode || !this.contains(ideanode)) {
        // Click did not target a .title-arrow or .maintxt-arrow within an .ideanode
        return;
    }
    if (arrow.matches(".title-arrow")) {
        // A .title-arrow was clicked
        titleArrowClick.call(arrow, ideanode, event);
    } else {
        // A .maintxt-arrow was clicked
        mainArrowClick.call(arrow, ideanode, event);
    }
});

function titleArrowClick(ideanode, event) {
    // Applying `querySelector` to find elements within .ideanode
    ideanode.querySelector(".maintext").classList.toggle("hidden");
    ideanode.querySelector(".maintxt-arrow").classList.toggle("hidden");
    const comments = ideanode.querySelector(".comments");
    if (comments.classList.contains("hidden")){
        ;
    } else {
        comments.classList.toggle("hidden");    
    };
}

function mainArrowClick(ideanode, event) {
    ideanode.querySelector(".comments").classList.toggle("hidden");
}

Live Example:

// Single handler attached to the container
document.querySelector(".container").addEventListener("click", function(event) {
    // Identifying the clicked arrow and its corresponding .ideanode, if any
    const arrow = event.target.closest(".title-arrow, .maintxt-arrow");
    const ideanode = arrow && arrow.closest(".ideanode");
    if (!ideanode || !this.contains(ideanode)) {
        // Click did not target a .title-arrow or .maintxt-arrow within an .ideanode
        return;
    }
    if (arrow.matches(".title-arrow")) {
        // A .title-arrow was clicked
        titleArrowClick.call(arrow, ideanode, event);
    } else {
        // A .maintxt-arrow was clicked
        mainArrowClick.call(arrow, ideanode, event);
    }
});

function titleArrowClick(ideanode, event) {
    // Applying `querySelector` to find elements within .ideanode
    ideanode.querySelector(".maintext").classList.toggle("hidden");
    ideanode.querySelector(".maintxt-arrow").classList.toggle("hidden");
    const comments = ideanode.querySelector(".comments");
    if (comments.classList.contains("hidden")){
        ;
    } else {
        comments.classList.toggle("hidden");    
    };
}

function mainArrowClick(ideanode, event) {
    ideanode.querySelector(".comments").classList.toggle("hidden");
}
.hidden {
    display: none;
}
<div class="container">
    <div class="ideanode">
            <div class="ideanodeheader">Need</div>
            <div class="content">

                <div class="title">
                    <h3 contenteditable="True" onclick='this.focus();'>Title</h3>
                </div>
                <i class="fas fa-sort-down title-arrow">v</i>
                <div class="maintext">
                    <textarea placeholder="Text" class="maintextinput"></textarea>
                </div>
                <i class="fas fa-sort-down maintxt-arrow">v</i>
                <div class="comments">
                    <textarea placeholder="Comments" class="commentsinput"></textarea>
                </div>
            </div>
    </div>
    <div class="ideanode">
            <div class="ideanodeheader">Need</div>
            <div class="content">

                <div class="title">
                    <h3 contenteditable="True" onclick='this.focus();'>Title</h3>
                </div>
                <i class="fas fa-sort-down title-arrow">v</i>
                <div class="maintext">
                    <textarea placeholder="Text" class="maintextinput"></textarea>
                </div>
                <i class="fas fa-sort-down maintxt-arrow">v</i>
                <div class="comments">
                    <textarea placeholder="Comments" class="commentsinput"></textarea>
                </div>
            </div>
    </div>
    <!-- ...and so on... -->
</div>

Answer №3

One approach to handling this situation is by starting with a simple array of classnames and gradually transforming it into an Object with keys and values representing DOM nodes.

When dealing with events, it's advisable to utilize event delegation to avoid binding individual events to each card. By ensuring they share the same parent node, you can bind the events there instead.

// initial setup
var elements = ['maintext', 'title-arrow', 'maintxt-arrow'].reduce((acc, name) => {
  acc[name] = document.querySelector(`.${name}`)
  return acc
  }, {}
)

// iterating through each "element"
for( let element in elements )
  console.log(element)
<div class='maintext'></div>
<div class='title-arrow'></div>
<div class='maintxt-arrow'></div>

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

Reset functionality for input selector is malfunctioning

My HTML code looks like this: <input type="number"> <br> <input type="number"> <br> <input type="number"> <br> <button>+</button> In my JS script, I have the following: $('input').on('c ...

Check if a user is currently on the identical URL using PHP and JavaScript

In my Laravel and AngularJS project, I have a functionality where users can view and edit a report. I'm looking to add a feature that will prevent multiple users from editing the report at the same time - essentially locking it while one user is makin ...

Tips for creating a substitute for a standard prototype in JavaScript

Having trouble with a JavaScript interpreter that has a buggy Array.prototype.sort. I want to replace it, at least for testing purposes. I have a function called mySort(array, comparator), but how can I make it work like array.sort(comparator)? I also ne ...

Mastering Angular's ngFor directive allows for powerful manipulation of arrays in the front-end

My goal is to use the last n elements from the orbit array, a task easily achieved with | slice: -n. However, I am facing an issue where in each iteration, I not only need access to the respective item but also its successor. The problem lies in the fact t ...

Extract data dynamically from multiple JSON arrays using AngularJS

In the task at hand, I am faced with multiple JSON arrays and my goal is to extract specific data using a loop. Specifically, I am interested in obtaining the value of the key count. Let's take a look at the code snippet: .then(function(){ var t ...

Error handling in AngularJS and Firebase Promises

I am currently following a tutorial on thinkster.io and encountering some issues. The tutorial uses a deprecated SimpleLogin, so I have made the necessary code changes. However, I keep running into an error that says: TypeError: Cannot read property &apo ...

Move a div by dragging and dropping it into another div

Situation Within my project, there is a feature that involves adding a note to a section and then being able to move it to other sections, essentially tracking tasks. I have successfully implemented the functionality to dynamically add and drag notes with ...

The metallic material in Substance Painter appears as a dark shade in A-Frame

After exporting an object as gltf from Substance Painter, I am importing it into my A-frame scene. Strangely, the metallic outer material appears dark in my current scene while other materials like plastic appear white and are visible. Despite already ha ...

Using a customized layout, merge AngularJS into Vaadin

I experimented with integrating an angular JS application into Vaadin by utilizing a custom layout as shown below: VerticalLayout mainLayout = new VerticalLayout(); mainLayout.setMargin(true); mainLayout.setWidth("1380px"); setCompositionRoot( ...

Unable to execute multiple instances of Selenium PhantomJS concurrently

I have encountered an issue while using Selenium's node.js API to run PhantomJS instances against a series of web pages. The code that I have written to perform actions on the pages is functioning correctly, but it appears that only one instance of Se ...

Efficiently uploading numerous SVGs in vue.js using vue-svg-loader

Is there a more efficient way to import multiple SVGs in a Vue component without having to individually import each one as suggested in the documentation? Check out vue-svg-loader documentation: <script> import Info from "@/assets/svgs/info.svg"; ...

changing the css transformation into zoom and positioning

My goal is to incorporate pinch zoom and panning using hardware-accelerated CSS properties like transform: translate3d() scale3d(). After completing the gesture, I reset the transform and switch to CSS zoom along with absolute positioning. This method ensu ...

What is the best way to set a timer to count down to 00:00, pause on this time for a bit, and then switch to a

Currently, I am working on my own version of the 25 + 5 Clock. You can view my codepen version here: https://codepen.io/faber_g/pen/WNJOOwp Here is an example from Freecodecamp along with the tasks to complete: https://codepen.io/freeCodeCamp/full/XpKrrW ...

Retrieve the HTML content from a page that has been dynamically rendered using JavaScript

Looking for a way to retrieve HTML content from a page where the source code is rendered by JavaScript. I need to use this content in my C# app, but so far using WebForm has yielded no results. I'm working on an external dll (not a console or windows ...

Fluctuating Visibility with Jquery Show()

<html> <head> <script type="text/javascript"> var oauth = chrome.extension.getBackgroundPage().oauth; </script> <style type="text/css"> #note { display:none; } </style> <script src="Task.js" type="text/javascript"> ...

Error: An unidentified SyntaxError occurred with an unexpected token < within an anonymous function displayed on the console

In the process of developing an upload file application. The implementation involves AJAX and PHP. Everything functions smoothly on the localhost, but upon transferring it to the web server, a specific error is encountered: Uncaught SyntaxError: Unexpe ...

Electronic circuit embedded within a material-textured text field offering multiline functionality

While experimenting with TagsInput, I came across this helpful snippet on codesandbox that you can check out here. The challenge I encountered is when there are numerous chips, they extend beyond the boundaries of the text field. My goal is to implement ...

How to Add a Foreign Component to Your Vue Application

I'm trying to utilize a NPM package called vue-simple-spinner in my Vue CLI webpack application, but I keep encountering this error message: Unknown custom element: <vue-simple-spinner> - have you registered the component correctly? For recursi ...

Exploring beyond zero

In the image below, I am able to retrieve data from [0] using the following Node.js code snippet: const stockxAPI = require('stockx-api'); const stockX = new stockxAPI(); stockX.searchProducts('yeezy', { limit: 1 }) .then(products ...

How to install Typed.js without using Node.js

I'm currently working on a project that requires the JavaScript library typed.js, however, I am using the free webhost 000webhost which does not support Node.js. Since typed.js is typically installed using Yarn, NPM, or Bower - all of which require No ...