Skip to content

HTML

Created: 2015-03-16 20:39:35 -0700 Modified: 2019-09-09 10:11:50 -0700

  • securityheaders.io analyzes the score of any site. It includes these descriptions of headers:
    • HTTP Strict Transport Security is an excellent feature to support on your site and strengthens your implementation of TLS by getting the User Agent to enforce the use of HTTPS. Recommended value “Strict-Transport-Security: max-age=31536000; includeSubDomains”.
    • Content Security Policy is an effective measure to protect your site from XSS attacks. By whitelisting sources of approved content, you can prevent the browser from loading malicious assets.
      • If you ever have any problems with Content-Security-Policy, rather than changing that, just change Content-Security-Policy-Report-Only instead.
      • Here’s a sample policy (that I won’t keep up-to-date) that I set up for Bot Land via Lambda@Edge. It employs multiple headers so that I can use “unsafe-eval” just for *.bot.land, which is needed thanks to Lodash templates and also Test Mode in the game (for executing user code).
headers["content-security-policy"] = [
{
key: "Content-Security-Policy",
value:
"default-src 'self' *.bot.land; manifest-src 'self'; img-src 'self' blob: data: https://www.google-analytics.com https://static-cdn.jtvnw.net; script-src 'unsafe-inline' www.google-analytics.com https://ssl.google-analytics.com 'unsafe-eval' *.bot.land; style-src 'self' 'unsafe-inline' fonts.googleapis.com; font-src 'self' fonts.googleapis.com fonts.gstatic.com https://themes.googleusercontent.com https://at.alicdn.com/; object-src 'none'; frame-src https://*.youtube.com"
}
];
  • X-Frame-Options tells the browser whether you want to allow your site to be framed or not. By preventing a browser from framing your site you can defend against attacks like clickjacking. Recommended value “X-Frame-Options: SAMEORIGIN”.
  • X-Content-Type-Options stops a browser from trying to MIME-sniff the content type and forces it to stick with the declared content-type. The only valid value for this header is “X-Content-Type-Options: nosniff”.
  • Referrer Policy is a new header that allows a site to control how much information the browser includes with navigations away from a document and should be set by all sites.
  • Feature Policy is a new header that allows a site to control which features and APIs can be used in the browser.

You can use this technique (“first, last, invert, play”) to prevent doing expensive calculations by precalculating the animation ahead of time and letting it play out cheaply.

There’s a React library for this: https://github.com/aholachek/react-flip-toolkit

“.closest” is all you have to do. For example, here’s how I checked to see if an element is “tagged” with the “no-drag-allowed” class:

if (domElement.closest('.no-drag-allowed') != null) {
return; // prevent drag
}

I ran into an issue where I wanted this functionality:

  1. On mouse-enabled devices, tooltips would show on hover
  2. On touch-enabled devices, tooltips would show on long-press

I added “onMouseEnter” on a particular div, but I found that it was getting hit even on a real mobile device with no mouse. I needed some way to differentiate between a touch event and a mouse event. The solution I came up with was probably not optimal, but I figured it’s worth describing anyway.

The order of touch and mouse events is as follows (taken from this reference):

  1. touchstart
  2. touchmove
  3. touchend
  4. mouseover
  5. mousemove
  6. mousedown
  7. mouseup
  8. click

That means that if I set a variable like “waitingForTouchEnd” to true in “touchstart” and then set it to false in “mouseup” that I’ll know during a “mousemove” event whether the trigger of that action was a touch event.

Note that you can’t set waitingForTouchEnd to false in the actual “touchend” event because the entire “touch-” block of events happens before the entire “mouse-” block of events

I did a bunch of research on base64 vs. blobs on 2/20/2018 for the sake of Bot Land. There are two reasons I wanted to look into this:

  1. Divs in the DOM that uses base64 data take a ridiculous amount of space to view in DevTools because these nodes can’t be collapsed further. This makes it annoying to see parents, siblings, etc. in the Elements pane.
  2. Performance - I was told that blobs would be more performant.

I have inconclusive results on the performance front. There’s a jsperf page here, but waynee brings up some good points about how this could be misleading:

1:12 waynee955: This is a really good and interesting talk about performance and benchmarking in JS, I can only recommend it https://www.youtube.com/watch?v=65-RbBwZQdU

1:20 waynee955: He talked about how V8 compiler analyze the code. Because of the optimizations that the compiler does its really hard to write meaningful benchmarks. Then he showed a benchmark on jsperf that benchmarked calling a function normally vs apply vs bind. But in the end that benchmark resulted in en empty loop because the compiler actually completely removed the function at all because it was not doing anything. So the benchmark results did not represent anything. @Adam13531

I didn’t fully convert my own code since that would take a long time, so I didn’t get conclusive data for Bot Land.

My test code using Pixi boiled down to this:

function pixiDisplayObjectToBlob(pixiDisplayObject) {
const canvas = pixiRenderer.extract.canvas(pixiDisplayObject);
return new Promise((resolve, reject) => {
canvas.toBlob((blob) => {
resolve(blob);
});
});
}

