Results for Web Developer

Make Websites Efficient With Page Visibility

Saturday, September 19, 2020
Make Websites Efficient With Page Visibility
The video player will automatically play and pause depending on the visibility of the page.


Learn a few techniques to create a responsive website that efficiently handles being out of sight with the power-saving Page Visibility API.

We've had tabbed browsing for about a decade. Most users are familiar with the idea of having more than one website open at a time, but it's hard to deduce when your site has their attention. Traditional 'hacks' are to attach an onblur or onfocus listener, but these are far from flawless as they often give false positives.

Enter the Page Visibility API. Although this JS API is still in its infancy, it’s 'a means for site developers to programmatically determine the current visibility state of the page in order to develop power and CPU-efficient web applications'. It's currently a W3C Candidate Recommendation which has been championed by Microsoft and Google, so it’s not surprising to find it's supported in Chrome 13 and will be supported in IE10, as well as Firefox 10 and Opera 12.10. In this tutorial we'll pause and play a video; prevent erroneous visits being added to page analytics; and stop Ajax requests until the user returns, saving greatly on server load.
Make Websites Efficient With Page Visibility
Although a tad dry, it’s well worth reading up on the W3C spec – it tells you most of what you need to know and can really help you out if you get stuck


STEP 1: Adding The Listener
It's incredibly easy to start using the Page Visibility API; if you’ve ever added a click handler this syntax will look very familiar. The visibilitychange event is triggered by several actions: when a user navigates to/from the tab your site is in, when the browser is minimized and when the OS is locked.

CODE:
document.addEventListener( 'visibilitychange', function() {
//do stuff
}, false);
/* jQuery way */
$(document).on( 'visibilitychange', function() {
//do stuff
});
Page not in view: evidence that our JavaScript is up and running by switching between tabs.
Page not in view: evidence that our JavaScript is up and running by switching between tabs.


STEP 2: Catering For All Browsers
Of course, this wouldn’t be web development if it were that easy. Currently, most of the browsers that support the Page Visibility API do so with their own vendor prefix (except Opera). To account for this we can quickly check if the prefixed version is undefined or not.

CODE:
var prefix;
if ( typeof document.hidden !== 'undefined' ) {
prefix = '';
} else if ( typeof document.webkitHidden !== 'undefined' ) {
prefix = 'webkit';
} else if ( typeof document.mozHidden !== 'undefined' ) {
prefix = 'moz';
} else if ( typeof document.msHidden !== 'undefined' ) {
prefix = 'ms';
} else {
window.alert('Your browser doesn\'t support the Page Visibility API');
return;
}

In view! Now to deliver all that rich content we’ve been saving up…
In view! Now to deliver all that rich content we’ve been saving up…


STEP 3: Helping Ourselves
Right, we've now determined if the browser supports the API and which vendor preix to use. To use this data though, we need to check the Boolean value of document.hidden. We could check for each prefix like if (document.hidden || document.webkitHidden ||, etc), but that could quickly get tiring for our fingers. Instead, we’ve written a function to prevent RSI.

CODE:
function isHidden() {
if ( prefix === '' || typeof document.hidden !== 'undefined' ) {
return document.hidden;
} else if ( prefix === 'webkit' || typeof document.webkitHidden !== 'undefined' ) {
return document.webkitHidden;
} else if ( prefix === 'moz' || typeof document.mozHidden !== 'undefined' ) {
return document.mozHidden;
} else if ( prefix === 'ms' || typeof document.msHidden !== 'undefined' ) {
return document.msHidden;
} else {
return null;
}
}

STEP 4: The Universal Approach
With that in place, we can simply call that function to determine the page’s visibility. You’ll probably notice that we have explicitly checked if hidden is false as opposed to the shorthand !isHidden() because that would evaluate as true if isHidden() returned null.

CODE:
document.addEventListener( prefix + 'visibilitychange', function(event) {
if ( isHidden() ) {
} else if ( isHidden() === false) {
}
}, false);

