'use strict'
let turn =1;
let countx = 0;
let counto = 0;
function addx(id){
document.getElementById(id).innerHTML = '<p>x</p>';
turn = 0;
newclick();
countx++;
checkWin();
}
function addo(id){
document.getElementById(id).innerHTML = '<p>o</p>';
turn = 1;
newclick();
counto++;
checkWin();
}
function checkWin(){
if(countx==3||counto==3){
alert('you win');
}
}
function newclick(){
if(turn==1){
document.getElementById('leftTop').addEventListener("click",function (){addx('leftTop')});
document.getElementById('centerTop').addEventListener("click",function (){addx('centerTop')});
document.getElementById('rightTop').addEventListener("click",function (){addx('rightTop')});
document.getElementById('leftCenter').addEventListener("click",function (){addx('leftCenter')});
document.getElementById('centerCenter').addEventListener("click",function (){addx('centerCenter')});
document.getElementById('rightCenter').addEventListener("click",function (){addx('rightCenter')});
document.getElementById('leftBottom').addEventListener("click",function (){addx('leftBottom')});
document.getElementById('centerBottom').addEventListener("click",function (){addx('centerBottom')});
document.getElementById('rightBottom').addEventListener("click",function (){addx('rightBottom')});
}
else{
document.getElementById('leftTop').addEventListener("click",function (){addo('leftTop')});
document.getElementById('centerTop').addEventListener("click",function (){addo('centerTop')});
document.getElementById('rightTop').addEventListener("click",function (){addo('rightTop')});
document.getElementById('leftCenter').addEventListener("click",function (){addo('leftCenter')});
document.getElementById('centerCenter').addEventListener("click",function (){addo('centerCenter')});
document.getElementById('rightCenter').addEventListener("click",function (){addo('rightCenter')});
document.getElementById('leftBottom').addEventListener("click",function (){addo('leftBottom')});
document.getElementById('centerBottom').addEventListener("click",function (){addo('centerBottom')});
document.getElementById('rightBottom').addEventListener("click",function (){addo('rightBottom')});
}
}
newclick();
on the first click - countx = 1; counto=0.
on the second click - countx = 2; counto=1.
on the third click - countx = 4; counto=3.
what i want to get is:
first click countx =1;counto=0.
second click countx=1;counto=1.
third click countx=2;counto1.
what i wanted is every click is to add 1 to each count when it is his turn.
this is tictacteo game, every click is should replace the event listener.
the function of x somehow called 4 times on the third click, what i get "on board" is 2x and 1o but the countx = 4
addEventListener adds a new additional event listener every time it's run. Use just one event listener instead of trying to replace the old one to make this work better. Replacing them is unnecessary.
The turn variable already tracks whose turn it is. So you can use something like the following.
function genericClick(id) {
if (turn == 1) {
// add an x
document.getElementById(id).innerHTML = '<p>x</p>';
turn = 0;
countx++;
} else {
document.getElementById(id).innerHTML = '<p>o</p>';
turn = 1;
counto++;
}
checkWin();
}
Related
I wanted to display some divs on my HTML page with some animations. So I decided to use the setInterval method to increases the opacity of all of my div. I wrote the below code in order to do this (the toggleAll function is run at the beginning)
var tab = ["lien","contact","comp_info","comp_langue","apropos","loisir","etudes","exp_pro","projet"];
function increaseOpacityDiv(id,opacityMax){
console.log("fonction appelée : increaseOpacityDiv, opacity max :"+opacityMax);
if(document.getElementById(id).style.opacity >= opacityMax){
console.log("entré ! ");
clearInterval(boucle);
document.getElementById(id).style.opacity = opacityMax;
debugger;
}else{
recup = parseFloat(document.getElementById(id).style.opacity+0) + 0.01;
document.getElementById(id).style.opacity = recup;
}
}
function decreaseOpacityDiv(id){
console.log("fonction appelée : decreaseOpacityDiv");
if(document.getElementById(id).style.opacity <= 0){
clearInterval(boucle);
document.getElementById(id).style.opacity = 0;
}else{
recup = parseFloat(document.getElementById(id).style.opacity) - 0.01;
document.getElementById(id).style.opacity = recup;
}
}
function toggleAll(){
console.log("fonction appelée : toggleAll");
if(all){
console.log("faire disparaitre");
for (var i=0;i<tab.length;i++){
boucle = setInterval(decreaseOpacityDiv,5,tab[i]);
}
all=0;
}else{
console.log("faire apparaitre");
for (var i=0;i<tab.length;i++){
boucle = setInterval(increaseOpacityDiv,5,tab[i],1);
}
all=1;
}
}
It works, the browser displays correctly the divs. However I can't disappeared them, in the same way as I displayed them. I found out that my toggleAll function made a lot of setInterval and I can't stop all of them, so the increaseOpacityDiv is run like every 5ms. And the clearInterval method can just stop the last loop made by the setInterval, not the others. This is shown by this below :
console log
Do you know a way I can show my divs then stop all the setInterval ?
I'm trying to implement a functionality where the user can only order a set amount of times (let's take 5 times for example) before an alert shows up saying that the product he is trying to order has ran out of stock.
He can still of course, order other product that haven't been clicked on 5 times.
The problem I'm facing is that I don't know how to keep count of each order button (an anchor acting as a button) specifically.
I tried implementing that functionality and it does show an alert, but it takes count of all order buttons clicked.
This is how my functions looks:
let counter = 0; //global variable
function order_func() {
let every_product= document.querySelectorAll(".all_prods");
for (let each_prod = 0; each_prod < every_product.length; each_prod++) {
every_product[each_prod].addEventListener("click", function (e) {
if(count_stuff < 10){
add_it(e, each_prod); //e to preventDefault() and console.log "each_prod"
count_stuff++;
}else{
out_of_stock(e); //shows the alert it takes the event as argument to prevent default behavior.
}
});
}
}
add_it() is just a function that console.log()'s the product.
I'm only using vanilla JS, I don't want to use any other libraries for this :)
you can use data attribute to store temp values in page. take a look on this example using JQuery.
<!-- index.html -->
Order
<script>
$('#order-btn').click(function() {
var maxCounts = this.data('maxCounts');
var count = this.data('count');
if (count >= maxCount) {
alert('you cant order any more');
return;
}
this.data('count',count++);
// other logic .e.g Ajax call
})
</script>
you can do it using vanilla JS also:
<script>
let counter = 0; //global variable
function order_func() {
let every_product= document.querySelectorAll(".all_prods");
for (let each_prod = 0; each_prod < every_product.length; each_prod++) {
every_product[each_prod].addEventListener("click", function (e) {
var count_stuff = e.target.dataset.count; // or this.dataset.count
var max_count = e.target.dataset.maxCounts; // or this.dataset.maxCounts
if(count_stuff < max_count){
add_it(e, each_prod);
e.target.dataset.count = count_stuff++;
}else{
out_of_stock(e);
}
});
}
}
</script>
So I have created this javascript that animates a certain place using it's ID.
The problem is that there are many of those on the site and meaning this I'd have to duplicate this function a lot of times just to replace the x in getElementById("x").
So here is the code I fully done by myself:
var popcount = 0;
var opanumber = 1;
var poptimeout;
function pop() {
if (popcount < 10) {
popcount++;
if (opanumber == 1) {
document.getElementById("nav1").style.opacity = 0;
opanumber = 0;
poptimeout = setTimeout("pop()", 50);
}
else {
document.getElementById("nav1").style.opacity = 1;
opanumber = 1;
poptimeout = setTimeout("pop()", 50);
}
}
else {
popcount = 0;
document.getElementById("nav1").style.opacity = 1;
}
}
function stoppop() {
clearTimeout(poptimeout);
popcount = 0;
document.getElementById("nav1").style.opacity = 1;
}
I would gladly appreciate any information on how I could solve this situation and also any tutorials about using classes and "this".
Something like this; rather than hard code a value into a function it is better to pass the value in so you can reuse the function on more than one thing. In this case you can now call startPop and stopPop with the name of a CSS class.
var popTimeout;
function setOpacity(className, value) {
Array.prototype.forEach.call(
document.getElementsByClassName(className),
function(el) {
el.style.opacity = value;
}
);
}
function pop(className, popCount, opaNumber) {
if (popCount < 10) { //Must be even number so you end on opacity = 1
setOpacity(className, opaNumber);
popTimeout = setTimeout(function() {
pop(className, popCount++, 1-opaNumber);
}, 50);
}
}
function startPop(className) {
pop(className, 0, 0);
}
function stopPop(className) {
clearTimeout(popTimeout);
setOpacity(className, 1);
}
In case you are wondering about the 1 - opaNumber; this is a simpler way of switching a value between 1 and 0. As 1-1=0 and 1-0=1.
Well you started out with recognizing where you have the problem and that's already a good thing :)
To make your code a bit more compact, and get as many things as possible out of the local scope, you could check the following implementation.
It is in a sense a small demo, where I tried adding as much comments as possible.
I edited a bit more after realizing you rather want to use classnames instead of id's :) As a result, I am now rather using the document.querySelectorAll that gives you a bit more freedom.
Now you can call the startPop function with any valid selector. If you want to pop purely on ID, you can use:
startPop('#elementId');
or if you want to go for classes
startPop('.className');
The example itself also add's another function, nl trigger, that shows how you can start / stop the functions.
I also opted to rather use the setInterval method instead of the setTimeout method. Both callback a function after a certain amount of milliseconds, however setInterval you only have to call once.
As an extra change, stopPop also now uses the document.querySelectorAll so you have the same freedom in calling it as the startPop function.
I added 2 more optional parameters in the startPop function, namely total and callback.
Total indicates the maximum times you wish to "blink" the element(s), and the callback provides you with a way to get notified when the popping is over (eg: to update potential elements that started the popping)
I changed it a bit more to allow you to use it for hovering over an element by using the this syntax for inline javascript
'use strict';
function getElements( className ) {
// if it is a string, assume it's a selector like #id or .className
// if not, assume it's an element
return typeof className === "string" ? document.querySelectorAll( className ) : [className];
}
function startPop(className, total, callback) {
// get the element once, and asign a value
var elements = getElements( className ),
current = 0;
var interval = setInterval(function() {
var opacity = ++current % 2;
// (increase current and set style to the left over part after dividing by 2)
elements.forEach(function(elem) { elem.style.opacity = opacity } );
// check if the current value is larger than the total or 10 as a fallback
if (current > (total || 10)) {
// stops the current interval
stopPop(interval, className);
// notifies that the popping is finished (if you add a callback function)
callback && callback();
}
}, 50);
// return the interval so it can be saved and removed at a later time
return interval;
}
function stopPop(interval, className) {
// clear the interval
clearInterval(interval);
// set the opacity to 1 just to be sure ;)
getElements( className ).forEach(function(elem) {
elem.style.opacity = 1;
});
}
function trigger(eventSource, className, maximum) {
// get the source of the click event ( the clicked button )
var source = eventSource.target;
// in case the attribute is there
if (!source.getAttribute('current-interval')) {
// start it & save the current interval
source.setAttribute('current-interval', startPop(className, maximum, function() {
// completed popping ( set the text correct and remove the interval )
source.removeAttribute('current-interval');
source.innerText = 'Start ' + source.innerText.split(' ')[1];
}));
// change the text of the button
source.innerText = 'Stop ' + source.innerText.split(' ')[1];
} else {
// stop it
stopPop(source.getAttribute('current-interval'), className);
// remove the current interval
source.removeAttribute('current-interval');
// reset the text of the button
source.innerText = 'Start ' + source.innerText.split(' ')[1];
}
}
<div class="nav1">
test navigation
</div>
<div class="nav2">
Second nav
</div>
<div class="nav1">
second test navigation
</div>
<div class="nav2">
Second second nav
</div>
<a id="navigation-element-1"
onmouseover="this.interval = startPop( this )"
onmouseout="stopPop( this.interval, this )">Hover me to blink</a>
<button type="button" onclick="trigger( event, '.nav1', 100)">
Start nav1
</button>
<button type="button" onclick="trigger( event, '.nav2', 100)">
Start nav2
</button>
If you do want to take it back to using IDs, then you will need to think about popTimeout if you run this on more than one element at a time.
function setOpacity(id, value) {
document.getElementById(id).style.opacity = value;
}
function runPop(id) {
function pop(id, popCount, opaNumber) {
if (popCount < 10) { //Must be even number so you end on opacity = 1
setOpacity(id, opaNumber);
popTimeout = setTimeout(function() {
pop(id, popCount++, 1-opaNumber);
}, 50);
}
}
var popTimeout;
pop(id, 0, 0);
return function() {
clearTimeout(popTimeout);
setOpacity(id, 1);
}
}
var killPop = [];
function startPop(id) {
killPop[id] = runPop(id);
}
function stopPop(id) {
killPop[id]();
}
My code is running well...
but there is one thing I can't solve:
when I mouseover the image the loop starts well, but on subsequent mouseovers it starts changing faster and faster...
var Image = new Array("//placehold.it/400x180/?text=Welcome",
"//placehold.it/400x180/?text=To",
"//placehold.it/400x180/?text=My",
"//placehold.it/400x180/?text=Web+page",
"//placehold.it/400x180/?text=INPHP");
var Image_Number=0;
var Image_Length= Image.length;
function change_image(num){
Image_Number = Image_Number + num;
if(Image_Number > Image_Length)
Image_Number = 0;
if(Image_Number < Image_Length)
document.slideshow.src = Image[Image_Number];
return false;
Image_Number = Image_Length;
}
function auto () {
setInterval("change_image(1)", 1000);
}
<img src="//placehold.it/400x180/?text=Welcome" name="slideshow" onmouseover="auto()" />
On every mouseover you're reassigning a brand-new-interval™ resulting in multiple functions running at the same time.
name on IMG tag is an obsolete HTML5 attribute - See also img tag # W3.org
"change_image(1)" ...strings inside setInterval or setTimeout tigger eval. Which is bad. The real function name should be used instead: setInterval(functionName, ms)
You're not managing well the argument num
You cannot have code after a return statement
Use the mouseenter event (instead of mouseover)
and many more errors....
Here's a remake:
var images = [
"//placehold.it/400x180/?text=Welcome",
"//placehold.it/400x180/?text=To",
"//placehold.it/400x180/?text=My",
"//placehold.it/400x180/?text=Web+page",
"//placehold.it/400x180/?text=INPHP"
];
var c = 0; // c as Counter ya?
var tot = images.length;
var angryCat = false;
// Preload! Make sure all images are in tha house
for(var i=0; i<tot; i++) {
var im = new Image();
im.src= images[i];
}
function changeImage() {
document.slideshow.src = images[++c%tot];
}
function auto() {
if(angryCat) return; // No more mouse enter
angryCat = true;
setInterval(changeImage, 1000);
}
<img src="//placehold.it/400x180/?text=Welcome" name="slideshow" onmouseenter="auto()" />
The increment and loop is achieved using ++c % tot
The angryCat boolean flag helps to know it the auto() already started (mouse was there!), in that case it will return; (exit) the function preventing the creation of additional overlapping intervals on subsequent mouseenter (which was your main issue).
Additionally, I'd suggest to keep your JS away from HTML, so instead of the JS attribute event
onmouseenter="auto()"
assign an ID to your image id="myimage" and use JS:
document.getElementById("myimage").addEventListener("mouseenter", auto, false);
i have designed chess board using buttons.Initially all the values on the button will be null,upon loading the page all the pieces appear on them and the piece of code is as follows
<input type="button" id="A8" value="" style="background:#FFE4C4;font-size: 70px;height:90;width:100" onclick="check(this.id)">
and in the onLoad function,the ASCII charecter of the chess pieces are assigned as follows:
document.getElementById('A1').value=String.fromCharCode(9814);
Now what i want is to change the one piece from a button to another on clicking two buttons.i had tried a lot with the following script
function check(clicked_id) {
var Button_2 = "";
if (i < 2) {
i++;
// alert("i:"+i);
if (i == 1) {
Button_1 = clicked_id;
B1_val = document.getElementById(Button_1).value;
alert("B1 Button val:" + B1_val);
}
if (i == 2) {
var Button_2 = clicked_id;
B2_val = document.getElementById(Button_2).value;
alert("b1 val:" + B1_val);
alert("B2 val:" + B2_val);
B2_val = B1_val;
B1_val = "";
alert("B1 val:" + B1_val + "B2 val:" + B2_val);
}
} else {
alert("Only 2 butons should press..i:" + i);
i = 0;
}
// alert("clcked a button:"+clicked_id);
}
But the code is not working
If you just want to move the value from the location of the first click to the location of the second click, then you can do that fairly simply like this:
var lastClick;
function check(id) {
var src, dest;
if (!lastClick) {
// no previous click so just store the location of this first click
lastClick = id;
} else {
// move value from lastClick id to new id
src = document.getElementById(lastClick);
dest = document.getElementById(id);
dest.value = src.value;
src.value = "";
lastClick = null;
}
}
I assume that a real application would need all sorts of error handling that doesn't let you put a piece on top of another piece, ignores first clicks on empty spaces, enforces only legal moves, etc...