I'm working with HTML similar to this (coming from a third party web site - this is how the code is served, and I have no control over it):
<ul class"list">
<li>first list item</li>
<li>second list item</li>
<div>
<li>third list item</li>
<li>fourth list item</li>
</div>
<li>fifth list item</li>
<li>sixth list item</li>
</ul>
How can I select the fifth list item using CSS? With Javascript, if I use document.querySelectorAll('ul li') in the DOM, I get an iterable NodeList with all 6 of the <li> elements.
So then let list_items = document.querySelectorAll('ul li') allows me to access the fifth list item using list_items[4]. But if I try document.querySelectorAll('ul li:nth-child(5)') it doesn't work because, apparently, CSS only sees 4 <li> elements (the direct children of <ul>).
Is there a way to do this with CSS, or do I need to rework my approach?
* Edit *
I need to be able to select any of the li elements, arbitrarily, based on user input. The presence and number of li elements after the div section varies. I can correctly select any of the li elements before the div using something like ul > li:nth-of-type(${index_from_user_input}) - or within the div using something like ul > div > li:nth-of-type(${index_from_user_input}). I'm initially counting the number of lis and using that as the basis for the indexing...so if user input indicates that I need to select the third li, for example, I want to be able to simply use something like ul li:nth-of-type(3).
But if index_from _user_input points to an element after the div, then it doesn't work correctly, since CSS doesn't recognized the lis within the div, so the index numbers are off in my selector. I was hoping for a cleaner solution that uses only ul li:nth-child() or ul li:nth-of-type() so that I wouldn't have to check how many lis are within and after the div. I suspect it's just not possible, given the bad markup, but I'm not 100% sure of that, so I figured I'd ask here, in case somebody might have a clever way to do it. Otherwise, I can just rework the code and approach the whole thing differently.
this is how the code is served, and I have no control over it
Oh, you really do. You can just remove the erroneous div from the list with javascript.
Working Example:
/* IDENTIFY ELEMENTS */
var list = document.getElementsByTagName('ul')[0];
var erroneousDiv = list.getElementsByTagName('div')[0];
var select = document.getElementsByTagName('select')[0];
var button = document.getElementsByTagName('button')[0];
/* IDENTIFY LIST ITEMS */
var allListItems = document.querySelectorAll('ul li');
var listItemsToMove = erroneousDiv.getElementsByTagName('li');
var numberOfListItemsToMove = listItemsToMove.length;
/* REARRANGE LIST ITEMS AND REMOVE ERRONEOUS DIV */
for (var i = 0; i < numberOfListItemsToMove; i++) {
list.insertBefore(listItemsToMove[0], erroneousDiv);
}
list.removeChild(erroneousDiv);
/* SELECTOR FUNCTION */
function selectListItem() {
for (var i = 0; i < allListItems.length; i++) {
allListItems[i].removeAttribute('style');
}
var selected = select.value;
var listItem = document.querySelector('ul li:nth-of-type(' + selected + ')');
listItem.style.color = 'blue';
listItem.style.fontWeight = 'bold';
}
button.addEventListener('click', selectListItem, false);
ul, form {
display: inline-block;
margin-right: 80px;
vertical-align: middle;
}
<ul class"list">
<li>first list item</li>
<li>second list item</li>
<div>
<li>third list item</li>
<li>fourth list item</li>
</div>
<li>fifth list item</li>
<li>sixth list item</li>
</ul>
<form>
<select>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
</select>
</select>
<button type="button">Select List Item</button>
</form>
Related
I'm working in Vue file, and what i want is to highlight the active element, what would be the best way to accomplish it?
code
<li #click = "selectedComponent = 'appBugs1'"><i class="ion-bug"></i>Test 1</li>
<li #click = "selectedComponent = 'appBugs2'"><i class="ion-bug"></i>Test 2</li>
<li #click = "selectedComponent = 'appBugs3'"><i class="ion-bug"></i>Test 3</li>
So lets say the first "li" element was selected -> it should give red background to "li", and if another one selected, it must reset the first one and assign the red background to the new selected element.
I tried to search on the web, but there is nothing much about it, it would be easy if you have only 2 options, but my list is much larger. So what would be the best way to solve this?
You could do it like this.
<li #click = "selectComponent('appBugs1', $event)"><i class="ion-bug"></i>Test 1</li>
<li #click = "selectComponent('appBugs2', $event)"><i class="ion-bug"></i>Test 2</li>
<li #click = "selectComponent('appBugs3', $event)"><i class="ion-bug"></i>Test 3</li>
Add the following method:
selectComponent: function(component, event){
if(this.activeLink){
this.activeLink.classList.remove('highlight');
}
this.activeLink = event.target;
this.activeLink.classList.add('highlight');
this.selectedComponent = component;
};
And the property activeLink. Then add your css styling, for example:
.highlight{
background-color: yellow;
}
I'd like to use CSS to obtain an ordered list with continued numbering and in reverse. See attached figure.
Naturally, I'd like the resulting list to be in reverse order, i.e., [5], [4], etc.
Do I have to redesign the CSS part or is it a simple change? I can't figure out how to obtain the reverse numbering.
P.S.: I apologize for not putting the code in the question. Stack Overflow kept saying there was something wrong with it, even though everything was formed correctly in the preview. I lost patience after a few minutes trying to "fix" it.
Using CSS Counters
Currently there is no way to fully automate reverse numbering across multiple ol elements (without reversing the order of display of list items) with CSS counters when the no. of elements is dynamic. If reversing the order of display of list on the whole is fine, have a look at this answer.
You can make use of a bit of JavaScript along with counters to achieve this. The approach would be as follows:
Using JS, get the count (liCount) of applicable li elements when the page is loaded.
Set liCount as the second parameter for the counter-reset property on the parent container which would contain all the applicable ol elements. The second parameter to counter-reset property represents the initial/start value of the counter.
In CSS, set -1 as the second parameter for the counter-increment property. Generally the second parameter is the number by which the counter would be incremented every time. Here, since the value is set as -1 the counter would actually get decremented.
As normal, display the value of the counter using a :before pseudo element.
window.onload = function() {
var liCount = document.querySelectorAll('ol > li').length;
document.body.setAttribute('style', 'counter-reset: li ' + (liCount + 1));
}
ol {
list-style-type: none;
margin-left: 20px;
padding: 0px;
}
ol > li {
counter-increment: li -1;
}
ol > li:before {
content: "[" counter(li) "]";
padding-right: 10px;
}
<div id="content">
<h3>Year</h3>
<ul>
<li>2010-2015</li>
<li>2007-2008</li>
</ul>
</div>
<h3 id="2010-2015">2010-2015</h3>
<ol>
<li>A</li>
<li>B</li>
<li>C</li>
</ol>
<h3 id="2007-2008">2007-2008</h3>
<ol>
<li>D</li>
<li>E</li>
</ol>
Why not reversed attribute?
Browser Support for CSS counter is much better than the reversed HTML5 attribute. The reversed attribute is not at all supported by IE and Opera.
JS would still be required irrespective of the approach used (to assign counter-reset value for counters, start value for ol reversed) but the JS for setting counter-reset is much simpler than the other when the numbering is across multiple ordered lists.
Below is a sample snippet using the reversed attribute. I take no credit for the approach as it was taken from the linked thread and other answer here. I have used it with added JS only to illustrate what I meant in Point 2 above.
window.onload = function() {
var liCount = document.querySelectorAll('ol > li').length;
var olEls = document.querySelectorAll('ol[reversed]');
var prevLiCount = 0;
for (var i = 0; i < olEls.length; i++) {
/* the below lines are required because start for first ol is from 5 whereas for next is from 2 */
olEls[i].setAttribute('start', liCount - prevLiCount);
prevLiCount = olEls[i].querySelectorAll('li').length;
}
}
<div id="content">
<h3>Year</h3>
<ul>
<li>2010-2015
</li>
<li>2007-2008
</li>
</ul>
</div>
<h3 id="2010-2015">2010-2015</h3>
<ol reversed="reversed">
<li>A</li>
<li>B</li>
<li>C</li>
</ol>
<h3 id="2007-2008">2007-2008</h3>
<ol reversed="reversed">
<li>D</li>
<li>E</li>
</ol>
You could use something like this:
<ol reversed start="5">
<li>Coffee</li>
<li>Tea</li>
<li>Milk</li>
</ol>
how can I set color to p:selectOneMenu row,
I have a selectOneMenu with a client list. Canceled Customers want the row is red background color.
<p:selectOneMenu id="listaClientesmodi" value="#{clientesMB.selectedEmpClienteCancelarContrato}" converter="clientesConverter" panelStyle="width:500px"
effect="fade" var="p" style="width:500px" filter="true" filterMatchMode="contains">
<f:selectItems value="#{comunMB.itemsClientes}"/>
<p:column>
#{p.codigo} - #{p.nombre}
</p:column>
<f:ajax execute="#this" listener="#{clientesMB.cargarContratosClienteSelected}" render=":form2:panelDetalles :form2:panelEditContrato" />
</p:selectOneMenu>
If you want all rows to be painted:
.ui-selectonemenu-item {
background-color: aqua!important;
}
But if you want to make conditional painting; I found a bit complicated solution there can be another and much simpler solutions of course.
To be able to apply my solution you need a field which is the size of your clients those shown in the p:selectOneMenu. But more important thing you need to add text like canceled or whatever which will say to js function that item is need to be painted.
If comunMB.itemsClientes is shown in the p:selectOneMenu concatenate "canceled" string to canceled clients. Because the canceled clients need to be differentiated from others. And my solution requires it sorry for that was best I can do.
$(document).ready(function() {
for (var i = 0; i < '#{clientesMB.size}'; i++) {
if($(".ui-selectonemenu-items li:nth-child("+i+")").text().indexOf("Canceled") != -1) {
$(".ui-selectonemenu-items li:nth-child("+i+")").css({"background-color":"#ff1315"});
}
}
});
So; p:selectOneMenu generates a html list. If you investigate it from browsers developer settings, it's looking like:
<ul class="ui-selectonemenu-items">
<li>First Item</li>
<li>Second Item</li>
<li>Third Item</li>
</ul>
So function firstly selects the element from it's CSS class which is named as ui-selectonemenu-items and looks for it's children, and for each child it is looking for they are canceled or not.
indexOf function returns -1 when item doesn't include string of "canceled" so we are finding the elements which has "canceled" text and change their background color, that's it.
I would like to have all my menu items which are common to all the pages on my website in a single header file. The problem lies that I need to define a class for the current menu item so it changes color. I`m using superfish for the menu, here is a very simple mockup...
<nav>
<ul class="sf-menu">
<li id="first-li">Home</li>
<li class="current">Page 1
<ul>
<li>Submenu 1 </li>
<li>Submenu 2
<ul>
<li>SUBSubmenu 1 </li>
</ul>
</li>
</ul>
</li>
<li>Page 2 </li>
<li>Page 3</li>
<li>Page 4</li>
</ul>
</nav>
Is it possible to remove class="current" , move the whole nav to the header file and then only specify the current class on each page?
My actual menu code is much much larger and this is why I do not want it repeated on every page.
use this in your current page script
Use jquery for the thing..
$('.sf-menu li a').click(function() {
$('.sf-menu li.current').removeClass('current');
$(this).closest('li').addClass('current');
});
find the fiddle here..http://jsfiddle.net/VudYx/
Add class="current" to the index.php li in the header and just use the following jQuery code in header.
$(document).ready(function() {
var curUrl = window.location.pathname;
curUrl=curUrl.replace(/\/$/, "");//support urls with or without trailing slash
$( ".sf-menu li" ).each(function(i) {
if(($(this).children().length > 0) && ($(this).children(":first").attr("href").split('/').pop()==curUrl.split('/').pop()))
{
$( ".sf-menu li" ).removeClass("current");
$(this).addClass("current");
}
});
})
I got it working exactly how I want by combining a little of all the answers.
First I gave id`s to all the 1st tier menu items, the ones I want highlighted, then on each page I move the current class like so...
<script>
$(document).ready(function() {
$( ".sf-menu li" ).removeClass("current");
$( "#menu2" ).addClass("current");
});
</script>
clean, simple and works without clicking, thanks to all that answered you all helped.
Personally I'd achieve this using CSS.
If each menu item <li> has its own class, and your <body> has a unique ID per page, you can style the current item appropriately such that:
<li class="navHome">Home</li>
<li class="navAbout">About<li>
and your home/about pages have a body tag such that:
<body id="pgHome"> or <body id="pgAbout">
Then you can directly style the current page's menu item:
#pgHome .navHome,
#pgAbout .navAbout
{
// styles to highlight this item
}
This question already has answers here:
How to hover only the current li in nested ul?
(5 answers)
Closed 4 years ago.
I have got an simple html unordered list.
<ul>
<li>Item 1</li>
<li>
Group 1
<ul>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</li>
</ul>
I want to use CSS to make a simple effect when the mouse is over an Item or a Group.
li:hover
{
background-color:#ff0000;
}
It works quite fine for "Group 1" or "Item 1" (not contained in a group) - When I'm moving the mouse over the color changes. But if I move over "Item 2" or "Item 3" "Group 1" also remains hightlighted (red background). In this case I only want to highlight "Item 2" or "Item 3".
Has anyone an idea how to do this?
Thanks for your help!
===============================
EDIT
<ul>
<li>Item 1</li>
<li>
Group 1
<ul>
<li>Item 2</li>
<li>Group 2
<ul>
<li>Item 3</li>
<li>Item 4</li>
</ul>
</li>
</ul>
</li>
</ul>
Mouse Over xxx should highlight yyy
xxx -> yyy
Item1 -> Item1
Group1 -> Group1, Item2, Group2, Item3, Item4
Item2 -> Item2
Group2 -> Group2, Item3, Item4
Item3 -> Item3
Item4 -> Item4
Please see http://image-upload.de/image/r76d79/1c7af56a19.png ,just a quick drawing.
This solution isn't a purely HTML/CSS one, but it works. It uses the Javascript library jQuery.
http://jsfiddle.net/XP3Vp/
Put this in the head-section of your page:
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
<script type="text/javascript">
$('li').mouseover(function()
{
if ($('li ul li:hover').length)
{
$('li ul li:hover').css('background','red');
}
else
{
$('li:hover').css('background','red');
}
});
$('li').mouseout(function()
{
$(this).css('background', 'transparent');
});
</script>
Use this if you don't want the underlying list items to be highlighted as well when moving the cursor over Group 1: http://jsfiddle.net/CwhhN/
The best you can do is to colorize the ul as well ..
ul{background-color:#fff;}
li:hover
{
background-color:#ff0000;
}
something like this http://jsfiddle.net/gaby/DxsDa/ although it will still highlight the group 1 text..
Alternatively you can resort to invalid html but i would not suggest that for obvious reasons.. http://jsfiddle.net/gaby/DxsDa/1/
Group 1 contains Item 2. So, when you are hovering Item 2 you are also hovering Group 1.
Thus, with CSS what you want is not possible without mis-formatting HTML on purpose.
With JS you can get there, though.
If this is acceptable, refer to #RobinJ's answer.
Found probably the best solution at the jQuery documentation.
http://api.jquery.com/event.stopPropagation/
$('li').mouseover(function(e)
{
e.stopPropagation();
$(this).addClass('hover');
});
$('li').mouseout(function()
{
$(this).removeClass('hover');
});
Use class or id for UL element (which is parent for highlighted li's) and directly children selector:
ul#your_id > li:hover{ background-color: #f00; }
It will fix error which happens because you try to highlight every li's elements :)