Can modern CSS display/hide N children items from X by random? - css

Say I have X items in a <ul> DOM:
<ul>
<li>1</li>
<li>2</li>
...
<li>X</li>
<ul>
It's easy to do this with JavaScript but is it possible to only display N items by random with pure CSS commands?
For "display", I mean the opposite of display:none. So there are X-N items be regarded as display:none

This is not possible with only CSS because it will require some programming, and CSS is not a programming language.
You can do this with Javascript and CSS as follows:
const items = document.querySelectorAll("li");
const numberToShow = Math.floor(Math.random() * items.length);
for (let i = 0; i < items.length; i++) {
if (i < numberToShow) {
items[i].style.display = "block";
} else {
items[i].style.display = "none";
}
}

Related

Is there a way to select with CSS a string up to a specific character?

I want to be able to style my li list items when converted from markdown without having to tag them.
Specifically:
I want to make bold the first part of a list item when it is formed like a key value pair.
The visual result should look like the equivalent of this:
<ul>
<li><strong>key: </strong>value</li>
</ul>
So, is there a way to select the string inside the li item up to the colon?
This isn't possible in CSS alone, but it is quite straightforward to deploy a snippet of javascript which surrounds the key: part of the text with a <span>:
<li><span class="key">key:</span> value</li>
which then enables you to style that part of the text.
Working Example:
const myList = document.getElementsByTagName('ul')[0];
const myListItems = myList.getElementsByTagName('li');
for (let i = 0; i < myListItems.length; i++) {
let myListItemColonPosition = myListItems[i].textContent.indexOf(':')
markup = '';
markup += '<span class="my-key">';
markup += myListItems[i].textContent.substring(0, myListItemColonPosition);
markup += '</span>';
markup += myListItems[i].textContent.substring(myListItemColonPosition);
myListItems[i].innerHTML = markup;
}
li {
font-size: 16px;
line-height: 24px;
}
.my-key {
font-weight: 700;
}
<ul>
<li>key0: value0</li>
<li>key1: value1</li>
<li>key2: value2</li>
<li>key3: value3</li>
<li>key4: value4</li>
</ul>

CSS-Sass : Is there a way to select a group of elements and iterate over it?

I have an HTML-structure similar this one:
<div class="wrap">
<div class="char">A</div>
<div class="char">B</div>
<div class="char">C</div>
<div class="char">D</div>
</div>
Now I want to iterate over each element which has the class "char".
In JavaScript one possible way would be:
// ...
// Select elements with Class "char".
var chars = document.querySelectorAll('.char');
// ...
// After changing the node-list to an array I could ...
for (i = 0; i < chars.length; i++) {
// Do something with the element. E.g. apply an transform.
// ...
Is there a way to accomplish these operations "select group of elements", "iterate over group", "process each element" with Sass?
Update:
This should make it clear what I like to do. Things similar like this one but with just Sass (instead of JavaScript):
var chars = document.querySelectorAll('.char');
chars = Array.prototype.slice.call(chars);
var i;
for (i = 0; i < chars.length; i++) {
chars[i].style['margin-left'] = (i * 10) + 'px';
}
<div class="wrap">
<div class="char">A</div>
<div class="char">B</div>
<div class="char">C</div>
<div class="char">D</div>
<div class="char">E</div>
</div>
sass allows you to create own simple function
see for example http://hugogiraudel.com/2013/08/08/advanced-sass-list-functions/

How do you avoid repeating styles in shadow DOM?

I'm new to web components, so not sure if there's an easy answer or best practice on this one.
If I want to create a series of repeating elements with encapsulated style how do I avoid repeating the style block with each element.
A reduced version of the code I'm using is:
<ul id="wrapper"></ul>
<template id="template">
<style>
li { color: red }
</style>
<li></li>
</template>
<script>
var wrapper = document.getElementById('wrapper');
var tpl = document.getElementById('template');
var shadow = wrapper.createShadowRoot();
var arr = ['a', 'b', 'c'];
for(var i = 0, ii = arr.length; i < ii; i++) {
tpl.content.querySelector('li').textContent = arr[i];
var clone = document.importNode(tpl.content, true);
shadow.appendChild(clone);
}
</script>
The problem I have with that is that the shadow DOM has a duplicated style block for each <li>, and am not sure the best way round it.
I have tried nesting a template and that is either never going to work or I'm not doing it right.
Fast-forward to 2022 and we now have "constructable stylesheets". Reference here. Basically the browser is smart enough to use only a single constructed stylesheet for all those copies of your webcomponent on the page! :)
Quoting from the article:
Constructable Stylesheets make it possible to define and prepare shared CSS styles, and then apply those styles to multiple Shadow Roots or the Document easily and without duplication. Updates to a shared CSSStyleSheet are applied to all roots into which it has been adopted
How about checking for a style element and importing it separately?
<ul id="wrapper"></ul>
<template id="template">
<style>
li { color: red }
</style>
<li></li>
</template>
<script>
var wrapper = document.getElementById('wrapper');
var tpl = document.getElementById('template').cloneNode(true);
var shadow = wrapper.createShadowRoot();
var style = tpl.content.querySelector('style');
if (style) {
tpl.content.removeChild(style);
shadow.appendChild(document.importNode(style, true));
}
var arr = ['a', 'b', 'c'];
for(var i = 0, ii = arr.length; i < ii; i++) {
tpl.content.querySelector('li').textContent = arr[i];
shadow.appendChild(document.importNode(tpl.content, true));
}
</script>

