Collapse an accordion menu - javascript

I have this simple collapsible menu on www.keokuk.com
I would like for the previous menu to close when you click on the next one.
this is the javascript:
<script>
var coll = document.getElementsByClassName("collapsible");
var i;
for (i = 0; i < coll.length; i++) {
coll[i].addEventListener("click", function () {
this.classList.toggle("active");
var content = this.nextElementSibling;
if (content.style.maxHeight) {
content.style.maxHeight = null;
} else {
content.style.maxHeight = content.scrollHeight + "px";
}
});
}
</script>

I worked on a solution on your website.
But it appears you set max-height manually in an other javascript function so you can just do the same thing in the commented line.
document.querySelectorAll('.collapsible').forEach(el => {
el.addEventListener('click', (e) => {
document.querySelectorAll('.collapsible').forEach(e => {
e.classList.remove('active');
e.nextSibling.nextElementSibling.style.maxHeight = "0px";
});
e.target.classList.toggle('active');
e.target.nextSibling.nextElementSibling.style.maxHeight =
`${el.nextSibling.nextElementSibling.scrollHeight}px`;
});
});

Details are commented in example
// Collect all .switch into an array
const switches = [...document.querySelectorAll(".switch")];
// Bind each .switch to the click event
switches.forEach(s => s.addEventListener("click", openClose));
// Event handler passes Event Object as default
function openClose(event) {
// Reference the tag proceeding clicked tag
const content = this.nextElementSibling;
// Get the height of content
let maxHt = content.scrollHeight + 'px';
// Find the index position of clicked tag
let index = switches.indexOf(this);
// The clicked tag will toggle .active class
this.classList.toggle('active');
// Remove .active class from all .switch
switches.forEach((btn, idx) => {
/*
If current index does NOT equal index of
clicked tag...
...remove .active
*/
if (idx != index) {
btn.classList.remove('active');
}
});
/*
If clicked has .active class...
...set style property of max-height using CSS variables
*/
if (this.classList.contains('active')) {
content.style.setProperty('--maxH', maxHt + 'px');
} else {
content.style.setProperty('--maxH', '0px');
}
}
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box
}
:root {
font: 300 1.5ch/1.2 'Segoe UI';
--maxH: 0px;
}
body {
width: 100%;
min-height: 200%;
padding: 15px;
}
header {
width: max-content;
margin: 10px 0 0;
padding: 5px 10px;
border: 3px ridge black;
border-radius: 4px;
background: #aaa;
cursor: pointer;
}
section {
position: relative;
max-height: var(--maxH);
margin: 0;
padding: 5px 10px;
border: 3px ridge black;
border-radius: 4px;
background: #ddd;
opacity: 0;
pointer-events: none;
}
.active+section {
z-index: 1;
opacity: 1.0;
}
<header class='switch'>Read more...</header>
<section>
<p>Merchandise Morty, your only purpose in life is to buy & consume merchandise and you did it, you went into a store an actual honest to god store and you bought something, you didn't ask questions or raise ethical complaints you just looked into
the bleeding jaws of capitalism and said 'yes daddy please' and I'm so proud of you, I only wish you could have bought more, I love buying things so much Morty. Morty, you know outer space is up right? Are you kidding? I'm hoping I can get to both
of them, Rick! And there's no evidence that a Latino student did it.</p>
</section>
<header class='switch'>Read more...</header>
<section>
<p>Oh, I'm sorry Morty, are you the scientist or are you the kid who wanted to get laid? Why don't you ask the smartest people in the universe, Jerry? Oh yeah you can't. They blew up. Looossseeerrrrr. I am not putting my father in a home! He just came
back into my life, and you want to, grab him and, stuff him under a mattress like last month's Victoria's Secret?!
</p>
</section>

Related

Add and remove classes from Node in Javascript

