Showing posts with label forms. Show all posts
Showing posts with label forms. Show all posts

Saturday, 12 April 2025

Buttons or Divs? What to use, and when

With the power of CSS, HTML is significantly more visually versatile than it was in its inception more than two decades ago. Especially with divs. You can make divs appear as anything - paragraphs, block quotes and images. In extreme examples, you could even render entire paintings using many, many divs. 

A huge variety of shapes,
especially rectangular.

The humble div tag, coupled with CSS, is no longer just a rectangle on the browser. Using properties such as transform, border-radius, width and height, among others, a web developer can achieve a myriad of looks.

And this manifests quite frequently, in buttons. Previously, I discussed whether button or input tags would be preferable, but today we make a separate comparison between divs and buttons.

Divs as buttons

Making divs look like buttons is simple enough. How about behavior? Well, for that, JavaScript accomplishes this fairly easily.

One is a button and the other is a div.
<button>This is a button</button>
<div style="cursor:pointer; background-color:rgb(230, 230, 230); border:1px solid rgb(100, 100, 100); border-radius: 3px; font-family: sans-serif; font-size: 12px; width: 8em; padding: 0.2em; text-align: center">
This is a div
</div>


But they can both be made to perform certain actions on a click.
<button onclick="alert('I am a button');">This is a button</button>
<div onclick="alert('I am a div');" style="cursor:pointer; background-color:rgb(230, 230, 230); border:1px solid rgb(100, 100, 100); border-radius: 3px; font-family: sans-serif; font-size: 12px; width: 8em; padding: 0.2em; text-align: center">
This is a div
</div>


Depending on the browser, you should see no appreciable difference between these.
This is a div


How about submitting a form? Well, a button usually does this.
<form id="frmTest">
    <button>Submit<button>
</form>


But if you want a div to do this, all you really need is a bit more code.
<form id="frmTest">
    <div onclick="document.getElementById('frmTest').submit()">Submit<div>
</form>


Definitely possible, but should we?

Visually, there's not a lot of difference. In fact, styling divs to look like buttons, could even potentially offset visual differences of button rendering between browsers. For example, we take the code written earlier.

This is how it looks on Chrome.


This is how it looks on Safari. See? There's no visual change in the div we styled, but the button looks remarkably different.


However, not everything is about the visual. Especially not to the visually-impaired. The button tag and a div tag reads differently in semantics. On a screen reader, the button tag immediately stands out as a control to be clicked, while a div is semantically no different from any other div.

That is the greatest, and most significant difference. Not being blind, it is understandably difficult to imagine perceiving anything other than in visual terms, since a large part of what people like us perceive, is in the visual medium.

Conclusion

The internet was not only made for people like myself. The internet was meant as an equalizer where it came to information access. Not only did it mean that the average person now had access to information that was not available readily in the past, people with visual disabilities were supposed to be able to access this information.

And that access could be compromised if code was written with the visual intent in mind rather than the semantic.


T___T

Monday, 27 January 2025

Web Tutorial: The NodeJS Wood Snake Fortune Teller (Part 2/2)

Time to start submitting the form!

Before anything, we use this module to parse the form body. We also want to use the form data to call an API endpoint, and for that we need the Fetch module. There are various versions of Fetch, and for the simplest one, we'll use a slightly backdated version.
npm install --save body-parser
npm install --save node-fetch@2


In the bdChange() function, add this line after displaying "Please wait...". It submits the form.

assets/js/functions.js
function bdChange() {
  var errorContainer = document.getElementById("errorContainer");
  var txtBd = document.getElementById("txtBd");  
  var formFortune = document.getElementById("formFortune");
  var fortuneContainer = document.getElementById("fortuneContainer");

  var d = new Date();
  var bd = new Date(txtBd.value);

  if (bd.getTime() > d.getTime()) {
    errorContainer.innerHTML = "Error! You can't possibly be born in the future.";
  } else {
    errorContainer.innerHTML = "";
    fortuneContainer.innerHTML = "Please wait..."
    formFortune.submit();
  }
}


Now, in app.js, we'll work on the route fortune. We first want to include the Fetch module.

app.js
app.post("/fortune", (req, res)=> {
  let fetch = require("node-fetch");
});