STEP 5: Video HTML
Now we’re starting to get to grips with the Page Visibility API it’s time to do something with it. To kick-off, we’re going to be pausing and playing a video when the user visits a different tab. We’ll start by writing the HTML for the video and include the visibility.js script.

CODE:
<video width="550" height="360" controls autoplay>
<source src="my-video.webm" type="video/webm; codecs='vp8.0, vorbis'">
<source src="my-video.mp4" type="video/mp4; codecs='avc1.4D401E, mp4a.40.2'">
<source src="my-video.ogv" type='video/ogg; codecs="theoravorbis"'>
<p>Your browser can't play this video :(</p>
</video>
<script src="visibility.js"></script>

STEP 6: Controlling The Video
HTML5 video allows us to control its playback with JavaScript through its own very simple API. We’re going to be using two of its methods: play() and pause(). To use these we need to lock on to the video’s node; this assumes it’s the only video element on the page by getting the first in the list.

CODE:
var video = document.getElementsByTagName('video')[0];
document.addEventListener( prefix + 'visibilitychange', function(event) {
if ( isHidden() ) {
video.pause();
} else if ( isHidden() === false ) {
video.play();
}
}, false);

STEP 7: Pause For A Moment
This has the desired effect! Marvel as you never miss a frame of video playback again. Unfortunately, our implementation will pause and start it even if the video has already been watched or was paused by the user. To get around this we are going to check if the video is paused at the point they navigate away from the page.

CODE:
if ( isHidden() ) {
paused = video.paused;
video.pause();
}

STEP 8: Play It Again…
In the previous step, we used a property called paused to check if the video was stopped; we’re now going to use some other properties specific to the video tag to make an educated guess as to whether the video should be played when the user navigates back to our site.

CODE:
if ( video.currentTime > 0 && !paused && !video.ended ) {
video.play();
}

STEP 9: Let’s Recap
Great! We should now have a video that only plays if it was playing, wasn’t paused, and hasn’t ended. This is a good example of progressive enhancement where – if the technology is available – we can add extra levels of functionality to make users’ lives a little easier.

STEP 10: Visibility State
As well as document.hidden the Page Visibility API also adds another attribute: document.visibilityState. This has four possible values: visible, hidden, unloaded, and prerender. Like hidden, it’s current vendor-prefixed so let’s first make a similar function to isHidden() that deals with this.

CODE:
function visibilityState() {
if ( prefix === '' || typeof document.hidden !==\'undefined' ) {
return document.visibilityState;
} else if ( prefix === 'webkit' || typeof document.webkitHidden !== 'undefined' ) {
return document.webkitVisibilityState;
} else if ( prefix === 'moz' || typeof document.mozHidden !== 'undefined' ) {
return document.mozVisibilityState;
} else if ( prefix === 'ms' || typeof document.msHidden !== 'undefined' ) {
return document.msVisibilityState;
} else {
return null;
}
}

STEP 11: Verifying Analytics
When looking at visibilityState the First two values make sense, but prerender is a bit weirder. Some browsers (such as Chrome) pre-render pages that they think the user will click on to speed up browsing. Because this counts as a page view it can skew analytics into thinking they’re getting more views than they really are. We can Fix this with the Page Visibility API.

CODE:
if ( visibilityState() !== 'prerender' ) {
//your analytics code
}

STEP 12: How Long?
You might also find it useful to work out how long a user has been away from your website – either for analytics or to modify an element of your page in some way. When the visibilitychange event is fired it gives us a number of properties – one of which is the timestamp for when the event occurred.

CODE:
if ( isHidden() ) {
timeAway = new Date(event.timeStamp);
}

STEP 13: We Missed You…
We've used JavaScript’s built-in Date object so that we can work with the UNIX timestamp. To calculate how long they were away we can simply subtract one date from the other. We’re also dividing it by 1,000 to convert milliseconds to seconds and rounding the result up to make it more readable.

