Showing posts with label cross-browser compatibility. Show all posts
Showing posts with label cross-browser compatibility. Show all posts

Wednesday, 18 September 2019

Spot The Bug: Internet Explorer Strikes Again!

Hey guys, the Spot The Bug wagon just rolled back into town, and it's time to kick some bug butt!

Die, bugs, die!

Cross-browser compatibility can be a nightmare. And every once in a while, I'm forcibly reminded of that fact.

So here I was, using a jQuery AJAX call to populate a table. The endpoint led to a PHP file (named, imaginatively, getdata.php) which was grabbing data from a MYSQL database.

index.html
        <script>
            $(document).ready
            (
                $.ajax
                (
                    {
                        url: "getdata.php?type=quotes",
                        type: "GET",
                        success: function(result)
                        {
                            var data = JSON.parse(result);

                            $(data.quotes).each
                            (
                                function(i, x)
                                {
                                    $("#tblQuotes").append("<tr><td style='text-align:right'><b>" + x.person + "</b></td><td><i>" + x.quote + "</i></td></tr>");                                                      
                                }
                            )
                        }
                    }
                )
            );
        </script>


All was fine and dandy, until I detected a typo around the last row, and fixed it in the database. See that I spelled "Linus" wrongly? It's an easy mistake to make, given that "s" and "x" are just about right nest next to each other and he did create Linux. Anyways...


I ran the code again in Chrome, everything seemed fine. But once I tried to do the same in Internet Explorer, the typo came back!

What went wrong

It certainly wasn't the PHP. I ran the file directly in the browser, and even in Internet Explorer, it produced the correct results. This was what it was sending back to the AJAX call. Or was it?

getdata.php
$result = array("quotes" => $techQuotes);
echo json_encode($result);




It seemed a little suspicious that this was only happening in Internet Explorer. And since the PHP wasn't at fault, the next moving part was the AJAX call. Which meant that it was a front-end problem, which in turn gelled with the fact that it was only happening in Internet Explorer.

Why it went wrong

Apparently, Internet Explorer caches all GET requests by default. Therefore, since the endpoint was the same, it simply recycled the previous data! Nice going, Microsoft!

How I fixed it

I just explicitly set caching to off, right there.

index.html
                $.ajax
                (
                    {
                        url: "getdata.php?type=quotes",
                        type: "GET",
                        cache: false,
                        success: function(result)
                        {
                            var data = JSON.parse(result);

                            $(data.quotes).each
                            (
                                function(i, x)
                                {
                                    $("#tblQuotes").append("<tr><td style='text-align:right'><b>" + x.person + "</b></td><td><i>" + x.quote + "</i></td></tr>");                                                      
                                }
                            )
                        }
                    }
                )


And presto! That was how it looked in both Chrome and Internet Explorer now.

Conclusion

You know in this day and age it's so easy to forget that web developers are at the mercy of the browsers of the end-users, and what an utter pain it is to ensure cross-browser compatibility. You think using jQuery will solve all your problems, and surprise, surprise, it really doesn't. Sometimes it introduces new ones.

Cache you later!
T___T

Friday, 1 February 2019

Web Tutorial: The Lucky Number Piggy Widget (Part 1/2)

In a few days, we celebrate Chinese New Year.

It's the Year of the Pig, and boy do we have a doozy of a web tutorial here. OK, that's an exaggeration. It's pretty simple as tutorials go, but I definitely had fun with it.

Year of the Pig.

What I've got today is a little JavaScript widget that can be used to insert a little Lucky Number Generator in the shape of a cute piggy...

Why the Lucky Number Generator?

Well, Chinese New Year is predominantly about good fortune. And Singapore has this little gambling culture where we take four numbers and hope to strike lottery. I've never spent any money on lottery tickets myself; but if that's your thing, you do you, dude.

This little JavaScript widget needs to be plug-and-play (well, kind of). In essence, we should be able to embed it as a script link and call a function to display it. Nothing more complicated than that!

For this, we will use an example HTML file. This page has an olive brown background and yellow text, but that's not really relevant. It can use any colors you want (except pink, and you'll see why soon enough). There's nothing in here but a bit of CSS and some Lorem Ipsum text.

tt_cny2019.html
<!DOCTYPE html>
<html>
    <head>
        <title>Year of the Pig</title>

        <style>
            body
            {
                background-color: #444400;
                color: #AAAA00;
                font-family: verdana;
            }

            p
            {
                width:80%;
                text-align: justify;
            }
        </style>
    </head>

    <body>
        <h1>Test header</h1>

        <p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam mattis auctor eleifend. Aenean vel faucibus metus. Etiam tincidunt nulla

at risus pellentesque fermentum. Mauris at orci sem. Fusce id bibendum risus, eget ultricies libero. Suspendisse vitae posuere orci.

Integer eu purus tortor. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Phasellus tincidunt

varius massa quis aliquet. Mauris odio eros, mattis eu viverra vel, luctus non dolor. Proin eleifend quam eu est pulvinar ornare. Vivamus

tempus mi lacinia fermentum suscipit. Vestibulum ante arcu, finibus ac scelerisque a, finibus a ligula.
        </p>

        <h2>Sub header</h2>

        <p>
