Appending <a> and <img> to document.body removes href and src - javascript

So, I'm creating an article block like this:
<article>
<figure>
<img src="source" />
</figure>
<div>
<a href="link"/>
</div>
</article>
This is my js code:
var linkElement = document.createElement('a');
var linkText = document.createTextNode(*some text*);
linkElement.appendChild(linkText);
linkElement.href = *link*;
var imgElement = document.createElement('img');
imgElement.setAttribute("src", *some path*);
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);
document.querySelector('article').appendChild(article);
This is my html code:
<body>
<header></header>
<section>
<article></article>
</section>
<footer></footer>
<script src = *src of my js*></script>
</body>
If I create one article block, everything is fine. The problem arises when I create an array of article blocks. Every block except the last one loses its href and src for the 'a' and 'img'tags. An empty image box appears and the text without the link appears. Can anyone explain why it happens and how I can change that?
Output
I printing a list of FB movie pages with their picture and links to the page.
My original code:
// data has a list of FB movie pages, each containing the name of t he 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);
});
}

If just going by the OP code, it never appends anything to the DOM. The <article> is created and everything is appended to it but it never finds its way to the DOM.
Once that is taken care of you must be mindful of the urls assigned to your images. They will fail should you mix secured content with unsecured content.
Secured Content:
<img src='https://....>
Unsecured Content:
<img src='http://....>?
BTW, this Demo is reusable and easily expandable. The simple requirement is that for each <article> you must add an appropriate url to the url[] and posters[] arrays. Also the layout is streamlined: <img> is nested with <a> and <a> is nested within <fig>, and <fig> is nested within <article>. This arrangement makes the entire <img> a link.
Demo
Details commented in Demo and references are located below the Demo
// Array of urls to sites for lnk.href
var urls = ['https://www.starwars.com', 'https://www.marvel.com'];
// Array of urls to images for img.src
var posters = ["https://imgc.allpostersimages.com/img/print/u-g-F93H7K0.jpg?w=900&h=900&p=0", "https://imgc.allpostersimages.com/img/print/u-g-F90CQI0.jpg?w=900&h=900&p=0"];
// Reference the DOM target
var target = document.querySelector('.action');
/* Create a DocumentFragment Object to add all of your articles
|| to. The only thing you should ever append to the DOM is the
|| docFrag. It's costly to add to DOM so do it only once.
*/
var docFrag = document.createDocumentFragment();
/* One loop will:
|| - create the 4 components: article, figure, a, img
|| - get the urls from the 2 arrays and assign them to
|| lnk.href and img.src
|| - set the additional attributes to img
|| - append components: img to a--a to fig--fig to art--art to
|| docFrag--docFrag to target(a section attached to the DOM)
*/
/* This `for` loop will go 2 times because the .length of urls[]
|| array is 2 (note: i < urls.length)
*/
for (let i = 0; i < urls.length; i++) {
var art = document.createElement('article');
var fig = document.createElement('figure');
var lnk = document.createElement('a');
lnk.href = urls[i];
var img = document.createElement('img');
img.src = posters[i];
img.width = '200';
img.height = '300';
lnk.appendChild(img);
fig.appendChild(lnk);
art.appendChild(fig);
docFrag.appendChild(art);
}
target.appendChild(docFrag);
<!doctype html>
<html>
<head>
<meta charset='utf-8'>
</head>
<body>
<header></header>
<main>
<section class="action"></section>
</main>
<footer></footer>
</body>
</html>
Reference
What is the DOM?
DocumentFragment

Related

How to display an array of images as a list and make them clickable to display some text using javascript?

I need to display a list of images from an array and also make it clickable to display some text on click. Looking for some simple solution only with javascript.
var images = ["img1", "img2", "img3"];
var allPics = images.length;
var i = 0;
for(;i<allPics; i++){
myImg.src = images[i];
}
Example here:
https://jsfiddle.net/gmqLtd1u/1/
Now only one image is displayed.
Now only one image is displayed.
Because you're using one <img> and update its src several times in loop. After last iteration, its src is not updated anymore. That's why you see the last image.
Change your html, so that instead of <img> you have <div> as container/placeholder:
<!-- <img id="myImg"/> -->
<div id="myImg"></div>
And change your JS, to create <img> and append it to <div>:
for(;i<allPics; i++){
// myImg.src = images[i];
// TODO: adjust this to whatever you want
// in this example, use `<a>` that link to another page
// you can use javascript to show modal/alert too
var a = document.createElement('a');
a.href = 'example.html'; // TODO: adjust this
var img = document.createElement('img');
img.src = images[i];
a.appendChild(img);
document.getElementById('myImg').appendChild(a);
}
And maybe your CSS, to match with new output:
#myImg img {
...
}

