How to overrlay a gradient over an image [duplicate] - html

Given the following scenario, why does the :after selector require a content property to function?
.test {
width: 20px;
height: 20px;
background: blue;
position:relative;
}
.test:after {
width: 20px;
height: 20px;
background: red;
display: block;
position: absolute;
top: 0px;
left: 20px;
}
<div class="test"></div>
Notice how you do not see the pseudo element until you specify the content property:
.test {
width: 20px;
height: 20px;
background: blue;
position:relative;
}
.test:after {
width: 20px;
height: 20px;
background: red;
display: block;
position: absolute;
top: 0px;
left: 20px;
content:"hi";
}
<div class="test"></div>
Why is this the intended functionality? You would think that the display block would force the element to show up. Oddly enough, you can actually see the styles inside web debuggers; however, they do not display on the page.

Here are some references to various W3C specifications and drafts:
Selectors Level 3
The :before and :after pseudo-elements can be used to insert generated content before or after an element's content.
The :before and :after pseudo-elements
Authors specify the style and location of generated content with the :before and :after pseudo-elements. As their names indicate, the :before and :after pseudo-elements specify the location of content before and after an element's document tree content. The content property, in conjunction with these pseudo-elements, specifies what is inserted.
The content attribute
Initial: none
This property is used with the :before and :after pseudo-elements to generate content in a document. Values have the following meanings:
none - The pseudo-element is not generated.
The styling applied to ::before and ::after pseudo-elements affects the display of the generated content. The content attribute is this generated content, and without it present, the default value of content: none is assumed, meaning there is nothing for the style to be applied to.
If you don't want to repeat content:''; multiple times, you can override this simply by globally styling all ::before and ::after pseudo-elements within your CSS (JSFiddle example):
::before, ::after {
content:'';
}

The reason you need a content: '' declaration for each ::before and/or ::after pseudo-element is because the initial value of content is normal, which computes to none on the ::before and ::after pseudo-elements. See the spec.
The reason the initial value of content isn't an empty string but a value that computes to none for the ::before and ::after pseudo-elements, is twofold:
Having empty inline content at the start and end of every element is rather silly. Remember that the original purpose of the ::before and ::after pseudo-elements is to insert generated content before and after the main content of an originating element. When there's no content to insert, creating an additional box just to insert nothing is pointless. So the none value is there to tell the browser not to bother with creating an additional box.
The practice of using empty ::before and ::after pseudo-elements to create additional boxes for the sole purpose of layout aesthetics is relatively new, and some purists might even go so far as to call it a hack for this reason.
Having empty inline content at the start and end of every element means that every (non-replaced) element — including html and body — would by default generate not one box, but up to three boxes (and more in the case of elements that already generate more than just the principal box, like elements with list styles). How many of the two extra boxes per element will you actually use? That's potentially tripling the cost of layout for very little gain.
Realistically, even in this decade, less than 10% of the elements on a page will ever need ::before and ::after pseudo-elements for layout.
And so these pseudo-elements are made opt-in — because making them opt-out is not only a waste of system resources, but just plain illogical given their original purpose. The performance reason is also why I do not recommend generating pseudo-elements for every element using ::before, ::after.
But then you might ask: why not have the display property default to none on ::before, ::after? Simple: because the initial value of display is not none; it is inline. Having inline compute to none on ::before, ::after is not an option because then you could never display them inline. Having the initial value of display be none on ::before, ::after is not an option because a property can only have one initial value. (This is why the initial value of content is always normal and it is simply defined to compute to none on ::before, ::after.)

Based on your comments on others' answers, I believe your question is actually:
Why must the content property for pseudo-classes be set in the CSS, as
opposed to the content of non-pseudo-classes, which may be set in
either HTML or CSS?
The reason is that:
by definition, pseudo-classes are dynamically created for every single element specified by a page's HTML markup
all page elements, including pseudo-classes, must have a content property to be displayed.
HTML elements like <p> do as well, but you can set their content property quickly using markup (or with CSS declarations).
Howver, unlike non-pseudo-class-elements, pseudo-classes cannot be given values in the markup itself.
∴ Therefore, all pseudo-classes are invisible (their 'content' properties have no value) unless you tell them not to be (by giving them value with CSS declarations).
Take this simple page:
<body>
<p> </p>
</body>
We know this page will display nothing, because the <p> element has no text. A more accurate way to rephrase this, is that the <p> element's content property has no value.
We can easily change this, by setting the content property of the h1 element in the HTML markup:
<body>
<p>This sentence is the content of the p element.</p>
</body>
This will now display when loaded, because the content property of the <p> element has a value; that value is a string:
"This sentence is the content of the p element."
Alternatively, we can cause the <p> element to be displayed by setting the content property of the <p> element in the CSS:
p { content: "This sentence is the content of the p element set in the CSS."; }
These two ways of injecting the string into the <p> element are identical.
Now, consider doing the same thing with pseudo-classes:
HTML:
<body>
<p class="text-placeholder">P</p>
</body>
CSS:
p:before { content: "BEFORE... " ; }
p:after { content: " ...and AFTER"; }
The result:
BEFORE... P ...and AFTER
Finally, imagine how you would accomplish this example without using CSS. It's impossible, because there is no way to set the content of the pseudo-class in the HTML markup.
You might be creative and imagine something like this might work:
<p:before>BEFORE... </p>
<p> P </p>
<p:after> ...and AFTER</p>
But, it doesn't, because <p:before> and <p:after> are not HTML elements.
In conclusion:
Pseudo classes exist for every markup element.
They are invisible by default, because they are initialized with no content property.
You CANNOT set the content property for pseudo classes with HTML markup.
∴ Therefore, pseudo-elements' content property must be declared with CSS declarations, in order to be displayed.