Etiam vel volutpat eros. Mauris gravida diam at eros iaculis tempus. Pellentesque volutpat consectetur ligula, eu vehicula massa sagittis

eget. Vestibulum lacinia erat at vestibulum imperdiet. In dictum consequat nisi semper feugiat. Fusce pellentesque rhoncus massa, ac

luctus lacus semper a. Nunc eu tincidunt odio. Nunc varius lacus ante. Interdum et malesuada fames ac ante ipsum primis in faucibus.
        </p>

        <p>
Praesent nunc purus, convallis nec elit eget, cursus vehicula turpis. Nam enim nisl, pharetra at dolor in, iaculis pharetra purus. Mauris

blandit et dolor feugiat convallis. Fusce cursus eros vitae tempor pulvinar. Nunc eu pretium elit, sit amet congue ipsum. Maecenas ligula

ligula, gravida a leo a, consectetur cursus libero. Praesent ut lacinia ex. Donec non dolor scelerisque, rhoncus urna id, auctor ligula.

Interdum et malesuada fames ac ante ipsum primis in faucibus. Praesent et ante interdum, vulputate ante eu, interdum justo.

Pellentesque feugiat arcu non facilisis placerat. Pellentesque viverra diam vel mi laoreet pretium. Nulla malesuada finibus pellentesque.

Maecenas mi eros, euismod nec fermentum vel, efficitur et tortor.
        </p>

        <h2>Sub header</h2>

        <p>
Etiam vel volutpat eros. Mauris gravida diam at eros iaculis tempus. Pellentesque volutpat consectetur ligula, eu vehicula massa sagittis

eget. Vestibulum lacinia erat at vestibulum imperdiet. In dictum consequat nisi semper feugiat. Fusce pellentesque rhoncus massa, ac

luctus lacus semper a. Nunc eu tincidunt odio. Nunc varius lacus ante. Interdum et malesuada fames ac ante ipsum primis in faucibus.
        </p>

        <p>
Praesent nunc purus, convallis nec elit eget, cursus vehicula turpis. Nam enim nisl, pharetra at dolor in, iaculis pharetra purus. Mauris

blandit et dolor feugiat convallis. Fusce cursus eros vitae tempor pulvinar. Nunc eu pretium elit, sit amet congue ipsum. Maecenas ligula

ligula, gravida a leo a, consectetur cursus libero. Praesent ut lacinia ex. Donec non dolor scelerisque, rhoncus urna id, auctor ligula.

Interdum et malesuada fames ac ante ipsum primis in faucibus. Praesent et ante interdum, vulputate ante eu, interdum justo.

Pellentesque feugiat arcu non facilisis placerat. Pellentesque viverra diam vel mi laoreet pretium. Nulla malesuada finibus pellentesque.

Maecenas mi eros, euismod nec fermentum vel, efficitur et tortor.
        </p>

        <h2>Sub header</h2>

        <p>
Etiam vel volutpat eros. Mauris gravida diam at eros iaculis tempus. Pellentesque volutpat consectetur ligula, eu vehicula massa sagittis

eget. Vestibulum lacinia erat at vestibulum imperdiet. In dictum consequat nisi semper feugiat. Fusce pellentesque rhoncus massa, ac

luctus lacus semper a. Nunc eu tincidunt odio. Nunc varius lacus ante. Interdum et malesuada fames ac ante ipsum primis in faucibus.
        </p>

        <p>
Praesent nunc purus, convallis nec elit eget, cursus vehicula turpis. Nam enim nisl, pharetra at dolor in, iaculis pharetra purus. Mauris

blandit et dolor feugiat convallis. Fusce cursus eros vitae tempor pulvinar. Nunc eu pretium elit, sit amet congue ipsum. Maecenas ligula

ligula, gravida a leo a, consectetur cursus libero. Praesent ut lacinia ex. Donec non dolor scelerisque, rhoncus urna id, auctor ligula.

Interdum et malesuada fames ac ante ipsum primis in faucibus. Praesent et ante interdum, vulputate ante eu, interdum justo.

Pellentesque feugiat arcu non facilisis placerat. Pellentesque viverra diam vel mi laoreet pretium. Nulla malesuada finibus pellentesque.

Maecenas mi eros, euismod nec fermentum vel, efficitur et tortor.
        </p>   
    </body>
</html>


This will be what we've got.


Now, create another file. Let's call it cny2019.js, for lack of a better name. Save it in the same directory as tt_cny2019.html just for convenience. You should feel free to place it in a subfolder, though you'll need to make the necessary code adjustment. Inside the file, the function cny2019() is defined.

cny2019.js
function cny2019()
{

}


Here, we're including a link to cny2019.js in a script tag. The charset attribute is set to UTF-8 because we'll be using Chinese characters in there.

Wow, ain't that exciting?

tt_cny2019.html
    <head>
        <title>Year of the Pig</title>

        <style>
            body
            {
                background-color: #444400;
                color: #AAAA00;
                font-family: verdana;
            }

            p
            {
                width:80%;
                text-align: justify;
            }
        </style>
       
        <script charset="UTF-8" src="cny2019.js">
        </script>
    </head>


