Reputation: 57
I have looked through a number of similar questions but can not find a specific example of one that answers in vanilla JS how to add and remove a class to a different element from the one hovered over. I know it has something to do with setting up a loop and iterating through the elements, but I got lost in the exact process.
Basically, I have a close button on my cards and I want the close button to be shown only when someone hovers over the card. I have added 2 different classes to show it and hide it, however, my script does not work.
Unfortunately, I am not allowed to use JQuery.
var closeButton = document.getElementsByClassName('close');
var card = document.getElementsByClassName('.card')
for (var i = 0; i < card.length; i++) {
card[i].addEventListener("mouseover", function() {
for (var i = 0; i < closeButton.length; i++) {
closeButton[i].classList.add('shown');
closeButton[i].classList.remove('hidden');
}
});
card[i].addEventListener("mouseout", function() {
for (var i = 0; i < closeButton.length; i++) {
closeButton[i].remove('shown');
closeButton[i].add('hidden');
}
});
}
/*! normalize.css v1.1.0 | MIT License | git.io/normalize */
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
nav,
section,
summary {
display: block
}
audio,
canvas,
video {
display: inline-block;
*display: inline;
*zoom: 1
}
audio:not([controls]) {
display: none;
height: 0
}
[hidden] {
display: none
}
html {
font-size: 100%;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%
}
html,
button,
input,
select,
textarea {
font-family: sans-serif
}
body {
margin: 0
}
a:active,
a:hover {
outline: 0
}
h1 {
font-size: 2em;
margin: .67em 0
}
h2 {
font-size: 1.5em;
margin: .83em 0
}
h3 {
font-size: 1.17em;
margin: 1em 0
}
h4 {
font-size: 1em;
margin: 1.33em 0
}
h5 {
font-size: .83em;
margin: 1.67em 0
}
h6 {
font-size: .67em;
margin: 2.33em 0
}
abbr[title] {
border-bottom: 1px dotted
}
b,
strong {
font-weight: bold
}
blockquote {
margin: 1em 40px
}
dfn {
font-style: italic
}
hr {
-webkit-box-sizing: content-box;
box-sizing: content-box;
height: 0
}
mark {
background: #ff0;
color: #000
}
p,
pre {
margin: 1em 0
}
code,
kbd,
pre,
samp {
font-family: monospace, serif;
_font-family: 'courier new', monospace;
font-size: 1em
}
pre {
white-space: pre;
white-space: pre-wrap;
word-wrap: break-word
}
q {
quotes: none
}
q:before,
q:after {
content: '';
content: none
}
small {
font-size: 80%
}
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline
}
sup {
top: -0.5em
}
sub {
bottom: -0.25em
}
dl,
menu,
ol,
ul {
margin: 1em 0
}
dd {
margin: 0 0 0 40px
}
menu,
ol,
ul {
padding: 0 0 0 40px
}
nav ul,
nav ol {
list-style: none;
list-style-image: none;
padding: 0;
margin: 0
}
img {
border: 0;
-ms-interpolation-mode: bicubic
}
svg:not(:root) {
overflow: hidden
}
figure {
margin: 0
}
form {
margin: 0
}
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: .35em .625em .75em
}
legend {
border: 0;
padding: 0;
white-space: normal;
*margin-left: -7px
}
button,
input,
select,
textarea {
font-size: 100%;
margin: 0;
vertical-align: baseline;
*vertical-align: middle
}
button,
input {
line-height: normal
}
button,
select {
text-transform: none
}
button,
html input[type="button"],
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button;
cursor: pointer;
*overflow: visible
}
button[disabled],
html input[disabled] {
cursor: default
}
input[type="checkbox"],
input[type="radio"] {
-webkit-box-sizing: border-box;
box-sizing: border-box;
padding: 0;
*height: 13px;
*width: 13px
}
input[type="search"] {
-webkit-appearance: textfield;
-webkit-box-sizing: content-box;
box-sizing: content-box
}
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none
}
a:link {
text-decoration: none !important;
}
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0
}
textarea {
overflow: auto;
vertical-align: top
}
table {
border-collapse: collapse;
border-spacing: 0
}
* {
-webkit-box-sizing: border-box;
box-sizing: border-box;
-webkit-transition: all 1s ease;
transition: all 1s ease;
}
html {
font-size: 100%;
font-family: sans-serif;
height: 100%;
}
body {
min-height: 100%;
margin: 0;
padding: 0;
background: #f8f8f8;
}
#wrapper {
-webkit-box-sizing: border-box;
box-sizing: border-box;
margin: 0 auto;
padding: 20px;
max-width: 980px;
background: #fff;
-webkit-box-shadow: 0px 2px 1px -1px rgba(0, 0, 0, 0.2), 0px 1px 1px 0px rgba(0, 0, 0, 0.14), 0px 1px 3px 0px rgba(0, 0, 0, 0.12);
box-shadow: 0px 2px 1px -1px rgba(0, 0, 0, 0.2), 0px 1px 1px 0px rgba(0, 0, 0, 0.14), 0px 1px 3px 0px rgba(0, 0, 0, 0.12);
border-radius: 2px;
}
.clearfix {
overflow: auto;
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
.headline {
padding: 0 10px;
}
.headline .promoted-stories {
font-weight: bold;
color: #404040;
}
.headline .taboola-link {
float: right;
}
.headline .taboola-link a {
font-weight: 300;
line-height: normal;
text-align: right;
color: #888888;
font-size: 11px;
}
.cards {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
}
.card {
display: block;
min-height: 1px;
margin: 1%;
-webkit-box-flex: 0;
-ms-flex: 0 0 31.33333333%;
flex: 0 0 31.33333333%;
-webkit-box-shadow: 0px 0px 1px -2px rgba(0, 0, 0, 0.2), 0px 0px 2px 0px rgba(0, 0, 0, 0.14), 0px 0px 0px 0px rgba(0, 0, 0, 0.12);
box-shadow: 0px 0px 1px -2px rgba(0, 0, 0, 0.2), 0px 0px 2px 0px rgba(0, 0, 0, 0.14), 0px 0px 0px 0px rgba(0, 0, 0, 0.12);
-webkit-transition: all .25s;
transition: all .25s;
position: relative;
}
.card:hover {
-webkit-transform: translate(0, -2px);
transform: translate(0, -2px);
-webkit-box-shadow: 0px 1px 2px -1px rgba(0, 0, 0, 0.2), 0px 3px 10px 0px rgba(0, 0, 0, 0.14), 0px 1px 3px 0px rgba(0, 0, 0, 0.12);
box-shadow: 0px 1px 2px -1px rgba(0, 0, 0, 0.2), 0px 3px 10px 0px rgba(0, 0, 0, 0.14), 0px 1px 3px 0px rgba(0, 0, 0, 0.12);
}
.overlay {
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
}
.card .thumbnail {
display: block;
height: 130px;
}
.card img {
height: 100%;
width: 100%;
border: none;
-o-object-fit: cover;
object-fit: cover;
-o-object-position: 50% 50%;
object-position: 50% 50%;
}
.card-content {
padding: 5px 10px 25px;
}
.card .card-content .card-title {
margin: 5px 0 0 0;
overflow: hidden;
color: #000;
font-weight: bold;
max-height: 72px;
font-size: .875rem;
line-height: 1.5rem;
text-decoration: none;
}
.card .card-content .category {
color: #999999;
font-size: 11.0px;
font-weight: bold;
text-decoration: none;
margin: 5px 0 0 0;
overflow: hidden;
max-height: 52px;
}
.close {
position: absolute;
right: 15px;
top: 5px;
}
.shown {
display: block;
}
.hidden {
display: none;
}
.close:before,
.close:after {
position: absolute;
left: 0;
right: 0;
content: ' ';
height: 15px;
width: 2px;
background-color: #fff;
}
.close:before {
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
}
.close:after {
-webkit-transform: rotate(-45deg);
transform: rotate(-45deg);
}
@media only screen and (max-width: 768px) {
.card {
-webkit-box-flex: 0;
-ms-flex: 0 0 48%;
flex: 0 0 48%;
}
}
@media only screen and (max-width: 480px) {
.card {
-webkit-box-flex: 0;
-ms-flex: 0 0 98%;
flex: 0 0 98%;
margin: 2%;
}
.card .thumbnail,
.card .thumbnail a:first-of-type {
height: 170px;
}
}
<section id="wrapper">
<div class="headline clearfix">
<span class="promoted-stories">Promoted stories</span>
<span class="taboola-link"><a href="#">Sponsored Links by Taboola</a></span>
</div>
<div class="cards clearfix">
<article class="card">
<a class="overlay" href="#overlay-link"></a>
<figure class="thumbnail">
<img src="https://img.ohmymag.co.uk/headline/480/0f2af4ec6e8d3971480358d00e67e2e8117d994e.jpg" alt="3 Reasons Why You Haven't Found Your Match Yet">
<a href="#" class="close hidden"></a><!-- close hidden button -->
</figure>
<div class="card-content">
<h2 class="card-title">3 Reasons Why You Haven't Found Your Match Yet</h2>
<p class="category">Dating life</p>
</div><!-- .card-content -->
</article><!-- .card -->
<article class="card">
<a class="overlay" href="#overlay-link"></a>
<figure class="thumbnail">
<img src="https://img.ohmymag.co.uk/headline/480/0f2af4ec6e8d3971480358d00e67e2e8117d994e.jpg" alt="Harry And Meghan Announce Baby On The Way">
<a href="#" class="close hidden"></a><!-- close hidden button -->
</figure>
<div class="card-content">
<h2 class="card-title">Harry And Meghan Announce Baby On The Way</h2>
<p class="category">Royals</p>
</div><!-- .card-content -->
</article><!-- .card -->
<article class="card">
<a class="overlay" href="#overlay-link"></a>
<figure class="thumbnail">
<img src="https://img.ohmymag.co.uk/headline/480/0f2af4ec6e8d3971480358d00e67e2e8117d994e.jpg" alt="Things Get Seriously Real As RuPaul Cast Open Up">
<a href="#" class="close hidden"></a><!-- close hidden button -->
</figure>
<div class="card-content">
<h2 class="card-title">Things Get Seriously Real As RuPaul Cast Open Up</h2>
<p class="category">Celebrities</p>
</div><!-- .card-content -->
</article><!-- .card -->
<article class="card">
<a class="overlay" href="#overlay-link"></a>
<figure class="thumbnail">
<img src="https://img.ohmymag.co.uk/headline/480/0f2af4ec6e8d3971480358d00e67e2e8117d994e.jpg" alt="A Tiger Collapsed In A Russian Circus Mid-Show">
<a href="#" class="close hidden"></a><!-- close hidden button -->
</figure>
<div class="card-content">
<h2 class="card-title">A Tiger Collapsed In A Russian Circus Mid-Show</h2>
<p class="category">Circus</p>
</div><!-- .card-content -->
</article><!-- .card -->
<article class="card">
<a class="overlay" href="#overlay-link"></a>
<figure class="thumbnail">
<img src="https://img.ohmymag.co.uk/headline/480/0f2af4ec6e8d3971480358d00e67e2e8117d994e.jpg" alt="Engagement On The Cards For Jack And Dani">
<a href="#" class="close hidden"></a><!-- close hidden button -->
</figure>
<div class="card-content">
<h2 class="card-title">Engagement On The Cards For Jack And Dani</h2>
<p class="category">Dating life</p>
</div><!-- .card-content -->
</article><!-- .card -->
<article class="card">
<a class="overlay" href="#overlay-link"></a>
<figure class="thumbnail">
<img src="https://img.ohmymag.co.uk/headline/480/0f2af4ec6e8d3971480358d00e67e2e8117d994e.jpg" alt="Attwood Hits Back At Rumours Of Dani Dyer Fued">
<a href="#" class="close hidden"></a><!-- close hidden button -->
</figure>
<div class="card-content">
<h2 class="card-title">Attwood Hits Back At Rumours Of Dani Dyer Fued</h2>
<p class="category">Gossip</p>
</div><!-- .card-content -->
</article><!-- .card -->
</div>
</section>
Upvotes: 1
Views: 1947
Reputation: 880
Try it
function find( el, tagName ) {
tagName = tagName.toUpperCase();
let nsi = el.nextElementSibling;
if ( nsi === null || nsi.children === null || nsi.children.length === 0 ) return null;
for ( let c of nsi.children ) {
if ( c.tagName === tagName ) return c;
}
return null;
};
let card = document.getElementsByClassName( 'card' );
for ( let c of card ) {
c.addEventListener( "mouseover", function ( e ) {
let el = find( e.target, "a" );
if ( el === null ) return;
el.classList.add( 'shown' );
el.classList.remove( 'hidden' );
}, false );
c.addEventListener( "mouseout", function ( e ) {
let open = document.querySelectorAll( ".close.shown" );
if ( open !== null ) {
for ( let obtn of open ) {
obtn.classList.remove( 'shown' );
obtn.classList.add( 'hidden' );
}
}
}, false );
};
Your Error JavaScript:
i)closeButton[i].remove( 'shown' );
will be run time error; remove is nottypeof("function")
here and you've to remove class from classList
ii)closeButton[i].add( 'hidden' );
will be run time error; add is nottypeof("function")
here and you've to add class into classList
iii)var card = document.getElementsByClassName('.card')
card
length should be 0 because it'll find all.card
notcard
class
//Correction
//closeButton[i].classList.remove( 'shown' );
//closeButton[i].classList.add( 'hidden' );
//var card = document.getElementsByClassName('card');
Learn more let of Iterating over an Array
Upvotes: 1
Reputation: 65806
Your actual JavaScript problems were that you have nested loops that declare and use the same variable as the parent loop, which resets the outer loop counter and therefore you don't loop the way you need to. Using different variables for the inner loops would be the key there.
Also, when you access the same variable in a nested loop that was declared at a higher scope, it sets up a "closure" around the i
variable. This causes all the cards to share the same i
value and by the time a user gets around to hovering over any of the cards, i
is one more than the length
of the card array and thus, out of bounds for finding any card to match up to. The solution would be to simply declare i
with let
instead of var
which sets up block level scope and avoids the closure.
Now, having said that, there is no need to have a class for shown and a class for hidden. You only need a class for hidden and you will simply apply or remove that one class to control the visibility of the button.
But, in actuality, you don't need any JavaScript for this at all, just a little CSS which controls the display
of the "button" based on whether the ancestor "card" is hovered.
/* Default styling of "close" buttons is hidden */
.card a.close {
display:none;
}
/* When ancestor "card" is hovered, change display to shown */
.card:hover a.close{
display:block;
font-weight:bold;
}
/* ******************************** */
/*! normalize.css v1.1.0 | MIT License | git.io/normalize */
figure {
margin: 0
}
a:link {
text-decoration: none !important;
}
* {
-webkit-box-sizing: border-box;
box-sizing: border-box;
-webkit-transition: all 1s ease;
transition: all 1s ease;
}
html {
font-size: 100%;
font-family: sans-serif;
height: 100%;
}
body {
min-height: 100%;
margin: 0;
padding: 0;
background: #f8f8f8;
}
#wrapper {
-webkit-box-sizing: border-box;
box-sizing: border-box;
margin: 0 auto;
padding: 20px;
max-width: 980px;
background: #fff;
-webkit-box-shadow: 0px 2px 1px -1px rgba(0, 0, 0, 0.2), 0px 1px 1px 0px rgba(0, 0, 0, 0.14), 0px 1px 3px 0px rgba(0, 0, 0, 0.12);
box-shadow: 0px 2px 1px -1px rgba(0, 0, 0, 0.2), 0px 1px 1px 0px rgba(0, 0, 0, 0.14), 0px 1px 3px 0px rgba(0, 0, 0, 0.12);
border-radius: 2px;
}
.clearfix {
overflow: auto;
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
.headline {
padding: 0 10px;
}
.headline .promoted-stories {
font-weight: bold;
color: #404040;
}
.headline .taboola-link {
float: right;
}
.headline .taboola-link a {
font-weight: 300;
line-height: normal;
text-align: right;
color: #888888;
font-size: 11px;
}
.cards {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
}
.card {
display: block;
min-height: 1px;
margin: 1%;
-webkit-box-flex: 0;
-ms-flex: 0 0 31.33333333%;
flex: 0 0 31.33333333%;
-webkit-box-shadow: 0px 0px 1px -2px rgba(0, 0, 0, 0.2), 0px 0px 2px 0px rgba(0, 0, 0, 0.14), 0px 0px 0px 0px rgba(0, 0, 0, 0.12);
box-shadow: 0px 0px 1px -2px rgba(0, 0, 0, 0.2), 0px 0px 2px 0px rgba(0, 0, 0, 0.14), 0px 0px 0px 0px rgba(0, 0, 0, 0.12);
-webkit-transition: all .25s;
transition: all .25s;
position: relative;
}
.card:hover {
-webkit-transform: translate(0, -2px);
transform: translate(0, -2px);
-webkit-box-shadow: 0px 1px 2px -1px rgba(0, 0, 0, 0.2), 0px 3px 10px 0px rgba(0, 0, 0, 0.14), 0px 1px 3px 0px rgba(0, 0, 0, 0.12);
box-shadow: 0px 1px 2px -1px rgba(0, 0, 0, 0.2), 0px 3px 10px 0px rgba(0, 0, 0, 0.14), 0px 1px 3px 0px rgba(0, 0, 0, 0.12);
}
.overlay {
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
}
.card .thumbnail {
display: block;
height: 130px;
}
.card img {
height: 100%;
width: 100%;
border: none;
-o-object-fit: cover;
object-fit: cover;
-o-object-position: 50% 50%;
object-position: 50% 50%;
}
.card-content {
padding: 5px 10px 25px;
}
.card .card-content .card-title {
margin: 5px 0 0 0;
overflow: hidden;
color: #000;
font-weight: bold;
max-height: 72px;
font-size: .875rem;
line-height: 1.5rem;
text-decoration: none;
}
.card .card-content .category {
color: #999999;
font-size: 11.0px;
font-weight: bold;
text-decoration: none;
margin: 5px 0 0 0;
overflow: hidden;
max-height: 52px;
}
.close {
position: absolute;
right: 15px;
top: 5px;
}
.close:before,
.close:after {
position: absolute;
left: 0;
right: 0;
content: ' ';
height: 15px;
width: 2px;
background-color: #fff;
}
.close:before {
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
}
.close:after {
-webkit-transform: rotate(-45deg);
transform: rotate(-45deg);
}
<section id="wrapper">
<div class="headline clearfix">
<span class="promoted-stories">Promoted stories</span>
<span class="taboola-link"><a href="#">Sponsored Links by Taboola</a></span>
</div>
<div class="cards clearfix">
<article class="card">
<a class="overlay" href="#overlay-link"></a>
<figure class="thumbnail">
<img src="https://img.ohmymag.co.uk/headline/480/0f2af4ec6e8d3971480358d00e67e2e8117d994e.jpg" alt="3 Reasons Why You Haven't Found Your Match Yet">
<a href="#" class="close hidden"></a><!-- close hidden button -->
</figure>
<div class="card-content">
<h2 class="card-title">3 Reasons Why You Haven't Found Your Match Yet</h2>
<p class="category">Dating life</p>
</div><!-- .card-content -->
</article><!-- .card -->
<article class="card">
<a class="overlay" href="#overlay-link"></a>
<figure class="thumbnail">
<img src="https://img.ohmymag.co.uk/headline/480/0f2af4ec6e8d3971480358d00e67e2e8117d994e.jpg" alt="Harry And Meghan Announce Baby On The Way">
<a href="#" class="close hidden"></a><!-- close hidden button -->
</figure>
<div class="card-content">
<h2 class="card-title">Harry And Meghan Announce Baby On The Way</h2>
<p class="category">Royals</p>
</div><!-- .card-content -->
</article><!-- .card -->
<article class="card">
<a class="overlay" href="#overlay-link"></a>
<figure class="thumbnail">
<img src="https://img.ohmymag.co.uk/headline/480/0f2af4ec6e8d3971480358d00e67e2e8117d994e.jpg" alt="Things Get Seriously Real As RuPaul Cast Open Up">
<a href="#" class="close hidden"></a><!-- close hidden button -->
</figure>
<div class="card-content">
<h2 class="card-title">Things Get Seriously Real As RuPaul Cast Open Up</h2>
<p class="category">Celebrities</p>
</div><!-- .card-content -->
</article><!-- .card -->
<article class="card">
<a class="overlay" href="#overlay-link"></a>
<figure class="thumbnail">
<img src="https://img.ohmymag.co.uk/headline/480/0f2af4ec6e8d3971480358d00e67e2e8117d994e.jpg" alt="A Tiger Collapsed In A Russian Circus Mid-Show">
<a href="#" class="close hidden"></a><!-- close hidden button -->
</figure>
<div class="card-content">
<h2 class="card-title">A Tiger Collapsed In A Russian Circus Mid-Show</h2>
<p class="category">Circus</p>
</div><!-- .card-content -->
</article><!-- .card -->
<article class="card">
<a class="overlay" href="#overlay-link"></a>
<figure class="thumbnail">
<img src="https://img.ohmymag.co.uk/headline/480/0f2af4ec6e8d3971480358d00e67e2e8117d994e.jpg" alt="Engagement On The Cards For Jack And Dani">
<a href="#" class="close hidden"></a><!-- close hidden button -->
</figure>
<div class="card-content">
<h2 class="card-title">Engagement On The Cards For Jack And Dani</h2>
<p class="category">Dating life</p>
</div><!-- .card-content -->
</article><!-- .card -->
<article class="card">
<a class="overlay" href="#overlay-link"></a>
<figure class="thumbnail">
<img src="https://img.ohmymag.co.uk/headline/480/0f2af4ec6e8d3971480358d00e67e2e8117d994e.jpg" alt="Attwood Hits Back At Rumours Of Dani Dyer Fued">
<a href="#" class="close hidden"></a><!-- close hidden button -->
</figure>
<div class="card-content">
<h2 class="card-title">Attwood Hits Back At Rumours Of Dani Dyer Fued</h2>
<p class="category">Gossip</p>
</div><!-- .card-content -->
</article><!-- .card -->
</div>
</section>
Upvotes: 0
Reputation: 195
In your code document.getElementsByClassName('.card')
will look for
elements which has class name as .card
and not card
. Also you don't need two classes for this functionality. Just removing hidden
class will do the work.
Here is updated JS code:
(function() {
document.querySelectorAll(".card").forEach(function(card) {
card.addEventListener("mouseenter", function() {
card.querySelector(".close").classList.remove("hidden");
})
card.addEventListener("mouseout", function() {
card.querySelector(".close").classList.add("hidden");
})
})
})()
Upvotes: 1