How to target CSS selectors to exclude any child elements that have a parent with a specific class at any level

Query

I am attempting to devise a CSS selector that targets all children within a specified parent, excluding them if any element along the path has a specific class.

Background

In my JavaScript project, I am developing a materialization class that transforms certain elements into their material counterparts. This process operates on a top-level application where users can create their own apps. I aim to specify that a particular set of elements should be exempt from this transformation.

Illustration

The following should be included:

<div>
  <input />
</div>

While this should not be selected:

<div class="no-material">
  <input />
</div>

The complexity arises because this exclusion label can appear anywhere. For example:

  <main>
    <section class="no-material">
      <form>
        <fieldset>
          <input />
        </fieldset>
      </form>
    </section>
  </main>

Or it might look like this:

  <main>
    <section>
      <form class="no-material">
        <fieldset>
          <input />
        </fieldset>
      </form>
    </section>
  </main>

Past Attempts

I have made several efforts to address this issue. The most effective approach so far is:

div:not(.no-material) > input:not(.no-material), div:not(.no-material) *:not(.no-material) input:not(.no-material)

However, this method still yields some false positives. One alternative involves increasing the specificity by adding numerous levels, such as:

div:not(.no-material) > input:not(.no-material),
div:not(.no-material) > *:not(.no-material) > input:not(.no-material),
div:not(.no-material) > *:not(.no-material) > *:not(.no-material) > input:not(.no-material)

This solution would require 20-50 levels (or more?), which is not very efficient.

Interactive Version

You can test your selectors by modifying the cssSelector in the provided JavaScript code snippet.

let cssSelector = [
  // Independent selectors
  'div:not(.no-material) > input:not(.no-material)',
  'div:not(.no-material) *:not(.no-material) input:not(.no-material)'
].join(',');

// This will get elements and run their names. We should get yes1-5, but not no1-5.
let inputs = document.querySelectorAll(cssSelector);
for (let input of inputs) console.log(input.getAttribute('name'));
<!-- Do not edit HTML, just the CSS selector -->

<main style="display: none;">

  <!-- Not selectable -->
  <div class="no-material">
    <input name="no-1">
  </div>

  <div>
    <input name="no-2" class="no-material">
  </div>

  <div>
    <label class="no-material">
      <input name="no-3">
    </label>
  </div>

  <div>
    <label class="no-material">
      <span>
        <input name="no-4">
      </span>
    </label>
  </div>

  <div>
    <label>
      <span class="no-material">
        <input name="no-5">
      </span>
    </label>
  </div>

  <!-- Selectable -->
  <div>
    <input name="yes-1">
  </div>

  <div>
    <input name="yes-2">
  </div>

  <div>
    <label>
      <input name="yes-3">
    </label>
  </div>

  <div>
    <label>
      <span>
        <input name="yes-4">
      </span>
    </label>
  </div>

  <div>
    <label>
      <span>
        <input name="yes-5">
      </span>
    </label>
  </div>

</main>
<!-- Do not edit HTML, just the CSS selector -->

Note: While other approaches exist, such as iterating through all children of an element with the '.no-material' class and assigning the 'no-material' class to each, this is resource-intensive. My preference is to resolve this issue using a CSS selector if feasible.

Thank you

Answer №1

To identify the elements (all), locate the elements with no-material on the element or its parent (no), and then eliminate the second set from the first set to determine the remaining elements (yes).

const difference = (a, b) => a.filter(elt => b.indexOf(elt) === -1);
  
const all = document.querySelectorAll("input");
const no = document.querySelectorAll(".no-material input, input.no-material");

const yes = difference([...all], [...no]);

console.log(yes.map(elt => elt.name));
<main style="display: none;">

  <!-- Not selectable -->
  <div class="no-material">
    <input name="no-1">
  </div>

  <div>
    <input name="no-2" class="no-material">
  </div>

  <div>
    <label class="no-material">
      <input name="no-3">
    </label>
  </div>

  <div>
    <label class="no-material">
      <span>
        <input name="no-4">
      </span>
    </label>
  </div>

  <div>
    <label>
      <span class="no-material">
        <input name="no-5">
      </span>
    </label>
  </div>

  <!-- Selectable -->
  <div>
    <input name="yes-1">
  </div>

  <div>
    <input name="yes-2">
  </div>

  <div>
    <label>
      <input name="yes-3">
    </label>
  </div>

  <div>
    <label>
      <span>
        <input name="yes-4">
      </span>
    </label>
  </div>

  <div>
    <label>
      <span>
        <input name="yes-5">
      </span>
    </label>
  </div>

</main>

Answer №2

If you want to take advantage of modern browsers, try utilizing css variables.

Start by defining it at the root level and then redefine it within your class:

:root {
    --mycolor: lightblue;
}

.container {
    --mycolor: lightgreen;
}

.test {
    background-color: var(--mycolor);
}
<div class="test">BASE</div>

<div class="container">
    <div class="test">BASE</div>
</div>

Answer №3

:not(.no-material, .no-material *)

This CSS code snippet will target all elements that do not have the class "no-material" or are not descendants of an element with the class "no-material."

The important thing to note here is the use of a space instead of the > selector, which you can learn more about at .

If you only want to select elements that are empty and marked as "yes," you can combine the above code with the :empty pseudo-selector like this:

:not(.no-material, .no-material *):empty

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

