Hi i been trying to change some divs style with javascript and it changes the style but if the divs display is changed to none and back to block it resets the style? i tried to fix this with important but that didn't seem to work? any ideas why this happens or if there is a better way i could achieve what i'm trying to do?
var Style = document.querySelectorAll('#mainui-features, #mainui-modes, #mainui-offers, #mainui-party, #mainui-play, #mainui-user, #mainui-settings');
for (var i = 0; i < Style.length; i++) {
Style[i].style.borderRadius = '1em'; // standard
Style[i].style.MozBorderRadius = '1em'; // Mozilla
Style[i].style.WebkitBorderRadius = '1em'; // WebKitww
Style[i].style.color = "#D35400";
Style[i].style.border = "2px solid #D35400";
Style[i].style.setProperty("background-image", "linear-gradient(to right, #92FE9D, #00C9FF)", "important");
}
Instead of inline styles you can a class
var Style = document.querySelectorAll('#mainui-features, #mainui-modes, #mainui-offers, #mainui-party, #mainui-play, #mainui-user, #mainui-settings');
for (var i = 0; i < Style.length; i++) {
Style[i].classList.add('styles')
}
function hide() {
for (var i = 0; i < Style.length; i++) {
Style[i].classList.add('hide')
}
}
function show() {
for (var i = 0; i < Style.length; i++) {
Style[i].classList.remove('hide')
}
}
.styles {
border-radius: 1em;
-moz-border-radius: 1em;
color: #D35400;
border: 2px solid #D35400;
background-image: linear-gradient(to right, #92FE9D, #00C9FF)
}
.hide {
display: none
}
<div id="mainui-features">1</div>
<div id="mainui-modes">2</div>
<div id="mainui-offers">3</div>
<div id="mainui-party">4</div>
<button onclick="hide()">Hide</button>
<button onclick="show()">Show</button>
Not sure what you're doing wrong, but the way you describe your requirements works without any problem. Maybe this helps in some way.
I didn't include all the IDs.
// Store all elements in an array
const elements = [...document.querySelectorAll("#mainui-features, #mainui-modes, #mainui-offers, #mainui-party")];
// Assign CSS values to each element in the array
elements.map( el => {
el.style.borderRadius = "1em";
el.style.color = "#D35400";
el.style.border = "2px solid #D35400";
el.style.backgroundImage = "linear-gradient(to right, #92FE9D, #00C9FF)";
});
// Assign display:none to each element
elements.map( el => el.style.display = "none" );
// And make them display:block again
elements.map( el => el.style.display = "block" );
div {
height: 1rem;
width: 3rem;
}
<div id="mainui-features">1</div>
<div id="mainui-modes">2</div>
<div id="mainui-offers">3</div>
<div id="mainui-party">4</div>
Related
I have a button with a default background color(black). I want to change the background color (from an array) of this button when hovering over it. I have it working at a fundamental level, but I want it to repeat the loop over and over.
this is what i have so far.
var color = ['#3e50a2', '#faa51a', '#ed1c24', '#2a9446'];
var i = -1;
document.querySelector('.customBtn').addEventListener('mouseover', function() {
i = 1 < color.length ? ++i : 0;
document.querySelector('.customBtn').style.background = color[i]
});
document.querySelector('.customBtn').addEventListener('mouseout', function() {
document.querySelector('.customBtn').style.background = '#000';
})
<a class="customBtn">Button</a>
I think you tried to reset the value of i with this i = 1 < color.length ? ++i : 0; but it doesn't. This will continuously increase the value never resetting it because 1 is always less than the length of the array.
I think you meant to increment the value and then reset if it's too big:
i = ++i < color.length ? i : 0;
Here's the complete code. I've refactored the query selector as there's no point to doing it more than once, and changed the mouseout background colour so you can read the button
var color = ['#3e50a2', '#faa51a', '#ed1c24', '#2a9446'];
var i = -1,
btn = document.querySelector('.customBtn');
btn.addEventListener('mouseover', function() {
i = ++i < color.length ? i : 0;
btn.style.background = color[i];
});
btn.addEventListener('mouseout', function() {
// revert to default colour
btn.style.background = '';
})
<input type="button" class="customBtn" value="My button" />
Just replace i = 1 < color.length ? ++i : 0; with i = (i+1 < color.length) ? ++i : 0;. That's all.
var color = ['#3e50a2', '#faa51a', '#ed1c24', '#2a9446'];
var i = -1;
document.querySelector('.customBtn').addEventListener('mouseover', function() {
i = (i+1 < color.length) ? ++i : 0;
document.querySelector('.customBtn').style.background = color[i]
});
document.querySelector('.customBtn').addEventListener('mouseout', function() {
document.querySelector('.customBtn').style.background = '#000';
})
<a class="customBtn">Button</a>
If you must use javascript, take a look at the second example.
Pure CSS solution
You could accomplish this with a CSS animation, which would be both more efficient and less error prone. Unless there's a specific reason you need to use javascript here I'd strongly recommend this approach.
This example could be modified to do hard transitions instead of fading from one color to the next, but here's a quick demo:
button {
background: black;
color: white;
border: none;
padding: 0.5em 1em;
}
button:hover {
animation: buttonhover 1s infinite;
}
#keyframes buttonhover {
0% {
background: #3e50a2;
}
25% {
background: #faa51a;
}
50% {
background: #ed1c24;
}
75% {
background: #2a9446;
}
}
<button>Hello</button>
Javascript Solution
If you must use javascript for whatever reason, you can use the % operator to keep from running off the end of the colors array:
const colors = ['#3e50a2', '#faa51a', '#ed1c24', '#2a9446'];
let index = 0;
let interval;
const hover = (e) => {
interval = setInterval(() => {
e.target.style.backgroundColor = colors[index];
index = (index + 1) % colors.length;
}, 300);
}
document.querySelector('button').addEventListener('mouseover', hover);
document.querySelector('button').addEventListener('mouseout', (e) => {
clearInterval(interval);
e.target.style.backgroundColor = 'black';
});
button {
background: black;
color: white;
border: none;
padding: 0.5em 1em;
}
<button>Hello</button>
I can find examples using jQuery, but I can't find answers how to do this in pure JavaScript.
Is it possible to combine these two class loops using JavaScript to simplify the code. I am going to end up with approx 20 classes for a project I am working on.
var srtClass1 = document.getElementsByClassName('class1');
for(var i = 0; i < srtClass1.length; i++) {
srtClass1[i].classList.add('newClass');
srtClass1[i].classList.remove('class1');
}
var srtClass2 = document.getElementsByClassName('class2');
for(var i = 0; i < srtClass2.length; i++) {
srtClass2[i].classList.add('newClass');
srtClass2[i].classList.remove('class2');
}
You could try something like this. You just have to fill the array with all of the classes you want to remove.
var classes = ['.class1', '.class2', '.class3'];
var elements = document.querySelectorAll(classes.join(','));
for (let element of elements) {
element.classList.add('newClass');
for (let className in classes) {
element.classList.remove(className);
}
}
.box {
display: block;
width: 100%;
height: 20px;
margin: 0 0 20px 0;
}
.class1 { background: red; }
.class2 { background: green; }
.class3 { background: blue; }
.newClass { background: yellow; }
<div class="box class1"></div>
<div class="box class2"></div>
<div class="box class3"></div>
You can merge the 2 collections:
var srtClass1 = [...document.getElementsByClassName('class1'), ...document.getElementsByClassName('class2')];
for(var i = 0; i < srtClass1.length; i++) {
srtClass1[i].classList.add('newClass');
srtClass1[i].classList.remove('class1');
srtClass1[i].classList.remove('class2');
}
And you can use higher order functions:
[...document.getElementsByClassName('class1'), ...document.getElementsByClassName('class2')].forEach( el => {
el.classList.add('newClass');
el.classList.remove('class1');
el.classList.remove('class2');
});
And even better (since classList.remove supports multiple arguments):
[...document.getElementsByClassName('class1'), ...document.getElementsByClassName('class2')].forEach( el => {
el.classList.add('newClass');
el.classList.remove('class1', 'class2');
});
Or if you want you can use querySelectorAll:
document.querySelectorAll('.class1, .class2').forEach(el => {
el.classList.add('newClass');
el.classList.remove('class1', 'class2');
});
Perhaps try making a list of the classes, then iterating over that instead:
function changeClasses() {
let classNames = ['class1', 'class2'];
classNames.forEach(className => {
const node = document.querySelector(`.${className}`);
if (node != null) {
node.classList.add('newClass');
node.classList.remove(className);
}
});
}
.class1 {
color: green;
}
.class2 {
color: blue;
}
.newClass {
color: red;
}
<body>
<div class="class1">class1</div>
<div class="class2">class2</div>
<button onclick="changeClasses()">Change classes</button>
</body>
I have a 16x16 grid of small squares. I have added a permanent "hover" effect to make the very first box turn red when I put my mouse over it. However, I want to add the same effect to all of the boxes on the page. I can't figure out how to do it - I have tried to add an event listener to the whole page and used target.nodeName and target.NodeValue, but to no avail. I have included the working version where the fix box turns red on mouseover.
var n=16; //take grid column value as you want
const bigContainer = document.querySelector('.bigContainer')
for(var i = 1; i < n; i++) {
bigContainer.innerHTML+='<div class="row">';
for(j = 0; j < n; j++) {
bigContainer.innerHTML+='<div class="smallBox">';
}
}
const smallBox = document.querySelector('.smallBox');
smallBox.addEventListener('mouseover', () => {
smallBox.classList.add('permahover');
});
.smallBox {
border: 1px solid black;
width: 20px;
height: 20px;
display: inline-block;
}
.permahover {
background: red;
}
h1 {
text-align: center;
}
.bigContainer {
text-align: center;
}
<h1>Etch-a-Sketch Assignment - The Odin Project</h1>
<div class="bigContainer">
</div>
The immediate problem you are having is that this is only querying, and subsequently adding an event listener to, one element.
const smallBox = document.querySelector('.smallBox');
smallBox.addEventListener('mouseover', () => {
smallBox.classList.add('permahover');
});
In the above portion of your code, querySelector only returns the first matching element. You may be looking for querySelectorAll here which returns a NodeList of matching elements.
You have two options (perhaps others if you want to restructure your code further). The naive approach is to, in fact, query for all of the cells and add event listeners to each of them.
var n=16; //take grid column value as you want
const bigContainer = document.querySelector('.bigContainer')
for(var i = 1; i < n; i++) {
bigContainer.innerHTML+='<div class="row">';
for(j = 0; j < n; j++) {
bigContainer.innerHTML+='<div class="smallBox">';
}
}
const smallBoxes = document.querySelectorAll('.smallBox');
[...smallBoxes].forEach(smallBox => {
smallBox.addEventListener('mouseover', () => {
smallBox.classList.add('permahover');
});
})
.smallBox {
border: 1px solid black;
width: 20px;
height: 20px;
display: inline-block;
}
.permahover {
background: red;
}
h1 {
text-align: center;
}
.bigContainer {
text-align: center;
}
<h1>Etch-a-Sketch Assignment - The Odin Project</h1>
<div class="bigContainer">
</div>
Another option is to use event delegation as you identified. Here is how you can leverage that. Note: this approach is a bit tricker for an aggressive event like "mouseover" as you may get false positive targets (like the outer container for example).
var n=16; //take grid column value as you want
const bigContainer = document.querySelector('.bigContainer')
for(var i = 1; i < n; i++) {
bigContainer.innerHTML+='<div class="row">';
for(j = 0; j < n; j++) {
bigContainer.innerHTML+='<div class="smallBox">';
}
}
bigContainer.addEventListener('mouseover', e => {
var target = e.target
if (target !== bigContainer) {
target.classList.add('permahover')
}
})
.smallBox {
border: 1px solid black;
width: 20px;
height: 20px;
display: inline-block;
}
.permahover {
background: red;
}
h1 {
text-align: center;
}
.bigContainer {
text-align: center;
}
<h1>Etch-a-Sketch Assignment - The Odin Project</h1>
<div class="bigContainer">
</div>
You need to use a delegation event, because all the small boxes don't exist on the page when the page is loaded (You can figure out in the inspector element that only your first box has the event listener).
So you listen the whole container (because it is always on the page on load)
bigContainer.addEventListener('mouseover', () => {
// Code for checking if we hovered a small div & if yes applying the style
});
...and then do a comparaison with the event.target (which will be the small div hovered)
if (event.target.matches('.smallBox')) {
event.target.classList.add('permahover');
}
var n=16; //take grid column value as you want
const bigContainer = document.querySelector('.bigContainer')
for(var i = 1; i < n; i++) {
bigContainer.innerHTML+='<div class="row">';
for(j = 0; j < n; j++) {
bigContainer.innerHTML+='<div class="smallBox">';
}
}
const smallBox = document.querySelector('.smallBox');
bigContainer.addEventListener('mouseover', () => {
if (event.target.matches('.smallBox')) {
event.target.classList.add('permahover');
}
});
.smallBox {
border: 1px solid black;
width: 20px;
height: 20px;
display: inline-block;
}
.permahover {
background: red;
}
h1 {
text-align: center;
}
.bigContainer {
text-align: center;
}
<h1>Etch-a-Sketch Assignment - The Odin Project</h1>
<div class="bigContainer">
</div>
You can use forEach method to loop through all boxes and add eventListener on each one.
If all of them have .smallBox class you can do it like this:
const smallBoxes = document.querySelectorAll('.smallBox');
smallBoxes.forEach(box => box.addEventListener('mouseover', () => {
smallBox.classList.add('permahover');
}))
I hope it helped you!
let smallBoxes = document.querySelectorAll('.smallBox');
[...smallBoxes].forEach(el => {
el.addEventListener('mouseover', e => e.target.classList.add('permahover'));
});
you should set the eventlistener to your DOM and ask if the trigger element are one of your elements which are that specific class. So you can handle every element with that class.
var n = 16; //take grid column value as you want
const bigContainer = document.querySelector('.bigContainer')
for (var i = 1; i < n; i++) {
bigContainer.innerHTML += '<div class="row">';
for (j = 0; j < n; j++) {
bigContainer.innerHTML += '<div class="smallBox">';
}
}
document.addEventListener('mouseover', function(e) {
if (e.target && e.target.className == 'smallBox') {
var target = e.target;
target.classList.add('permahover');
}
});
Working js fiddle: https://jsfiddle.net/nwukf205/
hope i could help you :)
if you got questions just ask
Have you tried the :hover selector? Not sure if you want specify any dynamic actions here, but it's easy to do basic stuff.
https://www.w3schools.com/cssref/sel_hover.asp
a:hover {
background-color: yellow;
}
I haven't tried your example myself but something similar to this has been answered here:
Hover on element and highlight all elements with the same class
PURE JS ONLY PLEASE - NO JQUERY
I have a div with overflow scroll, the window (html/body) never overflows itself.
I have a list of anchor links and want to scroll to a position when they're clicked.
Basically just looking for anchor scrolling from within a div, not window.
window.scrollTo etc. don't work as the window never actually overflows.
Simple test case http://codepen.io/mildrenben/pen/RPyzqm
JADE
nav
a(data-goto="#1") 1
a(data-goto="#2") 2
a(data-goto="#3") 3
a(data-goto="#4") 4
a(data-goto="#5") 5
a(data-goto="#6") 6
main
p(data-id="1") 1
p(data-id="2") 2
p(data-id="3") 3
p(data-id="4") 4
p(data-id="5") 5
p(data-id="6") 6
SCSS
html, body {
width: 100%;
height: 100%;
max-height: 100%;
}
main {
height: 100%;
max-height: 100%;
overflow: scroll;
width: 500px;
}
nav {
background: red;
color: white;
position: fixed;
width: 50%;
left: 50%;
}
a {
color: white;
cursor: pointer;
display: block;
padding: 10px 20px;
&:hover {
background: lighten(red, 20%);
}
}
p {
width: 400px;
height: 400px;
border: solid 2px green;
padding: 30px;
}
JS
var links = document.querySelectorAll('a'),
paras = document.querySelectorAll('p'),
main = document.querySelector('main');
for (var i = 0; i < links.length; i++) {
links[i].addEventListener('click', function(){
var linkID = this.getAttribute('data-goto').slice(1);
for (var j = 0; j < links.length; j++) {
if(linkID === paras[j].getAttribute('data-id')) {
window.scrollTo(0, paras[j].offsetTop);
}
}
})
}
PURE JS ONLY PLEASE - NO JQUERY
What you want is to set the scrollTop property on the <main> element.
var nav = document.querySelector('nav'),
main = document.querySelector('main');
nav.addEventListener('click', function(event){
var linkID,
scrollTarget;
if (event.target.tagName.toUpperCase() === "A") {
linkID = event.target.dataset.goto.slice(1);
scrollTarget = main.querySelector('[data-id="' + linkID + '"]');
main.scrollTop = scrollTarget.offsetTop;
}
});
You'll notice a couple of other things I did different:
I used event delegation so I only had to attach one event to the nav element which will more efficiently handle clicks on any of the links.
Likewise, instead of looping through all the p elements, I selected the one I wanted using an attribute selector
This is not only more efficient and scalable, it also produces shorter, easier to maintain code.
This code will just jump to the element, for an animated scroll, you would need to write a function that incrementally updates scrollTop after small delays using setTimeout.
var nav = document.querySelector('nav'),
main = document.querySelector('main'),
scrollElementTo = (function () {
var timerId;
return function (scrollWithin, scrollTo, pixelsPerSecond) {
scrollWithin.scrollTop = scrollWithin.scrollTop || 0;
var pixelsPerTick = pixelsPerSecond / 100,
destY = scrollTo.offsetTop,
direction = scrollWithin.scrollTop < destY ? 1 : -1,
doTick = function () {
var distLeft = Math.abs(scrollWithin.scrollTop - destY),
moveBy = Math.min(pixelsPerTick, distLeft);
scrollWithin.scrollTop += moveBy * direction;
if (distLeft > 0) {
timerId = setTimeout(doTick, 10);
}
};
clearTimeout(timerId);
doTick();
};
}());
nav.addEventListener('click', function(event) {
var linkID,
scrollTarget;
if (event.target.tagName.toUpperCase() === "A") {
linkID = event.target.dataset.goto.slice(1);
scrollTarget = main.querySelector('[data-id="' + linkID + '"]');
scrollElementTo(main, scrollTarget, 500);
}
});
Another problem you might have with the event delegation is that if the a elements contain child elements and a child element is clicked on, it will be the target of the event instead of the a tag itself. You can work around that with something like the getParentAnchor function I wrote here.
I hope I understand the problem correctly now: You have markup that you can't change (as it's generated by some means you have no control over) and want to use JS to add functionality to the generated menu items.
My suggestion would be to add id and href attributes to the targets and menu items respectively, like so:
var links = document.querySelectorAll('a'),
paras = document.querySelectorAll('p');
for (var i = 0; i < links.length; i++) {
links[i].href=links[i].getAttribute('data-goto');
}
for (var i = 0; i < paras.length; i++) {
paras[i].id=paras[i].getAttribute('data-id');
}
I am trying to make a chessboard using javascript and creating 64 divs with it.
The problem is, that it creates only the first div.
Here is the code:
div {
width: 50px;
height: 50px;
display: block;
position: relative;
float: left;
}
<script type="text/javascript">
window.onload=function()
{
var i=0;
var j=0;
var d=document.createElement("div");
for (i=1; i<=8; i++)
{
for (j=1; j<=8; j++)
{
if ((i%2!=0 && j%2==0)||(i%2==0 && j%2!=0))
{
document.body.appendChild(d);
d.className="black";
}
else
{
document.body.appendChild(d);
d.className="white";
}
}
}
}
</script>
As t-j-crowder has noted, the OP's code only creates one div. But, for googlers, there is one way to append multiple elements with a single appendChild in the DOM: by creating a documentFragment.
function createDiv(text) {
var div = document.createElement("div");
div.appendChild(document.createTextNode(text));
return div;
}
var divs = [
createDiv("foo"),
createDiv("bar"),
createDiv("baz")
];
var docFrag = document.createDocumentFragment();
for(var i = 0; i < divs.length; i++) {
docFrag.appendChild(divs[i]); // Note that this does NOT go to the DOM
}
document.body.appendChild(docFrag); // Appends all divs at once
The problem is, that it creates only the first div.
Right, because you've only created one div. If you want to create more than one, you must call createElement more than once. Move your
d=document.createElement("div");
line into the j loop.
If you call appendChild passing in an element that's already in the DOM, it's moved, not copied.
window.onload=function()
{
var i=0;
var j=0;
for (i=1; i<=8; i++)
{
for (j=1; j<=8; j++)
{
if ((i%2!=0 && j%2==0)||(i%2==0 && j%2!=0))
{
var d=document.createElement("div");
document.body.appendChild(d);
d.className="black";
}
else
{
var d=document.createElement("div");
document.body.appendChild(d);
d.className="white";
}
}
}
}
Although what T.J. Crowder writes works fine, I would recommend rewriting it to the code below, using a documentFragment, like Renato Zannon suggested. That way you will only write to the DOM once.
window.onload = function() {
var count = 5,
div,
board = document.getElementById('board'),
fragment = document.createDocumentFragment();
// rows
for (var i = 0; i < count; ++i) {
// columns
for (var j = 0; j < count; ++j) {
div = document.createElement('div');
div.className = (i % 2 != 0 && j % 2 == 0) || (i % 2 == 0 && j % 2 != 0) ? 'black' : 'white';
fragment.appendChild(div);
}
}
board.appendChild(fragment);
};
#board {
background-color: #ccc;
height: 510px;
padding: 1px;
width: 510px;
}
.black,
.white {
float: left;
height: 100px;
margin: 1px;
width: 100px;
}
.black {
background-color: #333;
}
.white {
background-color: #efefef;
}
<div id="board"></div>
function crt_dv(){
dv=document.createElement('div'),document.body.appendChild(dv)
};
crt_dv(),dv.className='white';crt_dv(),dv.className='black';
Also use: for(i=0;i<2;i++)