How to find html object in the document? - javascript

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>

Related

How can I create a syntax like vue js in vanilla JavaScript?

<div id="">
<span>{{msg}}</span>
</div>
Let's think msg is variable of JavaScript and now I want to get the parent tag of {{msg}} and push a new value by innerHTML, here {{msg}} working as an identity.
demo JavaScript example:
<script>
var msg = "This is update data";
{{msg}}.parentElement.innerHTML=msg;
</scritp>
This is not actual JavaScript code, only for better understanding.
You can use jquery easily to find that element and then replace the text
var msg = "This is update data";
$(`span:contains(${msg})`).html("Its New");
In javascript:
var spanTags = document.getElementsByTagName("span");
var msg = "This is update data";
var found;
for (var i = 0; i < spanTags.length; i++) {
if (spanTags[i].textContent == msg) {
found = spanTags[i];
break;
}
}
Now, you have found that element in found and you can now change its text
if (found) {
found.innerHTML = "New text";
}
The simplest approach is to treat the entire document as a string and then re-parse it when you're done.
The .innerHTML property is both an HTML decompiler and compiler depending on weather you're reading or writing to it. So for example if you have a list of variables that you want to replace in your document you can do:
let vars = {
msg: msg, // pass value as variable
test_number: 10, // pass value as number
test_str: 'hello' // pass value as string
};
let htmlText = document.body.innerHTML;
// find each var (assuming the syntax is {{var_name}})
// and replace with its value:
for (let var in vars) {
let pattern = '\\{\\{\\s*' + var + '\\s*\\}\\}';
let regexp = new RegExp(pattern, 'g'); // 'g' to replace all
htmlText = htmlText.replace(regexp, vars[var]);
}
// Now re-parse the html text and redraw the entire page
document.body.innerHTML = htmlText;
This is a quick, simple but brutal way to implement the {{var}} syntax. As long as you've correctly specified/designed the syntax to make it impossible to appear in the middle of html tags (for example <span {{ msg > hello </ }} span>) then this should be OK.
There may be performance penalties redrawing the entire page but if you're not doing this all the time (animation) then you would generally not notice it. In any case, if you are worried about performance always benchmark your code.
A more subtle way to do this is to only operate on text nodes so we don't accidentally mess up real html tags. The key to doing this is to write your own recursive descent parser. All nodes have a .childNodes attribute and the DOM is strictly a tree (non-cyclic) so we can scan the entire DOM and search for the syntax.
I'm not going to write complete code for this because it can get quite involved but the basic idea is as follows:
const TEXT_NODE = 3;
let vars = {
msg: msg, // pass value as variable
test_number: 10, // pass value as number
test_str: 'hello' // pass value as string
};
function walkAndReplace (node) {
if (node.nodeType === TEXT_NODE) {
let text = node.nodeValue;
// Do what you need to do with text here.
// You can copy the RegExp logic from the example above
// for simple text replacement. If you need to generate
// new DOM elements such as a <span> or <a> then remove
// this node from its .parentNode, generate the necessary
// objects then add them back to the .parentNode
}
else {
if (node.childNodes.length) {
for (let i=0; i<node.childNodes.length; i++) {
walkAndReplace(node.childNodes[i]); // recurse
}
}
}
}
walkAndReplace(document.body);

Splitting a string to create a HTML link

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);

Filter in AngularJS - text in html tags

I'm not very good at filtering and wanted to write a custom filter based on the following:
I call a service that returns a JSON object with HTML String thats concatenated with another string - so the HTML is funky.
I want to get the text1 and text2 form the following returned HTML string:
<span><b>text1</b><b>text2</b>text3</span>
I have no control how the above is returned to me, but i just wanted to get the two values and concatenate them: text1text2
There is a builtin DOM parser - or you can find a parser in your environment. See on MDN parsing XML and Element. So you could do something like this:
var x = "<span><b>text1</b><b>text2</b>text3</span>";
var oDOM = new DOMParser().parseFromString(x, "text/xml");
var b = oDOM.documentElement.getElementsByTagName("b");
b.length // 2
b[1].innerHTML // text2
HTH
if you just need to strip the html tags, I think you can use the below code
var noHTML = OriginalString.replace(/(<([^>]+)>)/ig,"");
For a filter implementation
angular.module('myNoHtmlFilterApp', [])
.filter('noHtml', function() {
return function(input) {
input = input || '';
var out = input.replace(/(<([^>]+)>)/ig,"");
return out;
};
})
Based on DineSH's answer - I did something like this:
$scope.getTextFromHTML = function(html){
var oDOM = new DOMParser().parseFromString(html, "text/xml");
var b = oDOM.documentElement.getElementsByTagName("b");
return b[0].innerHTML+b[1].innerHTML;
};
I have to pre-define the html string since its not on the DOM yet, like so:
var html = "<span><b></b><b></b></span>";
I will probably add a for loop later in case there is a string I missed, but for now, this is perfect. Thank you for all of your help!

Splitting node content in JavaScript DOM -

This existing answer is an excellent piece of code that very nearly does what I want. Like the OP in that questions I want HTML tags to be split, but based on a tag rather than an offset, and bounded by an item that should not be split.
That is, I want to turn this:
<p>
<strong>hi there, how <em>are <span>y<!--break-->ou</span> doing</em> today?</strong>
</p>
into this:
<p>
<strong>hi there, how <em>are <span>y</span></em></strong>
<!--break-->
<strong><em><span>ou</span> doing</em> today?</strong>
</p>
I'm still getting my head around javascript so while I had a play with the jsbin provided by #Hemlock I couldn't get it to do what I intended.
The given answer was:
function splitNode(node, offset, limit) {
var parent = limit.parentNode;
var parentOffset = getNodeIndex(parent, limit);
var doc = node.ownerDocument;
var leftRange = doc.createRange();
leftRange.setStart(parent, parentOffset);
leftRange.setEnd(node, offset);
var left = leftRange.extractContents();
parent.insertBefore(left, limit);
}
function getNodeIndex(parent, node) {
var index = parent.childNodes.length;
while (index--) {
if (node === parent.childNodes[index]) {
break;
}
}
return index;
}
No ranges required, you just need to duplicate all the cut elements and move their children around:
function splitOn(bound, cutElement) {
// will divide the DOM tree rooted at bound to the left and right of cutElement
// cutElement must be a descendant of bound
for (var parent = cutElement.parentNode; bound != parent; parent = grandparent) {
var right = parent.cloneNode(false);
while (cutElement.nextSibling)
right.appendChild(cutElement.nextSibling);
var grandparent = parent.parentNode;
grandparent.insertBefore(right, parent.nextSibling);
grandparent.insertBefore(cutElement, right);
}
}
(jsfiddle demo)
You could build your own split function by thinking how to split the content into an array and later concatinate the string together.
the problem with this answer is that it does not start/nor finish any split tag, like in your situation, is the SPAN element.
<script>
var content = document.getElementById('content');
var elements = document.getElementsByTagName('strong');
var array = element.split("<!--break-->");
var string = '';
for(var i = 0; i < array.length; i++) {
string += '<strong>' + sarray[i] + "</strong>';
}
content.innerHTML = string;
</script>
<div id="content">
<strong>hi there, how <em>are <span>y<!--break-->ou</span> doing</em> today?</strong>
</div>

How to get title tag in a string of html?

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);

Categories