Then add this to the body tag. It will call the cny2019() function. And that's all for the HTML file. You won't need to modify it again. That's a promise. It's merely a placeholder for the real coolness you're going to create in the JavaScript file.

tt_cny2019.html
<body onload="cny2019()">


Inside the cny2019() function, declare a variable, overlay, and set it to the returned result of the createOverlay() function. Then create the createOverlay() function.

cny2019.js
function cny2019()
{
    var overlay = createOverlay();

    function createOverlay()
    {

    }
}


In the createOverlay() function, we use the createElement() method to define a div, set to the variable div. Since we're not using a CSS file, we'll use JavaScript to set the properties.

- outline is set to white so that you can have a better visual.
- width and height are set to 100 and 400 pixels respectively, though you should feel free to change that later on if it suits you.
- position is set to fixed because we want the little piggy to go with the scroll.
- padding is set to 10 pixels for aesthetic purposes.
- right and bottom are both set to 0 so that this guy is fixed at the borrom right corner of the screen. Again, feel free to change this later on.

At the end of the function, we return the variable div.

cny2019.js
    function createOverlay()
    {
        var div = document.createElement("div");
        div.style.outline = "1px solid #FFFFFF";
        div.style.width = "100px";
        div.style.height = "400px";
        div.style.position = "fixed";
        div.style.padding = "10px";
        div.style.right = "0";
        div.style.bottom = "0";

        return div;
    }


Next, in the main function, declare a variable body. Use the getElementsByTagName() method to get the body tag as an array.

cny2019.js
function cny2019()
{
    var overlay = createOverlay();

    var body = document.getElementsByTagName("body");

    function createOverlay()
    {
        var div = document.createElement("div");
        div.style.outline = "1px solid #FFFFFF";
        div.style.width = "100px";
        div.style.height = "400px";
        div.style.position = "fixed";
        div.style.padding = "10px";
        div.style.right = "0";
        div.style.bottom = "0";

        return div;
    }
}


Since there'll be only one instance of the body tag, use the first (and only) element of the body array and invoke the appendChild() method to append overlay to body.

cny2019.js
function cny2019()
{
    var overlay = createOverlay();

    var body = document.getElementsByTagName("body");

    body[0].appendChild(overlay);

    function createOverlay()
    {
        var div = document.createElement("div");
        div.style.outline = "1px solid #FFFFFF";
        div.style.width = "100px";
        div.style.height = "400px";
        div.style.position = "fixed";
        div.style.padding = "10px";
        div.style.right = "0";
        div.style.bottom = "0";

        return div;
    }
}


Refresh. There you go, you should see a white rectangle on the bottom right corner of your screen.


Time to make a little piggy!

For this, we need a new variable, pig. pig will be the returned value of the createPig() function. Then we should append pig to overlay before overlay gets appended to the body.

cny2019.js
    var overlay = createOverlay();
    var pig = createPig();
    var body = document.getElementsByTagName("body");

    overlay.appendChild(pig);
    body[0].appendChild(overlay);


Do not refresh yet; we still have to create the createPig() function. In it, we declare the variable torso and set it to the newly created div. Then at the end of the function, we return torso. Are you beginning to see a pattern yet? If so, bully for you!

cny2019.js
    function createOverlay()
    {
        var div = document.createElement("div");
        div.style.outline = "1px solid #FFFFFF";
        div.style.width = "100px";
        div.style.height = "400px";
        div.style.position = "fixed";
        div.style.padding = "10px";
        div.style.right = "0";
        div.style.bottom = "0";

        return div;
    }

    function createPig()
    {
        var torso = document.createElement("div");

        return torso;
    }


Next, let's set the properties of torso. Our goal here is a nice pink oval. To that end, width, height, borderRadius and backgroundColor are self-explanatory. position needs to be set to relative and float set to right, so the pig knows its place. The cursor property has been set to pointer because we intend to be clicking on the pig later and need to make it obvious.

cny2019.js
    function createPig()
    {
        var torso = document.createElement("div");
        torso.style.width = "80px";
        torso.style.height = "70px";
        torso.style.borderRadius = "50%";
        torso.style.position = "relative";
        torso.style.backgroundColor = "rgba(255, 100, 100, 1)";
        torso.style.float = "right";
        torso.style.cursor = "pointer";

        return torso;
    }


Here's your pink oval now...


Let's give the pig a tail. First, declare the variable tail and set it to a newly created div. Then append it to torso.

cny2019.js
    function createPig()
    {
        var torso = document.createElement("div");
        torso.style.width = "80px";
        torso.style.height = "70px";
        torso.style.borderRadius = "50%";
        torso.style.position = "relative";
        torso.style.backgroundColor = "rgba(255, 100, 100, 1)";
        torso.style.float = "right";
        torso.style.cursor = "pointer";

        var tail = document.createElement("div");

        torso.appendChild(tail);

        return torso;
    }


Of course, we'll need to set some styling properties. First, let's give this div a green outline and color. Then we'll use the ζ symbol as its contents.

