Javascript FAQ drop down - javascript

Im trying to create a simple FAQ drop down but for some reason it is not working. Would you mind taking a look?
Thanks guys!
CSS
#faqs h3 { cursor:pointer; }
#faqs h3.active { color:#d74646; }
#faqs div { height:0; overflow:hidden; position:relative; }
#faqs div p { padding:0; margin-bottom:15px; }
JavaScript:
$(document).ready(function() {
$('#faqs h3').each(function() {
var tis = $(this),
state = false,
answer = tis.next('div')
.hide()
.css('height','auto')
.slideUp();
tis.click(function() {
state = !state;
answer.slideToggle(state);
tis.toggleClass('active',state);
});
});
});
HTML
<div id="faqs">
<h3>This is question 1?</h3>
<div>
<p>This is the answer to question #1.</p>
</div>
<h3>This is question 2?</h3>
<div>
<p>This is the answer to question #2.</p>
</div>
</div>

The functions below can be used for what you need:
HTML
<div id="QA">
<ul>
<li>Question 1</li>
<li>Answer 1</li>
<li>Question 2</li>
<li>Answer 2</li>
</ul>
<ul>
<li>Question 3</li>
<li>Answer 3</li>
<li>Question 4</li>
<li>Answer 4</li>
</ul>
</div>
JavaScript:
<script type="text/javascript">
function toggle(tag) {
var x=document.getElementsByName(tag)[0];
var a = x.parentNode
if (a.style.display=='block'){
a.style.display='none'
}else{
a.style.display='block'
}
}
function init() {
//this function will add show hide functionality to paired list items,
//as long as the answer is a list item straight after the question list item.
//You can also have as many separate lists as you want.
//all lists must be contained within a div with id QA
var obj = document.getElementById('QA');
var elements = obj.getElementsByTagName('li');
var index = 1
//add javascript to question elements
//you could also add styling to question elements here
for (var i=0; i < elements.length; i+=2){
var element = elements[i];
element.innerHTML = "<a href='javascript:toggle(" + index + ")'>" + element.innerHTML + "</a>"
index = index + 1
}
//add bookmark to answer elements and add styling
var index = 1
for (var i=1; i < elements.length; i+=2){
var element = elements[i];
element.innerHTML = "<a name='" + index + "' id='" + index + "'></a>" + element.innerHTML
index = index + 1
element.style.padding = '0px 0px 10px 20px' //add indent to answer
element.style.listStyleType = 'none' //remove bullet
element.style.display = 'none' //hide answer element
}
}
window.onload = init;
</script>

Related

jQuery - show list items that match data-attribute

