JS Hover Over One Item to Make Another Move - javascript

I've got a simple text button with an image of an arrow next to it. I'm wanting the arrow image to move when someone hovers over the button.
I currently have this working in one instance with JS 'document.getElementById...', but I have several buttons across my site that I'd like to have the same behavior. My first thought would be to use a class instead of an id, and use the same functions.
For whatever reason, document.getElementsByClassName doesn't work - even in one instance.
Here's a simpler version to demonstrate - View on Codepen: https://codepen.io/sdorr/pen/JxYNpg
HTML
<HTML>
hover over me
<div id="block"></div>
hover over me
<div class="block"></div>
CSS
* {
margin: 0;
padding: 0;
}
.button {
color: #000000;
text-decoration: none;
background-color: cyan;
margin: 0;
display: block;
width: 300px;
padding: 20px;
text-align: center;
}
#block {
width: 30px;
height: 30px;
background-color: red;
}
.block {
width: 30px;
height: 30px;
background-color: green;
}
JS
function move() {
document.getElementById("block").style.marginLeft = "35px";
}
function moveBack() {
document.getElementById("block").style.marginLeft = "0px";
}
function moveAlt() {
document.getElementsByClassName("block").style.marginLeft =
"35px";
}
function moveBackAlt() {
document.getElementsByClassName("block").style.marginLeft =
"0px";
}
First off, why isn't the behavior with a class working but an id works fine?
Secondly, would a class solve this issue and be scalable across all buttons with the same two functions (onmouseover / onmouseout)?
If not, any ideas on a solution? I currently have a solution I found using jQuery that does work, but when hovering over one button, all arrow images move across the site. I don't necessarily mind this behavior because only one button is really in view at a time - but I'm trying to learn JS and solve problems with my own solutions!

I greatly appreciate your desire to learn on your own and not rely on premade solutions. Keep that spirit and you will go places!
When it comes to getElementsById, we know this should work for one element, since the function returns a single Element.
However, what does getElementsByClassName return?
(see: https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName)
It returns an HTMLCollection which you can iterate over to change an single element's style.
So, to get this to work with JavaScript you need to write a function that will be able to identify the particular div.block you want to move. But, this puts you back to where you started, needing some particular identifier, like an id or a dataset value to pass to the function.
Alternately, based on the HTML structure you provide, you could look for nextElementSibling on the a that get's clicked. But I would set up an eventListener rather than adding a JS function as a value to the onmouseenter property.
const btns = document.getElementsByTagName('a');
/*** UPDATE forEach is a NodeList method, and will fail on HTMLCollection ***/
/* this fails -> Sorry! ~~btns.forEach(button=>{~~
/* the following will work
/**********/
for (let i = 0; i < btns.length; i++){
btns[i].addEventListener('mouseenter', function(e) {
//we pass e to the function to get the event and to be able to access this
const block = this.nextElementSibling;
block.style.marginLeft = "35px";
})
btns[i].addEventListener('mouseleave', function(e) {
const block = this.nextElementSibling;
block.style.marginLeft = "0";
})
}
But with siblings, there is a CSS-only solution.
We can use the Adjacent Sibling Selector combined with the :hover state selector and no JavaScript is needed, if we are just moving back and forth.
.button:hover+.block {
margin-left: 35px;
}
See the Snipped Below
* {
margin: 0;
padding: 0;
}
.button {
color: #000000;
text-decoration: none;
background-color: cyan;
margin: 0;
display: block;
width: 300px;
padding: 20px;
text-align: center;
}
.block {
width: 30px;
height: 30px;
background-color: green;
}
.button:hover+.block {
margin-left: 35px;
}
hover over me
<div class="block"></div>
hover over me
<div class="block"></div>