CODE:
} else if ( isHidden() === false ) {
var delta = new Date(event.timeStamp) - timeAway;
window.alert('You were away for ' + Math.round(delta / 1000) + ' seconds');
}

STEP 14: Changing The Title
This only fires when they leave and come back, but what if you want to do something on your site once the user has been away for a certain amount of time? We can use setInterval() to check how long they’ve been gone. If the user is away for, say, ten minutes, we change the title to ‘Close me!’.

CODE:
setInterval(function() {
var delta = Date.now() - timeAway;
if ( delta > 600000 ) {
document.title = 'Close me!';
clearInterval( this );
}
}, 10000);
Amaze (or annoy) your users by telling them to close your tab by changing the page’s title after a set time.
Amaze (or annoy) your users by telling them to close your tab by changing the page’s title after a set time.


STEP 15: Reverting The Title
Once they come back, we want to change the title back to what it was; you can do this with a simple if statement. It’s worth noting that just because we can do things like this it doesn’t mean we necessarily should. Adding a setInterval when a page isn’t being viewed is extra load on the CPU, but on the other hand, it does offer developers some extra creativity.

CODE:
} else if ( isHidden() === false ) {
if (document.title !== 'Page Visibility API tutorial') {
document.title = 'Page Visibility API tutorial';
}
}
The tab preview shows it has still only made one request.
The tab preview shows it has still only made one request.


STEP 16: Turning To Ajax
Okay, so the last example was a bit gimmicky – it’s unlikely many web developers are going to start encouraging people to close their page after they’ve been gone for ten minutes! Perhaps a more useful application of the Page Visibility API is to stop Ajax requests when they’re not needed.

CODE:
var requests = [],
makeRequests;
function getSomething() {
}
function stopRequests() {
}
To visualise Ajax requests the counter increases for every request made.
To visualise Ajax requests the counter increases for every request made.


STEP 17: This Was A Triumph
The requests variable is going to store all requests (ideally you’d remove requests once they’ve been completed). We’re going to use the jQuery ajax method for its simplicity. Within the getSomething() function, add:

CODE:
var request = $.ajax({
url: ' HYPERLINK "http://lab.fetimo.com/pagevis/resource.json'" http://mysite.com/resource.json',
success: function(response) {
$('body').append(response.message + '<br>');
}
});
requests.push(request);
You probably have better things to do with requests than simply output "This was a Triumph", but whatever the message, the code is the same.
You probably have better things to do with requests than simply output "This was a Triumph", but whatever the message, the code is the same.


STEP 18: Cancel That
jQuery has a handy property called beforeSend on its ajax method which lets you – among other things – set custom headers and, ultimately, decide if the request should be made. We’ll use this to only send a request if the page is visible by returning false if the page is hidden; returning false stops the request from being made.

CODE:
request = $.ajax({
beforeSend: function() {
if ( isHidden() ) {
stopRequests();
return false;
}
},

STEP 19: Setting intervals
To simulate an Ajax-style application making many requests we'll set up a setInterval to make a request every second within getSomething(). We assign it to a variable so that we can clear it in the next step – otherwise, it would be making requests forever! We also don’t want multiple timers, so it has been wrapped in an if statement.

CODE:
if ( !makeRequests ) {
makeRequests = setInterval(function() {
getSomething();
}, 1000);
}

STEP 20: Abort!
Next, we'll write what stopRequests() is meant to do. We will loop through the array and use jQuery’s abort() function on each of the active connections. We’ll then clear the interval that emulates an application making requests for us and reset the variable to be undefined; if we didn’t do this it would remain as the ID of the setInterval.

CODE:
requests.forEach(function( request ) {
request.abort();
});
clearInterval( makeRequests );
makeRequests = undefined;

STEP 21: Initialise Requests
The last thing to do is initialize getSomething() to start making requests both when the page is visible and on initial page load. This will complete emulating a JavaScript app making requests so that we can see the effect of our dabbling with the Page Visibility API.

CODE:
} else if ( isHidden() === false ) {
getSomething();
}
}, false); //close event listener
getSomething();
Once we go back to the tab, it starts to make requests again.
Once we go back to the tab, it starts to make requests again.