Declare headers an an object. We are preparing to send data via an OpenAI API endpoint. For that, the object needs to have the properties Authorization, OpenAI-Organization and Content-Type.

app.js
app.post("/fortune", (req, res)=> {
  let fetch = require("node-fetch");

  var headers = {
    "Authorization": ,
    "OpenAI-Oganization": ,
    "Content-Type":     
  }; 
 
});


Content-Type is JSON. The other two are strings based on your OpenAI account. As for what api is, it's a file we will create right now.

app.js
app.post("/fortune", (req, res)=> {
  let fetch = require("node-fetch");

  var headers = {
    "Authorization": "Bearer " + api.key,
    "OpenAI-Oganization": api.org,
    "Content-Type": "application/json"    
  };  
});


Having the details in this file helps protect your details. This information should be private.

api.js
module.exports = {
  org: "org-xxx",
  key: "sk-xxx"
}


Add this line. That will import the details of api.js so that any information is obtainable by referencing the object api.

app.js
var api = require("./api.js");
var express = require("express");


Continuing on with the fortune route, we declare messages as an empty array. Then we declare obj as n object and push obj into messages.

app.js
app.post("/fortune", (req, res)=> {
  let fetch = require("node-fetch");

  var headers = {
    "Authorization": "Bearer " + api.key,
    "OpenAI-Oganization": api.org,
    "Content-Type": "application/json"    
  };

  var messages = [];
  var obj = {

  }
  messages.push(obj);

});


obj should have the role property, value set to "user". The content property is the prompt string that will be sent to OpenAI. In it, we will use the birth date passed from the form.

app.js
app.post("/fortune", (req, res)=> {
  let fetch = require("node-fetch");

  var headers = {
    "Authorization": "Bearer " + api.key,
    "OpenAI-Oganization": api.org,
    "Content-Type": "application/json"    
  };

  var messages = [];
  var obj = {
    "role": "user",
    "content" : "It is currently 2025. My birth date is " + req.body.txtBd + ". Using exactly 5 paragraphs give me my Chinese Zodiac and element I also want a personality profile for myself. Lastly, provide a fortune for love, money and health."

  }
  messages.push(obj);
});


In order to use the body object, however, we need to summon the power of the module body-parser.

app.js
app.set("view engine", "handlebars");
app.set("port", process.env.PORT || 3000);

app.use(require("body-parser")());
app.use(express.static("assets"));


Back to the fortune route, we declare body as an object with model, messages and max_tokens as properties. messages will be the array messages that we just worked on, model will be whatever ChatGPT model you want to use, and max_tokens will be a matter of how much resources you want to dedicate to this call.

app.js
app.post("/fortune", (req, res)=> {
  let fetch = require("node-fetch");

  var headers = {
    "Authorization": "Bearer " + api.key,
    "OpenAI-Oganization": api.org,
    "Content-Type": "application/json"    
  };

  var messages = [];
  var obj = {
    "role": "user",
    "content" : "It is currently 2025. My birth date is " + req.body.txtBd + ". Using exactly 5 paragraphs give me my Chinese Zodiac and element I also want a personality profile for myself. Lastly, provide a fortune for love, money and health."
  };
  messages.push(obj);

  var body = {
    "model": "gpt-3.5-turbo",
    "messages" : messages,
    "max_tokens" : 2500
  }

});


We will then use fetch. Pass in as a first argument, the URL endpoint.

app.js
app.post("/fortune", (req, res)=> {
  let fetch = require("node-fetch");

  var headers = {
    "Authorization": "Bearer " + api.key,
    "OpenAI-Oganization": api.org,
    "Content-Type": "application/json"    
  };  

  var messages = [];
  var obj = {
    "role": "user",
    "content" : "It is currently 2025. My birth date is " + req.body.txtBd + ". Using exactly 5 paragraphs give me my Chinese Zodiac and element I also want a personality profile for myself. Lastly, provide a fortune for love, money and health."
  };
  messages.push(obj);

  var body = {
    "model": "gpt-3.5-turbo",
    "messages" : messages,
    "max_tokens" : 2500
  }

  fetch("https://api.openai.com/v1/chat/completions")
});