cny2019.js
        var tail = document.createElement("div");
        tail.style.outline = "1px solid #00FF00";
        tail.style.color = "rgba(0, 255, 0, 1)";
        tail.innerHTML = "&zeta;";

        torso.appendChild(tail);


Next, bold it and set it bigger, then align it enter.

cny2019.js
        var tail = document.createElement("div");
        tail.style.fontSize = "20px";
        tail.style.fontWeight = "bold";
        tail.style.textAlign = "center";
        tail.style.outline = "1px solid #00FF00";
        tail.style.color = "rgba(0, 255, 0, 1)";
        tail.innerHTML = "&zeta;";

        torso.appendChild(tail);


Finally, set the width and height properties. position must be relative, and the margin property is set so that it's horizontally in the middle of its parent div, which is torso.

cny2019.js
        var tail = document.createElement("div");
        tail.style.width = "80px";
        tail.style.height = "10px";
        tail.style.position = "relative";
        tail.style.margin = "10px auto 0 auto";
        tail.style.fontSize = "20px";
        tail.style.fontWeight = "bold";
        tail.style.textAlign = "center";
        tail.style.outline = "1px solid #00FF00";
        tail.style.color = "rgba(0, 255, 0, 1)";
        tail.innerHTML = "&zeta;";

        torso.appendChild(tail);


See that little green curly-wiurly? That's the tail! It's upside-down, but we'll fix it...


Here, we set the rotation point to the top middle of the div. Then we rotate it 180 degrees! The webKit properties have been added as well, for cross-browser compatibility.

cny2019.js
        var tail = document.createElement("div");
        tail.style.width = "80px";
        tail.style.height = "10px";
        tail.style.position = "relative";
        tail.style.margin = "10px auto 0 auto";
        tail.style.fontSize = "20px";
        tail.style.fontWeight = "bold";
        tail.style.textAlign = "center";
        tail.style.outline = "1px solid #00FF00";
        tail.style.color = "rgba(0, 255, 0, 1)";
        tail.innerHTML = "&zeta;";
        tail.style.webkitTransformOrigin = "50% 0";
        tail.style.transformOrigin = "50% 0";
        tail.style.webkitTransform = "rotate(-180deg)";
        tail.style.transform = "rotate(-180deg)";

        torso.appendChild(tail);


Yep, it looks fine now except for that little matter of the ugly green color. We can leave it for the time being.


Let's give this pig eyes. Declare the variable eyes the same way you declared tail, then append eyes to torso after appending tail.

cny2019.js
        var tail = document.createElement("div");
        tail.style.width = "80px";
        tail.style.height = "10px";
        tail.style.position = "relative";
        tail.style.margin = "10px auto 0 auto";
        tail.style.fontSize = "20px";
        tail.style.fontWeight = "bold";
        tail.style.textAlign = "center";
        tail.style.outline = "1px solid #00FF00";
        tail.style.color = "rgba(0, 255, 0, 1)";
        tail.innerHTML = "&zeta;";
        tail.style.webkitTransformOrigin = "50% 0";
        tail.style.transformOrigin = "50% 0";
        tail.style.webkitTransform = "rotate(-180deg)";
        tail.style.transform = "rotate(-180deg)";

        var eyes = document.createElement("div");

        torso.appendChild(tail);
        torso.appendChild(eyes);


Again, set the height and width properties, set the position property to relative, and the margin property to align it in the middle of torso. It should have some spacing, say 15 pixels, from tail.

cny2019.js
        var eyes = document.createElement("div");
        eyes.style.width = "80px";
        eyes.style.height = "20px";
        eyes.style.position = "relative";
        eyes.style.margin = "15px auto 0 auto";

        torso.appendChild(tail);
        torso.appendChild(eyes);


Then set font size, boldness and alignment.

cny2019.js
        var eyes = document.createElement("div");
        eyes.style.width = "80px";
        eyes.style.height = "20px";
        eyes.style.position = "relative";
        eyes.style.margin = "15px auto 0 auto";
        eyes.style.fontSize = "15px";
        eyes.style.fontWeight = "bold";
        eyes.style.textAlign = "center";

        torso.appendChild(tail);
        torso.appendChild(eyes);


The color of the eyes will be black. But we should still give the div a green outline. The eyes will use a combination of spaces and the • symbol.

cny2019.js
        var eyes = document.createElement("div");
        eyes.style.width = "80px";
        eyes.style.height = "20px";
        eyes.style.position = "relative";
        eyes.style.margin = "15px auto 0 auto";
        eyes.style.fontSize = "15px";
        eyes.style.fontWeight = "bold";
        eyes.style.textAlign = "center";
        eyes.style.outline = "1px solid #00FF00";
        eyes.style.color = "rgba(0, 0, 0, 1)";
        eyes.innerHTML = "&bull;&nbsp;&nbsp;&bull;";

        torso.appendChild(tail);
        torso.appendChild(eyes);


Let's take a look now!


This pig needs a snout. Rinse and repeat...

