"DRY" javascript functions? - javascript

I have a fair amount of functions like the ones below, all with some slight changes and querying different ids. Is there some way to concatenate this or will I just have to write them all out? Thanks.
function event() {
var path = document.querySelector('#container #path');
var length = path.getTotalLength();
console.log(path.getTotalLength());
path.style.transition = path.style.WebkitTransition =
'none';
path.style.strokeDasharray = length + ' ' + length;
path.style.strokeDashoffset = length;
path.getBoundingClientRect();
path.style.transition = path.style.WebkitTransition =
'stroke-dashoffset 0.4s linear';
path.style.strokeDashoffset = '0';
document.getElementById("container").style.visibility = "visible";
};
function event2() {
var path2 = document.querySelector('#container2 #path_2');
var length = path2.getTotalLength();
console.log(path2.getTotalLength());
path2.style.transition = path2.style.WebkitTransition =
'none';
path2.style.strokeDasharray = length + ' ' + length;
path2.style.strokeDashoffset = length;
path2.getBoundingClientRect();
path2.style.transition = path2.style.WebkitTransition =
'stroke-dashoffset 0.45s linear';
path2.style.strokeDashoffset = '0';
document.getElementById("container2").style.visibility = "visible";
};
function event3() {
var path3 = document.querySelector('#container3 #path_3');
var length = path3.getTotalLength();
console.log(path3.getTotalLength());
path3.style.transition = path3.style.WebkitTransition =
'none';
path3.style.strokeDasharray = length + ' ' + length;
path3.style.strokeDashoffset = length;
path3.getBoundingClientRect();
path3.style.transition = path3.style.WebkitTransition =
'stroke-dashoffset 0.4s linear';
path3.style.strokeDashoffset = '0';
document.getElementById("container3").style.visibility = "visible";
};
window.setTimeout(function() {
event();
}, 1);
window.setTimeout(function() {
event2();
}, 550);
window.setTimeout(function() {
event3();
}, 1100);

If I have a series of nearly identical functions
function showNumber1() {
window.alert("The number you wish to show is 1");
}
function showNumber2() {
window.alert("The number you wish to show is 2");
window.alert("Okay that's all");
}
function showNumber3() {
window.alert("The number you wish to show is 3");
}
I can usually replace them with a single function, like
function showNumber(n) {
window.alert("The number you wish to show is " + n);
if (n == 2) {
window.alert("Okay that's all");
}
}
So, rewrite your functions into a single function containing the parts that stay the same, and use parameters and if statements to represent the parts that differ.
Also, you seem to be using IDs as if they are classes. Instead of having
<div id="path_1"> ... <div id="path_2"> ...
You could use a class
<div class="path"> ... <div class="path"> ...
and identify them in your javascript using document.getElementsByClassName('path')[0] and document.getElementsByClassName('path')[1] .

Related

Letter by letter fading in in javascript without jquery

