Vanilla JavaScript getAttribute("href") from link with image inside - javascript

I am trying to create tabs with vanilla JavaScript, each tab has an icon and a title inside the link
<div class="tabs-bar-item">
<a href="#tab3">
<img class="icon" src="img/school.png">
<h3 class="title">Instruktāžas datu aizsardzībā</h3>
</a>
</div>
It should open the tab content
<div id="tab3" class="tabs-content-item">
The problem is that .getAttribute("href"); returns null when I have <img> and <h3> elements inside the link.
Everything works if I change the tab to
<div class="tabs-bar-item">
<img class="icon" src="img/school.png">
Instruktāžas datu aizsardzībā
</div>
but I want the tab content to open when clicked anywhere on .tabs-bar-item. How can it be done in vanilla JavaScript?
Full JS code:
let tabs = document.querySelectorAll('.tabs-bar .tabs-bar-item');
function tabClicks(tabClickEvent) {
for (let i = 0; i < tabs.length; i++) {
tabs[i].classList.remove("active");
}
let clickedTab = tabClickEvent.currentTarget;
clickedTab.classList.add("active");
tabClickEvent.preventDefault();
let contentPanes = document.querySelectorAll('.tabs-content-item');
for (i = 0; i < contentPanes.length; i++) {
contentPanes[i].classList.remove("active");
}
let anchorReference = tabClickEvent.target;
let activePaneId = anchorReference.getAttribute("href");
let activePane = document.querySelector(activePaneId);
activePane.classList.add("active");
}
for (i = 0; i < tabs.length; i++) {
tabs[i].addEventListener("click", tabClicks)
}
Css for tab content:
.tabs-content .tabs-content-item {
display: none;
}
.tabs-content .tabs-content-item.active {
display: block;
}

Element.closest searchs up the DOM to find an element matching the selector passed as argument. If Element already matches the selector, Element (the node on which closest is called) is returned.
Hence try changing
let anchorReference = tabClickEvent.target;
to
let anchorReference = tabClickEvent.target.closest('a');
as for example in
let tabs = document.querySelectorAll('.tabs-bar .tabs-bar-item');
function tabClicks(tabClickEvent) {
for (let i = 0; i < tabs.length; i++) {
tabs[i].classList.remove("active");
}
let clickedTab = tabClickEvent.currentTarget;
clickedTab.classList.add("active");
tabClickEvent.preventDefault();
let contentPanes = document.querySelectorAll('.tabs-content-item');
for (i = 0; i < contentPanes.length; i++) {
contentPanes[i].classList.remove("active");
}
let anchorReference = tabClickEvent.target.closest('a');
console.log( anchorReference);
let activePaneId = anchorReference.getAttribute("href");
let activePane = document.querySelector(activePaneId);
activePane.classList.add("active");
}
for (i = 0; i < tabs.length; i++) {
tabs[i].addEventListener("click", tabClicks)
}
.tabs-content .tabs-content-item {
display: none;
}
.tabs-content .tabs-content-item.active {
display: block;
}
<div class="tabs-bar">
<div class="tabs-bar-item">
<a href="#tab3">
<img class="icon" src="img/school.png">
<h3 class="title">Instruktāžas datu aizsardzībā</h3>
</a>
</div>
</div>
<div class=tabs-content>
<div id="tab3" class="tabs-content-item">
content of tab3 with self contained link: <a id="here" href="#here">here</a>. Clicking "here" should not close the tab!
</div>
</div>

First off : you can do this in pure CSS with the :target pseudo-class, no JS needed.
.tabs-content .tabs-content-item {
display: none;
}
.tabs-content .tabs-content-item:target {
display: block;
}
In the near future, you can also style the tab link for the currently open tab by using the :local-link pseudo-class.
In your code, tabClickEvent.currentTarget always refers to the DIV element that you attached the listener to. That element has no href. There are multiple ways to fix that. Removing the DIV altogether would be a good start, then you can attach a listener to the links, or make a global listener that checks if it's been clicked on a link or its descendant (using element.closest('a')).
Either way, properly implementing navigation with JavaScript is not as easy as you might think (this is also why the pure CSS solution is good). For example, if the user wants to open the link in a new (browser) tab, you'd have to add some code to read the page's fragment on load and open the correct tab without user interaction.

Related

Convert jQuery Script to plan JS for bootstrap nav bar drop down collapse on top of iframe

