You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Use margin when spacing between font related elements (<h1>, <h2>, <h3>, <p>): margin-top & margin-bottom (margin-bottom = margin-top = font-size by default)
margin: 0 auto;: horizontally center a block element
Disable all top margins to avoid margin collapsing: h1, h2, h3, p { margin-top: 0; }
Disable the bottom margin of the last element to make box padding easier.
Always use padding when dealing with parent & child spacing.
Inline Elements
You can only nest other different inline elements within a inline elements.
Margins only works for inline elements on horizontal dimensions, not vertical ones.
Paddings works for inline elements on all dimensions, but the surrounding elements will ignore them.
Width & height doesn't work on inline elements.
Spans
no semantic meaning, used only for styling purposes: colors, font weight, etc.
Styling Links
a:link: default link state
a:visited
a:focus: focused with tab
a:hover
a:active: clicked or held
Best practise:
The declaration order matters: lvfha.
Always change color both hover and focus.
A link should be very obvious.
Leave text-decoration on.
Inline-block Elements vs Inline Elements
inline: doesn't take up space vertically, will probably cover other content up.
inline-block: stay in side by side but also takes up space like block elements and won't cover other content.
Styling Buttons
You can use links as buttons. Just put a "class="button"" directly on the link.
Always directly put 'class="button"' on <a> rather than wrapping it up with an extra <div>. (Because, with a wrapper <div>, a link won't always fill up the space within the wrapper, and the user won't be able to click the space outside of the actually link.)
Always use padding to create spaces within buttons and containers rather than width / height. (Padding is more flexible with layout and content size changes.)
Always put more padding horizontally. Horizontal padding : vertical padding = 1.6 ~ 2
Always remove the text-decoration underline within a button.
Units
px: absolute unit. Used for print styles.
50%: usually used for width. Relative to the parent. (can act weird on heights but rarely used that way.)
percentages width usually works well with min/max-width: xxpx when you want to set a max or min width on a responsive element.
Block level elements are 100% wide (relative to its parent element) by default.
em: relative to an element's nearest parent that has a font size.
rem: relative to the root of the document, i.e. <html>
When to use different units?
A Rule of Thumb:
font-size = rem
padding & margin = em
width = em or percentage
Media Queries
Syntax: @media media-type and (media-features) {...}
media-type
screen
print
speech
media-condition (can be combined with the and keyword, which is more robust)
width: min-width / max-width (used most often)
orientation: landscape / portrait
specific features: hover
Order matters when using multiple media queries.
Navigation
Typical markup structure for a responsive navigation:
<header><!-- add a container-nav to apply flex box layout on --><divclass="container container-nav"><divclass="site-title"><h1>Site Title</h1><!-- use <p> for subtitles, not h2 (for titles of other sections) --><pclass="subtitle">This is a subtitle</p></div><nav><ul><!-- add a current-page class to identify the link of the current page --><li><ahref="/" class="current-page">Home</a></li><li><ahref="/about">About</a></li><li><ahref="/contact">Contact Us</a></li></ul></nav></div><!-- container --></header>
A web worker is a JS file running in a background thread independent from the web page thread.
The worker thread and the web page thread can send data to each other with the postMessage api. The data is copied when being sent. Even objects and json are copied using the structured clone algorithm.
Usage:
constworker=newWorker('/js/worker.js')// initializationworker.addEventListener('message',onMessage)// listen to message eventworker.postMessage({start: true})// send message to main threadworker.terminate()// shutdown
Service Worker
A service worker is a special type of web worker. It works like a proxy in browsers, and once it starts listening for outbound network requests, every single request that you send out will go through it and it will make requests and receive responses on behalf of the page. There is no way to tell a service worker to leave some requests out. So you have to make sure they get routed correctly.
One thing good to know is that your service worker is still bound by the rules of CORS(cross-origin resources sharing), which means the <img> tag loading some image off some CDN somewhere, which works completely normally on a normal page, won't work with a service worker setup unless the CDN publishes CORS headers.
But why?
Some use cases of service workers:
Caching and background sync for offline (main use case)
Rewriting a CDN that's down to another location and requesting from there without having to update the HTML.
Programmatically creating artificial response data or using the cached response for offline requests.
Programmatically preloading extra resources for a request for certain ones.
Pushing + notifications (with permissions)
Usage:
To initialize a service worker:
asyncfunctioninitServiceWorker(){// URL rewrite might be needed for sw scoping// register method is asyncswRegistration=awaitnavigator.serviceWorker.register('/sw.js',{updateViaCache: 'none',})// default sw lifecycle: installed => waiting (optional) => active// only one sw can be active at the time// a sw is active until the user navigates to a new page or refreshesserviceWorker=swRegistration.installing||swRegistration.waiting||swRegistration.active// when a new worker takes control of the pagenavigator.serviceWorker.addEventListener('controllerchange',functiononControllerChange(){serviceWorker=navigator.serviceWorker.controller},)}
Note: To manually reinstall a service worker, first stop the worker if it's still running, unregister it, and fire a navigate event to load a new one.
To make a service worker listen to its lifecycle events:
// won't run on restartself.addEventListener('install',onInstall)self.addEventListener('activate',onActivate)// will run on restartmain().catch(console.error)asyncfunctionmain(){console.log(`Service Worker (${version}) is starting...`)}asyncfunctiononInstall(){console.log(`Service Worker (${version}) installed.`)// skip the waiting phaseself.skipWaiting()}asyncfunctiononActivate(event){// if shutdown is necessary, don't do it until activation is handledevent.waitUntil(handleActivation())}asyncfunctionhandleActivation(){// to fire the `controllerchange` eventawaitclients.claim()console.log(`Service Worker (${version}) activated.`)}
And you can send messages to your service worker as it's basically a web worker. You can send the message to the message channel port it's listening on, or let the service worker controlling the current page send the message. Here is an example of sending messages from the page to the service workers based on the the page status changes.
varisOnline='onLine'innavigator ? navigator.onLine : truevarisLoggedIn=/isLoggedIn=1/.test(document.cookie.toString()||'')functionsendStatusUpdate(target){sendSWMessage({statusUpdate: {isOnline, isLoggedIn}},target)}functiononSWMessage(event){var{data}=event// let the sw ask the page for request status changeif(data.statusUpdate){console.log(`Received status update request from service worker, responding...`,)// send to the message channel port that the service worker is listening onsendStatusUpdate(event.ports&&event.ports[0])}}functionsendSWMessage(message,target){if(target){target.postMessage(message)}elseif(serviceWorker){serviceWorker.postMessage(message)}else{navigator.serviceWorker.controller.postMessage(message)}}functionready(){// ...window.addEventListener('online',functiononline(){offlineIcon.classList.add('hidden')isOnline=truesendStatusUpdate()})window.addEventListener('offline',functionoffline(){offlineIcon.classList.remove('hidden')isOnline=falsesendStatusUpdate()})asyncfunctioninitServiceWorker(){// ...serviceWorker=swRegistration.installing||swRegistration.waiting||swRegistration.activesendStatusUpdate(serviceWorker)navigator.serviceWorker.addEventListener('controllerchange',functiononControllerChange(){serviceWorker=navigator.serviceWorker.controllersendStatusUpdate(serviceWorker)},)}}
For the service workers side, they need to listen to the message event fired by all clients, listen on their message channel port for the client message and send messages through the channel to the clients on the other port of the channel, which looks like this:
That's basically how the clients and service workers talk to each other.
Next up, you can cache resources with service worker.
We are going to do it in a brutal-force way. First, get an id for each version of cached resources and list out the urls of all resources that need to be cached.
varcacheName=`ramblings-${version}`varurlsToCache={// resources urls that need to be cachedloggedOut: [// pages urls get rewrited by the server'/','/about','/contact','/login','/404','/offline',// other static files'/js/blog.js','/js/home.js','/js/login.js','/js/add-post.js','/css/style.css','/images/logo.gif','/images/offline.png',],}
And then define the function that put those resources into cache.
asyncfunctioncacheLoggedOutFiles(forceReload=false){varcache=awaitcaches.open(cacheName)returnPromise.all(urlsToCache.loggedOut.map(functionrequestFile(url){try{letresif(!forceReload){res=awaitcache.match(url)if(res){returnres}}letfetchOptions={method: 'GET',cache: 'no-cache',// cache must be validated by servercredentials: 'omit',}res=awaitfetch(url,fetchOptions)if(res.ok){awaitcache.put(url,res.clone())}}catch(error){}}),)}
Then call the caching function when necessary:
asyncfunctionmain(){awaitsendMessage({requestStatusUpdate: true})// no force reload on startup: cache everything missing and ignore everything already in the cacheawaitcacheLoggedOutFiles()}// ...asyncfunctionhandleActivation(){// recache everything on activationawaitcacheLoggedOutFiles({forceReload: true})// fire `controllerchange` eventawaitclients.claim()console.log(`Service Worker (${version}) activated.`)}
This way the service worker will cache everything you need when it's time.
But it would be better if we clear any old caches before cache new resources. The activation time suits well for this operation as we can be sure that the old service worker has been deleted and the new one has been installed and ready to roll. If we do this during the installation, the old worker might be still lingering around and could mess things up.
Now that we are done with caching, we can move onto the part where we use service worker to actually intercept network requests for offline use cases. To do that, we are basically going to build a router. First we need to listen to the fetch event, and the callback should return the response back.
For the router, we have to deal with requests to our own server. We can check the origin of the request url, try fetching the response and cache it. If the fetching fails, we will need to look into our caches and populate the response with the cache.
Things to think about when using TypeScript with react
@types/react
Commonly used:
ReactNode - ReactElement, ReactFragment, ReactPortal & all the primitive data types
Component - all class-based components
PureComponent - class-based optimized components
FC, FunctionComponent - complete interface for function components, often used to type external function components instead of typing my own
Good to know:
ReactChild - JSX elements, strings & numbers. Can be used with the children prop but covers smaller ground than ReactNode
ReactElement - JSX elements, which is either a intrinsic element, i.e. <button />, or a value-based element, i.e. <MyComponent />. Can used with cloneElement but rarely.
ComponentType - used for higher order components where you don't specifically deal with the intrinsic components