Closures in For Loops With Onclick Assignment - javascript

I'm trying to have a navBar that generates automatically by looping through an array of "Page" objects. Unfortunately, I seem to be falling into the loops/closure trap. I have read several threads related to this and in some cases have copy and pasted solution code and passed in my own variables but I'm struggling to make it assign onclicks correctly.
I know I'm close. In the below code are two options that I have tried.
Am I getting something wrong with the paremeter in parenthesis in the self-calling function? - the ()(divId)? I don't really understand this part.
Could I also be struggling because this is being done as an object method?
Any help much appreciated but go easy on me, I'm learning all this in my spare time! ;)
Thanks in advance.
EDIT: Jsfiddle: https://jsfiddle.net/mcgettrm/fs0mtz6n/
var navBar = {
display: function(){
for(i=0;i<pages.length;i++){
document.getElementById('navBar').innerHTML += pages[i].token;
var divId = pages[i].unique;
// code works fine up to here.
// option one(below): when navBar.display() is called the following code only adds
// the onclick to the final navbar link
document.getElementById(divId).onclick=(function(divId) {
return function() {
alert(divId);
};
})(divId);
//option two(below): when navBar.display() is called the following code logs
// the individual div id's correctly. But, it does it without being clicked. Then,
// only the last item in the loop is clickable.
(function(divId){
document.getElementById(divId).onclick= function(){
console.log(divId);
}
}
)(divId);
}
}
};

I've got it working here - https://jsfiddle.net/pqu9kr85/ it doesn't seem to have been to do with the binding of i more that you needed to build up the navigation html first, making sure it was in the DOM before binding the event. I put two separate loops, one to generate the nav, the second to bind the events. Also updated the page.display() to use this as that will have been affected by the value of i.

Related

How to not add a delay to a function

I am new to JavaScript and following this tutorial I have made the game work perfectly and it goes up to the part of were the the level changes when you destroy all the objects. However as I am learning I am trying to figure out how to make it so that it changes level without a delay.
The main part of the bit which switches level is :
if (!this.rockmodel.countLiving()) {
Asteroid.time.events.add(Phaser.Timer.SECOND * gameWindow.delayToStartLevel, this.levelIncrease, this);
}
However if I take out the delayToStartLevel bit, it does not switch level. So I tried to make it looks like this:
Asteroid.time.events.add(this.levelIncrease, this);
But the next level does not show at all. Not sure if I am being an idiot etc, but any help on this matter would be great.
Again just to make some sense, it works fine with the delay, I want to get rid of that function completely but its not working at all.
Thanks.
The time.events.add will add an event to the Phaser game object. In other words it will fire the given function after X milliseconds.
If you do not want a delay then you can just call the function directly, instead of postponing the function call. Something like this:
if (!this.rockmodel.countLiving()) {
this.levelIncrease();
}

Randomly failing Clone() in Snap.SVG

