What's wrong with this jquery loop? - javascript

To summarise briefly what I'm trying to do: I'm providing the facility for a user to view a gallery of thumbnail images, each with a corresponding download link. When the download link is clicked, I present the user with a confirmation div, and assuming the user clicks 'agree', they'll be able to proceed with the download of the full size version of the thumbnail.
To do this, I'm using a repeater to generate the thumbnails. I'm creating a unique id for each link within the 'ItemCreated' event, along with a unique hidden field that stores the relative path for the destination file for that thumbnail.
When the user clicks on the 'Download' link for the appropriate thumbnail, my code should select the 'agree' link, and update it's target path with the hidden field value of the item that was clicked (I hope that made sense?). This basically means whenever a 'Download' button is clicked, the 'agree' link is updated to direct you to the correct file.
The problem that I'm having however is that my 'agree' link never gets updated - it seems to point to the same file for every thumbnail.
Here's a snippet of the rendered thumbnail list:
<div class="download-listing">
<div class="download">
<img src="/img/thumb0.jpg" alt="" />
<div id="downloadLink0" class="dl">Download</div>
<input type="hidden" id="hf0" value="/GetImage.ashx?path=/img/0.jpg" class="hf" />
</div>
<div class="download">
<img src="/img/thumb1.jpg" alt="" />
<div id="downloadLink1" class="dl">Download</div>
<input type="hidden" id="hf1" value="/GetImage.ashx?path=/img/1.jpg" class="hf" />
</div>
<div class="download">
<img src="/img/thumb2.jpg" alt="" />
<div id="downloadLink2" class="dl">Download</div>
<input type="hidden" id="hf2" value="/GetImage.ashx?path=/img/2.jpg" class="hf" />
</div>
</div>
<input id="count" type="hidden" value="3" />
<!-- Hidden popup -->
<div id="popup">
<p><a id="close" class="bClose action">I disagree</a><a id="file-link" class="action" href="#">I agree</a></p>
</div>
Hopefully you can see from the above code that I'm trying to extract the hidden field path from the download that's clicked, and then update the #file-link 'href' with this value.
The Javascript/Jquery I'm using (and this is where the problem seems to be) is the following:
<script type="text/javascript">
$(document).ready(function () {
for (var i = 0; i < $("#count").val(); i++) {
var index = i;
$("#downloadLink" + index).click(function () {
$('#file-link').attr('href', $('#hf' + index).val());
$('#popup').bPopup();
});
}
});
</script>
However, none of this is working! What seems to be happening is that every download link points to the same path - the last one in the list. I can't figure out where I'm going wrong. Is there something obvious I'm missing?
I appreciate any help given!
Thanks

Isn't it easier to do this:
$(function(){
$(".download .dl").click(function(){
$('#file-link').attr('href', $(this).next("input").val());
$('#popup').bPopup();
});
});

Try Something like this...
$("div[id*='downloadLink']").click(function () {
$('#file-link').attr('href',$(this).siblings('img').attr('src'));
$('#popup').bPopup();
});
After a click on any download link, this code will pass the associated image href path to the file-link element.
here is the working fiddle

I'd recommend against using all those input fields. It just creates a bunch of unnecessary markup. Why not store the #count value simply in a JavaScript variable? And the inputs that contain the image paths could be removed as well. You could store that info in an attribute on each download link, named something like "data-path". For example:
<div id="downloadLink0" class="dl" data-path="/GetImage.ashx?path=/img/0.jpg">Download</div>
Now, going back to your original problem, the above markup would solve the issue quite easily:
$('.dl').click(function(){
$('#file-link').attr('href', $(this).attr('data-path')); //could also do $(this).data('path') if using jQuery 1.6 or later
$('#popup').bPopup();
});