cny2019.js
        var eyes = document.createElement("div");
        eyes.style.width = "80px";
        eyes.style.height = "20px";
        eyes.style.position = "relative";
        eyes.style.margin = "15px auto 0 auto";
        eyes.style.fontSize = "15px";
        eyes.style.fontWeight = "bold";
        eyes.style.textAlign = "center";
        eyes.style.outline = "1px solid #00FF00";
        eyes.style.color = "rgba(0, 0, 0, 1)";
        eyes.innerHTML = "&bull;&nbsp;&nbsp;&bull;";

        var snout = document.createElement("div");

        torso.appendChild(tail);
        torso.appendChild(eyes);
        torso.appendChild(snout);
       
        return torso;


Set width, height, position and margin properties. borderRadius will be 50%, of course, since the snout is round. Also give it a nice dark pink color.

cny2019.js
        var snout = document.createElement("div");
        snout.style.width = "20px";
        snout.style.height = "15px";
        snout.style.borderRadius = "50%";
        snout.style.position = "relative";
        snout.style.backgroundColor = "rgba(200, 50, 50, 1)";
        snout.style.margin = "0 auto 0 auto";

        torso.appendChild(tail);
        torso.appendChild(eyes);
        torso.appendChild(snout);
       
        return torso;


Set font size, boldness and alignment.

cny2019.js
        var snout = document.createElement("div");
        snout.style.width = "20px";
        snout.style.height = "15px";
        snout.style.borderRadius = "50%";
        snout.style.position = "relative";
        snout.style.backgroundColor = "rgba(200, 50, 50, 1)";
        snout.style.margin = "0 auto 0 auto";
        snout.style.fontSize = "10px";
        snout.style.fontWeight = "bold";
        snout.style.textAlign = "center";

        torso.appendChild(tail);
        torso.appendChild(eyes);
        torso.appendChild(snout);
       
        return torso;


Also give the text a deeper color, maybe brown. The content will be the • symbol. Again, give the div a green outline.

cny2019.js
        var snout = document.createElement("div");
        snout.style.width = "20px";
        snout.style.height = "15px";
        snout.style.borderRadius = "50%";
        snout.style.position = "relative";
        snout.style.backgroundColor = "rgba(200, 50, 50, 1)";
        snout.style.margin = "0 auto 0 auto";
        snout.style.fontSize = "10px";
        snout.style.fontWeight = "bold";
        snout.style.textAlign = "center";
        snout.style.outline = "1px solid #00FF00";
        snout.style.color = "rgba(100, 20, 20, 1)";
        snout.innerHTML = "&bull;&bull;";

        torso.appendChild(tail);
        torso.appendChild(eyes);
        torso.appendChild(snout);
       
        return torso;


Coming along well.


OK, a pig's not a pig without legs. This part is slightly more complicated. We need two hind legs and two front legs. You know what to do this time...

cny2019.js
        var snout = document.createElement("div");
        snout.style.width = "20px";
        snout.style.height = "15px";
        snout.style.borderRadius = "50%";
        snout.style.position = "relative";
        snout.style.backgroundColor = "rgba(200, 50, 50, 1)";
        snout.style.margin = "0 auto 0 auto";
        snout.style.fontSize = "10px";
        snout.style.fontWeight = "bold";
        snout.style.textAlign = "center";
        snout.style.outline = "1px solid #00FF00";
        snout.style.color = "rgba(100, 20, 20, 1)";
        snout.innerHTML = "&bull;&bull;";
   
        var legs = document.createElement("div");

        torso.appendChild(tail);
        torso.appendChild(eyes);
        torso.appendChild(snout);
        torso.appendChild(legs);
       
        return torso;


Set height, width, position and margin properties. For margin, we want it to shift higher up over snout, maybe by 17 pixels. Let's give it a green outline so you can see what you're doing.

cny2019.js
        var legs = document.createElement("div");
        legs.style.width = "70px";
        legs.style.height = "16px";
        legs.style.position = "relative";
        legs.style.margin = "-17px auto 0 auto";
        legs.style.outline = "1px solid #00FF00";

        torso.appendChild(tail);
        torso.appendChild(eyes);
        torso.appendChild(snout);
        torso.appendChild(legs);
       
        return torso;


What you basically did here, was create a container for the four legs. See? It overlaps snout now.


Now, leg1, leg2, leg3 and leg4 are more divs we hve to create. Let's make a function, createLeg(), to handle this. It basically creates a div, sets height, width and position properties, and makes the corners round. For the background color, we'll use green again.

cny2019.js
        var legs = document.createElement("div");
        legs.style.width = "70px";
        legs.style.height = "16px";
        legs.style.position = "relative";
        legs.style.margin = "-17px auto 0 auto";
        legs.style.outline = "1px solid #00FF00";
           
        var leg1 = createLeg();

        var leg2 = createLeg();

        var leg3 = createLeg();

        var leg4 = createLeg();

        torso.appendChild(tail);
        torso.appendChild(eyes);
        torso.appendChild(snout);
        torso.appendChild(legs);
       
        return torso;
    }

    function createLeg()
    {
        var leg = document.createElement("div");
        leg.style.position = "relative";
        leg.style.width = "8px";
        leg.style.height = "15px";
        leg.style.borderRadius = "50%";
        leg.style.backgroundColor = "rgba(0, 255, 0, 1)";

        return leg;
    }


