// Post Div
<div id="post-1"><img src="art/image01.jpg" /></div>
<div id="post-2"><img src="art/image02.jpg" /></div>
<div id="post-3"><img src="art/image03.jpg" /></div>
// MYSQL Json Encode Generated Information
var allPosts = [{"type":"tumblr", "link":"www.blah.com"},
{"type":"instagram", "link":"blah.com"},
{"type":"tumblr", "link":"www.blah.com"}]
var postTotals = 3;
// Generates FancyBox Links for Clickable Post Div
function generateFancyBox(){
for(i=1;i<=postTotals;i++){
$("#post-"+i).click(function(){
alert(allPosts[i].link); // This alerts "www.blah.com" just fine.
$.fancybox({
"type" : "iframe",
"href" : allPosts[i].link // This Variable is always "Undefined".
});
});
}
}
I don't understand why href : allPosts[i].link - always becomes undefined.
This code is a shorthand version of a larger project I'm working on. I have html DIVs that I need FancyBox to create clickable links.
I can not seem to get the allPosts[i].link to print the URL. However, right above it, when I alert(allPosts[i].link); it prints the URL just fine.
What am I doing wrong?
Replace the $.fancybox call with an immediately executing function, which creates a additional closure.
(function () {
alert(i);
$.fancybox({
"type": "iframe",
"href": allPosts[i].link // This Variable is always "Undefined".
});
})();
2 problems:
1) In the click event handler, wrap the click handler assignment in an Immediately Invoked Function Expression (IIFE).
JSFiddle it.
function generateFancyBox(){
for(i=1;i<=postTotals;i++){
(function (ii) {
$("#post-"+ii).click(function(){
console.log(allPosts[ii-1].link);
//$.fancybox({ "type" : "iframe", "href" : allPosts[ii-1].link });
});
}(i))
}
}
By the time the click handler (and fancybox) does it's job, i is not what it was when it was passed, as a reference, to fancybox. Passing i in as a parameter to the IIFE you're creating a new scope with a new variable ii that gets locked in to the current value of the for loop i when it was passed to the click handler.
2) There's also a problem with the loop and DIV indexes not sync'ing with the image/link array. So I changed:
allPosts[ii] to allPosts[ii-1]
Your loop and image DIVs start with "1" as in for(i=1; and post-1 but your image/link array is zero-based, which means it starts with "0" as in allPosts[0]. So when grabbing the image/link element, decrement counter by one.
The real problem is that you are creating closures inside a loop. See this for further information.
What you have to do is to create the closure outside of the loop and call it as the handler (or callback) of your click event, passing i as argument, so
the closure :
function launchFancybox(i) {
return function () {
$.fancybox({
type: "iframe",
href: allPosts[i].link
});
}
}
Then bind the click events to each div inside your generateFancyBox() function like :
function generateFancyBox() {
for (var i = 0; i < allPosts.length; i++) {
$("#post-"+(i+1)).click( launchFancybox(i) );
}
}
NOTES :
we need to initialize i declaring it with var (in strict mode, it will return an undeclared variable error if you don't)
notice that i should always start with 0, otherwise you will never have access to the first element of the allPosts array. You need to use (i+1) to refer to each div's ID though.
you don't actually need var postTotals = 3; since you can always refer to the length of your allPosts array
See JSFIDDLE
Related
Is there a way to trigger a function from within a rowFormatter? I'm using the responsiveLayout: "collapse"-option, and I really like it.
However, I would like to trigger the toggleList function (or what's it's called.... 1 from '19)
I would like to not go the .click() way, so I created my own (rip-off) solution within the rowClick:
let isOpen = row._row.modules.responsiveLayout.open;
var collapseEl = row._row.element.querySelector('div.tabulator-responsive-collapse');
if (!(isOpen)) {
collapseEl.classList.add("open");
if (collapseEl) {
collapseEl.style.display = '';
}
} else {
collapseEl.classList.remove("open");
if (collapseEl) {
collapseEl.style.display = 'none';
}
}
row._row.modules.responsiveLayout.open = !(isOpen);
But... There must be a good way to trigger toggleList(), instead of writing a rip-off function, which doing the same thing...
I've tried to look through the values and functions in row._row, with no luck. I'm 99.7% sure that I missed this part in the documentation........ But I've really tried to search the best I could.
TL;DR: I would like to trigger the toggleList() function defined within formatter, in my rowClick() event-function. Is that possible?
There is no toggleList function built into Tabulator.
In the example you reference there it is simply a function called toggleList that is defined inside the row formatter and triggered when an element added by the row formatted is clicked.
Because the toggleClick function is defined inside the row formatter its scope is limited to that formatter function so it cannot be accessed from outside it.
one way to get around this would be to assign the function to a property on the row data object then you could access it from else where in the table.
So if we take the example you provided a link to and at the top of the customResponsiveCollapseFormatter function add the following:
var data = cell.getData(); //retrieve the row data object
Yhen where we define the toggleList function, instead of the simple function definition we can assign it to a property on the data object, lets call it collapseToggle, we will also tweak it so it dosnt need the isOpen property passed in and insted flips the state of the open variable itself, that way it can be called from anywhere outside the formatter without knowledge of the current state:
data.collapseToggle = function toggleList(){
open = !open;
Then in our cellClick function we can check to see if the collapseToggle property is defined on the row data and then call it:
cellClick:function(e, cell){
var data = cell.getData();
if(data.collapseToggle){
data.collapseToggle();
}
}
I have two functions, one working, the other not.
They are equal, except that the one is looping through a variable, in which the global object of the scope is saved (hope this makes sense), and the other tries to loop through the text directly, but fails, because it throws the error:
Uncaught TypeError: Cannot read property '0' of undefined
Here is the fiddle:
http://jsfiddle.net/4p1p4wjy/2/
In my understanding, the 2nd version of the function is not working, because it somehow can't access the this.splittedText, from within the callback of the function.
First Working Function:
loopThroughSplittedText: function() {
// delete this
var locationInString = 0;
var splittedText = this.splittedText;
function delayedOutput() {
document.getElementById('output').innerHTML = splittedText[locationInString];
locationInString++;
if(locationInString < splittedText.length) {
setTimeout(delayedOutput, 200);
}
}
delayedOutput();
},
Second Not Working Function:
loopThroughSplittedTextNotWorking: function() {
// delete this
var locationInString = 0;
function delayedOutput() {
document.getElementById('output').innerHTML = this.splittedText[locationInString];
locationInString++;
if(locationInString < this.splittedText.length) {
setTimeout(delayedOutput, 200);
}
}
delayedOutput();
}
How do I make the 2nd function work, without saving the object inside a local variable first? I'd like to use the two-way databinding as best as possible.
How do I make the 2nd function work, without saving the object inside a local variable first?
You can't. this is a variable that is always local to the function it is used in, and its value depends on how the function is called. If you want to use its value in a different function, then you need to copy it into another variable.
The bind method provides a shorthand for doing that.
setTimeout(delayedOutput.bind(this), 200);
Simple answer, you don't.
Because your function is called through timeout, it's not in the same context anymore and 'this' will not refer to the same object anymore.
You can do this:
loopThroughSplittedTextNotWorking: function() {
// delete this
var locationInString = 0;
var that = this;
function delayedOutput() {
document.getElementById('output').innerHTML = that.splittedText[locationInString];
locationInString++;
if(locationInString < that.splittedText.length) {
setTimeout(delayedOutput, 200);
}
}
delayedOutput();
}
By saving the "this" variable into a local variable, you can access it in your "delayedOutput" function.
I realize it's basically just like your working example, just phrased a little different, but that's usually how I do it.
I am using Decarta JS API PIN event handler
I am displaying pins on a map and the click event for each pin is fired correctly. The trouble comes in when I try pass the pin id to the handler. It ALWAYS displays the last id loaded regardless of which pin is clicked on. My code for declaring the listener is:
for (var idx = 0; idx < jsonObjects.devices.length; idx++) {
var device = jsonObjects.devices[idx]
deCarta.Core.EventManager.listen('click', function() {home.pinPushed(device.id);}, device.pin);
}
This is done in a loop through all the locations I am displaying. They are displayed correctly at their respective coordinates, and logging the previous line clearly shows that each pin is declared with the correct id. However, whenever I click on a pin, the LAST id declared is the only id ever passed to the handler:
home.pinPushed(device.id)
How do I get each pin to pass the unique id when the pin is clicked?
Try this :
deCarta.Core.EventManager.listen('click',
(function(id) { return function (){home.pinPushed(id);}})(device.id), device.pin);
Beautify version :
deCarta.Core.EventManager.listen('click', (function (id)
{
return function ()
{
home.pinPushed(id); //<---- id here is the closured variable
}
})(device.id), device.pin);
Explanation :
Js has a function scope ( not braces ({}) scope.)
Your for loop is not creating ANY scope/function.
What I was doing is to create execution context for each iteration.
for each iteration im sending the current device.id to a function which in turn , returns another function which closured the variable.
I have the following little piece of code:
var instance = this;
window.onload = function () {
for (var i = 0; i < array.length; ++i) {
var currentDivId= array[i];
var currentDiv = document.getElementById(currentDivId);
try {
if (!currentDiv) {
throw 'Div id not found: ' + currentDivId;
}
var image = document.createElement('img');
image.src = 'img.jpg';
image.onclick = function() {
instance.doSomething(currentDivId);
};
currentDiv.appendChild(image);
}
catch(e) {
console.warn('oops');
}
}
};
This code is passed an array of id of divs. What it does is that, it renders an image at each of those divs and set their onclick property.
Say I have an array of strings: ['abc', 'xyz']
I want the code to place an image inside <div id="abc"></div> and another image inside <div id="xyz"></div>.
When you click the first image, instance.doSomething function should be called with parameter 'abc' and vice versa.
But the code does not work as expected. It always calls instance.doSomething with the last parameter in the array, in this case, 'xyz'.
I'm new to JS and still don't have a solid grasp of its inner workings. What's wrong here and how can I fix it?
Any help appreciated.
image.onclick = function() {
instance.doSomething(this.parentNode.id);
};
That should do it. Since we know that the image is inside the div we want to get at, just go one dom element up and get its id.
Welcome to the wonderful world of Javascript scoping issues. As it stands now, JS is treating your onclick code as something like "when this object is clicked, fetch the value stored in the currentDivID variable AT THE TIME THE CLICK occurs and pass it to the doSomething function".
What you should do is base the argument on the image object itself. Every DOM object knows where it is in the DOM tree, so at the time it's clicked, the onclick code should use DOM traversal operations to figure out which div it's inside of and dynamically retrieve its ID. That way you don't have to worry about binding variables and scoping issues... just figure out which div contains your image and get the ID at run time.
Try:
image.onclick = (function() {
var currentD = currentDivId;
return function() {
instance.doSomething(currentD);
}
})();
Hope it helps
Instead of re-writing a massive block of code each time, I'm trying to incorporate functions into my work but I'm having trouble making it work.
Basically, I've got a selection of radio buttons and I'm performing some stuff each time a radio button is clicked. (I'm actually loading an iFrame). However, I need to make the iFrame SRC different for each radio button so how would I cater for this within the function?
The function:
jQuery.fn.switchPreview = function () {
$('.liveDemoFrame').remove();
$('.liveDemoHand').append('<iframe class="liveDemoFrame" src="themes/src/' + themeName + '/" width="100%" height="300" scrolling="no"><p>Your browser does not support iframes.</p></iframe>');
$('.liveDemoHand').append('<div class="switchPreview"><div class="loadingPreview"></div></div>');
$('.liveDemoFrame').load(function() {
$('.switchPreview').fadeOut();
$('.switchPreview').remove();
$('.liveDemoFrame').fadeIn();
});
return this;
}
Within the iFrame SRC I have a variable called themeName. I need this to somehow change for each radio button. You can see within the code that calls the function I've tried to declare the variable each time but this still gives me an undefined error.
The code that calls it:
$('#cTheme1').click(function () {
$('input:radio[name=mgChooseTheme]:nth(1)').attr('checked',true);
var themeName = 'theme1';
$('.liveDemoFrame').switchPreview();
});
$('#cTheme2').click(function () {
$('input:radio[name=mgChooseTheme]:nth(2)').attr('checked',true);
var themeName = 'theme2';
$('.liveDemoFrame').switchPreview();
});
$('#cTheme3').click(function () {
$('input:radio[name=mgChooseTheme]:nth(3)').attr('checked',true);
var themeName = 'theme3';
$('.liveDemoFrame').switchPreview();
});
I'm sure this is something very simple but I'm still learning so little things always throw me off!
Pass in the themeName as an argument of the switchPreview function.
-Change the first line of the function to:
jQuery.fn.switchPreview = function (themeName) {
-For each of the three times you are calling the function, make sure you are passing in the argument, i.e:
$('.liveDemoFrame').switchPreview(themeName);
Why not just update the src tag of the iframe ...
$('#cTheme1').click(function () {
$('input:radio[name=mgChooseTheme]:nth(1)').attr('checked',true);
$('#liveDemoFrame').attr('src', <the new url>);
});
Then your iframe would already need to be part of the page :
<iframe id='liveDemoFrame' src='<default page>'></iframe>
i notice your using a CLASS attribute on your iFrame and accessing it using that - you really need to think about using the ID attribute if you are just wanting to refer to a single object. (as in my example above)