One of the posts on this website that consistently gets a significant amount of traffic (5000+ page views this month alone) is a ridiculous article I wrote that discusses how to make a child element not inherit the opacity setting of its parent.
As we all know, CSS transparency that uses the opacity property can be annoying in this area. Basically, if a parent element has an opacity value set at, say, 0.5, all of its children will inherit that opacity setting, and there’s no way to reverse that opacity on the child elements.
That post I wrote discussed a hacky workaround where you actually remove the element from its parent, position it absolutely, then move it so it still appears in the same place it did when it was a child. If you know anything about CSS positioning, then you know why this is not a great solution.
Nonetheless, I still get lots of traffic coming to that article not only from Google searches, but also from various Stack Overflow threads that link to it. So I decided to write a script that fixes this issue. But first, let’s settle a few things.
The Best Way to Resolve This Issue
I’m guessing that in more than 90% of cases, this is pretty much a non-issue. If you need transparency on a parent element, you can do it using a few different methods that avoid this parent-child opacity issue:
- If it’s only a background color on the parent, use RGBA or HSLA
- If it’s a background image, use a transparent PNG
- If you’re just desiring a “washed out” look for a color or background image or pattern, do it in your image editor, or sample the washed-out color you want and insert it with Hex or RGB in your CSS
But hey, it’s fun to write polyfills and workarounds for these types of problems, and it does seem like this sort of thing is in demand, even if most developers are approaching the problem in the wrong way to begin with.
Introducing thatsNotYoChild.js
I realized that the workaround to get the child elements out of their parent and repositioned is not that crazy. So I wrote a script that does this exact thing automatically, but it’s much more effective than that original solution.
Here’s an embedded pen demonstrating thatsNotYoChild.js in action:
Check out this Pen!
Go ahead, change the markup to anything you want inside the #parent
element; it should work for anything you put in there.
You can view the code in the embedded CodePen, and here’s a step by step description of what it does:
- Grab all child elements of the element that has the opacity setting, wrap them in a
<div>
- Use cloneNode to clone the newly-wrapped child group
- Place the new clone outside the parent element
- Change the ID of the original group
- Set the opacity of the original group to zero (you can reduce the opacity of the children but you can’t raise it)
- Use getBoundingClientRect() (which works everywhere that’s relevant) to find the exact position and width of the original child group
- Use element.style to absolutely position and size the clone group using the values obtained from
getBoundingClientRect()
- Use window.onresize to run the previous two steps every time the window is resized.
Compared to the old article I wrote that fixed this issue with a CSS-only solution, this solution has a few advantages. First, although the child elements are absolutely positioned, taking them out of the normal flow, the space the child elements occupy is still occupied by the original child group, which isn’t visible due to having its opacity set to 0. The cloned group overlays the same space, making it appear as if it never moves, and the other elements on the page don’t reposition themselves since they are subject to the positioning of the original, now invisible, child group.
The other advantage is that this solution doesn’t require any changes to the markup, whereas that other CSS-only solution required that you change the markup.
To use the script, just call the function like this, passing in the ID of the parent element that has opacity set:
thatsNotYoChild('parent');
Feedback?
I don’t know too much about the different DOM methods I used in this script. I’m guessing for example that window.onresize
is not great for performance and repaints.
This was, more or less, a fun little hacky script that’s not too heavy so maybe someone will find it useful, assuming there are no major performance issues with it.
If you have any feedback on improvements to the code, or see any potential bugs or drawbacks, feel free to comment and/or fork it.
+1 for the name
This sounds awesome, I hated that side effect of opacity, if it works you are my hero!
Interesting work around to do this. Probably heavy under certain circumstancies I guess.
I’ll stick with the pseudo-element hack to use a transparent image as background (without having to use a transparent PNG).
I usually create a fader for the opacity and background setting and just position it under my realy element.
This is really a nice solution. I searched a lot and found this solution which saves my life :)
Thanks buddy.
this is what i was looking for, tnx man
I assume it’s only working with one element per page, correct? At least it looks like it in my case.
Was hoping it wouldn’t, but as it’s using IDs…
Just call the function for each element that you want to apply it to:
Probably not the most efficient method, but this is such a hacky thing anyhow….
Thank you for your update. This post was very helpful for me. Thanks a lot!
you are my hero , solution 1 is awesome….
Hi,
I’m using your function, works really good, but my jquery or angular code doesnt work when i use thatsNotYoChild()
Can you post a demo or something? I can’t really debug code based on a vague explanation. I’m glad to help, but I have to see an actual working example, or else have way more detail.