Deteching if child already exists inside parent

I'm working on a Userscript for my use on StackOverflow, that basically replaces all images with a copy of the same image from a proxy.
What I want is for all of the image links posted in comments to be added to a new container inside the comment called imageContainer that should be created if it doesn't already exist, then all images should be moved to this container.
What I have currently sort of works - except for every image in a comment, a new container is created.
Here is the code I currently have:
var links = document.getElementsByTagName('a');
for(var i = 0; i < links.length ; i++) {
var matches = containsAny(links[i].href, matchURLs);
if(matches) {
//link variables
var link = links[i];
var linkParent = links[i].parentNode;
console.log(linkParent);
//create <img> element
var image = document.createElement("img");
image.src = proxy + link.href;
image.height = 50;
image.width = 50;
//alter <a> element
link.href = proxy + link.href;
link.target = "_blank";
link.innerHTML = "";
link.style.borderBottom = "none";
//attach <img> to <a>
link.appendChild(image);
var result = linkParent.contains(document.getElementById("imageContainer"));
console.log(result);
if(!result) {
//create imageContainer (div) element
var container = document.createElement("div");
container.id = "imageContainer";
linkParent.prepend(container);
}
//append image to container that exists from last check
linkParent.querySelector("#imageContainer").appendChild(link);
}
}
This line:
var result = linkParent.contains(document.getElementById("imageContainer"));
always results in false, even if I have already created the container on the previous loop, which means a new div is created for every image that is converted by the code, which makes it much harder for me to format correctly.
Basically, the current code will generate the following HTML in the comment I am testing this on:
<span class="comment-copy">
<div id="imageContainer"><img src="http://web.archive.org/web/https://i.stack.imgur.com/I8WNI.png" height="50" width="50"></div>
<div id="imageContainer"><img src="http://web.archive.org/web/https://i.stack.imgur.com/I8WNI.png" height="50" width="50"></div>
currently testing my new userscript (relevant to discussion)
</span>
As you can see, it contains 2 copies of the div imageContainer, even though the loop I have should check if it exists, and only create it if it doesn't.
The result I expect is as followed:
<span class="comment-copy">
<div id="imageContainer">
<img src="http://web.archive.org/web/https://i.stack.imgur.com/I8WNI.png" height="50" width="50">
<img src="http://web.archive.org/web/https://i.stack.imgur.com/I8WNI.png" height="50" width="50">
</div>
currently testing my new userscript (relevant to discussion)
</span>
The script is being run from tampermonkey on page load.
Why is a new div created for every link?
A Discovery:
There is a variable that is mentioned in the code above called matchURLs - I've just discovered the weirdest thing.
This variable is defined as
var matchURLs = ["https://i.stack.imgur.com/", "https://i.imgur.com/"];
it's basically just the URLs to check if the image is from (I don't want to proxy every image, only ones from the URLs above)
But the weird part of this is, if I remove the second link from the array, e.g if I define matchURLs as
var matchURLs = ["https://i.stack.imgur.com/"];
then the code works as expected and puts all of the images inside a single imageContainer div instead of multiple divs. Is this the cause of the issue? Why?
The containsAny() function is defined as:
function containsAny(str, substrings) {
for (var i = 0; i != substrings.length; i++) {
if(str.startsWith(substrings[i])) {
return true;
}
}
return false;
}
Use parentLink.querySelector( '.image-container' ) to try and select the image container that is a child of the .comment-copy element. If it is null, it doesn't exist, otherwise you have a reference to that element.
Run the snippet to see the first and third comment returning the proper element with innerHTML 1 and 3 respectively and the second comment returning null because the image container doesn't exist yet.
var links = document.getElementsByTagName('a');
for (var i = 0; i < links.length; i++) {
var link = links[i];
var linkParent = links[i].parentNode;
result = linkParent.querySelector('.image-container');
console.log(result);
}
<span class="comment-copy">
this comment has some links like <a href="https://i.stack.imgur.com/I8WNI.png" >image</a> and <a href="https://i.stack.imgur.com/I8WNI.png" >image</a>
<div class="image-container">1</div>
</span>
<span class="comment-copy">
this comment has some links like <a href="https://i.stack.imgur.com/I8WNI.png" >image</a> and <a href="https://i.stack.imgur.com/I8WNI.png" >image</a>
</span>
<span class="comment-copy">
this comment has some links like <a href="https://i.stack.imgur.com/I8WNI.png" >image</a> and <a href="https://i.stack.imgur.com/I8WNI.png" >image</a>
<div class="image-container">3</div>
</span>

Generate html using Javascript