I'm trying to convert a jQuery script to pure JS. This script is to fix an issue on a site of mine were I have a nav bar with drop downs with an iframe on the rest of the screen.
Without this script the downdowns don't collapse when anywhere in the iframe is clicked. The jQuery works but I cant get the JS only working and don't know much about JS
$(window).on("blur", function() {
$(".dropdown").removeClass("show"),
$(".dropdown-menu").removeClass("show"),
$(".dropdown-toggle").attr("aria-expanded", !1).focus()
});
window.blur(),
document.getElementsByClassName('dropdown').classList.remove('show'),
document.getElementsByClassName('dropdown-menu').classList.remove('show'),
document.getElementsByClassName('dropdown-toggle').getAttribute("aria-expanded", !1).focus;
You didn't provide the html, so it's not as easy to answer this question.
ES6 way
window.onblur = () => {
const
removeClass = (el, className) => el.classList.remove(className),
dropdowns = document.getElementsByClassName('dropdown'),
menus = document.getElementsByClassName('dropdown-menu'),
togglers = document.getElementsByClassName('dropdown-toggle')
for (const d of dropdowns) removeClass(d, 'show')
for (const m of menus) removeClass(m, 'show')
for (const t of togglers) t.getAttribute('aria-expanded', false).focus
}
.dropdown {
display: none;
}
.dropdown.show {
display: initial;
}
.dropdown-menu {
display: none;
}
.dropdown-menu.show {
display: initial;
}
<div class="dropdown-toggle" aria-expanded="true">
toggler
</div>
<div class="dropdown show">
<div class="dropdown-menu show">
menu
</div>
</div>
Old way
window.onblur = function () {
function removeClass(el, className) {
el.classList.remove(className)
}
// When calling `getElementsByClassName, you get collection of elements
// So you can't call `classList` method on it
var dropdowns = document.getElementsByClassName('dropdown')
var menus = document.getElementsByClassName('dropdown-menu')
var togglers = document.getElementsByClassName('dropdown-toggle')
// You have to iterate over found elements
for (var i = 0; i < dropdowns.length; i++) removeClass(dropdowns[i], 'show')
for (var i = 0; i < menus.length; i++) removeClass(menus[i], 'show')
for (var i = 0; i < togglers.length; i++) togglers[i].getAttribute('aria-expanded', false).focus
}

How to get index position of clicked element in javascript

