Tips to enhance javascript code - javascript

Was doing a nice-dropping navigation using css3 transforms.
Also written some javascript for this purpose.
But unfortunately it looks a bit untidy.
Would you guys please give me some tips to optimize javascript code.
The pen --> http://codepen.io/rokki_balboa/pen/doOqqv?editors=001
var bar = document.querySelector('.fa-bars');
var lis = document.getElementsByTagName('li');
bar.onclick = function() {
var delayIn = 0;
var delayOut = 1500;
if (!(lis[0].classList.contains('accordion'))) {
console.log(lis[5]);
[].forEach.call(lis, function(el) {
setTimeout(function() {
el.classList.add('accordion');
}, delayOut);
delayOut -= 300;
});
} else {
[].forEach.call(lis, function(el) {
setTimeout(function() {
el.classList.remove('accordion');
}, delayIn);
delayIn += 300;
});
}
};

If you're simply looking to reduce duplication, this might help:
var bar = document.querySelector('.fa-bars');
var lis = document.getElementsByTagName('li');
bar.onclick = function() {
var delay = {in: 0, out: 1500};
var adding = !(lis[0].classList.contains('accordion'));
[].forEach.call(lis, function(el) {
setTimeout(function() {
el.classList[adding ? 'add' : 'remove']('accordion');
}, delay[adding ? 'out' : 'in']);
delay[adding ? 'out' : 'in'] += (adding ? -300 : 300);
});
};
But it does so at some expense in readability. You'd have to make the call for your codebase as to which seems more maintainable.
In the future, https://codereview.stackexchange.com/ is a good place for code review help.

Related

jquery .fadeOut() is not a function error

