This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 4 years ago.
So I'm creating the a FB app that uses the FB SDK. In the code below, I am getting a list of movies I like and their FB page ids in 'data'. Then, I'm iterating through it to get the get the names of the movies and using the page ids, i get their page links and src of their page profile pictures. Then I use the details to create the following HTML block:
<article>
<figure>
<img src="source">
</figure>
<div>
<a href="link"/>
</div>
</article>
So, I dynamically create each article block and then append them to the below HTML code:
<body>
<header></header>
<section>
<article></article>
</section>
<footer></footer>
<script src = *source of my .js file*></script>
</body>
This is my js code:
// data has a list of FB movie pages, each containing the name of the movie and page id
function print(data)
{
//iterates through the object passed to print movie names in console.
var target = document.querySelector('article');
var docFrag = document.createDocumentFragment();
for (var i = 0; i < data.length; i++)
{
var temp = data[i];
var linkElement = document.createElement('a');
var linkText = document.createTextNode(temp["name"]);
linkElement.appendChild(linkText);
//getting the link to the movie's FB page
getLink(function(response){linkElement.href = response;},temp["id"]);
var imgElement = document.createElement('img');
//getting the src of the picture of the movie's page
getPic(function(response){imgElement.setAttribute("src", response);},temp["id"]);
imgElement.setAttribute("width", "304");
imgElement.setAttribute("height", "228");
imgElement.setAttribute("alt", temp["name"]);
var article = document.createElement("article"),
figure = document.createElement("figure"),
div = document.createElement("div");
div.appendChild(linkElement);
figure.appendChild(imgElement);
article.appendChild(figure);
article.appendChild(div);
console.log(article);
docFrag.appendChild(article);
}
target.appendChild(docFrag);
}
function getLink(callback,id)
{
FB.api('/'+id+'?fields=link', function(response)
{
callback(response.link);
});
}
function getPic(callback,id)
{
FB.api('/'+id+'?fields=cover{source}', function(response)
{
callback(response.cover.source);
});
}
My problem is that when I print the output, all the article blocks except the last one lose the href's and the src's for the 'img' blocks. When I print the articles in the console, the href and the src are empty except the last one. I think maybe it's got something to do with async calls to the FB SDK, but I'm not sure. Can anyone help?
var has function scope, not block scope, and is hoisted. Use const or let instead, especially in for loops, especially in asynchronous for loops:
for (let i = 0; i < data.length; i++) {
const temp = data[i];
const linkElement = document.createElement('a');
To the interpreter, what you're currently doing looks something like this:
function print(data) {
var target;
var docFrag;
var temp;
var linkElement;
// etc
for (i = 0; i < data.length; i++) {
temp = data[i];
linkElement = document.createElement('a');
// etc
So after the end of the loop, once the responses come back, each callback is referencing the same imgElement, the last one the for loop reassigned.
You can also use forEach, which is significantly better than for loops in most respects. (no manual iteration, automatic abstraction, accepts functions as arguments)
data.forEach((temp) => {
const linkElement = document.createElement('a');
// ...
Related
I'm making a fetch call to retrieve some data on player cards.
I'm trying to display said cards and running into issues accurately parsing JSON and showing info in HTML.
Here is an example HTML heading for a card:
<h3 class="card__title">Example Card</h3>
Here is some JavaScript code I used after the fetch call:
let cardList = json.results
let cardTitle0 = cardList[0].name.toString();
How do I change the text from Example Card -> cardTitle0 from my array? This question applies to other attributes from the array and HTML objects.
You'll need to use the h3 element to get the link:
const link = document.querySelector(".card__title a");
// These can be const.
const cardList = json.results
const cardTitle0 = cardList[0].name.toString();
link.innerHTML = cardTitle0;
For multiple cards, you'd have to loop through the cardList json result and also through the elements on the page.
This is just an idea off the top of my head so I can't verify this code works or is even the best option for addressing multiple cards but hopefully it'll get you going in the right direction.
const links = document.querySelectorAll(".card__title a");
const cardList = json.results;
// Loop through the links on the page.
for (let i = 0; i < links.length - 1; i++)
{
for (let j = 0; j < cardList.length - 1; j++)
{
const title = cardList[j].name.toString();
links[i].innerHTML = title;
}
}
I'm trying to write a javascript what is searching for all of the links on the page, then it is adding them to the bottom, under the original content.
"Document.links" seems to do the finding part, it is also listing them, but they are not clickable. So I tried to add some html codes (startHref and endHref lines), which broke the whole thing of course.
My (non-working) script:
var links = document.links;
for(var i = 0; i < links.length; i++) {
var preLink = document.createTextNode("LINK: ");
var linkHref = document.createTextNode(links[i].href);
var lineBreak = document.createElement("br");
var startHref = document.createElement("a href="");
var endHref = document.createElement(""");
document.body.appendChild(preLink);
document.body.appendChild(startHref);
document.body.appendChild(linkHref);
document.body.appendChild(endHref);
document.body.appendChild(lineBreak);
}
If this will work I'd also like to have them listed with a number in front of each line (starting with 1 - could be set in the preLink part) - if not too hard to implement.
Also, is there a way to list not all of the links, but only those matching with something? Like only links with a specific domain. Thank you!
As you have already found out, you can get all links in a document with:
var links = document.links;
Now you have an HTMLCollection. You can iterate through it and display all links. For better layout you can put them in a paragraph (p). This would be the loop:
for (var i = 0; i < links.length; i++) {
var p = document.createElement("p");
p.appendChild(links[i]);
document.body.appendChild(p);
}
Now all links are appended at the end of the page, every link is on its own line and they are clickable. Please try this out.
EDIT: as of your comment, if I understand it right, you have just to put one additional line:
for (var i = 0; i < links.length; i++) {
var p = document.createElement("p");
// the following line is added
links[i].innerHTML = links[i].href;
p.appendChild(links[i]);
document.body.appendChild(p);
}
That line will simply replace the inner HTML of the link with its value for the attribute href.
EDIT:
The variable links just points to document.links. The existing links are therefore removed from their original position and appended to the end. If you try to create new links in the for loop, like document.createElement("a") you will create an endless loop, because you're iterating through all links in the document. You remember, the variable links is not a snapshot of document.links when created, but points to it.
You can work around this with creating an array:
var links = [];
// populate the array links
for (var j = 0; j < document.links.length; j++) {
links.push(document.links[j].cloneNode());
}
Now this is a snapshot of all links on the page. Every links is cloned and pushed to the array. Now run the for loop and the original links aren't removed.
If the original link was something like:
This is an example.
it will become:
http://example.com
But if you want just:
http://example.com
then you have to adapt the code:
for (var i = 0; i < links.length; i++) {
var p = document.createElement("p");
var a = document.createElement("a");
a.href = links[i].href;
a.text = links[i].href; // you can use text instead of innerHTML
p.appendChild(a);
document.body.appendChild(p);
}
If you want to style the output you can add classes like this:
p.classList.add("my-css-class");
I hope this helps you to achieve your goal.
This is an Apps Script that goes through a webpage and collects img urls that are inside some div of a special class.
function getIMGs(url){
var url = 'url'
var result = UrlFetchApp.fetch(url);
if (result.getResponseCode() == 200) {
var doc = Xml.parse(result, true);
var bodyHtml = doc.html.body.toXmlString();
var doc = XmlService.parse(bodyHtml);
var html = doc.getRootElement();
var thumbs = getElementsByClassName(html, 'thumb');
var sheet = SpreadsheetApp.getActiveSheet();
for (i in Thumbs) {
var output = '';
var linksInMenu = getElementsByTagName(thumbs[i], 'img');
for(i in linksInMenu) {
output += XmlService.getRawFormat().format(linksInMenu[i]);
}
var linkRegExp = /data-src="(.*?)"/;
var dataSrc = linkRegExp.exec(output);
sheet.appendRow([dataSrc[1]]);
}
}
So first the code gets the html, and uses an auxiliary function to get certain elements, which look like this:
<div class="thumb"><div class="loader"><span class="icon-uniE611"></span></div><img src="//xxx" data-src="https://xxx/8491a83b1cacc2401907997b5b93e433c03c91f.JPG" data-target="#image-slider" data-slide-to="0"></div>
Then the code gets the img elements, and finally extracts the data-src address via RegExp.
While this kinda works, I have a problem:
1) After 9 loops it crashes, on the appendRow line, as the last 4 Thumbs elements don't have data-src, hence what i'm trying to write into the spreadsheet is null.
Any solution for this? I have fixed it for the moment by just doing 9 iterations only of the For loop, but this is far from optimal, as it's not automated and required me to go through the page to count the elements with data-src.
Also, any suggestion of a more elegant solution will be appreciated! I will be really grateful for any helping hand!
Cheers
I grabbed a few small scripts and threw them together to take google's new image layout and turn back into the old one, then take the images and replace them with the full size versions. Worked great until about last week. Not sure what changed on the server side.
(function() {
// Get list of all anchor tags that have an href attribute containing the start and stop key strings.
var fullImgUrls = selectNodes(document, document.body, "//a[contains(#href,'/imgres?imgurl\x3d')][contains(#href,'\x26imgrefurl=')]");
//clear existing markup
var imgContent = document.getElementById('ImgContent');
imgContent.innerHTML = "";
for(var x=1; x<=fullImgUrls.length; x++) {
//reverse X to show images in correct order using .insertBefore imgContent.nextSibling
var reversedX = (fullImgUrls.length) - x;
// get url using regexp
var fullUrl = fullImgUrls[reversedX].href.match( /\/imgres\?imgurl\=(.*?)\&imgrefurl\=(.*?)\&usg/ );
// if url was fetched, create img with fullUrl src
if(fullUrl) {
newLink = document.createElement('a');
imgContent.parentNode.insertBefore(newLink , imgContent.nextSibling);
newLink.href = unescape(fullUrl[2]);
newElement = document.createElement('img');
newLink.appendChild(newElement);
newElement.src = decodeURI(fullUrl[1]);
newElement.border = 0;
newElement.title = fullUrl[2];
}
}
function selectNodes(document, context, xpath) {
var nodes = document.evaluate(xpath, context, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
var result = [];
for (var x=0; x<nodes.snapshotLength; x++) {
result.push(nodes.snapshotItem(x));
}
return result;
}
})();
Google changed the 'ImgContent' id for the image table holder to something slightly more obscure. A quick change had everything working again. I made a simple problem complicated by looking past the easy stuff. Thanks to darvids0n for the enabling, he ultimately pointed out what I was missing.
the script is not going to work as said by bobby .
try this grease monkey script from user script repository.
rip Google image search :- http://userscripts.org/scripts/show/111342
okay i have come across a simple javascript code that will search all the hyperlinks in a page which works briliantly fast. the only problem is that the browser freezes when it tries to remake these links in a div, no error just as soon as i push the button the browser refuses to load. as you can see it gets the input from a form then searches every hyperlink for these terms then is supposed to populate a div with links but it doesn't. the code is as follows.
function search0(){
var lists = document.getElementsByTagName("a");
for (var i = 0; i < lists.length; i++) {
var output = lists[i];
var team1 = document.getElementById("search1").value;
var matchPos1 = output.innerHTML.search(team1);
if(matchPos1 != -1){
var team2 = document.getElementById("search2").value;
var matchPos2 = output.innerHTML.search(team2);
if(matchPos2 != -1){
var elem1 = document.createElement("a")
var styleattr=document.createAttribute("href");
styleattr.nodeValue=output;
elem1.setAttributeNode(styleattr);
var text1 = document.createTextNode(output.innerhtml);
elem1.appendChild(text1);
var parentdiv = document.getElementById("frame2");
parentdiv.appendChild(elem1);
}
}
}
}
You are creating an infinite loop.
The nodeList you create with document.getElementsByTagName("a") is live i.e. if you add a link to the page it will appear in this list automatically! Yes, that's right, even without requerying. Here's a reference doc.
You are adding links to the nodeList which are then matched and added to the end on the nodeList which are then matched and so on and so on
To do what you want to do you should create an initial array of links like this.
//creates a real js array from a nodelist
var list = Array.prototype.slice.call( document.getElementsByTagName("a"), 0 );
Here is an explanation of Array.prototype.slice.call
Also change case-sensitive mistake:
var text1 = document.createTextNode(output.innerhtml);
To
var text1 = document.createTextNode(output.innerHTML);