Change function from button click to page load - javascript

Is it possible to transform this code to generate when the page loads instead of when the button is clicked. I tried to change the events to load but it does not function. I do not know what I exactly I am doing wrong.
<p><button id="generate">Generate</button></p>
<p><code id="output"></code></p>
(function() {
function IDGenerator() {
this.length = 8;
this.timestamp = +new Date;
var _getRandomInt = function( min, max ) {
return Math.floor( Math.random() * ( max - min + 1 ) ) + min;
}
this.generate = function() {
var ts = this.timestamp.toString();
var parts = ts.split( "" ).reverse();
var id = "";
for( var i = 0; i < this.length; ++i ) {
var index = _getRandomInt( 0, parts.length - 1 );
id += parts[index];
}
return id;
}
}
document.addEventListener( "DOMContentLoaded", function() {
var btn = document.querySelector( "#generate" ),
output = document.querySelector( "#output" );
btn.addEventListener( "click", function() {
var generator = new IDGenerator();
output.innerHTML = generator.generate();
}, false);
});
})();

There is small bug.
Just change the document object to window like below
window.addEventListener('DOMContentLoaded', function() {
});

Related

Javascript Slideshow Speed

I have this Code with a Slideshow for Images. I Would like to set the time the images are fixed. (Image one rest for 10min and then fade out). All the Images should rest about 10min. I like to use this for a Infoscreen in my company. Please help :)
(function() {
function Slideshow( element ) {
this.el = document.querySelector( element );
this.init();
}
Slideshow.prototype = {
init: function() {
this.wrapper = this.el.querySelector( ".slider-wrapper" );
this.slides = this.el.querySelectorAll( ".slide" );
this.previous = this.el.querySelector( ".slider-previous" );
this.next = this.el.querySelector( ".slider-next" );
this.index = 0;
this.total = this.slides.length;
this.timer = null;
this.action();
this.stopStart();
},
_slideTo: function( slide ) {
var currentSlide = this.slides[slide];
currentSlide.style.opacity = 1;
for( var i = 0; i < this.slides.length; i++ ) {
var slide = this.slides[i];
if( slide !== currentSlide ) {
slide.style.opacity = 0;
}
}
},
action: function() {
var self = this;
self.timer = setInterval(function() {
self.index++;
if( self.index == self.slides.length ) {
self.index = 0;
}
self._slideTo( self.index );
}, 3000);
},
stopStart: function() {
var self = this;
self.el.addEventListener( "mouseover", function() {
clearInterval( self.timer );
self.timer = null;
}, false);
self.el.addEventListener( "mouseout", function() {
self.action();
}, false);
}
};
document.addEventListener( "DOMContentLoaded", function() {
var slider = new Slideshow( "#main-slider" );
});
})();
Well, there is only on thing that controls anything timing related in your code, so its a pretty safe bet that's what you want to change.
You have a setInterval() in action with its time set to 3000. Change that to 600000 (10m * 60s * 1000ms) and you should be all set.
action: function() {
var self = this;
self.timer = setInterval(function() {
self.index++;
if( self.index == self.slides.length ) {
self.index = 0;
}
self._slideTo( self.index );
}, 1000*60*10);
This function uses setInterval, which expects interval in ms as the second parameter. In this example its set to 3000 which is 3 seconds.
Change it to what I wrote above (1000ms is 1 second * 60 = 1 minute * 10 = 10 minutes).

Randomly generate enemies faster and faster Javascript

Hey I am using a Spawn function I found for a javascript game I am creating to randomy generate enemies to click on. But it is very slow and I would like it to be able to generate faster and faster.. is it possible to modify this code in that way? the spawn function is at the end of the code.
function Gem(Class, Value, MaxTTL) {
this.Class = Class;
this.Value = Value;
this.MaxTTL = MaxTTL;
};
var gems = new Array();
gems[0] = new Gem('green', 10, 0.5);
gems[1] = new Gem('blue', 20, 0.4);
gems[2] = new Gem('red', 50, 0.6);
function Click(event)
{
if(event.preventDefault) event.preventDefault();
if (event.stopPropagation) event.stopPropagation();
else event.cancelBubble = true;
var target = event.target || event.srcElement;
if(target.className.indexOf('gem') > -1){
var value = parseInt(target.getAttribute('data-value'));
var current = parseInt( score.innerHTML );
var audio = new Audio('music/blaster.mp3');
audio.play();
score.innerHTML = current + value;
target.parentNode.removeChild(target);
if (target.className.indexOf('red') > 0){
var audio = new Audio('music/scream.mp3');
audio.play();
endGame("You lose");
}
}
return false;
}
function Remove(id) {
var gem = game.querySelector("#" + id);
if(typeof(gem) != 'undefined')
gem.parentNode.removeChild(gem);
}
function Spawn() {
var index = Math.floor( ( Math.random() * 3 ) );
var gem = gems[index];
var id = Math.floor( ( Math.random() * 1000 ) + 1 );
var ttl = Math.floor( ( Math.random() * parseInt(gem.MaxTTL) * 1000 ) + 1000 ); //between 1s and MaxTTL
var x = Math.floor( ( Math.random() * ( game.offsetWidth - 40 ) ) );
var y = Math.floor( ( Math.random() * ( game.offsetHeight - 44 ) ) );
var fragment = document.createElement('span');
fragment.id = "gem-" + id;
fragment.setAttribute('class', "gem " + gem.Class);
fragment.setAttribute('data-value', gem.Value);
game.appendChild(fragment);
fragment.style.left = x + "px";
fragment.style.top = y + "px";
setTimeout( function(){
Remove(fragment.id);
}, ttl)
}
function Stop(interval) {
clearInterval(interval);
}
function endGame( msg ) {
count = 0;
Stop(interval);
Stop(counter);
var left = document.querySelectorAll("section#game .gem");
for (var i = 0; i < left.length; i++) {
if(left[i] && left[i].parentNode) {
left[i].parentNode.removeChild(left[i]);
}
}
time.innerHTML = msg || "Game Over!";
start.style.display = "block";
UpdateScore();
}
this.Start = function() {
score.innerHTML = "0";
start.style.display = "none";
interval = setInterval(Spawn, 750);
count = 4000;
counter = null;
function timer()
{
count = count-1;
if (count <= 0)
{
endGame();
return;
} else {
time.innerHTML = count + "s left";
}
}
counter = setInterval(timer, 1000);
setTimeout( function(){
Stop(interval);
}, count * 1000)
};
addEvent(game, 'click', Click);
addEvent(start, 'click', this.Start);
HighScores();
}
JS/HTML Performance tips:
1) var gems = new Array(); change to new Array(3);
2) cache all new Audio('...');
3) use getElementById it faster then querySelector('#')
4) prepare var fragment = document.createElement('span'); and cache it, use cloneNode to insert it.
5) fragment.style.left & top change to CSS3 transform: translate(). CSS3 transform use your video card GPU, style.left use CPU...
6) Use only single setInterval and run on all list of fragments.
7) You can make a pool of "fragments" and just show\hide, it will increase performance dramatically.
Basically it works slow due to many page redraws, every time you add new elements to page browser must to redraw whole screen, do not add elements, just show\hide them.