For the second argument, we need an object with the properties method, headers and body. method is "POST". headers is the object headers which we defined earlier, and body will be a JSON string representation of the body object.

app.js
app.post("/fortune", (req, res)=> {
  let fetch = require("node-fetch");

  var headers = {
    "Authorization": "Bearer " + api.key,
    "OpenAI-Oganization": api.org,
    "Content-Type": "application/json"    
  };  

  var body = {
    "model": "gpt-3.5-turbo",
    "messages" : messages,
    "max_tokens" : 2500
  }

  fetch("https://api.openai.com/v1/chat/completions", {
    method: "POST",
    headers: headers,
    body: JSON.stringify(body)
  }
)
});


Once the data has been obtained, we use the then() method to handle it. We'll use the text() method to send the text representation of the data stream response further down the line.

app.js
fetch("https://api.openai.com/v1/chat/completions", {
  method: "POST",
  headers: headers,
  body: JSON.stringify(body)
})
.then(response => response.text())


We then chain another then() method call to handle the data, and a catch() method to handle errors.

app.js
fetch("https://api.openai.com/v1/chat/completions", {
  method: "POST",
  headers: headers,
  body: JSON.stringify(body)
})
.then(response => response.text())
.then(data => {

})
.catch(err => {

});  


If there's an error, we render the form view but with err in the error property.

app.js
fetch("https://api.openai.com/v1/chat/completions", {
  method: "POST",
  headers: headers,
  body: JSON.stringify(body)
})
.then(response => response.text())
.then(data => {

})
.catch(err => {
  res.render("form", { error: err, fortune: "" });
});  


Handling data, we first declare json_data and assign to it the JSON representation of the string data.

app.js
fetch("https://api.openai.com/v1/chat/completions", {
  method: "POST",
  headers: headers,
  body: JSON.stringify(body)
})
.then(response => response.text())
.then(data => {
  var json_data = JSON.parse(data);
})
.catch(err => {
  res.render("form", { error: err, fortune: "" });
});  


We will derive the content by getting the first element of the choices array of json_data, then accessing the message and content properties.

app.js
fetch("https://api.openai.com/v1/chat/completions", {
  method: "POST",
  headers: headers,
  body: JSON.stringify(body)
})
.then(response => response.text())
.then(data => {
  var json_data = JSON.parse(data);
  var html_content = json_data.choices[0].message.content;

})
.catch(err => {
  res.render("form", { error: err, fortune: "" });
});  


We'll format this properly in HTML by using the replaceAll() method to replace all line breaks with HTML breaks.

app.js
fetch("https://api.openai.com/v1/chat/completions", {
  method: "POST",
  headers: headers,
  body: JSON.stringify(body)
})
.then(response => response.text())
.then(data => {
  var json_data = JSON.parse(data);
  var html_content = json_data.choices[0].message.content.replaceAll("\n\n", "<br /><br />");
})
.catch(err => {
  res.render("form", { error: err, fortune: "" });
});  


And finally, render the form view with html_content as the value of the fortune property.

app.js
fetch("https://api.openai.com/v1/chat/completions", {
  method: "POST",
  headers: headers,
  body: JSON.stringify(body)
})
.then(response => response.text())
.then(data => {
  var json_data = JSON.parse(data);
  var html_content = json_data.choices[0].message.content.replaceAll("\n\n", "<br /><br />");
  res.render("form", { error: "", fortune: html_content });
})
.catch(err => {
  res.render("form", { error: err, fortune: "" });
});  


Oops, the break tags do appear, but as text!

Add an extra pair of curly brackets for the fortune placeholder, to indicate that this should be formatted.

views/form.handlebars
<h1>Welcome to the Wood Snake Fortune Teller!</h1>

<form action="/fortune" id="formFortune" method="POST">
  <label for="txtBd">Birth Date</label>
  <br />
  <input type="date" name="txtBd" id="txtBd" value="2025-01-01" onChange="bdChange();" />
</form>

<div class="error" id="errorContainer">{{ error }}</div>
<br />
<div class="fortune" id="fortuneContainer"> {{{ fortune }}}</div>


Fixed!

This was a simple exercise in API calls using NodeJS. Hope you enjoyed, pretty sure I did!


Blessssssed fortunessssssss,
T___T

Saturday, 25 January 2025