Troubleshooting CSS issues in a Docker container running a VueJs project

I have successfully set up a VueJs project on AWS using Docker. Following the guidelines in the Real-World Example section of the documentation, I created a Dockerfile with instructions to copy my nginx conf file: # build stage FROM node:lts-alpine as buil ...

storing data in local storage using JavaScript

As someone new to coding, my aim is to create a basic "daily planner" page where users can input text on different lines. I've been attempting to use local storage for this task, but seem to be running into some issues. I've experimented with get ...

Transmitting information together with FormData as a single entity through AJAX

I'm attempting to transmit a FormData within a JSON array to an AJAX script: $('#form').submit(function(e) { e.preventDefault(); let formData = new FormData(this), data = {'action': 'insert', 'data& ...

Multiple callbacks triggered by jQuery CSS transitions

I am experiencing an issue with my menu animation. I am curious as to why the slideDown transition is occurring twice. You can view the JSFiddle here. <ul class=top-menu"> <li>Item 1</li> <li>Item 2</li> <ul cl ...

The infinite scroll feature on Next.js resulted in duplicating the last element during the initial fetch process

I have a project that involves fetching data from Strapi Rest API using Next.js. I am fetching and displaying cards based on the fetched content. To prevent loading all the data at once, I have implemented an infinite scroll feature using react-infinite-sc ...

php failed to send values to ajax, experiencing an error

To test if everything is working correctly, I am attempting to send a value from AJAX to PHP and retrieve it. However, when I click on a button to run the test, an error occurs and 'Failed' alert pops up. How can I resolve this issue to achieve s ...

Experiencing an Issue with NGINX Loading Vue 3 Vite SPA as a Blank White Page

I currently have the following NGINX configuration set up: events { worker_connections 1024; } http { server { listen 80; server_name localhost; location / { root C:/test; index index.html; ...

Applying classes dynamically to a custom form component in Angular to tailor its appearance

I am currently working on a custom form component where I need to dynamically apply classes to it. import { Component, forwardRef, Injector } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms&apos ...

Obtain database information to use as the default selection in a dropdown menu on a website using HTML, CSS, MySQL,

Hey everyone, I'm trying to retrieve data from the database to set as the default or selected option in my dropdown menu. I'm working on a page called Update Project where I have a dropdown component populated with records from my database. Howe ...

Preventing Angular markup from displaying when JavaScript code encounters an error and throws an exception

When AngularJS experiences a breakdown in the JavaScript code, it can expose hidden error messages, images, and data within the markup. I have come across ng-cloak which temporarily hides the markup until Angular fully loads. Are there strategies to prev ...

Utilizing a dynamically created Stripe checkout button

Currently, I am attempting to integrate a checkout button from the Stripe Dashboard into my VueJS Project. I have a feeling that I might not be approaching this in the correct manner, so if you have any advice, I would greatly appreciate it. In order to ...

CSS following a clicked event

http://codepen.io/anon/pen/GqmEAq My goal is to make the 'x' button clicked (the 'x' is created after clicking the plus sign on the menu) and then bring the dropdown back up. I have attempted: a:after:checked ~ .submenu{ max-height ...

What is the best way to assign a value to a class variable within a method by referencing the 'this' keyword?

Is there a way to set the state of this Ionic react app when displaying the outcome of a reset service? I am facing challenges with using this.setState({resetSuccess}) within a method due to scope issues. (Details provided in comments) Here is the relevan ...

Exploring the Power of Jasmine Testing with Ternary Conditions

Imagine a scenario where we are working with the following snippet of JavaScript code. object = _.isUndefined(object) ? '' : aDifferentObject.property; Is it possible to write a Jasmine test that covers both scenarios? Do we need to use two se ...

Steps to restrict input in a text area to only backspace and cursor movements

I'm in search of a jQuery function that restricts movements to only arrow keys and backspace within a textarea. However, there seems to be an issue with the arrow key movements not functioning correctly. function moveArrow(e){ if(e.which >= 3 ...

Troubleshooting Cross-Origin Resource Sharing Problem in AngularJS

I've come across several discussions regarding this issue, but none have been able to resolve my problem so far. In my small web app project, I am attempting to access the freckle API from letsfreckle.com. However, I am encountering difficulties. It ...

Steps for populating a dropdown list with a JSON response

I'm facing a challenge in inserting server data into a dropdown list because each object name begins with a random ID. Here's what I'm attempting to achieve here: $("#CampaignOpenActionSubscriber_campaign_id").change(function() { var el ...

Learn how to connect a formArray from the parent component to the child component in Angular with reactive forms, allowing you to easily modify the values within the formArray

In my parent component, there is a reactive form with controls and a form group. When the user selects a playerType from a dropdown menu, I dynamically add a formArray to the formGroup. This form array will contain either 2 or 3 form groups based on the p ...

The image source is not functioning properly to display the image, even though it is working perfectly on

I'm currently attempting to retrieve an image from Instagram. Everything seems to be in order with the image link on Instagram. However, when I try to use it within an img tag, it fails to fetch. One thing worth noting is that this particular image d ...

Is there a way to retrieve the immediate children of all elements using CSS selectors in Selenium?

Although I attempted to utilize the ">" syntax, selenium does not seem to accept it. I am aware that Xpath can be used to obtain what I need, however our project exclusively utilizes CSS selectors. My goal is to create a list containing only the immedi ...