JavaScript integer storing as undefined for some reason

basically I have a number stored within my javascript structure, something like the following:
MyProgram.data.main.padding;
console.log(MyProgram.data.main.padding); // will output the number, something like 34.
However, I need to store that number in a struct I have set up in
MyProgram.data.tests.main.padding; // this is a struct " {'width':0, 'padding':0, 'acceptable' //etc...}
The problem is when I do this:
MyProgram.data.tests.main.padding = MyProgram.data.main.padding;
console.log(MyProgram.data.tests.main.padding); // shows undefined
any ideas why I can't store the number?
I really appreciate any help...
Can someone load this example on jsfiddle please? I don't know how:
http://jsfiddle.net/hdnj52Lp/2/
RESULT OUTPUT ON MY LOCAL TEST: 0) acceptable: true fontSize undefined padding: undefined
function MyProgram() {
var mp = this;
this.main = {
'data' : {
'padding': 50,
'fontSize': 10,
'tests' : {
'padding':null,
'fontSize':null,
'results':new Array()
}
},
'test' : function () {
console.log('running');
var testResult = {'acceptable':false,
'fontSize':0,
'padding':0}
//after some testing:
var newComputedPadding = 100;
var newComputedFontSize = 32;
var acceptable = true;
testResult.acceptable = acceptable;
testResult.fontSize = newComputedFontSize;
testResult.padding = newComputedPadding;
mp.main.data.tests.results.push(testResult);
mp.main.outputResults();
},
'outputResults' : function () {
for(var i = 0; i < mp.main.data.tests.results.length; i++) {
console.log( i + ') acceptable: ' + mp.main.data.tests.results[i].acceptable + ' fontSize ' + mp.main.data.tests.results.fontSize + ' paddingSides: ' + mp.main.data.tests.results.padding);
}
}
}
}
var Load;
if (window.attachEvent) {
Load =function ( elem, event, handler ) {
elem.attachEvent( 'on'+event, function load() {
handler();
elem.detachEvent( 'on'+event, load );
});
};
} else {
Load =function ( elem, event, handler ) {
elem.addEventListener( event, function load() {
handler();
elem.removeEventListener( event, load, false );
}, false);
};
}
Load(window, 'load', function() {
var MP = new MyProgram();
MP.main.test();
});
You are simply printing
mp.main.data.tests.results.fontSize
instead of
mp.main.data.tests.results[i].fontSize
The code is correct for acceptable but you forgot the [i] part for fontSize and padding.

