Trying to figure out how to close old content when switching tabs - javascript

I have a very basic tab setup on my website and I managed to show the content I need when I press a tab. However, that content stays visible when I click another tab.
I also tried to write a function to remove all the clicked-on tab content before showing me the next one but the problem is it gives me an addEventListener error and it's been driving me crazy for the last couple of days.
const tabs = function(panelClass, numClass, techNum) {
document.getElementById(panelClass).addEventListener("click", () => {
removeStuff();
document.querySelector('.slide').style.display = "flex"
document.querySelector(numClass).style.display = "flex"
document.querySelector(techNum).style.display = "block"
});
};
function removeStuff(parameter) {
document.querySelector(parameter).addEventListener("click", () => {
document.querySelector('.slide').style.display = "none"
document.querySelector('.content-slide').style.display = "none"
document.querySelector('.picture').style.display = "none"
});
}
removeStuff('tab1');
removeStuff('tab2');
tabs('tab1', '.two', ".p2");
tabs('tab2', '.one', ".p1");
<img class='picture p2' src="./picture.jpg" height=140px;alt="">
<img class='picture p1 ' src="./picture.jpg" height=140px;alt="">
<div class="slide">
<!-- Slide 1 -->
<div class="content-slide active one">
<h3>Some text</h3>
<p>Lorem ipsum dolor sit.</p>
</div>
<!-- Slide2 -->
<div class="content-slide two">
<h3>Some Text</h3>
<p>Lorem ipsum dolor </p>
</div>
</div>

The easiest and better approach would be to check that document.getElementById(someID) is not null before adding an event listener to it:
Demo code:
const tabs = function(panelClass, numClass, techNum) {
var panel = document.getElementById(panelClass);
if(panel){
panel.addEventListener("click", () => {
removeStuff();
document.querySelector('.slide').style.display = "flex"
document.querySelector(numClass).style.display = "flex"
document.querySelector(techNum).style.display = "block"
});
}
};
function removeStuff(parameter) {
var tab = document.querySelector(parameter);
if(tab){
tab.addEventListener("click", () => {
document.querySelector('.slide').style.display = "none"
document.querySelector('.content-slide').style.display = "none"
document.querySelector('.picture').style.display = "none"
});
}
}
removeStuff('tab1');
removeStuff('tab2');
tabs('tab1', '.two', ".p2");
tabs('tab2', '.one', ".p1");
<img class='picture p2' src="./picture.jpg" height=140px;alt="">
<img class='picture p1 ' src="./picture.jpg" height=140px;alt="">
<div class="slide">
<!-- Slide 1 -->
<div class="content-slide active one">
<h3>Some text</h3>
<p>Lorem ipsum dolor sit.</p>
</div>
</div>
<!-- Slide2 -->
<div class="content-slide two">
<h3>Some Text</h3>
<p>Lorem ipsum dolor </p>
</div>
</div>

If this an actual copy-and-paste of your code, a couple of things stand out:
You closed your main slide div before "content-slide two". Given the final <div> tag in your markup...that doesn't look like what you meant to do. One way or another, you have an extra div tag.
Your call to removeStuff() in the tabs function does not pass a parameter. That's going to cause some issues with querySelector; you'd be invoking addEventListener on an undefined object in the removeStuff() function.
It's likely that these two things are part of the problems you're experiencing.

Related

Dynamically adding active class to a dynamically built nav menu line item using JavaSCript

