I am currently creating multiple navigation buttons for my website, each of them would show up menus.
So as example, this is the first menu:
<div class="outer-bar">
<ul class="down-bar" style="list-style:hidden">
<li>
<label id="down-nav-1" class="down-nav" onclick="Dropdown1()">Knives <b class="caret_down">▼</b></label>
<div class="mainsizer">
<div class="dropdown-menus" id="dropdown-menu-1">
<label class="m9_bayonet" style="width:30px;height:30px;position:absolute;top:5px;left:5px;cursor:pointer;"></label>M9 Bayonet
<label class="karambit" style="width:30px;height:30px;position:absolute;left:5px;top:59px;cursor:pointer;"></label>Karambit
<label class="bayonet" style="width:30px;height:30px;position:absolute;left:5px;top:116px;cursor:pointer;"></label>Bayonet
<label class="butterfly" style="width:30px;height:30px;position:absolute;left:5px;top:170px;cursor:pointer;"></label>Butterfly Knife
<label class="flip" style="width:30px;height:30px;position:absolute;left:5px;top:225px;cursor:pointer;"></label>Flip Knife
<label class="falchion" style="width:30px;height:30px;position:absolute;left:5px;top:280px;cursor:pointer;"></label>Falchion Knife
<label class="gut" style="width:30px;height:30px;position:absolute;left:5px;top:334.5px;cursor:pointer;"></label>Gut Knife
<label class="shadow" style="width:30px;height:30px;position:absolute;left:5px;top:390px;cursor:pointer;"></label>Shadow Daggers
<label class="huntsman" style="width:30px;height:30px;position:absolute;left:5px;top:444px;cursor:pointer;"></label>Huntsman Knife
<label class="bowie" style="width:30px;height:30px;position:absolute;left:5px;top:498.5px;cursor:pointer;"></label>Bowie Knife
</div>
</div>
</li>
So as you see on the <label> element #down-nav-1, it has onclick attribute linked to the function Dropdown1() and Display of menu is None and the time.
And this is the function itself in Javascript:
function Dropdown1() {
document.getElementById("dropdown-menu-1").style.display = "inline-block";
document.getElementById("down-nav-1").style.cssText = "border: solid 3px gray;background-color: gray;border-radius: 10px;";
}
All of this would simply work fine, please see Fiddle. (Only "Knives" button works atm).
But then i tried to add a function, so when user clicks outside of the menu, the display would be set to 'None' again:
function Dropdown1() {
document.getElementById("dropdown-menu-1").style.display = "inline-block";
document.getElementById("down-nav-1").style.cssText = "border: solid 3px gray;background-color: gray;border-radius: 10px;";
}
$(document).click(function(event) {
if(!$(event.target).closest('#dropdown-menu-1').length) {
if ( ($('#dropdown-menu-1').style.display = "None" ) {
$('#dropdown-menu-1').style.display = "inline-block";
}
}
})
And after adding this code, there would be no action when clicking the button. ( see the result ).
I don't understand the specific problem in there, but please note, i am new to Javascript, and don't know many things in there.
What could the problem be? can i do this with pure Javascript or should i use jquery in this case?
There is syntax & logical error in the code.
Updated your code:
function Dropdown1() {
if ($('#dropdown-menu-1').css("display") !== "none") {
$('#dropdown-menu-1').css("display", "none");
} else {
$('#dropdown-menu-1').css("display", "inline-block");
}
document.getElementById("down-nav-1").style.cssText = "border: solid 3px gray; background-color: gray; border-radius: 10px;";
}
$(document).click(function(event) {
if (!$(event.target).closest('.outer-bar').length) {
if ($('#dropdown-menu-1').css("display") !== "none") {
$('#dropdown-menu-1').css("display", "none");
}
}
});
Please use == or === instead of the = operator when comparing inside your if blocks.
Also brackets were not closed in the if block expressions.
Also, style.display is a property associated to a JavaScript object and not a jQuery object. You should use .css("display") for a jQuery object.
Updated working fiddle: https://jsfiddle.net/nashcheez/bjfz7twq/7/
If you are using jQuery, please use it consistently. Don't mix both jQuery and Vanilla JavaScript. To do something very simple dropdown like that, you just need to use JavaScript to only change classes and not the original effect. All the others should be handled by CSS.
Technically, this is achievable just using HTML and CSS. There are 1000s of examples outside. Also, you should use event handlers instead of functions for individual dropdowns, as it is better for scalability.
First thing, change this in CSS:
.mainsizer.open .dropdown-menus {
display: block;
}
And in your JavaScript, do this:
$(function () {
$("label").click(function () {
if ($(this).next(".mainsizer").hasClass("open"))
$(this).next(".mainsizer").toggleClass("open");
else {
$(".open").removeClass("open");
$(this).next(".mainsizer").addClass("open");
}
});
});
Ultimately, you should have this code:
$(function () {
$("label").click(function () {
if ($(this).next(".mainsizer").hasClass("open"))
$(this).next(".mainsizer").toggleClass("open");
else {
$(".open").removeClass("open");
$(this).next(".mainsizer").addClass("open");
}
});
});
.outer-bar {
position: relative;
display: block;
z-index: 1;
text-align: center;
top: 55px;
background-color: black;
height: 60px;
width: 100%;
}
.down-bar {
list-style-type: none;
font-size: 105%;
}
.down-bar li {
display: inline-block;
margin-top: 15px;
margin-right: 2%;
vertical-align: top;
}
.down-nav {
z-index: 1
font-size: 105%;
color: white;
font-family: 'Lato';
cursor: pointer;
transition: all 0.1s ease;
-moz-transition: all 0.1s ease;
}
.down-nav:hover {
color: white;
border: solid 3px gray;
background-color: gray;
border-radius: 10px;
font-family: 'Lato';
}
.down-nav li {
display: inline-block;
}
.caret_down {
font-size: 30%;
}
.mainsizer {
position: absolute;
width: 164px;
}
.dropdown-menus {
display: none;
position: absolute;
width: 164px;
right: 5%;
background-color: black;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 99;
transition: 0.1s ease-in;
margin-top: 5px;
}
.dropdown-menus a {
color: white;
text-align: center;
font-size: 0.9em;
padding: 8% 0px 15% 0%;
padding-right: 10%;
text-decoration: none;
display: block;
list-style-type: none;
white-space: nowrap;
margin-left: 0%;
text-indent: 4%;
font-family: 'Lato';
}
.dropdown-menus a:hover {
background-color: gray;
}
.mainsizer.open .dropdown-menus {
display: block;
}
<script src="https://code.jquery.com/jquery-2.2.4.js"></script>
<div class="outer-bar">
<ul class="down-bar" style="list-style:hidden">
<li>
<label id="down-nav-1" class="down-nav" onclick="Dropdown1()">Knives <b class="caret_down">▼</b></label>
<div class="mainsizer">
<div class="dropdown-menus" id="dropdown-menu-1">
<label class="m9_bayonet" style="width:30px;height:30px;position:absolute;top:5px;left:5px;cursor:pointer;"></label>M9 Bayonet
<label class="karambit" style="width:30px;height:30px;position:absolute;left:5px;top:59px;cursor:pointer;"></label>Karambit
<label class="bayonet" style="width:30px;height:30px;position:absolute;left:5px;top:116px;cursor:pointer;"></label>Bayonet
<label class="butterfly" style="width:30px;height:30px;position:absolute;left:5px;top:170px;cursor:pointer;"></label>Butterfly Knife
<label class="flip" style="width:30px;height:30px;position:absolute;left:5px;top:225px;cursor:pointer;"></label>Flip Knife
<label class="falchion" style="width:30px;height:30px;position:absolute;left:5px;top:280px;cursor:pointer;"></label>Falchion Knife
<label class="gut" style="width:30px;height:30px;position:absolute;left:5px;top:334.5px;cursor:pointer;"></label>Gut Knife
<label class="shadow" style="width:30px;height:30px;position:absolute;left:5px;top:390px;cursor:pointer;"></label>Shadow Daggers
<label class="huntsman" style="width:30px;height:30px;position:absolute;left:5px;top:444px;cursor:pointer;"></label>Huntsman Knife
<label class="bowie" style="width:30px;height:30px;position:absolute;left:5px;top:498.5px;cursor:pointer;"></label>Bowie Knife
</div>
</div>
</li>
<li>
<label id="down-nav-2" class="down-nav">Pistols <b class="caret_down">▼</b></label>
<div class="mainsizer">
<div class="dropdown-menus" id="dropdown-menu-2">
</label>Glock-18
</label>USP-S
</label>P2000
</label>Five-SeveN
</label>Desert Eagle
</label>Tec-9
</label>CZ75-Auto
</label>P250
</label>Dual Berettas
</label>R8 Revolver
</div>
</div>
</li>
<li>
<label id="down-nav-4" class="down-nav">SMGs <b class="caret_down">▼</b></label>
<div class="mainsizer">
<div class="dropdown-menus" id="dropdown-menu-3">
</label>P90
</label>MAC-10
</label>MP7
</label>MP9
</label>PP-Bizon
</label>UMP-45
</div>
</div>
</li>
<li>
<label id="down-nav-5" class="down-nav">Rifles <b class="caret_down">▼</b></label>
<div class="mainsizer">
<div class="dropdown-menus" id="dropdown-menu-4">
</label>AK-47
</label>Galil AR
</label>SG 553
</label>M4A4
</label>M4A1-S
</label>AUG
</label>Famas
</div>
</div>
</li>
<li>
<label id="down-nav-6" class="down-nav">Snipers <b class="caret_down">▼</b></label>
<div class="mainsizer">
<div class="dropdown-menus" id="dropdown-menu-5">
</label>AWP
</label>SSG 08
</label>SCAR-20
</label>G3SG1
</div>
</div>
</li>
<li>
<label id="down-nav-6" class="down-nav">Heavy <b class="caret_down">▼</b></label>
<div class="mainsizer">
<div class="dropdown-menus" id="dropdown-menu-6">
</label>Nova
</label>MAG-7
</label>XM1014
</label>Sawed-Off
</label>Negev
</label>M249
</div>
</div>
</li>
<li>
<label id="down-nav-7" class="down-nav">Others <b class="caret_down">▼</b></label>
<div class="mainsizer">
<div class="dropdown-menus" id="dropdown-menu-7">
</label>Keys
</label>Cases
</label>Passes
</label>Capsules
</label>Pins
</label>Packages
</label>Stickers
</label>Music Kits
</div>
</div>
</li>
</ul>
</div>
Full Screen Output: http://output.jsbin.com/jeropupogu
Here's a minimal, working (although not styled completely) version, using no JS, and very basic HTML and CSS. Should work in any browser.
.nav {
display: block;
background-color: black;
height: 5em;
}
.nav a {
color: white;
}
.nav ul {
list-style-type: none;
}
.nav > ul > li {
display: inline-block;
}
.nav ul li > ul {
display: none;
}
.nav ul li:hover > ul {
display: block;
}
.down-nav {
font-size: 105%;
color: white;
font-family: 'Lato';
cursor: pointer;
transition: all 0.1s ease;
border: solid .2em transparent;
}
.down-nav:hover {
border: solid .2em gray;
background-color: gray;
border-radius: 1em;
font-family: 'Lato';
}
<div class="nav">
<ul>
<li class="down-nav">Knives <span>▼</span>
<ul>
<li>M9 Bayonet</li>
<li>Karambit</li>
<li>Bayonet</li>
<li>Butterfly Knife</li>
<li>Flip Knife</li>
<li>Falchion Knife</li>
<li>Gut Knife</li>
<li>Shadow Daggers</li>
<li>Huntsman Knife</li>
<li>Bowie Knife</li>
</ul>
</li>
<li class="down-nav">Pistols <span>▼</span>
<ul>
<li>Glock-18</li>
<li>USP-S</li>
<li>P2000</li>
<li>Five-SeveN</li>
<li>Desert Eagle</li>
<li>Tec-9</li>
<li>CZ75-Auto</li>
<li>P250</li>
<li>Dual Berettas</li>
<li>R8 Revolver</li>
</ul>
</li>
</ul>
</div>
Related
I have a product page that fetches the products from a database table, I am using foreach to pull each product & each product displays as a card and using classes to filter them.
The problem is that the checkbox filter isnt working properly with multiple values (classes), if I have 2 classes the filter is only detecting the 1st class rather than all the classes(filters).
So for example, if I check the "Blue" checkbox I wont see any results since the JS is searching for a div that has only that one class ("filt-blue") but I need it to display all divs that contain "filt-blue" as well as any other filters that may be added.
See more on JSFIDDLE
function change() {
var checkboxes = document.getElementsByClassName('checkbox');
var chekboxInputs = Array.from(checkboxes).map(a => a.querySelector('input'));
var allAreUnselected = chekboxInputs.every(function(elem) {
return !elem.checked;
});
if (allAreUnselected) {
chekboxInputs.forEach(function(input) {
Array.from(document.querySelectorAll("." + input.getAttribute("rel"))).forEach(function(item) {
item.style.display = 'block';
});
});
} else {
chekboxInputs.forEach(function(input) {
Array.from(document.querySelectorAll("." + input.getAttribute("rel"))).forEach(function(item) {
item.style.display = input.checked ? 'block' : 'none';
});
});
}
}
change();
#media(max-width:768px){
.card{
width:100% !important;
margin:12px 6px !important;
}
}
.card {
text-align:left;
border-radius:4px;
margin:24px;
width:320px;
min-height:340px;
transition: all 0.2s;
border:var(--image-select-border);
}
.cards {
display: flex;
flex-wrap: wrap;
justify-content:center;
}
.card-body {
flex: 1 1 auto;
padding:12px;
}
.card-title {
margin-bottom:16px;
padding:12px;
}
#media(max-width:768px){
.card-img-top{
width:240px !important;
}
}
#media(max-width:768px){
.filtbtn{
width:90% !important;
margin-left:5% !important;
float:none !important;
}
}
.filterDiv {
display: none;
}
.show {
display: block;
}
#media(max-width:768px){
#opts{
margin-left:5% !important;
}
}
.optsel{
border-bottom:2px solid #0d6efd;
border-top: none;
border-left: none;
border-right: none;
}
.sidebar {
height:100%;
width:0;
position:fixed;
z-index:1;
top:76px;
right:0;
background-color:#111;
overflow-x:hidden;
transition: 0.5s;
padding-top: 60px;
}
.sidebar a {
padding:8px 12px;
text-decoration: none;
font-size:16px;
color: #818181;
display: block;
transition: 0.3s;
}
.sidebar .closebtn {
position: absolute;
top: 0;
margin-right:12px;
font-size:18px;
color:#222 !important;
background-color:#fff;
width:100%;
}
#media(max-width:768px){
.closebtn{
top:4px !important;
}
}
.openbtn {
font-size: 20px;
cursor: pointer;
background-color: #111;
color: white;
padding: 10px 15px;
border: none;
}
.openbtn:hover {
background-color: #444;
}
#main {
transition: margin-right .5s; /* If you want a transition effect */
padding: 20px;
}
/* On smaller screens, where height is less than 450px, change the style of the sidenav (less padding and a smaller font size) */
#media screen and (max-height: 450px) {
.sidebar {padding-top: 15px;}
.sidebar a {font-size: 22px;}
}
.optbtn{
background-color:#fff;
color: #222;
cursor: pointer;
padding:14px;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 16px;
transition: 0.4s;
}
.accordion {
background-color:#111;
color: #fff !important;
cursor: pointer;
padding: 18px;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 15px;
transition: 0.4s;
}
.notaccordion {
background-color:#111;
color: #fff !important;
cursor: pointer;
padding: 18px;
width: 100%;
border: none;
text-align: center;
outline: none;
font-size:18px;
transition: 0.4s;
font-weight:bolder;
}
.active, .accordion:hover {
opacity:0.9;
}
.accordion:after {
content: '\002B';
color: #777;
font-weight: bold;
float: right;
margin-left: 5px;
}
.active:after {
content: "\2212";
}
.filterpanel {
padding:0 18px;
background-color:#fff;
max-height: 0;
overflow: hidden;
transition: max-height 0.2s ease-out;
}
.checkbox{
color:#222 !important;
padding:12px 12px;
}
.checkbox-button {
cursor: pointer;
}
.checkbox span{
margin-left:12px;
}
.checkbox input[type=checkbox] {
box-sizing: border-box;
padding: 0;
}
.checkbox input {
font-size: 1rem;
line-height: 1.5;
padding: 11px 23px;
border: 1px solid black;
border-radius: 0;
outline: 0;
background-color: transparent;
}
.checkbox-button__input {
opacity: 0;
position: absolute;
}
.checkbox-button__control {
position: relative;
display: inline-block;
width: 20px;
height: 20px;
vertical-align: middle;
background-color: inherit;
color: #017b5f;
border: 2px solid #666;
}
.checkbox-button__input:checked+.checkbox-button__control:after {
content: "";
display: block;
position: absolute;
top: 2px;
left: 2px;
width: 12px;
height: 12px;
background-color:#0d6efd;
}
.checkbox-button__input:checked+.checkbox-button__control {
border-color:black;
border:2px solid black;
}
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous" id="bootstrap-css">
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.0/css/all.css" integrity="sha384-lZN37f5QGtY3VHgisS14W3ExzMWZxybE1SJSEsQp9S+oqd12jhcu+A56Ebc1zFSJ" crossorigin="anonymous">
<div class="filter">
<div class="checkbox">
<label class="checkbox-button">
<input type="checkbox" class="checkbox-button__input" id="choice1-1" name="choice1" onchange="change()" rel="filt-blue">
<span class="checkbox-button__control"></span>
</label>
<span><b>blue</b></span>
</div>
</div>
<div class="filter">
<div class="checkbox">
<label class="checkbox-button">
<input type="checkbox" class="checkbox-button__input" id="choice1-2" name="choice1" onchange="change()" rel="filt-red">
<span class="checkbox-button__control"></span>
</label>
<span><b>red</b></span>
</div>
</div>
<hr>
<div class="filter">
<div class="checkbox">
<label class="checkbox-button">
<input type="checkbox" class="checkbox-button__input" id="choice1-1a" name="choice2" onchange="change()" rel="filt-long">
<span class="checkbox-button__control"></span>
</label>
<span><b>long</b></span>
</div>
</div>
<div class="filter">
<div class="checkbox">
<label class="checkbox-button">
<input type="checkbox" class="checkbox-button__input" id="choice1-2a" name="choice2" onchange="change()" rel="filt-short">
<span class="checkbox-button__control"></span>
</label>
<span><b>short</b></span>
</div>
</div>
<div class="cards">
<div class="card filt-blue filt-long">
<h6 class="card-title"><b><?php echo $fetch['title'];?></b></h6>
<center><img src="/images/products/<?php echo $fetch['Img'];?>" class="card-img-top" style="width:160px;margin:0 auto;"></center>
<div class="card-body">
<small><?php echo $fetch['detail'];?></small>
<h6><b>Starting from - £<?php echo $fetch['SFprice'];?></b></h6>
</div>
</div>
<div class="card filt-blue filt-short">
<h6 class="card-title"><b><?php echo $fetch['title'];?></b></h6>
<center><img src="/images/products/<?php echo $fetch['Img'];?>" class="card-img-top" style="width:160px;margin:0 auto;"></center>
<div class="card-body">
<small><?php echo $fetch['detail'];?></small>
<h6><b>Starting from - £<?php echo $fetch['SFprice'];?></b></h6>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
This code should help your filter take into account multiple classes. There's a couple of ways you can filter. For example, if you have "long and blue" do you want to show ONLY long and blue? Or would you want to show a "Red Long" as well. This particular code takes into account multiple filter classes but in a logical OR fashion instead of an AND logic.
<body>
<label>Blue</label>
<input type="checkbox" name="filter-blue" onchange="change()">
<label>Red</label>
<input type="checkbox" name="filter-red" onchange="change()">
<label>Long</label>
<input type="checkbox" name="filter-long" onchange="change()">
<label>Short</label>
<input type="checkbox" name="filter-short" onchange="change()">
<div class="cards">
<p class="filter-blue">Blue Jeans</p>
<p class="filter-blue">Blue Cups</p>
<p class="filter-red">Red Shirt</p>
<p class="filter-red">Red Shirt</p>
<p class="filter-red filter-long">Long Red Shirt</p>
<p class="filter-red filter-short">Short Red Shirt</p>
<p class="filter-blue filter-long">Long Blue Shirt</p>
<p class="filter-blue filter-short">Short Blue Shirt</p>
</div>
<script type="text/javascript">
function change() {
// Step 1 - Get checked filters
let checkboxes = document.querySelectorAll('input[type="checkbox"]'),
filtered = [];
checkboxes.forEach(checkbox => {
if (checkbox.checked) {
filtered.push(checkbox.name);
}
});
// Step 2 - Show cards based on the filters
let cards = document.querySelectorAll('.cards p');
cards.forEach(card => {
if (
filtered.length === 0 || // If no filter is checked then show everything
filtered.some(r => card.classList.contains(r)) // If the filter matches the cards class. Filter it.
) {
card.style.display = 'block';
} else {
card.style.display = 'none';
}
});
}
</script>
</body>
Why doesn't the current code work?
Your code iterates over every input, finding all cards matching that input. The first input is blue, and the code iterates over all cards and correctly displays those matching blue. But it then continues to the next iteration, the red checkbox. No cards match and so they are both immediately hidden. So any matching card(s) are actually shown and then immediately hidden.
OK, how do we fix it?
Here's my approach:
Since we need to evaluate all checkboxes for each indvidual card, flip the order of iteration - for each card, iterate over each checkbox (rather than the other way around as you have);
Evaluate each checkbox: if it is checked and the current card has that attribute, flag it as matching, and continue checking the other checkboxes;
But if the checkbox is checked and the current card does not have that attribute, flag it as a non-match. Now we must immeidately bail out, so a future match doesn't override this failure;
After we've checked all inputs (or bailed out on a failure), display or hide the card depending on the final state of our match;
function change() {
var checkboxes = document.getElementsByClassName('checkbox');
var chekboxInputs = Array.from(checkboxes).map(a => a.querySelector('input'));
var allAreUnselected = chekboxInputs.every(function(elem) {
return !elem.checked;
});
if (allAreUnselected) {
chekboxInputs.forEach(function(input) {
Array.from(document.querySelectorAll("." + input.getAttribute("rel"))).forEach(function(item) {
item.style.display = 'block';
});
});
} else {
Array.from(document.querySelectorAll(".card")).forEach(function(card) {
// console.log('Start card: ', card);
let match = false;
for (const input of chekboxInputs) {
let attribute = input.getAttribute("rel");
// console.log('processing input with rel:', attribute);
if (input.checked && card.classList.contains(attribute)) {
// console.log('input is checked and card matches');
match = true;
} else if (input.checked && ! card.classList.contains(attribute)) {
// console.log('input is checked and card does not match');
match = false;
break;
}
}
// console.log('done checking inputs, match is:', match);
card.style.display = match ? 'block' : 'none';
});
}
}
change();
.card {
border: 1px solid black;
}
<div class="checkbox">
<input type="checkbox" name="choice1" onchange="change()" rel="filt-blue">
<span><b>blue</b></span>
</div>
<div class="checkbox">
<input type="checkbox" name="choice1" onchange="change()" rel="filt-red">
<span><b>red</b></span>
</div>
<div class="checkbox">
<input type="checkbox" name="choice2" onchange="change()" rel="filt-long">
<span><b>long</b></span>
</div>
<div class="checkbox">
<input type="checkbox" name="choice2" onchange="change()" rel="filt-short">
<span><b>short</b></span>
</div>
<br><br>
<div class="cards">
<div class="card filt-blue filt-long">
<b>Blue Long</b>
</div>
<div class="card filt-blue filt-short">
<b>Blue Short</b>
</div>
</div>
Notes
It is generally considered good practice to separate your HTML and JS. Here that would mean removing the inline onchange="change()" in the HTML, and replacing them with an event handler in the JS:
let checkbox = document.querySelector("input[name=checkbox]");
checkbox.addEventListener('change', change);
You will make it so much easier for others to help if you can create a minimal, complete, and verifiable example of the problem. In this case a good chunk of the time I spent on this problem was evaulating what I could strip out - a lot of the HTML and most of the CSS classes are irrlevant. The PHP obviously does not work, and none of the CSS matters. Neither Bootstrap nor jQuery CSS or JS refs are necessary.
When you strip out everything not related to the problem it is that much easier to understand, debug, and work with.
i am having the following Problem.
I implemented a nested accordion Menu JQuery script on my Website.
What i need to do now is that the accordion Menu sub categories expand based on an URL.
Here is the fiddle https://jsfiddle.net/w6fa87ov/
You will find that i set the Menu item "Test_2" within "Sub Category 1.2" on active.
When the user enters the corresponding URL i.e. "/myurl/Test_2" then the Accordion Menu should open the item "Sub Category 1.2" by default.
Iam not a JQuery programmer and i dont know how to do this in JQuery.
My noob consideration was something like:
- If i find an active element within the menu i "click" the corresponding element which should expand it
Thanks in advance, best regards Burnie
HTML:
<div id="main">
<div id="nestedAccordion">
<h5 id="id_element_TopKat_Food">First Top Category</h5>
<div id="container2">
<h6 id="id_element_Sub1Kat">Sub Category 1.1</h6>
<div id="container3">
<a class="accordionSubKategorie" href="/myurl/Test_0" style="text-decoration:none;">
<h7 id="id_element_Sub2Kat" class="Sub2Kat"> Test_0</h7>
</a>
</div>
<h6 id="id_element_Sub1Kat">Sub Category 1.2</h6>
<div id="container3">
<a class="accordionSubKategorie" href="/myurl/Test_1" style="text-decoration:none;">
<h7 id="id_element_Sub2Kat" class="Sub2Kat"> Test_1</h7>
</a>
<a class="accordionSubKategorie" href="/myurl/Test_2" style="text-decoration:none;">
<h7 id="id_element_Sub2Kat" class="Sub2Kat active"> Test_2</h7>
</a>
<a class="accordionSubKategorie" href="/myurl/Test_3" style="text-decoration:none;">
<h7 id="id_element_Sub2Kat" class="Sub2Kat"> Test_4</h7>
</a>
</div>
</div>
<h5 id="id_element_TopKat_Non-Food">Second Top Category</h5>
<div id="container2">
<h6 id="id_element_Sub1Kat">Sub Category 2.1</h6>
<div id="container3">
<a class="accordionSubKategorie" href="/myurl/Test_4" style="text-decoration:none;">
<h7 id="id_element_Sub2Kat" class="Sub2Kat"> Test_4</h7>
</a>
</div>
</div>
</div>
JQuery:
var parentDivs = $('#nestedAccordion div');
var childDivs = $('#nestedAccordion h6').siblings('div');
$('#nestedAccordion h5').click(function() {
parentDivs.slideUp();
if ($(this).next().is(':hidden')) {
$(this).next().slideDown();
} else {
$(this).next().slideUp();
}
});
childDivs.slideUp();
$('#nestedAccordion h6').click(function() {
childDivs.slideUp();
if ($(this).next().is(':hidden')) {
$(this).next().slideDown();
} else {
$(this).next().slideUp();
}
});
CSS:
h5 {
margin-bottom: 8px;
font-weight:bold;
font-size: 20px;
width: 100%;
display: block;
background: #6EB90A;
color: #fefefe;
padding: .75em;
border-radius: 0.15em;
cursor: pointer;
cursor: hand;
}
h5:hover {
background: #5c8a1c;
text-decoration: none;
color: white;
}
h6 {
margin-top:-3px;
font-size: 15px;
width: 100%;
display: block;
background: #FFFFFF;
border-color: #476767;
box-shadow: 0px 0px 0px 2px rgba(71,103,103,1);
color: #476767;
padding: .25em;
border-radius: 0.8em;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;
cursor: hand;
}
h6:hover {
background: #476767;
text-decoration: none;
color: white;
}
h7.Sub2Kat {
font-size: 15px;
width: 100%;
display: block;
color: #476767;
padding-top: 2px;
padding-bottom: .1em;
padding-left: 1.8em;
border-radius: 0.2em;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;
cursor: hand;
}
h7.Sub2Kat:hover {
background: #6EB90A;
color: white;
}
h7.Sub2Kat.active {
border: 2px solid #ddd;
border-radius: 0.8em;
padding-left: 1.2em;
}
.Sub2Katactive {
font-size:0.9em;
}
h7.Sub2Kat.active {
border: 2px solid #ddd;
border-radius: 0.8em;
padding-left: 1.2em;
}
If this is intended as a menu you could build everything in an unordered list with list elements inside. Any nested menus can be done as such:
<ul class="menu">
<li class="menuItem">
Page 1
<ul class="subMenu">
<li class="menuItem2">Sub Page 1</li>
<li class="menuItem2">Sub Page 2</li>
<li class="menuItem2">Sub Page 3</li>
</ul>
</li>
<li class="menuItem">Page 2</li>
<li class="menuItem">Page 3</li>
</ul>
DOing it this way will allow you to target the :active list item that is built in with browsers. Then you could use css and jquery to make the dropdown magic. In jquery you can target the :active anchor as such:
$('menu > li a:active').doSomething();
If this doesn't interest you, check out this other stack overflow answer that may be of help as well.
Jquery - determining which link was clicked
Cheers
Ok i solved my Prolbem.
Within my Django code which i use to construct the Template i give a specific Class to hasActive or hasPassive which refers to the menu items which has the active or only passive elements inside (elemnts that are not clicked based on the url).
In case someone has the same Problem ill provide more informationen on this.
Just learing JS. I have created 3 dropdown menu with different choices and added JS function. HTML and CSS seems to be right. I am concerned about my JS, because it just does not work. Is the even right for that purpose? I am not sure if I use ".this" in the right place.
I would really appreciate any hints!
my JAVASCRIPT:
document.addEventListener("DOMContentLoaded", function() {
console.log("DOM fully loaded and parsed");
var arrow= document.querySelectorAll(".list_arrow");
var panel= document.querySelectorAll(".list_panel");
for (var i= 0; i < arrow.length; i++) {
arrow[i].addEventListener('click', function showDiv(event) {
if (panel.this.style.display == 'none') { //panel(this.style.display=='none')?
panel.this.style.display = 'block';
}
else {
panel.this.style.display = 'none';
}
});
}
});
HERE IS MY CSS
.form {
margin-top:50px;
}
.drop_down_list {
border:1px solid #8de0d2;
width: 280px;
height:38px;
background-color: white;
margin-top:22px;
padding: 8px 12px;
position: relative;
}
.list_label {
font-size: 30px;
color: #e2e2e2;
font-family: 'ralewaylight', Arial, Tahoma, sans-serif;
}
.list_arrow {
width: 0;
height: 0;
border-left: 15px solid transparent;
border-right: 15px solid transparent;
border-top: 15px solid #24baa0;
display:block;
position: absolute;
right: 14px;
top: 20px;
cursor: pointer;
}
.list_panel {
background-color: white;
border: 1px solid #ccc;
width: 288px;
padding-left: 15px;
padding-bottom: 10px;
list-style: none;
margin: 0;
left: 0px;
z-index: 2;
position: absolute;
top: 54px;
display:none;
}
.list_panel li {
margin-top:10px;
cursor: pointer;
color: #585858;
}
<div class="form">
<div class="drop_down_list">
<span class="list_label">Choose a chair</span>
<span class="list_arrow"></span>
<ul class="list_panel">
<li>Clair</li>
<li>Margarita</li>
<li>Selena</li>
</ul>
</div>
</div>
<div class="drop_down_list">
<span class="list_label">Choose color</span>
<span class="list_arrow"></span>
<ul class="list_panel">
<li>red</li>
<li>black</li>
<li>orange</li>
</ul>
</div>
<div class="drop_down_list">
<span class="list_label">Choose material</span>
<span class="list_arrow"></span>
<ul class="list_panel">
<li>a</li>
<li>b</li>
</ul>
</div>
</div>
The this keyword refers to the current scope. It is of no use in the state you are using it. What I suggest is to locate the list_panel that is associated with the clicked arrow. There are numerous ways to do that, but you can use the following:
Note that I also added default display-none classes so that they get shown on the first click (this wasn't the case).
document.addEventListener("DOMContentLoaded", function() {
console.log("DOM fully loaded and parsed");
var arrow= document.querySelectorAll(".list_arrow");
for (var i= 0; i < arrow.length; i++) {
arrow[i].addEventListener('click', function showDiv(event) {
// Find the panel associated with the clicked arrow.
var parent = event.target.parentNode,
currentPanel = parent.children[2];
if (currentPanel.style.display == 'none') {
currentPanel.style.display = 'block';
}
else {
currentPanel.style.display = 'none';
}
});
}
});</script>
.form {
margin-top:50px;
}
.drop_down_list {
border:1px solid #8de0d2;
width: 280px;
height:38px;
background-color: white;
margin-top:22px;
padding: 8px 12px;
position: relative;
}
.list_label {
font-size: 30px;
color: #e2e2e2;
font-family: 'ralewaylight', Arial, Tahoma, sans-serif;
}
.list_arrow {
width: 0;
height: 0;
border-left: 15px solid transparent;
border-right: 15px solid transparent;
border-top: 15px solid #24baa0;
display:block;
position: absolute;
right: 14px;
top: 20px;
cursor: pointer;
}
.list_panel {
background-color: white;
border: 1px solid #ccc;
width: 288px;
padding-left: 15px;
padding-bottom: 10px;
list-style: none;
margin: 0;
left: 0px;
z-index: 2;
position: absolute;
top: 54px;
display:none;
}
.list_panel li {
margin-top:10px;
cursor: pointer;
color: #585858;
}
<div class="form">
<div class="drop_down_list">
<span class="list_label">Choose a chair</span>
<span class="list_arrow"></span>
<ul class="list_panel" style='display: none'>
<li>Clair</li>
<li>Margarita</li>
<li>Selena</li>
</ul>
</div>
</div>
<div class="drop_down_list">
<span class="list_label">Choose color</span>
<span class="list_arrow"></span>
<ul class="list_panel" style='display: none'>
<li>red</li>
<li>black</li>
<li>orange</li>
</ul>
</div>
<div class="drop_down_list">
<span class="list_label">Choose material</span>
<span class="list_arrow"></span>
<ul class="list_panel" style='display: none'>
<li>a</li>
<li>b</li>
</ul>
</div>
</div>
You may want to try using jQuery to help you with show() and hide() functions, though it is said that you might not need it :P
I don't understand the declaration of panel.. Thus I cannot go along panel.this. My solution is attached here:
http://codepen.io/anon/pen/akXqwA
The main problem is that, during the for-loop, the i will end at the value 3. When the event is triggered, i will always be 3, and I cannot access the panel with panel[i].
So I made a "hack" around it, by getting its nextElementSibling and changing its style.display. Also, I have initialised the value with the HTML (style="display: none;"). If you removed that part, the first click on the arrow will not show the items as the style.display value is not "none". There is a way to avoid changing that in HTML: try playing with the line ;)
Check this:
document.addEventListener("DOMContentLoaded", function() {
console.log("DOM fully loaded and parsed");
var arrow = document.querySelectorAll(".list_arrow");
var panel = document.querySelectorAll(".list_panel");
for (var i = 0; i < arrow.length; i++) {
arrow[i].addEventListener('click', function showDiv(event) {
var ulelm = this.parentNode.querySelector("ul");
if (ulelm.style.display == 'none') {
ulelm.style.display = 'block';
} else {
ulelm.style.display = 'none';
}
});
}
});
working example is here.
I wrote some code with three things in mind:
Highlighting a selection's border using 'on click'.
Selecting one item will remove the highlight from the other item.
The ability to deselect each item on click.
I've managed to get everything working for the most part, but I don't particularly like how complex the code is for the radial dot that appears when one item is selected.
Below is an example of what I'm talking about, particularly I'm looking for ways to refactor the code below into something a little more legible (shorter).
$(this).children('.radial').children().toggleClass('checked').parents('.itembox')
.siblings().children('.radial').children().removeClass('checked');
Here's a working example for more context (line 10):
var raceInternet = false;
var racePhone = false;
var raceTv = false;
$(function() {
var $targetDiv = $('#race-internet > .itembox');
var $radialDot = $('.radial > .center-dot');
$targetDiv.on('click', function() {
$(this).toggleClass('user-selected').siblings().removeClass('user-selected');
//Is it possible to refactor Line 10?
$(this).children('.radial').children().toggleClass('checked').parents('.itembox').siblings().children('.radial').children().removeClass('checked');
if ($targetDiv.is('.user-selected')) {
raceInternet = true;
} else {
raceInternet = false;
}
})
})
.itembox-container {
display: flex;
}
.boxes-2 {
width: calc((100% - 25px)/2);
margin: 10px;
padding: 10px;
}
.itembox {
position: relative;
display: inline-block;
border: 5px solid #e8e8e8;
border-radius: 10px;
cursor: pointer;
}
.user-selected {
border: 5px solid #E16E5B;
}
.itembox h4 {
color: #22ddc0;
font-weight: 700;
}
span.price {
display: inline-block;
font-weight: 400;
float: right;
color: #22ddc0;
}
.itembox > ul {
list-style: none;
}
.itembox > ul > li {
line-height: 3;
}
.radial {
position: absolute;
float: right;
height: 35px;
width: 35px;
padding: 2px;
border: 5px solid #e8e8e8;
border-radius: 50%;
top: 43%;
right: 10px;
}
.center-dot {
display: none;
position: relative;
height: 21px;
width: 21px;
background-color: #E16E5B;
border-radius: 50%;
}
.checked {
display: block;
}
.prime-aux:first-of-type {
top: 150px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section class="container">
<!-- Primary Content Container -->
<div class="prime-aux">
<div id="race-internet" class="itembox-container">
<div class="itembox boxes-2">
<h4>Gigabit Internet <span class="price">$60/mo</span></h4>
<ul>
<li>1,000 Mbps</li>
<li>No data caps</li>
</ul>
<div class="radial">
<div class="center-dot"></div>
</div>
</div>
<div class="itembox boxes-2">
<h4>Basic Internet <span class="price">$25/mo</span></h4>
<ul>
<li>25 Mbps</li>
<li>No data caps</li>
</ul>
<div class="radial">
<div class="center-dot"></div>
</div>
</div>
</div>
</div>
</section>
<!-- Primary Content Container End -->
View on JS Fiddle
You can eliminate a lot of your jQuery by just leveraging CSS. Typically, if I want to toggle a feature, I have it either display: block; or display: none; based upon a CSS selector. Then, I just use jQuery to toggle the parent element's class name. So for example:
.item.selected .checkmark {
display: block;
}
.item .checkmark {
display: none;
}
$('.item').click(function(){ $(this).toggleClass('selected') });
JSFiddle
I want to create a dynamic popup menu that adds a class to "lists" parent when radio button is selected // removes classes when unselected (HTML structure below cannot change...only can change CSS and JS)
I'm trying to make the "List Popup" popup directly under each "Green Gear" icon when clicked on/ hidden when clicked off (css classes).
Whenever a radio button is selected from the popup, I need it to add a class directly into the corresponding list's parent div, while not effecting any of the other lists.
The trick (for me) is making all of this happen dynamically with 1 popup menu that sits outside of each lists divs scope.
The HTML structure cannot be changed (this has to be completely done w/ JS and CSS).
Here's what I've got so far: https://jsfiddle.net/oneeezy/t5eou67k/
$(document).ready(function () {
/* Code to play with for show/hide popup
/* Code to play with for adding/removing classes when radio checked
$('.list-layouts input').click(function () {
$('.list-layouts input:not(:checked)').parent().removeClass("blue");
$('.list-layouts input:checked').parent().addClass("blue");
});
*/
});
/* Reset Styles */
* { box-sizing: border-box; margin: 0; padding: 0; }
h1 { padding: 0 0 .25rem; }
h2 { line-height: 1.6; }
aside { background: #e2e4e6; padding: 1rem; color: gray; margin: 1rem; border: 1px dashed gray; }
aside ul { list-style: inside; }
aside span { font-style: italic; color: rgba(0, 0, 0, .78); }
.page { background: rgba(137, 96, 158, .17); margin: 2rem 0; padding: 2rem; position: relative; }
.page::before { content: "<div> ...Scope"; color: rgba(0, 0, 0, .54); position: absolute; top: 0; left: 0; }
.page::after { content: "</div>"; color: rgba(0, 0, 0, .54); position: absolute; bottom: 0; left: 0; }
.purple { background: rgba(137, 96, 158, .17); }
.green { background: lime; }
.yellow { background: yellow; }
/* Trello (default tyles) */
.wrapper { display: flex; }
.list-wrapper { flex: 1; margin: 10px; }
.list { background: #e2e4e6; position: relative; padding: 0 10px 3px; }
.icon { display: block; position: absolute; top: 0; right: 0; width: 33px; height: 38px; background: lime; text-decoration: none; font-size: 2em; line-height: 1.3; color: darkgreen; }
.card { display: block; height: 50px; background: white; border-radius: 3px; border: 1px solid #ccc; padding: .5em; margin: 0 0 .5em; }
/* List Popup */
.list-popup { display: block; width: 350px; max-height: 800px; background: yellow; border-radius: 3px; border: 1px solid #ccc; padding: 1em; }
.list-popup p { padding: 0 0 1rem; }
.list-popup p span::after { content: "<div class='list-wrapper'>"; }
.list-layouts ul { list-style: none; }
.list-normal { }
.list-normal .list { }
.list-color { }
.list-color .list * { background: skyblue; }
.list-bold { }
.list-bold .list * { font-weight: bold; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!-- Description -->
<aside>
<h1>Dynamic popup menu that adds class to lists parent when radio button is selected // removes classes when unselected</h1>
<ul>
<li>I'm trying to make the <span class="yellow">"List Popup"</span> popup directly under each <span class="green">"Green Gear"</span> icon when clicked on/ hidden when clicked off (css classes).</li>
<li>Whenever a radio button is selected from the popup, I need it to add a class directly into the corresponding list's parent div, while not effecting any of the other lists.</li>
<li>The trick (for me) is making all of this happen dynamically with 1 popup menu that sits outside of each <span class="purple">lists divs scope</span>.</li>
</ul>
</aside>
<!-- Lists -->
<div class="page">
<div class="wrapper">
<div class="list-wrapper">
<div class="list">
<h2>List (1)</h2>
⚙
<div class="card">Woohoo! Woohoo! Woohoo! </div>
<div class="card">Woohoo! Woohoo! Woohoo! </div>
<div class="card">Woohoo! Woohoo! Woohoo! </div>
</div>
</div>
<div class="list-wrapper">
<div class="list">
<h2>List (2)</h2>
⚙
<div class="card">Woohoo! Woohoo! Woohoo! </div>
<div class="card">Woohoo! Woohoo! Woohoo! </div>
<div class="card">Woohoo! Woohoo! Woohoo! </div>
</div>
</div>
<div class="list-wrapper">
<div class="list">
<h2>List (3)</h2>
⚙
<div class="card">Woohoo! Woohoo! Woohoo! </div>
<div class="card">Woohoo! Woohoo! Woohoo! </div>
<div class="card">Woohoo! Woohoo! Woohoo! </div>
</div>
</div>
</div>
</div>
<!-- List Layout popup -->
<div class="page">
<div id="listPopup" class="list-popup">
<div>
<h3>List Popup</h3>
<p>These radio buttons should add a special class to the individual <span></span> when clicked on and removed when clicked off!</p>
</div>
<form class="list-layouts">
<ul>
<li>
<input type="radio" name="listLayout" id="listNormal">
<label for="listNormal">Normal</label>
</li>
<li>
<input type="radio" name="listLayout" id="listColor">
<label for="listColor">Bold</label>
</li>
<li>
<input type="radio" name="listLayout" id="listBold">
<label for="listBold">Italic</label>
</li>
</ul>
</form>
</div>
</div>
So first we will add the class to the list wrapper of the lists parent with the following code:
$('.icon').on('click', function () {
if(!$('#listPopup').hasClass("open")){
$('#listPopup').toggleClass("open");
}
$(this).parents('.list-wrapper').siblings().removeClass('blue');
if(!$(this).parents('.list-wrapper').hasClass("blue")){
$(this).parents('.list-wrapper').toggleClass("blue");
}
});
Then we need to create an onchange function for the radios to find this class and add the new class to that list.
$('.list-layouts input').on('change', function () {
var newClass = $(this).attr("id");
// Remove the following statement if you want your classes to stack up
$('.blue').removeClass("listNormal listColor listBold");
$('.blue').addClass(newClass);
});
Here is a working fiddle of the whole thing in action Fiddle