Each leg has different float and margin properties. leg1 and leg2 are forelegs, and thus they get 5 pixels from the top. leg3 and leg4 are hind legs, so they're a bit higher up.

cny2019.js
        var legs = document.createElement("div");
        legs.style.width = "70px";
        legs.style.height = "16px";
        legs.style.position = "relative";
        legs.style.margin = "-17px auto 0 auto";
        legs.style.outline = "1px solid #00FF00";
           
        var leg1 = createLeg();
        leg1.style.float = "left";
        leg1.style.margin = "5px 0 0 5px";
       
        var leg2 = createLeg();
        leg2.style.float = "right";
        leg2.style.margin = "5px 5px 0 0";

        var leg3 = createLeg();
        leg3.style.float = "left";
        leg3.style.margin = "-8px 0 0 0px";
       
        var leg4 = createLeg();
        leg4.style.float = "right";
        leg4.style.margin = "-8px 0px 0 0";

        torso.appendChild(tail);
        torso.appendChild(eyes);
        torso.appendChild(snout);
        torso.appendChild(legs);
       
        return torso;


Now, append leg3, leg1, leg4 and leg2 to legs in this order.

cny2019.js
        var legs = document.createElement("div");
        legs.style.width = "70px";
        legs.style.height = "16px";
        legs.style.position = "relative";
        legs.style.margin = "-17px auto 0 auto";
        legs.style.outline = "1px solid #00FF00";
           
        var leg1 = createLeg();
        leg1.style.float = "left";
        leg1.style.margin = "5px 0 0 5px";
       
        var leg2 = createLeg();
        leg2.style.float = "right";
        leg2.style.margin = "5px 5px 0 0";

        var leg3 = createLeg();
        leg3.style.float = "left";
        leg3.style.margin = "-8px 0 0 0px";
       
        var leg4 = createLeg();
        leg4.style.float = "right";
        leg4.style.margin = "-8px 0px 0 0";

        legs.appendChild(leg3);
        legs.appendChild(leg1);
        legs.appendChild(leg4);
        legs.appendChild(leg2);   

        torso.appendChild(tail);
        torso.appendChild(eyes);
        torso.appendChild(snout);
        torso.appendChild(legs);
       
        return torso;


There you go. Four stubby green legs!


Now, for some cleanup!

Everywhere we put the green outline, comment that line out. Everywhere we set the background or text color to green, change it to pink.

cny2019.js
        //tail.style.outline = "1px solid #00FF00";
        tail.style.color = "rgba(255, 100, 100, 1)";


cny2019.js
        //eyes.style.outline = "1px solid #00FF00";
        eyes.style.color = "rgba(0, 0, 0, 1)";


cny2019.js
        //snout.style.outline = "1px solid #00FF00";
        snout.style.color = "rgba(100, 20, 20, 1)";


cny2019.js
        var legs = document.createElement("div");
        legs.style.width = "70px";
        legs.style.height = "16px";
        legs.style.position = "relative";
        legs.style.margin = "-17px auto 0 auto";
        //legs.style.outline = "1px solid #00FF00";


cny2019.js
    function createLeg()
    {
        var leg = document.createElement("div");
        leg.style.position = "relative";
        leg.style.width = "8px";
        leg.style.height = "15px";
        leg.style.borderRadius = "50%";
        leg.style.backgroundColor = "rgba(255, 100, 100, 1)";

        return leg;
    }


Now that's a pig!


For bonus points...

Let's animate this sucker!

First, we make the tail wag. Give the tail div a class name, say, cny2019_tail. Imaginative, I know. Then set the transition property to half a second.

cny2019.js
        var tail = document.createElement("div");
        tail.className = "cny2019_tail";
        tail.style.width = "80px";
        tail.style.height = "10px";
        tail.style.position = "relative";
        tail.style.margin = "10px auto 0 auto";
        tail.style.fontSize = "20px";
        tail.style.fontWeight = "bold";
        tail.style.textAlign = "center";
        //tail.style.outline = "1px solid #00FF00";
        tail.style.color = "rgba(255, 100, 100, 1)";
        tail.innerHTML = "&zeta;";
        tail.style.webkitTransformOrigin = "50% 0";
        tail.style.transformOrigin = "50% 0";
        tail.style.webkitTransform = "rotate(-180deg)";
        tail.style.transform = "rotate(-180deg)";
        tail.style.webkitTransition = "all 0.5s";
        tail.style.transition = "all 0.5s";


In the main function, after appending pig to overlay, use the setInterval() function with an interval of 1 second. If you need more information on JavaScript Timer functions, here's a link.

cny2019.js
    var overlay = createOverlay();
    var pig = createPig();
    var body = document.getElementsByTagName("body");

    overlay.appendChild(pig);
    body[0].appendChild(overlay);

    setInterval
    (
        function()
        {

        },
        1000
    );

    function createOverlay()
    {
        var div = document.createElement("div");
        div.style.outline = "1px solid #FFFFFF";
        div.style.width = "100px";
        div.style.height = "400px";
        div.style.position = "fixed";
        div.style.padding = "10px";
        div.style.right = "0";
        div.style.bottom = "0";
        return div;
    }


