I'm working with an API that returns strings with inline links like so:
This is a question I'm asking on <my_link type="externalLink" data="https://stackoverflow.com/">StackOverflow</my_link> about splitting a string and reconstructing as a HTML link.
The reason for this is apparently so the API can be used by both web and native platforms and that HTML is kept away from the data. There are also internalLink types which will allow app developers to link to content within an app rather than opening a web browser.
I need to be able to pass this string into a function and return the full string with an tag like so:
This is a question I'm asking on StackOverflow about splitting a string and reconstructing as a HTML link.
Another thing to consider is that the string could have multiple links in it.
My initial attempt is basic and does get externalLink from the first link but I'm unsure of how to get the value of the data attribute and then re-run for any other links.
export default function convertLink(string) {
let stringWithLinks = string;
if (string.includes('<my_link')) {
const typeStart = string.indexOf('"') + 1;
const typeEnd = string.indexOf('"', typeStart);
const typeText = string.substring(typeStart, typeEnd); // externalLink
}
return stringWithLinks;
}
You can set the string as .innerHTML of a dynamically created element and use .getAttribute() to get the data attribute of <my_link> element, set .innerHTML of dynamically created <a> element and use .replaceChild() to replace <my_link> with <a> element
let str = `This is a question I'm asking on <my_link type="externalLink" data="https://stackoverflow.com/">StackOverflow</my_link> about splitting a string and reconstructing as a HTML link.`;
let div = document.createElement("div");
div.innerHTML = str;
let my_links = Array.prototype.map.call(div.querySelectorAll("my_link"), link =>
link.getAttribute("data"));
console.log(my_links);
for (let link of my_links) {
let a = document.createElement("a");
a.href = link;
a.target = "_blank";
a.innerHTML = div.querySelector("my_link").innerHTML;
div.replaceChild(a, div.querySelector("my_link"))
}
console.log(div.innerHTML);
Add the string as HTML of a new element. Loop over all the my_link elements extracting the relevant data, then build a new anchor that can then replace the my_link on each iteration.
function convertAllLinks(str) {
let el = document.createElement('div');
el.innerHTML = str;
el.querySelectorAll('my_link').forEach(link => {
let anchor = document.createElement('a');
anchor.href = link.getAttribute('data');
anchor.setAttribute('target', '_blank');
anchor.textContent = link.textContent;
link.parentNode.replaceChild(anchor, link);
});
return el.innerHTML;
}
convertAllLinks(str);
DEMO
Here's another solution using DOMParser(), in case you might need to do any more DOM modifications later on.
let stringWithLinks = `This is a question I'm asking on <my_link type="externalLink" data="https://stackoverflow.com/">StackOverflow</my_link> about splitting a string and reconstructing as a HTML link.`,
tempDOM = new DOMParser().parseFromString('<doc>' + stringWithLinks + '</doc>', "text/xml"),
linkElements = tempDOM.getElementsByTagName('my_link');
for (let i=0; i<linkElements.length; i++) {
let newA = document.createElement('a');
newA.setAttribute('src', linkElements[i].getAttribute('data'));
let linkType = linkElements[i].getAttribute('type');
if (linkType == 'externalLink') {
newA.setAttribute('target', '_blank');
}
newA.innerHTML = linkElements[i].innerHTML;
tempDOM.documentElement.replaceChild(newA, linkElements[i]);
}
console.log(tempDOM.documentElement.innerHTML);
Related
I'm making a moviefilter website for school. Now the last part is that I need to link the imdbID from my array to the movie posters on the screen. I made the links, the filter mechanics work but when I click on a poster all the ID's of that filter are being added to the end. Not just one.
My teacher says I can forEach trough the movieArray array and write everything I need in that loop. Can someone help me or look at my code what I'm doing wrong. I wrote 2 seperate functions now to create the arguments needed.
My code:
const addMoviesToDom = function (movieArray) {
const movieList = document.querySelector("#movielist");
movieList.innerHTML = "";
const moviePoster = movieArray.map(item => {
return item.Poster;
});
const imdbId = movieArray.map(item => {
return item.imdbID;
})
moviePoster.forEach(element => {
let newLi = document.createElement("li");
let newLink = document.createElement("a");
let images = document.createElement("img");
images.src = element;
newLink.setAttribute("href", "https://www.imdb.com/title/" + imdbId);
newLink.setAttribute("target", "_blank");
newLi.append(newLink);
newLink.append(images);
movieList.appendChild(newLi);
})
};
addMoviesToDom(movies);
Thanks in advance, hopefully you guys understand what I'm trying to explain, this is all pretty new for me.
imdbId is an array of all the IDs. When you concatenate that to "https://www.imdb.com/title/" it's turned into a string, which joins all the IDs with a , delimiter. So you're not setting the href to just the ID corresponding to the current moviePoster element.
You need to index the array to get the correct ID. forEach provides the array index as the second argument to the callback function, so you can change element => to (element, i) =>, and then use imdbId[i].
But a simpler way would be to skip creating the moviePoster and imdbId arrays, and just create all the elements from movieArray.
const addMoviesToDom = function(movieArray) {
const movieList = document.querySelector("#movielist");
movieList.innerHTML = "";
movieArray.forEach(element => {
let newLi = document.createElement("li");
let newLink = document.createElement("a");
let images = document.createElement("img");
images.src = element.Poster;
newLink.setAttribute("href", "https://www.imdb.com/title/" + element.imdbId);
newLink.setAttribute("target", "_blank");
newLi.append(newLink);
newLink.append(images);
movieList.appendChild(newLi);
})
};
addMoviesToDom(movies);
The following code I have is looking for a specific text in the DOM, based on a pattern.
Instead of replacing the text I would like to transform it to a link where the text is a parameter in the link.
Let's say the link would look like: https://google.com/search?q=matched_text_added_here
var allTextNodes = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT),
// some temp references for performance
tmptxt,
tmpnode,
// compile the RE and cache the replace string, for performance
identify = /ABC\d{7}/g,
replaceValue = "changed";
// iterate through all text nodes
while (allTextNodes.nextNode()) {
tmpnode = allTextNodes.currentNode;
tmptxt = tmpnode.nodeValue;
tmpnode.nodeValue = tmptxt.replace(identify, replaceValue);
}
Instead of replacing the text I would like to transform it to a link
So, instead of replacing the text:
create a link
let newLink = document.createElement('a')
configure the link
newLink.href = 'https://google.com/search?q=' + encodeURIComponent(nodeValue)
newLink.textContent = nodeValue
replace the text node with the link node
tmpnode.replaceWith(newLink)
I create an HTML object using such code:
var parseHTML = function(str) {
var tmp = document.implementation.createHTMLDocument();
tmp.body.innerHTML = str;
return tmp.body.children;
};
var htmlCollection = parseHTML('<div><article><h1>Article heading</h1></article></div>');
var articleNode = htmlCollection[0].childNodes[0];
In my html code I also have such element:
<article>
<h1>Article heading</h1>
</article>
So the question is: how can I find article element in my html document using articleNode?
Non-jQuery solution is preferred.
UPD: articleNode and article in the html are two separate elements. I need to reference the second one using the first one
UPD2 Use-case: I send document.location in an ajax request, and receive html code (as a string) for contents of 'content part' of the page from which the requestwas sent (I apologize for the tautology). Then I need to get the CSS path for that content container. That's why I convert string to html object and trying to find it in the document.
If you return the document fragment, you can query it using `querySelector[All], something like:
window.onload = function () {
var parseHTML = function(str) {
var tmp = document.implementation.createHTMLDocument();
tmp.body.innerHTML = str;
return tmp;
};
var htmlCollection = parseHTML('<div><article><h1>Article heading</h1>'+
'</article><article><h1>Article heading 2</h1>'+
'</article></div>');
var result = document.querySelector('#result');
var articleNodes = htmlCollection.querySelectorAll('div, article');
result.innerHTML = 'first article: '+articleNodes[1].outerHTML+'<br>';
result.innerHTML += 'the whole enchilada: '+articleNodes[0].outerHTML;
};
<div id="result"></div>
Hey i'm loading an html page using ajax into a string, now i want to find the title of the page and use it.
Now i did manage to get the <title> using regex but that returns the tag along with the title itself and i wish to extract that from the string or could there be a way to do that in the regex?
This is my code :
var title = result.match(/<title[^>]*>([^<]+)<\/title>/);
Now how do i get the actuall title after this/ instead of this?
.match() returns array of matches, use
var title = result.match(/<title[^>]*>([^<]+)<\/title>/)[1];
to get value in parentheses
load your response html string into a jQuery object like so and retrieve the text
$(response).find("title").text();
A relatively simple plain-JavaScript, and non-regex, approach:
var htmlString = '<head><title>Some title</title></head><body><p>Some text, in a paragraph!</p></body>',
html = document.createElement('html'),
frag = document.createDocumentFragment();
html.innerHTML = htmlString;
frag.appendChild(html);
var titleText = frag.firstChild.getElementsByTagName('title')[0].textContent || frag.firstChild.getElementsByTagName('title')[0].innerText;
console.log(titleText);
JS Fiddle demo.
I've, obviously, had to guess at your HTML string and removed the (presumed-present) enclosing <html>/</html> tags from around the content. However, even if those tags are in the string it still works: JS Fiddle demo.
And a slightly more functional approach:
function textFromHTMLString(html, target) {
if (!html || !target) {
return false;
}
else {
var fragment = document.createDocumentFragment(),
container = document.createElement('div');
container.innerHTML = html;
fragment.appendChild(container);
var targets = fragment.firstChild.getElementsByTagName(target),
result = [];
for (var i = 0, len = targets.length; i<len; i++) {
result.push(targets[i].textContent || targets[i].innerText);
}
return result;
}
}
var htmlString = '<html><head><title>Some title</title></head><body><p>Some text, in a paragraph!</p></body></html>';
var titleText = textFromHTMLString(htmlString, 'title');
console.log(titleText);
JS Fiddle demo.
CODE:
var title = result.match("<title>(.*?)</title>")[1];
Make the reg exp to case insensitive.
Here is the complete code:
var regex = /<title>(.*?)<\/title>/gi;
var input = "<html><head><title>Hello World</title></head>...</html>";
if(regex.test(input)) {
var matches = input.match(regex);
for(var match in matches) {
alert(matches[match]);
}
} else {
alert("No matches found!");
}
try this I think this will help. It perfectly works in my case. :)
var FindTag=(data='',tag='')=>{
var div=document.createElement('div');
div.innerHTML=data;
data=$(div).find(tag)[0].outerHTML;
return data;
}
var data=FindTag(data,"title");
Regular expressions aren't a good way to look for things in HTML, which is too complex for a simple one-off regex. (See the famous post on this topic.) Instead, use DOMParser's parseFromString and then look in the resulting document:
const html = "<!doctype html><head><title>example</title>";
const parser = new DOMParser();
const doc = parser.parseFromString(html, "text/html");
const title = doc.querySelector("title");
console.log(title.textContent);
I need to write a method that takes a String and parses it for links (a href). If it finds a link it should add target="_blank" to the link, if it is not already there.
Example:
The Inputstring "
Google and target="_blank">Yahoo are search engines
... should result in the output String
Google and Yahoo are search engines
Any idea how to realize this?
Not very difficult with plain js.
var links = document.getElementsByTagName('a');
var len = links.length;
for(var i=0; i<len; i++)
{
links[i].target = "_blank";
}
Fraught with problems but usable with plain JavaScript:
function addBlankTargets(s) {
return (""+s).replace(/<a\s+href=/gi, '<a target="_blank" href=');
}
Or with jQuery:
function addBlankTargets(s) {
var p = $('<p>' + s + '</p>');
p.find('a').attr('target', '_blank');
return p.html();
}
var s = 'Google and '
+ 'Yahoo '
+ 'are search engines.';
var x = addBlankTargets(s);
x; // => 'Google and
// Yahoo
// are search engines.'
If you are targeting at modern browsers, instead of manipulating a string containing HTML and having to handle all the many special cases, you can transform the string into DOM.
At this point manipulating the DOM is trivial and you can then convert it back to a serialised string.
function decorateRichText(html) {
const domParser = new DOMParser()
const document = domParser.parseFromString(html, `text/html`)
const serializer = new XMLSerializer()
// Handles external links
const links = document.querySelectorAll(`a`)
links.forEach((link) => {
if (link.href) {
if (isExternalUrl(link.href)) {
link.target = `_blank`
link.rel = `noopener noreferrer`
}
}
})
return serializer.serializeToString(document)
}
Leave the browser JS engine do the heavy stuff and remember: code that you don't write is code you have not to debug :-)
You can use jQuery to parse the element, add the attribute, and then read out the HTML, like so:
var addTarget = function(input) {
return $('<span>' + input + '</span>').find('a').attr('target', '_blank').html();
};
console.log(addTarget('Google'));
in two lines
var links = document.getElementsByTagName('a');
for(i in links)
links[i].target=="_blank"?links[i].style.color="#f0f" : links[i].style.color ='#0f0'
jsfiddle