home.

tagged: css

Javascript: Turning our animated loading button on and off

Now we have our animated loading button (see https://newfivefour.com/javascript-passing-dom-node-to-event-listener.html), we can turn it on and off.
 
Our body will be the same as before except we're using a dyn, with the identifier "loading", to give our composite button it's animated class:

body(
  div(
    dyn("change_it", ["one", "two", "three"], data =>
      ol(data.map(t =>
        li(text(t)))
      )
    ),
    div("display: flex; flex-direction: column; align-items: flex-start;",
      input(["placeholder", "Input: lon, man, liv, etc"]),
      div("position: relative", 
        dyn("loading", false, loading =>
          div("position: absolute;", ["class", loading ? "spinner" : ""]),
        ),
        button(text("Search")),
        ["click", (e, node) => fetchData(node.previousSibling.value)]
      ),
    )
  )
)

Next, in our fetchDate function we'll turn this on and off:

function fetchData(inputText) {
  dynSet("loading", true)
  sensibleFetch(`https://newfivefour.com:3000/name?name=${inputText}`)
  .then(json => {
    dynSet("loading", false)
    var list = json.map(place => place.name)
    dynSet("change_it", list)
  })
  .catch(e => {
    dynSet("loading", false)
    console.log("A bad request!", e)
  })
}

And now every time we make a network request the loading spinner will appear on the button and disappear when the fetch is over.
 
Here's a working example: https://jsfiddle.net/newfivefour/m0dta59v/11/
 
Next we can deal with cleaning up our code and looking at error messages.

javascript css


Javascript, CSS: Make an animated loading button that works with fetch

In our previous post we remotely got JSON from a server and updated our page with dyn based on user input. (https://newfivefour.com/javascript-dynamically-fetch-remote-data.html)
 
We're not showing the loading indication, however, but we can do that CSS3 animations. Here's our new HTML that we'll use instead of the old button:

div("position: relative", 
  div(["class", "spinner"]),
  button(text("Search"))
),

We now put the button in a relative div, and in that relative div we put a new absolute div that has the spinner class. Here's the CSS spinner class:

@keyframes spinner {
  to {transform: rotate(360deg);}
}

.spinner {
  box-sizing: border-box;
  top: 50%;
  left: 50%;
  width: 20px;
  height: 20px;
  margin-top: -10px;
  margin-left: -10px;
  border-radius: 50%;
  border: 2px solid #ccc;
  border-top-color: blue;
  animation: spinner .6s linear infinite;
}

It sets a animated transform that rotates the element 360 degrees. It applies that animation to the .spinner class infinately every 0.6 seconds.
 
The .spinner class has a border at its top, and it's 20px square. It's placed in the centre and it has a margin half it's width and height to centre it. It has a border-radius to make it round. It has a border-box sizing to stop the margin and padding enlarging the box.
 
We now have a spinning button. https://jsfiddle.net/newfivefour/17pj0wyt/7/ Next we will to turn it on and off and integrate it with fetch
 

javascript css


Javascript: A better JSON fetch with the dyn function that gets user input

Previously we used fetch to get remote JSON data to use with our dyn function. (https://newfivefour.com/javascript-use-fetch-with-our-dyn-function.html)
 
However, the JSON data did not change during user input. There was no user input! We can change that simply. Let's change our HTML so we have user input first:

div("display: flex; flex-direction: column; align-items: flex-start;",
  input(["placeholder", "search for something"]),
  button(text("search for UK constituency"), 
    ["click", ...]
  ),
)

The HTML is simple enough: We're making a column flex box and putting a input and button elements. I've left the click method empty. But we will grab the event object, use its target object and grab the data from input and send it to fetchData:

["click", e => fetchData(e.target.previousSibling.value)]

(The good thing about this is that with javascript genereated HTML you don't have to worry about a blank space changing the DOM.)
 
The fetch data method now takes an argument and we pass that to the fetch using a template literal:

function fetchData(inputText) {
  sensibleFetch(`https://newfivefour.com:3000/name?name=${inputText}`)
  .then(json => {
    var list = json.map(place => place.name)
    dynSet("change_it", list)
  })
  .catch(e => console.log("A bad request!", e))
}

Here's the JSFiddle that demonstrates that: https://jsfiddle.net/newfivefour/fzr123xn/
 
Note if you leave the input blank, you'll get a console.log, the one we defined in .catch(e => console.log("A bad request!", e)).

javascript css


Javascript: Add an error display to our system

Previously we added an animated loading button during our fetch. (https://newfivefour.com/javascript-making-custom-components.html)
 
Now we need to do the error display. We will have a closable fixed div on the screen. Here's the Javascript/HTML that does that:

div(`position: fixed; bottom: 0px; left: 0px; background-color: red;
     width: 100%; padding: 5px; box-sizing: border-box; 
     color: white; display: flex`, 
  text("some error"),
  span("flex-grow: 1", text("")),
  div("cursor: pointer", text("close"),
    ["click", (e, n) => n.parentNode.style.display="none"])
)

But we want this to be displayed or hidden based on a dyn call:

dyn("error", "", error => 
  div(`position: fixed; bottom: 0px; left: 0px; background-color: red;
       width: 100%; padding: 5px; box-sizing: border-box; 
       color: white; display: ${error.length>0 ? "flex" : "none"}`, 
    text(error),
    span("flex-grow: 1", text("")),
    div("cursor: pointer", text("close"),
      ["click", (e, n) => n.parentNode.style.display="none"])
  )
)

This is a large piece of dull code so let's put it into a function call:

function staticErrorDisplay(dynName) {
  return dyn(dynName, "", error => 
    div(`position: fixed; bottom: 0px; left: 0px; 
         background-color: red;
         width: 100%; padding: 5px; box-sizing: border-box; 
         color: white; display: ${error.length>0 ? "flex" : "none"}`, 
        text(error),
        span("flex-grow: 1", text("")),
        div("cursor: pointer", text("close"),
            ["click", (e, n) => n.parentNode.style.display="none"])
    )
  )
}

Previously we had some divs to make a column layout aligned on the left. That was also a bit tedius so let's put it in a function also:

function columnLeft() {
  return div(`display: flex; flex-direction: column; 
              align-items: flex-start;`, [...arguments])
}

Our new Javascript generated HTML will look like this:

body(
  div(
    staticErrorDisplay("error"),
    dyn("change_it", ["one", "two", "three"], data =>
      ol(data.map(t =>
        li(text(t)))
      )
    ),
    columnLeft(
      input(["placeholder", "Input: lon, man, liv, etc"]),
      loadingButton(
        button(text("Search!")), 
        (e, node) => fetchData(node.previousSibling.value),
        "loading"
      ),
    )
  )
)

Now it's time to turn on and off that error from our fetchData function, where we turn on and off this new dyn function.

function fetchData(inputText) {
  dynSet("loading", true)
  dynSet("error", "")
  sensibleFetch(`https://newfivefour.com:3000/name?name=${inputText}`)
  .then(json => {
    dynSet("loading", false)
    var list = json.map(place => place.name)
    dynSet("change_it", list)
  })
  .catch(e => {
    dynSet("loading", false)
    dynSet("error", e)
    console.log("A bad request!", e)
  })
}

Now press search when there's nothing in the input box and my NodeJS server will serve you up a 400 response.
 
Now we have dynamic DOM updates, remote data fetches, an animated loading button on remote data fetch, an error dialogue and some very simple new "tags".
 
The fetchData method is getting a bit out of hand, but we can come to that later.
 
Here's a working example: https://jsfiddle.net/newfivefour/0vo6h41f/32/

javascript css


Use html to order a border with a title around things

You can group items with a border in HTML using fieldset and you can also give that border a title using legend.
 
The title breaks the border briefly at the top.
 
Here's an example which you can use to impress your friends, family, dogs, cats and potential parliamentary representatives.

<fieldset style="border: 1px dashed black">
  <legend>Ladies and gentlemen we are floating in space</legend>
  Sup
</fieldset>

css html

Page 1 of 3
next