I would like to dynamically add an 'active' class to the navigation menu link when it is clicked using addEventListener. The nav menu is has also been added dynamically. I need to do this with only JavaScript. Thank you for any help.
Here is what I have so far. Not sure where to go from here
const navElements = document.querySelectorAll('section')
const navBar = document.getElementById('navbar__list')
navElements.forEach(function(section){
const navlistElement = `<li class='menu__link ${section.className}' data-link=${section.id}><a href="#${section.id}">${section.dataset.nav}</li>`
navBar.insertAdjacentHTML('beforeend',navlistElement)
})
const activeClasses = document.querySelectorAll('.menu__link');
for (x of activeClasses) {
x.addEventListener('click', function(event) {
event.preventDefault();
x.removeClass('active');
$(this).addClass('active');
});
}
<nav class="navbar__menu">
<!-- Navigation starts as empty UL that will be populated with JS -->
<ul id="navbar__list"></ul>
</nav>
...
<section id="section1" data-nav="Section 1" class="your-active-class">
<div class="landing__container">
<h2>Section 1</h2>
<p>Lorem ipsum dolor sit amet</p>
</div>
</section>
...
The main reason it isn't working for you is that the elements don't exist in the document yet when you try to add the listeners.
There are two ways to fix this. One would be to add a listener to each <li> as you create them and before inserting them into the DOM. But the simpler solution is to use event delegation and attach one listener to the <ul> element and handle the event.targets.
You have a number of syntax errors, but below is a working edit of your snippet.
To control the classes assigned to an element you need to use the classList property of the element and its provided methods to add(), remove() or toggle() a class.
const navBar = document.querySelector('.navbar__list');
const navElements = document.querySelectorAll('section');
navBar.addEventListener('click', function (event) {
event.preventDefault();
navBar.querySelector('.active')?.classList.remove('active');
event.target.classList.add('active');
});
navElements.forEach(function (section) {
const navlistElement = `<li class='menu__link ${section.className}' data-link=${section.id}><a href="#${section.id}">${section.dataset.nav}</li>`
navBar.insertAdjacentHTML('beforeend', navlistElement)
})
.active {
background-color: tomato;
}
<nav class="navbar__menu">
<!-- Navigation starts as empty UL that will be populated with JS -->
<ul class="navbar__list"></ul>
</nav>
<section id="section1" data-nav="Section 1" class="your-active-class">
<div class="landing__container">
<h2>Section 1</h2>
<p>Lorem ipsum dolor sit amet</p>
</div>
</section>
<section id="section2" data-nav="Section 2" class="your-active-class">
<div class="landing__container">
<h2>Section 2</h2>
<p>Lorem ipsum dolor sit amet</p>
</div>
</section>

Showing messages based on scroll position

I am trying to create a page where there is an avatar in the bottom right hand corner of the screen and when a user scrolls past certain elements on the page, the avatar brings up a popup that gives some brief user feedback based on the point that he is passing.
I have no idea the best way to go about this, I am thinking there will be an ID attached to the scroll point and when that point is scrolled past a tooltip or something will appear for a brief amount of time until the user scrolls past said ID.
Any help would be much appreciated
It changes the #message's content by scrolling across sections (.section) in the page and identifying which one is visible on viewport. Complete code on the link below:
https://jsfiddle.net/hw475zeL/
Markup:
<div class="container">
<h1>Custom message on scrolling</h1>
<div id="section-1" class="section">
<h2>Section 1</h2>
</div>
<div id="section-2" class="section">
<h2>Section 2</h2>
</div>
<div id="section-3" class="section" data-message="Showing section 3">
<h2>Section 3</h2>
</div>
<div id="section-4" class="section" data-message="Showing section 4">
<h2>Section 4</h2>
</div>
<div id="section-5" class="section" data-message="Showing section 5">
<h2>Section 5</h2>
</div>
<div id="section-6" class="section">
<h2>Section 6</h2>
</div>
</div>
<div id="message" style="visibility: hidden; opacity: 0;">Teste</div>
JavaScript:
var sections = document.querySelectorAll('.section');
var message = document.querySelector('#message');
document.onscroll = function(event) {
for (var section of sections) {
if (elementInViewport(section) && section.hasAttribute('data-message')) {
message.innerText = section.getAttribute('data-message');
message.style.visibility = 'visible';
message.style.opacity = 1;
break;
}
message.style.visibility = 'hidden';
message.style.opacity = 0;
}
}
And the elementInViewport() function from this answer:
https://stackoverflow.com/a/125106/5862990
If you set every element as a row and then use
<div class="row" onmouseover="avatarscript">
<div class="row" onmouseover="avatarscript2">
in avatarscripts use jquery or javascript to change to the appropriate avatar-text.
you can use scroll event listner.
// what should we do when scrolling occurs
var runOnScroll = function(evt) {
// not the most exciting thing, but a thing nonetheless
console.log(evt.target);
};
// and then make each element do something on scroll
elements.forEach(function(element) {
window.addEventListener("scroll", runOnScroll, {passive: true});
});

