How can I conditionally add a class to a ul if it has no li children?
I would like to use the syntax
class=" #(myCondition == 1 ? "myClass" : "")"
This is actually a JavaScript conditional, HTML/CSS isn't able to run that. What we can do instead is use script tags to use JavaScript to run the conditional.
class=" #(myCondition == 1 ? "myClass" : "")"
Can instead be written with JavaScript using Document object model to add classes to a element.
So what you might do instead is set the specific ul to an id of your choosing in the HTML.
<ul id="example">
Then we can add the JavaScript to the bottom of the <body>
<script>
if(myCondition == 1){
var ex = document.getElementById("example");
ex.className += " myClass";
}
</script>
document.querySelectorAll(".ul").forEach(el => el.classList += el.children.length ? " hasChildren" : "")
.ul{
background: lightskyblue;
padding:1em;
color: white;
}
.hasChildren{
background-color: orangered;
}
<ul class="ul">
<li>Item</li>
</ul>
<ul class="ul">
</ul>
<ul id="example"></ul>
<script type="text/javascript">
jQuery( document ).ready( function ()
{
if ( jQuery( "#example" ).children().length == 0 )
{
jQuery( "#example" ).addClass("myClass");
}
})
</script>
Related
I'm creating dynamic tabs. I'm currently facing two problems:
When I click on the span x to delete current tab, it deletes all my tabs.
When I getting the array data, it always gets the first tab data only.
Can anyone help me with this? I've tried many ways but I still cannot get my desired result. Here is my fiddle Dynamic Tabs.
Currently my array result looks like this for the 2nd problem when there is two tabs, '2023' and '2025':
[{
February: "1",
January: "1",
Year: "2023"
}, {
February: "1",
January: "1",
Year: "2023"
}]
My expected result would be:
[{
February: "1",
January: "1",
Year: "2023"
}, {
February: "1",
January: "1",
Year: "2025"
}]
$(document).ready(function() {
addTab();
});
$('#add_tab').click(function() {
addTab()
});
//delete current tab
$(".nav-tabs").on("click", "span", function() {
var anchor = $(this).siblings('a');
console.log(anchor)
$(anchor.attr('href')).remove();
$(this).parent().remove();
$(".nav-tabs").children('a').first().click();
});
function addTab() {
var nextTab = $(".nav-tabs").children().length;
var date = new Date().getFullYear() + nextTab;
// create the tab
$('<a class="nav-link" href="#tab-' + date + '" data-toggle="tab">' + date + '</a><span> x </span>').appendTo('#tabs');
// create the tab content
var html = "";
html += '<div class="tab-pane monthSettings" id="tab-' + date + '">';
html += '<label><b>Year: </b></label>';
html += '<input class="txtYear" type="text" value="' + date + '">';
html += '<label><b>January: </b></label>';
html += '<input class="txtJanuary" type="number" value="1">';
html += '<label><b>February: </b></label>';
html += '<input class="txtFebruary" type="number" value="1">';
html += '</div>';
//append to tab-content
var test = $(html).appendTo('.tab-content');
// make the new tab active
$('#tabs a:last').tab('show');
}
//get array
$(document).on('click', '#btnGetArray', function(e) {
var array = []
$(".monthSettings").each(function() {
let detail = {
Year: $(".txtYear").val() || 0,
January: $(".txtJanuary").val() || 0,
February: $(".txtFebruary").val() || 0,
}
array.push(detail)
console.log(array)
});
});
#import url('http://getbootstrap.com/2.3.2/assets/css/bootstrap.css');
.container {
margin-top: 10px;
}
.nav-tabs>a {
display: inline-block;
position: relative;
margin-right: 10px;
}
.nav-tabs>a>span {
display: none;
cursor: pointer;
position: absolute;
right: 6px;
top: 8px;
color: red;
}
.nav-tabs>a>span {
display: inline-block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script type="text/javascript" src="https://getbootstrap.com/2.3.2/assets/js/bootstrap.js"></script>
<link rel="stylesheet" type="text/css" href="https://getbootstrap.com/2.3.2/assets/css/bootstrap.css">
<div class="bg-gray-300 nav-bg">
<nav class="nav nav-tabs" id="tabs">
+ Add Year
</nav>
</div>
<div class="card-body tab-content"></div>
<button id="btnGetArray">GetData</button>
The issue is because your selectors for retrieving the .txtYear, .txtJanuary and .txtFebruary will only look at the value of the first element in the collection, no matter how many it finds.
To correct this you can use find() from the parent element, which you can reference from the each() loop, to retrieve the child elements in that iteration.
Taking this a step further, you can simplify the logic by using map() instead of each() to build your array, but the use of find() remains the same.
In addition, there's some other improvements which can be made to the code, such as ensuring all event handlers are within document.ready and using template literals to make the HTML string concatenation easier to read.
jQuery($ => {
$('#add_tab').on('click', addTab);
addTab();
$(".nav-tabs").on("click", "span", function() {
var anchor = $(this).siblings('a');
console.log(anchor)
$(anchor.attr('href')).remove();
$(this).parent().remove();
$(".nav-tabs").children('a').first().click();
});
$(document).on('click', '#btnGetArray', e => {
var array = $(".monthSettings").map((i, container) => ({
Year: $(container).find('.txtYear').val() || 0,
January: $(container).find('.txtJanuary').val() || 0,
February: $(container).find('.txtFebruary').val() || 0,
})).get();
console.log(array);
});
});
function addTab() {
var nextTab = $(".nav-tabs").children().length;
var date = new Date().getFullYear() + nextTab;
$(`<a class="nav-link" href="#tab-${date}" data-toggle="tab">${date}</a><span> x </span>`).appendTo('#tabs');
var html = `
<div class="tab-pane monthSettings" id="tab-${date}">
<label><b>Year: </b></label>
<input class="txtYear" type="text" value="${date}" />
<label><b>January: </b></label>
<input class="txtJanuary" type="number" value="1" />
<label><b>February: </b></label>
<input class="txtFebruary" type="number" value="1" />
</div>`
var test = $(html).appendTo('.tab-content');
// make the new tab active
$('#tabs a:last').tab('show');
}
#import url('http://getbootstrap.com/2.3.2/assets/css/bootstrap.css');
.container {
margin-top: 10px;
}
.nav-tabs>a {
display: inline-block;
position: relative;
margin-right: 10px;
}
.nav-tabs>a>span {
display: none;
cursor: pointer;
position: absolute;
right: 6px;
top: 8px;
color: red;
}
.nav-tabs>a>span {
display: inline-block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script type="text/javascript" src="https://getbootstrap.com/2.3.2/assets/js/bootstrap.js"></script>
<link rel="stylesheet" type="text/css" href="https://getbootstrap.com/2.3.2/assets/css/bootstrap.css">
<div class="bg-gray-300 nav-bg">
<nav class="nav nav-tabs" id="tabs">
+ Add Year
</nav>
</div>
<div class="card-body tab-content"></div>
<button id="btnGetArray">GetData</button>
So I'm working on this small frontend project with some jQuery I wanted to do something where there is a list of names and if you click it, a class of "selected" should be added, but I want to remove that class if some other option is selected.
So suppose Name-1 is selected It gets a background Color Change, but If Name-2 is selected right after the background color for Name-1 should go away and add to Name-2, and I have multiple names so I can't make a function for every single option.
I was wondering if there is a easier way to do it.
HTML
<ul class="names">
<li class="name selected">Name-1</li>
<li class="name">Name-2</li>
<li class="name">Name-3</li>
</ul>
CSS
.selected {
background-color: rgba(0, 0, 0, 0.3);
}
you can do it like this:
$("ul.names li").on("click", function(){
$("ul.names li").removeClass("selected");
$(this).addClass("selected");
})
EDIT - explanation
you select all the <li> tags by this selector ul.names li and manipulate them together, first you remove the selected class from all li tags by this $("ul.names li").removeClass("selected"); and then you add selected class to the chosen li tag represented by the this key word
$("ul.names li").on("click", function() {
$("ul.names li").removeClass("selected");
$(this).addClass("selected");
})
.selected {
background-color: rgba(0, 0, 0, 0.3);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul class="names">
<li class="name selected">Name-1</li>
<li class="name">Name-2</li>
<li class="name">Name-3</li>
</ul>
Something like this should work.
$('.name').on('click', function() {
$('.name').removeClass('selected').css('background','none');
$(this).addClass('selected').css('background',$(this).data('bg'));
});
$('.name.selected').css('background',$('.name.selected').data('bg'));
.selected {
color: #fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul class="names">
<li class="name selected" data-bg="green">Name-1</li>
<li class="name" data-bg="red">Name-2</li>
<li class="name" data-bg="blue">Name-3</li>
</ul>
I have a website you can visit at http://www.ssdpsjal.tk
As you can see when you visit the website using PC, the navigation menu appears on the loader. Why is this happening?
I have only added the following lines to my css:-
<style>
.navbar-inverse {
background-color: #3A1F3B;
border-color: #222222;
}
.navbar {
margin-bottom: 0px;
}
</style>
And a class to call it->
<nav class="MY_OLD_CLASSES + nav nav_inverse">
I have added nothing else, please help me fix the problem.
Help Appreciated
EDIT
My full code is
<style>
.navbar-inverse {
background-color: #3A1F3B;
border-color: #222222;
}
.navbar {}
</style>
<div class="navbar navbar-inverse nav-bar-main" role="navigation">
<div class="container">
<nav class="main-navigation clearfix visible-md visible-lg" role="navigation">
<ul class="main-menu sf-menu">
<?php
foreach ($main as $lvl1){
if(strpos($lvl1->href, 'http') !== false){ $lvl1_link = $lvl1->href;} else {$lvl1_link = link_url().$lvl1->href;}
echo '<li>'.$lvl1->title.'';
If ($lvl1->dropdown == "1"){
echo '<ul class="sub-menu" style="width:300px;">';
foreach ($sub as $lvl2){
If ($lvl2->mid == $lvl1->id){
If ($lvl2->dropdown == "1"){
if(strpos($lvl2->href, 'http') !== false){$lvl2_link = $lvl2->href;} else {$lvl2_link = link_url().$lvl2->href;}
echo '<li>'.$lvl2->title.'<ul class="sub-menu">';
foreach($subsub as $lvl3){
If ($lvl3->mid == $lvl2->id){
if(strpos($lvl3->href, 'http') !== false){$lvl3_link = $lvl3->href;} else {$lvl3_link = link_url().$lvl3->href;}
echo '<li>'.$lvl3->title.'</li>';
}
}
echo '</ul></li>';
}else{
if(strpos($lvl2->href, 'http') !== false){$lvl2_link = $lvl2->href;} else {$lvl2_link = link_url().$lvl2->href;}
echo '<li>'.$lvl2->title.'</li>';
}
}
}
echo '</ul>';
}
echo '</li>';
}
?>
</ul> <!-- /.main-menu -->
</nav> <!-- /.main-navigation -->
</div> <!-- /.container -->
</div> <!-- /.nav-bar-main -->
EDIT 2
If I remove navbar and navbar-inverse classes, the problem is fixed but i don't want the UI to change so I cant remove them.
Since you are adding class loaded to body, you can do:
body:not(.loaded) .navbar {
display: none;
}
I have found the solution. (My bad, I did not see it before :( )
According to my JQuery navbar and navbar-inverse were already occupied somwhere else so I simply used style="" tag
Here is my code:
a) I have a row of buttons at the top formatted horizontally as such:
HTML:
<ul class="nav">
Work
Volunteer
Education
Skills
References
Images
</ul>
b) I have div blocks each displaying a paragraph:
<div class="jobs">
<h2>text</h2>
<h3>text</h3>
<h4>text</h4>
</div>
c) I want the CSS to not display the jobs div yet:
.jobs {
display: none;
}
d) Now that I hover over the first button I want the jobs div to display:
.button1:hover+.jobs {
display: block
}
e) Repeat for all other div sections
.volunteer {
display: none;
}
.button2:hover+.volunteer {
display:block
}
You will need to markup HTML differently.
.jobs, .volunteer {
display: none;
}
.button1:hover+.jobs, .button2:hover+.volunteer {
display: block;
/* position the divs under the navigation links */
position: absolute;
top: 120px;
}
<ul class="nav">
<li>
Work
<div class="jobs">
<h2>h2 jobs</h2>
<h3>h3 jobs</h3>
<h4>h4 jobs</h4>
</div>
</li>
<li>
Volunteer
<div class="volunteer">
<h2>h2 volunteer</h2>
<h3>h3 volunteer</h3>
<h4>h4 volunteer</h4>
</div>
</li>
<li> Education</li>
<li> Skills</li>
<li> References</li>
<li> Images</li>
</ul>
This is impossible, as described, with your current HTML, with only HTML and CSS (though only perhaps until the reference and :matches() pseudo-selectors arrive). However, if, rather than :hover you'd be willing to work with clicks on the list-elements, it can be done (without JavaScript). Given the corrected HTML:
<ul class="nav">
<li>Work
</li>
<li> Volunteer
</li>
<!-- and so on... -->
</ul>
<div id="details">
<div id="jobs"></div>
<div id="volunteer"></div>
<!-- and so on... -->
</div>
The following CSS will show the relevant div element once the <a> element has been clicked on (note that the use of an id is essential for this to work):
#details > div {
/* to hide the eleemnt(s) initially: */
display: none;
}
#details > div:target {
/* to show the relevant element once the relevant link is clicked: */
display: block;
}
#details > div[id]::after {
content: attr(id);
}
#details > div {
display: none;
}
#details > div:target {
display: block;
}
<ul class="nav">
<li>Work
</li>
<li> Volunteer
</li>
<li> Education
</li>
<li> Skills
</li>
<li> References
</li>
<li> Images
</li>
</ul>
<div id="details">
<div id="jobs"></div>
<div id="volunteer"></div>
<div id="education"></div>
<div id="skills"></div>
<div id="references"></div>
<div id="images"></div>
</div>
With plain JavaScript, on the other hand, it can be achieved with:
// the 'e' argument is automatically to the function by addEventListener():
function toggleRelevant (e) {
// caching the 'this' element:
var self = this,
// finding the div element with a class equal to the href of the 'a' element
// (though we're stripping off the leading '#':
relevantElement = document.querySelector('div.' + self.getAttribute('href').substring(1) );
// if the event we're responding to is 'mouseover' we set the display of the
// found div to 'block', otherwise we set it to 'none':
relevantElement.style.display = e.type === 'mouseover' ? 'block' : 'none';
}
// finding all the a elements that are in li elements:
var links = document.querySelectorAll('li a');
// iterating over those a elements, using Array.prototype.forEach:
[].forEach.call(links, function(linkElem){
// adding the same event-handler for both mouseover and mouseout:
linkElem.addEventListener('mouseover', toggleRelevant);
linkElem.addEventListener('mouseout', toggleRelevant);
});
function toggleRelevant(e) {
var self = this,
relevantElement = document.querySelector('div.' + self.getAttribute('href').substring(1));
relevantElement.style.display = e.type === 'mouseover' ? 'block' : 'none';
}
var links = document.querySelectorAll('li a');
[].forEach.call(links, function(linkElem) {
linkElem.addEventListener('mouseover', toggleRelevant);
linkElem.addEventListener('mouseout', toggleRelevant);
});
div[class] {
display: none;
}
div[class]::before {
content: attr(class);
color: #f00;
border: 1px solid #f00;
padding: 0.2em;
}
<ul class="nav">
<li>Work
</li>
<li> Volunteer
</li>
<!-- and so on... -->
</ul>
<div class="jobs">
<h2>text</h2>
<h3>text</h3>
<h4>text</h4>
</div>
<div class="volunteer">
<h2>text</h2>
<h3>text</h3>
<h4>text</h4>
</div>
<!-- and so on... -->
I don't think this is do able in css since display blocks (job, volonteer, ...) and button are not parent. But in jQuery this is fairly simple :
$('.buttonX').hover(
function() {
// Styles to show the box
$('.boxX').css(...);
},
function () {
// Styles to hide the box
$('.boxX').css(...);
}
);
It sounds like you're trying to do some kind of a tab menu where pressing a specific button shows a different content. Here's a SO page that describes how it's done: How to make UL Tabs with only HTML CSS
Currently, I am using something like
<li class="active">Home</li>
<li>A</li>
<li>B</li>
<li>C</li>
where I have four html files index.html, a.html, b.html and c.html, which are selected based on the links clicked.
Instead, I want to have the content all in the same HTML file with common headers and footers and just selectively display content depending on which button was clicked.
How can I do that?
you can do this
put content of each page in div has unique id and display all none and each a in li have id of div
html
<li><a class="div1" href="#1">A</a></li>
<li><a class="div2" href="#2">B</a></li>
<li><a class="div3" href="#3">C</a></li>
<div id="1" class="hide" style=" width:100%; height: 100px; background-color:red; "></div>
<div id="2" class="hide" style=" width:100%; height: 100px; background-color:gold; "></div>
CSS
.hide
{
display:none;
}
JS
<script>
$(function () {
$('li').on('click', function (e) {
var href = $(this).find('a').attr('href');
window.location.hash = href;
});
$('.div1').on('click', function (e) {
$("#1").removeClass("hide");
$("#2").addClass("hide");
$("#3").addClass("hide");
});
$('.div2').on('click', function (e) {
$("#2").removeClass("hide");
$("#1").addClass("hide");
$("#3").addClass("hide");
});
$('.div3').on('click', function (e) {
$("#3").removeClass("hide");
$("#1").addClass("hide");
$("#2").addClass("hide");
});
if (window.location.hash == "#1") {
$("#1").removeClass("hide");
$("#3").addClass("hide");
$("#2").addClass("hide");
}
if (window.location.hash == "#2") {
$("#2").removeClass("hide");
$("#3").addClass("hide");
$("#1").addClass("hide");
}
if (window.location.hash == "#3") {
$("#3").removeClass("hide");
$("#1").addClass("hide");
$("#2").addClass("hide");
}
});
</script>