I have a slider in my page and slider's indicators are dynamic, It bases on slider's elements' number and width of body.
My code block is:
function setIndicators(){
const indicator = document.createElement("div");
indicator.className = "indicator active";
indicatorContainer.innerHTML = "";
for(let i = 0;i <= maxIndex; i++){
indicatorContainer.appendChild(indicator.cloneNode(true));
}
updateIndicators();
}
which is working fine. But I want to show active indicator but I cannot manipulate elements' classes.
I tried this:
function updateIndicators(index) {
indicators.forEach((indicator) => {
indicator.classList.remove("active");
});
let newActiveIndicator = indicators[index];
newActiveIndicator.classList.add("active");
}
And I am not able to reach every indicators using index or anything I know/find. Also, it seems like NodeList not a HTML element.
Other things you may need:
const indicatorContainer = document.querySelector(".container-indicators");
const indicators = document.querySelectorAll(".indicator");
let maxScrollX = slider.scrollWidth - body.offsetWidth;
let baseSliderWidth = slider.offsetWidth;
let maxIndex = Math.ceil(maxScrollX / baseSliderWidth);
A better one I would suggest using the indicators in a different way. Since your HTML isn't shared, I have to assume a few things:
function clearAll() {
const activeOnes = document.querySelectorAll(".active");
activeOnes.forEach(function(activeOne) {
activeOne.classList.remove("active");
});
}
function chooseOne(index) {
clearAll();
const indicators = document.querySelectorAll(".indicator");
indicators[index].classList.add("active");
}
* {
font-family: 'Operator Mono', consolas, monospace;
}
.indicators {
border: 2px solid #ccc;
display: inline-block;
width: auto;
margin: 15px;
}
.indicators .indicator {
padding: 15px;
line-height: 1;
background-color: #fff;
flex-grow: 1;
text-align: center;
display: inline-block;
}
.indicator.active {
background-color: #f90;
}
<div class="indicators"><div class="indicator">I1</div><div class="indicator">I2</div><div class="indicator">I3</div><div class="indicator">I4</div><div class="indicator">I5</div></div>
<button onclick="chooseOne(2); return false">Select I3</button>
<button onclick="chooseOne(3); return false">Select I4</button>
I would have done this differently this way.
Preview

Is there a way to check the bottom of a child div?

I'm trying to implement an Infinity scroll.
But not a window object, the target is a child div with a scroller.
Is there a way to examine the current height of a child div with JavaScript?
For example, I would like to request an event when the scroll touches at the end.
This is my template code.
<div
style="overflow-y: scroll; height:500px;"
class="scroll-content"
#scroll="onScroll"
>
Here is an example:
var listElm = document.querySelector('#infinite-list');
// Add items.
var nextItem = 1;
var loadMore = function() {
for (var i = 0; i < 10; i++) {
var item = document.createElement('li');
item.innerText = 'Item ' + nextItem++;
listElm.appendChild(item);
}
}
// Detect when scrolled to bottom.
listElm.addEventListener('scroll', function() {
if (listElm.scrollTop + listElm.clientHeight >= listElm.scrollHeight) {
loadMore();
}
});
// Initially load some items.
loadMore();
#infinite-list {
/* We need to limit the height and show a scrollbar */
width: 200px;
height: 100px;
overflow: auto;
/* Optional, only to check that it works with margin/padding */
margin: 30px;
padding: 20px;
border: 10px solid black;
}
/* Optional eye candy below: */
li {
padding: 10px;
list-style-type: none;
}
li:hover {
background: #ccc;
}
<ul id='infinite-list'>
</ul>
The following function returns, whether the user has scrolled to the bottom of a certain element:
function scrollEnd(el) {
return (el.scrollTop + el.offsetHeight >= el.scrollHeight);
}
If you add this to a scroll event listener:
element.addEventListener('scroll', () => {
if (scrollEnd(element)) {
// the user reached the end
}
})
I tried this on a textarea, should work with anything, though.

Javascript functions trigger in Google inspects feature, but not otherwise