var i = 0;
var txt = 'UNITING PEOPLE'; /* The text */
var txt2 = ' &';
var txt3 = 'DELIVERING ';
var txt4 = 'INTELLIGENCE';
var txt5 = 'Access your hub for Open Journalism';
var speed = 0;
function typeWriter(fade) {
if (i < txt.length + txt2.length + txt3.length + txt4.length + txt5.length) {
if(i < txt.length-1) {
var element = document.createElement("p");
element.style.display = "inline-block";
element.style.opacity = "0.1";
element.style.fontSize = "38px";
element.style.fontFamily = "Helvetica-Bold";
element.style.whiteSpace = "pre";
element.style.marginTop = "0";
element.textContent += txt.charAt(i);
document.getElementById("words").children[0].children[0].appendChild(element)
unfade(element, fade);
i++;
setTimeout(typeWriter(fade), 10*fade);
} else if(i==txt.length-1) {
var element = document.createElement("p");
element.style.display = "inline-block";
element.style.opacity = "0.1";
element.style.fontSize = "38px";
element.style.fontFamily = "Helvetica-Bold";
element.style.whiteSpace = "pre";
element.style.marginTop = "0";
element.textContent += txt.charAt(i);
document.getElementById("words").children[0].children[0].appendChild(element)
unfade(element, fade);
i++;
setTimeout(typeWriter(fade), 1500);
} else if(i < txt.length + txt2.length-1)
...
}
setTimeout(typeWriter(100), 1500);
function unfade(element, fade) {
var op = 0.1; // initial opacity
element.style.display = 'inline-block';
var timer = setInterval(function () {
if (op >= 1){
clearInterval(timer);
}
element.style.opacity = op;
element.style.filter = 'alpha(opacity=' + op * 100 + ")";
op += op * 0.1;
}, fade);
}
I'm not sure what the bug is. Each letter should fade through about 100ms, and it should wait until each letter is done before the next letter begins.
Each element comes in inline and begins with a small opacity in the right font and font size, with spaces making room for text with the whitespace set to pre.
The text content updates with just 1 character at a time, and it's appended to the correct section before being faded in. The fading in is set to be the same as the overall letter fade, before the increment is counted.
Once the set timeout occurs, the delay fade is simply ignored and it all begins at once, so it's like the fade is set to 0 over all.
And yet when the code is cut and one section implemented it does one letter at a time.

Outputting an image in JavaScript onto a hangman game

I am creating a hangman game and I have to output images, when the users lives get down from 7-0.
I have put this code into my javascript , however it only outputs the if(lives ==8) image.
Unfortunality it does not seem to work at all ?
I am very new to javascript and when I have read the other answers, I have found them hard to understand.
The fact that it doesn't output , does this mean I have an error in my code somewhere. ?
function setup() {
alphabet = "abcdefghijklmnopqrstuvwxyz";
lives = 8;
var words = ["ayeupmeducks", "pieceofcake", "bullinachinashop", "hangfire","greeneyedmonster", "hairraising","bringhomethebacon","adiamondintherough","onceinabluemoon","afootinthedoor","bitethebullet"];
messages = {
win: 'Congratulations you have won the game of Hangman!',
lose: 'You have been Hanged !!',
guessed: ' already guessed, please try again...',
validLetter: 'Please enter a letter from A-Z'
};
var getHint = document.getElementById("hint");
var showClue = document.getElementById("clue");
getHint.onclick = function() {
hints =
["Stoke Greeting","Saying Something is Easy", "Very Clumsy","delaying something for a minute", "When you are jealous of something","Something is frightening", "Earn Money", "Rough Exterior however has some potential", "When something rarely happens", "When you have succeeded/ getting yourself known by a company","accepting something , when you do not want to"];
var hintIndex = words
showClue.innerHTML = "Clue: - " + hints [idx];
};
gussedLetter = matchedLetter = '';
numberofMatchedLetters = 0;
/* This chooses the word which will be displayed on the page */
idx = Math.floor(Math.random() * words.length);
currentWord = words[idx];
output = document.getElementById("output");
message = document.getElementById("message");
guessInput = document.getElementById("letter");
message.innerHTML = 'You have ' + lives + ' lives remaining';
output.innerHTML = '';
document.getElementById("letter").value = '';
guessButton = document.getElementById("guess");
guessInput.style.display = 'inline';
guessButton.style.display = 'inline';
letters = document.getElementById("letters");
letters.innerHTML = '<li class="current-word">Current word:</li>';
var letter, i;
for (i = 0; i < currentWord.length; i++) {
/* returns the character at the specified index in a string.*/
letter = '<li class="letter letter' + currentWord.charAt(i).toUpperCase() + '">' + currentWord.charAt(i).toUpperCase() + '</li>';
/* inserts the results node into Dom at the correct place, The BeforeEnd- inside the element, after its last child.*/
letters.insertAdjacentHTML('beforeend', letter);
}
}
function gameOver(win) {
if (win) {
output.innerHTML = messages.win;
output.classList.add('win');
} else {
output.innerHTML = messages.lose;
output.classList.add('error');
}
guessInput.style.display = guessButton.style.display = 'none';
guessInput.value = '';
}
window.onload = setup();
document.getElementById("restart").onclick = setup;
guessInput.onclick = function () {
this.value = '';
};
document.getElementById('hangman').onsubmit = function (e) {
if (e.preventDefault) e.preventDefault();
output.innerHTML = '';
output.classList.remove('error', 'warning');
guess = guessInput.value;
if (guess) {
if (alphabet.indexOf(guess) > -1) {
if ((matchedLetter && matchedLetter.indexOf(guess) > -1) || (gussedLetter && gussedLetter.indexOf(guess) > -1)) {
output.innerHTML = '"' + guess.toUpperCase() + '"' + messages.guessed;
output.classList.add("warning");
}
else if (currentWord.indexOf(guess) > -1) {
var lettersToShow;
lettersToShow = document.querySelectorAll(".letter" + guess.toUpperCase());
for (var i = 0; i < lettersToShow.length; i++) {
lettersToShow[i].classList.add("correct");
}
for (var j = 0; j < currentWord.length; j++) {
if (currentWord.charAt(j) === guess) {
numberofMatchedLetters += 1;
}
}
matchedLetter += guess;
if (numberofMatchedLetters === currentWord.length) {
gameOver(true);
}
}
else {
gussedLetter += guess;
lives--;
message.innerHTML = 'You have ' + lives + ' lives remaining';
if (lives === 0) gameOver();
}
}
else {
output.classList.add('error');
output.innerHTML = messages.validLetter;
}
}
else {
output.classList.add('error');
output.innerHTML = messages.validLetter;
}
return false;
};
var x = document.createElement("IMG");
if (lives==8){
x.setAttribute("src", "Hangman-0.png");
x.setAttribute("width", "304");
x.setAttribute("height", "228");
x.setAttribute("alt", "Hangman");
document.body.appendChild(x);}
if (lives==7){
x.setAttribute("src", "Hangman-1.png");
x.setAttribute("width", "304");
x.setAttribute("height", "228");
x.setAttribute("alt", "Hangman");
document.body.appendChild(x);}
From looking at you code, you can simplify a lot - each image has the same height, width, and alt, so you can move all of that out of the function. Also, assuming the src attribute is just a number that is incrementing, you can instead do something like:
var num_lives = 8;
var lives_left = 8;
function guess() {
lives_left--;
x.setAttribute('src', 'Hangman-' + (num_lives - lives_left) + '.png');
}
So, as a working example, your code would basically be:
var url = 'https://www.oligalma.com/downloads/images/hangman/files/';
const num_lives = 10;
var lives = 10;
var x = document.createElement("IMG");
x.setAttribute("width", "304");
x.setAttribute("height", "228");
x.setAttribute("alt", "Hangman");
x.setAttribute("src", url + (num_lives - lives) + '.jpg');
document.body.appendChild(x);
function change() {
lives--;
if (lives < 0) {
lives = num_lives;
}
x.setAttribute("src", url + (num_lives - lives) + '.jpg');
}
<button onClick="change()">Guess</button>

JQuery Letters fade in randomly

This is my first time coding in JQuery. Below is my code:
var div = document.getElementById('fadeletters1'),
letters = div.textContent.split('');
while(div.hasChildNodes()) div.removeChild(div.firstChild);
for(var i = 0; i < letters.length; i++) {
var letter = document.createElement('span'),
style = 'opacity ' + (Math.random() * 5 + 1) + 's linear';
letter.appendChild(document.createTextNode(letters[i]));
letter.style.WebKitTransition = letter.style.transition = style;
letter.style.opacity = 0;
div.appendChild(letter);
}
setTimeout(function() {
for(var i = 0; i < div.childNodes.length; i++) {
div.childNodes[i].style.opacity = 1;
}
}, 0);
<div id=fadeletters1>Helllooo This is a test for the website</div>
So the letters do fade in but then in the starting of the animation, letter are kinda visible and then fades in after couple seconds. I want it to pop up from 0 visibility to 100 visibility as its fades in.
I am trying to acheive something like this site does: http://method.digital/
You're changing the rate at which the transition takes place, instead you want to change the delay before the transition starting:
var div = document.getElementById('fadeletters1'),
letters = div.textContent.split('');
while(div.hasChildNodes()) div.removeChild(div.firstChild);
for(var i = 0; i < letters.length; i++) {
var letter = document.createElement('span'),
style = 'opacity 0.6s linear',
delay = (Math.random() * 4) + 's';
letter.appendChild(document.createTextNode(letters[i]));
letter.style.WebKitTransition = letter.style.transition = style;
letter.style.WebKitTransitionDelay = letter.style.transitionDelay = delay;
letter.style.opacity = 0;
div.appendChild(letter);
}
setTimeout(function() {
for(var i = 0; i < div.childNodes.length; i++) {
div.childNodes[i].style.opacity = 1;
}
}, 0);
<div id=fadeletters1>Helllooo This is a test for the website</div>
If you want to run it for multiple divs you can wait until the first is complete before moving onto the next (this example only appears to work sometimes, make sure to hit refresh before running):
function RunAnimation(target,delay) {
var div = document.getElementById(target),
letters = div.textContent.split('');
while (div.hasChildNodes()) div.removeChild(div.firstChild);
setTimeout(function(){
for (var i = 0; i < letters.length; i++) {
var letter = document.createElement('span'),
style = 'opacity 0.6s linear',
delay = (Math.random() * 4) + 's';
letter.appendChild(document.createTextNode(letters[i]));
letter.style.WebKitTransition = letter.style.transition = style;
letter.style.WebKitTransitionDelay = letter.style.transitionDelay = delay;
letter.style.opacity = 0;
div.appendChild(letter);
}
setTimeout(function() {
for (var i = 0; i < div.childNodes.length; i++) {
div.childNodes[i].style.opacity = 1;
}
}, 0);
}, delay);
}
RunAnimation('fadeletters1')
RunAnimation('fadeletters2', 5000);
RunAnimation('fadeletters3', 10000);
<div id="fadeletters1">Helllooo This is a test for the website</div>
<div id="fadeletters2">This is a second div which also fades in</div>
<div id="fadeletters3">And who knows maybe you want a third</div>
The thing is there is two delays...
One for the animation and one for a timeout. This is the timeout that really gives the effect you are looking for.
// Get the letters from the original string.
var letters = $("#fadeletters1").text().split("");
// Remove the original string.
$("#fadeletters1").text("");
// Create a span for each letters and append them to the document.
letters.forEach(function(item,index){
var span = $("<span class='fade'>").text(item);
$("#fadeletters1").append(span);
});
// Animate each spans
$(document).find(".fade").each(function(){
// Random delay
var delay = Math.random();
var letter = $(this);
// Set a timeout to animate the spans
setTimeout(function(){
letter.animate({"opacity":1},delay*1000);
},delay*3000);
});
.fade{
opacity:0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="fadeletters1">Helllooo This is a test for the website</div>

Randomize/shuffle cards in stack with jQuery

I am building a very simple memory game for a small project. The logic is as follows:
click on the input field to choose with how many pairs would you like to play
create divs with classes card1, card2 etc.
clone divs and randomize their place in the array
Here is my script (fork in JSFiddle):
$(".button").click(function () {
// get the value from the input
var numCards = parseInt($('input').val());
for (var i = 1; i <= numCards; i++) {
// create the cards
$(".container").append("<div class='card" + i + " cards'></div>") &&
$(".card" + i).clone().appendTo(".container");
}
// randomize cards in stack
var cards = $(".cards");
for (var i = 0; i < cards.length; i++) {
var target = Math.floor(Math.random() * cards.length - 1) + 1;
var target2 = Math.floor(Math.random() * cards.length - 1) + 1;
var target3 = Math.floor(Math.random() * cards.length - 1) + 1;
cards.eq(target).before(cards.eq(target2)).before(cards.eq(target3));
}
});
what I need now is to adjust the 3rd step, meaning to dynamically create the target vars, and the last line of the code
cards.eq(target).before(cards.eq(target2)).before(cards.eq(target3));
So please make me a suggestion - how would you do it? And bare in mind this is a project for beginners. Thank you!
$(".button").click(function () {
// get the value from the input
var numCards = parseInt($('input').val());
for (var i = 1; i <= numCards; i++) {
// create the cards
$(".container").append("<div class='card" + i + " cards'></div>") &&
$(".card" + i).clone().appendTo(".container");
}
var parent = $(".container");
var divs = parent.children();
while (divs.length) {
parent.append(divs.splice(Math.floor(Math.random() * divs.length), 1)[0]);
}
});
see jsfidle: http://jsfiddle.net/007y4rju/8/
source: http://jsfiddle.net/C6LPY/2/
Here is the version of the code in jsfiddle - http://jsfiddle.net/007y4rju/6/
Please, check if the behavior is consistent with the original code.
$(document).ready(function () {
$(".button").click(function () {
// get the value from the input
var numCards = parseInt($('input').val());
for (var i = 1; i <= numCards; i++) {
// create the cards
$(".container").append("<div class='card" + i + " cards'></div>") &&
$(".card" + i).clone().appendTo(".container");
}
// randomize cards in stack
var cards = $(".cards");
var startTarget = Math.floor(Math.random() * cards.length - 1) + 1;
var collection = cards.eq(startTarget);
var nextTarget;
var i;
for (i = 0; i < cards.length; i++) {
nextTarget = Math.floor(Math.random() * cards.length - 1) + 1;
collection.before(cards.eq(nextTarget));
}
});
});
You can randomize index in a class name (card%i%) when cloning divs. Then you don't need to shuffle cloned divs; you can append them as is.
$(".button").click(function () {
// get the value from the input
var numCards = parseInt($('input').val());
for (var i = 1; i <= numCards; i++) {
// create the cards
$(".container").append("<div class='card" + i + " cards'></div>");
}
var aIndices = [];
for (var i = 1; i <= numCards; i++) {
var ix;
do ix = Math.round(Math.random() * (numCards - 1)) + 1;
while (aIndices.indexOf(ix) >= 0);
aIndices.push(ix);
// clone
$(".card" + ix).clone().appendTo(".container");
}
});

Weird Javascript behavior that involves counters

I call the function
animate(0,"mainNav",0.02,0.04);
And the declaration
function animate(num, element, transitionUnit, delayUnit) {
var delay = 0;
transition = 0;
//var delayUnit = 0.02; transitionUnit = 0.04;
var x = document.getElementById(element).getElementsByTagName("LI");
for (i = 0; i <= x.length - 1; i++) {
x[i].style.WebkitTransform = "translate3d(" + num + "px,0,0)";
x[i].style.transition = transition + "s " + delay + "s ease-in-out";
delay += delayUnit;
transition += transitionUnit;
if (x[i].querySelectorAll('ul li').length > 0) {
x[i].style.background = "rgba(0,0,0,0.35)";
}
}
}
If I uncomment...
//var delayUnit = 0.02; transitionUnit = 0.04;
...It works flawlessly.
But I want to use it with other functions that send different delayUnits and transitionUnits. Am I missing something?
Explicitly declare all the variables:
function animate(num, element, transitionUnit, delayUnit) {
var delay = 0;
var transition = 0;
var x = document.getElementById(element).getElementsByTagName("LI");
for (var i = 0; i <= x.length - 1; i++) {
x[i].style.WebkitTransform = "translate3d(" + num + "px,0,0)";
x[i].style.transition = transition + "s " + delay + "s ease-in-out";
delay += delayUnit;
transition += transitionUnit;
if (x[i].querySelectorAll('ul li').length > 0) {
x[i].style.background = "rgba(0,0,0,0.35)";
}
}
}
Using implicit globals in for loops causes problems.
As aSeptik mentioned, you might be inverting values:
animate(0,"mainNav",0.04,0.02);

Categories