Render XML data with ReactLayout - javascript

The XML data in PhoneCallResponse does not appear in the generated HMTL.
Probably because React uses XML for its components.
Is there a special way to handle this?
PhoneCallResponse = React.createClass({
render() {
return (
<Response>
<Say voice="alice">
Thanks for the call. Configure your number's voice U R L to change this message.
</Say>
<Pause length="1"/>
<Say voice="alice">
Let us know if we can help you in any way during your development.
</Say>
</Response>
);
}
});
FlowRouter.route('/twilio/', {
action: function() {
ReactLayout.render(Layout, {
content: <PhoneCallResponse />
});
}
});

React reasons with JSX (it's not XML!) this way: Capital letter at the start of the tag indicates a React component. Lowercase tags are rendered straight out as DOM elements without further computation. At the end of the day, React renders HTML views, not XML.
Your code will cause a reference error, as neither Response, Say or Pause are defined components. The reason is the way JSX is transpiled before React ever does anything: JSX tags with a capital letter are treated as components. So <Response></Response> will be converted to React.createElement(Response, {}, null), and if Response isn't a valid React component within the scope, the javascript engine will complain, and everything crashes. Lowercase tags however are treated as strings, and React will simply render an HTML tag <response></response>
If you insist on mixing XML in with your HTML, or your goal is to render an XML document, then use lowercase tags or dangerouslySetInnerHTML which is the React way of injecting strings in the DOM. Keep in mind however that HTML5 is not XML in the way that XHTML was, and XHTML is more or less dead. Standard compliant custom tags these days follow the pattern <x-custom-tag>, <x-another-custom-tag> and so forth, just to make sure you avoid any name collision that might be caused by new tags in a future HTML standard.
On a side note: React plays well with HTML5 custom elements and shadow DOM. Sadly, this part of the HTML standard is very "work in progress", and apparently miles away from being ready to use, but as of Chrome version 49 this example should demonstrate the general idea: https://jsfiddle.net/dannyjolie/je8pmazk/.

Related

Strict HTML parser in JavaScript

In HTML, block elements can't be children of inline elements. Browsers however are happy to accept this HTML:
<i>foo <h4>bar</h4> fizz</i>
and render it intuitively as expected; neither do they choke on it using DOMparser.
But it's not valid and is therefore hard to convert to another schema. Pandoc parses the above as (option1):
<i>foo </i><h4>bar</h4> fizz
which is at least valid but not faithful. Another approach would be (option2):
<i>foo </i><h4><i>bar</i></h4><i> fizz</i>
Is there a way to force DOMparser to do a more strict parsing that would result in option 1 or 2? (It doesn't seem possible).
Alternatively, what would be the best approach to deal with this, that is, given the first string, get option 1 or 2 as a result? Is there a JS parser that does this (and other strict enforcing of the standard)?
Edit: it turns out the HTML parser of at least Chrome (78.0.3904.108) behaves differently when the content is in a p instead of, say, a div. When the HTML above is in a p then it gets parsed as option 2! But it's left as is when inside a div.
So I guess the question is now: how to enforce the behavior of ps onto divs?

React: Rendering non-existing HTML Element

While reading the React docs, I realized that in JSX, elements starting with a lowercase letter are treated as HTML element. While putting this into practice, I noticed something interesting: When I pass a non-existing, lowercase element, React renders this just fine.
function Foo() {
return (<foo>hello world</foo>);
}
ReactDOM.render(
<Foo />,
document.getElementById('container')
);
This renders like this:
<div id="container">
<foo data-reactroot="">hello world</foo>
</div>
While trying to find an answer on how this is possible, I encountered several projects dealing with Web Components/Custom Elements in React, which confused me a bit.
Does this have something to do with Custom Elements? Will this work in any browser that is supported by React?
No, this has nothing to do with Custom Elements.
It's perfectly fine to create an element which has a tag name that doesn't represent any predefined HTML element. You will get an instance of HTMLUnknownElement:
const myElement = document.createElement('foo');
console.log(myElement.nodeName);
console.log(myElement instanceof HTMLUnknownElement);
Unknown elements don't have special behavior. If you want create a <foo> element which has special behavior, you have to use Custom Elements.
As PeterMader explains, you are creating a custom html element with that syntax. I'll expand on that answer a bit.
First of all it's important to understand that JSX is just a syntactic sugar and that it ultimately transpiles into JavaScript. React elements are created with createElement.
React.createElement(type, props, children);
What's important to note here is that type can be one of the following:
A reference to a React Component (e.g Foo in your example).
A string.
If you are writing React without JSX, then it doesn't matter if you reference a component as foo or as Foo because you have complete control of how you define your variables. In other words, this is plain JavaScript and JSX rules don't apply.
However, if you are using React with JSX, the following rules apply:
type starts with a capital letter = React Component.
type starts with a lowercase letter = HTML element.
In fact, the official documentation says:
When an element type starts with a lowercase letter, it refers to a built-in component like <div> or <span> and results in a string 'div' or 'span' passed to React.createElement. Types that start with a capital letter like <Foo /> compile to React.createElement(Foo) and correspond to a component defined or imported in your JavaScript file.
We recommend naming components with a capital letter. If you do have a component that starts with a lowercase letter, assign it to a capitalized variable before using it in JSX.
So what about <foo />?
In the event that the type of a React Element doesn't resolve into a React Component and is not a standard HTML, React will still treat it as an HTML element.
To my knowledge, React actually doesn't have any whitelist of "valid" HTML tags, but treats anything starting with lowercase the same way. The result in your browser may differ though, as per PeterMader's response.
As such, this (JSX):
<foo>hello world</foo>
will, in modern browsers, yield (HTML):
<foo>hello world</foo>
which is perfectly fine technically, but probably still not a good idea.

React.js: Set innerHTML vs dangerouslySetInnerHTML

Is there any "behind the scenes" difference from setting an element's innerHTML vs setting the dangerouslySetInnerHTML property on an element? Assume I'm properly sanitizing things for the sake of simplicity.
Example:
var test = React.createClass({
render: function(){
return (
<div contentEditable='true' dangerouslySetInnerHTML={{ __html: "Hello" }}></div>
);
}
});
vs
var test = React.createClass({
componentDidUpdate: function(prevProp, prevState){
this.refs.test.innerHTML = "Hello";
},
render: function(){
return (
<div contentEditable='true' ref='test'></div>
);
}
});
I'm doing something a bit more complicated than the above example, but the overall idea is the same
Yes there is a difference!
The immediate effect of using innerHTML versus dangerouslySetInnerHTML is identical -- the DOM node will update with the injected HTML.
However, behind the scenes when you use dangerouslySetInnerHTML it lets React know that the HTML inside of that component is not something it cares about.
Because React uses a virtual DOM, when it goes to compare the diff against the actual DOM, it can straight up bypass checking the children of that node because it knows the HTML is coming from another source. So there's performance gains.
More importantly, if you simply use innerHTML, React has no way to know the DOM node has been modified. The next time the render function is called, React will overwrite the content that was manually injected with what it thinks the correct state of that DOM node should be.
Your solution to use componentDidUpdate to always ensure the content is in sync I believe would work but there might be a flash during each render.
You can bind to dom directly
<div dangerouslySetInnerHTML={{__html: '<p>First · Second</p>'}}></div>
According to Dangerously Set innerHTML,
Improper use of the innerHTML can open you up to a cross-site
scripting (XSS)
attack. Sanitizing user input for display is notoriously error-prone,
and failure to properly sanitize is one of the leading causes of web
vulnerabilities on the internet.
Our design philosophy is that it should be "easy" to make things safe,
and developers should explicitly state their intent when performing
“unsafe” operations. The prop name dangerouslySetInnerHTML is
intentionally chosen to be frightening, and the prop value (an object
instead of a string) can be used to indicate sanitized data.
After fully understanding the security ramifications and properly
sanitizing the data, create a new object containing only the key
__html and your sanitized data as the value. Here is an example
using the JSX syntax:
function createMarkup() {
return {
__html: 'First · Second' };
};
<div dangerouslySetInnerHTML={createMarkup()} />
Read more about it using below link:
documentation: React DOM Elements - dangerouslySetInnerHTML.
Based on (dangerouslySetInnerHTML).
It's a prop that does exactly what you want. However they name it to convey that it should be use with caution
Yes there is a difference b/w the two:
dangerouslySetInnerHTML: React diffing algorithm (https://reactjs.org/docs/reconciliation.html) is designed to ignore the HTML nodes modified under this attribute thereby slightly improving the performance.
If we use innerHTML, React has no way to know the DOM is modified. The next time the render  happens, React will overwrite the content that was manually injected with what it thinks the correct state of that DOM node should be.
That's where componentDidUpdate comes to rescue!

Dynamic Unicode Generation into the DOM

I have a function linking to a method for a JavaScript library I'm working on. Basically taking romanized Korean and converting it to specific Unicode sequences and re-inserting it into the DOM. The resulting strings that it generates are correct, but the re-insertion back into the DOM seems off...
For example: If I have the following in my DOM:
<ko>hangug-eo</ko>
The function is meant to convert it accordingly, replacing hangug-eo with 한국어 to show on the browser:
한국어 within the <ko> tags...
The function that does the string setting within the DOM is as follows:
function (){
var z=document.getElementsByTagName('ko');
for(x=z.length;x--;){
z[x].childNodes[0].data=kimchi.go(z[x].childNodes[0].data);
}
}
However, it seems that all this seems to be doing is just placing the &# Unicode entities straight into the DOM without it converting to their respective character equivalents... So all I'm seeing is 한국어
Can anyone please point out what I may be doing wrong?
kimchi.go() is the function that ultimately provides the Unicoded string...
You can always just set the text directly using textContent without having to use HTML entities:
z[x].textContent = '한국어';
But if you need to use HTML entities, just use innerHTML instead
z[x].innerHTML = kimchi.go(z[x].childNodes[0].data);
You can see the latter in the example below.
https://jsfiddle.net/nmL3to8w/1/

Replace content of a web page with stuff from AJAX response

The idea is to replace the contents of a web page with the same page requested through an ajax call, but only the HTML elements that are different.
Current I have this in my $.ajax success callback:
var replace = function(first, second){
$(first.html() !== second.html())
first.replaceWith(second);
};
replace($('#container'), response.find('#container'));
Which works, but because the content gets always replaced I get to see a "clipping" effect. The ajax request runs multiple times, almost every second, until a certain class is added to the container tag from the ajax response, so the clipping is very annoying.
Basically I want to only replace the elements that have different html, but somehow start from the last level, to prevent replacing of elements that have the same html code.
I made a fiddle example here: http://jsfiddle.net/2u4eB/
So in that markup only the contents of the <b> tag should be replaced, and not the entire div like it is now, because only <b> is different.
Does anyone have any pointers on how could I achieve this?
If you can make some assumptions, then it's not so hard:
Assumption 1: The markup is exactly the same every time
Assumption 2: The only thing which changes is the TEXT inside of certain html tags
You must then know the HTML tags. If you are a consistent person, all of your dynamic data should be wrapped in a similar tag - in your question you mentioned a <b> tag, so lets make a third assumption:
Assumption 3: All dynamic data is wrapped with a <b> tag
Then all you have to do is this:
var replace = function(first, second) {
var oldValues = first.find('b'),
newValues = second.find('b');
oldValues.each(function(i) {
if(oldValues[i].textContent != newValues[i].textContent) {
oldValues[i].textContent = newValues[i].textContent;
}
});
};
replace($('#container'), response.find('#container'));
NOTE: this works because jQuery's find() returns a list of nodes in document order - so Assumption #1 is very important.
I highly recommend using a framework that supports client side binding. (Examples include but are not limited to Knockout, Handlebars, Angular, Underscore) This will give you better results faster than writing low level DOM Manipulation.
Knockout.js and Underscore.js are my favorites, but there are many great options.

Categories

Resources