Other people have already suggested different ways to achieve what you want, but nobody explained why your current code doesn't work.
The reason it currently doesn't work is because of how scope works in Javascript. There is no block scope* and so your index variable is defined once, and updated every time the loop runs, until in the end it has the maximum (last) value. Then whenever your event handler is run, index still has this value, and the last item will be used.
So, in JS, the easiest way to get a new scope is to use a closure. Here's an example adapted from your code:
$(document).ready(function () {
for (var i = 0; i < $("#count").val(); i++) {
var fn = (function(index) {
return function () {
$('#file-link').attr('href', $('#hf' + index).val());
$('#popup').bPopup();
};
})(i);
$("#downloadLink" + i).click(fn);
}
});
This is not as good a way to solve your actual problem as some of the other answers. However, it demonstrates the concept of creating a scope: you're calling a function that takes one parameter, index, for which you pass the loop iterator variable i. This means that the function inside it (which it returns) can now always access the value of this parameter. The inner function gets stored in fn, which then gets passed as the click handler.
If this looks really tricky, here's a more in-depth look at function and scope in Javascript.
*Note that proposed new versions of Javascript/Ecmascript may add block scoped variables. It is not currently implemented in a cross-browser fashion, however.

You should probably calculate it from the event source (#downloadLinkn), by getting n from the end of the string.

Related

Is it possible to declare Javascript function variables in html?

Sorry, I am just very new in this and had a previous experience in C++, and the question is it possible to do in javascript/html.
I want to make a function in JavaScript which replaces image on click using an array of image locations. Is it possible somehow to declare the needed variable (position number in the array) in the html? So I don't have to create a separate function for each individual image.
In the c++ you make a function and then declare a variable inside the brackets. Is it possible here, and if not, is there any close solution?
JavaScript:
var imgArray = ["images/2.jpg","images/3.jpg"]
function newImage() {
document.getElementById('pic').src = imgArray[1];
}
HTML:
<div class="project" id="ba">
<p onclick="newImage()">Poster</p>
</div>
Is it possible to insert the number in html "newImage(NUMBER)"?
You can send the index number from HTML and receive that in the javascript function as a parameter:
function newImage(index) {
document.getElementById('pic').src = imgArray[index];
}
// in the html
<div class="project" id="ba">
<p onclick="newImage(1)">Poster</p>
</div>
If you plan on using only one <p>, you can initialize a counter variable which gets incremented every time you click on "poster" label and mod it to the length of the images array. It would loop the available images.
var imgArray = ["images/2.jpg","images/3.jpg"]
var counter = 0;
function newImage() {
document.getElementById('pic').src = imgArray[counter];
counter = ++counter % imgArray.length;
}
<div class="project" id="ba">
<p onclick="newImage()">Poster</p>
</div>
<img id="pic" src="#"/>
Else, update your newImage() function to have an argument newImage(index) and pass the needed index in your <p onclick="newImage(1)">poster</p>
You can't really declare variables in HTML. So it's impossible to do something like onclick="newImage(variable);", with exclusively HTML. If you're using a framework like ASP.NET you can do things like onclick="newImage(#variable);" using Razor. I believe Angular, React, etc. all provide similar functionality.
However, there are other ways to achieve something similar in a "vanilla" setup.
If it's just a static number you can pass it with no variable. Something like onclick="newImage(3);"
You can also set a value attribute which can be accessed in JavaScript as well. something like <p id="poster" value="3" onclick="newImage();">Poster</p>.
Then in JS:
function newImage(){
value = document.getElementById("poster").value;
/* do something with the value */
}
If you're using PHP you can also pass PHP variables to JavaScript through the onclick function as demonstrated here. I would recommend this route if you're dynamically generating your HTML (e.g. within a PHP loop) and might not want to hard code each individual value.

How to link two html pages

So i'm working on a chat application and i want to add smileys. So i used two html pages. the first one contain the text area when we wright the messages and an iframe that references the second html page.
<div class="col-12">
<textarea class="col-12 row-12 var_MessageInput" id="textmsg" placeholder="Write a reply..."></textarea>
</div>
in the second html page i have smiley images
<img src="../../../images/sad_smile.gif" onclick="insertSmiley('sad');"/>
<img src="../../../images/angel_smile.gif" onclick="insertSmiley('angel');"/>
<img src="../../../images/happy_smile.gif" onclick="insetSmiley('happy');" />
So i want that when i click at a smiley image that a text got inserted in my text area so i used the following script
function insertSmiley(smiley) {
var currentText = document.getElementById("textmsg");
var smileyWithPadding = " " + smiley + " ";
currentText.value += smileyWithPadding;
currentText.focus();
}
But it doens't work :( i thought the problem might be in document.getElementById since it's another html page but i have no idea how to solve it.
Thanks a lot
Have you included the <script src="Scripts_Chatapp/emoticone.js"></script> in iframe too? If yes, remove the script reference from iFrame.
Move the script reference <script src="Scripts_Chatapp/emoticone.js"></script> at top of page with other script tags.
Change the onclick="insertSmiley('sad');" TO onclick="parent.insertSmiley('sad');".
This will call the parent function and make changes on that page, since element exists on parent.
You have one typo in onclick="insetSmiley('happy');" it should be onclick="insertSmiley('happy');". I checked it and it works for me.
In your JS code, use:
window.location = "second_HTML_Page.html" ;
The problem is that when the function is triggered within the iFrame, the scopes become complicated.
A solution would be to make the function global by defining it at the window level. And then when the function is to be triggered inside the iFrame, it can call the function of the iFrame's parent (the main window).
Example Fiddle: http://jsfiddle.net/e37rrtb1/2/

Replace image in another div with text upon mouse over elsewhere

I have tried around a dozen different ways to do this, I'm only going to post my most recent attempts though.
Basically I am trying to have it so that when a user hovers the mouse over an image on the page, another image in a separate div gets replaced with text.
I have the JS variables for the text elements to replace as well as the original image being replaced to put it back.
My most recent attempts were:
function replaceMainPage(x) {
$('#logozone').empty();
$('#logozone').append(x);
}
function hoverCircles(y) {
$(this).hover(replaceMainPage(y), replaceMainPage(mainLogo));
}
I don't really understand the "this" feature of JS, but have also tried that function as:
function hoverCircles(y) {
$('#logozone').hover(replaceMainPage(y), replaceMainPage(mainLogo));
}
From here I have tried calling the function is my main JS file like so:
$("#cir1").hover(replaceMainPage(logoReplacements.cakes), replaceMainPage(mainLogo));
As well as tried doing it in html on the img element itself with a onmouseover. Nothing has worked yet.
Am also open to non-JS ways of handling this, but I looked for those as well and couldn't find anything.
This will help you, here is a jsFiddle:
var oldContent = '';
$('.hoverMe').hover(function() {
oldContent = $('.toReplace').html();
$('.toReplace').html('xxxxxxxx');
}, function() {
$('.toReplace').html(oldContent);
});
HTML
<div class="hoverMe">
HOVER ME
</div>
<div class="toReplace">
<img src="//cdn2.hubspot.net/hub/360031/hubfs/feature_practicetest.jpg?t=1470774914678&width=365">
</div>
I've created a global variable called oldContent. When hover on the .hoverMe div, store the old content of that div, change the content of div, and when you move your mouse out, put back the old content.
UPDATE
Based on OP comment, I create another jsFiddle where you can set any text on the .hoverMe
If there are longer texts, or kind of texts what can not be stored in the data attribute, then add data-id="1" to n, and retreive the text through ajax based on the id.
HTML
<div id="firstImgDiv">
<div class="text"></div>
<img src="a.png" onmouseenter="replaceImg('secondImgDiv', 'new Text')">
</div>
<div id="secondImgDiv">
<div class="text"></div>
<img src="b.png" onmouseenter="replaceImg('firstImgDiv', 'new Text')">
</div>
JS
function replaceImg(id, text){
// show all images again
$('img').show();
// clear all text
$('.text').html('');
// hide target img
$('#'+id+' > img').hide();
// set target text
$('#'+id+' > .text').html(text);
}

Add new AngularJS variables with JavaScript

I'm trying to make a dynamic form with AngularJS and JavaScript. The objective is to add how many inputs the user need and transform all those inputs in AngularJS variables that print it on body. So I got that code:
$(function(){
var number = 1;
$('a.add').click(function(e){
e.preventDefault();
$('#this_div_contains_settings').append('<input type="text" name="example'+number+'" ng-model="example'+number+'" placeholder="Anything">');
number++;
});
$('#this_div_contains_settings').on('click','a.design_button', function(e){
e.preventDefault();
$(this).parent().remove();
});
});
This function add a INPUT on my DIV with the different ng-model every time it run.
The problem is, it just work's if the {{example1}} is already on my BODY, if I add it later with another function, it just doesn't work.
I'm new with AngularJS, so I didn't understand if I need to "refresh" the AngularJS everytime I add new variable or something like that.
Any help will be appreciated :)
Using jQuery is the wrong way to solve this problem. Instead, create an array inside of your controller that will hold the models for all of these inputs.
Depending on how you define/create your controllers, $scope may be replaced with this
$scope.examples = [];
Next, create a function that will add a new example to the array:
$scope.addExample = function () {
$scope.examples.push("");
}
Now, in your template, create the inputs using an ng-repeat:
<div id="this_div_contains_settings">
<input ng-repeat="example in examples" type="text" name="example" ng-model="example" placeholder="Anything">
</div>
and have your "add" button call your addExample function on click:
<a class="add" ng-click="addExample()">Add Example</a>
Finally, remove all of the code that was included in your question.
And for your .design_button that removes all the examples, that's easy too:
<a class="design_button" ng-click="examples = []">remove all examples!</a>
by that same concept, you could even remove the need for the addExample function, however i tend to keep logic in the controller (well, actually in the services, but that's another topic) anyway rather than putting it in the template.

independently working div in Jquery

I am trying to make an independently working div which has a form inside of it.
I use jquery to calculate the price of a product depending of the user's selections in the form. However the user is able to add multiple items in his 'cart' so the form is duplicated to another div. The problem is that the calculation pattern can't separate these two divs and the calculation will be incorrect. The form is also interactive so it will be generated by the user's input. This is really complex set and renaming every variable by the 'product number' doesn't sound really efficient to me.
I'm kind of stuck here and i don't really know how to solve this problem. I had an idea that what if I put an iframe inside of the div and load my form and its calculation script inside of it, and then use post command to transfer the price of the product to the 'main page' to calculate the total price of all of the products the user wanted.
However it seems that jQuery scripts doesn't work independently inside of these iframes, they still have connection so they broke each other.
i will appreciate any kind of suggestions and help to solve this matter, thank you!
here's the code so far
Heres the body
var productNumber = 1;
<div id="div_structure">
</div>
<button id="newProduct" >Add new product</button><br \>
add new item
<!-- language: lang-javascript -->
$('#newProduct').click(function ()
{
$('<div id="productNo'+productNumber+'">')
.appendTo('#div_structure')
.html('<label onclick="$(\'#div_productNo'+productNumber+'\').slideToggle()">Product '+productNumber +' </label>'+
'<button onclick="$(\'#product'+productNumber+'\').remove()">Remove</button>');
$('<div id="div_product'+productNumber+'" style="display: none;">').appendTo('#product'+productNumber+'');
$('<iframe src="productform.html" seamless frameborder="0" crolling="no" height="600" width="1000">').appendTo('#div_product'+productNumber+'');
productNumber++;
});
it also has a function that allows the user to remove the inserted div.
Here's just few lines from the productform
$(document).ready(function()
{
$('#productCalculation').change(function ()
{
shape = $('input[name=productShape]:checked', '#productCalculation').val();
alert(shape);
});
});
<form id="productCalculation">
<div id="div_productShape" class="product1">
<h1>Select the shape of the product</h1>
<input type="radio" name="productShape" value="r1">R1</input><br \>
<input type="radio" name="productShape" value="r2">R2</input><br \>
<input type="radio" name="productShape" value="r3">R3</input><br \>
</div>
.
.
.
</form>
I translated all of the variables so they may not function correctly since i didn't test the translated version. So the problem is, if i try to make selections in the second generated div it wont even alert() the selected variable
There are two problems with this code: You say somewhere "I translated all of the variables so they may not function correctly since i didn't test the translated version. So the problem is, if i try to make selections in the second generated div it wont even alert() the selected variable". This is because event handlers are attached to elements that are in the DOM at that specific moment. To get it to work for all elements, use event delegation:
$(document).ready(function()
{
$(document).on( 'change', '#productCalculation', function ()
{
shape = $('input[name=productShape]:checked', '#productCalculation').val();
alert(shape);
});
});
Your other question is "My question in a nutshell: Is there a way to restrict jquery to function only in certain div even though i use the same variable names in the second div ". You can use the this variable to access the element the click was invoked on. From this element you can traverse the DOM if needed, for example with .parent().
$('div').on( 'change', function( e ) {
console.log( $(this).val() );
} );

Categories

Resources