The function for finding the active menu option and showing the li with the matching data-type only works if the active class is on the first or second li. If I put the active class on the third or fourth li, no items show for either table.
How do I show all of the items with the data-type that matches the data-related value with the active class?
Note the click function works fine, the issue is showing the li for the active selection on load.
$(document).ready(function() {
//Retreive active list on load:::::::::::::::::::::::::::::::
$("[data-section='table']").each(function() {
var $container = $(this);
var item = $container.find(".billing__item");
var active = $container.find(".ladder__ul li.active");
var $id = item.attr("data-type");
var datarel = active.attr("data-related");
item.hide();
// Find out what Li has the active class, then return matching items on load:
if (datarel == "all") {
item.css("display", "flex");
}
if (datarel == $id) {
$(".billing__item[data-type='" + $id + "']").css("display", "flex");
}
});
//Retreive active list on Click:::::::::::::::::::::::::::::::
$(".ladder__ul li").on("click", function(e) {
e.preventDefault();
var $li = $(this);
var $table = $li.closest("[data-section='table']");
var $id = $li.attr("data-related");
$(".ladder__ul li.active").removeClass("active");
$(this).addClass("active");
$table.find(".billing__item").each(function() {
var $item = $(this);
var $type = $item.attr("data-type");
//Hide every list on load::
$item.hide();
if ($li.attr("data-related") == "all") {
$item.css("display", "flex");
}
if ($li.attr("data-related") == $type) {
$item.css("display", "flex");
}
});
});
});
li {
padding: .9rem;
border: 1px solid;
cursor: pointer
}
.active {
background: blue
}
[data-section='table'] {
border: 2px solid;
pading: 1.3rem
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div data-section="table">
<ul class="ladder__ul">
<li data-related="all">All</li>
<li data-related="one" class="active">One</li>
<li data-related="two">two</li>
<li data-related="three">three</li>
</ul>
<div class="billing__list">
<div class="billing__item" data-type="one">One</div>
<div class="billing__item" data-type="two">Two</div>
<div class="billing__item" data-type="one">One</div>
<div class="billing__item" data-type="three">Three</div>
</div>
</div>
<div data-section="table">
<ul class="ladder__ul">
<li data-related="all">All</li>
<li data-related="a">a</li>
<li data-related="b" class="active">b</li>
</ul>
<div class="billing__list">
<div class="billing__item" data-type="a">a</div>
<div class="billing__item" data-type="b">b</div>
<div class="billing__item" data-type="a">a</div>
<div class="billing__item" data-type="b">b</div>
</div>
</div>
I think the problem is because $id is not equal to datarel
The .attr() method gets the attribute value for only the first element in the matched set.
ref: https://api.jquery.com/attr/
So your $id variable is always one because that's how you layout your elements in the html and One is the value of the first element with attribute data-type
<div class="billing__item" data-type="one">One</div>
<div class="billing__item" data-type="two">Two</div>
<div class="billing__item" data-type="one">One</div>
<div class="billing__item" data-type="three">Three</div>
I believe you are complicating things with the $id, you don't need it
$("[data-section='table']").each(function () {
var $container = $(this);
var item = $container.find(".billing__item");
var active = $container.find(".ladder__ul li.active");
var datarel = active.attr("data-related");
item.hide();
// Find out what Li has the active class, then return matching items on load:
if (datarel == "all") {
item.css("display", "flex");
}
$(".billing__item[data-type='" + datarel + "']").css("display", "flex");
});
Using datarel directly should suffice.
The problem from id. Because you get $id from item.attr("data-type") but the item is multiple element so it alway to get the first item data. That why at first section it work, but the second section work incorrect.
So you need to change your code like this:
//Retreive active list on load:::::::::::::::::::::::::::::::
$("[data-section='table']").each(function() {
var $container = $(this);
var item = $container.find(".billing__item");
var active = $container.find(".ladder__ul li.active");
var datarel = active.attr("data-related");
item.hide();
// Find out what Li has the active class, then return matching items on load:
if (datarel == "all") {
item.css("display", "flex");
} else {
$(".billing__item[data-type='" + datarel + "']", $container).css("display", "flex");
}
});

I'm trying to get the parent of the parent (div) of the li-element which I click

I'm trying to get the parent of the parent (div) of the li-element which I click and change/remove it's class but I don't know how to tell JS that it should get the specific li class that I click. Sorry for this simple question I'm fairly new to JS.
<!DOCTYPE html>
<html>
<body>
<p>List:</p>
<div class="div">
<ul>
<li class="lis">Coffee</li>
<li class="lis">Tea</li>
<li class="lis">Water</li>
</ul>
</div>
<script>
let li = document.getElementsByClassName("lis")
li.click() = function() {
var x = li.parentElement.parentElement.classList.value
if(x.classList.contains("div")) {
x.remove.classList("div")
}
}
</script>
</body>
</html>
<script>
var elements = document.getElementsByClassName("lis");
var myFunction = function(e) {
x = e.target.innerHTML;
e.target.parentElement.parentElement.innerHTML=x
};
for (var i = 0; i < elements.length; i++) {
elements[i].addEventListener('click', myFunction, false);
}
</script>
You can use .closest() to select the closest div and after that, you can remove the class from that element.
Try below working code -
var elements = document.getElementsByClassName("lis");
var myFunction = function() {
var x = this.closest('.div')
if (x) {
this.closest('.div').classList.remove("div")
console.log('Div Element Class Removed!');
}
};
for (var i = 0; i < elements.length; i++) {
elements[i].addEventListener('click', myFunction, false);
}
<!DOCTYPE html>
<html>
<body>
<p>List:</p>
<div class="div">
<ul>
<li class="lis">Coffee</li>
<li class="lis">Tea</li>
<li class="lis">Water</li>
</ul>
</div>
</body>
</html>
I recommend to delegate to the nearest static container
Also you had remove.classList - that should be classList.remove
Here I give an ID to the UL and test if the LI clicked has the lis class
document.getElementById("ul").addEventListener("click",function(e) {
const tgt = e.target.closest("li")
if (tgt.classList.contains("lis")) {
tgt.closest("div").classList.remove("div")
}
})
.div { background-color:red }
<p>List:</p>
<div class="div">
<ul id="ul">
<li class="lis">Coffee</li>
<li class="lis">Tea</li>
<li class="lis">Water</li>
</ul>
</div>
I came up with another good solution that let's me add and remove the div class, the parent of specified li elements. I added an extra div to make sure it always gets the certain parent that I've specified with .closest(body > div) and used a for loop to make the array that it creates select one certain li element, the one I currently click on.
<!DOCTYPE html>
<html>
<body>
<p>List:</p>
<div class="div">
<ul>
<li class="lis">Coffee</li>
<li class="lis">Tea</li>
<li class="lis">Water</li>
</ul>
</div>
<div>
<ul>
<li class="l">Coffee</li>
<li class="l">Tea</li>
<li class="l">Water</li>
</ul>
</div>
<script>
let lists = document.getElementsByClassName("lis");
let divs = document.getElementsByTagName("div");
for (let list of lists) {
list.addEventListener("click", () => {
list.closest("body > div")
for (let div of divs) {
div.addEventListener("click", () => {
if (div.classList.contains("div")) {
div.classList.remove("div")
} else {
div.classList.add("div")
}
})
}
})
}
</script>
<style>
.div {
color: brown;
font-size: larger;
}
</style>
</body>
</html>
var li = document.getElementsByClassName("lis");
for (var i=0; i<li.length; i++) {
li[i].onclick = function() {
var el = this.parentElement.parentElement;
if (el.className == "div") {
el.className = "";
}
}
}

JavaScript: Node List to Array Conversion

I am trying to convert Node List to Array. I want to print the list (caption 1,...caption 5), but it prints:
[object HTMLLIElement],[object HTMLLIElement],[object HTMLLIElement],[object HTMLLIElement],[object HTMLLIElement],[object HTMLLIElement]
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Caption</title>
<style>
.captn {
position: absolute;
padding: 10px;
margin: 200px auto;
text-align: center;
font-family:serif, fantasy;
font-size:36px;
color: #009933;
text-decoration: none;
text-transform: uppercase;
list-style-type: none;
}
</style>
</head>
<body>
<div>
<ul id = "caption" class="captn"><li id = "caption0">caption 0</li><li id = "caption1">caption 1</li><li id = "caption2">caption 2</li><li id = "caption3">caption 3</li><li id = "caption4">caption 4</li><li id = "caption5">caption 5</li></ul>
</div>
<script>
var msg;
var cap = [];
var capList;
var f = document.getElementsByClassName("captn");
msg = f.item(0).childNodes;
b = f.item(0).childNodes.length;
var classAr = Array.prototype.slice.call(msg);
document.write(classAr);
</script>
</body>
</html>
Use querySelectorAll and select all elements under class captn with an id beginning with caption, then convert the node list to array usingArray.from and lastly map through the array, returning a new array containing only the textContent
var captions = Array.from(
document.querySelectorAll('.captn [id^="caption"]')
);
var captionsText = captions.map(function(caption) {
return caption.textContent;
});
document.write(captionsText);
<div>
<ul id="caption" class="captn">
<li id="caption0">caption 0</li>
<li id="caption1">caption 1</li>
<li id="caption2">caption 2</li>
<li id="caption3">caption 3</li>
<li id="caption4">caption 4</li>
<li id="caption5">caption 5</li>
</ul>
</div>
You can use Array.from().
Array.from(nodeList)
First of all I want to thank #Red Mercury that his answer greatly boosted my knowledge in JavaScript. I am new to Stack Overflow and don't know how to mark his answer green, if there is a gold mark, I would do for him. Thank you Red Mercury.
What I was trying to do is to put captions list as a sliding captions. It was easy to do with arrays in Javascript, but I was unable to convert node list into array, because nodelist did not work in loops with innerHtml. So here is the solution:
<html>
<head>
<title>Slide 3</title>
<style>
.captn {
padding-top: 550px;
text-align:center;
font-family:serif, fantasy;
font-size:36px;
color: #009933;
text-decoration: none;
text-transform: uppercase;
}
</style>
</head>
<body>
<div>
<ul id = "caption" class="captn">
<li id = "caption0">caption 0</li>
<li id = "caption1">caption 1</li>
<li id = "caption2">caption 2</li>
<li id = "caption3">caption 3</li>
<li id = "caption4">caption 4</li>
<li id = "caption5">caption 5</li>
<li id = "atim">item</li>
<li id = "caption6">caption 6</li>
</ul>
</div>
<script>
var i = 0;
var j = 0;
var intv = 1000;
var msg = "";
var cap = [];
var capList = "";
var captions = Array.from(
document.querySelectorAll('.captn [id ^= "caption"]')
);
var captionsText = captions.map(function(caption) {
return caption.textContent;
})
for (i = 0; i < captions.length; i++) {
cap[i] = captionsText[i] + "<br>";
msg = cap[i];
capList += msg.replace(/\,/g, "");
}
b = captions.length;
function swapImage() {
var elm = document.getElementById("caption");
elm.innerHTML = cap[j];
if(j < b - 1 ) {
j++;
}
else {
j = 0;
}
setTimeout("swapImage()", intv);
}
window.onload=swapImage;
</script>
</body>
</html>

JS Dropdown Menu Behavior

I have a dropdown menu working but I can't figure out how to close the previous menu onclick. All the menues stay open so I need them to close when a different menu is open.
Please see my jsfiddle
https://jsfiddle.net/yvhnphp4/
$(document).ready(function(){
// Dropdown menu
var findDropdowns = document.querySelectorAll(".has-dropdown");
for(var i = 0; i < findDropdowns.length; i++) {
if(i == 0) {
var dropdownId = "has-dropdown-1";
findDropdowns[i].setAttribute("id", dropdownId);
}else {
var addOneToIndex = i + 1;
dropdownId = "has-dropdown-" + addOneToIndex;
findDropdowns[i].setAttribute("id", dropdownId);
}
var targetDropdown = document.getElementById(dropdownId);
targetDropdown.onclick = dropdownTrigger;
}
function dropdownTrigger(e) {
e.preventDefault();
var showHideDropdown = e.target.nextElementSibling;
showHideDropdown.setAttribute("class", "show");
}
});
<ul class="nav">
<li><a class="has-dropdown" href="">Link</a>
<ul>
<li>Sub-Link</li>
<li>Sub-Link</li>
<li>Sub-Link</li>
</ul>
</li>
<li><a class="has-dropdown" href="">Link</a>
<ul>
<li>Sub-Link</li>
<li>Sub-Link</li>
<li>Sub-Link</li>
</ul>
</li>
</ul>
.nav ul {display:none;}
.nav ul.show{display:block;}
You can simply remove the .show class from all ul tags in .nav that still have it, before opening a new dropdown:
function dropdownTrigger(e) {
var opened = document.querySelectorAll(".nav ul.show");
for(var i = 0; i < opened.length; i++) {
opened[i].removeAttribute("class");
}
...
}
Note that since you're using jQuery anyway ($(document).ready) there is probably a much better way to do this.
Also, use href="#" instead of href="".

Javascript to hide every element in div if it's height is 0

I'm looking for a way to hide every element (set to display:none) in a div (marked with a class or an id) if the height of the element is 0.
Example. Hide elem1 and elem4
<div class="container">
<ul id="elem1" style="height:0"> ... </ul>
<ul id="elem2" style="height:50"> ... </ul>
<ul id="elem3" style="height:40"> ... </ul>
<ul id="elem4" style="height:0"> ... </ul>
<ul id="elem5" style="height:60"> ... </ul>
</div>
Please help
Try this one for size:
.container ul[style="height:0"] {
display: none;
}
Demo: http://jsfiddle.net/qwertynl/yBJEe/
The following will work no matter if there is a space after the : or not:
.container ul[style~="0"], .container ul[style="height:0"] {
display: none;
}
Demo: http://jsfiddle.net/qwertynl/yBJEe/3/
Pure JS sample. Mark your elements with a class. This will work:
<div class="container">
<ul class='yourClass' id="elem1" style="height:0">.1..</ul>
<ul class='yourClass' id="elem2" style="height:50">.2..</ul>
<ul class='yourClass' id="elem3" style="height:40">.3..</ul>
<ul class='yourClass' id="elem4" style="height:0">.4..</ul>
<ul class='yourClass' id="elem5" style="height:60">.5..</ul>
</div>
JS script:
(function hideElements() {
var the_elements = document.getElementsByClassName('yourClass');
for (var i = 0; i < the_elements.length; ++i) {
var item = the_elements[i];
if (item.style.height === '0px') {
item.style.display = 'none';
}
}
})();
Note that the value retrieved is '0px'.
JSFiddle working sample: http://jsfiddle.net/amontellano/EgHLt/5/
This CSS block would help
.container ul {
overflow: hidden;
}
You can do it like this, using querySelectorAll()
var x = document.querySelectorAll("ul[id*='elem']"); //select all elements with an ID containing 'elem'
for (var i=0; i<x.length; i++) { //loop the element array and check if height == 0
if (x[i].style.height == 0) x[i].style.display = "none"; //set display to none
}

Categories