Guard clause

A Guard Clause (one of the SmalltalkBestPracticePatterns, and equally applicable in a whole bunch of languages) is a chunk of code at the top of a function (or block) that serves a similar purpose to a Precondition.

It typically does one (or any or all) of the following:

  • checks the passed-in parameters, and returns with an error if they’re not suitable.
  • checks the state of the object, and bails out if the function call is inappropriate.
  • checks for trivial cases, and gets rid of them quickly.

For example:

draw() {
  if (! isVisible()) return;
  ...
}

// without Guard Clause
function getPayAmount() {
  let result;
  if (isDead)
    result = deadAmount();
  else {
    if (isSeparated)
      result = separatedAmount();
    else {
      if (isRetired)
        result = retiredAmount();
      else
        result = normalPayAmount();
    }
  }
  return result;
}

// with Guard Clause
function getPayAmount() {
  if (isDead) return deadAmount();
  if (isSeparated) return separatedAmount();
  if (isRetired) return retiredAmount();
  return normalPayAmount();
}

Responsive Image

<picture>
	<source srcset="img-mobile.jpg" media="(max-width: 767px)">
	<source srcset="img-tablet.jpg" media="(min-width: 768px) and (max-width: 991px)">
	<source srcset="img-desktop.jpg" media="(min-width: 992px) and (max-width: 1200px)">
	<source srcset="img-widescreen.jpg" media="(min-width: 1201px)">

	<img src="img-desktop.jpg" />
</picture>

Links:

CSS Responsive Table

JSFiddle Demo.

HTML:

<table class="table-stacked">
  <thead>
    <tr>
      <th>#</th>
      <th>First Name</th>
      <th>Last Name</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td data-th="#">1</td>
      <td data-th="First Name">Brenda</td>
      <td data-th="Last Name">Bear</td>
    </tr>

    <tr>
      <td data-th="#">2</td>
      <td data-th="First Name">Frank</td>
      <td data-th="Last Name">Fox</td>
    </tr>

    <tr>
      <td data-th="#">3</td>
      <td data-th="First Name">William</td>
      <td data-th="Last Name">Wolf</td>
    </tr>
  </tbody>
</table>

CSS:

.table-stacked {
  width: 100%;
}

.table-stacked thead tr {
  border-bottom: 1px solid gray;
}

@media (max-width: 768px) {
  .table-stacked thead {
    display: none;
  }
  .table-stacked td {
    display: flex;
    border-color: gray;
  }
  .table-stacked td:before {
    content: attr(data-th);
    font-weight: bold;
    width: 30%;
    min-width: 80px;
    padding: 10px;
    padding-left: 0;
    margin: -10px 10px;
    margin-left: 0;
    border-right: 1px solid gray;
    border-width: 2px;
  }
  .table-stacked td:last-child {
    border-bottom: 1px solid gray;
    border-color: gray;
  }
}

Progressive enhancement

The above figure is often used to demonstrate a minimum viable product, but I think it can also be used to demonstrate a minimum viable experience. The skateboard may be a little slower, but it doesn’t stop the user getting to where they want to go. So, if the user’s browser doesn’t support JavaScript or modern CSS then it doesn’t break, it presents the default experience instead: a button which instructs the user to generate a report. The user will experience a very similar process, but has to perform one extra click.

The beauty of this approach is that the site doesn’t ever appear broken and the user won’t even be aware that they are getting the ‘default’ experience. With progressive enhancement, every user has their own experience of the site, rather than an experience that the designers and developers demand of them.

JavaScript fetch()

const fetchData = (url, userOptions = {}) => {
    const defaultOptions = {
        method: 'POST',
        credentials: 'same-origin',
        cache: 'no-cache',
        headers: {
            'Content-Type': 'application/json',
            'LoginToken': 'l0g1n-t0k3n'
        }
    };
    // const options = jQuery.extend(defaultOptions, userOptions);
    const options = Object.assign({}, defaultOptions, userOptions);
    return fetch(url, options);
};

// Usage
fetchData(url)
    .then(response => response.json())
    .then(response => {
        console.log(response);
    })
    .catch(error => console.error('Error: ', error));

fetchData(url, {method: 'GET'})
    .then((response) => {
        return response.text();
    })
    .then(response => {
        let $responseContent = $(response).find('.content').html();
        $('.content').html($responseContent);
    })
    .catch(error => console.error('Error: ', error));

All fetch() parameters:

return fetch(url, {
        method: "POST", // *GET, POST, PUT, DELETE, etc.
        mode: "cors", // no-cors, cors, *same-origin
        cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
        credentials: "same-origin", // include, same-origin, *omit
        headers: {
            "Content-Type": "application/json; charset=utf-8",
            // "Content-Type": "application/x-www-form-urlencoded",
        },
        redirect: "follow", // manual, *follow, error
        referrer: "no-referrer", // no-referrer, *client
        body: JSON.stringify(data), // body data type must match "Content-Type" header
    })

fetch() example – http://jsfiddle.net/62s1tyob/:

const number = document.getElementById('number');
const submit = document.getElementById('submit');
const content = document.getElementById('content');

submit.addEventListener('click', () => {
  const num = number.value;

  if (!!num) {
    fetch(`http://numbersapi.com/${num}`)
      .then(response => response.text())
      .then(text => {
        content.innerHTML = text;
      })
      .catch(error => {
        console.log(error);
      });
  }
});

html:

<input id="number" type="number" value="25" />
<button id="submit">Get Fun Fact!</button>
<p id="content"></p>

jQuery custom events

Trigger custom jQuery event:

$(document).trigger('user.add'); // trigger custom event

$(document).on('user.add', function () {
    // run code when custom event was triggered
});


Trigger custom jQuery event with passing custom parameter:

$(document).trigger('user.remove', [userId]); // trigger custom event

$(document).on('user.remove', function (event, userId) {
    console.log(userId);
});