-2

Is it possible to apply CSS to an element that doesn't have a specific element as a child?

I can't use :has() because I need to support older browsers. A JavaScript solution is allowed.

See code example.

<!-- Element I want to hide -->
<div>
  <a class="link"> click me </a>
</div>

<!-- Element with <p> inside. I don't want to hide it. -->
<div>
  <a class="link">
    <p class="p-Inside-Link">click me</p>
  </a>
</div>

My attempt that did not work:

div a:not( > .p-Inside-Link){
    display: none;
}
jpalecek
  • 47,058
  • 7
  • 102
  • 144
  • _"Given that my app doesnt support "has"_ I assume you mean the browsers you need to support do not support the [`:has()` pseudo-class](https://developer.mozilla.org/en-US/docs/Web/CSS/:has)? – Darryl Noakes Aug 08 '23 at 15:07
  • 1
    You forgot to post your attempt to solve this problem. – Scott Hunter Aug 08 '23 at 15:07
  • 2
    If you can't change the HTML structure, and `:has()` is off limits, I don't believe this is possible with just CSS. – DBS Aug 08 '23 at 15:07
  • This is going to fall under "Is there a parent selector?" question. You can't do this with CSS alone. – disinfor Aug 08 '23 at 15:20
  • added attempt, and yes, doesnt support has() – user21357723 Aug 08 '23 at 15:22
  • @disinfor , No , I knew this answer while doing my research, . But thanks for sharing. This is why I asked by writing down my situation – user21357723 Aug 08 '23 at 15:26
  • Sorry man, but what they suggested to you, but you don't want to listen apparently to, is to use `:has()`. Your question basically comes down to *Does CSS has a parent selector?*. There is no way to do this without `:has()` or using `JavaScript` – Red Aug 08 '23 at 15:27
  • becasue there are some browser version in my situation are restricted to not having has() ; I also want to use has() xd. And I do think some poeple outside may have similar situation because of developing application within in old enviroment. The answer suggested doesnt mentioned it is restricted to browser without has() and check with workaround @Red – user21357723 Aug 08 '23 at 15:36
  • So use `:has()` in combination with `JavaScript`, there is no possible way for CSS to do what you asked for, except for `:has()` https://caniuse.com/?search=%3Ahas `:has()` will be supported by FF this year aswell. – Red Aug 08 '23 at 15:39
  • but even using :has() , i still cant make it, I tried in my local updated browser @Red – user21357723 Aug 08 '23 at 15:40
  • 1
    @user21357723 as I said, use it in combination with `JavaScript`. Use `JavaScript` to hide the element when there is not native browser support for `:has()`. – Red Aug 08 '23 at 15:43
  • @ Given that it is in react, may i know how to use Javasript to do so? i am not so familir wiht using javascript to alter style @Red – user21357723 Aug 08 '23 at 15:46
  • @user21357723 See provided asnwer. – Red Aug 08 '23 at 15:54
  • 1
    @user21357723 You may want to update the title, content and tags of your question if the answer can use JavaScript to solve your issue - especially since you did your research and saw there is no way to do this in CSS (as you can't rely on `has()` or you know there is not a parent selector, yet asked anyway since your situation is *exactly* a parent selector CSS problem). – disinfor Aug 08 '23 at 16:54
  • you are right , @disinfor , cant be too rigid when the situation indeed make sense to ask a question. – user21357723 Aug 08 '23 at 17:11

2 Answers2

2

Without using :has() this is not possible. So in this case you rely on using JavaScript or alter the HTML.

But since editing the HTML structure is not possible in your case lets use JavaScript

// Select all divs that contain an element with class .link
const links = document.querySelectorAll('div .link');

// Filter the links to only divs that don't contain 
// an class with .p-Inside-Link 
const linksToHide = [...links].filter(link => !link.querySelector('.p-Inside-Link'));

[...linksToHide].forEach(link => {
  // Use parentNode on the link element to hide the surounding div element
  link.parentNode.style.display = 'none';
});
<!-- Element I want to hide -->
<div>
  <a class="link"> HIDE ME </a>
</div>

<!-- Element with <p> inside. I don't want to hide it. -->
<div>
  <a class="link">
    <p class="p-Inside-Link">SHOW ME</p>
  </a>
</div>
Red
  • 6,599
  • 9
  • 43
  • 85
  • May i know if I put this in useEffect , does it make sense ? Or i should put it on top of the code – user21357723 Aug 08 '23 at 15:58
  • Probaly just at the top of your component's javascript code section. – Red Aug 08 '23 at 15:59
  • Thanks for your answer, after running the code, the element is still there. But I dont see your code has problem.... – user21357723 Aug 08 '23 at 16:24
  • in fact, my code is like this const links = document.querySelectorAll( '.modal .table .text.celllink.primary' ) – user21357723 Aug 08 '23 at 16:26
  • the last .text.celllink.primary is the className of the real element . it is like className=' text celllink primary' , would this be a problem to select it? – user21357723 Aug 08 '23 at 16:27
  • 1
    @user21357723 no, `querySelector` supports all valid *css selectors*. So select elements just like you would do in *CSS* – Red Aug 08 '23 at 16:37
  • But when I apply the code inside chrome, it can find it when using querySelectorAll. But in the debug stepper, querySelectorAll didnt get anything. I think the code is right, it is just that in run time, querySelectorAll didnt get anything. But after running everything and I try it in chrome console, it did get the element – user21357723 Aug 08 '23 at 16:37
  • 1
    You probaly need to run the code on a `onMount` hook, which runs the code when the *DOM* has been loaded, in react that is the `useEffect` hook. – Red Aug 08 '23 at 17:23
  • Btw, can we reopen this question, indeed your answer isnt get seen before, the suggested answer in other post didnt answer my question, where I am asking for a javascript question. Why is it so stubborn to say it is duplicated, indeed we come up with our answer which is not raised before – user21357723 Aug 09 '23 at 06:21
  • No, you have to edit the question for that. Your question boils down to the duplicates question. – Red Aug 09 '23 at 11:57
  • May I ask why is that querySelectorAll is not returning a live stream node list, but it can still manipulate and apply effect on screen @Red – user21357723 Aug 14 '23 at 03:07
2

Here is a simple solution. Loop through the .link anchors inside of a div. Then for each link check to see if p-inside-link doesn't exist. If it doesn't exist add a CSS class called hide. Then in CSS add a class for hide that has a display of none.

Adding classes instead of modifying the style via javascript will give you more future capabilities.

const links = document.querySelectorAll("div .link");

links.forEach((link) => {
  if(!link.querySelector(".p-Inside-Link")){
    link.parentNode.classList.add("hide");
  }
});
  
  .hide{display:none;}
<!-- Element I want to hide -->
<div>
  <a class="link"> click me </a>
</div>

<!-- Element with <p> inside. I don't want to hide it. -->
<div>
  <a class="link">
    <p class="p-Inside-Link">click me (keep)</p>
  </a>
</div>
imvain2
  • 15,480
  • 1
  • 16
  • 21