Probably a simple fix but I've been stuck working through this now for a couple days. I'm building a tic-tac-toe project and I would like to build a method inside my user object called userClick() that satisfies the following conditions:
When the user clicks on a tictactoe tile:
Change the HTML text inside that tile (div) to the user's marker (X or O);
add a CSS class of taken to the tile;
increase the user's score/move count by 1.
Here's the relevant Javascript/jQuery code I've tried to fiddle around with:
var user = {
score: 0,
marker: "X",
userClick: function() {
$('.box').onClick(function() {
this.text(marker);
this.addClass('taken');
score += 1;
});
};
My HTML has 9 divs arranged in a tictactoe grid and all divs have the class of box. My CSS file also has a class of taken that changes the background color of the div when clicked. However, when I click on any of the divs, nothing happens. I've tried making numerous tweaks to the function to get it to work, with no luck. I'm still rather inexperienced with closures, event handlers, and this binding so maybe someone with more experience can shed some light on this for me.
Thanks!
Edit: Here's a JSFiddle of the entire code base so far in case it helps. (Problematic method begins at line 74).
It's not fully clear to me (much less the parser) what you expect "this" to be in your context. Is it the jquery selected element you want? Or is it the user object?
Given that you're trying to add a CSS class to it, I'll assume you want the clicked element. Your handler assignment should then look like so:
$('.box').on('click', function() {
var box = $(this); // refers to the clicked element
box.text(marker);
box.addClass('taken');
score += 1;
});
this inside the handler stands for the DOM element, but both text and addClass belong to jQuery Object. So you need to use them like this.
$(this).text(marker)
I put a fiddle up here for a pretty bare bones jQuery tic-tac-toe per your mention: https://jsfiddle.net/6ynyr7hx/
HTML
<div id="game_container">
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
jQuery
$(function() {
var marker_count = 1;
$('.box').click(function() {
if (marker_count === 9) {
$('#game_container').append('GAME OVER');
}
if (marker_count % 2 === 00) {
marker = "X";
} else {
marker = "O";
}
$(this).text(marker).addClass("taken");
//Increment the counter
marker_count = marker_count + 1;
});
});
CSS
#game_container {
width: 140px;
text-align: center;
}
.box {
display: block;
float: left;
width: 40px;
height: 40px;
line-height: 40px;
text-align: center;
margin: 2px;
border: 1px solid #CCC;
}
*NOTE: I didn't bother clearing the floats as this was just a quickie...
// PROBLEM METHOD
userClick: function userClick(marker) { <--- You have to pass the marker
marker = "x"; <- That should be the passed marker
$(".box").click(function() {
$(this).text(marker).addClass("taken");
score += 1;
});
}
}
you will need to pass the marker states and scores back and forth on changing turns etc... This at least gets you a little closer to play with it. Also remove all the html head code including the js and then use the javascript panel cog wheel icon to include jquery x.x.x and set it to onDomready. Pay attention to the terminations I changed in the above code. There was a straggler in there that was causing the script to fail. And un-comment out your on ready userClick function in the fiddle.
Missing } following function at userClick; call user.userClick() within game.initializeGame(); substitute user.marker and user.score for marker, score within .click() function attached to .box selector.
// Game functions
initializeGame: function() {
user.chooseMarker();
computer.chooseMarker();
user.setMarkerInUI();
user.updateScore();
computer.setMarkerInUI();
computer.updateScore();
user.userClick();
$('#score strong').text(totalScore);
}
// PROBLEM METHOD
userClick: function userClick() {
$(".box").click(function() {
console.log(this, user.marker)
$(this).text(user.marker);
$(this).addClass(".taken");
user.score += 1;
})
}
A start plnkr https://plnkr.co/edit/cAOj0Xa0rcNucM9lft1G?p=preview
Related
I am making an etch-a-sketch and I am trying to use an addEventListener event of 'mouseover' to add a CSS class where the background of the square the mouse hovers over will turn black. I have a function that creates a single box and if I put the event listener inside this function it works, however if I try to do it outside the function it doesn't work
The function box creates a single box (which will get repeated using the addMultipleBox function) and adds a mouseover event. In this scenario the mouseover works correctly
function box() {
let square = document.createElement('div')
square.setAttribute('class', 'box')
container.appendChild(square)
square.addEventListener('mouseover', () => {
square.classList.add('blackPen')
})
}
//creates the etch a sketch board with multiple 'boxes'
function addMultipleBoxes() {
for(let i = 0; i < 256; i++) {
box()
}
}
Now if I try and grab the class of 'box' outside the function and add an event listener to it nothing happens. I do have this code at the bottom so it's not like i'm trying to grab divs before they are created.
I'd like to be able to grab it outside so I can create another function that on a mouse click I remove the class of 'blackPen' which will remove the background color of black on the square, essentially wiping the board clean. Here is what I have for that
let boxx = document.querySelector('.box')
console.log(boxx)
boxx.addEventListener('mouseover', () => {
boxx.classList.add('blackPen')
})
When I console.log 'Boxx' I get the <div class="box"></div>. If I console.log "square" above in the box function I get the same thing as Boxx.
Any insight would be much appreciated!
The box() function adds the class to each element as it is made, as required.
A reference made to an element using querySelector contains only one element - the first in the document with the specified selector. See https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector
This snippet illustrates the idea with four divs, all the same class.
let boxx = document.querySelector('.box')
boxx.addEventListener('mouseover', () => {
boxx.classList.add('blackPen')
})
.box {
display: inline-block;
width: 70px;
aspect-ratio: 1;
background: yellow;
border: 1px solid red;
}
.blackPen {
background: black;
}
<p><b>mouseover applied to element got by querySelector</b></p>
<p>(move mouse over divs)</d>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<p>querySelector, returns the <em>first</em> Element within the document that matches the specified selector</p>
A reference to all elements with a given class can be made using querySelectorAll(), which returns a live node list with references to all the elements of the given selector. See: https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll
Having made the reference, you will still have to loop through them in order to add the event listeners required. node lists and html collections are array-like structures that can have individual members referenced by and index [0],[1], and so on.
This snippet illustrates adding a new class to each div in a node list formed using querySelectorAll:
let boxx = document.querySelectorAll('.box')
for (let i=0; i<boxx.length; i++) {
boxx[i].addEventListener('mouseover', () => {
boxx[i].classList.add('blackPen');
boxx[i].classList.remove('cancel');
});
boxx[i].addEventListener('click', () => {
boxx[i].classList.add('cancel')
});
} // next i boxx element;
.box {
display: inline-block;
width: 70px;
aspect-ratio: 1;
background: yellow;
border: 1px solid red;
}
.blackPen {
background: black;
}
.cancel {
background: yellow;
}
<p><b>mouseover applied to all elements got by querySelectorAll</b><br>(move mouse over divs)</d>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<p>A click event was also added to all elements, to replace the class added by the mouseover event<br>click boxes to see the effect</p>
Note that the node collection only needs to be made once to be used for any further manipulations. In the snippet a mouseover event adds a class to make the background black while a click event cancels it.
I have a site with a lot of different div. The thing they have in common is all share (besides their unique classes) a shared class. Lets just call it .changeClass.
What I am looking for is code with a button (or radio buttons) and by clicking the button, the background instance of all these divs will get the same one (which the .changeClass has). So the .changeClass will just be active when the button is toggled/clicked.
I am looking for a way to do this with pure javascript and no Jquery.
Sorry for being a noob :-)
In the solution below, clicking the <button> element will add/remove the class style .changeClass to all elements that have the class style .apply applied.
let button = document.getElementById('change');
let containers = document.getElementsByClassName('apply');
function changeButtonText() {
if(button.innerHTML === "Add")
button.innerHTML = "Remove";
else
button.innerHTML = "Add";
}
button.addEventListener('click', function() {
for(let index = 0 ; index < containers.length ; ++index)
containers[index].classList.toggle('changeClass');
changeButtonText();
});
div {
margin-top: 25px;
}
.apply {
border: 3px solid black;
}
.changeClass {
background-color: black;
color: white;
border: 3px solid red;
margin-top: 25px;
}
<button id="change">Add</button>
<div class="apply">1</div>
<div class="apply">2</div>
<div class="apply">3</div>
<div class="apply">4</div>
<div class="apply">5</div>
First lets get all divs that are on the DOM
const divs = document.getElementsByTagName("div");
You will have array of all the divs that are on the DOM. Then add your class to all of it. In order to do that, lets loop it.
divs.forEach(div => div.className += div.className + " changeClass");
Could this be what you are looking for?
In html:
<button onclick="changeColor('blue');">blue</button>
In JS
function changeColor(newColor) {
var elem = document.getElementsByClassName("changeClass");
elem.style.color = newColor;
}
The HTML color can be any color you would like it to be, just change they name from blue to any color or input a hex code.
We have multiple divs with the same class value
We have given a function to the button that we want the event to happen when it is clicked, using the onclick method. Now when we click the button, the function called myFunction will run.
HTML:
<div class="changeClass">Im Example Div</div>
<div class="changeClass">Me Too</div>
<button type="submit" onclick="myFunction()">Click To Change Div BgColors !
</button>
We must define myFunction as Javascript and change the background color.
We have defined a function called myFunction.
With the getElementsByClassName selector in our function, we got all the data with the class value changeClass in object format.
To add a background (or any css property) to all of these objects; We put the object in a for loop and now we split our elements.
We can now define a background color for our elements with the style.backgroundColor parameter.
JavaScript:
function myFunction(){
var divs = document.getElementsByClassName('changeClass');
for(var i=0; i< divs.length; i++){
divs[i].style.backgroundColor = 'red';
}
}
For more detailed information, you can refer to the resources: https://www.w3schools.com/jsref/met_document_getelementsbyclassname.asp
Don't be sorry for being new at something and wanting to learn more!
So what you are saying is that the divs you want to change all have a common class of "changeClass". If this is the case then you want a function is passed an argument value of the color you want to be changed. Since all of your divs are static and you probably don't plan on changing, declare a variable outside of this function that has the following code
const divs = document.getElementsByClassName("changeClass")
Then, inside of the function, loop through all of the divs collected inside the variable "divs", or whatever you want to call it. Since "getElementsByClassName" returns a collection, it does not have the built in "foreach" and "map" methods. So you have to use a for loop preferably the following.
const divs = document.getElementsByClassName("changeClass");
function changeColor(color) {
for (let element of divs) {
element.style.backgroundColor = color;
}
}
I may have interpreted this wrong but I hope it helps
You may find using a CSS variable helpful.
For example:
function bg(color) {
document.body.style.setProperty('--bg', color);
}
body {
--bg: cyan;
}
.container {
display: flex;
gap: 1vw;
}
.container div {
width: 100px;
height: 100px;
background-color: black;
}
.container div.changeClass {
background-color: var(--bg);
}
<body>
<button onclick="bg( 'red');">Red</button>
<button onclick="bg( 'green');">Green</button>
<button onclick="bg( 'blue');">Blue</button>
<button onclick="bg( 'black');">Black</button>
<div class="container">
<div class="changeClass"></div>
<div class="changeClass"></div>
<div class="changeClass"></div>
<div></div>
<div class="changeClass"></div>
</div>
</body>
Then when one of the radio buttons is clicked it sets the variable --bg.
Here's a simple snippet:
First of all - thank you for all your replies. And yes I should have included code. I tried so many things that i just gave up at som point - got confused what was right code and what was just rubbish. So I appreciate so much that you all took time to answer me. This was my first post so now I know for the future. The answers I got all was possible ways to solve my problem - so thank you all. I will do better next time. You are awesome...
BTW - All solutions seems to work - but can only checkmark one of them as you know.
You can add or remove a class to change the colours of different div:
document.queryselector('.className').classList.add('classNamethatyouwanttoadd');
document.queryselector('.className').classList.remove('classNamethatyouwanttoadd');
I am implementing an User Interface for a project I'm working on and can be found here : Toobrok
Each time the mouse of the user enters a div, a class is added to this div to highlight it, I use the stopPropagation() method to restrict the highlighting to the div whose z-index is higher (the top div in the z axis).
However, sometimes, my user needs to select an element hidden by another one, when the dimensions of the 2 elements are different, and if the bottom div is larger, he can find some points of the bottom div not hidden by the top one, but when the dimensions are the same, I would like the user to be able to press a key to change the depth (on the z-axis) of his selection.
The relevant code is given below (in CoffeeScript), but a javascript solution would also help me:
Ui.bind = (elements, index) ->
ids = Ui.getIdSelector(elements)
$(ids).attr("centroid", index)
$(ids).mouseover (event) ->
event.stopPropagation()
Ui.highlight $(ids)
$(ids).mouseout (event) ->
event.stopPropagation()
Ui.resetHighlight $(ids)
I hope the question is clear and looking forward to your answer.
This is an example of HTML to consider :
<!DOCTYPE html>
<html>
<head>
<title> Sample page </title>
</head>
<body>
<div id="container">
<div id="child1">Some text...</div>
</div>
</body
</html>
And the related css :
#container {
height: 200px;
width: 500px;
}
#child1 {
height: 90%;
width: 90%;
}
When the mouse enters the child1 element, this element is highlighted, I want the container element to highlight when the user press a specific key.
I could use the JQuery parent() function to select that element on this example, but I am not sure it is a good solution, sometimes, the parent can have a size of 0px and and then a mouseover on this element would not be consistent. I want to select the element normally selected by Javascript if I do not use the stopPropagation() event.
I actually just found something that might help :
How to undo event.stopPropagation in jQuery?
But I cannot use that in my case... Because my condition is another user action, and I cannot synchronously wait for an user to do something.
I started writing code but then decided to leave implementation to you. Here is the text explanation:
At some point of time (probably when user press button to cycle through all hovered elements) you have to find all candidates for highlighting. There is no other way to do it rather than manually loop through all your elements and check if mouse position is inside their bound rect. You can get mouse coordinates from argument in mouseover callback. Save all these hovered elements in some array.
Next, you have to manually choose which element to highlight. Just highlight the first element in saved array and move the element to the end of array. You also may want to increase this element z-index and add callback for mouseout to this element.
Hope it helps, feel free to ask if you need more details.
You could use the CSS property pointer-events to make the child insensitive. Then events will be targeted to the element displayed below. For simple highlighting you should use pure CSS, however, jQuery can be helpful not to highlight the parent element as well while child is hovered without Ctrl.
Some example (also uploaded to JSFiddle, click into the output pane to make it responsive for keyboard events):
<div id="container1" class="container">
<div id="child1" class="child">Some text...</div>
</div>
div { border:1px dashed red; } /* for demo */
.container
{ height: 200px;
width: 500px;
}
.child
{ height: 90%;
width: 90%;
}
.insensitive
{ pointer-events:none;
}
.container:hover:not(.no-hilight),
.child:hover
{ background-color:yellow;
}
/* other color for demo */
.child:hover{ background-color:green; }
// make events passthrough child when <Ctrl> is held down
$(document).on('keydown keyup', function(ev) {
if (ev.key === 'Control') // for performance
$('.child')[ev.ctrlKey?'addClass':'removeClass']('insensitive');
});
// don't hilight container when child is hovered
$('.child').on('mouseover', function(ev)
{
$('.container').addClass('no-hilight');
});
// never suppress hilight when container is hovered directly
$('.container').on('mouseover', function(ev)
{ if(ev.target === ev.currentTarget)
$('.container').removeClass('no-hilight');
});
// just test which element a click is targeted to
$(document).on('click', function(ev)
{ console.log('click:', ev.target);
});
var preId = 0;
function makeBlack(id)
{
if(id)
{
$('#'+id).css('border-color','black');
}
}
function makered(id)
{
$('#'+id).css('border-color','red');
}
$(document).ready(function(){
$('div').mouseout(function() {
var currentid = this.id;
makeBlack(currentid);
preId = currentid;
});
$('div').mouseleave(function() {
var currentid = this.id;
makeBlack(currentid);
preId = currentid;
});
$('div').mouseover(function() {
var currentid = this.id;
makeBlack(currentid);
makered(preId);
preId = currentid;
});
$('div').mouseenter(function() {
var currentid = this.id;
makered(currentid);
preId = currentid;
});
});
Have you tried something like this for the CSS?
#container.hover{
height: 200px;
width: 500px;
//add a background-color to that element since its a div element
//background-color: (colour)
}
i should hope that the div element would automatically highlight the container div with whichever color you have selected
i am trying to make a colour change when a button is clicked and i managed to do this however i want to change the colour of not just the main content container but more containers how do i do this?
function changeblackandwhite(objDivID) {
if(document.getElementById(objDivID).style.color=='black'){
document.getElementById(objDivID).style.color='white';
document.getElementById(objDivID).style.backgroundColor='black';
}
else if(document.getElementById(objDivID).style.color=='white'){
document.getElementById(objDivID).style.color='black';
document.getElementById(objDivID).style.backgroundColor = 'white';
}
else{
document.getElementById(objDivID).style.color='black';
document.getElementById(objDivID).style.backgroundColor='white';
}
}
<img src="images/colour.jpg" title="Change Text/Backgroud Colors">
There are dozens of ways you can accomplish this.
You could change the argument of your function to be an array of strings. You could also reduce the complexity of your function as well
<script type="text/javascript">
changeblackandwhite = function() {
for( var idx=0; idx < arguments.length; idx++) {
var tgtDiv= document.getElementById(arguments[i]);
if(tgtDiv.style.color=='black'){
tgtDiv.style.color='white';
tgtDiv.style.backgroundColor='black';
}
else{
tgtDiv.style.color='black';
tgtDiv.style.backgroundColor='white';
}
}
};
</script>
<img src="images/colour.jpg" title="Change Text/Backgroud Colors">
As another reader questioned - you can do this with jQuery in a single line.
With jQuery, you can declare the elements in question to have a class attribute.
Using jQuery, you can then do something like:
$('div.someClass').css({'color': 'black', 'background-color': 'white'});
The argument to jQuery can be a class based selector, an id based selector, or any other selector you choose.
If you are open to jquery and you assign 1 class in common with these two divs you can do the following:
This should get you started (see this jsfiddle): I changed the fiddle to include a neater solution where clicking on the button adds and removes classes on the containers which allows you to set multiple attributes including the text color in one quick call.
<div class='container'>
</div>
<div class='container'>
</div>
<button id="changeColor" type="button">Change Color </button>
<script type="text/javascript">
$(document).ready( function() {
$('#changeColor').click( function() {
if ($('.container').hasClass("blackContainer")){
$('.container').addClass("whiteContainer");
$('.container').removeClass("blackContainer");
} else {
$('.container').removeClass("whiteContainer");
$('.container').addClass("blackContainer");
}
});
});
</script>
//CSS
.blackContainer {
background-color: black;
color: white;
}
.whiteContainer {
background-color: white;
color: black;
}
I made a jsfiddle for you to play around with jsfiddle
I also did the javascript/jQuery in a similar way as the OP since it usually helps them understand.
As stated above, there are several different ways to do this, I've done but one.
The document.ready function sets up an event listener for the object to be clicked, most of the time this is how you'll see events coded. So when the link is clicked, it calls the function with the string name of the object the listener is for.
$(document).ready(function() {
$("#changeit").click(function(){
changeblackandwhite("Maincontainer");
})
});
After the event listener is assigned, it will call the function below when the link is clicked on.
// Here's your function, put the current color in a var, check if it's black
// if black, change colors, else make it black.
function changeblackandwhite(objDivID) {
var curColor = $("#" + objDivID).css("color");
if( curColor == 'rgb(0, 0, 0)'){
$("#"+objDivID).css({'color':'white','background-color':'black'});
} else {
$("#"+objDivID).css({'color':'black','background-color':'ghostwhite'});
}
}
update at the bottom
There are 4 divs that are set to look like toggle buttons. When a button is toggled on:
-it is animated as a pressed button,
-it retrieves some content and it places that content into a box, and then
-it returns a value of 1 to an array.
(no problem.)
Problem:
When there is already one button button pressed, I don't understand how to toggle the first button off without also turning the other one off or affecting the other buttons. How can I pass the output of one button to the others so they know who they have to turn off when they turn on?
My solution thus far has been to create 2 arrays:
var arrayValues [ a, b, c, d]; //the values of each button state: [0,0,0,0] <-all off | all on-> [1,1,1,1]
var addedValues = [a + b + c + d]; //the values of each array item added together: [0+0+0+0]= 0 <-all off | all on-> [1,1,1,1]=4
and then
if (addedValues = 0) {
console.log("cool, nothing is pressed yet. I am going to return true, but where does that return value go? How can I access it?");
return true;
} else if (addedValues > 1) {
console.log("ok I now know that at least one button has already been pressed, but how can I tell the other buttons which one was already pressed?");
}
For example if the first button is toggled on
arrayValues = [1,0,0,0]
and now the second button has been toggled on so it says
arrayValues = [1,1,0,0]
but how can I pass that information into all of the buttons? This next part is obviously flawed but it's the only thing I could think of doing:
} else if(addedValues >= 2) {
arrayValues[0] = arrayValues[0] - 1;
arrayValues[1] = arrayValues[1] - 1;
arrayValues[2] = arrayValues[2] - 1;
arrayValues[3] = arrayValues[3] - 1;
}
so now, the only values that are not negative are the two buttons in active states... but that does nothing for because we already knew that. How can I tell the buttons which button to subtract 1 from without affecting any of the other buttons?
Update: To see the madness in context http://jsfiddle.net/Luhring/EjW7A/23/
*update: *
Just to clarify: the buttons aren't only just toggling their appearances, they're changing other content displayed on the page:
When you click each button the content changes. each button has 1 original group of original content that is toggled on/off with the button. like changing the channel on a tv screen with a remote control.
so if button 1 is pressed, when button 2 is pressed button 1 must turn off (removing its' content and animating back up to its' original position) in order to allow button 2's stuff to display.
shout out to #nbrooks for writing 4 lines of code that more or less did as much as I did in +100. Still not solved but his is WAY more efficient than mine (you can see his version here: http://jsfiddle.net/EjW7A/20/ ) )
Updated Demo, according to new reqs: http://jsfiddle.net/EjW7A/24/
$(function() {
$('.plain').click(function() {
var newClassName = $(this).is('.selected') ? '' : this.id;
if ($(this).is('#content')) return;
$(this).toggleClass('selected', 1000);
$('#content').attr('class', 'plain '+newClassName);
$('.selected').not(this).removeClass('selected');
});
});
Update to your fiddle demo
The best way to do this is just give the elements a common class, to which you can bind a click handler and a css rule. This will accomplish your function of only having one button being pressed at a time, plus the ability to turn it on/off without affecting the others.
Javascript (jQuery):
$(function() {
$('.plain').click(function() {
$(this).toggleClass('selected');
$('.selected').not(this).removeClass('selected');
});
});
HTML
<div id="a" class="plain">
<p>A</p>
</div>
CSS
.plain {
width: 200px; height: 200px; margin: 20px; text-align:center; float: left;
font-size: 100px; color:#fff; background-color:red;
}
p { margin-top: 25%; margin-bottom:25%; }
.selected { background-color: blue; }
If you are doing the submitting with JavaScript, then this should be a much simpler approach: http://jsfiddle.net/EjW7A/15/
HTML
<div id="a" class="a1 toggleButton">
<p>A</p>
</div>
<div id="b" class="b1 toggleButton">
<p>B</p>
</div>
<button id ="test">test</button>
JavaScript
jQuery(function() {
jQuery(".toggleButton").click(function() {
jQuery(".toggleButtonToggled").removeClass("toggleButtonToggled");
jQuery(this).addClass("toggleButtonToggled");
});
jQuery("#test").click(function() {
var value = jQuery(".toggleButtonToggled:first").attr('id');
alert("Toggled button is: "+ value);
});
});