MVC Razor recursive list numbering

I have a recursive list like this:
#ShowTree(pages, 1)
#helper ShowTree(List<pages> pages, int level)
{
<ul>
#for (int i = 0; i < pages.Count(); i++)
{
<li>
<h1>#pages[i].PageName</h1>
#if (pages[i].children.Any())
{
#ShowTree(pages[i].children, level + 1)
}
</li>
}
</ul>
}
This works all fine, but I need the list to have a format of:
1
1.1
1.2
1.2.1
1.2.2
1.3
And so on.
The reason that I have the "level"-parameter is an earlier attempt to create this, without luck.
Also, I need each heading to be leveled, with <h1>, <h2>, <h3> (...).
All help is appreciated! Thanks.
You may try something like this :
#ShowTree(pages)
#helper ShowTree(List<pages> pages, int level=1, string prefix ="")
{
<ul>
#for (int i = 0; i < pages.Count(); i++)
{
<li>
#Html.Raw(String.Format("<h{0}>", level)) #String.Format("{0}{1}", prefix, i + 1) #pages[i].PageName #Html.Raw(String.Format("</h{0}>", level))
#if (pages[i].children.Any())
{
#ShowTree(pages[i].children, level + 1, String.Format("{0}{1}.", prefix, i + 1))
}
</li>
}
</ul>
}

Apply style to first element in a row of similar elements

I have the following list (the numbers are just for reference)
<div class="A">alpha1</div>
<div class="B">alpha2</div>
<div class="A">alpha3</div>
<div class="A">alpha4</div>
<div class="A">alpha5</div>
<div class="B">alpha6</div>
<div class="A">alpha7</div>
I want to apply one style to DIVS 1, 3 and 7, because they are the first of their class (A) in a row of elements of the same class. Is there a pseudo element / magic I can use for that? Something like (inventing)
not(.A) & .A {color:red} -> if class is A and it is not preceded by an A
Thanks!
You use the :not() pseudo-class with an adjacent sibling combinator + to match an .A that is not immediately preceded by an .A:
:not(.A) + .A
You'll also need to use :first-child to select the very first .A element since it's not preceded by anything:
.A:first-child
Combine them, and you have:
:not(.A) + .A, .A:first-child { color: red; }
jsFiddle demo
Here is a cross browser solution using JavaScript:
http://jsfiddle.net/YhvGw/
function applyStyleToFirstDiv(className, styleAttr, val,baseSelector) {
//Allow to specify a base element to search in
if(baseSelector == null){
baseSelector = document
}
var divElements = baseSelector.getElementsByTagName("div"),
len = divElements.length;
var prevWas = false,currentIs;
// Go through all the divs
for (var i = 0; i < len; i++) {
var cur = divElements[i];
var classes = cur.className.split(" ");
currentIs = false;
for (var j = 0; j < classes.length; j++) {
//If you find a matching class
if (classes[j] === className) {
currentIs = true;
break;
}
}
//If the current one matches, and the last one didn't, apply the style, otherwise don't
if(currentIs && !prevWas){
cur.style[styleAttr] = val;
}
prevWas = currentIs;
}
}
//usage sample
applyStyleToFirstDiv("A","color","yellow");
Here is an example:
div:not(.A) + .A, .A:first-of-type{
color:red;
}

Resources