I'm having a problem where occasionally clone() is failing inside Element.insertAfter because el.node.parentNode is null.
Inside top level html
Snap.load(path, function (data) {
window.picto_svg = data.select('svg');
...
jQuery('#content').load('interior.html');
});
Inside interior.html
$(document).ready(function () {
var svg_copy = picto_svg.clone();
});
Am I just getting lucky that it usually doesn't fail? I don't want to display the svg before cloning. I end up cloning it and adjusting the clone several times, so just want to keep one pure one.
I would probably post more code on a jsfiddle, as there's not enough information to be sure.
However, my first guess would that you need to move this line..
var svg_copy = picto_svg.clone();
Inside the Snap.load function. The problem is possibly that you are trying to create a clone before the file has been loaded, so sometimes it works, sometimes it doesn't.
Its also not clear if you are appending the svg or not as well, but first I would make sure that all function calls and variable assignments that rely on a file being loaded are called from the load function.
Yes, it seems I was getting lucky. To reliably clone an Element, it must have been appended first. I appended to a hidden div and the random failures went away.
Snap.load(path, function (data) {
var placeholder = Snap('#placeholder');
var appended = placeholder.append(data);
window.picto_svg = placeholder.select('svg');
http://codepen.io/matelich/pen/BKyVYv

Adjust (column size of) all visible dataTables

I'm trying to create a generic JS method that will adjust (fnAdjustColumnSizing()) all visible dataTables. Problem is that I just can't get the syntax quite right...
So far, i got this close:
$.fn.dataTable.fnTables(true); //this gets all visible dataTables...
$('#givenTable').dataTable().fnAdjustColumnSizing(); //this adjusts a given dataTable
$.each($.fn.dataTable.fnTables(true), function(singleTable) {
$(singleTable).dataTable().fnAdjustColumnSizing();
}); // And this just don't work! Don't know why...
Any ideas or suggestions on an alternative way to acomplish this?
EDIT: I marked the answer below as the correct answer but i did found what was wrong on my original approach (and will include it as it may be usefull to others): It is the syntax of the $.each's provided function, which should receive 2 parameters, being the first one the index and the second the element itself. So:
$.each($.fn.dataTable.fnTables(true), function(idx, singleTable) {
$(singleTable).dataTable().fnAdjustColumnSizing();
}); // This works!
The DataTables API documentation contains an example that might help you:
var table = $.fn.dataTable.fnTables(true);
if ( table.length > 0 ) {
$(table).dataTable().fnAdjustColumnSizing();
}

Greasemonkey, replace every occurence of string every second

i try to figure out a greasemonkey script that replaces every onmousedown on a site with an ondblclick. And i want it to constantly update, like every 1,5 Seconds, because the page refreshes using AJAX.
This is the script i came up with, but it doesn't seem to be working.
window.setInterval(document.body.innerHTML= document.body.innerHTML.replace('onmousedown','ondblclick');,1500);
The page it should work with is internal use only. But a good example would be the google search, where onmousedown is used for the links of the results to swap out the URL before you click it.
I also tried it without the semicolon after the document.body.innerHTML.replace.
I'm really new to JavaScript, but since i'm the only one in the company who can code, this one is stuck with me.
Any help would be appreciated.
Also, a small "side question"
Do i have to use #exclude, or is it enough to only use #include internal.companysite.tld* so it will only work on this site ?
A direct answer: you need to supply a function to setInterval - and it's best to set a variable so that you can later cancel it with clearInterval() if necessary.
function myF(){document.body....;}
var myIntv = setInterval(myF, 1500);
You could also do it using an anonymous function in one line as you're trying to do... do that this way:
var myIntv = setInterval(function(){document.body....;}, 1500);
I wouldn't suggest this as the solution to your problem. What it sounds like you want to do is manipulate the active DOM - not really change the UI. You likely need something like this:
var objs = document.getElementsBy__(); // ById/ByName/etc - depends on which ones you want
for (var i in objs){objs[i].ondblclick = objs[i].onmousedown;objs[i].onmousedown = undefined;} // just an example - but this should convey the basic idea
Even better, if you can use jQuery, then you'll be able to select the proper nodes more easily and manipulate the event handlers in a more manageable way:
$(".class.for.example").each(function(){this.ondblclick = this.onmousedown;this.onmousedown = undefined;}); // just an example - there are multiple ways to set and clear these

Javascript test results. can anyone explain them?

I was recently writing a blog post about checking if jquery elements exist before binding event handlers, I did a quick jsfiddle which you can see here
The thing I dont understand, is that the results show (using chrome to measure in microseconds) that test 2 is a lot faster then test 1.
You'll see from the jsfiddle that test 2 checks the existent of the matching before binding a click event
TEST 1 is:
console.time('time_1');
$('.yep').click(function() {
alert('clicked');
});
console.timeEnd('time_1');
test 1 just tried to bind the event
TEST 2:
console.time('time_2');
if ($('.yep').length) {
$('.yep').click(function() {
alert('clicked');
});
}
console.timeEnd('time_2');
test 2 check the element exists before binding.
I am running the two bits of code on some, 87 I think 'section' elemenets, one of which has a class of 'yep'
I cant really see why the second test is faster, as its doing more work.
results:
time_1: 0.856ms
time_2: 0.146ms
Can anyone shed some light and help out a confused developer.
thanks
n.b please dont reply with alternative ways to bind click events in jquery, the .click is just used as a simple test
The primary thing going on here is that the first time you query a selector, the engine has to do more work than subsequent queries, which can sometimes be cached. If you reverse the first two tests, you'll find that whichever one runs first tends to be the slower one.
Despite that, and mostly as a side note, in test 2 you're querying the DOM twice, first to check the length, and then to hook up the handler. If the query is cached it doesn't matter much, but still, just do it once:
console.time('time_x');
var yep = $('.yep');
if (yep.length) {
yep.click(function() {
alert('clicked');
});
}
console.timeEnd('time_x');
Note, though, that calling click on a jQuery set with no elements in it is a harmless no-op (not an error or anything), so there's no need for the length check unless you're also doing something else you haven't shown.

Categories