Then, from React, I did something like this:

class BlobTest extends Component {
constructor(props) {
super(props);
this.state = {
isLoaded: false,
blob: null,
};
}
loadBlob() {
return pixiDisplayObjectToBlob(someDisplayObject)
.then((blob) => {
this.setState({
blob,
isLoaded: true,
});
});
}
render() {
let url = _.isNil(this.state.blob) ? '' : URL.createObjectURL(this.state.blob);
return (
<img src={url}/>
);
}
}

(note: the above code is sort of incomplete; loadBlob needs to be called from somewhere, and someDisplayObject needs to exist)

Other things to note:

  • toBlob isn’t available on every browser. Read more here toward the bottom.
    • There’s a polyfill, but it’s apparently not performant
    • Falling back to base64 would probably be easy enough
  • For my specific scenario in Bot Land, I’d have to change tons of code to get this working since I rely on base64 so much. It wouldn’t be difficult to change that code, but it may take a while. I’d need to figure out how caching works for blobs. There was some conversation on this in my chat on 2/20/2018:

12:30 freaktechnik: blobs have normal gc, blob URIs are the complicated thing

12:30 freaktechnik: as in the blob:// URLs

12:30 freaktechnik: and blob URIs are refered to as object urls

12:30 freaktechnik: (createObject URL, deleteObjectURL iirc)

12:31 freaktechnik: (I think it the URI adds a reference to the blob, which is atypical for js or something, not entirely sure how it works, but probably not just normal gc magic, else deleting the URI wouldn’t be a thing)

12:32 HiDeoo: Ho it’s not, I thought they renamed revoke ^^

12:32 HiDeoo: Got me scared for a while xD

12:32 freaktechnik: HiDeoo you’re right, it’s revoke

12:33 freaktechnik: and it clears the reference handle the URL created, like I assumed

12:40 freaktechnik: jsperf thing for data vs. blob URIs from canvases https://jsperf.com/loading-images-blobs-vs-dataurls/3

12:40 freaktechnik: (though pixi overhead may still kill it, depending on what pixi does there)

12:41 freaktechnik: oh, in chromium data URIs are faster

12:41 freaktechnik: welp…

12:42 freaktechnik: ah, wait, that’s not going via canvas

12:43 HiDeoo: Yeah and if the issue for Adam13531 is the DOM output, the bench won’t really say anything about that

12:43 freaktechnik: no it is?

12:43 freaktechnik: I’m confused

12:43 HiDeoo: Looks like it is from what he said

12:44 freaktechnik: hm, it seems that the perf is all over the place for data vs. blob URIs

12:49 freaktechnik: (they are also measuring revoking the blob URI in the perf, though)

12:51 freaktechnik: it’s funny how perf is much more extreme on phones compared to desktops

I haven’t actually used this feature yet, but freaktechnik told me about it. It seems to be for using different image sources based on a media query.

9:35 freaktechnik: I’ve only used https://ericportis.com/posts/2014/srcset-sizes/ and https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-srcset so far

I used to use “.which” on key events like this:

if (e.which === KEYCODE.RETURN) { foo(); }

Then I would manifest a file full of the keycodes, and those would look like this:

export const RETURN = 13;
export const A = 'A'.charCodeAt(0); // similar for other letters
export const ONE = '1'.charCodeAt(0); // similar for other numbers

That’s super tedious, but it’s also deprecated. You can now use either “e.key” or “e.code”, both of which are supported on modern browsers.

e.key (reference): it’s a string, you don’t really need constants manifested somewhere since they’re already human-readable and will never change, and you can differentiate between locations (e.g. “main keyboard” vs. numpad). Example:

if (e.key === 'Enter') { foo(); }

e.code: it’s a string representing the physical key that you pressed, so if I change keyboard layouts and press ’/’, it’ll still be “Slash” instead of “é” on a [Canadian] French keyboard.

Note that “e.key” will be uppercase if you’re holding shift, so if you want to use “e.key” to see if “W” is being held regardless of the state of shift, then you need to check for both “w” and “W” (either with toLowerCase() or just two comparisons). Otherwise, you could just use “e.code” and check against “KeyW” regardless of whether shift is held.

This site generates all of the different favicons that you need and also tells you why you need them: https://realfavicongenerator.net/faq

Make sure the manifest.json (reference) is named correctly, since that’s what shows when someone tries pinning your site. It also has a bunch of other properties like:

“short_name”: “Bot Land”,

“start_url”: ”/?utm_source=homescreen”,

“background_color”: “#373737”,

“display”: “standalone”,

“orientation”: “landscape”,

http://quirktools.com/screenfly/

If you want to give the user a chance to save their progress before leaving a page, you can use the “beforeunload” event:

window.addEventListener('beforeunload', (e) => {
const dialogText = 'Dialog text here';
e.returnValue = dialogText;
return dialogText;
});