How To Hide And Display Multiple Divs using JavaScript

How would I go about hiding and showing multiple divs using JavaScript? I don't want to use JQuery. I can make it work for hiding and showing one div but not multiple divs. The problem originates because I'm using PHP to display multiple records. These records are included in divs which have the same ID.
document.getElementById( 'history-slider' ).addEventListener( 'click', function() {
document.getElementById('edit-slider').style.display = 'block';
document.getElementById('history-slider').style.display = 'none';
}, false );
document.getElementById( 'edit-slider' ).addEventListener( 'click', function() {
document.getElementById('history-slider').style.display = 'block';
document..getElementById('edit-slider').style.display = 'none';
}, false );
.edit-slider {
display: none;
}
<div class="panel-body panel-strip" id="history-slider">
<h3>Title</h3>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
<p>
<img src="img/time_icon.png" class="time-icon"> <span class="hour-text">4.00 hrs</span>
</p>
</div>
<hr class="calendar-divider">
<div class="panel-body panel-strip edit-slider">
<div class="row pull-right">
<a href="add.php">
<div class="col-xs-4 delete-panel">
<img src="img/delete_icon.png" class="edit-images center-block"><span class="text-center edit-texts">Delete</span>
</div>
</a>
<a href="http://google.com/">
<div class="col-xs-4 edit-panel">
<img src="img/edit_icon.png" class="edit-images center-block"><span class="text-center edit-texts edit-text">Edit</span>
</div>
</a>
<a href="http://google.com/">
<div class="col-xs-4 record-panel">
<img src="img/record_icon.png" class="edit-images center-block"><span class="text-center edit-texts">Record</span>
</div>
</a>
</div>
</div>
HTML;
<div class="panel-body panel-strip" id="history-slider">
<h3>Title</h3>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
<p>
<img src="img/time_icon.png" class="time-icon"> <span class="hour-text">4.00 hrs</span>
</p>
</div>
<hr class="calendar-divider">
<div class="panel-body panel-strip edit-slider">
<div class="row pull-right">
<a href="add.php">
<div class="col-xs-4 delete-panel">
<img src="img/delete_icon.png" class="edit-images center-block"><span class="text-center edit-texts">Delete</span>
</div>
</a>
<a href="http://google.com/">
<div class="col-xs-4 edit-panel">
<img src="img/edit_icon.png" class="edit-images center-block"><span class="text-center edit-texts edit-text">Edit</span>
</div>
</a>
<a href="http://google.com/">
<div class="col-xs-4 record-panel">
<img src="img/record_icon.png" class="edit-images center-block"><span class="text-center edit-texts">Record</span>
</div>
</a>
</div>
</div>
JavaScript;
document.getElementById( 'history-slider' ).addEventListener( 'click', function() {
document.getElementById('edit-slider').style.display = 'block';
document.getElementById('history-slider').style.display = 'none';
}, false );
document.getElementById( 'edit-slider' ).addEventListener( 'click', function() {
document.getElementById('history-slider').style.display = 'block';
document..getElementById('edit-slider').style.display = 'none';
}, false );
I have also set in the CSS to hide the "edit-slider" div on page load.
.edit-slider {
display: none;
}
The HTML is echoed out in a loop for every record in the database. Information is also added in replace of the placeholder text.
How should I best go about making it so that if a div if clicked it is hidden and the corresponding div is shown in it's place?
I was thinking about doing something about individually giving the divs separate ID's in PHP and than passing those ID's to JavaScript and creating some sort of a loop? My knowledge of JavaScript isn't massive so I don't really know how easy or difficult this method would be. Or is there a much easier method?
This is my first stack overflow post,so sorry if I'm doing anything wrong or missed something.
If you use classes instead of IDs you can use document.QuerySelectorAll() to get all the divs with that class and then show or hide as necessary.
Something like below would hide all divs with an edit-slider class and reveal (assuming they were already hidden) all divs with a history-slider class.
(function() {
var editSliders = document.querySelectorAll('div.edit-slider');
for(var i=0;i<editSliders.length;i++){
editSliders[i].style.display = 'none';
}
var historySliders = document.querySelectorAll('div.history-slider');
for(var i=0;i<historySliders.length;i++){
historySliders[i].style.display = 'block';
}
})();
First, consider using class instead of id to set multiple elements with the same attribute and value. Then, use the following script:
<script type="text/javascript">
document.querySelectorAll('div.history-slider').forEach(item => {
item.addEventListener("click", myFunction);
});
function myFunction() {
this.hide;
}
</script>