Web Tutorial: The NodeJS Wood Snake Fortune Teller (Part 1/2)

What'ssssssss up?! (see what I did there?)

It's about to be the Year of the Snake. Chinese New Year is upon us in a week. To that end, I'd like to continue my experimentations with NodeJS. Today, we're going to explore two separate things - how to manage assets in a NodeJS app, and how to call and retrieve data via a REST endpoint using the Fetch module.

To that end, we will be building a fortune teller app. Way to lean into the Chinese mysticism, eh?

Let's begin with some module installation. We will need Express and Handlebars for starters, just to render the layouts.
npm install --save express
npm install --save express-handlebars


Let's continue with a bit of file structure setup. We should have a directory called assets. Within, let's have three subfolders - img, css and js. No prizes for guesing what files they hold! Then create the views directory, and within it, have the layouts folder. Create these files in their respective folders.

views/layouts/main.handlebars
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>2025 Wood Snake Fortune Teller</title>
  </head>
  <body>
    {{{ body }}}  
  </body>
</html>


views/404.handlebars
<h1>404</h1>

<p>Not found!</p>


views/500.handlebars
<h1>500</h1>

<p>There was an error.</p>
<p><b>{{ errorMessage }}</b></p>


views/form.handlebars
<h1>Welcome to the Wood Snake Fortune Teller!</h1>

<form>

</form>


Those are all the views we're going to have. It's good to get them out of the way. Now let's work on the main app. Create app.js. This beginning code sets up Express as the middleware and Handlebars as the view engine. main.handlebars is specified as the layout template file.

app.js
var express = require("express");

var app = express();

var handlebars = require("express-handlebars").create({defaultLayout:"main"});
app.engine("handlebars", handlebars.engine);

app.set("view engine", "handlebars");
app.set("port", process.env.PORT || 3000);


We will then fill in routes for the default, the form handling POST route process, the 404 and 500 error handling views. At the end of it, we "start" the app by using the listen() method to listen on Port 3000.

app.js
app.set("view engine", "handlebars");
app.set("port", process.env.PORT || 3000);

app.get("/", (req, res)=> {

});

app.post("/fortune", (req, res)=> {

});

app.use((req, res, next)=> {
  res.status(404);
  res.render("404");
});

app.use((err, req, res, next)=> {
  res.status(500);
  res.render("500", { errorMessage: err.code });
});

app.listen(app.get("port"), ()=> {

});


Let's add some placeholders to this view.

views/form.handlebars
<h1>Welcome to the Wood Snake Fortune Teller!</h1>

<form>

</form>

{{ error }}
<br />
{{ fortune }}


For the default route, add this line to render the form view which we just modified.

app.js
app.get("/", (req, res)=> {
  res.render("form");
});


Add this object as a second argument, with the properties error and fortune, to mirror the placeholders.
app.js
app.get("/", (req, res)=> {
  res.render("form", { error: "", fortune: "" });
});


Then set the value of fortune as a string.

app.js
app.get("/", (req, res)=> {
  res.render("form", { error: "", fortune: "To get your fortune in the year 2025, please provide your birth date." });
});


Simple so far, right?

Let's link some styling and CSS, and images in. We first take this image and save it in the img folder of the assets directory.

assets/img/snake.jpg

Then we save styles.css in the css folder of the assets directory, and functions.js in the js folder of the assets directory. Keep the both balnk, for now. Next, we need to add this line to app.js. The static() method of express, with "assets" passed in as an argument, returns a reference to the directory specified. When that is passed into the use() method of app, that directory becomes the default location of every static file specified thereafter.

app.js
app.set("view engine", "handlebars");
app.set("port", process.env.PORT || 3000);

app.use(express.static("assets"));

app.get("/", (req, res)=> {
  res.render("form", { error: "", fortune: "To get your fortune in the year 2025, please provide your birth date." });
});


So if we add these lines, styles.css and functions.js would be links to the assets directory.

views/layouts/main.handlebars
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>2025 Wood Snake Fortune Teller</title>

    <link rel="stylesheet" type="text/css" href="css/styles.css">
    <script type="text/javascript" src="js/functions.js"></script>

  </head>
  <body>
    {{{ body }}}  
  </body>