Toggle class on HTML element without jQuery

I have a section on my website which holds all the content, but I want a "sidebar" with hidden content to smoothly appear from the left at the push of an external button.
CSS transitions can handle the smoothness no problem, and jQuery toggle() can switch between classes to move the hidden div in and out of the screen.
How can I get the same effect without using jQuery?
You can toggle classes using the classList.toggle() function:
var element = document.getElementById('sidebar');
var trigger = document.getElementById('js-toggle-sidebar'); // or whatever triggers the toggle
trigger.addEventListener('click', function(e) {
e.preventDefault();
element.classList.toggle('sidebar-active'); // or whatever your active class is
});
That should do everything you need - if you have more than one trigger I'd recommend using document.querySelectorAll(selector) instead.
You can implement it only by CSS3:
<label for="showblock">Show Block</label>
<input type="checkbox" id="showblock" />
<div id="block">
Hello World
</div>
And the CSS part:
#block {
background: yellow;
height: 0;
overflow: hidden;
transition: height 300ms linear;
}
label {
cursor: pointer;
}
#showblock {
display: none;
}
#showblock:checked + #block {
height: 40px;
}
The magic is the hidden checkbox and the :checked selector in CSS.
Working jsFiddle Demo.
HTML ONLY
You can use <summary>. The following code doesn't have any dependency.
No JavaScript, CSS at all, HTML only.
<div class="bd-example">
<details open="">
<summary>Some details</summary>
<p>More info about the details.</p>
</details>
<details>
<summary>Even more details</summary>
<p>Here are even more details about the details.</p>
</details>
</div>
For more detail, go to MDN official docs.
you can get any element by id with javascript (no jquery) and the class is an attribute :
element.className
so have this as a function:
UPDATE:
since this is becoming a somewhat popular I updated the function to make it better.
function toggleClass(element, toggleClass){
var currentClass = element.className || '';
var newClass;
if(currentClass.split(' ').indexOf(toggleClass) > -1){ //has class
newClass = currentClass.replace(new RegExp('\\b'+toggleClass+'\\b','g'), '')
}else{
newClass = currentClass + ' ' + toggleClass;
}
element.className = newClass.trim();
}
function init() {
animateCSS(document.getElementById("slide"), 250, {
left: function (timePercent, frame) {
var endPoint = 128,
startPoint = 0,
pathLength = endPoint - startPoint,
base = 64, //slope of the curve
currentPos = Math.floor(startPoint + (Math.pow(base, timePercent) - 1) / (base - 1) * pathLength);
return currentPos + "px";
}
}, function (element) {
element.style.left = "128px";
});
};
var JobType = function () {
if (!(this instanceof JobType)) {
return new JobType(arguments[0]);
};
var arg = arguments[0];
this.fn = arg["fn"];
this.delay = arg["delay"];
this.startTime = arg["startTime"];
this.comment = arg["comment"];
this.elapsed = 0;
};
function JobManager() {
if (!(this instanceof JobManager)) {
return new JobManager();
};
var instance;
JobManager = function () {
return instance;
};
JobManager.prototype = this;
instance = new JobManager();
instance.constructor = JobManager;
var jobQueue = [];
var startedFlag = false;
var inProcess = false;
var currentJob = null;
var timerID = -1;
var start = function () {
if (jobQueue.length) {
startedFlag = true;
currentJob = jobQueue.shift();
var startOver = currentJob.delay - ((new Date()).getTime() - currentJob.startTime);
timerID = setTimeout(function () {
inProcess = true;
currentJob.fn();
if (jobQueue.length) {
try {
while ((jobQueue[0].delay - ((new Date()).getTime() - currentJob.startTime)) <= 0) {
currentJob = jobQueue.shift();
currentJob.fn();
};
}
catch (e) { };
}
inProcess = false;
start();
}, (startOver > 0 ? startOver : 0));
}
else {
startedFlag = false;
timerID = -1;
};
};
instance.add = function (newJob) {
if (newJob instanceof JobType) {
stopCurrent();
var jobQueueLength = jobQueue.length;
if (!jobQueueLength) {
jobQueue.push(newJob);
}
else {
var currentTime = (new Date()).getTime(),
insertedFlag = false;
for (var i = 0; i < jobQueueLength; i++) {
var tempJob = jobQueue[i],
tempJobElapsed = currentTime - tempJob["startTime"],
tempJobDelay = tempJob["delay"] - tempJobElapsed;
tempJob["elapsed"] = tempJobElapsed;
if (newJob["delay"] <= tempJobDelay) {
if (!insertedFlag) {
jobQueue.splice(i, 0, newJob);
insertedFlag = true;
}
};
if (i === (jobQueueLength - 1)) {
if (!insertedFlag) {
jobQueue.push(newJob);
insertedFlag = true;
}
}
};
};
if ((!startedFlag) && (!inProcess)) {
start();
};
return true;
}
else {
return false;
};
};
var stopCurrent = function () {
if (timerID >= 0) {
if (!inProcess) {
clearTimeout(timerID);
timerID = -1;
if (currentJob) {
jobQueue.unshift(currentJob);
};
};
startedFlag = false;
};
};
return instance;
};
function animateCSS(element, duration, animation, whendone) {
var frame = 0,
elapsedTime = 0,
timePercent = 0,
startTime = new Date().getTime(),
endTime = startTime + duration,
fps = 0,
averageRenderTime = 1000,
normalRenderTime = 1000 / 25,
myJobManager = JobManager();
var inQueue = myJobManager.add(JobType({
"fn": displayNextFrame,
"delay": 0,
"startTime": (new Date).getTime(),
"comment": "start new animation"
}));
function playFrame() {
for (var cssprop in animation) {
try {
element.style[cssprop] = animation[cssprop].call(element, timePercent, frame);
} catch (e) { }
};
};
function displayNextFrame() {
elapsedTime = (new Date().getTime()) - startTime;
timePercent = elapsedTime / duration;
if (elapsedTime >= duration) {
playFrame();
if (whendone) {
whendone(element);
};
return;
};
playFrame();
frame++;
averageRenderTime = elapsedTime / frame;
fps = 1000 / averageRenderTime;
inQueue = myJobManager.add(JobType({
"fn": displayNextFrame,
"delay": (fps < 15 ? 0 : normalRenderTime - averageRenderTime),
"startTime": (new Date).getTime(),
"comment": frame
}));
}
};
(function () {
if (this.addEventListener) {
this.addEventListener("load", init, false)
}
else {
window.onload = init;
}
}());
// By Plain Javascript
// this code will work on most of browsers.
function hasClass(ele, clsName) {
var el = ele.className;
el = el.split(' ');
if(el.indexOf(clsName) > -1){
var cIndex = el.indexOf(clsName);
el.splice(cIndex, 1);
ele.className = " ";
el.forEach(function(item, index){
ele.className += " " + item;
})
}
else {
el.push(clsName);
ele.className = " ";
el.forEach(function(item, index){
ele.className += " " + item;
})
}
}
var btn = document.getElementById('btn');
var ele = document.getElementById('temp');
btn.addEventListener('click', function(){
hasClass(ele, 'active')
})
I did not test but the code below should work.
<script>
function toggleClass(){
var element = document.getElementById("a");
element.classList.toggle("b");
}
document.getElementById("c").addEventListener('click', toggleClass )
</script>