//Overlay mobile menu open
$('#burger-icon').on('click', function(e) {
e.stopPropagation();
document.getElementById('fp-menu').style.height = "100%";
let overlay_content = document.getElementsByClassName("overlay-content")[0];
let pipe = overlay_content.querySelector(".pipe");
let contact = document.querySelector("#sidebar-leftButton");
let case_studies = document.querySelector("#sidebar-rightButton");
if (screen.width < 1000) {
let overlay_contentA = document.querySelectorAll(".overlay a");
for (i = 0; i < overlay_contentA.length; i++) {
overlay_contentA[i].style.color = "white";
}
//changes the social icons to white if in mobile view.
document.getElementById('instagram').src = "instagram_white.svg";
document.getElementById('linkedin').src = "linkedin_white.svg";
document.getElementById('twitter').src = "twitter_white.svg";
//gets rid of the pipe in menu that is visible for the desktop version
//removes sidetabs while in overlay menu
pipe.style.display = "none";
contact.style.display = "none";
case_studies.style.display = "none";
//changes how the elements are displayed when overlay is triggered
$('#fp-menu .news').removeClass("col-sm-2");
$('#fp-menu .portfolio').removeClass("col-sm-3");
$('#fp-menu #social').removeClass("col-sm-6");
}
});
function closeOverlayMenu() { //closes the overlay mobile menu
// e.stopPropagation();
$('#fp-menu').animateCss('slideUp');
$('#fp-menu').css('height', 0);
let overlay_content = document.getElementsByClassName("overlay-content")[0];
let pipe = overlay_content.querySelector(".pipe");
let contact = document.querySelector("#sidebar-leftButton");
let case_studies = document.querySelector("#sidebar-rightButton");
let overlay_contentA = document.querySelectorAll(".overlay a");
for (i = 0; i < overlay_contentA.length; i++) {
overlay_contentA[i].style.color = "black";
}
//changes icons to black on slide up of overlay
document.getElementById('instagram').src = "instagram.svg";
document.getElementById('linkedin').src = "linkedin.svg";
document.getElementById('twitter').src = "twitter.svg";
//restores elements of the original homepage that were hidden for overlay
pipe.style.display = "block";
contact.style.display = "block";
case_studies.style.display = "block";
$('#fp-menu .news').addClass("col-sm-2");
$('#fp-menu .portfolio').addClass("col-sm-3");
$('#fp-menu #social').addClass("col-sm-6");
};
document.getElementsByTagName('body')[0].onresize = function() {
closeOverlayMenu();
};
//overlay mobile menu close
$('#closebtn').on('click', function(e) {
e.stopPropagation();
$('#fp-menu').animateCss('slideUp');
$('#fp-menu').css('height', 0);
//if overlay mobile menu is down, close it by clicking the X
if (screen.width < 1000) {
closeOverlayMenu();
console.log(document.querySelectorAll("#social"));
}
});
//overlay menu styling
.overlay {
height: 0;
width: 100%;
position: fixed;
z-index: 999;
top: 0;
left: 0;
background-color: #000000;
overflow-x: hidden;
transition: 0.5s;
}
.overlay-content {
position: relative;
top: 5%;
width: 100%;
text-align: center !important;
margin-top: 90px;
.row{
padding: 50px 30px 50px 30px;
.column{
float: left;
width: 33.33%;
padding: 0 5px 0 5px;
}
}
img {
width: 50px;
}
div {
padding: 30px 0 30px 0;
}
}
.overlay a {
padding: 8px;
text-decoration: none;
font-size: 36px;
color: #FFFFFF;
}
.overlay a:hover, .overlay a:focus {
color: #f1f1f1;
}
#closebtn {
display:block;
position: relative;
top: 5px;
right: 20px;
font-size: 60px;
}
#social{
position: relative;
top: 10px;
a {
padding: 5% 5% 5% 5%;
}
}
#fp-menu{
display: block;
color: $menu_black;
}
.pipe{
display: block;
transition: 0.4s;
}
<div id="fp-menu" class="overlay">
<div id="closebtn" style="color: white;">X</div>
<div class="column overlay-content">
<!-- <div class="column"> -->
<div class="col-sm-2 news">
NEWS
</div>
<div class="col-sm-1 pipe">
|
</div>
<div class="col-sm-3 portfolio">
PORTFOLIO
</div>
<div id="social" class="col-sm-6">
<img id="instagram" src='instagram.svg' />
<img id="linkedin" src='linkedin.svg' />
<img id="twitter" src='twitter.svg' />
</div>
</div>
</div>
So the weirdest thing, I've got 1 function that is to trigger under 2 conditions: when the window is resized and when the exit button is clicked.
This relates to an overlay menu that's actually intended for mobile users.
The functions work as they are supposed to, in both cases, when I have the inspection console open in chrome. However, when I close this and return to the normal browser window, the functions cease to execute.
On the mobile it is fine. I have only encountered this problem on the desktop/laptop (as I tried it on different desktops [iMacs] and 2 laptops [Macbooks, but the type of hardware I don't think matters]).
Currently the icons in the "#social" div are not being changed to white, which is what I expect to happen when opened, and when closed, they return to black. They are staying black throughout the execution.
Has anyone ever experienced this before? This is related to a Wordpress platform site. Totally custom built, no themes.
Please let me know if this description helps. If any code is needed, please let me know.
Ps: I thought it was a caching problem, on the terminals or on the server, and I cleared the cache on both but the issue still persists.
function closeOverlayMenu(){//closes the overlay mobile menu
$('#fp-menu').animateCss('slideUp');
$('#fp-menu').css('height', 0);
let overlay_content = document.getElementsByClassName("overlay-content")[0];
let pipe = overlay_content.querySelector(".pipe");
let contact = document.querySelector("#sidebar-leftButton");
let case_studies = document.querySelector("#sidebar-rightButton");
let overlay_contentA = document.querySelectorAll(".overlay a");
for (i = 0; i < overlay_contentA.length; i++) {
overlay_contentA[i].style.color = "black";
}
//changes icons to black on slide up of overlay
document.getElementById('instagram').src="https://s3-eu-west-1.amazonaws.com/mvt-hosted-images/instagram.svg";
document.getElementById('linkedin').src="https://s3-eu-west-1.amazonaws.com/mvt-hosted-images/linkedin.svg";
document.getElementById('twitter').src="https://s3-eu-west-1.amazonaws.com/mvt-hosted-images/twitter.svg";
//restores elements of the original homepage that were hidden for overlay
pipe.style.display="block";
contact.style.display="block";
case_studies.style.display="block";
$('#fp-menu .news').addClass("col-sm-2");
$('#fp-menu .portfolio').addClass("col-sm-3");
$('#fp-menu #social').addClass("col-sm-6");
};
I would expect that the function would trigger without the dev console being active. Please let me know if any further description or information would help.

Element on a menu not responding to a trigger event

I've made a menu that reveals a drop down menu when you click or touch it. At least that's what happens when you select the word 'Menu2' but unfortunately it's not what happens when you select the words 'Menu3'.
On Menu3, for some reason my code is not recognising the selection of the anchor element and then as a consequence the id of that anchor element is not being passed to the functions which will make the sub-menu appear and disappear.
The strangest thing is that when I replace the 'else if' statement with an 'if' statement the menu under 'Menu2' will appear when I select 'Menu3'!
The thing I took from this was that the querySelectorAll method and the For loop are working. It remains a mystery me why the third menu choice can't be selected.
My question is can anyone work why the menu below 'Menu3' is not opening and closing?
The listeners in the javascript code are activated when the window is loaded.
var timeout = 500;
var closetimer = 0;
var ddmenuitem = 0;
function listen(elem, evnt, func) {
if (elem.addEventListener) { //W3C DOMS.
elem.addEventListener(evnt, func, false);
} else if (elem.attachEvent) { //IE DOM 7
var r = elem.attachEvent("on" + evnt, func);
return r;
}
}
function attachListeners() {
var selectors = document.querySelectorAll("a#a2, a#a3");
for (var i = 0; i < selectors.length; i++) {
selectors[i].addEventListener("focus", function(event) {
var id_of_clicked_element = event.target.id
});
if (id_of_clicked_element = 'a2') {
var touch_div = document.getElementById(id_of_clicked_element);
// return false;
} else if (id_of_clicked_element = 'a3') {
touch_div = document.getElementById(id_of_clicked_element);
//return false;
}
}
listen(touch_div, 'touchstart', function(event) {
// get new layer and show it
event.preventDefault();
mopen(id_of_clicked_element);
}, false);
listen(touch_div, 'mouseover', function(event) {
// get new layer and show it
mopen(id_of_clicked_element);
}, false);
listen(touch_div, 'click', function(event) {
// get new layer and show it
mopen(id_of_clicked_element);
}, false);
}
function m1View() {
var y = document.getElementById('m1');
if (y.style.visibility === 'hidden') {
y.style.visibility = 'visible';
} else {
y.style.visibility = 'hidden';
}
}
function m2View() {
var z = document.getElementById('m2');
if (z.style.visibility === 'hidden') {
z.style.visibility = 'visible';
} else {
z.style.visibility = 'hidden';
}
}
// open hidden layer
function mopen(x) { // get new layer and show it
var openmenu = x;
if (openmenu = 'a2') {
m1View();
} else if (openmenu = 'a3') {
m2View();
}
}
window.onload = attachListeners;
#ddm {
margin: 0;
padding: 0;
z-index: 30
}
#ddm li {
margin: 0;
padding: 0;
list-style: none;
float: left;
font: bold 14px arial
}
#ddm li a {
display: block;
margin: 0 0 0 0;
padding: 12px 17px;
width: 130px;
background: #CC0066;
color: #FFF;
text-align: center;
text-decoration: none
}
#ddm li a:hover {
background: #CC0066
}
#ddm div {
position: absolute;
visibility: hidden;
margin: 0;
padding: 0;
background: #EAEBD8;
border: 1px solid #5970B2
}
#ddm div a {
position: relative;
display: block;
margin: 0;
padding: 5px 10px;
width: 130px;
white-space: nowrap;
text-align: left;
text-decoration: none;
background: #EAEBD8;
color: #5C124A;
font: 13px arial;
border: 1px solid #CC0066
}
#ddm div a:hover {
background: #CC0066;
color: #FFF
}
<ul id="ddm">
<li>Menu1</li>
<li>
Menu2
<div id="m1">
Dropdown 1.1
Dropdown 1.2
Dropdown 1.3
Dropdown 1.4
Dropdown 1.5
Dropdown 1.6
</div>
</li>
<li>
Menu3
<div id="m2">
Menu4
</div>
</li>
<li>Menu5</li>
<li>Menu6</li>
</ul>
<div style="clear:both"></div>
A JSfiddle can be found here: https://jsfiddle.net/Webfeet/z9x6Ly6k/27/
Thank you for any help anyone can provide.
NewWeb
I'd suggest a couple of things. First, like Leo Li suggested, I think you may have overcomplicated this a little. For instance, you could replace your attachListeners function with something like this:
function attachListeners() {
var selectors = document.querySelectorAll("a#a2, a#a3");
for (var i = 0; i < selectors.length; i++) {
selectors[i].addEventListener('touchstart', function(event){
event.preventDefault();
mopen(event.target.id);
}, false);
selectors[i].addEventListener('mouseover', function(event){
event.preventDefault();
mopen(event.target.id);
}, false);
selectors[i].addEventListener('click', function(event){
event.preventDefault();
mopen(event.target.id);
}, false);
}
}
But, besides that, one of the biggest problems is in the mopen() function. Instead of checking the value being passed in for x, you're reassigning it. Just switch the equals signs with triple equals, like this:
if (openmenu === 'a2') {
m1View();
} else if (openmenu === 'a3') {
m2View();
}
It's still probably not quite what you're looking for but here's a fork of your JSfiddle with my changes - https://jsfiddle.net/n90ryvfd/
Hope that helps!