</html>


Now, add these divs in the HTML. Your body placeholder should fit into the div styled using the body CSS class.

views/layouts/main.handlebars
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>2025 Wood Snake Fortune Teller</title>

    <link rel="stylesheet" type="text/css" href="css/styles.css">
    <script type="text/javascript" src="js/functions.js"></script>
  </head>
  <body>
    <div class="container">
      <div class="header">
        
      </div>

      <div class="body">

        {{{ body }}}  
      </div>    
    </div>

  </body>
</html>


Time to fill in the CSS file! I want this to be mobile-sized, so I've specified a modest width of 300 pixels. The margin property puts it in the middle of the screen if you view it on a larger screen. Font and alignment have been set, though those are more visual choices. I've given the div round corners, an set the overflow property to hidden so as to bring out the round corners, and even given the whole thing a shadow! The background color is a specific choice of pale grey - to match the background of snake.jpg as much as I can.

assets/css/styles.css
.container {
  width: 300px;
  font-size: 12px;
  font-family: Verdana;
  margin: 0 auto 20px auto;
  background-color: rgb(196, 195, 200);
  text-align: center;
  border-radius: 10px;
  overflow: hidden;
  box-shadow: 8px 8px 5px 2px rgba(0, 0, 0, 0.5);
}


...and we have this!

We'll style header and body next. header has a specified background image. Since assets is already the default root directory for all static content, we just need to go up one level from the css folder before going into img to find snake.jpg. body just has a smaller width and the padding property compensates for it.

assets/css/styles.css
.container {
  width: 300px;
  font-size: 12px;
  font-family: Verdana;
  margin: 0 auto 20px auto;
  background-color: rgb(196, 195, 200);
  text-align: center;
  border-radius: 10px;
  overflow: hidden;
  box-shadow: 8px 8px 5px 2px rgba(0, 0, 0, 0.5);
}

.header {
  width: 300px;
  height: 205px;
  background-repeat: no-repeat;
  background-size: cover;
  background-image: url("../img/snake.jpg");
}

.body {
  width: 280px;
  padding: 10px;
}


Here we go!


We'll work on the form next. Set it to call the fortune route when submitting and specify that the method is POST. Also, give it an id.

views/form.handlebars
<form action="/fortune" id="formFortune" method="POST">

</form>


Inside the form, we will need an input and label. The input will be of the type date and both name and id will be txtBd. The default value is the first day of 2025.

views/form.handlebars
<form action="/fortune" id="formFortune" method="POST">
  <label for="txtBd">Birth Date</label>
  <br />
  <input type="date" name="txtBd" id="txtBd" value="2025-01-01" />
</form>


Now in the CSS, let's style this. These are just to make things look good and don't really affect functionality.

assets/css/styles.css
.body {
  width: 280px;
  padding: 10px;
}

label {
  font-weight: bold;
}

input[type="date"] {
  font-size: 1.2em;
  width: 150px;
  height: 1.5em;
  padding: 0.5em;
}


Here's the input. The user is supposed to select a date from this.

Now let's do a bit of front-end scripting. In the input, set the onchange attribute to call bdChange().

views/form.handlebars
<form action="/fortune" id="formFortune" method="POST">
  <label for="txtBd">Birth Date</label>
  <br />
  <input type="date" name="txtBd" id="txtBd" value="2025-01-01" onChange="bdChange();" />
</form>


Add divs to enclose the error and fortune placeholders. The ids and classes are as follows.

views/form.handlebars
<form action="/fortune" id="formFortune" method="POST">
  <label for="txtBd">Birth Date</label>
  <br />
  <input type="date" name="txtBd" id="txtBd" value="2025-01-01" onChange="bdChange();" />
</form>

<div class="error" id="errorContainer">{{ error }}</div>
<br />
<div class="fortune" id="fortuneContainer">{{ fortune }}</div>


Now fill in the JavaScript file with the bdChange() function.

assets/js/functions.js
function bdChange() {

}


Declare these variables, referencing the divs, the form and the input.

assets/js/functions.js
function bdChange() {
  var errorContainer = document.getElementById("errorContainer");
  var txtBd = document.getElementById("txtBd");  
  var formFortune = document.getElementById("formFortune");
  var fortuneContainer = document.getElementById("fortuneContainer");

}