So every 1 second, we get tail, which is the array returned by the getElementsByClassName() method with "cny2019_tail" passed in as an argument.

cny2019.js
    setInterval
    (
        function()
        {
            var tail = document.getElementsByClassName("cny2019_tail");
        },
        1000
    );


Rotate the first (and only) element of tail by -185 degrees. Why? Because it's already been rotated by 180 degrees.

cny2019.js
    setInterval
    (
        function()
        {
            var tail = document.getElementsByClassName("cny2019_tail");

            tail[0].style.transform = "rotate(-185deg)";
        },
        1000
    );


Use the setTimeout() function to rotate it back to -175 degrees after half a second. So every 1 second, you rotate one way. And within that 1 second, you rotate it the other way. The net effect is that you get a wagging tail. Refresh and try it!

cny2019.js
    setInterval
    (
        function()
        {
            var tail = document.getElementsByClassName("cny2019_tail");

            tail[0].style.transform = "rotate(-185deg)";

            setTimeout
            (
                function()
                {
                    tail[0].style.transform = "rotate(-175deg)";
                },
                500
            );
        },
        1000
    );


Next, we'll apply the same logic to the eyes...


Give the eyes div a class name of cny2019_eyes.

cny2019.js
        var eyes = document.createElement("div");
        eyes.className = "cny2019_eyes";
        eyes.style.width = "80px";
        eyes.style.height = "20px";
        eyes.style.position = "relative";
        eyes.style.margin = "15px auto 0 auto";
        eyes.style.fontSize = "15px";
        eyes.style.fontWeight = "bold";
        eyes.style.textAlign = "center";
        //eyes.style.outline = "1px solid #00FF00";
        eyes.style.color = "rgba(0, 0, 0, 1)";
        eyes.innerHTML = "&bull;&nbsp;&nbsp;&bull;";


This time, we'll use an interval of 5 seconds.

cny2019.js
    var overlay = createOverlay();
    var pig = createPig();
    var body = document.getElementsByTagName("body");

    overlay.appendChild(pig);
    body[0].appendChild(overlay);

    setInterval
    (
        function()
        {

        },
        5000
    );

    setInterval
    (
        function()
        {
            var tail = document.getElementsByClassName("cny2019_tail");

            tail[0].style.transform = "rotate(-185deg)";

            setTimeout
            (
                function()
                {
                    tail[0].style.transform = "rotate(-175deg)";
                },
                500
            );
        },
        1000
    );


Change the content of eyes[0] to dashes instead of the • symbol.

cny2019.js
    setInterval
    (
        function()
        {
            var eyes = document.getElementsByClassName("cny2019_eyes");

            eyes[0].innerHTML = "-&nbsp;&nbsp;-";
        },
        5000
    );


Then within that 5 second timeframe, use the setTimeout() function to change it back in half a second! So you get a quickly blinking pig every 5 seconds.

cny2019.js
    setInterval
    (
        function()
        {
            var eyes = document.getElementsByClassName("cny2019_eyes");

            eyes[0].innerHTML = "-&nbsp;&nbsp;-";

            setTimeout
            (
                function()
                {
                    eyes[0].innerHTML = "&bull;&nbsp;&nbsp;&bull;";
                },
                500
            );
        },
        5000
    );


Well, that's it for the pig!

The purpose of this web tutorial was to create a JavaScript file that can be included and called. So you don't even need to prepare any placeholders on your pages. Just include the JavaScript file, and call the cny2019() function.

That was quite the blast, but that's not the end yet! 

Next

Time for some lucky number generation!

Tuesday, 1 January 2019

Five Code Practices to Observe Starting From 2019

Hi, guys. 2019 is here. Time for more resolutions!

Back in 2016, I made a short list of things to keep to, and to my chagrin, by mid-2017, I'd slacked off on a couple items. Still, I think I made a good go of it. This time round, my resolutions are all code-based.

1. Spacing

I'm absolutely terrible with this. And in 2019, I'd like to sternly remind myself to keep my code readable by putting in spaces where it's warranted. Sure, it makes not a lick of difference to the compiler or interpreter. But this isn't for the benefit of the machine - it's for the benefit of the poor human eyes that have to read my code.

From crap like this...
var x=(y<obj.average?obj.average+y:(obj.average+y)/(obj.items.length+1));


To this!
var x = (y < obj.average ? obj.average + y : (obj.average + y) / (obj.items.length + 1));


2. Naming Conventions

Another thing I'm really bad at. I tend to name variables either with very vague meanings such as temp or str. Worse, I might even use Hungarian Notation like strTemperature or varX. See, this is pointless. Like in the case of varX, no shit it's a variable, right? Barring constants which I don't use much of, most declared elements are variables.

Here, I resolve to reserve Hungarian Notation only for naming HTML elements, such as txtFullName for a text field or rbOptions for a radio button. It's a habit I carried over from my days using Visual Basic, and it's a hard one to break.

A good practice would be to go from...
String txtName;
Int intId;


