I am new to unit tests and want to create a test that will track the inner html after a click event which will indicate the switching between two users. I have done several searches and have not been able to figure how to properly attach the unit test to the event.
Most of the resources that I have found that are similar seem to be related to React and not Vanilla Javascript. This is my front-end code:
const xPlayer = 'X';
const oPlayer = 'O';
const boardSquares = document.querySelectorAll('.square');
let whosTurn = 0;
let board;
gameSetup();
function gameSetup() {
board = Array.from(Array(9).keys());
for (var i = 0; i < boardSquares.length; i++) {
boardSquares[i].innerText = '';
boardSquares[i].addEventListener('click', turnClick, false);
}
}
function turnClick(currentDiv) {
if(document.getElementById(currentDiv.target.id).innerHTML === '') {
writeInSquare(currentDiv);
}
}
function writeInSquare(currentDiv) {
if (whosTurn % 2 === 0) {
document.getElementById(currentDiv.target.id).innerHTML = xPlayer;
whosTurn++;
}
else {
document.getElementById(currentDiv.target.id).innerHTML = oPlayer;
whosTurn++;
}
}
export default{
boardSquares, whosTurn, gameSetup, turnClick, writeInSquare, checkIfGameComplete
};
This is my test file:
import sum from './index.js';
describe('sum', function() {
it('should change from xPlayer to oPlayer based on if whoseTurn is even or odd', function ({
}))
})
Html:
<div class="wrapper">
<div class="square" id="0">
</div>
<div class="square" id="1">
</div>
<div class="square" id="2">
</div>
<div class="square" id="3">
</div>
<div class="square" id="4">
</div>
<div class="square" id="5">
</div>
<div id="mocha"></div>
So basically there would be a switch between xplayer and yplayer that I would want to test to see if is happening from the click event
There is a way you could simulate an event on a DOM element manually. That's mentioned here. It involves creating a new Event (MouseEvent in your case) and then calling dispatchEvent with it, on the DOM element you wish to simulate the click. It's pretty tedious and has issues cross-browsers.
There is a popular library called simulant, that makes this really easy. A click event would be as simple as
simulant.fire( target, 'click' );
where target is the DOM element you want to simulate the click.
A simple demo where I simulate a click on a button.
const target = document.getElementById("target");
const simulate = document.getElementById("simulate");
target.addEventListener("click", () => alert("Target clicked!"))
simulate.addEventListener("click", () => simulant.fire(target, "click"))
<script src="https://unpkg.com/simulant/dist/simulant.umd.js"></script>
<button id="target">Click me</button>
<button id="simulate">Simulate</button>
Related
I've been playing around with jQuery for ages but am finally trying to learn clean Vanilla JS.
I have a list of elements:
<div id="seriesList" class="seriesList rollable">
<div class="seriesLink" series="7">
<h3 class="name">Carrow Road</h3><p class="location">Norwich</p>
</div>
<div class="seriesLink" series="6">
<h3 class="name">White Heart Lane</h3><p class="location">London</p>
</div>
<div class="seriesLink" series="5">
<h3 class="name">Parc des Princes</h3><p class="location">Paris</p>
</div>
</div
I'm toggling a series of GSAP animation after clicking one of the .seriesLink. The first one i'm trying to achieve is making every elements exept the one clicked disapear.
i.e: I click on #carrow-road — #white-lane and #parc-des-princes would disapear.
I have this:
document.querySelectorAll(".seriesLink").forEach(item => {
item.addEventListener('click', event => {
// ForEach.Not ?
document.getElementById("seriesList").classList.toggle("rollable");
document.getElementById("home").classList.add("scrollable");
document.getElementById("rightPanel").classList.remove("scrollable");
tlOpenSeries.play();
})
})
The "class" system in Javascript is getting me lost, as I don't seem to be able to target my elements successfully.
I can't find a way to "reproduce" the each.not jquery provides. Any idea? Shall I add a class first to the clicked element and then target all elements without this "active" class? Is there a shortcut?
Many thanks
To accomplish that in vanilla JS you have to loop through the elements and check if the current element is not the clicked element.
Demo:
var divs = document.querySelectorAll(".seriesLink");
divs.forEach(item => {
item.addEventListener('click', event => {
for(var i = 0; i < divs.length; i++){
if(event.currentTarget != divs[i]){ // check here
divs[i].style.display = "none";
}
}
//.......
//.......
});
});
<div id="seriesList" class="seriesList rollable">
<div class="seriesLink" series="7">
<h3 class="name">Carrow Road</h3><p class="location">Norwich</p>
</div>
<div class="seriesLink" series="6">
<h3 class="name">White Heart Lane</h3><p class="location">London</p>
</div>
<div class="seriesLink" series="5">
<h3 class="name">Parc des Princes</h3><p class="location">Paris</p>
</div>
</div>
you can use filter:
const seriesLinks = document.querySelectorAll(".seriesLink");
seriesLinks.forEach(item => {
item.addEventListener('click', event => {
seriesLinks
.filter(i => i != item)
.forEach(i => // your logic... //);
//... rest of your code ... //
})
})
but anymay i think that a good practice is to split the code to simple little functions, for example hideAllExceptCurrent (allElemArray, currentElem), hideAllToggleCurrent (allElemArray, currentElem)
I am trying to return the class of the parentnode of a selected element using Javascript. Here is what I have, but it's not working. What am I doing wrong?
function pClass(){
var pc = this.parentNode.className;
return pc;
}
thanks.
It works like a charm. What's the issue?
function pClass(){
var pc = this.parentNode.className;
console.log(pc);
return pc;
}
const btn = document.getElementById('btn');
btn.addEventListener('click', pClass);
<div class="parentFoo">
<button type="button" id="btn">Click me</button>
</div>
My hunch is that this isn't an html element. What do you get when you do a console.log(this);? Below is an example of how you can achieve what you're after. If you can show us a little more code (namely, how you're using your pClass function), it would help give you code that's more tailored to what you're doing.
const child = document.querySelector('#childDiv');
const parentClass = child.parentElement.className;
console.log(parentClass);
<div class="parent-div">
<div id="childDiv"></>
</div>
It may not be working becuase of the way you call pClass() - you did not show this in your code.
However, this method will work for you:
<div class="myClass">
<div id="myDiv" onclick="getParentClass(this)">Click Me!</div>
</div>
function getParentClass(elem) {
var pc = elem.parentNode.className;
alert("Parent class: " + pc)
}
I have created a show and function using Angular. At the moment i have the basics working. When the user hovers over tile the class is added and removed.
<article class="col-sm-6" ng-mouseenter="showHiddenTile()" ng-mouseleave="hideHiddenTile()">
<div class="grid-block--img">
<img src="/assets/homepage/home-tile5.png">
<div class="grid-headings">
<h2>a new<br/>home for<br/>whiskey</h2>
<p><span class="heading--bold">WORK.</b><span> Food and Beverage Design<
</div>
<div class="grid-block-hidden" ng-class="{isVisble: tileBlock}">My overlay</div>
</div>
</article>
I want to use this show and hide function multiple times throughout the site. At the moment when I hover over one of the elements it adds the isActive class to all elements instead of individually.
Angular code
// SHOW AND HIDE FUNCTION
$scope.showHiddenTile = function() {
$scope.tileBlock = true;
}
$scope.hideHiddenTile = function() {
$scope.tileBlock = false;
}
How can I target the isVisble class individually?
Have an array
$scope.array = [];
push it to array when mouseenter event
function showMethod(element){
$scope.array.push(element);
}
slice it from array when mouseleave event
function hideMethod(element){
$scope.array.slice($scope.array.indexOf(element),1);
}
use this condition in ng-class
ng-class="array['blockName'] != -1"
You could do something like:
<article class="col-sm-6" ng-mouseenter="showHiddenTile('block-id')" ng-mouseleave="hideHiddenTile('block-id')">
And:
$scope.showHiddenTile = function(blockId) {
$scope.tileBlock[blockId] = true;
}
$scope.hideHiddenTile = function(blockId) {
$scope.tileBlock[blockId] = false;
}
$scope.isShowTitle = function(blockId) {
return $scope.tileBlock[blockId];
}
And:
<div class="grid-block-hidden" ng-class="{isVisble: isShowTitle('block-id'}">My overlay</div>
And then have a unique block-id per article.
why not have 2 styles
.grid-block-hidden{
//style when mouse is not on
}
grid-block-hidden:hover{
//style on hover
//isVisble class
}
Project: Close all other divs with same class at once when we click on one, except of course the one we click on.
I found here 2 parts of code that I think could do it, but I do not know how to put it together.
To get my elements with same class (MaxPackage) - I am aware not all browser handle the getElement...
var elements = document.getElementsByClassName("MaxPackage");
for (var i = 0, len = elements.length; i < len; i++) {
// elements[i].style ... <-- I do not know what this means
}
And the part I found (as is) that could do the toggle part I think:
var prevId;
function toggle_visibility(id) {
if(prevId && id !== prevId){
$("#"+prevId).slideToggle("slow");
}
var e = document.getElementById(id);
$(e).slideToggle("slow");
prevId = id;
}
Thank you for your help, I am not a programmer, so please do not be afraid to explain :) Then I will need to know how to trigger it (onload or add function in element)?
Edited: I got a very simple solution:
$('div.MaxPackage').click(function(){
$('div.MaxPackage').hide();
$(this).show();
});
Try this, which is a very simple vanilla javascript way to get what you want
<div class="MaxPackage" onclick="closeThem(event)">one</div>
<div class="MaxPackage" onclick="closeThem(event)">two</div>
<div class="MaxPackage" onclick="closeThem(event)">three</div>
<div class="MaxPackage" onclick="closeThem(event)">four</div>
<script type="text/javascript">
function closeThem(e) {
var divs = document.getElementsByClassName('MaxPackage');
for (var i=0; i < divs.length; i++) {
if (e.target !== divs[i]) {
divs[i].style.display = 'none';
}
}
}
</script>
In jQuery the following will work
function addSingleSelect(classToSelect) { // Pass in the name of the class
$('.' + classToSelect).click(
function(event) {
var selected = event.target; // Get Selected Click Target
$('.' + classToSelect).css('display', 'none'); // Hide EVERYTHING
$(selected).css('display','') // Show the selected item
}
);
}
Assume you had
<div id="1" class="MaxPackage">Max 1</div>
<div id="2" class="MaxPackage">Max 2</div>
<div id="3" class="MaxPackage">Max 3</div>
you could call
addSingleSelect('MaxPackage');
and do the same for anything else as well with a different class
HTML
<div id="4" class="MinPackage">Min 4</div>
<div id="5" class="MinPackage">Min 5</div>
<div id="6" class="MinPackage">Min 6</div>
JS
addSingleSelect('MinPackage');
So I have a mini slide menu in my website there is a menu you can choose what you want to read. There are points to click, when u clicked it the point get a red background.
But there is a problem.
When i click one point and then an other point the first clicked point have to lose his background.
Here is my HTML:
<div id="slide_button" onClick="clicked(this);"><dir class="button_1"></dir></div>
<div id="slide_button" onClick="clicked(this);"><dir class="button_2"></dir></div>
<div id="slide_button" onClick="clicked(this);"><dir class="button_3"></dir></div>
<div id="slide_button" onClick="clicked(this);"><dir class="button_4"></dir></div>
<div id="slide_button" onClick="clicked(this);"><dir class="button_5"></dir></div>
Here is my JS:
function clicked(slide_button) {
slide_button.getElementsByTagName("dir")[0].style.backgroundColor="red";
}
HERE IS AN EXAMPLE ON FIDDLE.
My "QUESTION IS" what i have to do to solve that?
What should I pay attention?
First you need to fix your HTML becaue your id values aren't unique. In fact, you don't even need id values, so you should use "slide_button" as a class. You can then use it to select all the buttons:
<div onClick="clicked(this);" class="slide_button"><dir></dir></div>
<div onClick="clicked(this);" class="slide_button"><dir></dir></div>
<div onClick="clicked(this);" class="slide_button"><dir></dir></div>
<div onClick="clicked(this);" class="slide_button"><dir></dir></div>
<div onClick="clicked(this);" class="slide_button"><dir></dir></div>
The CSS needs to be changed now so "slide_button" is a class selector, instead of an id selector:
.slide_button {
display: inline-block;
}
As for clearing the background, clear all of them before coloring the selected one red:
function clicked(slide_button) {
var buttons = document.getElementsByClassName('slide_button');
for(var i = 0; i < buttons.length; i++) {
buttons[i].getElementsByTagName('dir')[0].style.backgroundColor = '';
}
slide_button.getElementsByTagName('dir')[0].style.backgroundColor = 'red';
}
jsfiddle
This uses just JavaScript with no JQuery, but if you are using JQuery, you might as well use it here. The code is a lot shorter and easier to follow.
Here's a JQuery version:
$(function() {
$('.slide_button').click(function() {
var $button = $(this);
$button.children(':first').css({ backgroundColor: 'red' });
$button.siblings().children(':first').css({ backgroundColor: '' });
});
});
Note: This registers a click-handler, so you can get rid of the "onclick" attirbutes.
jsfiddle
You have to select all other points and set their background to none.
Or remeber which point is selected and on select another just remove background on last and remeber current point, then set its background to red.
See fiddle: http://fiddle.jshell.net/399Dm/5/
At first id should be unique per element.
<div class="slide_button"><dir class="button"></dir></div>
<div class="slide_button"><dir class="button"></dir></div>
<div class="slide_button"><dir class="button"></dir></div>
<div class="slide_button"><dir class="button"></dir></div>
<div class="slide_button"><dir class="button"></dir></div>
Second, you should store reference of clicked element if you want later remove background color, and instead of inline event handlers or binding all elements would be better if you use event delegation.
Demonstration
(function () {
"use strict";
// getting parent node of divs, due to bind click event. then
var ele = document.querySelector(".slide_button").parentNode,
prev = null; // store previous clicked element
ele.addEventListener("click", clickHandler); // event handler.
function clickHandler(e) {
var t = e.target; // get target of clicked element
// filter by target node name and class. edit: removed class checking
if (t.nodeName.toLowerCase() === "dir") {
// checking value of prev !== null and it's not same element.
if (prev && prev !== t) {
prev.style.backgroundColor = "";
}
prev = t; // store clicked element
t.style.backgroundColor = "red";
}
}
}());
I have fixed the fiddle so that it works hopefully as you plan.
http://jsfiddle.net/399Dm/8/ There you go!
var forEach = function(ctn, callback){
return Array.prototype.forEach.call(ctn, callback);
}
function clear(element, index, array) {
element.getElementsByTagName("dir")[0].style.backgroundColor="";
}
function clicked(slide_button) {
forEach(document.getElementsByClassName("slide_button"), clear);
//.style.backgroundColor="";
slide_button.getElementsByTagName("dir")[0].style.backgroundColor="red";
}
I had a slightly different method than #atlavis but a similar result.
http://fiddle.jshell.net/2AGJQ/
JSFIDDLE DEMO
jQuery
$('.slide_button').click(function(){
$('.slide_button dir').css("background-color", "inherit");
$(this).find('dir').css("background-color", "red");
});
HTML - Your markup is invalid because you have duplicate ids. Make them classes as below instead.
<div class="slide_button" >
<dir class="button_1"></dir>
</div>
<div class="slide_button">
<dir class="button_2"></dir>
</div>
<div class="slide_button">
<dir class="button_3"></dir>
</div>
<div class="slide_button">
<dir class="button_4"></dir>
</div>
<div class="slide_button">
<dir class="button_5"></dir>
</div>
CSS change
.slide_button {
display: inline-block;
}
If you can look at the following jsfiddle, I used jQuery to get what you want.