Using :not() selector to style <a> elements not inside <p> elements - css

I am trying to style any <a> in my website that doesn't appear inside a <p> to look more like a button.
I have been looking at the :not() selector and think this should work to select them:
:not(p) a {}
However doesn't seem to work in the way I expect. I have a codepen here as an example
https://codepen.io/pummra/pen/eYJoNqd

You can address the direct children in the selector using > between the two tags:
:not(p) > a
Otherwise that selector (without the >) would almost always apply, since an a tag inside a p tag is for example also a (not direct) child of the body tag.
Applied to your Codepen example:
a {
color: #00f;
}
:not(p) > a {
background-color: #1779ba;
color: #fefefe;
display: inline-block;
vertical-align: middle;
margin: 0 0 1rem 0;
padding: 0.85em 1em;
}
<div>
<h1>An interesting article</h1>
<p>This is an interesting article about something. There will be a bunch of copy here. Some of it might even have a link in. The links in the copy would look like normal links. The links outside of the paragraph should look like buttons.</p>
Read more about this
</div>

Related

How to style every first letter in a paragraph that is not a link

I am currently styling every first letter of a paragraph that follows a heading 3 with the following code, to make them stand out:
.someClass h3+p:first-letter{
font-size: 2em;
}
Now, I want to exclude all links ("a"-tags) from the selection. I looked into the .not() selector, but
.someClass h3+p:not(a):first-letter{
font-size: 2em;
}
is not working.
Any suggestions would be appreciated! Thank you.
Let's debug by breaking down your current selector in English:
.someClass h3+p:not(a):first-letter
.someClass h3 - select the h3 element inside .someClass element
.someClass h3+p - select the p element immediately after h3 inside .someClass
.someClass h3+p:not(a) - select the p element that's not an anchor element--
We can stop right there - a <p> element will never also be an <a> because an element can't have multiple tags. Since you're actually trying to affect an <a> INSIDE the <p>, a :not() selector isn't applicable here.
Fortunately, there is another solution - a little bit of an out-of-the-box one: if you change your problematic <a> elements that start the <p> to become block-level elements (using display: inline-block to preserve the intended layout), they won't be affected by their parent's first-letter rule anymore.
Here's how that works:
.someClass h3+p:first-letter {
font-size: 2em;
}
.someClass h3+p a:first-child {
display: inline-block;
}
<div class="someClass">
<h3>H3</h3>
<p>Paragraph Text</p>
<h3>H3</h3>
<p>Paragraph Text</p>
</div>

How to edit :After-Content() when hovering? [duplicate]

