I'm attempting to put CSS styles on the list items in the first line of a list but it seems that neither Chrome, Firefox, nor Safari will accept the style.
ul:first-line > li {
display: inline;
/* my styles here */
}
Have I overlooked the way in which I'm specifying the style, is this an oversight in CSS implementation or a deliberate CSS specification? If it is the latter, is there a good rationale behind this?
JSFiddle: http://jsfiddle.net/e3zzg/
Edit:
Please note, it seems pretty definitive that this can currently not be achieved using CSS alone but from a research standpoint and for posterity, I'm curious as to why this is. If you read the W3C CSS specification on the firstline pseudo-element there doesn't seem to be any mention of inner elements. Thanks to everyone trying to provide alternate solutions, but unless there actually is a CSS solution, the question here is 'why', not 'how' or 'is it possible'.
Here's "Why" What You Want to Do Cannot Be Done
The selectors 3 spec is a little more up to date. The following is taken from that.
The "why" is because the :first-letter is pseudo-element, that is, a "fake" or "false" element. It is producing a "fictional tag sequence", which is not recognizable in relation to other real elements. So your...
ul:first-line > li
...suffers from the same issues as this selector string...
ul:before + li
...where the combinator (whether > or +) is only looking at the "element" not the "pseudo-element" for selection. The second string does not target the "first" li of the ul that is following a :before pseudo-element. If it were to work at all, it would target an li that follows the ul in the html sequence (which, of course, there would never be one in a valid html layout).
However, a selector string similar to the second one above would not work anyway, because in actuality, the form of the above strings is not valid, as confirmed by the statement in the specifications that says:
Only one pseudo-element may appear per selector, and if present it
must appear after the sequence of simple selectors that represents the
subjects of the selector.
In other words, a pseudo-element can only be positioned dead last in the selector sequence, because it must be the target of the properties being assigned by that selector. Non valid forms apparently are simply ignored just like any invalid selector would be.
I think you would be better off with:
ul > li:first-child
:first-line is only useful for text elements
The only option to make a class apart for the second line is adding through Javascript a concrete className to them and setting the background for them. To get the current line you should iterate the elements and compare it's distance to the list top and it's previous siblings. I made a jQuery example so you can get the idea: http://jsfiddle.net/JmqxM/
$("ul.numerize-lines").each(function () {
var list = $(this);
var currentDistance = 0;
var currentLine = 0;
list.find("li").each(function () {
var item = $(this);
var offset = .offset();
var topDistance = offset.top;
if (topDistance > currentDistance) {
currentDistance = topDistance;
currentLine += 1;
}
item.addClass("line-" + currentLine);
});
});
and the css:
ul li.line-2{
background-color: #FFF;
}
Pretty sure the :first-line should be applied to the element itself that contains the text (rather than the parent, as you have).
ul > li:first-line { /*style*/ }
Or if your list items contain tags or something else like that...
ul > li p:first-line { /*style*/ }
Related
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.
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');
}
});
I would like to reproduce this, just with CSS:
http://jsfiddle.net/g32Xm/
$(function(){
var text = $('h2').text();
var atext = text.split("");
var newText = '';
for(var i=0; i< atext.length; i++){
newText += '<span>'+ atext[i]+'</span>';
}
$('h2').html(newText);
});
CSS
h2 span:hover{
position:relative;
bottom:3px;
}
Is there any workaround that doesn't envolve Javascript? and (i forgot to mention) without putting the spans in the html
Thanks in advance
CSS is generally applied to selectors, not individual letters in a text node. With modern CSS, you can use the :first-letter pseudoelement, but as far as I know, this is about as far as you can go with styling individual characters. The only way is wrapping each character in a separate element (a span, probably) and working with that.
So, to cut the long answer short: as of now, no, there's no way to do that with just CSS.
You can eventually wrap every single character in a span manually and avoid using javascript that way:
HTML
<h2>
<span>M</span><span>a</span><span>n</span><span>d</span><span>a</span><span>r</span><span>i</span><span>n</span><span>a</span>
<span>L</span><span>i</span><span>m</span><span>ó</span><span>n</span>
</h2>
CSS
h2 > span:hover{
position:relative;
bottom:3px;
}
JSFiddle example
The intent is to target all the other elements of the same type & same level whenever one is hovered. Tried
a:hover ~ a
Only to notice that this doesn't target the elements before the hovered one... Is there a solution with css? Or should I just somehow js my way out of this
This is a variation on the parent or < selector question (of which there are many). I know it's not quite the same, but I'm sure you can imagine how a sibling selector would be derived from a parent selector.
Jonathan Snook has an excellent blog post on why this doesn't exist, and I don't think I can do any better, so I'll leave you to read that if it interests you. Basically, it's a technically difficult job because of the way elements are selected, and it would lead to a whole world of mess in terms of code structure.
So the short answer is, this doesn't exist and you'll need to resort to JS to fix it, I'm afraid.
Edit: Just a couple of examples of fixes. Using jQuery:
$(selector).siblings().css({...});
or if you want to include the element:
$(selector).parent().children().css({...});
Or in vanilla JS:
var element = document.querySelectorAll(selector); // or getElementById or whatever
var siblings = element.parentNode.childNodes;
for (var i = 0; i < siblings.length; i++) {
if (siblings[i] !== element) { // optional
siblings[i].style.color = 'red';
}
}
You can do this by using jQuery to toggle the hover states instead of CSS:
HTML:
<div>
Link 1
Link 2
Link 3
</div>
CSS:
div a {color: #000;}
div a.hover {color: #00f;}
jQuery:
$("div a").hover(
function(){
$("div a").addClass("hover");
},
function(){
$("div a").removeClass("hover");
}
);
Fiddle
I need an if/else statement for my CSS which can count list items. Would this be possible?
Basically I want to say, if there are less than 10 list items, the UL container should be 200px wide, and it there are more than 10 list items, it should be 400px wide. Something like that.
Can it be done?
I would appreciate a working demo on jsFiddle, both so I can see working code, and for anyone who looks here in the future so they can see a working example and how to do it :)
CSS only does styles, but not dynamically (unless with assistance of JS). you can use the following JS snippet for the task. just to make sure, load this at the very last, just before the </body>
<script type="text/javascript">
(function resize() {
//get all lists with selected name
var lists = document.getElementsByClassName('myList');
//loop through all gathered lists
for (i = 0; i < lists.length; i++) {
//shorthand elements for easy use
var list = lists[i];
var items = list.getElementsByTagName('li');
//append class names
list.className = (items.length < 10) ? 'myList less' : 'myList more';
}
}())
</script>
.less{
width:200px;
}
.more{
width:400px;
}
CSS has no if else statements. You can do this easily with jQuery. Another option would be to use LESS or SCSS.
Short answer: no. CSS offers no conditional support.
Long answer: you need to use javascript or a server side language to either add a class when there are more than 10 items (or elements) in the list, or in the case of javascript, directly manipulate the style after it's loaded.
That doesn't sound possible for CSS. There are no logical if/else statements in the CSS spec. Your next best bet would probably be javascript. You could achieve this with jQuery with the following code:
if($('ul#target-list li').length < 10) {
$('ul#target-list').css('width', 200);
}
else {
$('ul#target-list').css('width', 400);
}
Pure CSS3 Solution
If you only want to support CSS3, then this does what you need:
li {
width: 200px;
}
li:nth-last-child(n+11),
li:nth-last-child(n+11) ~ li {
width: 400px;
}
But you will need to make the ul either display: inline-block or float it so that the width is controlled by the li elements themselves. This may require you to wrap the ul (display: inline-block) in a div so that it still is a block element in the flow of the page if you need it so.