jQuery Traversal -- Find an Element Relative to Common Ancestor

I frequently find I'm needing to select an element that is nearby, typically within a common container, but which is not a sibling or within the same "tree line". For example, given this HTML:
<div id="container-left" class="container">
<div class="sidebar">
<button class="more-link">Show Extras</button>
</div>
<div class="content">
<div class="tidbits">
<p>Lorem ipsum beep bop boop</p>
<p class="extra hidden">Exxtra info about lorem ipsum!</p>
</div>
</div>
</div>
<div id="container-right" class="container">
<div class="sidebar">
<button class="more-link">Show Extras</button>
</div>
<div class="content">
<div class="tidbits">
<p>Lorem ipsum beep bop boop</p>
<p class="extra hidden">Exxtra info about lorem ipsum!</p>
</div>
</div>
</div>
What I'll do is attach an event listener to the "Show Extras" buttons, which target the p tags with class of extra, and on click, toggle the hidden class. So (using jQuery) I typically select like this:
$(".more-link").on("click",function(){
var $this = $(this);
var $extraElement = $this.closest(".container").find(".extra");
$extraElement.toggleClass("hidden");
});
My question: is there a better way to select the extra element than the .closest().find() combo? Something about it just feels a little clunky.
well.. you could navigate to sidebar siblings and find extra... but the mess would be similar...
Other way to do it is to generate a HTML5 data attribute at button:
<button class="more-link" data-extra='#some-generated-extra-id'>Show Extras</button>
...
<p id='some-generated-extra-id' class="extra hidden">Exxtra info about lorem ipsum!</p>
your code:
$(".more-link").on("click",function() {
var $extraElement = $($(this).data('extra'));
$extraElement.toggleClass("hidden");
});

Expand/Collapse HTML divs simultaneously

I am trying to create a row of HTML divs that expand and collapse when you click on them. Please see this jsFiddle to get an idea of what I mean: http://jsfiddle.net/Lm6Pg/3/. (You may have to expand the result pane or use the full screen result to get all the divs to appear on a single row.)
Currently, I am using One% CSS Grid to get all the divs to appear on one row, then toggling different CSS column classes to expand and collapse the divs according to the current state and what div was clicked.
<div id="content" class="onepcssgrid-1200">
<div class="onerow">
<div class="tile col4" id="about">
<h3>About</h3>
<div class="text">
<p>Lorem ipsum dolor sit amet.</p>
</div>
</div>
<div class="tile col4" id="other">
<h3>Other</h3>
<div class="text">
<p>Lorem ipsum dolor sit amet.</p>
</div>
</div>
<div class="tile col4 last" id="stuff">
<h3>Stuff</h3>
<div class="text">
<p>Lorem ipsum dolor sit amet.</p>
</div>
</div>
</div>
Here is my javascript (I've changed it from what's in the jsFiddle because I was messing around with dynamically determining which column classes needed to be toggled):
$(".tile").click(function() {
var tile = $(this);
var otherTiles = tile.siblings();
var currentSelectedTile = otherTiles.filter(".selected");
var unselectedOtherTiles = otherTiles.not(".selected");
var otherTileWeight, tileWeight;
if (currentSelectedTile.length) {
otherTileWeight = 3;
tileWeight = 3;
} else {
otherTileWeight = 4;
tileWeight = 4;
}
tile.toggleClass("selected col" + tileWeight + " col6", 600);
currentSelectedTile.toggleClass("selected col3 col4", 600);
unselectedOtherTiles.toggleClass("col" + otherTileWeight + " col3", 600);
});
This seems like a lot of code that might be wrapped up in a jQuery UI function or some other library that eluded me when I put this together. Is there an easier, more concise, or just plain better way to do this? The solution does not need to use jQuery nor One% CSS Grid, any library is fine. However, it does need to be in a responsive layout that preferably still functions as you'd expect when the divs are on top of each other.

Categories

Resources