Until you add content: ..., the psuedo-element doesn't actually exist.
Setting other style properties is not enough to force the browser to create the element.

2020 Edit
:before
Is syntax from CSS2 for better practices you should write the newest syntax from CSS3 which is
::before
with double semicolons
Full answer and differences can be found here:
What is the difference between :before and ::before?

Related

How to target #text element with CSS [duplicate]

What I would like to do (not in IE obviously) is:
p:not(.list):last-child + :text {
margin-bottom: 10px;
}
Which would give a text node a margin. (Is that even possible?) How would I get the text node with CSS?
Text nodes cannot have margins or any other style applied to them, so anything you need style applied to must be in an element. If you want some of the text inside of your element to be styled differently, wrap it in a span or div, for example.
You cannot target text nodes with CSS. I'm with you; I wish you could... but you can't :(
If you don't wrap the text node in a <span> like #Jacob suggests, you could instead give the surrounding element padding as opposed to margin:
HTML
<p id="theParagraph">The text node!</p>
CSS
p#theParagraph
{
border: 1px solid red;
padding-bottom: 10px;
}
Text nodes (not wrapped within specific tags) can now be targeted in very specific use cases using the ::target-text pseudoelement selector. A query parameter (url-encoded; e.g. whitespace must be encoded as %20) that matches a string of text can be styled like this:
::target-text { /* color, background color, etc */ }
Just like other highlight pseudoelements, only certain style properties are supported, as listed here.
There is a demo for this (parent link is on the MDN page for ::target-text). Change the query parameter string for 'text' to different strings to see how different text becomes styled.
One limitation of the ::target-text pseudoelement selector is that only the first matching string of text can be styled. In addition, at 67.8%, browser support is modest as of January 2022.

Why does a pseudo-element style rule on a standard element apply to a related pseudo-element also?

I am wondering why this code will also change the first letter of h1::before. I search about the CSS specificity--all three of them are same since they all have one element and one pseudo element (if I am wrong, please tell me). Then I think they should follow by order. This is the process in my mind: It will change the first letter of h1, then add a pseudo -element before h1 with content, and then add the pseudo-element after. The first letter won't change. Can anyone help me understand it?
h1::first-letter {
font-size: 70px;
font-style: italic;
color: yellow;
}
h1::before {
content: "This is";
color: green;
}
h1::after {
content: "of Website";
}
<h1>Heading</h1>
The generated code looks more like this:
<h1>
::before
Heading
::after
</h1>
The ::first-letter doesn't "create" a new element, it's different from ::before and ::after.
So as the documentation says:
A combination of the ::before pseudo-element and the content property may inject some text at the beginning of the element. In that case, ::first-letter will match the first letter of this generated content.
And that's why the text inside the ::before gets styled with the ::first-letter properties.
Your misunderstanding may be that CSS is applied in sequential order. This is not the case. CSS is compiled into a structure that applies in its entirety, not as a series of steps or instructions. It's not code.
Any rule that applies to any element in the CSS, and which is not overridden by another later or more specific rule will apply.

In HTML, do ::after and ::before pseudoelements only work with renderable "non-replaced" elements?

I recently noticed on a project that the ::after pseudoelement did not add any content after an input element:
input::after { content: "xxxxxxxxxx"; } // no "virtual last child" inserted into DOM "after" the input
(reference to "virtual last child" is from MDN page here)
Of course, an element such as a div will have content "after" it, and the last child "::after" will be inserted into the DOM:
div:empty::after { content: "xxxxxxxxxx"; }
//In DOM
<div>::after</div>
but nothing is inserted in these cases:
head, script { content: "qaqaqa"; }
My initial assumption was that any HTML elements that are rendered by the browser with tags that should be closed (.e.g, p, body, html, div,....etc) will have ::after inserted as a last child (and ::before inserted as a first-child), whereas elements that do NOT fit this (e.g., script, head, img, br, input,...etc) will not exhibit this behavior. My CodePen attempts suggested this was correct.
I read through documentation which finally led me to this resource, in which a note reads:
Note. This specification does not fully define the interaction
of :before and :after with replaced elements (such as IMG in HTML).
This will be defined in more detail in a future specification.
(for definition of "replaced elements" see here)
So now my refined assumption regarding ::after and ::before is that these pseudoelements are only applicable to "renderable" elements which cannot be classified as "replaced elements" (Note this refined assumption now excludes textarea from use with ::after / ::before, whereas my initial assumption would have included it - textarea::after was tested with CodePen and no ::after last child is inserted into the DOM).
Would the refined assumption be correct?
HTML does not define which elements are replaced elements or which elements can contain ::before and ::after pseudo-elements. Neither do CSS2.1 or selectors-3. The latest rewrite of css-content-3, however, states pretty unambiguously:
Replaced elements do not have '::before' and '::after' pseudo-elements
although implementations of course are not consistent with the draft (famously or otherwise, WebKit/Blink) since this wasn't hitherto defined.
Whether an element can have ::before and ::after pseudo-elements is not defined by its content model (i.e. if it's void or otherwise), or whether it has an end tag, in HTML. And again, a lot of this is implementation-dependent. For example, some implementations allow br of all things to have ::before and ::after pseudo-elements, because nobody knows exactly how br is supposed to be implemented in terms of CSS and each browser does it its own way (because neither HTML nor CSS actually defines this).
A head element and any of its descendants can have ::before and ::after pseudo-elements — all you have to do is change their display to something other than none. Obviously, they're not supposed to be displayed, but that doesn't stop anyone trying to be clever.
As far as CSS is concerned, input and textarea are both considered replaced elements, even though textarea has a start tag, end tag, and content. Whether these elements should or must be replaced elements is not stated (not even in section 14.5 of WHATWG HTML), but most browsers render them as replaced elements by default, and this behavior usually can't be changed. And for the purposes of the ::before and ::after pseudo-elements not being supported, that's all that matters.
Don't guess, read: https://drafts.csswg.org/selectors-3/#gen-content
The ::before and ::after pseudo-elements can be used to describe generated content before or after an element's content.
input has no content, so no after element is shown. Same applies for every HTML element with no content (br, menuitem, link...). All auto-closing tags have no content, and some others (like script) have none too.

Is there a CSS3 selector for text nodes? [duplicate]

What I would like to do (not in IE obviously) is:
p:not(.list):last-child + :text {
margin-bottom: 10px;
}
Which would give a text node a margin. (Is that even possible?) How would I get the text node with CSS?
Text nodes cannot have margins or any other style applied to them, so anything you need style applied to must be in an element. If you want some of the text inside of your element to be styled differently, wrap it in a span or div, for example.
You cannot target text nodes with CSS. I'm with you; I wish you could... but you can't :(
If you don't wrap the text node in a <span> like #Jacob suggests, you could instead give the surrounding element padding as opposed to margin:
HTML
<p id="theParagraph">The text node!</p>
CSS
p#theParagraph
{
border: 1px solid red;
padding-bottom: 10px;
}
Text nodes (not wrapped within specific tags) can now be targeted in very specific use cases using the ::target-text pseudoelement selector. A query parameter (url-encoded; e.g. whitespace must be encoded as %20) that matches a string of text can be styled like this:
::target-text { /* color, background color, etc */ }
Just like other highlight pseudoelements, only certain style properties are supported, as listed here.
There is a demo for this (parent link is on the MDN page for ::target-text). Change the query parameter string for 'text' to different strings to see how different text becomes styled.
One limitation of the ::target-text pseudoelement selector is that only the first matching string of text can be styled. In addition, at 67.8%, browser support is modest as of January 2022.

What does ::before on its own line do in HTML?

This might be a stupid question, but if I have something like this:
<div id="topDiv">
<header id="headerId" class="headerClass" style="display: block;">
::before
<div>...</div>
<div>...</div>
<div>...</div>
</header>
</div>
What does this 'before' in the html do?
I understand if I do the following in the CSS it adds these properties before every p element: (this is from a W3Schools example)
p::before {
content: "Read this -";
background-color: yellow;
color: red;
font-weight: bold;
}
You're seeing that ::before because that is how your browser's developer tools represents a CSS ::before pseudo-element in the document tree view.
If you literally had a string of text "::before" in your HTML file, it would do nothing special as it has no meaning in HTML; it would just show up as the text "::before" on your page.
I guess there can be two reason (Its via personal experience & observation not a valid reference for support)
1) It provides a visual representation of these pseudo selectors according to their meaning
::before before the div to which its applied to and ::after after the div to which its applied to
2) Pseudo selectors are used to insert some content:'' in HTML. So this provides a distinct representation. User can easily inspect which content is inserted in HTML via these pseudos.
And Yes its on each browser how the implement and show the pseudos.
good luck!
It is stating that you have a pseudo element in your html. Since it is not a 'real' element, you can't physically manipulate it like you can do with 'real' elements like div's and buttons.
You may also see ::after appearing in places, representing somewhere for either your id="headerId" or class="headerClass" has a ::before element declared.
the whole reason they are not declared like <after></after> or anything like this is because it is 'a ghost' or 'shadow' of a 'real element', and hence can only be styled/manipulated in the css before the html is rendered.