... to longer, more meaningful Camel Case names.
String previousTransitionAuditorFullName;
Int previousTransitionAuditorId;


3. Readmes

There's a lot of code in my GitHub account. Unfortunately, not all of it is documented with nicely formattered markdown readme files. I'm making the effort to at least make a note of how the code is written, how it should be run, and so on. Specifically, the idea behind the code.

## Functions

**getAverageStat(statType)**
This function accepts a string, *statType* as a parameter and uses its value to iterate through the corresponding array and determine the average value.

**getTotalStat(statType)**
This function accepts a string, *statType* as a parameter and uses its value to iterate through the corresponding array and determine the total value.


I like to think my code is simple enough to read, but a short note in the repository is responsible coding. No one should have to read through my code without first having an idea of what they're reading. At this point in time, I'm trying to make up for my laxness in the past by documenting repositories as and when I find the time.


4. CSS cross browser

Now, if anyone has any experience with cross-browser CSS, it's using browser prefixes such as -moz- or -webkit-. I tended to do the below, thinking that it didn't really matter, which is wrong. The effect may be less than desired depending on what browse the code is running in, and what styling is being used.

.shootingStar
{
    transition: all 4s ease;
    -webkit-transition: all 4s ease;
    -moz-transition: all 4s ease;
    -ms-transition: all 4s ease;
    -o-transition: all 4s ease;    
}


The correct way is this, and it needs to be hammered home. The standard non-prefixed line has to be last.
.shootingStar
{
    -webkit-transition: all 4s ease;
    -moz-transition: all 4s ease;
    -ms-transition: all 4s ease;
    -o-transition: all 4s ease;
    transition: all 4s ease;
}

Editor's Note: The other prefixes are being included as examples. Some of them are no longer in use for the purposes of the transition property.

5. Doctype declaration

Web pages these days start out like this. Would you believe I sometimes miss out the very first line?!
!DOCTYPE html
<html>
    <head>
        <title>Test Title</title>
    </head>

    <body>
    </body>
</html>


It's easy to blame Sublime Text Editor, which I'm using, for this, since it doesn't include the doctype declaration as part of the shortcuts. However, like any professional, the buck stops with me. Missing the doctype potentially causes a whole host of problems that may cause the page to appear incorrectly. I won't go into those here; suffice to say, every time I catch myself doing this, any code I've written will need to be re-tested.

That's it!

There are a lot more code practices I should be picking up, but these should go a long way. Happy 2019, geeks!

echo strStandardSignOffMessage;
T___T

Saturday, 23 April 2016

Web Tutorial: Wheel of Fortune, Safari Edition

It appears that my Wheel of Fortune web tutorial met some... misfortune.

Sometime after I had released the code, I happened to test it on Safari. And this is what I got.


Oh, the horror.

Despite the fact that I had conscientiously written WebKit equivalents of the CSS classes and specifications, Safari has this bug where the overflow property does not play nice with the border-radius property. And that sucks because there are some truly interesting effects to be enjoyed when these properties are combined.

That said, I can only blame myself for not checking that the code works on Safari and Opera. Most of the time, it's Firefox and Chrome. I don't test on Internet Explorer because, well, fuck IE.

What's the Safari fix, then?

Well, I'm sure given enough time and effort, one could find a way to break through this wall. But a good developer also entertains the notion of going around this wall. Which is precisely what we're going to do today.

First, add the CSS specification for safari_fix.
            #wheel_wrapper
            {
                width:500px;
                height:500px;
                position:relative;
                margin:0px auto 400px auto;
            }

            #safari_fix
            {
                width:500px;
                height:500px;
                position:absolute;
                z-index:100;
                margin-left:-120px;
                margin-top:-120px;
                border:120px solid #000000;
                border-radius:50%;
            }


Here are what the properties do...
- width:500px, height:500px, border-radius:50%; to create a circular div the same size as the wheel_wrapper div.
- position:absolute, z-index:100 because we want to overlap wheel_wrapper.
- border: 120px solid #00000 to create a very thick black border around the div. Why black? That's so you can see it better.
- margin-left and margin-top properties have been set to -120px in order to offset the positioning effects of having a 120 pixel-wide border.

Now create a div with the id safari_fix and put in inside wheel_wrapper.
        <div id="wheel_wrapper">
            <div id="safari_fix">
       
            </div>   
            <div class="semicircle_wrapper wheel2">
                <div class="segment_wrapper semicircle2">
                    <div class="segment7 segment">


Here's what you should have. A huge black God-awful ugly border around your wheel, hiding all the even uglier jagged corners. Try your code. Make sure everything works.



Now change the border color to match your background.
            #safari_fix
            {
                width:500px;
                height:500px;
                position:absolute;
                z-index:100;
                margin-left:-120px;
                margin-top:-120px;
                border:120px solid #FFFFFF;
                border-radius:50%;
            }


Voila! A cheap and dirty fix for Safari.



Not the best solution, but...

Yep, this is not the most elegant solution you could have. If you ever change the background color, you'll have to change the color of the border of your safari_fix div as well. But if it works, it works. Until I have a better solution, this stays.

Seeya around,
T___T