I have a gallery page that is updated often with new images. I use simple HTML to post the photos. My process currently is copy and paste the set of tags for a photo and change the number to correspond with the image file name. E.G. I change the number 047 to 048. Copy-Paste, change it to 049. This goes on until I have reached the number of additional photos. As you can see, this is very inefficient and there must be a better way of doing this. I was wondering if there is a simpler way to achieve this with Javascript? Perhaps generate additional tags by inputing a certain number or range?
Any ideas that would make this process efficient are welcomed please! Thank you!
<div class="cbp-item trim">
<a href="../assets/images/trim/img-trim-047.jpg" class="cbp-caption cbp-lightbox" data-title="">
<div class="cbp-caption-defaultWrap">
<img src="../assets/images/trim/img-trim-047.jpg" alt="">
</div>
</a>
</div>
You could use a templating solution. There are several libraries for that, but you can also implement it yourself.
Here is one way to do that:
Put the HTML for one image in a script tag that has a non-standard language property so the browser will just ignore it
Put some keywords in there that you'll want to replace, e.g. {url}. You can invent your own syntax.
Read that template into a variable
In the JS code, put all the images' URLs in an array of strings
For each element in that array, replace the keywords in the template string with that particular URL, and concatenate all these resulting HTML snippets.
Inject the resulting HTML into the appropriate place in the document.
Here is a snippet doing that:
// Add new images here:
var images = [
"https://upload.wikimedia.org/wikipedia/commons/thumb/e/e0/SNice.svg/330px-SNice.svg.png",
"https://nettemarie357.files.wordpress.com/2014/09/smiley-face.jpg?w=74&h=74",
];
// Load the template HTML
var template = document.querySelector('script[language="text/template"]').innerHTML;
// Use template to insert all the images:
container.innerHTML = images.map(url => template.replace(/{url}/g, url)).join('');
img { max-width: 50px }
<div id="container"></div>
<script language="text/template">
<div class="cbp-item trim">
<a href="{url}" class="cbp-caption cbp-lightbox" data-title="">
<div class="cbp-caption-defaultWrap">
<img src="{url}" alt="">
</div>
</a>
</div>
</script>
This would help you creating it programatically:
var new_row = document.createElement('div');
new_row.className = "cbp-item trim";
var a = document.createElement('a');
a.href = "../assets/images/trim/img-trim-047.jpg";
a.className= "cbp-caption cbp-lightbox";
document.body.appendChild(a);
var div = document.createElement('div');
div.className = "cbp-caption-defaultWrap";
var img = document.createElement('img');
img.src= "../assets/images/trim/img-trim-047.jpg";
div.appendChild(img);
a.appendChild(div);
new_row.appendChild(a);
If it is just about printing HTML, I suggest you to use plugins like Emmet for Sublime Text editor.
When you install this plugin and see how it works, you can simple create a complex html in a way that 'for' loop would do this. This will help you to change only the image/link number of every item.
Check the demo in the link, that I added.
Here's an example in Java Script that will generate the html you will need. Set the total to whatever number you need to generate the number of images you want.
var total = 47;
var hook = document.getElementById('hook');
// Main Node for SlideShow
var node = document.createElement('div');
node.classList = "cbp-item trim";
// Work out the correct number
var n = function(int) {
var length = int.toString().length;
return length === 1
? '00' + int
: length === 2
? '0' + int
: length
}
// Create the item
var createItem = function(int){
// Create Anchor
var a = document.createElement('a');
a.href = '../assets/images/trim/img-trim-' + ( n(int) ) + '.jpg" class="cbp-caption cbp-lightbox';
a.classList = 'cbp-caption cbp-lightbox';
// Create Div
var div = document.createElement('div');
div.classList = 'cbp-caption-defaultWrap';
// Create Image
var img = document.createElement('img');
img.src = '../assets/images/trim/img-trim-' + ( n(int) ) + '.jpg';
img.alt = 'gallery image';
// Finalise Dom Node
var container = div.appendChild(img)
a.appendChild(div);
// Return Final Item
return a
}
// Create Items
for (var i = 1; i < total + 1; i++) {
node.appendChild(createItem(i));
}
// Append Main Node to Hook
hook.appendChild(node);
<div id="hook"></div>

refresh html content with each new GET request

