JSFiddle - React, Tailwind, and code Playground
by gweax
HTML
<div class="message">
<img class="avatar" src="https://randomuser.me/api/portraits/lego/2.jpg">
<p class="content">
Datis nepis potus colonia.
<time class="publish-date" datetime="2017-10-16T14:01:28.817Z">Just now</time>
</p>
</div>
CSS
.message {
display: flex;
margin: 1em;
align-items: flex-end;
}
.avatar {
width: 80px;
height: 80px;
}
.content {
background: deepskyblue;
border-radius: 1em;
padding: 1em;
position: relative;
margin: 0 0 0 2em;
}
.content::before {
content: '';
position: absolute;
bottom: 1em;
left: -2em;
border-style: solid;
border-width: 0 2em 1em 0;
border-color: transparent deepskyblue;
}
.publish-date {
font-style: italic;
font-size: 0.8em;
display: block;
}
.message:nth-of-type(odd) {
flex-direction: row-reverse;
}
.message:nth-of-type(odd) > .content {
background-color: peru;
color: white;
margin: 0 2em 0 0;
}
.message:nth-of-type(odd) > .content::before {
border-width: 0 0 1em 2em;
border-color: transparent peru;
left: auto;
right: -2em;
}
JavaScript
/*
* Instead of displaying one message, I made an array of several messages.
* So we can test more than one case at once.
*/
var messages = [{
"avatarUrl": "https://randomuser.me/api/portraits/lego/6.jpg",
"message": "Situs vi late in iset aevernit.",
"published": "2017-11-03T13:42:00.605Z"
}, {
"avatarUrl": "https://randomuser.me/api/portraits/lego/3.jpg",
"message": "Never trust the user <script>alert('hacked')<\/script>",
"published": "2017-11-03T14:55:55.123Z"
}, {
"avatarUrl": "https://randomuser.me/api/portraits/lego/4.jpg\" onload=\"alert('hacked')",
"message": "Never trust the user",
"published": "2017-11-03T14:51:31.008Z"
}];
/*
* I iterate over all messages. The inner function handles one
* message at a time.
*/
messages.forEach(function(data) {
var msg = document.createElement('div');
msg.className = 'message';
// I use the innerHTML property to build the element.
// This shows the danger of innerHTML: The data can contain
// HTML itself, which may be malicious.
//
// The embedded script element in the second message looks
// like a danger. But script elements set with innerHTML
// aren't executed. So we're safe.
//
// The avatarUrl in the third message however is malicious.
// It contains the string
// " onload="alert('hacked')
// When included in the HTML of the img element, the quotation
// mark ends the src attribute. Then an onload attribute is
// added. When the image loads, the JavaScript is executed.
//
// I should have escaped every value that may come from user
// input. You can find a function to do so at the bottom.
msg.innerHTML =
'<img class="avatar" src="' + data.avatarUrl + '">' +
'<p class="content">' +
data.message +
'<time class="publish-date" datetime="' + data.published + '">Just now</time>' +
'</p>';
document.body.appendChild(msg);
});
/**
* Escape characters with a special meaning in HTML, so that
* the string is not interpreted as HTML
...