I have an array, store, containing three images. I'm trying to display each image from this array in ascending order on each button click.
ie: first there should not be any image, on first click of the button the first image of the array should load on class level.
How can I achieve this?
function store()
{
var level=['https://via.placeholder.com/75x75?text=1','https://via.placeholder.com/75x75?text=2','https://via.placeholder.com/75x75?text=3'];
}
<div class="level" style=" width=100px; height:100px; border:2px solid #000;">
<img src="" id="levelimage" style=" width=100px; height:100px;"/>
</div>
<button onclick="store()">Click me</button>
I'm a bit confused of what you're asking but I think this will do the trick:
var i = 0;
function store() {
var level = ['https://via.placeholder.com/75x75text=1','https://via.placeholder.com/75x75?text=2','https://via.placeholder.com/75x75?text=3']
document.querySelector("img").src=level[i++];
if (i>level.length-1)i=0;
}
<div class="level" style=" width=100px; height:100px; border:2px solid #000;">
<img src="" id="levelimage" style=" width=100px; height:100px;"/>
</div>
<button onclick="store()">Click me</button>
Introduce a global variable i that will stay consistent no matter which function call was it and increase it by one every click, also use DOM to select your img tag and change it's src
var i=0;
function store() {
var level=['https://via.placeholder.com/75x75?text=1','https://via.placeholder.com/75x75?text=2','https://via.placeholder.com/75x75?text=3'];
document.querySelector(".level").firstChild.src=level[i];
i+=1;
}
// A named function, which expects two arguments:
// e: the Event Object, passed automatically from
// EventTarget.addEventListener();
// haystack: the array of images.
function imageProgress(e, haystack) {
// finding the relevant image by navigating the DOM:
// e.target gives the element that initiated the event,
// previousElementSibling finds the previous sibling that is
// an element, Element.querySelector() finds the first (if any)
// element that matches the supplied CSS selector:
let target = e.target.previousElementSibling.querySelector('img'),
// finding the current image:
currentImage = target.src,
// finding the index of the current image within the
// array of images (this returns -1 if the image
// is not found):
currentIndex = haystack.indexOf(currentImage);
// updating the src property of the <img> element;
// we first increment the currentIndex variable, and
// divide that updated index by the length of the Array.
// if there is no current image shown (or the src can't be
// found in the Array) we start at the first array-index;
// otherwise we increment through the Array:
target.src = haystack[++currentIndex % haystack.length];
}
let images = ['https://via.placeholder.com/75x75?text=1', 'https://via.placeholder.com/75x75?text=2', 'https://via.placeholder.com/75x75?text=3'],
// finding the <button> element:
button = document.querySelector('button');
// using unobtrusive JavaScript to bind the event-handler,
// using an arrow function:
button.addEventListener('click', (e) => {
imageProgress(e, images)
});
*,
::before,
::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.level {
width: 100px;
height: 100px;
border: 2px solid #000;
display: flex;
}
.level img {
margin: auto;
}
.level,
button {
margin: 1em;
}
<div class="level">
<img src="" id="levelimage" />
</div>
<button>Click me</button>
JS Fiddle demo.
References:
Arrow functions.
document.querySelector().
Element.querySelector().
EventTarget.addEventListener().
NonDocumentTypeChildNode.previousElementSibling.
Related
I am developing a website which has a few filter buttons which are grouped into several groups. I am trying to find a way to set the class of one of these buttons to "filter-set" while all other buttons in the group are set to "not-set".
Each button is a DIV with a unique ID.
i have some bloated code where each button has its own function and sets the associated buttons to "not-set" but this seems inefficient and im sure there's a better way!
Bloated code example:
function setClassR(){
document.getElementById('filter_rare').className= 'filter-set';
document.getElementById('filter_common').className= 'not-set';
document.getElementById("filter_occasional").className = 'not-set';
}
function setClassC(){
document.getElementById('filter_rare').className= 'not-set';
document.getElementById('filter_common').className= 'filter-set';
document.getElementById("filter_occasional").className = 'not-set';
}
function setClassO(){
document.getElementById('filter_rare').className= 'not-set';
document.getElementById('filter_common').className= 'not-set';
document.getElementById("filter_occasional").className = 'filter-set';
}
I would like to be able to have a function for each group of filters which when run using an onClick=function() sets the clicked button to "filter-set" and all others to "not-set"
I have tried the following code but it doesnt appear to run:
function setClassSeas(rareClass, commonClass, occClass) {
setClass("filter_rare", rareClass);
setClass("filter_common", commonClass);
setClass("filter_occ", occClass);
}
function setClass(IDName, displayValue) {
var items = document.getElementById(IDName);
for (var i=0; i < items.length; i++) {
items[i].className = (displayValue? "filter-set" : "not-set");
}
}
UPDATE///
HTML Code for the Divs acting as buttons:
<div id="filter_rare" title="Rare"
class="not-set"
onclick="chosenFrequency('frequency=Rare'); setClassR();"></div>
<div id="filter_common" title="Common"
class="not-set"
onclick="chosenFrequency('frequency=Common'); setClassC();"></div>
<div id="filter_occasional" title="Occasional"
class="not-set"
onclick="chosenFrequency('frequency=Occasional'); setClassO();"></div>
If every button has a class, say filter-button, then you can address all buttons at once.
In modern development you should attach an event handler instead of using inline onclick handlers.
With all buttons having a common class you can find them all at once. I'm changing your buttons to look like this, adding the "filter-button" class and removing the onclick handler:
<div id="filter_rare" title="Rare"
class="filter-button not-set">Rare</div>
(I've put text in the div just to simplify this demonstration)
Now collect all the filter buttons:
let filters = document.querySelectorAll('div.filter-button');
This gets you a NodeList of elements (kind of like an Array but not one) You'll want to attach an onclick event handler to each of the buttons. To do this you can use the NodeList.forEach() call.
filters.forEach(node => node.addEventListener('click', someFunction));
In the function that gets called when you click a button, you want to clear any filter-set class that's currently set, put back the original not-set class, then set the filter-set class only on the button that was clicked. This will look something like this:
function someFunction(event) {
// again, use forEach to do the same thing to each filter button
filters.forEach( function(node) {
node.classList.remove('filter-set');
node.classList.add('not-set');
} );
// now add the 'filter-set' class on the button that was clicked
event.target.classList.add('filter-set');
}
The good thing about using classList instead of just doing className="something" is that classList can add/remove classes while leaving other classes alone; doing className="something" wipes out all the classes that are present and replaces them with "something".
Putting that all together, and using an anonymous function instead of named function gives this snippet:
let filters = document.querySelectorAll('div.filter-button');
filters.forEach(node => node.addEventListener('click',
function(event) {
console.log(event.target);
filters.forEach(function(node) {
node.classList.remove('filter-set');
node.classList.add('not-set');
});
event.target.classList.add('filter-set');
}));
/* Make these look like buttons; put a green border on them */
.filter-button {
min-height: 2ex;
max-width: 12em;
padding: .25em;
margin: .7em .3em;
background-color: lightgreen;
border: 2px solid green;
border-radius: 4px;
}
/* use a Red border on any button that has "filter-set" */
.filter-button.filter-set {
border: 2px solid red;
}
/* limit the height of the stack-snippet console */
div.as-console-wrapper {
max-height: 2.5em;
}
<div id="filter_rare" title="Rare"
class="filter-button not-set">Rare</div>
<div id="filter_common" title="Common"
class="filter-button not-set">Common</div>
<div id="filter_occasional" title="Occasional"
class="filter-button not-set">Occasional</div>
Using the class not-set is really redundant — you could just have no extra class on buttons by default and it would simplify things a little. Buttons would have the class(es) filter-button or filter-button filter-set.
Change your setClass function according to this. Hope it will work. document.getElementById() function will always return a single element (not a list of elements). Even if you have multiple elements having the same ID this function will always return the first element having the given ID. Do not forget to call your setClassSeas() function from html.
function setClassSeas(rareClass, commonClass, occClass) {
setClass("filter_rare", rareClass);
setClass("filter_common", commonClass);
setClass("filter_occ", occClass);
}
function setClass(IDName, displayValue) {
var item = document.getElementById(IDName);
item.className = displayValue ? "filter-set" : "not-set";
}
<div id="filter_rare" title="Rare" class="not-set"
onclick="chosenFrequency('frequency=Rare'); setClassSeas(true, false, false);"></div>
<div id="filter_common" title="Common" class="not-set"
onclick="chosenFrequency('frequency=Common'); setClassSeas(false, true, false);"></div>
<div id="filter_occasional" title="Occasional" class="not-set"
onclick="chosenFrequency('frequency=Occasional'); setClassSeas(false, false, true);"></div>
Here is another (as in "alternative") way to do it (but with jQuery, oh no!)
$('body').click(function(e) {
let $clicked = $(e.target);
console.log("Clicked " + $clicked.attr('id'))
if ($clicked.hasClass('filter')) {
let $filters = $clicked.closest('.filter-group').find('.filter');
let unset = $clicked.hasClass('set');
$filters.toggleClass('not-set', true);
$filters.toggleClass('set', false);
if (!unset) {
$clicked.toggleClass('not-set', false);
$clicked.toggleClass('set', true);
}
}
})
button.filter.not-set {
background: white;
}
button.filter.set {
color: white;
background: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="filter-group">
<button id="filter_rare" class="filter not-set">
filter_rare
</button>
<button id="filter_common" class="filter not-set">
filter_common
</button>
<button id="filter_occasional" class="filter not-set">
filter_occasional
</button>
</div>
<div class="filter-group">
<button id="filter_one" class="filter not-set">
filter_one
</button>
<button id="filter_two" class="filter not-set">
filter_two
</button>
<button id="filter_three" class="filter not-set">
filter_three
</button>
</div>
I have a image that is currently being styled with Jquery once it's clicked. I eventually hide it in Javascript. I want to reshow it, but I want it to have the border removed.
Here is HTML:
<div id="playOptionsWager" style="display: none">
<h4>Choose your move to beat the computer!</h4>
<img id="clickedRockWager" src="img/rock.jpg" onclick="playWagerRock()" />
<img id="clickedPaperWager" src="img/paper.jpg" onclick="playWagerPaper()"/>
<img id="clickedScissorsWager" src="img/scissors.jpg" onclick="playWagerScissors()" />
</div>
Jquery:
$(function () {
$("img").on("click",function() {
$(this).siblings().css('border','0px')
$(this).css('border', "solid 2px red");
});
});
Here is what I was trying in Javascript:
function autobet() {
coinBalance -= currentBet*2;
alert(getBalance());
document.getElementsByTagName("IMG").style.border="";
}
However when it reshows the div it has the border on it still.
Thanks for the help!
Your issue is that document.getElementsByTagName("IMG") returns a collection of elements, so simply applying .style.border on this collection won't work. Instead, you need to loop over this collection, and set every image within it to have no border using .style.border = 0;:
See working example (with div) below:
function removeBorder() {
[...document.getElementsByTagName("div")].forEach(elem => {
elem.style.border = 0;
});
}
.box {
height: 100px;
width: 100px;
background: black;
display: inline-block;
}
.active {
border: 3px solid red;
}
<div class="box"></div>
<div class="box active"></div>
<div class="box"></div>
<br />
<button onclick="removeBorder()">Remove border</button>
Also note that [...document.getElementsByTagName("IMG")] is a way of converting the collection of elements into an array of elements, which thus allows us to use the .forEach method to loop over it.
You started with jQuery, let's continue with jQuery.
function autobet() {
coinBalance -= currentBet*2;
alert(getBalance());
$("img").css("border","");
}
The problem is that getElementsByTagName() returns a collection not one element.
First you need to iterate over the collection of html elements you have - when using getElementsByTagName you get back an array of elements.
Second you need to give the elements a style of zero.
const divElements = document.getElementsByTagName("IMG");
for (let i=0; i < divElements.length; i++) {
divElements[i].style.border = 0;
}
You can see the code on stackbliz -
https://stackblitz.com/edit/border-issue?file=index.js
How would i go about making something like this - i have multiple elements with a certain class and i would like this to happen:
element1 - onclick fires someFunction(13);
element2 - onclick fires someFunction(27);
element3 - onclick fires someFunction(81);
i am loading these elements in dynamically so i can't put it manually into my js file. I also can't give them an onclick as i load them with php.
I am looking for a purely js answer so please no jQuery.
function setMyHandler(){
var elements = document.getElementsByClassName('someClass');
for(var i = 0; i < elements.length; i++){
elements[i].onclick = function(){};
}
}
But I would be better advised to use you event delegate. Set Handler on root element. And checking event.target. http://javascript.info/tutorial/event-delegation
Approach #1
In your PHP code, create your elements with onclick tags, with intended input into the function.
<div class="someclass" onclick="someFunction(1)"></div>
Approach #2
When the page loads, iterate through all elements with your given classname and attach listeners to them. For this approach to work, the divs must have the numbers which will be entered in SomeFunction included in arbitrary tags.
<div data="13" > =) </div>
window.onload = function() {
for (var i =0; i < document.getElementsByClassName("classname").; i++){
var Div = document.getElementsByClassName("classname")[i];
Div.addEventListener("click", function(){
someFunction(Div.data);
});
}
}
You can delegate events by attaching event listener to parent element. To get the target element you can use event.target
Here's the sample code how you can achieve it:
var parentElement = document.querySelector("#parent");
parentElement.addEventListener("click", function () {
var currentTarget = event.target;
if (currentTarget.tagName === "LI") { // If you want LI to be clickable
currentTarget.style.backgroundColor = "#eee";
currentTarget.style.color = "#606060";
}
});
Here's the jsfiddle for the same: https://jsfiddle.net/dx8Lye29/
The simplest way of doing this is giving the elements a data-attribute with the parameter for the function you want to run:
<div class="someclass" data-parameter="12">
<div class="someclass" data-parameter="13">
<div class="someclass" data-parameter="14">
and then run this:
function setMyHandler(){
var elements = document.getElementsByClassName('someclass');
for(var i = 0; i < elements.length; i++){
elements[i].onclick = function(){ myFunction(this.data.parameter); };
}
}
Here is a link for data attributes: data attributes
And here is a snipplet to see the simple version of it in action:
function setMyHandler(){
var elements = document.getElementsByClassName('someclass');
for(var i = 0; i < elements.length; i++){
elements[i].onclick = function(){window.alert(this.dataset.somevalue);};
}
}
setMyHandler();
body {
display: flex;
justify-content: center;
}
div.someclass {
margin: 2%;
width: 50px;
height: 50px;
border-radius: 50%;
cursor: pointer;
background: #80bfff;
box-shadow: 0px 2px 6px 0px rgba(0,0,0,0.7);
transition: background 0.3s ease;
}
div.someclass:hover {
background: #3399ff;
}
<div class="someclass" data-somevalue="13"> </div>
<div class="someclass" data-somevalue="14"> </div>
<div class="someclass" data-somevalue="15"> </div>
I appended a few divs with inside img tags. Every tag has own unique id = "theImg"+i where "i" is number. I want to mouseover on specific img and show the content of span (which also have specific id with number). Here is my code so far but not working.
var j;
document.onmouseover = function(r) {
console.log(r.target.id);
j = r.target.id;
}
$(document).on({
mouseover: function(e){
$("span").show();
},
mouseleave: function(e){
$("span").hide();
}
}, "img#"+j);
If you have a span after every img, maybe it's a good idea to not use JavaScript at all? ;-)
You could use :hover pseudoclass in CSS, making your thing always work reliably.
Consider the following example:
img + span {
display: none;
}
img:hover + span {
display: block;
}
/*/ Optional styles /*/
div {
position: relative;
float: left;
}
div img + span {
position: absolute;
color: #fff;
background: #27ae60;
border: solid 1px #2ecc71;
border-radius: 50px;
z-index: 1;
bottom: 1em;
width: 80%;
left: 50%;
margin-left: -43%;
padding: 2% 3%;
text-align: center;
}
<div>
<img src="https://placehold.it/400x200">
<span>This is an image of a gray rectangle!</span>
</div>
<div>
<img src="https://placehold.it/200x200">
<span>This is an image of a gray square!</span>
</div>
<div>
<img src="https://placekitten.com/g/400/200">
<span>This is an image of a cute kitten inside a rectangle!</span>
</div>
<div>
<img src="https://placekitten.com/g/200/200">
<span>This is an image of even cuter kitten inside a square!</span>
</div>
So the issue is that you are trying to set your handler on a dynamic selector ("img#"+j) but this will not work. For one thing, that equation will be evaluated only once, on page load, when j is undefined.
So you want to do this instead:
target only img tags for your mouse over... Better yet, give your special images all the same css class so you can attach the event handlers only to those. That will be more efficient.
When an image is moused over or out of, grab it's id attribute, extract the number from it, then use that to build a selector for the appropriate span to show.
var get_span_from_image = function(image) {
var image_id = image.attr("id");
var matches = image_id.match(/theImg(\d+)/);
if(matches) return $("theSpan" + matches[1]);
return $(); // nothing found, return an empty jQuery selection
};
$("img").hover(
function() { // mouse over
get_span_from_image($(this)).show();
},
function() { // mouse out
get_span_from_image($(this)).hide();
}
);
Note: There are better ways to "link" two nodes together, but this is just to answer your question with the current structure you have.
UPDATE: Some ideas to link two nodes together
So instead of trying to extract a number from an id attribute, a better way would be to tell either one of the image or span about it's sibling. You could output your html like this, for instance:
<img id="theImg1" data-target="theSpan1" class="hoverable" src="..."/>
....
<span id="theSpan1">...</span>
Of course now your ideas could be anything - you don't have to use numbered values or anything.
Then your hover code becomes quite simply:
var get_span_from_image = function(image) {
var span_id = image.data("target");
return $("#" + span_id);
};
$("img").hover(
function() { // mouse over
get_span_from_image($(this)).show();
},
function() { // mouse out
get_span_from_image($(this)).hide();
}
);
Hope this helps!
I want on ajax call change the values loaded from CSS file, it means not only for one element like:
document.getElementById("something").style.backgroundColor="<?php echo "red"; ?>";
but similar script which is change the css value generally, not only for element by ID, idealy like background color for CSS CLASS divforchangecolor:
CSS:
.divforchangecolor{
display: block;
margin: 20px 0px 0px 0px;
padding: 0px;
background-color: blue;
}
HTML:
<div class="divforchangecolor"><ul><li>something i want to style</li><ul></div>
<div class="divforchangecolor">not important</div>
<div class="divforchangecolor"><ul><li>something i want to style</li><ul></div>
<div class="divforchangecolor">not improtant</div>
Ideal solution for me:
onclick="--change CSS value divforchangecolor.backgroundColor=red--"
but i need to change CSS to reach .divforchangecolor ul li and .divforchangecolor ul li:hover
If you can't just apply the classname to these elements. You could add a new selector to your page. The following vanilla JS would be able to do that (jsFiddle).
function applyDynamicStyle(css) {
var styleTag = document.createElement('style');
var dynamicStyleCss = document.createTextNode(css);
styleTag.appendChild(dynamicStyleCss);
var header = document.getElementsByTagName('head')[0];
header.appendChild(styleTag);
};
applyDynamicStyle('.divforchangecolor { color: pink; }');
Just adapt the thought behind this and make it bullet proof.
var elements=document.getElementsByClassName("divforchangecolor");
for(var i=0;i<elements.length;i++){
elements[i].style.backgroundColor="red";
}
var e = document.getElementsByClassName('divforchangecolor');
for (var i = 0; i < e.length; i++) e[i].style.backgroundColor = 'red';
Use getElementByClassName() and iterate over the array returned to achieve this
You can select elements by class with document.getElementsByClassName or by css selector (includes class) with document.querySelectorAll().
Here are two approaches, for example: Live demo here (click).
Markup:
<div class="divforchangecolor"></div>
<div class="divforchangecolor"></div>
<div class="divforchangecolor"></div>
<div class="divforchangecolor"></div>
<div class="some-container">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
JavaScript:
var toChange = document.getElementsByClassName('divforchangecolor');
for (var i=0; i<toChange.length; ++i) {
toChange[i].style.backgroundColor = 'green';
}
var toChange2 = document.querySelectorAll('.some-container > div');
for (var i=0; i<toChange.length; ++i) {
toChange2[i].style.backgroundColor = 'red';
}
I recommend the second solution if it is possible in your case, as the markup is much cleaner. You don't need to specifically wrap the elements in a parent - elements already have a parent (the body, for example).
Another option is to have the background color you want to change to in a css class, then you can change the class on your elements (and therefore the style changes), rather than changing the css directly. That is also good practice, as it lets you keep your styles all in css files, while js is just manipulating which one is used.
On the whole document your approach can be a bit different:
ajax call
call a function when done
conditionally set a class on the body like <body class='mycondition'></body>
CSS will take care of the rest .mycondition .someclass: color: red;
This approach will be more performant than using JavaScript to change CSS on a bunch of elements.
You can leverage CSS selectors for that:
.forchangecolor {
display: block;
margin: 20px 0px 0px 0px;
padding: 0px;
background-color: blue;
}
.red-divs .forchangecolor {
background-color: red;
}
Then, with javascript, add the red-divs class to a parent element (could be the <body>, for example), when one of the divs is clicked:
document.addEventListener("click", function(event) {
var target = event.target;
var isDiv = target.className.indexOf("forchangecolor") >= 0;
if(isDiv) {
document.body.classList.add("red-divs");
}
});
Working example: http://jsbin.com/oMIjASI/1/edit