I have been practicing my Vanilla Js/jQuery skills today by throwing together a newsfeed app using the news-api.
I have included a link to a jsfiddle of my code here. However, I have removed my API key.
On first load of the page, when the user clicks on an image for a media outlet, e.g. 'techcrunch', using an addEventListener, I pass the image's id attribute to the API end point 'https://newsapi.org/v1/articles' and run a GET request which then proceeds to create div elements with the news articles content.
However, after clicking 1 image, I cannot get the content to reload unless I reload the whole page manually or with location.reload().
On clicking another image the new GET request is running and returning results, as I am console logging the results.
I am looking for some general guidance on how to get the page content to reload with each new GET request.
Any help would be greatly appreciated.
Many thanks for your time.
Api convention:
e.g https://newsapi.org/v1/articles?source=techcrunch&apiKey=APIKEYHERE
EventListener:
sourceIMG.addEventListener('click', function() {
$.get('https://newsapi.org/v1/articles?source=' + this.id + '&sortBy=latest&apiKey=APIKEYHERE', function(data, status) {
console.log(data);
latestArticles = data.articles;
for (i = 0; i < latestArticles.length; i++) {
//New Article
var newArticle = document.createElement("DIV");
newArticle.id = "article";
newArticle.className += "article";
//Title
//Create an h1 Element
var header = document.createElement("H1");
//Create the text entry for the H1
var title = document.createTextNode(latestArticles[i].title);
//Append the text to the h1 Element
header.appendChild(title);
//Append the h1 element to the Div 'article'
newArticle.appendChild(header);
//Author
var para = document.createElement("P");
var author = document.createTextNode(latestArticles[i].author);
para.appendChild(author);
newArticle.appendChild(para);
//Description
var description = document.createElement("H4");
var desc = document.createTextNode(latestArticles[i].description);
description.appendChild(desc);
newArticle.appendChild(description);
//Image
var image = document.createElement("IMG");
image.src = latestArticles[i].urlToImage;
image.className += "articleImg";
newArticle.appendChild(image);
//Url link
//Create a href element
var a = document.createElement('a');
var link = document.createElement('p');
var innerLink = document.createTextNode('Read the full story ');
link.appendChild(innerLink);
a.setAttribute("href", latestArticles[i].url);
a.innerHTML = "here.";
link.appendChild(a);
newArticle.appendChild(link);
//Append the Div 'article' to the outer div 'articles'
document.getElementById("articles").appendChild(newArticle);
}
});
}, false);
I tried your fiddle using an api key. It is working for me in that content new content is appended to the previous content in the #articles div. If I'm understanding your question, when a news service image is clicked you would like for only that news service's articles to show. To do that you would need to clear the contents of #articles before appending new content.
To do that with plain js you could use the following above your for loop:
// Removing all children from an element
var articlesDiv = document.getElementById("articles");
while (articlesDiv.firstChild) {
articlesDiv.removeChild(articlesDiv.firstChild);
}
for (i = 0; i < latestArticles.length; i++) {...
Full disclosure, I added the variable name 'articlesDiv' but otherwise the above snippet came from https://developer.mozilla.org/en-US/docs/Web/API/Node/removeChild

modify the end of an image source from 's.jpg' to 'o.jpg' with javascript

My CMS has a plugin for pulling in facebook images with stories. I cannot change it.
In the source it creates the image URL ends in s.jpg but I would like to modify it so it ends in o.jpg in order to pull in the largest photo type
This is the code:
<div class="post_picture">
<img src="http://photos-g.ak.fbcdn.net/hphotos-ak-ash3/534627_477964682254266_1412043521_s.jpg" alt="">
</div>
Is this possible? I would image you copy the source URL, modify the end from s.jpg to o.jpg and the replace the old URL w/ the new one.
EDIT:
Thanks everyone for your replies & solutions, below is another solution I found...
$('.div1>img').attr('src',function(i,e){
return e.replace("s.jpg","o.jpg");
})
ex - http://jsfiddle.net/designaroni/4Da2a/
function imgSrcOverwrite() {
var imgs = document.getElementsByTagName('img'),
loopImg, fbImgs = [];
// Loop through each IMG, check if it's a facebook image &
// then replace _s.jpg with _o.jpg in the src attribute
for ( var x=0; x<imgs.length; x++ ) {
loopImg = imgs[x];
if ( loopImg.src.indexOf('photos-g.ak.fbcdn.net') > -1 ) {
loopImg.src = loopImg.src.replace('_s.jpg', '_o.jpg');
fbImgs.push(loopImg);
}
}
imgs = loopImg = null;
// return array of fb images so you can do more stuff with them
return fbImgs;
}
imgSrcOverwrite();
If you're using jQuery I'd replace the second line with
var imgs = $('.post_picture img'),
http://jsfiddle.net/CoryDanielson/jmzk2/
Here's a quick example of how it may be possible.
// get image dom element
var img = document.getElementById('imageId');
// update src
img.src = img.src.replace(/s.jpg/, 'o.jpg');
If you are using jquery,
Try to access the img tag by this -
var src = '';
$('.post_picture').find('img').each(function(){
src = $(this).attr('src').toString().replace('_s.jpg', '_o.jpg');
$(this).attr('src',src);
});

Categories

Resources