I'm working on swapping out some content in a simple div using the code below.
var slides = [
[],
[],
[],
[]
];
slides[0]['title'] = "Content One";
slides[0]['body'] = "Here is a little bit of useful information for you to read.";
slides[0]['link-text'] = "READ MORE...";
slides[0]['URL'] = "/go/somewhere";
slides[1]['title'] = "Content Two";
slides[1]['body'] = "This is different information for you to read.";
slides[1]['link-text'] = "READ THIS INSTEAD...";
slides[1]['URL'] = "/go/elsewhere";
var overlayTitle;
var overlayBody;
var overlayLink;
var i = 0;
var overlayInterval = setInterval(overlaySlider(), 600);
function overlaySlider() {
if (i == 1) {
i = 0;
} else {
i++;
}
overlayTitle = slides[i]['title'];
overlayBody = slides[i]['body'];
overlayLink = '' + slides[i]['link-text'] + '';
$("#hvoTitle").fadeOut()(400, function() {
$(this).html(overlayTitle).fadeIn();
});
$("#hvoBody").fadeOut()(400, function() {
$(this).html(overlayBody).fadeIn();
});
$("#hvoLink").fadeOut()(400, function() {
$(this).html(overlayLink).fadeIn();
});
}
Everything works until I get to the first .fadeOut, then I get an error that .fadeOut is not a function. I believe it should be in the version of the library I am using. (full, not slim).
Here is a fiddle I am trying to get working:
https://jsfiddle.net/529to1w5/41/
Problem
fadeOut()(
Solution
fadeout(
Simple syntax error, glad to help.

Preloader with JavaScript

I have faced such a problem. I need to make a preloader with a percentage for a page but I don't know how. Actually, I don't need animation or simple preloader. What do I can and what do I have?
window.onload = function() {
var images = document.images,
imagesTotalCount = images.length,
imagesLoadedCount = 0,
preloader = document.getElementById('js_preloader'),
percDisplay = document.getElementById('js_preload__percentage');
for(var i = 0; i < imagesTotalCount; i++) {
image_clone = new Image();
image_clone.onload = image_loaded;
image_clone.onerror = image_loaded;
image_clone.src = images[i].src;
}
function image_loaded() {
imagesTotalCount++;
percDisplay.innerHTML = (((100 / imagesTotalCount) * imagesLoadedCount) << 0) + '%';
if(imagesLoadedCount >= imagesTotalCount) {
setTimeout(function() {
if(!preloader.classList.contains('done')) {
preloader.classList.add('done');
}
}, 1500);
}
}
};
This aproach allows to see all images to be downloaded and and calculate percentage. But how do I can also take in count the download of css and js files?
You could use the same approach, but with document.scripts and document.styleSheets collections.

How to use Mina in Snap svg?

This is an animation code using Snap svg:
var s = Snap(3000,3000);
var circle7 = s.circle(130,90,5);
var circle8 = s.circle(155,90,5);
var circle9 = s.circle(180,90,5);
var circle10 = s.circle(205,90,5);
var circle11 = s.circle(230,90,5);
var circle12 = s.circle(255,90,5);
circle.attr({fill:"#ffffff",opacity:0});
circle1.attr({fill:"#ffffff",opacity:0});
circle2.attr({fill:"#ffffff",opacity:0});
circle3.attr({fill:"#ffffff",opacity:0});
circle4.attr({fill:"#ffffff",opacity:0});
circle5.attr({fill:"#ffffff",opacity:0});
circle6.attr({fill:"#ffffff",opacity:0});
circle7.attr({fill:"#ffffff",opacity:0});
circle8.attr({fill:"#ffffff",opacity:0});
circle9.attr({fill:"#ffffff",opacity:0});
circle10.attr({fill:"#ffffff",opacity:0});
circle11.attr({fill:"#ffffff",opacity:0});
circle12.attr({fill:"#ffffff",opacity:0});
circle.animate({fill:"#0000FF",opacity:1},4500);
circle1.animate({fill:"#0000FF",opacity:1},4000);
circle2.animate({fill:"#0000FF",opacity:1},3500);
circle3.animate({fill:"#0000FF",opacity:1},3000);
circle4.animate({fill:"#0000FF",opacity:1},2500);
circle5.animate({fill:"#0000FF",opacity:1},2000);
circle6.animate({fill:"#0000FF",opacity:1},1500);
circle7.animate({fill:"#0000FF",opacity:1},4500);
circle8.animate({fill:"#0000FF",opacity:1},5000);
circle9.animate({fill:"#0000FF",opacity:1},5500);
circle10.animate({fill:"#0000FF",opacity:1},6000);
circle11.animate({fill:"#0000FF",opacity:1},6500);
circle12.animate({fill:"#0000FF",opacity:1},7000);
var cloud = s.image("D:/DigiMKey/login page_files/cloud.png", 0 , 260, 180,
125);
cloud.attr({opacity:0,width:100,height:65});
cloud.animate({opacity:1,width:200,height:125},1000)
var school =s.image("D:/DigiMKey/images/School-Icon.png", 265, 50, 100,100);
school.attr({opacity:0,width:80,height:80});
school.animate({opacity:1,width:100,height:100},2000)
I want to animate these objects using Mina so that they can be executed one after another.
Objects should fade in one by one.
how to do that?
This code gives no errors, however they all come at once instead of one after another.
There is a callback in animate function, use it to call the next animation.
var
s = Snap(300,300),
circle7 = s.circle(130,90,5).attr({fill:"#fff",opacity:0}),
circle8 = s.circle(155,90,5).attr({fill:"#fff",opacity:0}),
circle9 = s.circle(180,90,5).attr({fill:"#fff",opacity:0}),
circle10 = s.circle(205,90,5).attr({fill:"#fff",opacity:0}),
circle11 = s.circle(230,90,5).attr({fill:"#fff",opacity:0}),
circle12 = s.circle(255,90,5).attr({fill:"#fff",opacity:0}),
duration = 500,
props = {fill:"#00f",opacity:1};
/*
var anim = function() { circle.animate({fill:"#00f",opacity:1}, duration,mina.linear,anim1);}
var anim1 = function() { circle1.animate({fill:"#00f",opacity:1},duration,mina.linear,anim2);}
var anim2 = function() { circle2.animate({fill:"#00f",opacity:1},duration,mina.linear,anim3);}
var anim3 = function() { circle3.animate({fill:"#00f",opacity:1},duration,mina.linear,anim4);}
var anim4 = function() { circle4.animate({fill:"#00f",opacity:1},duration,mina.linear,anim5);}
var anim5 = function() { circle5.animate({fill:"#00f",opacity:1},duration,mina.linear,anim6);}
var anim6 = function() { circle6.animate({fill:"#00f",opacity:1},duration,mina.linear,anim7);}
*/
var anim7 = function() { circle7.animate(props,duration,mina.linear,anim8);}
var anim8 = function() { circle8.animate(props,duration,mina.linear,anim9);}
var anim9 = function() { circle9.animate(props,duration,mina.linear,anim10);}
var anim10= function() { circle10.animate(props,duration,mina.linear,anim11);}
var anim11= function() { circle11.animate(props,duration,mina.linear,anim12);}
var anim12= function() { circle12.animate(props,duration,mina.linear);};
anim7();
<script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.4.1/snap.svg-min.js"></script>
There is nothing there that makes them animate in a sequence.
The way to animate in a sequence is to use a callback, which is part of the animate() method. So you can do...
circle1.animate({ fill:"#0000FF" }, 1000, mina.linear, callbackFunc2 );
function callbackFunc2() {
circle2.animate({ fill:"#0000FF" }, 1000, mina.linear, callbackFunc3 );
}
function callbackFunc3()...
And so on. Naturally that can feel a bit clunky when you have a lot, so I find it useful to add a sequence plugin to help. For example here
function nextFrame ( el, frameArray, whichFrame ) {
if( whichFrame >= frameArray.length ) { return }
el.animate( frameArray[ whichFrame ].animation, frameArray[ whichFrame ].dur, nextFrame.bind( null, el, frameArray, whichFrame + 1 ) );
}
I also extended it a bit and suggested it here (see the jsfiddle) which may be of use, if you're doing a lot of them.

Call inner Javascript functions from .js file

I'm working on an ASP.NET MVC 4 application. I am using the GetUikit css library which also offers some basic javascript/ jQuery powered stuff. I'm using the Off canvas component which is actually working.
Getuikit: https://github.com/uikit/uikit http://getuikit.com/index.html
Offcanvas Component: http://getuikit.com/docs/offcanvas.html
I can call the offcanvas as advertised via an anchor tag. That's no problem at all. I would want to be able to hide and show the offcanvas area from javascript. I've tracked down the specific Javascript section in the UIKit provided .js file. This section looks like this:
(function($, UI) {
"use strict";
var $win = $(window),
$doc = $(document),
Offcanvas = {
show: function(element) {
element = $(element);
if (!element.length) return;
var doc = $("html"),
bar = element.find(".uk-offcanvas-bar:first"),
rtl = ($.UIkit.langdirection == "right"),
dir = (bar.hasClass("uk-offcanvas-bar-flip") ? -1 : 1) * (rtl ? -1 : 1),
scrollbar = dir == -1 && $win.width() < window.innerWidth ? (window.innerWidth - $win.width()) : 0;
scrollpos = {x: window.scrollX, y: window.scrollY};
element.addClass("uk-active");
doc.css({"width": window.innerWidth, "height": window.innerHeight}).addClass("uk-offcanvas-page");
doc.css((rtl ? "margin-right" : "margin-left"), (rtl ? -1 : 1) * ((bar.outerWidth() - scrollbar) * dir)).width(); // .width() - force redraw
bar.addClass("uk-offcanvas-bar-show").width();
setTimeout(function() {
/*SELF ADDED FOR ARROW*/
var elementArrow = document.getElementById('notification-arrow');
$(elementArrow).css("display", "inline-block");
/*--------------------*/
}, 250);
element.off(".ukoffcanvas").on("click.ukoffcanvas swipeRight.ukoffcanvas swipeLeft.ukoffcanvas", function(e) {
var target = $(e.target);
if (!e.type.match(/swipe/)) {
if (!target.hasClass("uk-offcanvas-close")) {
if (target.hasClass("uk-offcanvas-bar")) return;
if (target.parents(".uk-offcanvas-bar:first").length) return;
}
}
e.stopImmediatePropagation();
Offcanvas.hide();
});
$doc.on('keydown.ukoffcanvas', function(e) {
if (e.keyCode === 27) { // ESC
Offcanvas.hide();
}
});
},
hide: function(force) {
var doc = $("html"),
panel = $(".uk-offcanvas.uk-active"),
rtl = ($.UIkit.langdirection == "right"),
bar = panel.find(".uk-offcanvas-bar:first");
if (!panel.length) return;
/*SELF ADDED FOR ARROW*/
$('#notification-arrow').css("display", "none");
/*--------------------*/
if ($.UIkit.support.transition && !force) {
doc.one($.UIkit.support.transition.end, function() {
doc.removeClass("uk-offcanvas-page").attr("style", "");
panel.removeClass("uk-active");
window.scrollTo(scrollpos.x, scrollpos.y);
}).css((rtl ? "margin-right" : "margin-left"), "");
setTimeout(function(){
bar.removeClass("uk-offcanvas-bar-show");
}, 50);
} else {
doc.removeClass("uk-offcanvas-page").attr("style", "");
panel.removeClass("uk-active");
bar.removeClass("uk-offcanvas-bar-show");
window.scrollTo(scrollpos.x, scrollpos.y);
}
panel.off(".ukoffcanvas");
$doc.off(".ukoffcanvas");
}
}, scrollpos;
var OffcanvasTrigger = function(element, options) {
var $this = this,
$element = $(element);
if($element.data("offcanvas")) return;
this.options = $.extend({
"target": $element.is("a") ? $element.attr("href") : false
}, options);
this.element = $element;
$element.on("click", function(e) {
e.preventDefault();
Offcanvas.show($this.options.target);
});
this.element.data("offcanvas", this);
};
OffcanvasTrigger.offcanvas = Offcanvas;
UI["offcanvas"] = OffcanvasTrigger;
// init code
$doc.on("click.offcanvas.uikit", "[data-uk-offcanvas]", function(e) {
e.preventDefault();
var ele = $(this);
if (!ele.data("offcanvas")) {
var obj = new OffcanvasTrigger(ele, UI.Utils.options(ele.attr("data-uk-offcanvas")));
ele.trigger("click");
}
});
})(jQuery, jQuery.UIkit);
I found one similar thread on Stackoverflow (How can I access the inner functions of this script?) which suggested I'd need to use the following method:
jQuery.UIkit.offcanvas.offcanvas.show('#alerts-canvas');
Where #alerts-canvas is the id of my offcanvas area. When I try to call this from javascript I get the following Javascript error: Uncaught TypeError: Cannot read property 'offcanvas' of undefined.
I have no idea what I'm doing wrong, but I really hope this will work because I really need this to work.
I made sure the script is linked (this happens for all pages in the general _Layout page.
Do you guys have any idea what I might be doing wrong?
try
$.UIkit.offcanvas.show('#alerts-canvas');

Using each() method to animate typing of code

I am trying to use JQuery's each() method to animate typing on multiple blocks of code, but I keep getting this error:
Uncaught TypeError: Cannot call method 'createDocumentFragment' of undefined
Check out some example code in this Fiddle...
And for your convenience, the JS is listed below:
$('.typeanimator').each(function(index) {
console.log(index);
var codeBlock = $(this).text();
var done;
var blockLength = codeBlock.length;
var charCounter = 0;
$(this).text('|');
(function typeAnimator() {
var typingSimulator = Math.round(Math.random() * (200));
done = setTimeout(function() {
console.log("Le print.");
charCounter++;
var typeSection = codeBlock.substring(0, charCounter);
$(this).text(typeSection + '|');
typeAnimator();
if (charCounter == blockLength) {
$(this).text($(this).text().slice(0, -1));
clearTimeout(done);
}
}, typingSimulator);
}());
});
The problem is in the use of $(this) inside the typeAnimator function. You are actually want to refer the this from the parent function but instead you are getting a totally different this. So, use a temporary variable to store the $(this)
$('.typeanimator').each(function(index) {
...
var self = $(this);
self.text('|');
(function typeAnimator() {
var typingSimulator = Math.round(Math.random() * (200));
done = setTimeout(function() {
...
self.text(typeSection + '|');
typeAnimator();
if (charCounter == blockLength) {
self.text(self.text().slice(0, -1));
clearTimeout(done);
}
}, typingSimulator);
}());
});
Updated fiddle
$('.typeanimator').each(function(index, current) {
console.log(index);
var codeBlock = $(current).text();
var done;
var blockLength = codeBlock.length;
var charCounter = 0;
$(current).text('|');
(function typeAnimator(context) {
var typingSimulator = Math.round(Math.random() * (200));
done = setTimeout(function() {
console.log("Le print.");
charCounter++;
var typeSection = codeBlock.substring(0, charCounter);
$(context).text(typeSection + '|');
typeAnimator(context);
if (charCounter == blockLength) {
$(context).text($(context).text().slice(0, -1));
clearTimeout(done);
}
}, typingSimulator);
}(current));
});
This should work, always remember that javascript is really picky about the context when using 'this'. As well .each() has nifty parameter of current item :) Gl, and good code.

Categories

Resources