Javascript function benchmarking

Today I have tried to write a simple prophiler utility for javascript functions.
The problem is that the more interaction I launch the more the system slows down so
I think that at some point I have a memory leak leading to this problem.
The experiment's code follows.
//Benchmark prototype
var Prophiler = (function( ){
var _benchMark = (function( func, duration, callBack ){
var _interval = 1000;
var _startMark = new Date().getTime();
var _now = _startMark;
var _count = 0;
var _rounds = 0;
var _throttle = (function( ){
while( ( _now - _startMark ) < _interval ){
func( );
_now = new Date().getTime( );
_count++;
}
_startMark = new Date().getTime();
_rounds++;
if( _rounds <= ( duration ) ){
window.setTimeout( _throttle, 25 );
return false;
}
else{
var _res = {};
_res.elapsedTime = duration;
_res.executions = _count;
_res.averageTime = _res.elapsedTime / _res.executions;
_res.averageTimeMs = _res.averageTime * 1000;
callBack( _res );
return false;
}
});
_throttle( );
});
return{
getProphile : function( params ){
_benchMark( params.subject, params.duration, params.callBack );
}
}
})( );
//Test
var sumNum = function( param1, param2 ){
var y = param1;
for( var i = 0; i < param2; i++ ){
y += param2;
}
};
Prophiler.getProphile({
subject: function( ){
sumNum( 10, 30 );
},
duration: 5,
callBack: function( data ){
console.log( data );
}
});
Just for the sake of comparison between the module pattern and a prototypal pattern. The performance improvement is more noticeable in Chrome but can be also noticed in firefox.
function BenchMark(fn, duration, callback){
this.fn = fn;
this.duration = duration;
this.callback = callback;
this.rounds = 0;
this.count = 0
this.timerId = null;
this.startMark = (new Date()).getTime();
this._execute = this.scope(this, 'execute');
this._execute();
}
BenchMark.prototype.scope = function(context, method){
return function(){
context[method].call(context);
}
}
BenchMark.prototype.execute = function(){
if(this.timerId !== null){
clearTimeout(this.timerId);
}
var count = 0, now = this.startMark;
while( (now - this.startMark) < 1000){
this.fn();
now = (new Date()).getTime();
count++;
}
this.startMark = (new Date()).getTime();
this.count += count;
this.rounds++;
if(this.rounds <= this.duration){
this.timerId = setTimeout(this._execute, 25);
return false;
}else{
var averageTime = this.duration / this.count;
this.callback({
elapsedTime : this.duration,
executions : this.count,
averageTime : averageTime,
averageTimeMs : averageTime * 1000
});
return false;
}
}
function Profiler(){
}
Profiler.prototype.benchmark = function(fn, duration, callback){
new BenchMark(fn, duration, callback);
}
function sumNum( param1, param2 ){
var y = param1;
for( var i = 0; i < param2; i++ ){
y += param2;
}
};
var profiler = new Profiler();
var profilerCalled = 0;
var intervalId = setInterval(function(){
console.log('Profiler: ', profilerCalled+1)
profiler.benchmark(function(){
sumNum(10, 30)
}, 5, function(result){
console.log(result)
});
profilerCalled++;
if(profilerCalled == 10){
clearInterval(intervalId);
console.log('stopped')
}
}, 10000);
In the end I found the error.
It was an algo misconception.
Obiviously each call adds a 25ms delay that get loosed since _startMark = new Date().getTime(); is called after comparison!
Now it works quite well!

Categories

Resources