javascript chatbox / messenger script

I'm trying to write a facebook like chatbox, but i've encountered a small problem.
I'm using the following code (it's only test code, so it's not really clean):
css code:
#messenger {
position: fixed;
bottom: 0px;
right: 10px;
width: 200px;
height: 300px;
z-index: 4;
background-color: #ECECEC;
border: 1px solid #000;
}
#messenger.p {
text-align: right;
}
#contacts {
margin: 5px 5px 5px 5px;
}
#chatspace {
position: fixed;
bottom: 0px;
right: 240px;
height: 20px;
left: 20px;
background-color: #ECECEC;
border: 1px solid #000;
z-index: 4;
}
.chatbox {
position: absolute;
bottom: 0px;
width: 200px;
height: 200px;
z-index: 4;
background-color: #ECECEC;
border: 1px solid #000;
}
html/javascript code:
<script type="text/javascript">
var i = 0;
function oc_chatbox() {
if (i == 0) {
document.getElementById('contacts').style.visibility = 'hidden';
document.getElementById('messenger').style.height = '20px';
i = 1;
}
else {
document.getElementById('contacts').style.visibility = 'visible';
document.getElementById('messenger').style.height = '300px';
i = 0;
}
}
function new_chat(userid) {
var new_right;
new_right = document.getElementById('messenger').style.right;
//alert('old value: '+ new_right);
new_right += 20;
//alert('New value of right: '+ new_right);
document.getElementById('chatspace').innerHTML = '<div id="'+userid+'" class="chatbox" style="right: '+new_right+'px;"></div>';
//document.write('<div id="'+userid+'" class="chatbox" style="right: '+new_right+'px;"></div>');
}
</script>
<div id="chatspace"></div>
<div id="messenger">
<p>Collapse</p>
<div id="contacts">
<ul>
<li>contact A</li>
</ul>
</div>
</div>
the problem is, that when I try to add new chats to the chatbar, i can't seem the place them next to each other.
anyone who can help ?
EDIT:
so i changed to javascript code to:
var last = null;
function new_chat(userid) {
if(userid==null)
userid = "user666";
var new_right;
var margin = 10;
var messenger = window.last==null?document.getElementById('messenger'):window.last; //Take the messenger or the last added chat
new_right = document.body.clientWidth-messenger.offsetLeft; //Compute the window size
console.log(new_right); //Log the number
new_right += margin; //keep spaces between divs
var newChat = document.createElement("div"); //DOM create DIV
newChat.id = userid;
newChat.className = "chatbox shadow";
newChat.style.right = new_right+"px";
newChat.innerHTML = '<p>'+userid+'</p><p><textarea></textarea></p>';
window.last = newChat; //Remember whichever is last
document.body.appendChild(newChat);
}
and now it works, thanks !
You cannot get an element right offset using its style, unlest the style is set and valid. Instead you must get element.offsetLeft and size of window area and do this:
new_right = windowSize()[0]-messenger.offsetLeft;
Where window size is this function.
Here is my, working, version of your function:
var last = null;
function new_chat(userid) {
if(userid==null)
userid = "user666";
var new_right;
var margin = 20;
var messenger = window.last==null?document.getElementById('messenger'):window.last; //Take the messenger or the last added chat
new_right = windowSize()[0]-messenger.offsetLeft; //Compute the window size
console.log(new_right); //Log the number
new_right += margin; //keep spaces between divs
var newChat = document.createElement("div"); //DOM create DIV
newChat.id = userid;
newChat.className = "chatbox";
newChat.style.right = new_right+"px";
window.last = newChat; //Remember whichever is last
document.body.appendChild(newChat);
}
You may get errors if console is not defined in your brouwser. But in such case you should take a better browser. Normally, the if(console!=null) is put in code.
And here is the link.
You should try adding a float style.
.chatbox {
float: right;
}
Add that to your chatbox styles. You may need to mess around a bit to make sure the float doesn't mess with your other elements. You may need a better container for them.
If you want to get really fun, you can add .draggable() from jQuery, and you can have them snap to your chat bar. You can then change the order of your chats.

Categories