If you want placeholder images of particular sizes, here are some helpful services:

Without setting a ‘rel’ attribute, the page that you open could control its parent page (e.g. with a redirect). To fix it, just set that attribute as shown below:

<a href=“blah” target=“_blank” rel=“noopener noreferrer”>Click me</a>

You could also use this to safely open from JS:

function safeOpen(url) {

const w = window.open(undefined, ‘_blank’);

w.opener = null;

w.location = url;

}

Also, there’s a possible performance implication if you don’t set the ‘rel’ tag (reference).

================================================================================

5/23/2015

Jade: CSS style tags (just put a period at the end of the “style” line).

doctype html

html

head

title= title

script(type=‘text/javascript’, src=‘client/misc/loginpage.js’)

style(type=‘text/css’).

li {

padding-top: 9px;

padding-bottom: 3px;

color: blue;

}

body(onload=‘slotmachines.client.login.main.start()‘)

h1= Login

================================================================================

4/19/2015

Jade: hrefs inside a list

Note: there are spaces after the pipes; they’re very important!

ul

li.big-list-items: a(href=“/login”) Login

li.big-list-items

a(href=“/game”) Game

|

a(href=“/game?user=1”) (user1)

|

a(href=“/game?user=2”) (user2)

|

a(href=“/game?user=3”) (user3)

|

a(href=“/game?user=4”) (user4)

|

a(href=“/game?user=5”) (user5)

li.big-list-items: a(href=“/validate”) Validator

li.big-list-items: a(href=“/colorpicker”) Color-picker

================================================================================

4/12/2015

Suppose you have this:

HTML:

<div>

<button>Click me</button>

</div>

JS:

$(‘div’).click(function(){console.log(‘clicked div’);});

$(‘button’).click(function(event){console.log(‘clicked button’);});

Clicking the button will actually output “clicked div” AND “clicked button”, so use event.stopPropagation() to prevent it from going to the parent.

Note: “return false;” at the end of a handler is the same as doing what’s below (reference):

event.stopPropagation()

event.preventDefault()

Quirk: could NOT figure out how to make this work with checkboxes (<input> tags). There are apparently some StackOverflow threads on this:

My solution in the end was to do something really hacky:

// Put this in the parent element that contains a checkbox. By adding this, clicking

// the checkbox will still cause this code to be hit, but the ‘if’ check will force an early return.

var eventTarget=eventTarget = (event.target);

if (eventTarget.hasClass(uibuttontext)eventTarget.hasClass('ui-button-text') || eventTarget.attr(‘type’) === ‘checkbox’) {

return;

}

================================================================================

3/16/2015

This is a great resource on float (reference).

You should also read on clearing floats though (reference).

Related:

When making two divs next to each other, you should really just use inline-block on both (reference) instead of trying to float things weirdly. UPDATE/NOTE: inline-block elements should technically be converted into spans (reference).

HTML:

<div class=“left-div”>

<textarea type=‘text’>This is a text area</textarea>

</div>

<div class=“right-div”>

<textarea type=‘text’>This is a text area</textarea>

</div>

CSS:

.left-div {

display: inline-block;

width: 200px;

}

textarea {

width: 100%;

}

.right-div {

display: inline-block;

width: 200px;

}

Output:

To make something go beneath it (e.g. a set of buttons), just put the buttons in their own div as a sibling to the two existing divs.

If the contents of the divs get pushed around weirdly, then add vertical-align:top to them (reference).

Also, you need to make sure you have specific widths assigned to each.

================================================================================

Pressing enter doesn’t submit a form

Section titled Pressing enter doesn’t submit a form

Apparently, if you only have a single input in a form, then pressing enter should work to call the form’s “onsubmit” function. However, if you have more inputs in the form (e.g. a “change password” dialog), then you must also have a button in the form or else you can’t press enter to submit. To fix this, just add a hidden button with type=“submit”.

It’s possible that you’re trying to add it to an element whose display is “none” by default, in which case it seems to get screwed up.

On Android, I still had an issue, and I just randomly tried this and it worked:

const twitterTextEle = document.querySelector(‘#twitterText’);

twitterTextEle.innerHTML += ‘<span></span>‘;

The HTML was this

<div id=“twitterText”>

<a

class=“twitter-share-button”

data-show-count=“false”

href=‘https://twitter.com/intent/tweet?text=your text here’

>

</a>

</div>

I also had to add a min-width and min-height

Something is pixelated that shouldn’t be

Section titled Something is pixelated that shouldn’t be

E.g.

instead of…

This can be a problem with non-integer coordinates or having a “transform: translate” on an element. The solution/workaround depends on which problem you have and what you’re trying to do, so I’ll just give one simple example:

There was a dialog that I wanted to center on the page, so I did “top: 50%; left: 50%; transform: translate(-50%, -50%);“. This resulted in the pixelated checkbox that you see above, so we had to change it so that the parent had “display: flex; justify-content: center; align-items: center;“.