STEP 22: Tidying up
Now that we know this works you can change the requests array from storing every request made to just active ones by removing them on request completion. If you know you only ever make one request at a time you can simply do request.pop(); however, here we’re going to compare each element in the array and see if it’s exactly the same as the variable in memory.

CODE:
complete: function() {
requests.forEach(function( req, i ) {
if ( req === request ) {
requests.splice( i, 1 );
}
});
}
Make Websites Efficient With Page Visibility Make Websites Efficient With Page Visibility Reviewed by Kamal Thakur on Saturday, September 19, 2020 Rating: 5

Continuous Testing using Sismo

Sunday, August 30, 2020
Sismo is free and open source, and can be downloaded from the SensioLabs site.
Sismo is free and open source, and can be downloaded from the SensioLabs site.

"Learn how to easily test your PHP projects using SensioLabs’ excellent Sismo suite"

Testing code is something that every developer has to do to ensure a quality product on completion of a project. Most coders when they start testing just manually follow the procedure of the code and try to find where the issue is, possibly echoing out a few variables here and there. While this works fine sometimes, for larger projects or projects you have inherited from other developers it can be a time consuming and frustrating process. Thankfully there are lots of tools available to help simplify testing and to find bugs as soon as they are introduced. PHP is no different and has apps such as SimpleTest or PHPUnit. SensioLabs have been making waves with their modern Symfony2 framework and the associated modules, and recently released the Sismo testing suite. Sismo is a continuous testing server that is written in PHP but can integrate with any language. All you need to do is tell it what program you are using to test within the config file. This tutorial will show you how to test your PHP projects using Sismo, Git, and PHPUnit.

STEP 1: Download Sismo
To start off we need to download the Sismo software, which can be found at sismo.sensiolabs.org. Sismo is quite simply just one PHP file, but don’t be fooled, its a very powerful little tool. Place the file into the web directory of your project.

STEP 2: Install PEAR
You’ll also need to have the PEAR package manager installed if you don’t already. For Windows users, run the file shown from the search bar, for OS X and Linux users, you’ll need to run them from a terminal window. We need to do this, as this is how we will install PHPUnit, which is to be the testing software that we’ll use.

CODE:
Windows:
c:\php\go-pear.bat.

Linux:
$ wget http://pear.php.net/go-pear.phar
$ php go-pear.phar


OS X:
$ wget http://pear.php.net/go-pear.phar
$ php -d detect_unicode=0 go-pear.phar

Installation is done using the PEAR package manager, which is used for many other PHP libraries.
Installation is done using the PEAR package manager, which is used for many other PHP libraries.

STEP 3: Install PHPUnit
Once you have PEAR set up its time to install PHPUnit. PHPUnit is one of the most popular unit testers for PHP and is a valuable tool to have. Back in the terminal, enter the commands below to start the installation.

CODE:
pear config-set auto_discover 1
pear install pear.phpunit.de/PHPUnit

STEP 4: Example Test For PHPUnit
When you write your tests for PHPUnit, you extend the main PHPUnit class and then place your test methods inside that. It’s normal to keep your tests in a separate folder for the rest of your app.

CODE:
require_once 'PHPUnit/Framework.php';
class ArrayTest extends PHPUnit_Framework_TestCase
{
}

STEP 5: Main Test Methods
If you are not used to unit testing, then now is a good time to take a look at how it works and why you really should do it. Unit testing means writing code that verifies the output of your main app code. You write tests for each section of your code until your entire project is covered. You can then run all the tests as one when implementing new features to quickly find bugs across the entire project.