I am trying to get the index value of a particular element that is clicked on from a for loop array. What i would like to do is then use this index value and pass it to a variable called indexValue which will then determine a specific div to show. The div it will show will be the one with the same index value that was passed to the variable. I hope this makes sense, I am just not to sure how to achieve this?
The markup would be something like this
HTML
<div class="button"></div>
<div class="button"></div>
<div class="button"></div>
<div class="button"></div>
CSS
.test {
display: none;
}
.show {
display: block;
}
JS
var button = document.querySelectorAll('.button');
for (var i = 0; i < button.length; i++) {
button[i].addEventListener('click', function () {
var indexValue: //Get index value from clicked element
var test = document.querySelectorAll('.test');
test[indexValue].classlist.add('show');
}
}
You can put your eventListener in a closure that takes a parameter from your loop. The alert is only so that when you click it in the fiddle you can see the result of the index
var button = document.querySelectorAll('.button');
for (var i = 0; i < button.length; i++) {
button[i].addEventListener('click', ((j) => {
return function() {
alert(j)
}
})(i))
}
http://jsfiddle.net/cbpw61ur/20/

Re-use if statement for all click handler events in javascript

I have a few click handler events on my page that simply add an active class to an element. I only want the event to trigger if the current section has the class active. I have this working ok but what I am doing is running the same for loop and if statement check for the active class on my section for the different click events. Looking for the simplest way to re-use it using just javascript not jquery.
<section class="section section-1 active">
<div class="button">
<div class="hidden-background"></div>
</div>
<div class="hidden-content"></div>
</section>
<section class="section section-2">
<div class="button">
<div class="hidden-background"></div>
</div>
<div class="hidden-content"></div>
</section>
<section class="section section-3">
<div class="button">
<div class="hidden-background"></div>
</div>
<div class="hidden-content"></div>
</section>
The button click event checks for the active section and just finds the first div tag (hidden-background) and applies an active class to it.
var section = document.querySelectorAll('.section');
var button = document.querySelectorAll('.button');
for (var s = 0; s < section.length; s++) {
if (section[s].classList.contains('active')) {
for (var b = 0; b < button.length; b++) {
button[b].addEventListener('click', function(){
this.getElementsByTagName('div')[0].classList.add('active');
});
}
}
}
The second click event again checks for the active section class and then adds the class active to the hidden-content div.
var section = document.querySelectorAll('.section');
var hiddenContent = document.querySelectorAll('.hidden-content');
for (var s = 0; s < section.length; s++) {
if (section[s].classList.contains('active')) {
for (var h = 0; h < hiddenContent.length; h++) {
hiddenContent[h].addEventListener('click', function(){
this.classList.add('active');
});
}
}
}
Thank you
I would do something like this:
const makeActive = function(evt) {
evt.target.parentElement
.querySelectorAll('.hidden-background, .hidden-content')
.forEach( el => el.classList.add('active'));
};
document
.querySelectorAll('.section.active .button')
.forEach( btn => btn.addEventListener('click', makeActive) );
And assuming the active sections also are toggled by javascript. (The above would no longer work if someone changed the active section, since the eventListener is still bound to the old section)
const makeActive = function(evt) {
if(
evt.target.className.contains('button') &&
evt.target.parentElement.className.contains('active')
){
evt.target.parentElement
.querySelectorAll('.hidden-background, .hidden-content')
.forEach( el => el.classList.add('active'));
}
};
document
.querySelectorAll('.section')
.forEach( section => section.addEventListener('click', makeActive) );
Of course, this whole thing would be easier with only css:
.hidden-background, .hidden-content { display: none; }
.active .hidden-background, .active .hidden-content { display: block; }
but I suppose that is a different question altogether...

How to display items of a particular div on mouseover

I have the div structure
<div id="navigate">
<div class="menu">
<div class="group">Mail</div>
<div class="item">Folders</div>
<div class="item">Messages</div>
</div>
<div class="menu">
<div class="group">Contacts</div>
<div class="item">Friends</div>
<div class="item">Work</div>
</div>
<div class="menu">
<div class="group">Setting</div>
<div class="item">General</div>
<div class="item">Account</div>
</div>
</div>
Right now all items are hidden, and only divs with class 'group' is shown. What I would like to do is if I mouse over a specific menu div, only items of that menu would appear.
Right now I have this code:
function initialise()
{
hideAllItems();
setMouseOvers();
}
function hideAllItems()
{
var nav = document.getElementById("navigate");
var items = nav.getElementsByClassName("item");
for(var i = 0; i < items.length; i++)
{
items[i].style.visibility = "hidden";
items[i].style.display = "none";
}
}
function setMouseOvers()
{
var nav = document.getElementById("navigate");
var menuArr = nav.getElementsByClassName("menu");
for(var x = 0; x < menuArr.length; x++)
{
var itemArrs = menuArr[x].getElementsByClassName("item");
/*var show = function(){ show(itemArrs); };
var hide = function(){ hide(itemArrs); };*/
menuArr[x].onmouseover=function(){ show(itemArrs); };
menuArr[x].onmouseout=function(){ hide(itemArrs); };
}
}
function show(itemArr)
{
for(var i = 0; i < itemArr.length; i++)
{
alert(itemArr[i].innerHTML);
itemArr[i].style.visibility = "visible";
itemArr[i].style.display = "block";
}
}
function hide(itemArr)
{
for(var i = 0; i < itemArr.length; i++)
{
itemArr[i].style.visibility = "hidden";
itemArr[i].style.display = "none";
}
}
And this works, thought it only displays General and Account no matter which menu I hover over. I vaguely understand whats going wrong, but I can't see anyway to fix it. Any ideas? I do not want to change the html structure (e.g. add ids, or create specific classes) if i can help it!
I know that you most probably are looking for a javascript solution, but you could use a simple CSS solution:
.group:hover ~ .item {
display: block;
}
Working Fiddle
But be aware that it is not supported by older IE (< 8) browsers SUPPORT. It depends on your target group if you want to use it.
Why not simply using CSS: DEMO
.menu .item{
display:none;
}
.menu:hover .item{
display:block;
}
As you ask for an JavaScript Only solution (no change in HTML/css) i suggest the following:
The problem is using "itemArrs" in an anonymous function, as only the latest written "itemArrs" is used for all of them, use "this" instead.
for example:
...
groups[x].onmouseover=function(){ show(this); };
...
and
function show(item) {
var items = item.parentNode.getElementsByClassName("item");
...
A complete JS-only solution that works can be found here:
http://jsfiddle.net/Wn4d4/3/

How to change visibility of div's child elements in "onmouseover" function

In my website I would like to change some style properties of a div when user moves the mouse over it. I would also like to hide/show some child elements of this div. I don't have any experience with JavaScript, I'm experimenting with some code I found in the Internet.
Let's say my div looks like that:
<div class="Advertisement">
<h2 class="Title">title</h2>
</div>
And I want to hide this h2 element after moving the mouse over the div. My JS Script looks like this:
window.onload = function() {
var lis = document.getElementsByClassName("Advertisement");
for (var i = 0; i < lis.length; i++) {
lis[i].onmouseover = function() {
this.style.backgroundColor = "green";
this.style.width = "800px";
var children = lis[i].childNodes.getElementsByClassName("Title");
for (var j = 0; j < children.length; j++) {
children[j].onmouseover = function() {
this.style.visibility = "hidden";
};
}
};
}
};​
Changing of size and background color works fine, but the "h2" element doesn't disappear. What did I do wrong?
Actually you don't need JavaScript for that task. Why not use plain HTML/CSS?
Try this:
<style>
div.advertisement:hover > h2, div.advertisement:focus > h2 {
color: red;
}
div.advertisement > h3 {
display: none;
}
div.advertisement:hover > h3, div.advertisement:focus > h3 {
display: block;
}
</style>
<div class="advertisement" tabindex="-1">
<h2>title</h2>
<h3>hidden text</h3>
</div>
This one actually shows something, but of course it works vice versa with hiding your h2.
Extension by RyanB
This is similiar to an answer I'd give. I would say the hidden text should be a <p>, <span> or a <div> versus a <h3> to have better semantics. Also add tabindex="-1" to the div if it is that important. Adding tabindex="-1" allows the <div> to receive focus.
lis[i] is undefined here and no need of childnode
So,instead of this
var children = lis[i].childNodes.getElementsByClassName("Title");
Write
var children = this.getElementsByClassName("Title");

Categories

Resources