I made a simple website who contains a sidebar. On click on the Sidebar Icon, it should open or close. The following code that I wrote does extactly that.
/** toggleStatus is a anonymous function and can be called with its name */
let navStatus = false;
let toggleStatus = function () {
let getSidebar = document.querySelector(".sidebar");
let getSidebarUl = document.querySelector(".sidebar ul");
let getSidebarLinks = document.querySelectorAll(".sidebar a");
if (navStatus === false) {
// if Sidebar is closed
closeMenu();
getSidebarUl.style.visibility = "visible";
getSidebar.style.width = "272px"; // change width of sidebar so the content can fit
let arrayLength = getSidebarLinks.length;
for (let i = 0; i < arrayLength; i++) {
// Smake every List item Visible
getSidebarLinks[i].style.opacity = "1";
}
navStatus = true;
} else {
// if Sidebar is open
getSidebarUl.style.visibility = "hidden";
getSidebar.style.width = "50px"; // change width of sidebar to the base Design
let arrayLength = getSidebarLinks.length;
for (let i = 0; i < arrayLength; i++) {
// make every List item invisible
getSidebarLinks[i].style.opacity = "0";
}
navStatus = false;
}
};
Now I have to write a test with Jest for this function, but I have no idea where to begin with. I cant feed it with any Input and compare the expected Output. Does anyone have some hints for a newbie?
Thank you!
If your functions don't have explicit output it doesn't mean it does nothing. The function has some "behavior" and in your case, it does some DOM manipulations. So you should test for it.
You have two options to approach it:
Mock needed DOM API functions and expect mocked object's props in the shape you like.
Use other tools to render actual HTML with snapshot testing.
Related
I am trying to alternate the innerHTML of a at set Intervals.
Dear friends,
I am new to coding. I have created a div with an image, and a < p > element (that includes a < span >.
I have assigned two classes to the div, and I want it to alternate between the 2 classes at set intervals.
In addittion, I am trying to toggle the text inside the span, by using innerHTML.
So far I have managed succesfully to toggle the class, but I can't make the innerHTML to work.
I have the following code:
if(categProducts[idx].discount && categProducts[idx].low){
var Interval = setInterval(
function changeClass(){
document.getElementById('myDiv').classList.toggle("low");
},3000
)
var Interval2= setInterval(function changeText(){
var x=document.getElementById('mySpan').innerHTML
if (x==="<br> Only Few Cakes Left!!"){
x.innerHTML="<br> Discount! Best Price!!"
}
else {
x="<br> Only Few Cakes Left!!"
}
console.log(x)
}, 3000)
}
So far, the innerHTML of the only toggles once, and then it doesn't change again.
I can't make it work and I don't understand why.
The rest of the code is the following:
for (let idx in categProducts){
if (categProducts[idx].category==="cakes") {
const parentElement=document.getElementById("divCakes")
const myDiv=document.createElement("div")
parentElement.appendChild(myDiv)
myDiv.className="product"
const myImg=document.createElement("img")
myImg.src=categProducts[idx].imageURI
myImg.alt=categProducts[idx].alt
myDiv.appendChild(myImg)
myDiv.id="myDiv"
const myP=document.createElement("p")
myP.innerHTML=categProducts[idx].name
myDiv.appendChild(myP)
mySpan=document.createElement("span")
myP.appendChild(mySpan)
mySpan.id="mySpan"
IMO, you should define your desired classes and content inside arrays. Works for two, and more.
const changeContent = (() => {
const classes = ['first-class', 'second-class'];
const spanText = ['first text', 'second text'];
let index = 0;
return function() {
document.getElementById('mySpan').innerHTML = "";
document.getElementById('myDiv').classList = "";
document.getElementById('mySpan').innerHTML = spanText[index];
document.getElementById('myDiv').classList = classes[index];
index++;
if(index === classes.length) {
index = 0;
}
}
})();
setInterval(changeContent, 3000);
This functions uses closures to define global variables.
I have a little JS script I wrote to rename layer names so that they're all unique. It just recursively loops through all the layers in a Photoshop document you have open and will make a list of all the names. If it finds that a name is already in that list, it will append a number to the layer name until it finds a unique name.
I have that working, however one odd behaviour is that in the course of this script running it turns every layer visible. No part of my script is intended to affect visibility. And for testing, I added lines to turn off visibility on every layer after they were checked/renamed, but that doesn't change anything. Every layer is still visible at the end.
For reference, I'm using Photoshop CS2, and the scripts are run through File > Scripts > Rename Duplicate Layers
Here's the two lines I used for disabling visibility (that didn't work):
layerSet.artLayers[i].visibility = false;
and
layerSet.layerSets[i].visibility = false;
And here's the full body of the regular script, that doesn't attempt to change visibility at all.
function main()
{
if (app.documents.length <= 0)
{
return;
}
var doc = app.activeDocument;
layerNames = [];
renameLayerNames(doc, layerNames);
alert("Script complete.");
}
// Recursively iterate over layers to rename them
function renameLayerNames(layerSet, layerNames)
{
var name = '';
for (var i = 0; i < layerSet.artLayers.length; i++)
{
name = layerSet.artLayers[i].name;
name = uniqueName(name, layerNames);
layerSet.artLayers[i].name = name;
}
for (var i = 0; i < layerSet.layerSets.length; i++)
{
name = layerSet.layerSets[i].name;
name = uniqueName(name, layerNames);
layerSet.layerSets[i].name = name;
// Recurse
renameLayerNames(layerSet.layerSets[i], layerNames);
}
}
// Ensure name is unique, or add an incrementing number to it.
function uniqueName(name, layerNames)
{
dupe = 0;
original = name;
while (contains(layerNames, name)){
dupe++;
name = original + ' ' + dupe.toString();
}
layerNames.push(name);
return name;
}
// Check if array contains object
function contains(array, object)
{
for (var i = 0; i < array.length; i++){
if (array[i] == object){
return true
}
}
return false
}
I believe the property is visible not visibility i.e.
layerSet.artLayers[i].visible = false;
layerSet.layerSets[i].visible = false;
When I try the code referenced in SO #1, I get the console logging a blank string:
installChoices() {
var choices = this.game.page.options;
for (var i = 0; i < choices.length; i++) {
var choice = choices[i];
var choiceDiv = document.createElement("choice" + i);
choiceDiv.innerText = choice[0];
choiceDiv.onclick = function() {
console.log(this.id);
}
this.choicesContainer.appendChild(choiceDiv);
}
}
I want to bind to my class function clicked
installChoices() {
var choices = this.game.page.options;
for (var i = 0; i < choices.length; i++) {
var choice = choices[i];
var choiceDiv = document.createElement("choice" + i);
choiceDiv.innerText = choice[0];
choiceDiv.onclick = this.clicked;
this.choicesContainer.appendChild(choiceDiv);
}
}
clicked(e) {
console.log(e.parentNode); //this is undefined
console.log(e.srcElement);
}
But that shows undefined. When I log srcElement, I get the full element
<choice0>path 1</choice0>
I want to get just the div id when I click, so I can parse that and do logic.
I'd recommend the following approach, as it is the standard:
//assign the event
choiceDiv.addEventListener('click', clicked)
//define the listener
function clicked(event) {
console.log(event.currentTarget)
}
update:
I'm tempted to offer a fix to your code, because I don't think you're achieving what are you trying to actually do:
function installChoices() {
var choices = this.game.page.options;
for (var i = 0; i < choices.length; i++) {
var choice = choices[i];
var choiceDiv = document.createElement("div");
choiceDiv.id = "choice" + i;
choiceDiv.innerText = choice[0];
choiceDiv.addEventListener("click", clicked);
this.choicesContainer.appendChild(choiceDiv);
}
}
function clicked(ev) {
console.log(ev.currentTarget.id); //this will log "choice0"
}
Your "clicked" function are receiving an Event rather than a HTML Element. This is the default behavior when an onClick event triggered.
The click event have a srcElement property, indicating the source element the click event occurred upon. This event object have no parentNode property.
Use e.srcElement.parentNode instead.
BTW, in the SO #1 example, it assign "showIt(this)" to onClick, so browser pass "this", the target element rather than the event object, to the onClick function.
i have made a JSFiddle to show the problem
http://jsfiddle.net/molokoloco/yvTje/
In few words, i put some listener on the animationStart event, add a new class to the element and then, in a plugin i do, i need to check if the element have an animation (can be associated with the class) before doing, or not, something.
My probleme is i have to wait 25/80 millisecond before it's possible to check if an animation is started or not...
Something i do bad or any suggestion ?
var animationStarted = false,
s = '';
var listener = function(e) {
switch (e.type) {
case "animationstart":
case "webkitAnimationStart":
// BUT "animationstart" do not trigger instantaneously
// For the moment i compute 25 milliseconds on my Chrome & FF
var diff = (new Date().getTime()) - s;
console.log('animationStarted after ' + diff + ' ms'); // HERE THE RESULT : 30ms
break;
}
};
var setup = function() {
var e = document.getElementById("watchme");
e.addEventListener("animationstart", listener, false);
e.addEventListener("webkitAnimationStart", listener, false);
// HERE WE ADD THE CLASS
e.className = "slidein";
s = new Date().getTime(); // Time at witch the class (with anim) is applyed
};
setup();
Use feature detection even before you try to start the animation.
var div = document.createElement("DIV");
div.style["animation"] = "animName 5s infinite";
div.style["WebkitAnimation"] = "animName 5s infinite";
if (div.style["animationDuration"] == "5s") {
// Supports animation
} else (div.style["WebkitAnimationDuration"] == "5s") {
// Supports -webkit-animation
}
This tests for the browser's ability to set individual style attributes using shorthand. If you just set a value and read it back the same way, you'll get false positives.
EDIT: Sorry, misunderstood the problem.
Regarding using events to check for stuff, you will probably have to wait a few milliseconds, no matter what you do... You could check the style definitions themselves using document.styleSheets, but that would be pretty exhausting, depending on the specificity of the css.
var animationName = "";
for (var i = 0; i < document.styleSheets.length; ++i) {
var sheet = document.styleSheets[i];
var rules = sheet.cssRules || sheet.rules;
for (var j = 0; j < rules.length; ++j) {
var rule = rules[j];
if (theElement.matchesSelector(rule.selectorText)) {
var theStyle = rule.style;
animationName = theStyle["WebkitAnimationName"];
}
}
}
It's a pretty crude way of doing this, I know...
Quite new to OO Js, used to program with function after function so trying to fix that now!
I'm making a tab layout -
I create a tab by calling: tab.NewTab();
I can access the tabs at tab[0], tab[1] etc
var tabCount = 0;
var tabs = [];
tabs.NewTab = function (){
var tabName = "tab" + tabCount;
tabs[tabCount] = new Tab(tabName);
tabCount++;
};
function Tab(tabName){
return{
name: tabName
}
}
I wanted to make a function that counts how many tabs are open:
tabs.HowMany = function () {
for (var i in tabs) {
alert("new");
}
};
This is returning the methods too (0,1, NewTab, HowMany).
Any advice?
You're looking for tabs.push(new Tab(tabName));
Then ditch tabCount, and instead use the length property native to all javascript arrays: tabs.length
Also, your Tab constructor is wrong. As it is currently written it should not be called with new. Just call Tab('someName') and it will return to you the object you're looking for. If you do that however, change it to tab since non-constructor functions should be lowercase.
If you're really eager to use the new keyword, this is what Tab should look like:
function Tab(tabName){
this.name = tabName;
}
EDIT
If you want to iterate over all members of your array, this is the simplest way:
for (var i = 0; i < tabs.length; i++)
var currentTab = tabs[i];
You need to change var tabs = []; to var tabs = new Array();.
And add items to it using tabs.push(new Tab(tabName));
Simple count:
var count = tabs.lenght
Enumeration of items:
for (var i = 0; i < tabs.length; i++) {
alert(tabs[i]);
}
You could do:
tabs.HowMany = function () {
for (var i in tabs) {
if(tabs.hasOwnProperty(i)){
if(typeof tabs[i] !== 'function'){
alert('new')
}
}
}
};
but you'd better switch using it as array
tabs.push(new Tab(tabName))
tabs.HowMany = function () {
return tabs.length
};