How can I write :hover and :visited condition for a:before?
I'm trying a:before:hover, but it's not working.
This depends on what you're actually trying to do.
If you simply wish to apply styles to a :before pseudo-element when the a element matches a pseudo-class, you need to write a:hover:before or a:visited:before instead. Notice the pseudo-element comes after the pseudo-class (and in fact, at the very end of the entire selector). Notice also that they are two different things; calling them both "pseudo-selectors" is going to confuse you once you run into syntax problems such as this one.
If you're writing CSS3, you can denote a pseudo-element with double colons to make this distinction clearer. Hence, a:hover::before and a:visited::before. But if you're developing for legacy browsers such as IE8 and older, then you can get away with using single colons just fine.
This specific order of pseudo-classes and pseudo-elements is stated in the spec:
One pseudo-element may be appended to the last sequence of simple selectors in a selector.
A sequence of simple selectors is a chain of simple selectors that are not separated by a combinator. It always begins with a type selector or a universal selector. No other type selector or universal selector is allowed in the sequence.
A simple selector is either a type selector, universal selector, attribute selector, class selector, ID selector, or pseudo-class.
A pseudo-class is a simple selector. A pseudo-element, however, is not, even though it resembles a simple selector.
However, for user-action pseudo-classes such as :hover1, if you need this effect to apply only when the user interacts with the pseudo-element itself but not the a element, then this is not possible other than through some obscure layout-dependent workaround. As implied by the text, standard CSS pseudo-elements cannot currently have pseudo-classes. In that case, you will need to apply :hover to an actual child element instead of a pseudo-element.
1 Of course, this does not apply to link pseudo-classes such as :visited as in the question, since pseudo-elements aren't links.
Write a:hover::before instead of a::before:hover: example.
To change a menu link's text on mouseover (different language text on hover), here is the
jsfiddle example
HTML:
<a align="center" href="#"><span>kannada</span></a>
CSS:
span {
font-size: 12px;
}
a {
color: green;
}
a:hover span {
display: none;
}
a:hover:before {
color: red;
font-size: 24px;
content: "ಕನ್ನಡ";
}
Try to use .card-listing:hover::after, hover, and after using ::. It will work.
Or you can set pointer-events:none to your a element and pointer-event:all to your a:before element, and then add hover CSS to a element:
a{
pointer-events: none;
}
a:before{
pointer-events: all
}
a:hover:before{
background: blue;
}
BoltClock's answer is correct. The only thing I want to append is that if you want to only select the pseudo element, put in a span.
For example:
<li><span data-icon='u'></span> List Element </li>
instead of:
<li> data-icon='u' List Element</li>
This way you can simply say
ul [data-icon]:hover::before {color: #f7f7f7;}
which will only highlight the pseudo element, not the entire li element.
You can also restrict your action to just one class using the right pointed bracket (">"), as I have done in this code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
span {
font-size: 12px;
}
a {
color: green;
}
.test1>a:hover span {
display: none;
}
.test1>a:hover:before {
color: red;
content: "Apple";
}
</style>
</head>
<body>
<div class="test1">
<span>Google</span>
</div>
<div class="test2">
<span>Apple</span>
</div>
</body>
</html>
Note: The hover:before switch works only on the .test1 class

CSS pseudo class for elements with no text

Is there a CSS pseudo class for elements with no text nodes like so:
div:nocontent {
display: none;
}
I know that there is the :empty pseudo-class but the thing I want should ignore tags...
For example:
<div>
<p></p>
</div>
Any solution for this?
EDIT:
For clarification it could be a syntax like this:
<div class="checker">
<div class="somemarkupcontainerthatcomesfromthesystemandcantberemoved"></div>
</div>
And hide the whole thing like this (this would be optimal):
.checker:nocontent {
display: none;
}
If you want to target your div, you need a parent selector, which does not (yet) exist, so to solve that a script is needed.
As suggested by Hitmands, check this post for a script sample.
When it comes to script, one have also the server side approach, where a replacement script could parse out empty tags before sending the result to the client.
If you want to target your p, if empty and is a child of your div, you can do like this.
div {
border: 2px solid red;
min-height: 20px;
}
p {
border: 2px solid blue;
height: 20px;
}
div:not(:empty) p:empty {
display: none;
}
<div>
<p></p>
</div>
<br />
<div>
<p>Hey</p>
</div>
Yes, empty check if the node is empty (so with no text and no child nodes).
There are no CSS-only options for your purpose.

CSS selector involving pseudo class first-child and dropcap

I need to format HTML similar to below. Basically a quote is optional, and I need to dropcap the first letter of the body paragraph.
<article>
<p class="quote"> <!-- quote is optional -->
Genius begins great works; labor alone finishes them.-- Joseph Joubert
</p>
<p> <!-- "L" is a dropcap -->
Life is like a box of chocolates.
</p>
<p>...</p>
<p>...</p>
</article>
My CSS looks like this:
article > p:first-child:first-letter
{
float: left;
font-family: Georgia, serif;
font-size: 360%;
line-height: 0.85em;
margin-right: 0.05em;
}
p.quote {
font-weight: bold;
}
It doesn't work currently when the quote is introduced. AFAIK I can't select the article's first child P which is not class "quote." I'll use jQuery if I can't figure this out, but for now I'm looking for a way to do it CSS only.
Thanks in advance!
If you're OK with using CSS3 selectors, try using these (grouped together):
/* Select the first child if it's not a .quote */
article > p:not(.quote):first-child:first-letter,
/* Or, if the first child is a .quote, select the following one instead */
article > p.quote:first-child + p:first-letter
{
float: left;
font-family: Georgia, serif;
font-size: 360%;
line-height: 0.85em;
margin-right: 0.05em;
}
See the jsFiddle demo
Otherwise I think you'll have to play with some overrides to get the desired effect.
Some explanation
The negation pseudo-class :not() is always processed independently of all other types, IDs, classes and pseudo-classes in the compound selector. This is regardless of how you arrange it with your other selectors.
To put it another way: you can't use :not() to remove, or filter out, elements that match what's in the negation, from a selection matched by the rest of the simple selector. It also means that the set of the elements matched by the :*-child() and :*-of-type() pseudo-classes is not affected by :not().
So the first selector above,
article > p:not(.quote):first-child:first-letter
works roughly like this:
Find every p element.
If not found, ignore.
If found, check whether this p is the :first-child and if it's :not(.quote).
If it's not the first child, ignore.
If it has the quote class, ignore.
If it matches both pseudo-classes, check whether this p is a child of article.
If not, ignore.
If so, grab its :first-letter.
Apply rules.
The second selector, on the other hand, is relatively straightforward:
article > p.quote:first-child + p:first-letter
All it does is take the p that comes right after the first child of article if it's a p.quote, and apply rules to its :first-letter.
Why don't you use <q> or <blockquote> for the quote? then you can use
p:first-of-type:first-letter
p.dropCap:first-child:first-letter {
float: left;
color: #903;
font-size: 75px;
line-height: 60px;
padding-top: 4px;
padding-right: 8px;
padding-left: 3px;
font-family: Georgia;
}

css or html5 for first and last elements

Users can enter descriptions which may include paragraphs or lists. Or they may just enter text without any enclosing <p> or <ul> elements. What I need to do is remove most of the padding and margin above the first element and below the last element so that the user entered content has a nice tight border around it. So I could do one of the following:
Use a css rule I was unaware of to target only the first and last elements
Use css3 or html5 (I assume there's something within these to easily do what I want) and hope everyone upgrades their browsers asap while the older browsers just get a slightly uglier version of the page
Find the first and last elements with Javascript and modify accordingly
Modify the html to add a class like <p class="first">
Ideally the 1st solution exists, does it? I'm ok with the 2nd solution though if not, does it exist? The last 2 I don't care for...
UPDATE: don't care about IE6. But I do need to deal with the situation that if there's just text to begin with, without any <p> or <ul> or other elements, then actually nothing special needs to be done for the top margin/padding.
Use :first-child and :last-child like this. Note that > and :first-child (CSS2) doesn't work in IE6 and below, and :last-child (CSS3) doesn't work in IE8 and below. The only real workaround to both is to use a .first and .last class respectively (you can add them dynamically with JavaScript as Phrogz says).
.description > p, .description > ul {
margin: 1.5em 0;
}
.description > :first-child {
margin-top: 0;
}
.description > :last-child {
margin-bottom: 0;
}
I added the > combinator to prevent elements like strong or li getting selected. What does it mean?
Something like this?
.container * + p, .container * + ul
{
margin: 1em 0 0;
}
.container p, .container ul
{
margin: 0;
}
BoltClock's answer works great in most cases, but IE8 and earlier ignores the :...-child pseudo-selectors.
You can use jQuery to accomplish the same thing, while targetting more browsers.
//On ready...
$(function(){
//Update styles dynamically
$('ul:last').css({'margin-bottom':0,'padding-bottom':0});
$('ul:first').css({'margin-top':0,'padding-top':0});
});
Have you considered wrapping the content in a container with a negative margin? It requires the content to at least be wrapped in a single p element (not hard to test/add melodramatically).
CSS:
.container {border:1px solid black;}
.container .subcontainer {margin:-1em 0;}
.container p {margin:1em 0;}
HTML:
<div class="container"><div class="subcontainer">
<p>My first paragraph.</p>
<p>My second paragraph.</p>
</div></div>

Resources