CSS hover menus with Microsoft Surface (touch device)

If you have a css hover menu you may notice that it doesn't play well with Microsoft Surface IE with either a touch pen or finger. A long press shows the menu but on releasing the menu hides again.

This is because the touch device is unaware that the hover menu is supposed to remain visible after the hover intent has ended. iOS and other touch operating systems handles this logically but the powers to be seem to require websites to be "touch optimised".

Here's what you need to do:

<nav>
	<ul>
		<li>
			<a>Hover menu</a>
			<ul>
				<li><a href="http://example.org">Item 1</li>
				<li><a href="http://example.org">Item 2</li>
				<li><a href="http://example.org">Item 3</li>
			</ul>
		</li>
	</ul>
</nav>

You'll need to make sure that your hover anchor has an href on it to make it "touchable" on certain devices, you can do this with either href="#" or href="javascript:void" , then you need to add aria-haspopup="true" to tell the device that after the user releases the tap, whatever was visible should remain visible until another interaction.

<nav>
	<ul>
		<li>
			<a aria-haspopup="true" href="#">Hover menu</a>
			<ul>
				<li><a href="http://example.org">Item 1</li>
				<li><a href="http://example.org">Item 2</li>
				<li><a href="http://example.org">Item 3</li>
			</ul>
		</li>
	</ul>
</nav>

Graceful degradation Helvetica Neue font family

Here's the tested and proven method of using Helvetica Neue fonts on your website. No Helvetica? No problem, degrades to Arial and Sans Serif for safety.

body {
	font-family: "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, sans-serif; 
	font-weight: 300;
}

On webkit browsers the font (along with others) may appear to have spare pixels around the lettering, making it look "fuzzy". To fix this we can tell webkit browsers to smooth the text with antialiasing:

body {
	-webkit-font-smoothing: antialiased;
}

This font stack originally appeared on CSS Tricks and has been modified slightly for compatibility with devices.

IE & CDN jQuery source fallback

<!--[if !IE]><!--><script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script><script>window.jQuery || document.write('<script src="/js/jquery-3.0.0.min.js"><\/script>')</script><!--<![endif]-->
<!--[if IE]><script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script><script>window.jQuery || document.write('<script src="/js/jquery-2.2.4.min.js"><\/script>')</script><![endif]-->

Sending dynamic status headers from PHP

Sending the right HTTP protocol for headers from PHP is easy, you just need to find which HTTP protocol your server is using. Most servers will be using HTTP/1.1, but allowing PHP to serve the protocol version for you means safer headers for older systems and greater cross-platform compatibility.

Rather than sending

header( 'HTTP/1.1 404 Not Found' );

we're going to send

header( $_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found' );

where

$_SERVER['SERVER_PROTOCOL']

gives us the HTTP protocol which matches the previous 'HTTP/x.x' format.

This can be used with any header that requires the HTTP protocol to be sent, it is not limited to 404 errors in particular.

EDIT 2015-10-10:

Available from PHP 5.4.0, function 'http_response_code' is the alternative to this and sends the correct protocol along with the correct status code description.

Usage: http_response_code(404);
Output: HTTP/1.1 404 Not Found

Recursively zip files with PHP

This function is useful for zipping files/directories when you only have FTP access to a site, and for when you are using a web host ‘File Manager’ which bugs out on permissions or otherwise.

function Zip($source, $destination)
{
    if (!extension_loaded('zip') || !file_exists($source)) {
        return false;
    }
    $zip = new ZipArchive();
    if (!$zip->open($destination, ZIPARCHIVE::CREATE)) {
        return false;
    }
    $source = str_replace('\', '/', realpath($source));
    if (is_dir($source) === true)
    {
        $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST);
        foreach ($files as $file)
        {
            $file = str_replace('\', '/', $file);
            // Ignore "." and ".." folders
            if( in_array(substr($file, strrpos($file, '/')+1), array('.', '..')) )
                continue;
            $file = realpath($file);
            if (is_dir($file) === true)
            {
                $zip->addEmptyDir(str_replace($source . '/', '', $file . '/'));
            }
            else if (is_file($file) === true)
            {
                $zip->addFromString(str_replace($source . '/', '', $file), file_get_contents($file));
            }
        }
    }
    else if (is_file($source) === true)
    {
        $zip->addFromString(basename($source), file_get_contents($source));
    }
    return $zip->close();
}

Zip('/folder/to/compress/', './compressed.zip');

Recursively copy files with PHP

This function is useful for copying files when you only have FTP access to a site, and for when you are using a web host 'File Manager' which bugs out on permissions or otherwise.

function recurse_copy($src, $dst) { 
    $dir = opendir($src); 
    @mkdir($dst); 
    while(false !== ( $file = readdir($dir)) ) { 
        if (( $file != '.' ) && ( $file != '..' )) { 
            if ( is_dir($src . '/' . $file) ) { 
                recurse_copy($src . '/' . $file,$dst . '/' . $file); 
            } 
            else { 
                copy($src . '/' . $file,$dst . '/' . $file); 
            } 
        } 
    } 
    closedir($dir); 
} 

recurse_copy('./big-directory', './big-directory-new');

Download file with cURL & PHP

CURLOPT_RETURNTRANSFER is a simple way of copying a file from a remote server onto your own. However, if you're downloading a large file you may hit memory limits because the entire contents of the download have to be read to memory before being saved.

Note: Even if your memory limit is set extremely high, you would be putting unnecessary strain on your server by reading in a large file straight to memory.

Instead you can write the download straight to a file stream using CURLOPT_FILE.

$url  = 'http://www.example.com/a-large-file.zip';
$path = '/path/to/a-large-file.zip';
$fp = fopen($path, 'w');
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_FILE, $fp);
$data = curl_exec($ch);
curl_close($ch);
fclose($fp);

Batch Delete from Table in MSSQL

This script allows you to perform an operation in batches. This should allow you to keep the log file for the database down whilst performing large operations.

SET ROWCOUNT allows you to set how many records will be effected per query.
SET @intFlag = 1 sets the loop to start at 1
WHILE (@intFlag <=100) loop through this code X times (for this example 100)
SET @intFlag = @intFlag + 1 add 1 to the loop count

SET ROWCOUNT 500000;

DECLARE @intFlag INT
SET @intFlag = 1
WHILE (@intFlag <=100)
BEGIN

DELETE FROM tblTemp WHERE tmpDate < '2012-10-01'

SET @intFlag = @intFlag + 1
END
GO

Here the ROWCOUNT is set to 500,000. On this server attempting to delete 1,000,000 records by running 2 batches of 500,000 delete records was 35 seconds faster than running a single 1,000,000 delete records statement. But running 10 batches of 100,000 delete records was also slower than 2 batches at 500,000 records. This may vary server to server.

JavaScript str_pad function

JavaScript equivalent of str_pad function for PHP

function strPad(input, length, string) {
    string = string || '0'; input = input + '';
    return input.length >= length ? input : new Array(length - input.length + 1).join(string) + input;
}

Usage: strPad ('1', 3, 0);
Output: 001