Neocities, PHP and SQL

Sunday, 17th September 2023

Important Update: Thursday, 21st March 2024

000webhost has "archived" my account" due to "not enough visitors". What this means is they've completely disabled it, and access to the control panel or any of my files, and they want me to pay for their premium service to re-gain access...

I've used 000webhost on and off for years, and this is the first time this has happened to me. I find it perhaps a *tad* shady and I definitely will not be paying for their premium service or using them again.

On the bright side the only thing on there was my poll so I haven't lost anything important.

Unfortunately this means the end of the example poll being here. But I'll leave the tutorial up, it might still be useful since the steps work with any PHP host.

I would seriously recommend if you really want PHP to get a reputable paid host (I mention a couple below). Personally as of late I've found I have plenty of ideas I can do with just HTML, JS and CSS haha.

This has made me appreciate Neocities itself even more. I've been on the free tier for years had zero issues with it. So thanks Neocities!

Apologies for any inconvenience! While you're here why not check out the blog or some of my other projects?




Introduction

In this tutorial I will show you how to code a PHP/SQLite powered poll exactly like the one above. Your PHP and SQLite files will be hosted free on 000webhost.com. The HTML, CSS and JavaScript files will be hosted on your Neocities website.

After completing this tutorial, you'll understand how the concepts work and could move on to making other widgets for your Neocities website. There's no reason why you couldn't create comment systems, high score tables, entire forums, whatever you like!

The nice thing about knowing how to do this stuff yourself is that it gives you complete control and customization of stuff that you'd normally depend on a third party for. And I think this is very much in the spirit of Neocities.

Prerequisites + Notes

Creating a 000webhost Account

I'm going to assume you already have a Neocities account. If not just visit Neocities.org and make one, it takes like 2 minutes.

I've had to do some of these instructions off memory so be sure to let me know in the comments if you get stuck. xD

Creating a 000webhost account is a tiny bit more involved, but it's still pretty straight forward and I don't think screenshots are necessary, but here are the steps if you'd like a little help creating the account. If you know what you're doing just make the account and go straight to the "Get Talking!" heading below.

Get Talking!

Okay, we're good to start actually getting into this thing! To start with, we want to get your 000webhost site and your Neocities site talking to each other.

I think we'll begin on the Neocities side of things. Go to your Neocities account. Click Edit Site. Create a folder called "poll".

Go inside the poll folder. Create a file named dev.html and paste in the following HTML code:

<!doctype html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Poll Dev Page</title> </head> <body> <div class="poll"></div> <p>This is where I'll test my poll works.</p> </body> </html>

Pretty standard stuff. The only new thing you might see there is the viewport meta. All that is, is a magic thing which makes it easier for pages to look right on mobile devices. It's not strictly necessary in this tutorial, but I always put it in when starting a new project. And before you tell me you viewed the current page's source and it wasn't there... My site's "CMS", Static Ultra puts it in dynamically. :P

We've also put an empty <div> with class "poll" into the document. Soon our JavaScript will insert the poll's HTML into that <div>.

Okay. Visit your dev.html in your web browser just to confirm it's alright. You should see a white page with black text saying "This is where I'll test my poll works.".

Next, we're gonna write some JavaScript.

Go back to the poll folder in the Neocities site editor. Create a file called "poll.js".

In there, paste in the following code:

"use strict"; function initPoll() { // Get the class="poll" div that's in the document var pollDiv = document.querySelector( ".poll" ); // Set its HTML to something for now pollDiv.innerHTML = `This will actually be a poll soon!`; }

"use strict"; just makes the browser force us to write better JS, by complaining about more things. I recommend it in all your JS files.

We have a function called initPoll(), which sets the HTML of the div with class "poll" to "This will actually be a poll soon!".

Open dev.html back up for editing. Just before the <title> line, insert the following line:

<script src="poll.js"></script>

Also, insert the following line before the closing </body> line:

<script>initPoll();</script>

Your dev.html thus far, should now look like this:

<!doctype html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <script src="poll.js"></script> <title>Poll Dev Page</title> </head> <body> <div class="poll"></div> <script>initPoll();</script> </body> </html>

Visit dev.html in your browser again. If everything's correct, you should see the page as before, except the previously empty div with class "poll" should now say "This should actually be a poll soon!". If you see this, you know your JavaScript is working. You may need to press Ctrl+F5 to force the browser to update to the latest version of dev.html (something to keep in mind for the rest of the tutorial).

So, let's have a quick recap.

Anyway, we're supposed to be getting our Neocities and 000webhost sites talking to each other. So let's get back to that!

Open up poll.js for editing, and replace the code with the following:

"use strict"; // Global poll div variable, so we can access it in all our functions var pollDiv = ``; // Init the poll function initPoll() { // === Set the poll div to an initial value === // Get the class="poll" div that's in the document pollDiv = document.querySelector( ".poll" ); // Set it's HTML to something for now pollDiv.innerHTML = `Loading poll...`; // === Let's say hello to the server-side of our widget! === // Create a http request object to talk to the server-side var xhr = new XMLHttpRequest(); // Specify which functions to call request OK or error xhr.onerror = onXhrError; xhr.onload = onXhrLoad; // Specify the request type, and URL we want to talk to xhr.open("GET", "https://ssscott.000webhostapp.com/poll.php"); // Send the request xhr.send(); } // Callback for when http request failed function onXhrError(e) { pollDiv.innerHTML = `Unable to load poll`; } // Callback for when http request OK function onXhrLoad(e) { // This line gives us access to the xhr object we created earlier, // without having to store it anywhere. Nice var xhr = e.currentTarget; // The xhr object contains the response from the server. Lets put // that into the poll div! pollDiv.innerHTML = xhr.responseText; }

🦉 Be sure to change "ssscott" to your own 000webhost site name!

Alright, the code contains comments, but let's break it down.

We've changed the initPoll() function. We've taken the pollDiv variable and declared it in global scope so all our functions can use it.

At the start of the function, we get the poll div as before, but now we set the polDiv's HTML to "Loading poll...", which it will stay as while we're waiting for a response from the server.

We have created an XMLHttpRequest object. We will use this object to send a http request to our 000webhost site. A http request is like when you visit a page on a website. For example, when you visit neocities.org/somepage.html, your browser does a http request, downloads somepage.html and displays it to you in your browser. In this case, we are doing a http request to poll.php using JavaScript. Instead of the contents of poll.php being displayed in the browser, it is stored in a variable that we can use in JS!

We set up the xhr object, telling it which functions to call when the request finishes or fails. We specify a URL for the request. We fire off the request with send();.

And finally, we have created the two functions onXhrError() and onXhrLoad(), which are called when we get a response or an error with the xhr request.

Visit dev.html in your web browser (you may need to Ctrl+F5). And guess what! The onXhrError() function fires and you get an error! D'oh!

Well, no worries, let's see if we can do something about that.


Believe it or not, the code above is actually 100% correct. So what's the issue? Well, for security reasons, our 000webhost site is ignoring the request!

So, mosey back on over to 000webhost. Open up your site's control panel, click File Manager, go into the public_html folder, right click on the .htaccess file, click edit, and paste the following line at the top of the file:

Header set Access-Control-Allow-Origin "https://scott2.neocities.org"

🦉 Be sure to change "scott2" to your own Neocities site name!

The above is quite important, and kinda the key to this whole thing. It enables your Neocities site to talk to your 000webhost site!

Click Save + Close.

Now, let's create that poll.php. On the top bar, there's an icon that looks like a white page with a + symbol on it. Click that to create a new file. In the box that comes up, type poll.php.

Right-click on poll.php, and type in the following and then save:

<?php echo "Hello from 000webhost.com!"; ?>

Alright, if you're new to PHP, no worries, here's a quick rundown!

The "<?php" line, just says "everything that follows is PHP code!".

echo just means print. So anything you type inside the double quotes will be output when a browser (or in our case JS) requests the poll.php page. And "?>" just says "we've finished writing PHP code!".

So, as mentioned earlier, whereas with HTML the entire HTML is downloaded when a browser visits a .html page, PHP is different. The PHP is processed on the server, and only the output of the code is sent to the browser. So, just for fun try visiting yourname.000webhostapp.com/poll.php directly in your web browser. The only thing you will see, even if you right-click and view the source, is "Hello from 000webhost.com!".

Anyway, we and our visitors won't be actually visiting poll.php directly in practice. Instead of requesting it by typing it into a web browser address bar, we are requesting it with our xmlHttpRequest/xhr object above, and storing the result in a variable.

Save and close, visit your Neocities poll dev.html, hit Ctrl+F5 and you should find your page successfully requests poll.php from your 000webhost.com site, and puts its output into the page!


Believe it or not, there's not THAT much left to do now! Let's start building our poll HTML.

Open up poll.js on Neocities for editing. Take another glance at the line where we receive the response from the server, and set the innerHTML to it. It looks like this:

pollDiv.innerHTML = xhr.responseText;

At the time we were just making sure our NC and 000wh sites were able to talk to each other. Now, instead we're going to start filling out the poll HTML and CSS. So, change the line above to:

// Empty the poll innerHTML, we're about to add our poll's HTML and CSS to it pollDiv.innerHTML = ``; // Add poll HTML pollDiv.innerHTML += ``; // Add poll CSS pollDiv.innerHTML += ``;

Okay, that should give you an overview, we're clearing the poll div, then adding some HTML to it, then adding some CSS to it. Let's fill out that HTML and CSS properly. Change the pollDiv.innerHTML += ``; line to the following:

pollDiv.innerHTML += ` <div class="poll-title"> Who is your favourite Simpsons character out of these? </div> <div class="poll-option poll-option-1"> <div class="poll-lbl">Homer Simpson</div> <div class="poll-vote-bar"><div>&nbsp;</div></div> <div class="poll-num-votes">1</div> <div class="poll-vote-btn"><input type="button" value="Vote!" -data-optionNum="1"></div> </div> <div class="poll-option-spacer">&nbsp;</div> <div class="poll-option poll-option-2"> <div class="poll-lbl">Sideshow Bob</div> <div class="poll-vote-bar"><div>&nbsp;</div></div> <div class="poll-num-votes">1</div> <div class="poll-vote-btn"><input type="button" value="Vote!" -data-optionNum="2"></div> </div> <div class="poll-option-spacer">&nbsp;</div> <div class="poll-option poll-option-3"> <div class="poll-lbl">Moe Szyslak</div> <div class="poll-vote-bar"><div>&nbsp;</div></div> <div class="poll-num-votes">1</div> <div class="poll-vote-btn"><input type="button" value="Vote!" -data-optionNum="3"></div> </div> <div class="poll-option-spacer">&nbsp;</div> <div class="poll-option poll-option-4"> <div class="poll-lbl">Krusty the Clown</div> <div class="poll-vote-bar"><div>&nbsp;</div></div> <div class="poll-num-votes">1</div> <div class="poll-vote-btn"><input type="button" value="Vote!" -data-optionNum="4"></div> </div> <div class="poll-option-spacer">&nbsp;</div> <div class="poll-option poll-option-5"> <div class="poll-lbl">Mr Burns</div> <div class="poll-vote-bar"><div>&nbsp;</div></div> <div class="poll-num-votes">1</div> <div class="poll-vote-btn"><input type="button" value="Vote!" -data-optionNum="5"></div> </div> <div class="poll-status"> Please vote using one of the buttons! </div> `;

Take a look at the HTML code.

We've inserted a div for the poll's title "Who is your favourite Simpsons character out of these?".

We've inserted a bunch of poll-option divs for each possible answer. The poll option divs are given a .poll-option class so we can style them all at once with CSS, and a .poll-option-NUM class so we can target them individually later with JS.

Each poll option contains a div for the Poll answer, a div for a vote bar to visually show the vote count, a div for showing the vote count as a number, and a div containing a button for visitors to click to vote for that option.

In between each poll option is also a div simply used for spacing. The alternative would have been to use the margin property in CSS, but I like this way better.

If you visit your dev.html now, you should see a pretty ugly looking poll! So let's spruce it up a bit with some CSS.

Change the pollDiv.innerHTML div to the following:

pollDiv.innerHTML += ` <style> .poll { width:100%; max-width:500px; margin-left:auto; margin-right:auto; border:solid 1px grey; padding:8px; } .poll-title { font-weight:bold; background:palegreen; padding:8px; text-align:center; margin-bottom:8px; } .poll-option { display:flex; flex-direction:row; align-items:center; } .poll-option div { margin-right:8px; } .poll-option div:last-child { margin-right:0; } .poll-lbl { width:140px; } .poll-vote-bar { flex:1; } .poll-vote-bar div { width:50%; background:lightblue; } .poll-option-spacer { height:4px; overflow-y:hidden; } .poll-status { margin-top:8px; background:lightgrey; text-align:center; padding:4px; font-style:italic; } </style> `;

So we've used some CSS to colour the poll in and lay it out properly. This isn't a CSS tutorial, so I won't go explaining every property, but I think if you take a look and then view your dev.html, you should get the idea.

If you visit your poll now, you'll see a poll box, a blue background heading for the poll title, the various poll options, and a grey status div at the bottom.


Alright! We're really getting somewhere here. At the moment however, the poll will forever show 1 vote each for each answer. When we load the poll, we'd like to see the actual vote count and bars right? Well, this is where the PHP side of things comes into play.

Head back on over to 000webhost, and open up poll.php for editing.

Just as a quick reminder, at the moment it should look like this:

<?php echo "Hello from 000webhost.com!"; ?>

So that we have a quick overview of our plan, change it to the following:

<?php // Create (if not exist) and open poll database // - todo #1 // Create (if not exist) poll table if if it doesn't already exist // - todo #2 // Insert (if not exist) the row for the Simpsons poll // - todo #3 // Submit vote if one sent // - todo #later // Retrieve the current vote counts from the table // - todo #4 // Output the vote counts using echo // - todo #5 ?>

As you can see from the following, here's what we're about to do:

Alrighty, so, the first step is to create the poll database file if it doesn't exist, and open it. We can actually do this in one line! Swap the todo #1 line for the following:

$db = new SQLite3( "polls.db" );

Pretty easy eh? That line will create an sqlite file called poll.db if it doesn't exist yet. Either way, the $db variable gives us access to it.

Swap the todo #2 line for the following:

$db->exec( " CREATE TABLE IF NOT EXISTS polls ( idNum INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, option1Count INTEGER, option2Count INTEGER, option3Count INTEGER, option4Count INTEGER, option5Count INTEGER ) " );

The above creates a table inside the polls.db database called "polls" if it doesn't exist. The table has 6 columns.

The first column is an idNum (ID number). It is an INTEGER (a number). Don't worry too much IIt is a PRIMARY KEY, which means it's the main unique identifier for all the rows in this table. It AUTOINCREMENTS, which means it goes up by 1 each time we add a row to the table. It is NOT NULL, which means it must have a value. In this tutorial we only have one poll, and thus one row but we are prepared for possibly adding more polls in the future.

Anywho, the remaining columns are counters for poll options. They are all INTEGERS.

So, that sums up creating our "polls" table. Let's move on to creating a row for the Simpsons poll.

Replace the todo #3 line with the following:

$result = $db->query( "SELECT idNum FROM polls WHERE idNum=1" ); $row = $result->fetchArray(); if( $row == false ) { $db->exec( " INSERT INTO polls ( idNum, option1Count, option2Count, option3Count, option4Count, option5Count ) VALUES ( NULL, '0', '0', '0', '0', '0' ) " ); }

First, we check if the row exists at all yet, by SELECTing row with idNum value 1 from the table, and storing it in $result.

We then attempt to make an array from $result. If it's false, that means the row doesn't exist yet. So we Call $db-exec() to run a query which INSERTS the row into the table. The query uses NULL for idNum to let AUTOINCREMENT take care of it for us. And zeros for all the optionXCount values, since no-one has cast any votes yet.

Replace the #todo4 line with the following:

$result = $db->query( "SELECT * FROM polls WHERE idNum=1" ); $row = $result->fetchArray();

Once again we use $db->query and do a SELECT. However this time, we use an asterisk to select ALL of the columns from the row.

We then use $result->fetchArray() to and store the array in $row.

Replace the #todo5 line with the following:

echo $row[ "option1Count" ] . "," . $row[ "option2Count" ] . "," . $row[ "option3Count" ] . "," . $row[ "option4Count" ] . "," . $row[ "option5Count" ];

This line outputs all the values from the row, separated by commas.

If you visit your poll.php in a web browser now, you should see a white page with black text that just says 0,0,0,0,0.


Alright! So now it's time to DO something with that value. Let's head back on over to Neocities and open up our poll.js for editing.

At the end of the onXhrLoad(), before the closing curly bracket, paste the following code:

// Get all the elements that we're going to alter or use var pollVoteBar1Div = document.querySelector( ".poll-option-1 .poll-vote-bar div" ); var pollVoteBar2Div = document.querySelector( ".poll-option-2 .poll-vote-bar div" ); var pollVoteBar3Div = document.querySelector( ".poll-option-3 .poll-vote-bar div" ); var pollVoteBar4Div = document.querySelector( ".poll-option-4 .poll-vote-bar div" ); var pollVoteBar5Div = document.querySelector( ".poll-option-5 .poll-vote-bar div" ); var pollNumVotes1Div = document.querySelector( ".poll-option-1 .poll-num-votes" ); var pollNumVotes2Div = document.querySelector( ".poll-option-2 .poll-num-votes" ); var pollNumVotes3Div = document.querySelector( ".poll-option-3 .poll-num-votes" ); var pollNumVotes4Div = document.querySelector( ".poll-option-4 .poll-num-votes" ); var pollNumVotes5Div = document.querySelector( ".poll-option-5 .poll-num-votes" ); var pollVote1Btn = document.querySelector( ".poll-option-1 input[type=button]" ); var pollVote2Btn = document.querySelector( ".poll-option-2 input[type=button]" );; var pollVote3Btn = document.querySelector( ".poll-option-3 input[type=button]" ); var pollVote4Btn = document.querySelector( ".poll-option-4 input[type=button]" ); var pollVote5Btn = document.querySelector( ".poll-option-5 input[type=button]" ); var pollStatusDiv = document.querySelector( ".poll-status" ); // Get the string returned from the server var str = xhr.responseText; // Turn the voteCounts into integers var strParts = str.split(","); var option1VoteCount = parseInt( strParts[ 0 ] ); var option2VoteCount = parseInt( strParts[ 1 ] ); var option3VoteCount = parseInt( strParts[ 2 ] ); var option4VoteCount = parseInt( strParts[ 3 ] ); var option5VoteCount = parseInt( strParts[ 4 ] ); // Work out vote percentages var totalVotes = option1VoteCount + option2VoteCount + option3VoteCount + option4VoteCount + option5VoteCount; var option1VotePercent = 0; var option2VotePercent = 0; var option3VotePercent = 0; var option4VotePercent = 0; var option5VotePercent = 0; if( totalVotes > 0 ) { option1VotePercent = option1VoteCount / totalVotes * 100; option2VotePercent = option2VoteCount / totalVotes * 100; option3VotePercent = option3VoteCount / totalVotes * 100; option4VotePercent = option4VoteCount / totalVotes * 100; option5VotePercent = option5VoteCount / totalVotes * 100; } // Put the vote counts into the HTML pollNumVotes1Div.innerHTML = option1VoteCount.toString(); pollNumVotes2Div.innerHTML = option2VoteCount.toString(); pollNumVotes3Div.innerHTML = option3VoteCount.toString(); pollNumVotes4Div.innerHTML = option4VoteCount.toString(); pollNumVotes5Div.innerHTML = option5VoteCount.toString(); // Update the width of the vote bars using the percentages pollVoteBar1Div.style.width = option1VotePercent + "%"; pollVoteBar2Div.style.width = option2VotePercent + "%"; pollVoteBar3Div.style.width = option3VotePercent + "%"; pollVoteBar4Div.style.width = option4VotePercent + "%"; pollVoteBar5Div.style.width = option5VotePercent + "%";

Let's break it down again. First we get all the elements that we're going to do stuff with:

// Get all the elements that we're going to alter or use var pollVoteBar1Div = document.querySelector( ".poll-option-1 .poll-vote-bar div" ); var pollVoteBar2Div = document.querySelector( ".poll-option-2 .poll-vote-bar div" ); var pollVoteBar3Div = document.querySelector( ".poll-option-3 .poll-vote-bar div" ); var pollVoteBar4Div = document.querySelector( ".poll-option-4 .poll-vote-bar div" ); var pollVoteBar5Div = document.querySelector( ".poll-option-5 .poll-vote-bar div" ); var pollNumVotes1Div = document.querySelector( ".poll-option-1 .poll-num-votes" ); var pollNumVotes2Div = document.querySelector( ".poll-option-2 .poll-num-votes" ); var pollNumVotes3Div = document.querySelector( ".poll-option-3 .poll-num-votes" ); var pollNumVotes4Div = document.querySelector( ".poll-option-4 .poll-num-votes" ); var pollNumVotes5Div = document.querySelector( ".poll-option-5 .poll-num-votes" ); var pollVote1Btn = document.querySelector( ".poll-option-1 input[type=button]" ); var pollVote2Btn = document.querySelector( ".poll-option-2 input[type=button]" );; var pollVote3Btn = document.querySelector( ".poll-option-3 input[type=button]" ); var pollVote4Btn = document.querySelector( ".poll-option-4 input[type=button]" ); var pollVote5Btn = document.querySelector( ".poll-option-5 input[type=button]" ); var pollStatusDiv = document.querySelector( ".poll-status" );

We get the string returned from the server, which is currently "0,0,0,0,0", into a variable:

var str = xhr.responseText;

We transform this value into their individual integer/number vote counts. We do this by splitting the string by commas, which will store the separate parts in strparts. And then converting each individual part into an integer using parseInt().

// Turn the voteCounts into integers var strParts = str.split(","); var option1VoteCount = parseInt( strParts[ 0 ] ); var option2VoteCount = parseInt( strParts[ 1 ] ); var option3VoteCount = parseInt( strParts[ 2 ] ); var option4VoteCount = parseInt( strParts[ 3 ] ); var option5VoteCount = parseInt( strParts[ 4 ] );

In a moment we are going to make the load bars have the appropriate width, using CSS width:XX%; To do this we need to know the percentage of the vote for each answer, so the next bit of code works that out. You're not allowed to divide by zero, hence the check if totalVotes are > 0.

// Work out vote percentages var totalVotes = option1VoteCount + option2VoteCount + option3VoteCount + option4VoteCount + option5VoteCount; var option1VotePercent = 0; var option2VotePercent = 0; var option3VotePercent = 0; var option4VotePercent = 0; var option5VotePercent = 0; if( totalVotes > 0 ) { option1VotePercent = option1VoteCount / totalVotes * 100; option2VotePercent = option2VoteCount / totalVotes * 100; option3VotePercent = option3VoteCount / totalVotes * 100; option4VotePercent = option4VoteCount / totalVotes * 100; option5VotePercent = option5VoteCount / totalVotes * 100; }

Since we have all the information we need now, we insert the vote counts into the poll's HTML, and update the CSS of the vote percent bars so they have the correct width:

// Put the vote counts into the HTML pollNumVotes1Div.innerHTML = option1VoteCount.toString(); pollNumVotes2Div.innerHTML = option2VoteCount.toString(); pollNumVotes3Div.innerHTML = option3VoteCount.toString(); pollNumVotes4Div.innerHTML = option4VoteCount.toString(); pollNumVotes5Div.innerHTML = option5VoteCount.toString(); // Update the width of the vote bars using the percentages pollVoteBar1Div.style.width = option1VotePercent + "%"; pollVoteBar2Div.style.width = option2VotePercent + "%"; pollVoteBar3Div.style.width = option3VotePercent + "%"; pollVoteBar4Div.style.width = option4VotePercent + "%"; pollVoteBar5Div.style.width = option5VotePercent + "%";

Visit your dev.html now, and you should see that each answer currently has 0 votes. The vote bars will have disappeared! Since there are no votes and they each have a width of 0% at this time.


I guess that means we're onto the final step, enabling people to actually cast a vote!

Once again, go to the end of the onXhrLoad() function, after the code you just wrote, and add the following:

// Make the vote buttons work pollVote1Btn.onclick = onVote1BtnClick; pollVote2Btn.onclick = onVote2BtnClick; pollVote3Btn.onclick = onVote3BtnClick; pollVote4Btn.onclick = onVote4BtnClick; pollVote5Btn.onclick = onVote5BtnClick;

Now go to the very end of the entire file, and add the following:

// Send a vote to the server function castVote( optionID ) { // Send a vote to the server var xhr = new XMLHttpRequest(); xhr.onerror = onXhrError; xhr.onload = onXhrLoad; xhr.open("GET", "http://ssscott.000webhostapp.com/poll.php?vote=" + optionID); xhr.send(); } // Called by button click events, cast a vote using the function below function onVote1BtnClick( e ) { castVote( 1 ); } function onVote2BtnClick( e ) { castVote( 2 ); } function onVote3BtnClick( e ) { castVote( 3 ); } function onVote4BtnClick( e ) { castVote( 4 ); } function onVote5BtnClick( e ) { castVote( 5 ); }

🦉 Be sure to change "ssscott" to your own 000webhost site name!

We have made a function called castVote, which takes an optionID, which will be 1-5 depending on the button pressed. This function sends an http request to the server like before, except this time it adds a parameter to the URL called vote, which includes the number 1-5.

We've also made 5 functions, one for each button. The 5 functions simply call castVote() with the appropriate number 1-5, which in turn will send the appropriate request to the server.


So, head back to the 000webhost file manager and open back up poll.php for editing, if you don't already have it open.

Look for the following lines:

// Submit vote if one sent // - todo #later

Swap them for the following:

// Submit vote if one sent if( isset( $_GET[ "vote" ] ) ) { $vote = (int)$_GET[ "vote" ]; if( $vote >= 1 && $vote <= 5 ) { $db->exec( " UPDATE polls SET option" . $vote . "Count=option" . $vote . "Count+1 WHERE idNum=1 " ); } }

Visit your dev.html in your web browser. You should now be able to cast votes!

So we're finished right? Well not quite. There's one more thing to take care of. We don't want the same person to cast multiple votes over and over right?

There's a couple ways to do this. But I think for the sake of a simple poll we're gonna do it the easiest way, even if it's not the most robust.

Open poll.js for editing once again. Look for the following two lines:

// Get the string returned from the server var str = xhr.responseText;

Before them, insert the following:

// If visitor has already voted, disable all poll buttons and update status if( localStorage.getItem( "pollVoteDone" ) != null ) { if( localStorage.getItem( "pollVoteDone" ) == "true" ) { pollVote1Btn.disabled = true; pollVote2Btn.disabled = true; pollVote3Btn.disabled = true; pollVote4Btn.disabled = true; pollVote5Btn.disabled = true; } pollStatusDiv.innerHTML = "Thanks for voting!"; }

What we have done here is checked inside "local storage" for a variable called pollVoteDone, and if it's set to "true", disable all the poll vote buttons.

Local storage is a permanent browser storage for variables. So it will still be set when the user returns. Visitors can clear their local storage, but 99% of people won't bother.

So, now we need to SET the variable when the visitor casts a vote.

At the end of the castVote() function, add the following lines:

// Set that the poll visitor has voted localStorage.setItem( "pollVoteDone", "true" );

Visit dev.html again, and view your completed poll. If you cast a vote now, you will not be able to vote again afterwards. Lucky you, as the programmer you got to vote twice! :P


The End

Congratulations! If you made it this far you now know how to create a PHP Poll widget for your Neocities site!

If you'd like to include your poll on one of your pages, all you have to do is load it in using <script src="polll/poll.js"></script> and initialise it by calling initPoll() before the closing </body> tag on your page.

I hope you enjoyed the tutorial. If you have any feedback or suggestions, feel free to leave them below!