I am new to this technology, and this is my first project.
I am calling an API and receiving a big parsed JSON file. From that entire big JSON.text (which is HTML code inside JSON.TEXT), how can I tap a particular class from that text?
Here is a sample:
After parsing the JSON.text - the outcome is -
some big html code then <table class ="info"> some big html code with TD and TR tag then </table> then again big HTML code.
I have to tap this table with class="info" from the entire parsed JSON.text which is HTML code.
screenshot -
Use DOMParser:
let response = {
parse: {
text: {
"*": `<div><\/div>
<p><\/p>
<table class="infobox vcard"><\/table>`
}
}
};
let parser = new DOMParser();
// extract the text part and pass it to the parser
let doc = parser.parseFromString(response.parse.text["*"], "text/html");
// get the first table element from the DOM, and display its `class` attribute
let tab = doc.querySelector("table");
console.log(tab.className); // infobox vcard
Once you've got the HTML out of your JSON file into a string you have two options. Option 1 is a regular expression:
// You have some string html
let match = html.match(/<table class="([^"])*"/)[1]
// match is the string "class1 class2" or whatever
match = match.split(" ")
// match is now an array ["class1", "class2"]
Option 2 is to find an HMTL parsing library (look one up), use it to parse the whole string, find the table using the output of that library, and extract its class that way.
Related
So we have an HTML that's generated from a YAML. We then process part of the DOM tree in the browser with remarkablejs. Namely, we get all elements with a "markdown" tag, parse its text with remarkablejs, and then replace its innerHTML. Unfortunately, either we don't render HTML entities correctly or we don't render HTML tags correctly.
Is there a recommended way to parse markdown in the browser?
// sample text:
// - markdown list with <b>`bold &`</b>
var elements = document.getElementsByClassName('markdown');
var count = elements.length;
// generates HTML entities correctly but not HTML tags
for (let index = 0; index < count; index++) {
var newText = md.render(elements[index].textContent);
elements[index].innerHTML = newText;
}
// generates HTML tags correctly but not HTML entities
for (let index = 0; index < count; index++) {
var newText = md.render(elements[index].innerHTML);
elements[index].innerHTML = newText;
}
Update: I think I understand what happens, remarkable treats & as & followed by amp;, and therefore, what I get back is & which doesn't render correctly. But then even if I change the input to just &, innerHTML changes that back to &.
Update: i was able to repo: https://jsfiddle.net/v1o7hLnr/. What's breaking remarkable is two things: 1) using the innerHTML to get the text to parse, and 2) wrapping the HTML entity in a code block. See here: https://jsfiddle.net/vLr1qbfc/
Here's what's happening (see https://jsfiddle.net/k8hy2mtf/):
We get the text to parse from the innerHTML.
The innerHTML encodes & to &.
Because the text is wrapped in a code block, ex: `x&y`, remarkable receives `x&y`.
remarkable then promptly ignores the HTML entity
(This is why textContent worked.)
So then my original question stands... what's the best way to properly render an HTML document with remarkable? Should we be parsing at build time instead?
I have a JSON response which is returning correctly and have successfully inputted data from it on my HTML page. However, this one JSON value returns some HTML code in which I do not need all of it, just the second anchor element. How would you guys do this?
The code below, the top two work fine. The P element does not. Any ideas? I just get undefined in the console.
// get photo url from description JSON value.
var imageURL = $(description).find('a')[ 1 ];
var parseImageURL = $(imageURL).attr('href');
// get author URL from description JSON value
var authorURL = $(description).find('a')[ 0 ];
var parseAuthorURL = $(authorURL).attr('href');
// get photo description from descriptions JSON value
var descriptionText = $(description).find('P')[5];
console.log(descriptionText);
Parse HTML
You need to "parse" the HTML. There's (at least) two ways to do it:
DOMParser or create an element and stuff the HTML string in there.
The following example shows the second method.
In my example, I assign some string to HTML but you are getting it in some object that you have parsed from JSON.
Create an element. Could be any old element, but I chose div
assign the HTML string to the innerHTML of the element created in step 2
use querySelectorAll to select the node you want. In this example, I selected all of the p elements.
I simply logged out the second p, but in your case, you add it to the page, I assume
let HTML = "<p>something</p><p>else</p>";
let el = document.createElement('div');
el.innerHTML = HTML;
let ps = el.querySelectorAll('p');
console.log(ps[1]);
I am working on a website that shows different APIs we provide. It showcases the example code and preview for each API and a button to open code in plunker. Since we have 100s of examples, we are not creating plunker for every example, but opening a new tab "https://plnkr.co/edit/?p=preview" and appending the corresponding html code into it.
Problem:
I have a total HTML document as a string in a javascript variable.
var html = "`<DOCTYPE html><html>....<body><script>abc.loader.load({apiToken:'', name:'',...});</script></body>`"
I have to change the object inside abc.loader.load(). Is there any way I can pick up the object inside abc.loader.load() and make changes in javascript and replace with existing object.
You can use a template literal and inject ${JSON.stringify(obj)} into that, like this:
<!--
var obj = {apiToken:'', name:''};
var html = `<!DOCTYPE html><html>....<body>
<script>abc.loader.load(${JSON.stringify(obj)});</script></body></html>`;
console.log(html);
-->
The script content is wrapped in <!-- --> to avoid misinterpretation of < inside your string.
Reading out the object and writing it again after mutation:
Although I would try to stick to the above template string, and would repeat the injection of different objects into it, here is what you could do when you have a given HTML string, and need to extract the object. This only works if the object literal is JSON compliant, i.e. it double quotes strings, and also property names:
<!--
var html = `<!DOCTYPE html><html>....<body>
<script>abc.loader.load({"apiToken":"", "name":""});</script></body></html>`;
html = html.replace(/(loader\.load\()(.*?)(?=\);<\/script>)/, function (_, prefix, json) {
var obj = JSON.parse(json); // get the object
obj.test = 3; // modify the object as you wish
return prefix + JSON.stringify(obj); // put the object
});
console.log(html);
-->
I'm practicing my Javascript by making a browser plugin to display external comment from Reddit on other webpages. The comments come in this format:
<div class="md"><p>I have them all over my yard. I didn't realize they spread so bad when I planted them.
They look cool with early morning dew on them though.</p>
</div>
I need to re-introduce the HTML characters (i.e. <div> => <div>), in order to put the formatted HTML onto the page.
Is there some native functionality Javascript provides to do this?
From what I can tell: x = document.createElement("div"); x.innerHTML = rawComment does not work, as the HTML is escaped, and the innerHTML returns a <div> with a string in it instead of a series of DOM nodes.
What you might try to do is the following:
// some dummy deocded text
let encoded = '<div class="md"><p>I have them all over my yard.</p></div>';
// create a new textarea and insert your encoded text
let dummyElement = document.createElement('textarea');
dummyElement.innerHTML = encoded;
// retrieve the textarea's value, which will be your decoded text
let decoded = dummyElement.value;
// decoded will be: <div class="md"><p>I have them all over my yard.</p></div>
Working Fiddle
This will work without jQuery, as you're only using the pure Javascript function's of your browser.
I am creating a templating system which can be interpreted at client side with Javascript to construct a fill in the blanks form e.g. for a letter to a customer etc.
I have the template constructed and the logic set out in pseudo code, however my unfamiliarity with jQuery I could use some direction to get me started.
The basic idea is there is a markup in my text node that denotes a field e.g. ${prologue} this is then added to an array called "fields" which will then be used to search for corresponding node names in the xml.
XML
<?xml version="1.0" encoding="UTF-8"?>
<message>
<text>${Prologue} - Dear ${Title} ${Surname}. This is a message from FUBAR. An engineer called but was unable to gain access, a new appointment has been made for ${ProductName} with order number ${VOLNumber}, on ${AppointmentDate} between ${AppointmentSlot}.
Please ensure you are available at your premises for the engineer. If this is not convenient, go to fubar.com or call 124125121515 before 12:00 noon the day before your appointment. Please refer to your order confirmation for details on what will happen on the day. ${Epilogue} - Free text field for advisor input<
</text>
<inputTypes>
<textBox type="text" fixed="n" size="100" alt="Enter a value">
<Prologue size="200" value="BT ENG Appt Reschedule 254159" alt="Prologue field"></Prologue>
<Surname value="Hoskins"></Surname>
<ProductName value=""></ProductName>
<VOLNumber size="8" value="" ></VOLNumber>
<Epilogue value=""></Epilogue>
</textBox>
<date type="datePicker" fixed="n" size="8" alt="Select a suitable appointment date">
<AppointmentDate></AppointmentDate>
</date>
<select type="select" >
<Title alt="Select the customers title">
<values>
<Mr selected="true">Mr</Mr>
<Miss>Miss</Miss>
<Mrs>Mrs</Mrs>
<Dr>Dr</Dr>
<Sir>Sir</Sir>
</values>
</Title>
<AppointmentSlot alt="Select the appointment slot">
<values>
<Morning>9:30am - 12:00pm</Morning>
<Afternoon>1:00pm - 5:00pm</Afternoon>
<Evening>6:00pm - 9:00pm</Evening>
</values>
</AppointmentSlot>
</select>
</inputTypes>
</message>
Pseudocode
Get list of tags from text node and build array called "fields"
For each item in "fields" array:
Find node in xml that equals array item's name
Get attributes of that node
Jump to parent node
Get attributes of parent node
If attributes of parent node != child node then ignore
Else add the parent attributes to the result
Build html for field using all the data gathered from above
Addendums
Is this logic ok, is it possible to start at the parent of the node and navigate downwards instead?
Also with regards to inheritence could we get the parent attributes and if the child attributes are different then add them to the result? What about if the number of attributes in the parent does not equal the number in the child?
Please do not provide fully coded solutions, just a little teasers to get me started.
Here is what I have so far which is extracting the tags from text node
//get value of node "text" in xml
var start = $(xml).find("text").text().indexOf('$');
var end = $(xml).find("text").text().indexOf('}');
var tag = "";
var inputType;
// find all tags and add them to a tag array
while (start >= 0)
{
//console.log("Reach In Loop " + start)
tag = theLetter.slice(start + 2, end);
tagArray.push(tag);
tagReplaceArray.push(theLetter.slice(start, end + 1));
start = theLetter.indexOf('$', start + 1);
end = theLetter.indexOf('}', end + 1);
}
Any other recommendations or links to similar problems would be welcome.
Thankyou!
I am using a similar technique to do html templating.
Instead of working with elements, I find it easier to work with a string and then convert it to html. In your case with jQuery, you could do something similar:
Have your xml as a string:
var xmlString='<?xml version="1.0" encoding="UTF-8"?><message><text>${Prologue} - Dear ${Title} ${Surname}... ';
Iterate through the string to do the replacements with a regex ($1 is the captured placeholder, for example Surname):
xmlString.replace(/$\{([^}]+)}/g,function($0,$1)...}
Convert to nodes if needed:
var xml=$(xmlString);
The benefits of the regex:
faster (just a string, you're not walking the DOM)
global replace (for example if Surname appears several times), just loop through your object properties once
simple regex /${([^}]+)}/ to target the placeholder
Get list of tags from text node and build array called "fields"
To create the array I would rather user regular expression, this is one of the best use for it (in my opinion) because we are indeed searching for a pattern :
var reg = /\$\{(\w+)\}/gm;
var i = 0;
var fields = new Array();
while ( (m = reg.exec(txt)) !== null)
{
fields[i++] = m[1];
}
For each item in "fields" array
jQuery offers some utility functions :
To iterate through your fields you could do this : $.each(fields, function(index, value){});
Navigating through the nodes and retrieving the values
Just use the jQuery function like you are already doing.
Building the HTML
I would create templates objects for each types you would take in charge (in this example : Text, Select)
Then using said templates you could replace the tokens with the HTML of your templates.
Displaying the HTML
Last step would be to parse the result string and append it at the right place:
var ResultForm = $.parseHTML(txt);
$("#DisplayDiv").append(ResultForm);
Conclusion
Like you asked, I did not prepare anything that works right out of the box, I hope it will help you prepare your own answer. (And then I hope you will share it with the community)
This is just a framework to get you going, like you asked.
first concept is using a regex to just find all matches of ${ }. it returns an array like ["${one}","${t w 0 }","${ three}"].
second concept is a htmlGenerator json object mapping "inputTypes-->childname" to a function responsible for the html print out.
third is not to forget about natural javascript. .localname will give you the xml element's name, and node.attributes should give you a namedNodeMap back (remember not to perform natural javascript against the jquery object, make sure you're referencing the node element jQuery found for you).
the actual flow is simple.
find all the '${}'tokens and store the result in an array.
find all the tokens in the xml document and using their parents info, store the html in an map of {"${one}":"<input type='text' .../>","${two}":"<select><option value='hello'>world!</option></select>" ...}
iterate through the map and replace every token in the source text with the html you want.
javascript
var $xmlDoc = $(xml); //store the xml document
var tokenSource =$xmlDoc.find("message text").text();
var tokenizer=/${[^}]+/g; //used to find replacement locations
var htmlGenerators = {
"textBox":function(name,$elementParent){
//default javascript .attributes returns a namedNodeMap, I think jquery can handle it, otherwise parse the .attributes return into an array or json obj first.
var parentAttributes = ($elementParent[0] && $elementParent.attributes)?$elementParent.attributes:null;
//this may be not enough null check work, but you get the idea
var specificAttributes =$elementParent.find(name)[0].attributes;
var combinedAttributes = {};
if(parentAttributes && specificAttributes){
//extend or overwrite the contents of the first obj with contents from 2nd, then 3rd, ... then nth [$.extend()](http://api.jquery.com/jQuery.extend/)
$.extend(combinedAttributes,parentAttributes,specificAttributes);
}
return $("<input>",combinedAttributes);
},
"date":function(name,$elementParent){
//whatever you want to do for a 'date' text input
},
"select":function(name,$elementParent){
//put in a default select box implementation, obviously you'll need to copy options attributes too in addition to their value / visible value.
}
};
var html={};
var tokens = tokenSource.match(tokenizer); //pull out each ${elementKey}
for(index in tokens){
var elementKey = tokens[index].replace("${","").replace("}"),"");//chomp${,}
var $elementParent = $xmlDoc.find(elementKey).parent();//we need parent attributes. javascript .localname should have the element name of your xml node, in this case "textBox","date" or "select". might need a [0].localname....
var elementFunction = ($elementParent.localname)?htmlGenerators[elementParent.localname]:null; //lookup the html generator function
if(elementFunction != null){ //make sure we found one
html[tokens[index]] = elementFunction(elementKey,elementParent);//store the result
}
}
for(index in html){
//for every html result, replace it's token
tokenSource = tokenSource.replace(index,html[index]);
}