Then use JavaScript's Date class to declare d as today's date. And bd as the date from the input.

assets/js/functions.js
function bdChange() {
  var errorContainer = document.getElementById("errorContainer");
  var txtBd = document.getElementById("txtBd");  
  var formFortune = document.getElementById("formFortune");
  var fortuneContainer = document.getElementById("fortuneContainer");

  var d = new Date();
  var bd = new Date(txtBd.value);

}


Do a comparison using the getTime() method. If bd is later than today's date, display the error.

assets/js/functions.js
function bdChange() {
  var errorContainer = document.getElementById("errorContainer");
  var txtBd = document.getElementById("txtBd");  
  var formFortune = document.getElementById("formFortune");
  var fortuneContainer = document.getElementById("fortuneContainer");

  var d = new Date();
  var bd = new Date(txtBd.value);

  if (bd.getTime() > d.getTime()) {
    errorContainer.innerHTML = "Error! You can't possibly be born in the future.";
  }

}


If not, clear the errorContainer div and put this message into the fortuneContainer div.

assets/js/functions.js
function bdChange() {
  var errorContainer = document.getElementById("errorContainer");
  var txtBd = document.getElementById("txtBd");  
  var formFortune = document.getElementById("formFortune");
  var fortuneContainer = document.getElementById("fortuneContainer");

  var d = new Date();
  var bd = new Date(txtBd.value);

  if (bd.getTime() > d.getTime()) {
    errorContainer.innerHTML = "Error! You can't possibly be born in the future.";
  } else {
    errorContainer.innerHTML = "";
    fortuneContainer.innerHTML = "Please wait..."
  }

}


Just a bit more styling...

assets/css/styles.css
.body {
  width: 280px;
  padding: 10px;
}

.error {
  color: rgb(200, 0, 0);
  font-size: 0.8em;
  font-weight: bold;
}

.fortune {
  border-top: 3px double rgb(100, 100, 100);
  padding-top: 10px;
}


label {
  font-weight: bold;
}

input[type="date"] {
  font-size: 1.2em;
  width: 150px;
  height: 1.5em;
  padding: 0.5em;
}


Try this with an error.


And without.


Next

Submitting the form and getting your fortune!

Monday, 30 May 2016

Data Transport Methods Across Webpages (Part 2/2)

It's time to look at the other method of sending data.

The POST method

The POST method is a more effective way of sending data. The data is embedded in the headers and sent to the next page.

<form method="POST">
    <input name="x" value="test">
    <input name="y" value="12345">
    <input type="submit" name="btSubmit" value="submit">
</form>


Pros

Way more secure than GET - All data is hidden. Note that I said more, not totally secure. POST has its vulnerabilities which we'll explore at a later date.

Versatility in data formats and lengths - You can send long paragraphs of text, in addition to everything you can already send via GET. Also, and this is no trivial matter, you can send binary files.

Cons

Breaks page flow - clicking on the Back button or reloading the page will cause a popup to appear, asking if you wish to re-send your data. Depending on the nature of your data, re-sending the data may cause something to break. Below is a sample of this popup. The message varies from browser to browser.


Use POST for...

... almost everything. Long and complex data, especially files.

... data that needs to be kept private, e.g. transactions or passwords.

Do not use POST for...

... pages you may want to be cached.

... pages where backwards-forwards navigation is an issue.

The methods in a nutshell

The GET method has a well-deserved reputation for being widely used - sometimes overused. I once spoke to a software developer who looked upon GET with disdain and was of the opinion that GET is an unsafe and "cheap" way of sending data. She was only half right. GET is all that, and so much more. There is a place for everything, and GET is no exception. GET has its uses - some of which are not immediately apparent to people who aren't web developers.

On the other hand, I've been guilty of using POST when it would have been more beneficial to use a GET instead. Oh, well. Live and learn.

The lesson for the day is - the correct tool for the correct job. Whatever you end up choosing, exercise appropriate caution.

That's all for the time being. I'll POST again soon. (snicker)
T___T

Saturday, 28 May 2016

Data Transport Methods Across Webpages (Part 1/2)