As Vecta mentioned, getElementsByClassName returns an array-like. You'll need to do something like this to get the first element:
function moveAlt() {
document.getElementsByClassName("block")[0].style.marginLeft = "35px";
}
function moveBackAlt() {
document.getElementsByClassName("block")[0].style.marginLeft = "0px";
}
However a better solution might be to use document.querySelector, which operates similarly to jQuery's $() syntax:
function moveAlt() {
document.querySelector(".block").style.marginLeft = "35px";
}
function moveBackAlt() {
document.querySelector(".block").style.marginLeft = "0px";
}

Related

Putting and removing background color on click from button

I have problem with this code because it works. Well, partially. My idea is to change background color of button on which user has clicked but data-line attributes increments only once and by that changing background color only once. (it enters in else and changes data-line value from 0 to 1 but my idea is to increment its value further and then by concluding whether value is odd or even to remove or add bg-danger class). All buttons (all with trashCan class) initially have data-line attribute with value zero and neutral background color. What is wrong with this code?
$(".trashCan").click(function(){
let trashCan = $(this);
let trash = parseInt(trashCan.data('line'))+1;
trashCan.attr('data-line',trash);
if(trash%2==0){
trashCan.removeClass("bg-danger");
}
else {
trashCan.addClass("bg-danger");
}
});
The issue is because you're accessing the data-line attribute with a mix of attr() and data(). The former reads from the DOM, while the latter reads from jQuery's internal cache. If you get the value with one of them you need to also set the value using the same method.
In the example below I've amended the logic to use data() only, which is the preferred choice where possible. If you absolutely have to update the DOM, then you would use attr() instead.
$(".trashCan").click(function() {
let trashCan = $(this);
let trash = parseInt(trashCan.data('line')) + 1;
trashCan.data('line', trash);
if (trash % 2 == 0) {
trashCan.removeClass("bg-danger");
} else {
trashCan.addClass("bg-danger");
}
});
.trashCan {
background-color: #CCC;
color: #000;
border-radius: 5px;
padding: 10px 30px;
border: 0;
}
.trashCan.bg-danger {
background-color: #C00;
color: #FFF
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button class="trashCan" data-line="0">Trash</button>
However, it's worth noting that this logic is not necessary. It seems that your goal is simply to toggle the class on successive clicks, in which case just use toggleClass() and remove the data attribute entirely:
$(".trashCan").click(function() {
$(this).toggleClass('bg-danger');
});
.trashCan {
background-color: #CCC;
color: #000;
border-radius: 5px;
padding: 10px 30px;
border: 0;
}
.trashCan.bg-danger {
background-color: #C00;
color: #FFF
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button class="trashCan">Trash</button>

How to fix 'click me' button to show and hide a certain window

My click me button is not working and it should do the following:
When you click the <a> tag makes the .shade area show up
when you click anywhere in the shaded area make the shade disappear
let button = document.querySelector('.js-click-me');
button.addEventListener('click', event => {
let shade = event.querySelectorAll('shade.js-click-me');
if (shade.style.display === 'none') {
shade.style.display = 'block';
}
});
.shade {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: #444;
color: #fff;
font-size: 50px;
text-align: center;
display: none;
}
Click Me
<div class="shade js-click-me">Show Me</div>
I expect when I click the button the .shade CSS should show and when I click the shaded area it should revert back to the first window.
OP's Code and explanations
I'll break down your JS:
let button = document.querySelector('.js-click-me');
Using querySelector will only return the first element that matches the query. In this case, that only matches your anchor tag.
button.addEventListener('click', event => {
let shade = event.querySelectorAll('shade.js-click-me');
if (shade.style.display === 'none') {
shade.style.display = 'block';
}
});
So here you're only adding the event listener to the first a tag. Then you're passing in the event object which is an instance of MouseEvent, which doesn't have a method called querySelectorAll. That only exists on HTML Elements, or on the document itself. In this case you'd want to just use document.querySelector since you're referencing the main scope of the document and you only want to select one single element. You missed the preceding . before referencing your .shade.js-click-me element as well.
Also, the content of your function only ever tries to toggle one way: if it's hidden, show it. If you want it to toggle both ways, you'll need the eventListener on both elements, and you'll have to check "if hidden, show OR if shown, hide".
Finally, shade.style.display will not be === to none to start even though your CSS includes it. that style property only references anything inside the inline style property of an element, like <div class="shade" style="display: none"></div>. What you'll need instead is window.getComputedStyle(shade).display.
Working Example
Run the snippet below to see how it all comes together.
let buttons = document.querySelectorAll('.js-click-me');
for (let i = 0; i < buttons.length; i += 1) {
buttons[i].addEventListener('click', event => {
let shade = document.querySelector('.shade');
const display = window.getComputedStyle(shade).display;
shade.style.display = display === 'none' ? 'block' : '';
});
}
.shade {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: #444;
color: #fff;
font-size: 50px;
text-align: center;
display: none;
}
<a href="#" class="js-click-me">
Click Me </a>
<div class="shade js-click-me">
Show Me</div>
There are a few problems here.
First of all, You're using querySelectorAll to target the shade class inside the eventListener. This returns all elements with the given class as an array, even if there's only one element. If this is intentional, you have to iterate through all the elements, then removing/adding the styling. If this isn't intentional, replace it with querySelector, so it only returns a single element.
You're also missing a . in your selector parameter. I believe it should say be .shade.js-click-me.
I'd suggest using a class to add and remove the display property of the .shade element. It'd be something like:
.shade--hidden {
display: none;
}
Remember to remove the display: none from inside the shade class. With this class, you can add and remove it inside your event listener as you please, thus toggling the shade display state:
button.addEventListener('click', event => {
let shade = event.querySelector('.shade.js-click-me');
if (shade.classList.contains('shade--hidden'))
shade.classList.remove('shade--hidden');
})
I created a quick codepen with the changes listed above:
https://codepen.io/Phoenix1355/pen/PgxKwG?editors=0010
You could toggle the class name if you want. like:
<button id="myBtn">Try it (Click Me)</button>
<div id="myDIV">
This is a DIV element.
</div>
.shade {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: #444;
color: #fff;
font-size: 50px;
text-align: center;
display: none;
}
JS:
const btn = getElementById("myBtn");
btn.addEventListener("click", function(){
const element = document.getElementById("myDIV");
element.classList.toggle("mystyle");
});
let shade = document.querySelector(".shade")
let clickme = document.querySelector(".click-me")
clickme.addEventListener("click", event => {
shade.classList.toggle("toggle")
});
shade.addEventListener("click", event => {
shade.classList.toggle('toggle')
});
.shade {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: #444;
color: #fff;
font-size: 50px;
text-align: center;
}
.toggle {
display: none
}
Click Me
<div class="shade toggle">Show Me</div>

Change Unit Background Color on hover using Jquery

I am trying to get a cell, within div table created in jquery, to change color when I hover over it and remain that color when the mouse leaves the cell.
I have tried adding a .hover command but when I add it the entire grid goes away.
Here is my code at JSfiddle: https://jsfiddle.net/davidtaylorjr/eemLsjg7/8/
$(document).ready(function() {
$(function() {
for (var x = 0; x < 16; x++) {
for (var y = 0; y < 16; y++) {
$("<div>").addClass("unit").appendTo('#container');
}
}
});
$(".unit").hover() {
$(this).css("background-color", "black");
});
});
#container {
background-color: lightblue;
height: 192px;
width: 192px;
}
.unit {
background-color: white;
height: 10px;
width: 10px;
margin: 1px;
float: left;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container"></div>
Your logic is correct aside from two syntax issues. Firstly, you need to provide a function to hover() to be executed when the mousenter and mouseleave events fire. Secondly, you have nested document.ready handlers which you should unwrap. With those fixed it works fine.
Note however that you can make a couple of tweaks to improve the logic. Firstly the nested loops are redundant as you append the same HTML in all iterations. You can make this a single loop. Secondly it's better practice to keep all styling in CSS, so you can simply use addClass() to change the background colour. Lastly, the hover() creates two events, of which the mouseleave isn't needed for your code, so you can use just mouseenter to make it more efficient.
With all that said, try this:
$(document).ready(function() {
var html = ''
for (var x = 0; x < 16 * 16; x++) {
html += '<div class="unit"></div>';
}
$(html).appendTo('#container');
$(".unit").mouseenter(function() {
$(this).addClass('black');
});
});
#container {
background-color: lightblue;
height: 192px;
width: 192px;
}
.unit {
background-color: white;
height: 10px;
width: 10px;
margin: 1px;
float: left;
}
.unit.black {
background-color: #000;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container"></div>
Also note that you can remove the loop altogether and use the fill() method of an array to create your .unit elements:
var arr = new Array(256);
arr.fill('<div class="unit"></div>');
$('#container').html(arr.join(''));
Note that this is unsupported in IE and Safari, although a polyfill is available at MDN
The following will change the background color of your element when the mouse enters, and subsequently unbind the handler (so the code will only execute on first mouseenter - as specified in your description)
$(".unit").mouseenter(function() {
$(this).css("background-color", "black");
$(this).unbind('mouseenter');
});
jQuery mouseenter documentation for reference: https://api.jquery.com/mouseenter/#mouseenter-handler
Updated (and simplified for the sake of demonstration) fiddle: https://jsfiddle.net/eemLsjg7/9/

show/hide div on click with JS and introduction

I'm fairly new to the front-end web developing sphere and I have only studied HTML/CSS for ~ a month and a half and just about 1 week or less into JS. Since I want to practice what I learn from different websites I make my own to test my knowledge. I want to apologize in advance if I am asking too many questions, but there aren't any people I know that I can share coding issues with.
So I wanted to make ( just for testing ) a show/hide div which is activated when you click a button with JS. I can make it show, but I wanted to try to make it hide/show with an "if/else" function. I thought my code was right but it doesn't seem to work and I can't find a solution. I'll share with you my code ( the part of it which I have problems with actually) and will be very grateful if you can help me find a solution.
HTML :
<button type="button" onclick="slide()" >Click Me</button>
<div class="divtest" id="dropdown">
<span>If you are seeing this, then your JS worked! </span>
</div>
The CSS ( some things are pointless, I just added them in to test a bit ):
.divtest {
position: absolute;
left: 25%;
bottom: 30px;
width: 50%;
height: 100px;
border: 2px solid black;
box-sizing: border-box;
box-shadow: 5px 5px 10px 3px;
text-align: center;
padding: 40px;
margin: 0 auto;
font-family: Cooper, sans-serif;
font-weight: 100;
transition: 1s ease;
background-color: limegreen;
color: black;
display: none;
}
button {
position: absolute;
width: 50%;
left: 25%;
bottom: 130px;
padding: 10px;
border: 2px solid black;
background-color: limegreen;
box-shadow: 5px 5px 50px 3px;
color: black;
cursor: pointer;
font-family: Cooper, sans-serif;
font-weight: 100;
}
the JS:
<script>
function slide() {
var drop = document.getElementById("dropdown"); // declares the element with id="dropdown" as a var
var dropSetting = drop.style.display; // declares that the variable "drop"'s display property is also a variable
if (dropSetting == "none") { // if the current value of display = "none" ( which it is as u can see in the CSS)
dropSetting == "block"; // Then set it to "block"
}
else { // if the value of display != none
dropSetting == "none"; // then set it to "none"
}
}
</script>
If you have any questions towards the code or anything, please feel free to ask as this is a separate feature contained in my test website so it is not connected in any way to other elements/attributes. I tried this code first in another way (without declaring dropSetting as a var, just adding in a few lines in the if/else function ) but it still did not work.I don't think JS recognizes the "style.display" as a property because Brackets doesn't highlight it. Thank you very much for your time in advance, and I hope that soon I too will be able to help some people out with what I know!
Also - a side question - What are your thoughts on treehouse? I have heard very good things about them and I'm thinking about signing up to further my knowledge.
Have a nice day!
For code compatibility, try to use methods, no shortcuts, for attributes use:
var drop = document.getElementById("dropdown");
drop.setAttribute('style', 'display: block');
var display = drop.getAttribute('display'); //Here is all the inline css, better do like below:
And it is much better for a clean and faster code, make all css clases you need:
.hide{
display:none;
}
JS:
drop.classList.add('hide');
drop.classList.remove('hide');
drop.classList.toggle('hide');
Never used Treehouse, but for myself the best teacher is a good IDE for web like Atom of VScode, google Chrome console (F12), and those are your books:
-http://www.w3schools.com/js/js_examples.asp
-https://developer.mozilla.org/en-US/docs/Web/API/Element
And this is your teacher for questions:
stackoverflow.com
You dont need anything more.
PD: Your logic is ok, just don't get used to code shortcuts, they may not work in all environments. (like elem.attribute instead of elem.getAttribute(''))
Your code doesn't works because you didn't assign display property to appropriate value (block / none). you have used comparison operator("==") instead of equals ("=").
if (dropSetting == "none") { // if the current value of display = "none" ( which it is as u can see in the CSS)
dropSetting = "block"; // Then set it to "block"
}
else { // if the value of display != none
dropSetting = "none"; // then set it to "none"
}
"==" operator in your code will just return true/false without changing display property of the div.
The problem is that your dropSetting is not an object, just string. When you change it (if change) you don't change the object (style in this case). Try something like this:
var drop = document.getElementById("dropdown"); //get reference to the object
drop.style.display = drop.style.display == 'none' ? 'block' : 'none'; //if(...){do something}else{do something else}
Another possibility:
var dropStyle = document.getElementById("dropdown").style;
if(dropStyle.display == 'none'){
dropStyle.display = 'block';
}else
dropStyle.display = 'none';

How do I make an ease function for animation in javascript?

I know there is already a function in jquery that allows you to animate. But, I want to know how make an element ease into a position I want it to go with javascript. I have a simple function where if you click on a button a div will appear and it will slide down a little bit. The problem is that the animation of the sliding down is a bit choppy sometimes, but other times its fine. Is there a way to make the animation smooth everytime I click the button?
Snippet
document.querySelector('div#ex2 button').onclick = function() {
var counter;
// shows the div
document.querySelector('div#reveal').style.display = "block";
// used to decrease bottom position
var dec = 20;
counter = setInterval(moveD, 10);
// moves the div down
function moveD() {
dec -= 2;
document.getElementById('reveal').style.bottom = dec + "px";
if (dec < 1) {
clearInterval(counter);
log('function is done');
document.getElementById('reveal').style.bottom = 0;
} // moveD if
} // function moveD
}; // onclick function
div#reveal {
height: 100px;
width: 300px;
background: purple;
padding: 5px;
position: relative;
bottom: 20px;
display: none;
color: white;
text-align: center;
vertical-align: center;
margin: 10px 0;
}
<div id="ex2">
<h2>Example 2</h2>
<p></p>
<h4>results:</h4>
<button>Click it</button>
<div id="reveal">This will be revealed</div>
</div>
I think you can solve your issue just by using CSS animation honestly.
Keep in mind, you should always use CSS to do something instead of javascript if at any way possible.
w3schools link
Specifically (you can scrolldown a bit) there is an animation-timing-function which you would be interested in.
Check out all these easing functions: https://github.com/danro/jquery-easing/blob/master/jquery.easing.js.
If you want to see them in use check out my animation library at http://nanimation.js.org/.
Check this for more information on the functions and their parameters: jQuery easing function — variables' comprehension.

Categories