Deteching if child already exists inside parent - javascript

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>

Related

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

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

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>

Add/Delete Button

I'm working on this project, and what it is supposed to do is show a series of "business cards" that contain the name of someone and their image. When you hover over the card an x button appears and clicking it will delete the card from the page. There are also to inputs one for the url of an image and another for the name of the person, under these 2 inputs are a submit button, and when it's submitted it's supposed to create the card.
Right now I have the code to create the card, but it does not appear to be working.
document.getElementById('btn').onclick = function() {
var addPhoto = document.getElementById('imagename').value,
src = addPhoto +'.png',
img = document.createElement('img');
img.src = src;
var addName =document.getElementById('actorname').value;
var card = document.createElement('div');
card.setAttribute('class','card');
var title = document.createElement('h1');
container.innerHTML = addName;
var cardImage = document.createElement('div');
cardImage.setAttribute('class','card-image');
var img = document.createElement('img');
image.setAttribute('src',addPhoto);
var button = document.createElement('input');
button.setAttribute('class','buttons');
button.setAttribute('type','image');
button.setAttribute('src','img/close.png');
card.appendChild(title);
card.appendChild(cardImage);
cardImage.appendChild(image);
cardImage.appendChild(button);
document.getElementById("lefty").appendChild(card);
};
To add a new card, you should set everything up from the ground. You should create the following in Javascript before adding it to the page:
<div class="card">
<h1>the_name</h1>
<div class="card-image">
<img src="the_link"/>
<input class="buttons" type="image" src = "img/close.png"/>
</div>
</div>
You already know how to get *'the_name'* and *'the_link'* (the variables for your cards). I'm gonna use those variables in my solution. First you have to create all the elements.
var card = document.createElement('div');
card.setAttribute('class','card');
var title = document.createElement('h1');
container.innerHTML = the_name;
var cardImage = document.createElement('div');
cardImage.setAttribute('class','card-image');
var image = document.createElement('img');
image.setAttribute('src',the_link);
var button = document.createElement('input');
button.setAttribute('class','buttons');
button.setAttribute('type','image');
button.setAttribute('src','img/close.png');
Now you want to paste all the elements to the webpage. First append the elements to each other, then append them to the place you want to get them to (in your case the body).
card.appendChild(title);
card.appendChild(cardImage);
cardImage.appendChild(image);
cardImage.appendChild(button);
document.body.appendChild(card);
You can delete an element in an odd way using javascript. First you have to get the domNode of the element you want to get rid of. For example one of the two nodes:
var unwanted = document.getElementsByTagName('a')[0];
var unwanted = document.getElementById('id_of_unwanted_item');
then you have to execute the following function:
unwanted.parentNode.removeChild(unwanted)
You have the basic idea, you just need to take it a few steps farther. For the delete you'll want to add an ID to each card and then call a JS function with that ID:
<div class="card" id="william">
<h1>William Finley (Phantom of the Paradise)</h1>
<div class="card-image">
<img src="img/avatars/william.png"/>
<input class="buttons" type="image" src = "img/close.png" onclick="removeCard('william')"/>
</div>
</div>
<script type="text/javascript">
var removeCard = function(cardId) {
var cardElement = document.getElementById(cardId);
cardElement.parentNode.removeChild(cardElement);
}
</script>
When you create a card (which Nicky explained well) make sure you add the ID and onclick event to your element generation.

How can I link to an anchor tag that doesn't exist until the Javascript has created it?

I have a page that has a set of <div> elements, and each one has an anchor tag associated with it. It looks something like this:
<a name="anchor-0"></a>
<div id="div0">Some stuff</div>
<a name="anchor-1"></a>
<div id="div1">More stuff</div>
<a name="anchor-2"></a>
<div id="div2">Yet more stuff</div>
The problem is that this set of <div> and <a> tags are generated by Javascript, and so they don't exist until after the page has been created. When I create a link like this:
http://www.mywebsite.com/mypage.html#anchor-2
... it loads the page but does not jump to the anchor-2 position, which is only created some time after the browser has had time to execute the Javascript that generated it.
How can I get the browser to move to the selected anchor tag position once the Javascript has generated them?
Here is essentially what the Javascript that is generating the HTML looks like:
function init() {
gapi.client.setApiKey('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
gapi.client.load('blogger', 'v2', function() {
var request = gapi.client.blogger.posts.list({
'blogId': 'xxxxxxxxxxxxxxxxxxxxxxxx',
'fields': 'items(content,title)'
});
request.execute(function(response) {
var main = document.getElementById("main");
var anchor = 0;
for (var i = 0; i < response.items.length; i++)
{
var Div = document.createElement("div")
$(Div).append(response.items[i].title);
$(main).append(Div);
anchor = document.createElement("a");
anchor.name = "anchor-" + anchor;
anchor = anchor +1;
}
});
});
}
after creation of element, you could do:
location.hash = "#anchor-2";
or using scrollIntoView
element = document.getElementById('your_element-id');
element.scrollIntoView();
get the hash value from url, by doing something like::
var hashVal = window.location.hash.substr(1);
//then jump to that hash
location.hash = "#" + hashVal;

Dynamically Insert Links In Span Tag Using Javascript (around anchor text)

On a html page I have:
<span class="me">See More</span>
And I should process it using javascript to get this (create link out of text):
<span class="me"><a href="http://domain.com>See More</a></span>
Never mind 0 I figure out that it works well. My bad, sorry.
function changespan() {
var spans = document.querySelectorAll('span.image');
for (var i = spans.length; i--; ) {
var a = document.createElement('a');
a.href = "http://domain.com";
spans[i].appendChild(a).appendChild(a.previousSibling);
}
}
Let me know what is appendChild(a) and .appendChild(a.previousSibling)?
Here is the demo: http://jsfiddle.net/8eK7z/1/

Categories

Resources