Web developers all know that there the Internet is a stateless medium and to mitigate that unfortunate fact, there are two basic ways of transporting data across pages - GET and POST. While this is bread-and-butter stuff for all who deal with the web, ultimately not every developer understands GET and POST as well as they should, mostly operating on a vague understanding of which to use, and when.

And that is why, today I'll be dealing with both cases.

The GET method

The GET method is the default way of transporting data. In a HTML form, if the method attribute is not specified in the form tag, the browser automatically assumes a GET. Here's an example. Pay attention to the form tag.
<form>
    <input name="x" value="test">
    <input name="y" value="12345">
    <input type="submit" name="btSubmit" value="submit">
</form>


is the same as
<form method="GET">
    <input name="x" value="test">
    <input name="y" value="12345">
    <input type="submit" name="btSubmit" value="submit">
</form>


The GET method of transporting data basically embeds the data in the URL in data-value pairs separated by ampersands. Assuming that this form is in a page named tt_test.asp, submitting the form would give you a URL of:
tt_test.asp?x=test&y=12345&btSubmit=submit


This also means that you can use the GET method to transport data without using a form.

Pros

Quick and dirty - As mentioned, you don't need a form to send data via the GET method. You merely need to formulate the URL properly.

Caching - Since GET is essentially a URL, it follows that pages generated from the GET method may also be cached, and this shaves valuable time off page loads. URL caching in turn facilitates pages generated using the GET method being crawled by search engines, which leads to better Search Engine Optimization. And this is the one thing that cements GET its place as a viable method of data transport despite its obvious inferiority to POST in many areas.

Cons

Size limitations - The URL can hold only that much data. Therefore using GET to send long strings of data (over 1000 characters including the URL, depending on browser) is not advisable.

Format - GET can send only text data. Even then, special characters have to be encoded. If, for example, you wanted to send an ampersand as part of your data, you would have to be very careful that the it is not mistaken as a separator.

Security - The data appears in the URL. Duh.

Use GET for...

... simple data that can be easily sanitized and whitelisted.

... data that does not need to be private.

Do not use GET for...

... data that should be hidden, such as passwords.

... complex data

... long strings

Absolutely do not use GET for...

... entire SQL queries (Jesus Christ, do you have a death wish or something?!)

tt_test.asp?query=SELECT x from table_y WHERE id=3

Next

We take a look at the POST method.


Saturday, 13 June 2015

Five User Interface Fails

The User Interface, known as UI, is an integral part of any web application. It is the bridge between the user and the system, and vital to effective use of said system. There's where an unhealthy amount of people misunderstand UI. It doesn't have to look pretty. It has to be easy to use, and as intuitive as possible.

In my time as a web developer, I've come across some truly mystifying interface elements. These contravene common sense in a huge way. These make you wonder what the hell the developer was thinking, or if he was thinking at all. I've even been guilty of some of these.

Fail, buddy,

It happens. Sometimes developers get lazy. Sometimes they get frustrated and take the easy way out. Or sometimes, they just don't go through as much testing as they should.

1. The Time Traveler Birth Date

Please enter your Date of Birth:

Kind of clumsy, but otherwise nothing really wrong with this one. Except that when I came across this little beauty, it was 2012. The years in the dropdown list go all the way up to 2015!

Now I can accept that maybe 1 year old babies surf the site. Maybe. But a user who will be born in the next few years?! Come on.

2. The Troll Form

Contest Form

Title
Name
Email
Mobile
Address
Postal Code
Company
GST Reg
Designation
Business
Please describe your experiences in logistics, if any
In your opinion, what are the ideal conditions for freight transfer?
What do you see in the future of automation and technology?
Please describe your experience at the convention so far.
Are there any areas in which the organizers could improve on?


Long form, eh? Did you try the Submit button? Yes, entries were closed and this genius of a developer decided to just add a JavaScript alert to inform the user, after the user fills in the whole damn form and clicks the Submit button. Imagine the frustration!

I confess. I was the lazy bastard who did this back then. Got hell from my boss for it, and deservedly so. Somehow I decided that I really didn't want to go through the trouble of hiding the entire form and writing a nice informative message right at the top of the page.

3. The Unreadable Calendar

2014
San Mun Tus Wen Thu Fry Sat
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31


