using css empty to hide an element - css

Using chrome to inspect I see some code like this:
<div class="entry-content">
<p>We .... </p>
</div>
<footer class="entry-footer">
</footer>
Sometimes this footer is empty, and at other's it isn't.
When it is empty I try to hide it with:
footer.entry-footer:empty {
display:none;
}
but it doesn't work.
So I am either doing something wrong (or I guess it isn't really empty!)

:empty requires the element to be empty of whitespace too.
Here is an example, .test.blue and .test.red have white space and don't display: none; (without the JS below, where .test.red becomes hidden)...
if you want to remove the white space post load, here is some JS to do that:
var empties = document.querySelectorAll( '[selector_here]' );
for ( key in empties ) {
if ( typeof empties[key].innerHTML != "undefined" ) empties[key].innerHTML = empties[key].innerHTML.trim()
}
The JS above trims the the whitespace from any element matching the given selector, in my example I used the class empty (you can see it working on .test.red)
But i would recommend removing it from the HTML

I did a quick test for you and it would seem that white space counts as content. Beginning your closing tag on a new line will quietly insert a newline character, so which is why your selector for :empty fails.
As a solution, your html should be look like the following:
<footer class="entry-footer"></footer>
Because there's literally nothing between the start and end tags, the element passes as being :empty

Related

Why is my nested web component adding vertical space in Lit?

I have a weird issue where when I add padding-left: 32px to an element, vertical space gets added. If the CSS says 0, and I add the space manually in Chrome debugger, the vertical space isn't there. This is only happening with nested components. I'm not sure if I'm misusing something or if I have found a bug.
I have code like this:
<cai-setting-row class="itemGroupMiddle doubleIndent" data-type="A"
>Not Nested A</cai-setting-row
><cai-setting-row class="itemGroupMiddle doubleIndent" data-type="A"
>Not Nested B</cai-setting-row
>
<cai-setting-row-account></cai-setting-row-account>
The render of cai-setting-row-account is just the same markup:
render() {
return html`<cai-setting-row
class="itemGroupMiddle doubleIndent"
data-type="A"
>Nested A</cai-setting-row
><cai-setting-row class="itemGroupMiddle doubleIndent" data-type="A"
>Nested B</cai-setting-row
>`;
}
It renders like this:
The "Not Nested" elements look right. The "Nested" ones have extra space and you can see a weird border on top that is the distance of the padding.
I have a functioning sandbox here:
https://studio.webcomponents.dev/edit/8u0cg76BNEiSoHXQT8by/
I misunderstood how class is used on a custom component. Doing <my-component class="foo"> adds foo to the :host. My code in sandbox needed to change the magic of const parentClass = this.getAttribute('class') ?? ''; to const parentClass = this.getAttribute('itemClass') ?? '';, such that I wouldn't accidentally be applying classes to the :host and the intended element.

:empty doesn't work if there's blank spaces?

Trying to find a pseudo class that'll target a <div> like this:
<div class="nav-previous">
</div>
I've tried :blank and :empty but neither can detect it. Is it just not possible to do?
https://jsfiddle.net/q3o1y74k/3/
:empty alone is enough.
By the current Selectors Level 4 specification, :empty can match elements that only contain text nodes that only contain whitespace as well as completely empty ones. It’s just there aren’t many that support it as per the current specification.
The :empty pseudo-class represents an element that has no children except, optionally, document white space characters.
From the MDN:
Note: In Selectors Level 4, the :empty pseudo-class was changed to act like :-moz-only-whitespace, but no browser currently supports this yet.
The :-moz-only-whitespace CSS pseudo-class matches elements that only contain text nodes that only contain whitespace. (This includes elements with empty text nodes and elements with no child nodes.)
As the others mentioned, this isn't possible with CSS yet.
You can check to see if there's only whitespace with JavaScript however. Here's a simple JS only solution, "empty" divs that match are blue, while divs that have text are red. Updated to add an empty class to the empty divs, which would allow you to target them easily with the selector .empty in your CSS.
The JS only "empty" comparison would look like this:
if(element.innerHTML.replace(/^\s*/, "").replace(/\s*$/, "") == "")
And if you're using jQuery it would be a bit easier:
if( $.trim( $(element).text() ) == "" ){
var navs = document.querySelectorAll(".nav-previous");
for( i=0; i < navs.length; i++ ){
if(navs[i].innerHTML.replace(/^\s*/, "").replace(/\s*$/, "") == "") {
navs[i].style.background = 'blue';
navs[i].classList.add( 'empty' );
} else {
navs[i].style.background = 'red';
}
}
.nav-previous {
padding: 10px;
border: 1px solid #000;
}
.nav-previous.empty {
border: 5px solid green;
}
<div class="nav-previous">
</div>
<div class="nav-previous">Not Empty </div>
The problem with your approach is that your container is not actually empty.
The :empty pseudo-class represents an element that has no children at
all. In terms of the document tree, only element nodes and content
nodes (such as DOM text nodes, CDATA nodes, and entity references)
whose data has a non-zero length must be considered as affecting
emptiness;
As you have empty spaces this pseudo class will not do the trick.
The :blank pseudo class should be the right one, because this is its definition:
This blank pseudo-class matches elements that only contain content
which consists of whitespace but are not empty.
the problem is that this pseudo class isn't implemented by any browser yet as you can check in the link below. So you will need to wait until it get implemented to be able to use this selector.
This pretty much explains the behavior you are facing
https://css4-selectors.com/selector/css4/blank-pseudo-class/
The best approach here is just to be sure that your div will actually be empty, so your approach will work.
the best that you can do is to define an empty class like this:
.empty{
display:none;
}
and then add this JS code here, it will append the empty class to your blank items:
(function($){
$.isBlank = function(html, obj){
return $.trim(html) === "" || obj.length == 0;
};
$('div').each(function() {
if($.isBlank(
$(this).html(),
$(this).contents().filter(function() {
return (this.nodeType !== Node.COMMENT_NODE);
})
)) {
$(this).addClass('empty');
}
});
})(jQuery);
check it working here,
https://jsfiddle.net/29eup5uw/
You just can't without JavaScript/jQuery implementation.
:empty selector works with empty tags (so without even any space in them) or with self-closing tags like <input />.
Reference: https://www.w3schools.com/cssref/css_selectors.asp
If you want to use JavaScript implementation, I guess here you will find the answer: How do I check if an HTML element is empty using jQuery?
:empty indeed only works for totally empty elements. Whitespace content means it is not empty, a single space or linebreak is already enough. Only HTML comments are considered to be 'no content'.
For more info see here: https://css-tricks.com/almanac/selectors/e/empty/
The :blank selector is in the works, it will match whitespace, see here: https://css-tricks.com/almanac/selectors/b/blank/. But it seems to have no browser support yet.
Update:
See here for possible solutions to this involving jQuery.

Targetting element:only-child with no sibling text node [duplicate]

I would like to select anchor tags only when they're completely by themselves, that way I can make those look like buttons, without causing anchors within sentences to look like buttons. I don't want to add an extra class because this is going within a CMS.
I originally was trying this:
article p a:first-child:last-child {
background-color: #b83634;
color: white;
text-transform: uppercase;
font-weight: bold;
padding: 4px 24px;
}
But it doesn't work because text content isn't considered as criteria for :first-child or :last-child.
I would like to match
<p><a href='#'>Link</a></p>
but not
<p><a href='#'>Link</a> text content</p>
or
<p>text content <a href='#'>Link</a></p>
Is this possible with CSS?
The simple answer is: no, you can't.
As explained here, here and here, there is no CSS selector that applies to the text nodes.
If you could use jQuery, take a look at the contains selector.
Unfortunately no, you can't.
You have to use JS by it self or any librady of it to interact with content of elements and found where is each element in the content.
If you wish me to update my answer with a JS example prease ask for it.
I don't think it's generally possible, but you can come close. Here are some helpful places to start:
The Only Child Selector which would allow you to select all a elements which have no siblings like so a:only-child {/* css */}. See more here. (Also see edit)
The Not Selector which would allow you to exclude some elements perhaps using something along the lines of :not(p) > a {/* css */} which should select all anchors not in a paragraph. See some helpful information here.
Combining selectors to be as specific as possible. You might want all anchors not in an h1 and all anchors not in a p.
Example:
The final product might look like this:
a:only-child, :not(p) > a {/* css */}
This should select all anchors that are only children and anchors that are not in a paragraph.
Final note:
You may want to consider making the buttons actual button or input tags to make your life easier. Getting the HTML right first usually makes the CSS simpler.
Edit: the only child ignores the text, so that's pretty much useless here. I guess it's less doable than I thought.
jQuery Code Example:
// this will select '<p><a></a></p>' or '<p><a></a>text</p>'
// but not '<p><a></a><a></a></p>'
$('p').has('a:only-child').each(function() {
const p = $(this); // jQuerify
let hasalsotext = false;
p.contents().each(function(){
if ((this.nodeType === 3) && (this.nodeValue.trim() !== "")) {
hasalsotext = true;
return false; // break
}
});
if (!hasalsotext) {
$('a', p).addClass('looks-like-a-button');
}
});

How to write regular expressions in CSS

How do I use regular expressions in CSS? I found a tutorial here for matching static strings in CSS, but I haven't been able to find one for using regular expressions to match multiple strings in CSS. (I found one here, but I couldn't get it to work. I also looked at the W3C documentation on using regular expressions, but I couldn't make sense of the document.)
I'm want to match a series of <DIV> tags whose ids start at s1 and increase by one (ie. #s1 #s2 #s3...).
I know that div[id^=s], div[id^='s'], and div[id^='s'] each perform the match as I intend it in my CSS. However, each of those also match an id of #section, which I don't want to happen. I believe that "/^s([0-9])+$/" is the equivalent PHP string--I'm just looking for it in CSS version.
There is no way to match elements with a regular expression, even in CSS3. Your best option is probably to simply use a class for your divs.
<style>
.s-div {
// stuff specific to each div
}
</style>
<div id="s1" class="s-div"><!-- stuff --></div>
<div id="s2" class="s-div"><!-- stuff --></div>
<div id="s3" class="s-div"><!-- stuff --></div>
<div id="s4" class="s-div"><!-- stuff --></div>
<div id="s5" class="s-div"><!-- stuff --></div>
Also remember that you can separate multiple class names by a space inside a class attribute.
<div class="class1 class2 class3"></div>
javascript:
/* page scrape the DIV s# id's and generate the style selector */
re=/<div\b[^>]*\b(id=(('|")?)s[0-9]+\2)(\b[^>]*)?>/ig;
alert(
document . body . innerHTML .
match(re) . join("") .
replace(re,"div[$1], ") + "{ styling details here }" );
alert(
("test with <div id=s2 aadf><DIV ID=123> <DIV adf=asf as><Div id='s45'>" +
"<div id=s2a ><DIV ID=s23 > <DIV asdf=as id=S9 ><Div id='x45' >") .
match(re) . join("") .
replace(re,"div[$1], ") + "{ styling details here }"
);
The test yields
div[id=s2], div[id='s45'], div[ID=s23], div[id=S9], { styling details here }
Note the dangling , and the case preserved S9.
If you don't want or can't use the solution posted by #zneak, you could do that editing the labels with javascript, but i'll advice you: It's a hell of work.
The following CSS will select #s0, #s1, ... , #s9 and not #section, though a browser must implement the CCS3 negation :not().
The final selection is equivalent to:
/^s[0-9]?.*[0-9]$/
which says that each id must start with s and a number and end with a number like:
s6, s42, s123, s5xgh7, ...
The :not() line vacuously excludes those ID's that do not start properly using an empty style {}.
<style>
div:not([id^=s0]):not([id^=s1]):not([id^=s2]):not ... :not([id^=s9]) {}
div[id^=s][id$=0], div[id^=s][id$=1], div[id^=s][id$=2], ... div[id^=s][id$=9] { ... }
</style>
CSS3 does not use regular expressions to define selectors BUT ...
CSS Conditional Rules Module Level 3
defines a very specific function, regexp(<string>), that parses a URL with a regular expression when creating an #document rule.
<style>
/* eliminate the alphabet except s - NB some funny characters may be left */
/* HTML id's are case-sensitive - upper case may need exclusion & inclusion */
div[id*=a], div[id*=b], div[id*=c], ..., div[id*=q], div[id*=r] {}
div[id*=t], div[id*=u], div[id*=v], div[id*=w], div[id*=x], div[id*=y], div[id*=z] {}
div[id*='_'], div[id*='-'], div[id*='%'], div[id*='#'] {}
/* s can not be embedded */
div[id*=0s], div[id*=1s], div[id*=2s], ..., div[id*=9s] {}
/* s will be followed by a string of numerals - maybe a funny char or two */
div[id^=s0], div[id^=s1], div[id^=s2], ... div[id^=s9] { ... }
</style>

CSS text-transform capitalize on all caps

Here is my HTML:
small caps &
ALL CAPS
Here is my CSS:
.link {text-transform: capitalize;}
The output is:
Small Caps & ALL CAPS
and I want the output to be:
Small Caps & All Caps
Any ideas?
You can almost do it with:
.link {
text-transform: lowercase;
}
.link:first-letter,
.link:first-line {
text-transform: uppercase;
}
It will give you the output:
Small Caps
All Caps
There is no way to do this with CSS, you could use PHP or Javascript for this.
PHP example:
$text = "ALL CAPS";
$text = ucwords(strtolower($text)); // All Caps
jQuery example (it's a plugin now!):
// Uppercase every first letter of a word
jQuery.fn.ucwords = function() {
return this.each(function(){
var val = $(this).text(), newVal = '';
val = val.split(' ');
for(var c=0; c < val.length; c++) {
newVal += val[c].substring(0,1).toUpperCase() + val[c].substring(1,val[c].length) + (c+1==val.length ? '' : ' ');
}
$(this).text(newVal);
});
}
$('a.link').ucwords();​
Convert with JavaScript using .toLowerCase() and capitalize would do the rest.
Interesting question!
capitalize transforms every first letter of a word to uppercase, but it does not transform the other letters to lowercase. Not even the :first-letter pseudo-class will cut it (because it applies to the first letter of each element, not each word), and I can't see a way of combining lowercase and capitalize to get the desired outcome.
So as far as I can see, this is indeed impossible to do with CSS.
#Harmen shows good-looking PHP and jQuery workarounds in his answer.
I'd like to sugest a pure CSS solution that is more useful than the first letter solution presented but is also very similar.
.link {
text-transform: lowercase;
display: inline-block;
}
.link::first-line {
text-transform: capitalize;
}
<div class="link">HELLO WORLD!</div>
<p class="link">HELLO WORLD!</p>
HELLO WORLD! ( now working! )
Although this is limited to the first line it may be useful for more use cases than the first letter solution since it applies capitalization to the whole line and not only the first word. (all words in the first line)
In the OP's specific case this could have solved it.
Notes: As mentioned in the first letter solution comments, the order of the CSS rules is important! Also note that I changed the <a> tag for a <div> tag because for some reason the pseudo-element ::first-line doesn't work with <a> tags natively but either <div> or <p> are fine.
EDIT: the <a> element will work if display: inline-block; is added to the .link class. Thanks to Dave Land for spotting that!
New Note: if the text wraps it will loose the capitalization because it is now in fact on the second line (first line is still ok).
JavaScript:
var links = document.getElementsByClassName("link");
for (var i = 0; i < links.length; i++) {
links[i].innerHTML = links[i].innerHTML.toLowerCase();
}
CSS:
.link { text-transform: capitalize; }
What Khan "ended up doing" (which is cleaner and worked for me) is down in the comments of the post marked as the answer.
captialize only effects the first letter of the word. http://www.w3.org/TR/CSS21/text.html#propdef-text-transform
You can do it with css first-letter!
eg I wanted it for the Menu:
a {display:inline-block; text-transorm:uppercase;}
a::first-letter {font-size:50px;}
It only runs with block elements - therefore the inline-block!
May be useful for java and jstl.
Initialize variable with localized message.
After that it is possible to use it in jstl toLowerCase function.
Transform with CSS.
In JSP
1.
<fmt:message key="some.key" var="item"/>
2.
<div class="content">
${fn:toLowerCase(item)}
</div>
In CSS
3.
.content {
text-transform:capitalize;
}
If the data is coming from a database, as in my case, you can lower it before sending it to a select list/drop down list. Shame you can't do it in CSS.
After researching a lot I found jquery function/expression to change text in first letter in uppercase only, I modify that code accordingly to make it workable for input field. When you will write something in input field and then move to another filed or element, the text of that field will change with 1st-letter capitalization only. No matter user type text in complete lower or upper case capitalization:
Follow this code:
Step-1: Call jquery library in html head:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
Step-2: Write code to change text of input fields:
<script>
$(document).ready(function(){
$("#edit-submitted-first-name,#edit-submitted-last-name,#edit-submitted-company-name, #edit-submitted-city").focusout(function(){
var str=$(this).val();
str = str.toLowerCase().replace(/\b[a-z]/g, function(letter) {
return letter.toUpperCase();
});
$(this).val(str);
});});
</script>
Step-3: Create HTML input fields with same id's you use in jquery code like:
<input type="text" id="edit-submitted-first-name" name="field name">
The id of this input field is: edit-submitted-first-name (It using in jquery code in step-2)
**Result:
Make sure the text will change after you move your focus from that input field at another element. Because we using focus out event of jquery here.
Result should like this: User Type: "thank you" it will change with "Thank You".
**
Best of luck
The PHP solution, in backend:
$string = 'UPPERCASE';
$lowercase = strtolower($string);
echo ucwords($lowercase);
I know this is a late response but if you want to compare the performance of various solutions I have a jsPerf that I created.
Regex solutions are the fastest for sure.
Here is the jsPerf: https://jsperf.com/capitalize-jwaz
There are 2 regex solutions.
The first one uses/\b[a-z]/g. Word boundary will capital words such as non-disclosure to Non-Disclosure.
If you only want to capitalize letters that are preceded by a space then use the second regex
/(^[a-z]|\s[a-z])/g
if you are using jQuery; this is one a way to do it:
$('.link').each(function() {
$(this).css('text-transform','capitalize').text($(this).text().toLowerCase());
});
Here is an easier to read version doing the same thing:
//Iterate all the elements in jQuery object
$('.link').each(function() {
//get text from element and make it lower-case
var string = $(this).text().toLowerCase();
//set element text to the new string that is lower-case
$(this).text(string);
//set the css to capitalize
$(this).css('text-transform','capitalize');
});
Demo
all wrong it does exist --> font-variant: small-caps;
text-transform:capitalize; just the first letter cap

Resources