CODE:
functiontestToString(){
{

STEP 6: Testing of Main Test Methods
When writing unit tests, you need to break each method down into goals. Here we simply confirm that the variable we are testing contains the string ‘This is my var’. If it does not, then the test fails.

CODE:
functiontestToString(){
$result=$this->myVar->toString('contains%s');
$expected='containsThis is my var';
$this->assertTrue($result==$expected);

STEP 7: Second Method
As another example of a unit test method, we can test that we have the correct amount of elements within an array by calling the assertCount method. This test would clearly fail, as we are asserting that we have 12 elements when we only have three within our array.

CODE:
public function testAmount()
{
$this->assertCount(12, array('myString' , 'anotherString' , 'etc'));
}

STEP 8: Test Out Sismo
We need to create a config file for Sismo to read from. As Sismo is one file, we have to make the directory and file contents ourselves. It’s easiest with Terminal. Navigate on the command line to the folder you placed Sismo in.

STEP 9: Create Config Directory
In Terminal enter the commands below. This creates our config directory and then a PHP config file that we will populate with our settings. Sismo is straightforward, but there are a few handy settings you can tweak.

CODE:
mkdir ~/.sismo
cd ~/.sismo
touch config.php

The main web interface is attractive, but most of the work can be done using just the console.
The main web interface is attractive, but most of the work can be done using just the console.

STEP 10: Start config.php
Now edit the config.php file to contain the settings with which we need to work. Start off by creating a new array called $projects, then we can add a notification system. Uncomment the notifier variable relevant to your OS.

CODE:
<?php
$projects = array();
For OS X use the Growl notification system on test end
$notifier = $notifier = new Sismo\Notifier\
GrowlNotifier('pa$$word');
For Linux, use Dbus notifications
$notifier = new Sismo\Notifier\DBusNotifier();

STEP 11: Add Your Git Repos
Sismo works with Git repositories, so you will need to have a local and GitHub hosted repo to get the most out of the testing suite. Replace the relevant values in the code below with your own repos.

CODE:
$projects[] = new Sismo\GithubProject
('myLocalProject', '/Users/kiksy/myProj', $notifier);
$projects[] = new Sismo\GithubProject
('myProj', 'kiksy/myProj', $notifier);

STEP 12: Project Settings
Then we need to add a new project to our Sismo object. Once we have done that we can set our remote repository location, this will be somewhere on github.com. The branch that you will need is the most likely master, but you can obviously get this to your own branch you’re working on.

CODE:
$myProj = new Sismo\Project('myProj');
$myProj->setRepository('https://github.com/kiksy/myProj.git');
$myProj->setBranch('master');

STEP 13: Remaining Project Settings
The last settings we need to add make up the command that will run to start your tests. As we are using PHPUnit, it links to the script to run that. You can switch this out though and change it for any testing software that you want to use for your specific language. Finally, we add the commit route and return our $project variable.

CODE:
$myProj->setCommand('./vendors.sh; phpunit');
$myProj->setSlug('symfony-local');
$myProj->setUrlPattern('https://github.com/kiksy/myProj/commit/%commit%');
$myProj->addNotifier($notifier);
$projects[] = $myProj;
return $projects;

STEP 14: Changing The Config File Location
As the config file is very specific for one project, and the default location is set to be within the root of the user directory, you may want to move it – perhaps to the bottom of the project directory. That way you can run multiple Sismo tests on the same machine for different projects. Simply run this command to do so:

CODE:
php sismo.php --config-file=.sismo/config.php

STEP 14: Changing The Config File Location

STEP 15: Run Sismo Test
Open the browser and go to localhost/myProj/sismo.php – you should then see the main Sismo report page. This is where the updates will come on the status of each test and whether or not they have passed. The actual running of the tests is done from the command line.

STEP 15: Run Sismo Test



STEP 16: Run Your Unit Test
The next thing we need to do is run our unit test and see the end result. Make sure you are in the correct directory within your project and then run the command below. In your browser you should then see the results of the test.

CODE:
php sismo.php build

STEP 17: Possible Issues
While Sismo is fairly easy to set up, it is possible to come into some problems. Even if you don’t use it, Sismo is dependent on SQLite3, and so this needs to be installed before Sismo will successfully run. While OS X mostly comes installed with SQLite, some of the Linux flavors don’t. Run the below to start the installation.

CODE:
sudo apt-get install php5-cli php5-dev make
sudo apt-get install libsqlite3-0 libsqlite3-dev
sudo apt-get install php5-sqlite3
sudo apt-get remove php5-sqlite3
cd ~
wget http://pecl.php.net/get/sqlite3-0.6.tgz

STEP 18: Make SQLite3
After we get command has finished downloading the tarball, you need to unzip it and then build the extension from the source. After that the final command will restart Apache. If this doesn’t work for you, you can also try ‘apt-get install php5-SQLite’.

CODE:
$ tar -zxf sqlite3-0.6.tgz
$ cd sqlite3-0.6/
$ sudo phpize
$ sudo ./configure
$ sudo make
$ sudo make install
$ sudo apache2ctl restart

STEP 19: More Descriptive Output
To make Sismo output more information from the command line, either to debug the installation or to find out more on the state of your builds, you can use the --verbose switch when running Sismo. Just enter the command below.

CODE:
php sismo.php build --verbose

STEP 19: More Descriptive Output

STEP 20: Silence Sismo
Alternatively, if you are logging the results of the tests, you might not want Sismo to output so much information. In this case you can tell Sismo to be quiet, simply by using the --q or --quiet switch. If at any time you get stuck, you can also use -h to get help on a topic.

CODE:
php sismo.php build --quiet

STEP 20: Silence Sismo



STEP 21: Extending Sismo
As mentioned earlier, the default location for the config file is in the root of your own personal user directory. This isn’t great if you have lots and lots and lots of projects on the go at one time, or if you are working remotely on a project, perhaps one with multiple branches. SismoFinder is a simple wrapper to solve that problem for you.

CODE:
git clone https://github.com/havvg/SismoFinder.git

STEP 22: SismoFinder Config
You then just need to replace the Sismo config file with the following code. This autoloads the project into the correct directory and saves time. You can also continue to use the other settings such as the choice of notifications.

CODE:
$loader = new Symfony\Component\ClassLoader\UniversalClassLoader();
$loader->registerNamespaces(array(
'SismoFinder' => '/Users/kiksy/myProjSismoFinder/src',
));
$loader->register();
$finder = new SismoFinder\Finder();
$finder->addWorkspace('/Users/kiksy/myProj');
return $finder->getProjects();


STEP 22: SismoFinder Config



Code Library: The Sismo PHP Config Code

The Sismo.php config file is the only thing you need to add to start using the continuous testing server.

CODE:
//$notifier = $notifier = new Sismo\Notifier\GrowlNotifier('pa$$word');
//$notifier = new Sismo\Notifier\DBusNotifier();
$projects[] = new Sismo\GithubProject('myLocalProject', '/Users/kiksy/myProj', $notifier);
$projects[] = new Sismo\GithubProject('myProj', 'kiksy/myProj',$notifier);
$myProj = new Sismo\Project('myProj');

The GitHub repository is set here. Make sure to add the .git at the end, in the same way, you would clone a repo.

CODE:
$myProj->setRepository('https://github.com/kiksy/myProj.git');
$myProj->setBranch('master');
$myProj->setCommand('./vendors.sh;phpunit');
$myProj->setSlug('symfony-local');
$myProj->setUrlPattern('https://github.com/kiksy/myProj/commit/%commit%');
$myProj->addNotifier($notifier);
$projects[] = $myProj;

Finally, we return an array of projects for Sismo to monitor. If the projects are not valid, then Sismo will let you know they have failed in the terminal window.

CODE:
return $projects;

Alternative to PHPUnit:
One of the other popular unit testing suites for PHP is SimpleTest. SimpleTest can be downloaded from www.simpletest.org, and can be used with Sismo simply by linking to the relevant directory.
Continuous Testing using Sismo Continuous Testing using Sismo Reviewed by Kamal Thakur on Sunday, August 30, 2020 Rating: 5
Powered by Blogger.