Years ago, while sourcing for a suitable JavaScript calendar plugin to steal reference, I came across this one. Now, let me just say that the code in that plugin was brilliant. No sarcasm there. It was written by an Indian programmer. I learned a fair bit from reverse-engineering it. And one of the things I unfortunately learned was that this dude can't spell.

It doesn't matter how awesome your code is - if no one can read your calendar, it's a fail.

4. The Must-click Button

Proin a mi consectetur, fringilla lacus a, placerat lectus. Nunc placerat pretium libero quis vehicula. Proin accumsan nec lacus id bibendum. Morbi tincidunt id lorem eu luctus. Phasellus nec efficitur odio. Sed pulvinar faucibus dictum. Sed eu volutpat magna. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Integer eros lacus, malesuada eget pellentesque at, laoreet sed ipsum. In vehicula sapien et aliquet ullamcorper. Cras pretium convallis accumsan. Nulla placerat congue orci non pharetra. Nulla ornare rhoncus dapibus.


So I'm supposed to mouse-over the button for more information, and when I mouse-out, the information disappears. No problem with that, right? Until you consider the fact that this site was supposed to be on a mobile device. Since when was there a mouse-out in a touch-screen device? The user is only going to end up clicking the button whether or not he wants to.

Also, this site peddles mobile applications. How's that for irony?

Yes, I'm guilty of this one too. Kind of. It wasn't really my idea. I was given specs to fulfill, and it only occurred to me what a fail this was while I was doing it.

5. The Revolving Door


Interdum et malesuada fames ac ante ipsum primis in faucibus. Morbi eget volutpat tellus. Aenean pellentesque lectus id massa vulputate pharetra. Nunc fermentum erat et nulla tempus, nec fermentum mi rutrum. Sed mauris nisl, fermentum quis diam at, hendrerit viverra elit. Etiam erat est, tincidunt in semper nec, rhoncus maximus turpis. Quisque libero arcu, dictum vel tincidunt vitae, tempus vitae libero. Duis sed congue massa, at consectetur augue. Nullam tempus est nec libero dictum egestas. Mauris sit amet magna sed felis congue accumsan nec quis magna. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer consectetur porta lacus, lobortis efficitur urna dignissim maximus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus ornare sapien malesuada gravida blandit. In ac ligula mollis, aliquet libero ullamcorper, egestas massa. Vivamus ornare nulla in nisl blandit euismod.
Aliquam imperdiet purus eget vehicula pulvinar. Pellentesque in lorem mauris. Maecenas suscipit sem nulla, quis mollis mauris tincidunt eget. Aenean congue lobortis mauris, vitae rutrum felis luctus ac. Donec ex lacus, luctus ut diam ac, faucibus lacinia dolor. Maecenas interdum dignissim quam eu mattis. Nam egestas urna et massa mattis ultricies porta non augue. Nullam fermentum elit justo, et gravida nisi mattis eget. Aliquam pellentesque tellus lectus, sed vulputate ipsum tincidunt id. Nullam velit ex, posuere at sagittis ornare, consectetur in nibh. Curabitur in hendrerit turpis. In augue magna, molestie nec consectetur porttitor, ultrices ut sem. Quisque sed aliquet velit. Nullam venenatis feugiat urna. Suspendisse non elit vitae lectus vestibulum dignissim. Aenean erat dui, scelerisque et fringilla eget, sodales a erat.


Back Button right next to Logout Button. A fail all by itself.

And when you consider the fact that this on a mobile device and users' fingers are generally not small enough to avoid consistently clicking on the Logout Button rather than the Back Button... boy, the word "fail" doesn't even come close. Imagine logging in, and then accidentally logging yourself out again each time you try to navigate.

For those of you privileged enough to have access to Singapore's NS Portal, try logging in via mobile to see what I mean. Information in there is supposed to be classified, so I'm not going to provide a screenshot. SAF, are you listening? I hate your portal, it stinks!

Editor's Note: At the time of this posting, it appears that the SAF has finally cottoned on to the utter fail-laciousness of their portal, and improved the layout accordingly. Well done!

Such utter fail.

Five of the most frustrating interface fails known to man. OK, I'm exaggerating. I'm sure there's worse out there, but blimey, are these annoying!


T___T